//! §2.7 and §2.1: each code, and the order of the checks, pinned by pairs of faults. use wai_vector::refuse::Code; use wai_vector::vectors::{all_vectors, corpus_store, find}; use wai_vector::{wos1, wvg1}; fn code_of(group: &str, name: &str) -> (Code, &'static str) { let v = find(group, name).unwrap_or_else(|| panic!("no vector {group}/{name}")); let r = match v.ext { "wos1" => wos1::parse(&v.bytes).err(), _ => wvg1::load(&v.bytes, &mut corpus_store()).err(), }; let r = r.unwrap_or_else(|| panic!("{group}/{name} was accepted")); (r.code, r.step) } fn accepted(group: &str, name: &str) -> bool { let v = find(group, name).unwrap(); match v.ext { "wos1" => wos1::parse(&v.bytes).is_ok(), _ => wvg1::load(&v.bytes, &mut corpus_store()).is_ok(), } } #[test] fn wos1_load_check_order() { let g = "refusals"; for (name, code) in [ ("wos1-short", Code::OutlineMalformed), ("wos1-magic", Code::OutlineMalformed), ("wos1-flags", Code::OutlineMalformed), ("wos1-n-glyphs-cap", Code::TooLarge), ("wos1-notes-zero", Code::OutlineMalformed), ("wos1-length", Code::OutlineMalformed), ("wos1-points-limit", Code::PointsLimit), ("wos1-gid-order", Code::OutlineMalformed), ("wos1-sums", Code::OutlineMalformed), ("wos1-ends-order", Code::OutlineMalformed), ("wos1-tag", Code::OutlineMalformed), ("wos1-grammar", Code::OutlineMalformed), ("wos1-grammar-one-point-control", Code::OutlineMalformed), ("wos1-box", Code::OutlineMalformed), ("wos1-notes-utf8", Code::OutlineMalformed), ("wos1-points-16384", Code::PointsLimit), ] { assert_eq!(code_of(g, name).0, code, "{name}"); } // The pairs: each fault alone has another code, so the first check wins. assert_eq!(code_of(g, "wos1-points-limit-before-gid-order"), (Code::PointsLimit, "wos1.4"), "check 4 before check 5"); assert_eq!(code_of(g, "wos1-zero-before-points-limit"), (Code::OutlineMalformed, "wos1.4"), "records in order"); assert_eq!(code_of(g, "wos1-points-limit-before-zero"), (Code::PointsLimit, "wos1.4"), "n_points first within a record"); assert_eq!(code_of(g, "wos1-cap-before-length"), (Code::TooLarge, "wos1.2"), "check 2 before check 3"); assert_eq!(code_of(g, "wos1-zero-glyphs-before-points-cap"), (Code::OutlineMalformed, "wos1.2"), "field order in check 2"); assert!(accepted(g, "wos1-set-a")); assert!(accepted(g, "wos1-points-16383")); } #[test] fn record_pass_reports_field_range_before_any_cross_table_check() { // ITEM.kind = 4 with ref = 0, beside an inline set marked held (a step 5(a) sum). assert_eq!(code_of("refusals", "s4-item-kind-four"), (Code::FieldRange, "4")); // KEY.interp = 2 + j with j >= n_tables, beside the same sum fault. assert_eq!(code_of("refusals", "s4-key-interp-before-sum"), (Code::FieldRange, "4")); // Each fault alone. assert_eq!(code_of("refusals", "s4-key-interp"), (Code::FieldRange, "4")); assert_eq!(code_of("refusals", "s5a-inline-sum"), (Code::PayloadMalformed, "5a")); } #[test] fn the_four_new_sum_rules() { for name in ["s5a-path-contours-sum", "s5a-path-points-sum", "s5a-strings-sum", "s5a-glyphs-sum", "s5a-clusters-sum"] { assert_eq!(code_of("refusals", name), (Code::PayloadMalformed, "5a"), "{name}"); } } #[test] fn exact_length_and_trailing_bytes() { let min = find("canonical", "minimal").unwrap().bytes; assert!(wvg1::load(&min, &mut corpus_store()).is_ok()); let mut long = min.clone(); long.push(0); let e = wvg1::parse(&long).unwrap_err(); assert_eq!((e.code, e.step), (Code::PayloadMalformed, "3"), "one trailing byte"); let e = wvg1::parse(&min[..min.len() - 1]).unwrap_err(); assert_eq!((e.code, e.step), (Code::PayloadMalformed, "3"), "one byte short"); let e = wvg1::parse(&min[..79]).unwrap_err(); assert_eq!((e.code, e.step), (Code::PayloadMalformed, "1"), "a short header"); } #[test] fn steps_in_order() { let g = "refusals"; for (name, code, step) in [ ("s1-magic-before-cap", Code::PayloadMalformed, "1"), ("s2-cap-before-range", Code::TooLarge, "2"), ("s2-cap-before-length", Code::TooLarge, "2"), ("s4-header-before-record", Code::FieldRange, "4"), ("s4-set-carriage-before-zero", Code::FieldRange, "4"), ("s4-set-zero-before-length", Code::PayloadMalformed, "4"), ("s4-item-zero2-before-ref", Code::PayloadMalformed, "4"), ("s4-item-rule-before-zero2", Code::FieldRange, "4"), ("s4-path-points-limit-before-zero", Code::PointsLimit, "4"), ("s5a-sum-before-typing", Code::PayloadMalformed, "5a"), ("s5c-typing-before-groups", Code::FieldRange, "5c"), ("s6-order-held-first", Code::OutlineNotHeld, "6"), ("s6-order-inline-first", Code::OutlineMalformed, "6"), ("s7-before-geometry", Code::GlyphMissing, "7"), ] { assert_eq!(code_of(g, name), (code, step), "{name}"); } } /// Every vector's code is the one its note names. #[test] fn every_vector_has_the_code_its_note_names() { let mut store = corpus_store(); let mut n = 0; for v in all_vectors() { let got = match v.ext { "wos1" => wos1::parse(&v.bytes).err().map(|e| e.code.name()), "wvg1" => wvg1::load(&v.bytes, &mut store).err().map(|e| e.code.name()), _ => continue, }; let want = match v.note.rsplit_once("-> ") { Some((_, rest)) => Some(rest.split(|c: char| !(c.is_ascii_lowercase() || c == '_')).next().unwrap()), None => { assert!(v.note.ends_with("accepted"), "{}: the note names no outcome", v.name); None } }; assert_eq!(got, want, "{}/{}: {}", v.group, v.name, v.note); n += 1; } assert!(n > 200, "{n} vectors"); }