open-standards/wai/wai-rs/tests/synthesis_mark.rs
Transaction Science af2e81f2bf tests/synthesis_mark.rs: a claim of §7.9.7 needs every column of its row survived
The test checked §7.9.7's claims against the generated sets' tallies alone, so a claim
the photographs' column refutes passed it (screen-b, falsified); it now reads the row's
three columns, as the second implementation does.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-21 20:27:58 -04:00

220 lines
12 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! The registered watermark scheme of extensions/synthesis.md §7.5 and its corpus
//! `live-conformance/synthesis-mark/`: every file regenerates byte for byte from its fixed seeds
//! (the measurement among them) and no other file is there; every embedding, detection and case
//! gets what it states; the extension's definition, generator polynomial and shuffle are the
//! reference's; and §7.5.8 claims only attacks the measurement shows survived, with §7.5.6's
//! figures the measurement's. Run it in release: the measurement embeds and attacks every
//! picture of the measurement sets.
#![cfg(feature = "synthesis_mark")]
use serde_json::Value;
use std::collections::BTreeMap;
use std::path::PathBuf;
use wai::synthesis::mark;
use wai::synthesis_mark::corpus;
fn wai_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("..")
}
fn committed() -> BTreeMap<String, Vec<u8>> {
let root = wai_dir().join("live-conformance/synthesis-mark");
let mut out = BTreeMap::new();
let mut stack = vec![root.clone()];
while let Some(d) = stack.pop() {
for e in std::fs::read_dir(&d).expect("the committed corpus").flatten() {
let p = e.path();
if p.is_dir() {
stack.push(p);
continue;
}
let rel = p.strip_prefix(&root).expect("under the corpus").to_string_lossy().replace('\\', "/");
if rel != "README.md" {
out.insert(rel, std::fs::read(&p).expect("a file"));
}
}
}
out
}
fn extension() -> String {
std::fs::read_to_string(wai_dir().join("extensions/synthesis.md")).expect("the extension")
}
/// The text of the section that starts at `head`, up to the next heading of its level or above.
fn section(text: &str, head: &str) -> String {
let at = text.find(&format!("\n{head}")).unwrap_or_else(|| panic!("a section {head}"));
let level = head.chars().take_while(|c| *c == '#').count();
let end = text[at + 1..]
.match_indices("\n#")
.find(|(i, _)| text[at + 1 + i + 1..].chars().take_while(|c| *c == '#').count() <= level)
.map_or(text.len(), |(i, _)| at + 1 + i);
text[at..end].to_string()
}
fn fold(s: &str) -> String {
s.split_whitespace().collect::<Vec<_>>().join(" ")
}
#[test]
fn the_committed_corpus_regenerates_from_its_seeds() {
let files = committed();
let generated: BTreeMap<String, Vec<u8>> = corpus::generate().into_iter().collect();
let extra: Vec<&String> = files.keys().filter(|k| !generated.contains_key(*k)).collect();
let differ: Vec<&String> = generated.keys().filter(|k| files.get(*k) != generated.get(*k)).collect();
assert!(extra.is_empty() && differ.is_empty(), "extra {extra:?}, differ or missing {differ:?}: run `wai_synthesis_mark_vectors gen`");
}
#[test]
fn every_vector_and_case_gets_what_it_states() {
let fails = corpus::verify(&committed());
assert!(fails.is_empty(), "{}", fails.join("\n"));
}
/// §7.4's definition is the reference's, with its digest; §7.5.3's generator polynomial and
/// the shuffle's first entries are the reference's.
#[test]
fn the_extension_pins_the_scheme() {
let text = extension();
let s74 = section(&text, "### 7.4 ");
let block = s74.split("```").nth(1).expect("§7.4's definition");
let def: String = block.lines().map(str::trim).collect();
assert_eq!(def, mark::definition_text(), "§7.4's definition");
let digest: String = mark::digest().iter().map(|b| format!("{b:02x}")).collect();
assert!(fold(&s74).contains(&format!("its digest is `{digest}`")), "§7.4's digest");
let s753 = fold(&section(&text, "#### 7.5.3 "));
assert!(s753.contains(&format!("in hexadecimal, are `{}`", mark::GENERATOR_HEX)), "§7.5.3's generator");
let p = &mark::layout().perm;
assert!(s753.contains(&format!("π begins {}, {}, {}, {},", p[0], p[1], p[2], p[3])), "§7.5.3's shuffle");
}
/// §7.5.8 claims only attacks every set survived with no unmarked reading; §7.5.6's figures are
/// battery.json's.
#[test]
fn the_extension_claims_only_what_the_measurement_shows() {
let text = extension();
let battery: Value = serde_json::from_slice(&committed()["battery.json"]).expect("battery.json");
let s758 = fold(&section(&text, "#### 7.5.8 "));
let at = s758.find("For pictures, these attacks, each with the parameter measured: ").expect("§7.5.8's claims");
let rest = &s758[at..];
let clips_at = rest.find(" For clips: ").expect("the clips' claims");
let names = |s: &str| -> Vec<String> { s.split('`').skip(1).step_by(2).map(String::from).collect() };
let pictures = names(&rest[..clips_at]);
let clips = names(&rest[clips_at..rest[clips_at..].find(". ").map_or(rest.len(), |e| clips_at + e)]);
let has = |kind: &str, a: &String| battery["claims"][kind].as_array().expect("claims").iter().any(|x| x == a);
assert!(!pictures.is_empty() && pictures.iter().all(|a| has("pictures", a)), "§7.5.8 claims {pictures:?}");
assert!(!clips.is_empty() && clips.iter().all(|a| has("clips", a)), "§7.5.8 claims {clips:?}");
let w = &battery["words"];
let s756 = fold(&section(&text, "#### 7.5.6 "));
let fp = battery["false_positive_log2"].as_str().expect("a bound").trim_start_matches('-').to_string();
assert!(s756.contains(&format!("2^−{fp}, about")), "§7.5.6's bound");
let pairs = w["pairs"].as_u64().expect("pairs");
let words = format!(
"have a mean of {} ones with a standard deviation of {}, and the {},{:03} pairs of those words from different pictures differ in a mean of {} bits with a standard deviation of {} and in no fewer than {};",
w["ones_mean"].as_str().unwrap(),
w["ones_sd"].as_str().unwrap(),
pairs / 1000,
pairs % 1000,
w["distance_mean"].as_str().unwrap(),
w["distance_sd"].as_str().unwrap(),
w["distance_min"]
);
assert!(s756.contains(&words), "§7.5.6's figures: {words}");
}
#[test]
fn the_corpus_holds_only_what_its_vectors_name() {
let files = committed();
let doc: Value = serde_json::from_slice(&files["vectors.json"]).expect("vectors.json");
let text = String::from_utf8_lossy(&files["vectors.json"]).to_string() + &String::from_utf8_lossy(&files["vectors-tent64s.json"]);
for k in files.keys().filter(|k| k.starts_with("frames/")) {
let h = &k["frames/".len()..k.len() - ".rgb".len()];
assert!(text.contains(h), "{k} is named by no vector");
assert_eq!(&mark::sha256(&files[k]).iter().map(|b| format!("{b:02x}")).collect::<String>(), h, "{k} is not named by its SHA-256");
}
assert!(doc["cases"].as_array().is_some_and(|c| !c.is_empty()));
}
/// §7.9.1's definition is the reference's, with its digest; §7.9.2's shuffle begins as the
/// reference's; §7.9.5's bound is the corpus's.
#[test]
fn the_extension_pins_the_tiled_scheme() {
use wai::synthesis::mark::sync;
let text = extension();
let s = section(&text, "#### 7.9.1 ");
let block = s.split("```").nth(1).expect("§7.9.1's definition");
let def: String = block.lines().map(str::trim).collect();
assert_eq!(def, sync::definition_text(), "§7.9.1's definition");
let digest: String = sync::digest().iter().map(|b| format!("{b:02x}")).collect();
assert!(fold(&s).contains(&format!("its digest is `{digest}`")), "§7.9.1's digest");
let p = &sync::tile_layout().perm;
assert!(fold(&section(&text, "#### 7.9.2 ")).contains(&format!("π begins {}, {}, {}, {},", p[0], p[1], p[2], p[3])), "§7.9.2's shuffle");
let files = committed();
let doc: Value = serde_json::from_slice(&files["vectors-tent64s.json"]).expect("vectors-tent64s.json");
let fp = doc["scheme"]["false_positive_log2"].as_str().expect("a bound").trim_start_matches('-').to_string();
assert!(fold(&section(&text, "#### 7.9.5 ")).contains(&format!("2^−{fp}, about")), "§7.9.5's bound");
}
/// §7.9.7 claims only attacks every generated set survived with no unmarked reading, and its
/// table's generated columns are battery-tent64s.json's; §7.9.5's figures are its words'.
#[test]
fn the_extension_claims_only_what_the_tiled_measurement_shows() {
let text = extension();
let battery: Value = serde_json::from_slice(&committed()["battery-tent64s.json"]).expect("battery-tent64s.json");
let s = section(&text, "#### 7.9.7 ");
let mut rows: BTreeMap<(String, String), String> = BTreeMap::new();
for r in battery["table"].as_array().expect("a table") {
let (d, t, e) = (r["detected"].as_u64().unwrap(), r["trials"].as_u64().unwrap(), r["errors_max"].as_u64().unwrap());
let cell = if d > 0 { format!("{d} / {t} ({e})") } else { format!("{d} / {t}") };
rows.insert((r["attack"].as_str().unwrap().to_string(), r["set"].as_str().unwrap().to_string()), cell);
}
let mut seen = 0;
// The picture rows' three columns, for the claims below.
let mut columns: BTreeMap<String, Vec<String>> = BTreeMap::new();
for line in s.lines() {
let cells: Vec<&str> = line.trim().trim_matches('|').split('|').map(str::trim).collect();
if !cells[0].starts_with('`') || cells.len() < 2 {
continue;
}
let a = cells[0].trim_matches('`').to_string();
if cells.len() == 4 {
columns.insert(a.clone(), cells[1..].iter().map(|c| c.to_string()).collect());
assert_eq!(cells[1], rows[&(a.clone(), "spectral".into())], "§7.9.7 {a} spectral");
assert_eq!(cells[2], rows[&(a.clone(), "leaves".into())], "§7.9.7 {a} leaves");
seen += 1;
} else if cells.len() == 2 {
assert_eq!(cells[1], rows[&(a.clone(), "clips".into())], "§7.9.7 {a} clips");
seen += 1;
}
}
assert_eq!(seen, battery["image_attacks"].as_array().unwrap().len() + battery["clip_attacks"].as_array().unwrap().len(), "§7.9.7's rows");
let f = fold(&s);
let at = f.find("For pictures: ").expect("§7.9.7's claims");
let rest = &f[at..];
let clips_at = rest.find(" For clips: ").expect("the clips' claims");
let names = |x: &str| -> Vec<String> { x.split('`').skip(1).step_by(2).map(String::from).collect() };
let pictures = names(&rest[..clips_at]);
let clips = names(&rest[clips_at..rest[clips_at..].find(". ").map_or(rest.len(), |e| clips_at + e)]);
let has = |kind: &str, a: &String| battery["claims"][kind].as_array().expect("claims").iter().any(|x| x == a);
assert!(!pictures.is_empty() && pictures.iter().all(|a| has("pictures", a)), "§7.9.7 claims {pictures:?}");
assert!(!clips.is_empty() && clips.iter().all(|a| has("clips", a)), "§7.9.7 claims {clips:?}");
// A claim is an attack every column survived, the photographs' among them: "n / n (e)".
let whole = |c: &String| c.split_once(" / ").is_some_and(|(d, rest)| rest.split_once(" (").is_some_and(|(t, _)| d == t));
for a in &pictures {
assert!(columns.get(a).is_some_and(|cs| cs.len() == 3 && cs.iter().all(whole)), "§7.9.7 claims {a}, which a column of its table does not show survived");
}
let w = &battery["words"];
let pairs = w["pairs"].as_u64().expect("pairs");
let words = format!(
"{} words have a mean of {} ones with a standard deviation of {}, and the {},{:03} pairs of those words from different pictures differ in a mean of {} bits with a standard deviation of {} and in no fewer than {};",
w["words"],
w["ones_mean"].as_str().unwrap(),
w["ones_sd"].as_str().unwrap(),
pairs / 1000,
pairs % 1000,
w["distance_mean"].as_str().unwrap(),
w["distance_sd"].as_str().unwrap(),
w["distance_min"]
);
assert!(fold(&section(&text, "#### 7.9.5 ")).contains(&words), "§7.9.5's figures: {words}");
}