midi-harbor/tests/headless_parity.rs
2026-09-28 13:59:10 -05:00

63 lines
2.6 KiB
Rust

//! Every request the graphical interface can make, the command line can make too (FR-039c,
//! SC-014b).
//!
//! A headless machine has only the command line, so anything reachable from the GUI alone would
//! be something a headless user cannot do. Each call in the contract takes its own request type,
//! so the request types a crate names are the calls it can make.
#![allow(clippy::expect_used, clippy::panic, clippy::unwrap_used)]
use std::collections::BTreeSet;
use std::path::Path;
/// Collects every `…Request` type named in the Rust files under `dir`.
fn requests_named_in(dir: &Path) -> BTreeSet<String> {
let mut found = BTreeSet::new();
let mut pending = vec![dir.to_path_buf()];
while let Some(dir) = pending.pop() {
for entry in std::fs::read_dir(&dir).expect("a source directory") {
let path = entry.expect("a directory entry").path();
if path.is_dir() {
pending.push(path);
continue;
}
if path.extension().is_none_or(|ext| ext != "rs") {
continue;
}
let text = std::fs::read_to_string(&path).expect("a source file");
for word in text.split(|c: char| !c.is_ascii_alphanumeric() && c != '_') {
let named_request = word.len() > "Request".len()
&& word.ends_with("Request")
&& word.starts_with(|c: char| c.is_ascii_uppercase());
if named_request {
found.insert(word.to_owned());
}
}
}
}
found
}
/// Locks that every contract request the GUI names, the command line names too.
///
/// A headless machine has only the command line, so a call only the window can make is one a
/// headless user cannot. Scanning the sources rather than listing calls by hand means a new
/// window feature fails here until the command line gains it.
#[test]
fn every_gui_request_is_reachable_from_the_command_line() {
let root = Path::new(env!("CARGO_MANIFEST_DIR"));
let gui = requests_named_in(&root.join("crates/gui/src"));
let cli = requests_named_in(&root.join("crates/cli/src"));
// An empty set would pass trivially, which is how this test would stop meaning anything if
// the interface moved its calls somewhere else.
assert!(
gui.len() >= 5,
"found only {gui:?} in the GUI; has its client moved?"
);
let missing: Vec<&String> = gui.difference(&cli).collect();
assert!(
missing.is_empty(),
"the GUI can make these requests and the command line cannot: {missing:?}"
);
}