use std::collections::BTreeSet;
use std::path::Path;
fn repo_root() -> &'static Path {
Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("crates/")
.parent()
.expect("repo root")
}
fn parse_sh_modes(content: &str) -> BTreeSet<String> {
let mut modes = BTreeSet::new();
let mut in_modes = false;
for line in content.lines() {
let trimmed = line.trim();
if trimmed.starts_with("# Modes:") {
in_modes = true;
continue;
}
if in_modes {
if trimmed.starts_with("# Common flags:")
|| trimmed.starts_with("# Env overrides:")
|| trimmed.is_empty()
{
break;
}
if let Some(rest) = trimmed.strip_prefix("#") {
let text = rest.trim();
if text.starts_with("--") {
let mode = text
.split_whitespace()
.next()
.unwrap()
.trim_start_matches("--");
modes.insert(mode.to_string());
} else if text.starts_with("(default)") {
modes.insert("default".to_string());
}
}
}
}
modes
}
fn parse_ps1_modes(content: &str) -> BTreeSet<String> {
let mut modes = BTreeSet::new();
let mut in_modes = false;
for line in content.lines() {
let trimmed = line.trim();
if trimmed.starts_with("# Modes:") {
in_modes = true;
continue;
}
if in_modes {
if trimmed.starts_with("# Common flags:")
|| trimmed.starts_with("# Env overrides:")
|| trimmed.is_empty()
{
break;
}
if let Some(rest) = trimmed.strip_prefix("#") {
let text = rest.trim();
if text.starts_with("-") {
let mode = text
.split_whitespace()
.next()
.unwrap()
.trim_start_matches("-")
.to_lowercase();
modes.insert(mode);
} else if text.starts_with("(default)") {
modes.insert("default".to_string());
}
}
}
}
modes
}
fn parse_sh_flags(content: &str) -> BTreeSet<String> {
let mut flags = BTreeSet::new();
let mut in_flags = false;
for line in content.lines() {
let trimmed = line.trim();
if trimmed.starts_with("# Common flags:") {
in_flags = true;
continue;
}
if in_flags {
if trimmed.starts_with("# Env overrides:") || trimmed.is_empty() {
break;
}
if let Some(rest) = trimmed.strip_prefix("#") {
let text = rest.trim();
if text.starts_with("--") {
let flag = text
.split_whitespace()
.next()
.unwrap()
.split('=')
.next()
.unwrap()
.trim_start_matches("--")
.to_string();
flags.insert(flag);
}
}
}
}
flags
}
fn kebab_case(name: &str) -> String {
let mut out = String::with_capacity(name.len() + 2);
for (index, ch) in name.chars().enumerate() {
if ch.is_ascii_uppercase() && index > 0 {
out.push('-');
}
out.extend(ch.to_lowercase());
}
out
}
fn parse_ps1_flags(content: &str) -> BTreeSet<String> {
let mut flags = BTreeSet::new();
let mut in_flags = false;
for line in content.lines() {
let trimmed = line.trim();
if trimmed.starts_with("# Common flags:") {
in_flags = true;
continue;
}
if in_flags {
if trimmed.starts_with("# Env overrides:") || trimmed.is_empty() {
break;
}
if let Some(rest) = trimmed.strip_prefix("#") {
let text = rest.trim();
if text.starts_with('-') {
let flag = text.split_whitespace().next().unwrap();
flags.insert(kebab_case(flag.trim_start_matches('-')));
}
}
}
}
flags
}
fn extract_public_key(content: &str) -> Option<String> {
for line in content.lines() {
if line.contains("RWTPnJ/p6xVJ3TJIxr+ZVHMD/MTHWZhsdE38Go/oD3DYBoi4bePR55go") {
return Some("RWTPnJ/p6xVJ3TJIxr+ZVHMD/MTHWZhsdE38Go/oD3DYBoi4bePR55go".to_string());
}
}
None
}
#[test]
fn install_scripts_expose_matching_modes_and_parity() {
let root = repo_root();
let sh_path = root.join("install.sh");
let ps1_path = root.join("install.ps1");
assert!(sh_path.exists(), "install.sh must exist at repo root");
assert!(ps1_path.exists(), "install.ps1 must exist at repo root");
let sh_content = std::fs::read_to_string(&sh_path).expect("read install.sh");
let ps1_content = std::fs::read_to_string(&ps1_path).expect("read install.ps1");
let sh_modes = parse_sh_modes(&sh_content);
let ps1_modes = parse_ps1_modes(&ps1_content);
assert!(
!sh_modes.is_empty(),
"install.sh must document public execution modes"
);
assert_eq!(
sh_modes, ps1_modes,
"install.sh and install.ps1 must document identical public modes"
);
for required in &["default", "diagnose", "calibrate", "uninstall"] {
assert!(
sh_modes.contains(*required),
"installer modes must contain mandatory '{required}' mode"
);
}
}
#[test]
fn install_scripts_share_public_signing_key_and_repo() {
let root = repo_root();
let sh_content = std::fs::read_to_string(root.join("install.sh")).expect("read install.sh");
let ps1_content = std::fs::read_to_string(root.join("install.ps1")).expect("read install.ps1");
let sh_key = extract_public_key(&sh_content);
let ps1_key = extract_public_key(&ps1_content);
assert!(sh_key.is_some(), "install.sh must carry release public key");
assert_eq!(
sh_key, ps1_key,
"install.sh and install.ps1 must use identical release public keys"
);
assert!(
sh_content.contains("santhreal/keyhog"),
"install.sh must target canonical santhreal/keyhog repository"
);
assert!(
ps1_content.contains("santhreal/keyhog"),
"install.ps1 must target canonical santhreal/keyhog repository"
);
}
#[test]
fn install_scripts_share_common_flags() {
let root = repo_root();
let sh_content = std::fs::read_to_string(root.join("install.sh")).expect("read install.sh");
let ps1_content = std::fs::read_to_string(root.join("install.ps1")).expect("read install.ps1");
let sh_flags = parse_sh_flags(&sh_content);
let ps1_flags = parse_ps1_flags(&ps1_content);
assert!(
!sh_flags.is_empty(),
"install.sh must document common flags"
);
assert_eq!(
sh_flags,
ps1_flags,
"install.sh and install.ps1 must document identical common flags\n \
only in install.sh: {:?}\n only in install.ps1: {:?}",
sh_flags.difference(&ps1_flags).collect::<Vec<_>>(),
ps1_flags.difference(&sh_flags).collect::<Vec<_>>(),
);
for flag in &sh_flags {
assert!(
sh_content.contains(&format!("--{flag}")),
"install.sh documents --{flag} but never parses it"
);
let switch: String = flag
.split('-')
.map(|word| {
let mut chars = word.chars();
match chars.next() {
Some(first) => first.to_ascii_uppercase().to_string() + chars.as_str(),
None => String::new(),
}
})
.collect();
assert!(
ps1_content.contains(&format!("${switch},"))
|| ps1_content.contains(&format!("${switch} ")),
"install.ps1 documents -{switch} but never declares it as a parameter"
);
}
}
#[test]
fn skipping_calibration_is_implemented_on_both_platforms() {
let root = repo_root();
let sh_content = std::fs::read_to_string(root.join("install.sh")).expect("read install.sh");
let ps1_content = std::fs::read_to_string(root.join("install.ps1")).expect("read install.ps1");
assert!(
sh_content.contains("--no-calibrate)") && sh_content.contains("SKIP_CALIBRATION=1"),
"install.sh must bind --no-calibrate to SKIP_CALIBRATION"
);
assert!(
ps1_content.contains("if ($NoCalibrate) {"),
"install.ps1 must branch on -NoCalibrate before calibrating"
);
for (name, content, notice, guard, call) in [
(
"install.sh",
&sh_content,
"Skipped autoroute calibration by explicit --no-calibrate.",
"SKIP_CALIBRATION",
"calibrate_and_verify",
),
(
"install.ps1",
&ps1_content,
"Skipped autoroute calibration by explicit -NoCalibrate.",
"$NoCalibrate",
"Invoke-CalibrateAndVerify",
),
] {
let lines: Vec<&str> = content.lines().collect();
let notice_at = lines
.iter()
.position(|line| line.contains(notice))
.unwrap_or_else(|| panic!("{name} must say out loud that it skipped calibration"));
let opener = lines[..notice_at]
.iter()
.rev()
.find(|line| line.contains("if ") || line.contains("if("))
.unwrap_or_else(|| panic!("{name}: the skip notice is not inside any branch"));
assert!(
opener.contains(guard),
"{name}: the skip notice must be inside the {guard} branch, found: {opener}"
);
let arm = lines[notice_at + 1..]
.iter()
.take(6)
.position(|line| {
line.contains("elif") || line.contains("elseif") || line.contains("else")
})
.unwrap_or_else(|| {
panic!("{name}: the skip notice must be one arm of the calibration branch")
});
assert!(
lines[notice_at + 1 + arm..]
.iter()
.take(3)
.any(|line| line.contains(call)),
"{name}: the arm opposite the skip notice must be the {call} the flag replaces"
);
}
}
#[test]
fn install_scripts_publish_execution_packs_before_calibration() {
let root = repo_root();
for (name, compile, calibrate) in [
(
"install.sh",
"publish_execution_packs",
"prime_autoroute_cache",
),
(
"install.ps1",
"Publish-ExecutionPacks",
"Invoke-AutorouteCalibration",
),
] {
let content =
std::fs::read_to_string(root.join(name)).unwrap_or_else(|e| panic!("read {name}: {e}"));
assert!(
content.contains("compile-execution-packs"),
"{name} must invoke `keyhog compile-execution-packs`; without it every scan \
recompiles the detector corpus"
);
assert!(
content.contains("signing.key"),
"{name} must provision the 32-byte execution-pack signing key"
);
let calls: Vec<usize> = content.match_indices(compile).map(|(i, _)| i).collect();
assert!(
calls.len() >= 2,
"{name} must define {compile} and call it from every install mode; found {} \
occurrence(s)",
calls.len()
);
let first_publish = calls[0];
for (index, _) in content.match_indices(calibrate) {
assert!(
first_publish < index,
"{name} calls {calibrate} at byte {index} with no earlier {compile}; \
packs must be published before calibration"
);
}
}
}
#[test]
fn install_scripts_probe_the_same_decode_heavy_bands() {
let root = repo_root();
let sh = std::fs::read_to_string(root.join("install.sh")).expect("read install.sh");
let ps1 = std::fs::read_to_string(root.join("install.ps1")).expect("read install.ps1");
let sh_bands: BTreeSet<u32> = sh
.lines()
.find_map(|line| {
line.trim()
.strip_prefix("decode_heavy_kib_sizes=")
.map(|value| value.trim_matches('"').to_string())
})
.expect("install.sh must declare decode_heavy_kib_sizes")
.split_whitespace()
.map(|band| band.parse().expect("a decode-heavy band is a KiB integer"))
.collect();
let ps1_lines: Vec<&str> = ps1.lines().collect();
let ps1_bands: BTreeSet<u32> = ps1_lines
.iter()
.position(|line| line.contains("New-DecodeHeavyCalibrationProbeKiB -Path"))
.and_then(|probe| {
ps1_lines[..probe]
.iter()
.rev()
.find(|line| line.contains("foreach ($kib in"))
.copied()
})
.and_then(|line| {
let open = line.find("@(")? + 2;
let close = line[open..].find(')')? + open;
Some(line[open..close].to_string())
})
.expect("install.ps1 must sweep decode-heavy bands with a foreach list")
.split(',')
.map(|band| {
band.trim()
.parse()
.expect("a decode-heavy band is a KiB integer")
})
.collect();
assert!(
sh_bands.len() >= 2,
"a decode family needs at least two measured bands to cover an unmeasured one; \
install.sh probes {sh_bands:?}"
);
assert_eq!(
sh_bands, ps1_bands,
"install.sh and install.ps1 must probe the same decode-heavy bands"
);
}
#[test]
fn install_scripts_verify_a_plain_scan_after_calibration() {
struct Wiring {
script: &'static str,
open: &'static str,
findings_exit_is_success: &'static str,
neutral_runner: &'static str,
chain: &'static [&'static str],
runner_call_sites: usize,
}
let root = repo_root();
for wiring in [
Wiring {
script: "install.sh",
open: "verify_autoroute_serves_a_scan() {",
findings_exit_is_success: "\"$check_status\" = \"1\"",
neutral_runner: "run_in_neutral_dir",
chain: &[
"calibrate_and_verify \"$bin\"",
"prime_autoroute_cache \"$bin\"",
"verify_autoroute_serves_a_scan \"$bin\"",
],
runner_call_sites: 2,
},
Wiring {
script: "install.ps1",
open: "function Test-AutorouteServesAScan {",
findings_exit_is_success: "$scanExit -eq 1",
neutral_runner: "Invoke-InNeutralDirectory",
chain: &[
"Invoke-CalibrateAndVerify -BinPath $BinPath",
"Invoke-AutorouteCalibration -BinPath $BinPath",
"Test-AutorouteServesAScan -BinPath $BinPath",
],
runner_call_sites: 2,
},
] {
let name = wiring.script;
let content =
std::fs::read_to_string(root.join(name)).unwrap_or_else(|e| panic!("read {name}: {e}"));
let start = content
.find(wiring.open)
.unwrap_or_else(|| panic!("{name} must define the post-calibration scan check"));
let body = content[start..]
.split_once("\n}\n")
.unwrap_or_else(|| panic!("{name}: post-calibration check has no closing brace"))
.0;
for link in wiring.chain {
assert!(
content.contains(link),
"{name}: the calibration chain must reach {link}"
);
}
let runner_calls = content.matches(wiring.neutral_runner).count();
assert!(
runner_calls >= wiring.runner_call_sites + 1,
"{name}: every calibration entry point must go through {} ({runner_calls} \
occurrence(s), expected the definition plus {} call site(s))",
wiring.neutral_runner,
wiring.runner_call_sites
);
assert!(
body.contains("scan"),
"{name}: the post-calibration check must run a real scan"
);
assert!(
!body.contains("--backend"),
"{name}: the post-calibration check must exercise the auto route, not a pinned backend"
);
assert!(
!body.contains("--autoroute-calibrate"),
"{name}: the post-calibration check must read the primed cache, not extend it"
);
assert!(
body.contains("--no-config"),
"{name}: the check must resolve the baseline configuration calibration measured"
);
assert!(
body.contains(wiring.findings_exit_is_success),
"{name}: exit 1 is findings, not a routing failure, and must pass the check"
);
}
}