use std::path::{Path, PathBuf};
const COVERED_PREFIXES: &[&str] = &[
"service_",
"claim_time",
"discarded_",
"index_lock_",
"journald_native",
"git_version",
"config_",
"process_identity",
"private_storage",
"recovery_files",
"work_item",
];
const LOCK_WORDS: &[&str] = &["Mutex", "RwLock", "Condvar", "flock(", "LOCK_EX", "LOCK_SH"];
const REASON_MARKER: &str = "Lock kept:";
const INVENTORY: &[&str] = &["landing/service_index_reclaim.rs", "service/journald_native.rs"];
fn rust_files_below(root: &Path) -> Vec<String> {
let mut found: Vec<String> = Vec::new();
let mut pending: Vec<PathBuf> = vec![root.to_path_buf()];
while let Some(directory) = pending.pop() {
for entry in std::fs::read_dir(&directory).expect("a readable source directory").flatten() {
let path: PathBuf = entry.path();
if path.is_dir() {
pending.push(path);
} else if path.extension().is_some_and(|extension| { return extension == "rs"; }) {
let relative: &Path = path.strip_prefix(root).expect("a path below the root");
found.push(relative.to_string_lossy().replace('\\', "/"));
}
}
}
found.sort();
return found;
}
fn is_covered(path: &str) -> bool {
let name: &str = path.rsplit('/').next().unwrap_or(path);
return COVERED_PREFIXES.iter().any(|prefix| { return name.starts_with(prefix); });
}
fn covered_sources() -> Vec<(String, String)> {
let directory: PathBuf = Path::new(env!("CARGO_MANIFEST_DIR")).join("src/native");
let mut found: Vec<(String, String)> = Vec::new();
for relative in rust_files_below(directory.as_path()) {
if is_covered(relative.as_str()) && !relative.ends_with("_tests.rs") {
let text: String = std::fs::read_to_string(directory.join(relative.as_str())).expect("a readable source");
found.push((relative, text));
}
}
found.sort();
return found;
}
fn lock_lines(source: &str) -> Vec<usize> {
return source
.lines()
.enumerate()
.filter(|(_, line)| {
let code: &str = line.trim_start();
return !code.starts_with("//") && !code.starts_with("use ") && !code.starts_with("pub use ") && LOCK_WORDS.iter().any(|word| { return code.contains(word); });
})
.map(|(number, _)| { return number; })
.collect();
}
fn reason_above(source: &str, number: usize) -> bool {
let lines: Vec<&str> = source.lines().collect();
let mut cursor: usize = number;
while cursor > 0 {
cursor -= 1;
let above: &str = lines[cursor].trim_start();
if above.starts_with("//") && above.contains(REASON_MARKER) {
return true;
}
if !above.starts_with("//") && !above.starts_with("#[") {
return false;
}
}
return false;
}
#[test]
fn a_lock_stands_only_in_a_file_on_the_inventory() {
let strays: Vec<String> = covered_sources()
.into_iter()
.filter(|(name, source)| { return !lock_lines(source.as_str()).is_empty() && !INVENTORY.contains(&name.as_str()); })
.map(|(name, _)| { return name; })
.collect();
assert!(strays.is_empty(), "locks outside the inventory (remove them, or keep them with the reason and list the file): {strays:?}");
}
#[test]
fn every_kept_lock_has_its_reason_beside_it_and_the_inventory_has_no_stale_entry() {
let sources: Vec<(String, String)> = covered_sources();
for kept in INVENTORY {
let (_, source) = sources.iter().find(|(name, _)| { return name == kept; }).unwrap_or_else(|| { panic!("{kept} is on the inventory but is not a covered source"); });
let lines: Vec<usize> = lock_lines(source.as_str());
let first: usize = *lines.first().unwrap_or_else(|| { panic!("{kept} is on the inventory and holds no lock any more"); });
assert!(reason_above(source.as_str(), first), "{kept}:{} declares a lock with no `{REASON_MARKER}` comment block above it", first + 1);
}
}
#[test]
fn every_covered_prefix_reaches_a_source_in_some_folder() {
let sources: Vec<(String, String)> = covered_sources();
for prefix in COVERED_PREFIXES {
let reached: bool = sources.iter().any(|(path, _)| {
return path.rsplit('/').next().is_some_and(|name| { return name.starts_with(prefix); });
});
assert!(reached, "no covered source starts with {prefix}");
}
}
#[test]
fn the_scan_finds_a_lock_in_text_that_has_one() {
let sample: &str = "// Mutex in a comment\nuse std::sync::Mutex;\nstruct A {\n held: Mutex<u8>,\n}\n";
assert_eq!(lock_lines(sample), vec![3]);
assert!(!reason_above(sample, 3));
assert!(reason_above("/// Lock kept: because.\n#[derive(Debug)]\nheld: Mutex<u8>,\n", 2));
}