Add reproducible unprivileged Linux build and mount checks

This commit is contained in:
2026-10-09 18:32:37 -04:00
parent 8b177e21a0
commit 988aad9951
11 changed files with 212 additions and 10 deletions
+96
View File
@@ -0,0 +1,96 @@
#![cfg(target_os = "linux")]
use sized::{scan_tree, ScanControl, ScanOptions};
use std::{fs, io::Write, os::unix::fs::MetadataExt, path::PathBuf, process::Command};
use tempfile::NamedTempFile;
#[test]
#[ignore = "requires the two owned tmpfs mounts from scripts/check-linux.sh"]
fn real_mount_boundary_is_respected_by_scanner_and_cli() {
let root = PathBuf::from(
std::env::var_os("SIZED_TEST_MOUNT_ROOT").expect("missing mounted Linux fixture"),
);
let foreign = root.join("foreign");
let root_metadata = fs::metadata(&root).unwrap();
let foreign_metadata = fs::metadata(&foreign).unwrap();
assert_ne!(
root_metadata.dev(),
foreign_metadata.dev(),
"not a real boundary"
);
let mut local_file = NamedTempFile::new_in(&root).unwrap();
let mut mounted_file = NamedTempFile::new_in(&foreign).unwrap();
local_file.write_all(b"local allocation").unwrap();
mounted_file.write_all(&[7; 8192]).unwrap();
local_file.as_file().sync_all().unwrap();
mounted_file.as_file().sync_all().unwrap();
for bounded in [false, true] {
let report = scan_tree(
&root,
&ScanOptions {
stay_on_filesystem: bounded,
threads: Some(2),
..ScanOptions::default()
},
&ScanControl::default(),
)
.unwrap();
assert!(report.root.complete);
assert!(report.issues.is_empty());
let mount = report
.root
.children
.iter()
.find(|n| n.path == foreign)
.unwrap();
assert_eq!(mount.identity.unwrap().device, foreign_metadata.dev());
assert_eq!(mount.skipped_mount, bounded);
assert_eq!(report.entries_scanned, if bounded { 3 } else { 4 });
let expected_blocks =
root_metadata.blocks() + local_file.as_file().metadata().unwrap().blocks();
if bounded {
assert!(mount.children.is_empty());
assert_eq!(mount.blocks, 0);
assert_eq!(mount.size_bytes, 0);
assert_eq!(report.root.blocks, expected_blocks);
} else {
assert_eq!(mount.children.len(), 1);
assert_eq!(mount.children[0].path, mounted_file.path());
assert_eq!(mount.children[0].size_bytes, 8192);
assert_eq!(
report.root.blocks,
expected_blocks
+ foreign_metadata.blocks()
+ mounted_file.as_file().metadata().unwrap().blocks()
);
}
let mut command = Command::new(env!("CARGO_BIN_EXE_sized"));
command
.arg(&root)
.args(["--format", "json", "--apparent", "--threads", "2"]);
if bounded {
command.arg("--one-file-system");
}
let output = command.output().unwrap();
assert!(
output.status.success(),
"{}",
String::from_utf8_lossy(&output.stderr)
);
assert!(output.stderr.is_empty());
let json: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
assert_eq!(json["complete"], true);
assert_eq!(json["total_blocks"], report.root.blocks);
let mount_json = json["entries"]
.as_array()
.unwrap()
.iter()
.find(|entry| entry["path"] == foreign.to_str().unwrap())
.unwrap();
assert_eq!(mount_json["skipped_mount"], bounded);
assert_eq!(mount_json["blocks"], mount.blocks);
assert_eq!(mount_json["apparent_size"], mount.size_bytes);
}
}