pub mod dfa;
pub mod eval;
pub mod scatter;
use crate::spec::EngineSpec;
#[inline]
pub fn specs() -> Vec<EngineSpec> {
vec![dfa::spec(), eval::spec(), scatter::spec()]
}
#[cfg(test)]
mod invariant_tests {
use super::specs;
use crate::spec::EngineInvariant;
#[test]
fn all_cpu_references_are_deterministic() {
for spec in specs() {
if !spec.invariants.contains(&EngineInvariant::Deterministic) {
continue;
}
let Some(cpu) = spec.cpu_fn else {
panic!("{} declares Deterministic but has no cpu_fn", spec.id);
};
let input = hand_crafted_input_for(spec.id);
let out1 = cpu(&input);
let out2 = cpu(&input);
assert_eq!(
out1, out2,
"{} violates Deterministic: same input produced different outputs",
spec.id
);
}
}
#[test]
fn dfa_output_is_ordered() {
let spec = specs().into_iter().find(|s| s.id == "engine.dfa").unwrap();
let cpu = spec.cpu_fn.unwrap();
let input = hand_crafted_input_for("engine.dfa");
let out = cpu(&input);
assert_eq!(out.len() % 12, 0, "DFA output not 12-byte aligned");
let starts: Vec<u32> = out
.chunks_exact(12)
.map(|c| u32::from_le_bytes([c[4], c[5], c[6], c[7]]))
.collect();
assert!(
starts.windows(2).all(|w| w[0] <= w[1]),
"DFA output starts are not ordered: {:?}",
starts
);
}
#[test]
fn dfa_no_output_lost() {
let spec = specs().into_iter().find(|s| s.id == "engine.dfa").unwrap();
let cpu = spec.cpu_fn.unwrap();
let input = hand_crafted_input_for("engine.dfa");
let out = cpu(&input);
assert_eq!(
out.len(),
24,
"DFA lost output: expected 2 matches (24 bytes)"
);
}
#[test]
fn scatter_no_output_lost() {
let spec = specs()
.into_iter()
.find(|s| s.id == "engine.scatter")
.unwrap();
let cpu = spec.cpu_fn.unwrap();
let input = hand_crafted_input_for("engine.scatter");
let out = cpu(&input);
let word = u32::from_le_bytes([out[0], out[1], out[2], out[3]]);
assert!(
word & (1 << 3) != 0,
"Scatter lost output: expected bit 3 to be set in rule 0"
);
}
#[test]
fn scatter_is_idempotent() {
let spec = specs()
.into_iter()
.find(|s| s.id == "engine.scatter")
.unwrap();
let cpu = spec.cpu_fn.unwrap();
let input = hand_crafted_input_for("engine.scatter");
let out1 = cpu(&input);
let out2 = cpu(&input);
assert_eq!(
out1, out2,
"Scatter violates NoDuplicateOutput: output changed on identical input"
);
}
#[test]
fn eval_bounded_resources() {
let spec = specs().into_iter().find(|s| s.id == "engine.eval").unwrap();
let cpu = spec.cpu_fn.unwrap();
let input = hand_crafted_input_for("engine.eval");
let out = cpu(&input);
assert_eq!(out.len(), 1, "Eval output length unexpected");
}
fn hand_crafted_input_for(id: &str) -> Vec<u8> {
match id {
"engine.dfa" => super::dfa::tests::build_dfa_input_for_invariants(),
"engine.eval" => super::eval::tests::build_eval_input_for_invariants(),
"engine.scatter" => super::scatter::tests::build_scatter_input_for_invariants(),
_ => panic!("unknown engine id: {id}"),
}
}
}