use lini::testing::{declared_edges, drawn_edges, laws, route_sample, routes_str};
use lini::{Rule, Severity};
use std::path::PathBuf;
const CLEARANCES: [f64; 7] = [6.0, 8.0, 9.0, 10.0, 12.0, 13.0, 16.0];
fn sample_paths() -> Vec<PathBuf> {
let mut paths: Vec<PathBuf> = std::fs::read_dir("samples")
.expect("read samples/")
.filter_map(|e| e.ok().map(|e| e.path()))
.filter(|p| p.extension().is_some_and(|x| x == "lini"))
.filter(|p| {
cfg!(feature = "icons")
|| !std::fs::read_to_string(p)
.unwrap_or_default()
.contains("|icon|")
})
.collect();
paths.sort();
paths
}
fn read(path: &std::path::Path) -> String {
std::fs::read_to_string(path).unwrap_or_else(|e| panic!("read {}: {e}", path.display()))
}
fn breaches(report: Vec<lini::Violation>) -> Vec<lini::Violation> {
report
.into_iter()
.filter(|v| v.severity != Severity::Info && v.rule != Rule::Impossible)
.collect()
}
#[test]
fn every_sample_satisfies_the_laws() {
for path in sample_paths() {
let src = read(&path);
let found = breaches(
lini::validate_str(&src).unwrap_or_else(|e| panic!("validate {}: {e}", path.display())),
);
assert!(
found.is_empty(),
"{}: the four laws must hold, got {found:?}",
path.display()
);
}
}
#[test]
fn impossible_links_are_exactly_the_known_capacity_truths() {
for path in sample_paths() {
let src = read(&path);
let impossible = lini::validate_str(&src)
.unwrap_or_else(|e| panic!("validate {}: {e}", path.display()))
.into_iter()
.filter(|v| v.rule == Rule::Impossible)
.count();
assert_eq!(impossible, 0, "{}: stray count moved", path.display());
}
}
#[test]
fn every_sample_compiles_and_routes_byte_identically() {
for path in sample_paths() {
let src = read(&path);
let svg =
lini::compile_str(&src).unwrap_or_else(|e| panic!("compile {}: {e}", path.display()));
let routes = routes_str(&src).expect("routes");
for _ in 0..2 {
assert_eq!(
lini::compile_str(&src).expect("recompile"),
svg,
"{}: compile is not deterministic",
path.display()
);
assert_eq!(
routes_str(&src).expect("reroute"),
routes,
"{}: routing is not deterministic",
path.display()
);
}
}
}
#[test]
fn every_sample_holds_the_laws_at_every_clearance() {
for path in sample_paths() {
let src = read(&path);
let declared = declared_edges(&src);
for c in CLEARANCES {
let laid = route_sample(&src, c);
let report = laws(&laid);
let impossible = report.iter().filter(|v| v.rule == Rule::Impossible).count();
let found = breaches(report);
assert!(
found.is_empty(),
"{} at clearance {c}: {found:?}",
path.display()
);
assert_eq!(
drawn_edges(&laid) + impossible,
declared,
"{} at clearance {c}: every edge must be drawn or reported",
path.display()
);
}
}
}
#[test]
fn routing_pcb_ten_times_stays_fast() {
let src = read(std::path::Path::new("samples/pcb.lini"));
let start = std::time::Instant::now();
for _ in 0..10 {
lini::compile_str(&src).expect("compile pcb");
}
let took = start.elapsed();
assert!(
took.as_secs_f64() < 30.0,
"10 debug compiles took {took:?}, budget 30 s"
);
}