use super::{FsApi, VIRTUAL_DISK_KIND, parse_block_device_name, reclassify_virtual_hdd};
use pipe_trait::Pipe;
use pretty_assertions::assert_eq;
use std::{
io,
path::{Path, PathBuf},
};
use sysinfo::DiskKind;
#[test]
fn test_parse_block_device_name() {
let cases: &[(&str, Option<&str>)] = &[
("/dev/sda", Some("sda")),
("/dev/sda1", Some("sda")),
("/dev/sdb3", Some("sdb")),
("/dev/vda", Some("vda")),
("/dev/vda1", Some("vda")),
("/dev/vdb2", Some("vdb")),
("/dev/xvda", Some("xvda")),
("/dev/xvda1", Some("xvda")),
("/dev/nvme0n1", Some("nvme0n1")),
("/dev/nvme0n1p1", Some("nvme0n1")),
("/dev/mmcblk0", Some("mmcblk0")),
("/dev/mmcblk0p1", Some("mmcblk0")),
("vda1", None),
("/dev/loop0", Some("loop0")),
];
for (input, expected) in cases {
let actual = parse_block_device_name(input);
println!("CASE: {input} → {actual:?} (expected {expected:?})");
assert_eq!(actual, *expected);
}
}
macro_rules! identity_reclassify_test_case {
(
$(#[$attr:meta])*
$name:ident where
block_device = $block:literal,
driver = $driver:literal,
disk_name = $disk_name:literal,
expected = $expected:expr,
) => {
$(#[$attr])*
#[test]
fn $name() {
static DEVICES: &[&str] = &[concat!("/sys/block/", $block)];
static DRIVERS: &[(&str, &str)] =
&[(concat!("/sys/block/", $block, "/device/driver"), $driver)];
struct Fs;
impl FsApi for Fs {
fn canonicalize(path: &Path) -> io::Result<PathBuf> {
path.to_path_buf().pipe(Ok)
}
fn path_exists(path: &Path) -> bool {
DEVICES.iter().any(|dev| path == Path::new(*dev))
}
fn read_link(path: &Path) -> io::Result<PathBuf> {
DRIVERS
.iter()
.find(|(drv_path, _)| path == Path::new(*drv_path))
.map(|(_, drv_name)| PathBuf::from(format!("/drivers/{drv_name}")))
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "mocked"))
}
}
assert_eq!(
reclassify_virtual_hdd::<Fs>(DiskKind::HDD, $disk_name),
$expected,
);
}
};
}
identity_reclassify_test_case! {
test_virtio_disk_is_reclassified where
block_device = "vda",
driver = "virtio_blk",
disk_name = "/dev/vda1",
expected = VIRTUAL_DISK_KIND,
}
identity_reclassify_test_case! {
test_virtio_blk_hyphen_disk_is_reclassified where
block_device = "vda",
driver = "virtio-blk",
disk_name = "/dev/vda1",
expected = VIRTUAL_DISK_KIND,
}
identity_reclassify_test_case! {
test_xen_vbd_disk_is_reclassified where
block_device = "xvda",
driver = "vbd",
disk_name = "/dev/xvda1",
expected = VIRTUAL_DISK_KIND,
}
identity_reclassify_test_case! {
test_xen_blkfront_underscore_disk_is_reclassified where
block_device = "xvda",
driver = "xen_blkfront",
disk_name = "/dev/xvda1",
expected = VIRTUAL_DISK_KIND,
}
identity_reclassify_test_case! {
test_xen_blkfront_hyphen_disk_is_reclassified where
block_device = "xvda",
driver = "xen-blkfront",
disk_name = "/dev/xvda1",
expected = VIRTUAL_DISK_KIND,
}
identity_reclassify_test_case! {
test_vmware_pvscsi_disk_is_reclassified where
block_device = "sda",
driver = "vmw_pvscsi",
disk_name = "/dev/sda1",
expected = VIRTUAL_DISK_KIND,
}
identity_reclassify_test_case! {
test_hyperv_storvsc_disk_is_reclassified where
block_device = "sda",
driver = "hv_storvsc",
disk_name = "/dev/sda1",
expected = VIRTUAL_DISK_KIND,
}
identity_reclassify_test_case! {
test_physical_disk_stays_hdd where
block_device = "sda",
driver = "sd",
disk_name = "/dev/sda1",
expected = DiskKind::HDD,
}
#[test]
fn test_mapper_symlink_resolves_to_virtual_partition() {
struct Fs;
impl FsApi for Fs {
fn canonicalize(path: &Path) -> io::Result<PathBuf> {
[("/dev/mapper/vg0-lv0", "/dev/vda1")]
.iter()
.find(|(src, _)| path == Path::new(*src))
.map(|(_, target)| PathBuf::from(*target))
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "mocked"))
}
fn path_exists(path: &Path) -> bool {
["/sys/block/vda"].iter().any(|dev| path == Path::new(*dev))
}
fn read_link(path: &Path) -> io::Result<PathBuf> {
[("/sys/block/vda/device/driver", "virtio_blk")]
.iter()
.find(|(drv_path, _)| path == Path::new(*drv_path))
.map(|(_, drv_name)| PathBuf::from(format!("/drivers/{drv_name}")))
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "mocked"))
}
}
assert_eq!(
reclassify_virtual_hdd::<Fs>(DiskKind::HDD, "/dev/mapper/vg0-lv0"),
VIRTUAL_DISK_KIND,
);
}
#[test]
fn test_mapper_dm_device_is_not_corrected() {
struct Fs;
impl FsApi for Fs {
fn canonicalize(path: &Path) -> io::Result<PathBuf> {
[("/dev/mapper/vg0-lv0", "/dev/dm-0")]
.iter()
.find(|(src, _)| path == Path::new(*src))
.map(|(_, target)| PathBuf::from(*target))
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "mocked"))
}
fn path_exists(path: &Path) -> bool {
path == Path::new("/sys/block/dm-0")
}
fn read_link(_: &Path) -> io::Result<PathBuf> {
Err(io::Error::new(io::ErrorKind::NotFound, "mocked"))
}
}
assert_eq!(
reclassify_virtual_hdd::<Fs>(DiskKind::HDD, "/dev/mapper/vg0-lv0"),
DiskKind::HDD,
);
}
#[test]
fn test_ssd_is_not_corrected() {
struct Fs;
impl FsApi for Fs {
fn canonicalize(_: &Path) -> io::Result<PathBuf> {
panic!("canonicalize should not be called for non-HDD disks");
}
fn path_exists(_: &Path) -> bool {
panic!("path_exists should not be called for non-HDD disks");
}
fn read_link(_: &Path) -> io::Result<PathBuf> {
panic!("read_link should not be called for non-HDD disks");
}
}
assert_eq!(
reclassify_virtual_hdd::<Fs>(DiskKind::SSD, "/dev/sda1"),
DiskKind::SSD,
);
}