use std::path::PathBuf;
use bathy_interpret::{all_rules, interpret, known_probe_ids};
use bathy_types::{ProbeCapture, Transport};
fn slack(rule_id: &str) -> usize {
match rule_id {
"mysql.handshake.v10.v1" => 1,
_ => 0,
}
}
fn corpus_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../fuzz/seeds/interpret")
}
fn edge_input(rule_id: &str) -> PathBuf {
corpus_dir().join(format!("span-edge-{rule_id}.bin"))
}
#[test]
fn every_rule_has_one() {
let missing: Vec<&str> = all_rules()
.filter(|rule| !edge_input(rule.id).is_file())
.map(|rule| rule.id)
.collect();
assert!(
missing.is_empty(),
"no span-edge input for {missing:?}. Each rule needs one input whose match ends \
at the last byte it can (see this file's header for why a 60-second fuzz run \
does not find these on its own). Write it to {}, and if the rule's grammar \
forces trailing bytes, add it to `slack` with the line that proves it.",
corpus_dir().display()
);
}
#[test]
fn every_edge_input_still_ends_where_the_response_does() {
for rule in all_rules() {
let path = edge_input(rule.id);
let Ok(response) = std::fs::read(&path) else {
continue; };
let slack = slack(rule.id);
let expected_end = response
.len()
.checked_sub(slack)
.expect("an edge input longer than its rule's required trailing bytes");
let mut seen = None;
for probe_id in known_probe_ids() {
let capture = ProbeCapture {
probe_id,
transport: Transport::Tcp,
port: 0,
request: None,
response: response.clone(),
elapsed_micros: 0,
truncated: false,
};
for i in interpret(&capture) {
if i.rule_id == rule.id {
seen = Some(i.matched_span.clone());
}
}
}
let span = seen.unwrap_or_else(|| {
panic!(
"{} no longer produces a `{}` match at all, so nothing about its span is \
being checked -- which reads as coverage while guarding nothing",
path.display(),
rule.id
)
});
assert_eq!(
span.end,
expected_end,
"rule {} matched {:?} in a {}-byte input whose match must end at byte {} \
(slack {}). A span one byte too long is the exact shape of the seven \
historical span mutants, and it is invisible to a fuzz run of this length \
on any input that is not this one.",
rule.id,
span,
response.len(),
expected_end,
slack,
);
assert_eq!(
response[span.clone()].len(),
span.end - span.start,
"slicing the response with rule {}'s span did not yield the span's own \
length -- which is what a consumer does with it",
rule.id
);
}
}
#[test]
fn no_edge_input_belongs_to_a_rule_that_no_longer_exists() {
let ids: Vec<&str> = all_rules().map(|rule| rule.id).collect();
let orphans: Vec<String> = std::fs::read_dir(corpus_dir())
.expect("the seed corpus directory")
.filter_map(Result::ok)
.filter_map(|entry| {
let name = entry.file_name().to_string_lossy().into_owned();
let rule = name.strip_prefix("span-edge-")?.strip_suffix(".bin")?;
(!ids.contains(&rule)).then_some(name)
})
.collect();
assert!(
orphans.is_empty(),
"span-edge inputs for no known rule: {orphans:?}"
);
}