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] = &["journald_native.rs"];
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 entry in std::fs::read_dir(directory).expect("the source directory").flatten() {
let name: String = entry.file_name().to_string_lossy().into_owned();
let covered: bool = COVERED_PREFIXES.iter().any(|prefix| { return name.starts_with(prefix); });
if covered && name.ends_with(".rs") && !name.ends_with("_tests.rs") {
found.push((name, std::fs::read_to_string(entry.path()).expect("a readable source")));
}
}
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 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));
}