use std::env;
use std::fs::{File, OpenOptions};
use std::mem::{align_of, offset_of, size_of};
use std::os::fd::AsRawFd;
use nix::errno::Errno;
use nvme_cli_sys::{nvme_admin_cmd, nvme_ioctl_id, nvme_passthru_cmd, nvme_passthru_cmd64};
#[test]
fn nvme_admin_cmd_alias_matches_passthru_cmd() {
assert_eq!(size_of::<nvme_admin_cmd>(), size_of::<nvme_passthru_cmd>());
assert_eq!(
align_of::<nvme_admin_cmd>(),
align_of::<nvme_passthru_cmd>()
);
}
#[test]
fn nvme_passthru_cmd_layout() {
assert_eq!(size_of::<nvme_passthru_cmd>(), 72);
assert_eq!(align_of::<nvme_passthru_cmd>(), 8);
assert_eq!(offset_of!(nvme_passthru_cmd, opcode), 0);
assert_eq!(offset_of!(nvme_passthru_cmd, flags), 1);
assert_eq!(offset_of!(nvme_passthru_cmd, rsvd1), 2);
assert_eq!(offset_of!(nvme_passthru_cmd, nsid), 4);
assert_eq!(offset_of!(nvme_passthru_cmd, cdw2), 8);
assert_eq!(offset_of!(nvme_passthru_cmd, cdw3), 12);
assert_eq!(offset_of!(nvme_passthru_cmd, metadata), 16);
assert_eq!(offset_of!(nvme_passthru_cmd, addr), 24);
assert_eq!(offset_of!(nvme_passthru_cmd, metadata_len), 32);
assert_eq!(offset_of!(nvme_passthru_cmd, data_len), 36);
assert_eq!(offset_of!(nvme_passthru_cmd, cdw10), 40);
assert_eq!(offset_of!(nvme_passthru_cmd, cdw11), 44);
assert_eq!(offset_of!(nvme_passthru_cmd, cdw12), 48);
assert_eq!(offset_of!(nvme_passthru_cmd, cdw13), 52);
assert_eq!(offset_of!(nvme_passthru_cmd, cdw14), 56);
assert_eq!(offset_of!(nvme_passthru_cmd, cdw15), 60);
assert_eq!(offset_of!(nvme_passthru_cmd, timeout_ms), 64);
assert_eq!(offset_of!(nvme_passthru_cmd, result), 68);
}
#[test]
fn nvme_passthru_cmd64_layout() {
assert_eq!(size_of::<nvme_passthru_cmd64>(), 80);
assert_eq!(align_of::<nvme_passthru_cmd64>(), 8);
assert_eq!(offset_of!(nvme_passthru_cmd64, opcode), 0);
assert_eq!(offset_of!(nvme_passthru_cmd64, flags), 1);
assert_eq!(offset_of!(nvme_passthru_cmd64, rsvd1), 2);
assert_eq!(offset_of!(nvme_passthru_cmd64, nsid), 4);
assert_eq!(offset_of!(nvme_passthru_cmd64, cdw2), 8);
assert_eq!(offset_of!(nvme_passthru_cmd64, cdw3), 12);
assert_eq!(offset_of!(nvme_passthru_cmd64, metadata), 16);
assert_eq!(offset_of!(nvme_passthru_cmd64, addr), 24);
assert_eq!(offset_of!(nvme_passthru_cmd64, metadata_len), 32);
assert_eq!(offset_of!(nvme_passthru_cmd64, data_len), 36);
assert_eq!(offset_of!(nvme_passthru_cmd64, cdw10), 40);
assert_eq!(offset_of!(nvme_passthru_cmd64, cdw11), 44);
assert_eq!(offset_of!(nvme_passthru_cmd64, cdw12), 48);
assert_eq!(offset_of!(nvme_passthru_cmd64, cdw13), 52);
assert_eq!(offset_of!(nvme_passthru_cmd64, cdw14), 56);
assert_eq!(offset_of!(nvme_passthru_cmd64, cdw15), 60);
assert_eq!(offset_of!(nvme_passthru_cmd64, timeout_ms), 64);
assert_eq!(offset_of!(nvme_passthru_cmd64, rsvd2), 68);
assert_eq!(offset_of!(nvme_passthru_cmd64, result), 72);
}
#[test]
fn passthru_defaults_are_zeroed() {
let command = nvme_passthru_cmd::default();
assert_eq!(command.opcode, 0);
assert_eq!(command.nsid, 0);
assert_eq!(command.addr, 0);
assert_eq!(command.data_len, 0);
assert_eq!(command.timeout_ms, 0);
assert_eq!(command.result, 0);
let command64 = nvme_passthru_cmd64::default();
assert_eq!(command64.opcode, 0);
assert_eq!(command64.nsid, 0);
assert_eq!(command64.addr, 0);
assert_eq!(command64.data_len, 0);
assert_eq!(command64.timeout_ms, 0);
assert_eq!(command64.result, 0);
}
#[test]
fn nvme_ioctl_rejects_non_nvme_file_descriptor() -> Result<(), Box<dyn std::error::Error>> {
let file = File::open("/dev/null")?;
let error = unsafe { nvme_ioctl_id(file.as_raw_fd()) }.unwrap_err();
assert_eq!(error, Errno::ENOTTY);
Ok(())
}
#[test]
#[ignore = "requires access to an NVMe namespace device"]
fn nvme_ioctl_id_on_real_device() -> Result<(), Box<dyn std::error::Error>> {
let device = env::var("NVME_TEST_DEVICE").unwrap_or_else(|_| "/dev/nvme0n1".to_owned());
let file = OpenOptions::new().read(true).open(&device)?;
let namespace_id = unsafe { nvme_ioctl_id(file.as_raw_fd())? };
assert_ne!(
namespace_id, 0,
"invalid namespace ID returned for {device}"
);
eprintln!("{device}: namespace ID {namespace_id}");
Ok(())
}