use kernel::Address;
use super::ScriptedMachine;
use crate::doctor::explain::{Explanation, explain};
use crate::doctor::needs::{Bits, Capability, lack_line, lacks};
use crate::doctor::paint::Ink;
use crate::doctor::scanning::{Drive, Exclusion, Scanning};
use crate::doctor::screen::{Asked, asked, run};
use crate::doctor::visit::{Visited, visit};
use crate::doctor::{Platform, examine};
fn lab() -> Address {
Address::parse("lab").unwrap()
}
#[test]
fn a_building_that_asks_for_a_browser_is_named_when_this_machine_has_none() {
let findings = examine(&ScriptedMachine::missing(&[
"gecko",
"chromedriver",
"msedgedriver",
"webkit",
]));
let bits = Bits {
browser: true,
shell: false,
interpreter: kernel::Interpreter::System,
};
let lacking = lacks(&lab(), &bits, &findings);
assert_eq!(lacking.len(), 1);
assert_eq!(lacking[0].capability, Capability::Browser);
let line = lack_line(&lacking[0]);
assert!(line.contains("lab"), "{line}");
assert!(line.contains("browser = true"), "{line}");
assert!(line.contains("gecko"), "{line}");
assert!(line.contains("chromedriver"), "{line}");
assert!(line.contains("not on the search path"), "{line}");
let one_road = examine(&ScriptedMachine::missing(&[
"chromedriver",
"msedgedriver",
"webkit",
]));
assert!(
lacks(&lab(), &bits, &one_road).is_empty(),
"a Gecko browser alone is a road, so the building lacks nothing"
);
let nothing_asked = Bits {
browser: false,
shell: false,
interpreter: kernel::Interpreter::System,
};
assert!(lacks(&lab(), ¬hing_asked, &findings).is_empty());
}
#[test]
fn a_building_that_runs_its_shell_under_pwsh_is_named_when_pwsh_is_missing() {
let findings = examine(&ScriptedMachine::missing(&["pwsh"]));
let bits = Bits {
browser: false,
shell: true,
interpreter: kernel::Interpreter::Pwsh,
};
let lacking = lacks(&lab(), &bits, &findings);
assert_eq!(
lacking
.iter()
.map(|lack| lack.capability)
.collect::<Vec<_>>(),
vec![Capability::Pwsh]
);
let line = lack_line(&lacking[0]);
assert!(line.contains("sandbox.interpreter = \"pwsh\""), "{line}");
let system = Bits {
interpreter: kernel::Interpreter::System,
..bits
};
assert!(lacks(&lab(), &system, &findings).is_empty());
}
#[test]
fn every_capability_names_at_least_one_item_the_table_carries() {
let named: std::collections::BTreeSet<&str> = crate::doctor::REQUIREMENTS
.iter()
.map(|item| item.name)
.collect();
for capability in Capability::ALL {
let tried = capability.any_of();
assert!(!tried.is_empty(), "{capability:?} tries nothing");
for item in tried {
assert!(
named.contains(item),
"{capability:?} tries {item}, not a row"
);
}
}
}
#[test]
fn a_building_whose_rules_will_not_read_is_reported_not_skipped() {
let dir = tempfile::tempdir().unwrap();
crate::assembly::init_city(dir.path()).unwrap();
city::create_building(dir.path(), &lab(), city::BuildingTemplate::Minimal).unwrap();
city::write_rules_against(
dir.path(),
&lab(),
&std::fs::read_to_string(city::rules_path(dir.path(), &lab())).unwrap_or_default(),
"confidential = false\nwrite = \"everything\"\nbrowser = true\n",
)
.unwrap();
let mill = Address::parse("mill").unwrap();
city::create_building(dir.path(), &mill, city::BuildingTemplate::Minimal).unwrap();
std::fs::write(
city::rules_path(dir.path(), &mill),
"confidential = true\nwrite = \"everything\"\nbrowser = true\n",
)
.unwrap();
let visited = visit(dir.path()).unwrap();
let lab_bits = visited.iter().find_map(|seen| match seen {
Visited::Bits { building, bits } if *building == lab() => Some(bits.clone()),
_ => None,
});
assert_eq!(
lab_bits,
Some(Bits {
browser: true,
shell: false,
interpreter: kernel::Interpreter::System,
})
);
assert!(
visited.iter().any(|seen| matches!(
seen,
Visited::Unreadable { building, .. } if *building == mill
)),
"a confidential building asking for a browser is rules that will not read"
);
assert!(
visit(&dir.path().join("no-such-city")).is_err(),
"a directory that is not a city is an error, not an empty report"
);
}
#[test]
fn explain_connects_a_refusal_code_to_what_this_machine_has() {
let findings = examine(&ScriptedMachine::missing(&["python-wasi", "shell"]));
let Explanation::Lines(lines) = explain("E_TOOL_UNAVAILABLE", &findings, Platform::current())
else {
panic!("E_TOOL_UNAVAILABLE is about this machine");
};
let python = lines
.iter()
.find(|line| line.contains("python-wasi"))
.expect("the python component is one of the items");
assert!(python.contains("absent"), "{python}");
assert!(
python.contains("->"),
"the line ends in what to do next: {python}"
);
assert!(
python.contains("components/python-wasi") || python.contains("manual"),
"{python}"
);
let engine = lines
.iter()
.find(|line| line.contains("sandbox-engine"))
.expect("the engine is one of the items");
assert!(
engine.ends_with("nothing to do"),
"a present item has no next move: {engine}"
);
let Explanation::Lines(browser) =
explain("E_BROWSER_UNAVAILABLE", &findings, Platform::current())
else {
panic!("E_BROWSER_UNAVAILABLE is about this machine");
};
assert!(browser.iter().any(|line| line.contains("gecko")));
assert!(matches!(
explain("E_GATE_DENIED", &findings, Platform::current()),
Explanation::NotAboutThisMachine(kernel::AxCode::GateDenied)
));
assert!(matches!(
explain("E_BOGUS", &findings, Platform::current()),
Explanation::NoSuchCode(_)
));
}
#[test]
fn the_line_is_read_as_flag_city_and_code() {
let words =
|raw: &[&str]| -> Vec<String> { raw.iter().map(|word| (*word).to_owned()).collect() };
let plain = asked(&words(&["doctor"]), None, std::env::temp_dir);
assert!(!plain.install && plain.city.is_none() && plain.explain.is_none());
assert_eq!(
plain.scanned,
std::env::temp_dir(),
"with no city named, the scanning part judges where `up` would put one"
);
assert!(asked(&words(&["doctor", "--install"]), None, std::env::temp_dir).install);
assert!(
!asked(&words(&["doctor", "--installed"]), None, std::env::temp_dir).install,
"a flag that only looks like --install is not --install"
);
let with_city = asked(
&words(&["doctor", "C:/cities/one", "--install"]),
None,
std::env::temp_dir,
);
assert_eq!(
with_city.city.as_deref(),
Some(std::path::Path::new("C:/cities/one"))
);
assert_eq!(with_city.scanned, std::path::Path::new("C:/cities/one"));
assert!(with_city.install);
let explained = asked(
&words(&["doctor", "--explain", "E_TOOL_UNAVAILABLE"]),
None,
std::env::temp_dir,
);
assert_eq!(explained.explain.as_deref(), Some("E_TOOL_UNAVAILABLE"));
assert!(
explained.city.is_none(),
"the code after --explain is not a city"
);
}
#[test]
fn doctor_with_a_city_names_the_building_on_the_screen() {
let dir = tempfile::tempdir().unwrap();
crate::assembly::init_city(dir.path()).unwrap();
city::create_building(dir.path(), &lab(), city::BuildingTemplate::Minimal).unwrap();
city::write_rules_against(
dir.path(),
&lab(),
&std::fs::read_to_string(city::rules_path(dir.path(), &lab())).unwrap_or_default(),
"confidential = false\nwrite = \"everything\"\nbrowser = true\n",
)
.unwrap();
let machine = ScriptedMachine::missing(&["gecko", "chromedriver", "msedgedriver", "webkit"]);
let mut nobody = std::io::Cursor::new(Vec::new());
let mut screen: Vec<u8> = Vec::new();
let asked = Asked {
install: false,
city: Some(dir.path().to_path_buf()),
scanned: std::env::temp_dir(),
explain: None,
ink: Ink::Plain,
};
let ready = run(&asked, &machine, &mut nobody, &mut screen).unwrap();
assert!(!ready);
let shown = String::from_utf8(screen).unwrap();
assert!(shown.contains("lab: browser = true"), "{shown}");
assert!(shown.contains("no gecko"), "{shown}");
let machine = ScriptedMachine::missing(&[]);
let mut screen: Vec<u8> = Vec::new();
let ready = run(&asked, &machine, &mut nobody, &mut screen).unwrap();
assert!(ready, "{}", String::from_utf8(screen).unwrap());
}
#[test]
fn explain_on_the_screen_is_one_line_per_item_and_never_a_failure() {
let machine = ScriptedMachine::missing(&["python-wasi"]);
let mut nobody = std::io::Cursor::new(Vec::new());
let mut screen: Vec<u8> = Vec::new();
let asked = Asked {
install: false,
city: None,
scanned: std::env::temp_dir(),
explain: Some("E_TOOL_UNAVAILABLE".to_owned()),
ink: Ink::Plain,
};
let ready = run(&asked, &machine, &mut nobody, &mut screen).unwrap();
assert!(ready);
let shown = String::from_utf8(screen).unwrap();
assert!(shown.contains("python-wasi"), "{shown}");
assert!(shown.contains("E_TOOL_UNAVAILABLE"), "{shown}");
}
#[test]
fn the_page_carries_the_scanning_reading_the_doctor_took() {
let city = std::env::temp_dir().join("one");
let machine = ScriptedMachine::missing(&[]).scanning(Scanning::Read {
city: city.clone(),
drive: Drive::Untrusted {
volume: "D:".to_owned(),
},
exclusion: Exclusion::Outside,
});
assert_eq!(
accounting::Machine::report(&machine, &city).scanning,
wire::DoctorScanning::Read {
city: city.display().to_string(),
drive: wire::DoctorDrive::Untrusted {
volume: "D:".to_owned()
},
exclusion: wire::DoctorExclusion::Outside,
}
);
}
#[test]
fn only_a_missing_required_use_item_makes_the_doctor_not_ready() {
let asked = Asked {
install: false,
city: None,
scanned: std::env::temp_dir(),
explain: None,
ink: Ink::Plain,
};
let answer = |absent: &[&'static str]| {
let mut screen: Vec<u8> = Vec::new();
let machine = ScriptedMachine::missing(absent);
run(
&asked,
&machine,
&mut std::io::Cursor::new(Vec::new()),
&mut screen,
)
.unwrap()
};
assert_eq!(
[
answer(&["zig", "lean", "just"]),
answer(&["gecko", "chromedriver", "msedgedriver", "webkit"]),
],
[true, false]
);
}