#![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); } }