use abstracttui::gfx::{jpeg, png};
use abstracttui::input::{Event, Parser};
use abstracttui::render::md::{self, MdStyles};
use abstracttui::testing::jpeg_build::FlatJpeg;
use abstracttui::testing::{glb_mutants, hostile_corpus, random_splits, GlbExpect, Rng};
use abstracttui::text::{CLikeLexer, Highlighter};
use abstracttui::three::Model;
#[test]
#[ignore = "big fuzz"]
fn parser_20k_hostile_chunks_and_split_invariance() {
let corpus = hostile_corpus(0x0B16_FADE, 20_000);
for chunk in &corpus {
let mut p = Parser::new();
let mut out = Vec::new();
p.feed(chunk, &mut out); }
let mut rng = Rng::new(0x0051_9117);
let mut mismatches = 0usize;
for (i, chunk) in corpus.iter().take(2_000).enumerate() {
let mut whole = Parser::new();
let mut a = Vec::new();
whole.feed(chunk, &mut a);
let mut piece = Parser::new();
let mut b = Vec::new();
for slice in random_splits(&mut rng, chunk, 5) {
piece.feed(&slice, &mut b);
}
if events_eq(&a, &b) {
continue;
}
mismatches += 1;
eprintln!(
"split-invariance mismatch at case {i}: {} vs {} events",
a.len(),
b.len()
);
}
assert_eq!(
mismatches, 0,
"parser split-invariance broke on {mismatches} cases"
);
eprintln!("parser: 20000 hostile chunks, 0 panics; 2000 split-invariance cases clean");
}
fn events_eq(a: &[Event], b: &[Event]) -> bool {
a == b
}
#[test]
#[ignore = "big fuzz"]
fn glb_5k_mutants_through_full_loader() {
let corpus = glb_mutants(0x06B1_0ADE, 5_000);
let total = corpus.len();
let (mut loaded, mut rejected, mut surprises) = (0usize, 0usize, 0usize);
for m in corpus {
match Model::load(&m.bytes) {
Ok(_) => {
loaded += 1;
if matches!(m.expect, GlbExpect::MustReject) {
surprises += 1;
}
}
Err(_) => rejected += 1,
}
}
eprintln!(
"glb: {loaded} loaded / {rejected} rejected / {total} total, 0 panics ({surprises} MustReject loaded — ratchet in adv_gfx)"
);
assert_eq!(loaded + rejected, total);
}
#[test]
#[ignore = "big fuzz"]
fn png_5k_hostile_vectors() {
let mut rng = Rng::new(0x00F1_6DEA ^ 0x1);
let mut ok = 0usize;
for _ in 0..5_000 {
let mut bytes = vec![0x89, b'P', b'N', b'G', 0x0D, 0x0A, 0x1A, 0x0A];
let n = rng.below(300);
for _ in 0..n {
bytes.push(rng.byte());
}
if let Ok(img) = png::decode(&bytes) {
ok += 1;
assert!(
img.width() <= 1 << 16 && img.height() <= 1 << 16,
"absurd PNG dims"
);
}
}
eprintln!("png: 5000 hostile vectors, {ok} decoded, 0 panics");
}
#[test]
#[ignore = "big fuzz"]
fn jpeg_3k_pathological_mutations() {
let bases: Vec<Vec<u8>> = vec![
FlatJpeg::grayscale(16, 16).build(),
FlatJpeg::color444(24, 16).build(),
FlatJpeg::grayscale(16, 16).with_flat_code_len(16).build(),
FlatJpeg::color444(16, 16).with_flat_code_len(12).build(),
];
let mut rng = Rng::new(0x001E_6600 ^ 0x2);
let (mut ok, mut rej) = (0usize, 0usize);
for _ in 0..3_000 {
let mut b = bases[rng.below(bases.len())].clone();
for _ in 0..1 + rng.below(8) {
let off = rng.below(b.len());
b[off] ^= (rng.byte()) | 1;
}
if rng.below(3) == 0 {
let cut = rng.below(b.len());
b.truncate(cut);
}
match jpeg::decode(&b) {
Ok(img) => {
ok += 1;
assert!(
img.width() <= 64 && img.height() <= 64,
"absurd JPEG dims from mutation"
);
}
Err(_) => rej += 1,
}
}
eprintln!("jpeg: 3000 pathological mutations, {ok} decoded / {rej} rejected, 0 panics");
}
#[test]
#[ignore = "big fuzz"]
fn markup_and_highlighter_5k_each() {
let styles = MdStyles::default();
let md_toks = [
"# ", "## ", "- ", "1. ", "> ", "```", "`", "*", "**", "[", "]", "(", ")", "!", "text ",
"\n", "\n\n", " ", "\t", "---", "\u{1b}", "\u{0}", "漢", "🎉",
];
let mut rng = Rng::new(0x_AD_F0_0D);
for _ in 0..5_000 {
let n = 1 + rng.below(80);
let mut src = String::new();
for _ in 0..n {
src.push_str(md_toks[rng.below(md_toks.len())]);
}
let blocks = md::parse(&src, &styles);
let rt = md::to_rich_text(&blocks, &styles);
assert!(rt.height() <= (src.matches('\n').count() as i32 + blocks.len() as i32 + 16));
}
let lex = CLikeLexer::rust();
let code_toks = [
"fn ", "let ", "// c", "/* b", "*/", "\"s", "\"x\"", "'c'", "0xFF", "3.14", "_i", "foo",
"+", "->", "{", "}", ";", "\\", "é", "漢", "\u{1b}", "\u{0}",
];
let mut rng = Rng::new(0x_C0DE_FACE);
for _ in 0..5_000 {
let n = 1 + rng.below(60);
let mut line = String::new();
for _ in 0..n {
line.push_str(code_toks[rng.below(code_toks.len())]);
}
let mut prev_end = 0usize;
for (r, _) in lex.spans(&line) {
assert!(r.start >= prev_end && r.end <= line.len() && r.start <= r.end);
assert!(line.is_char_boundary(r.start) && line.is_char_boundary(r.end));
prev_end = r.end;
}
}
eprintln!("markup: 5000 md docs + 5000 code lines, 0 panics, all ranges valid");
}
#[test]
#[ignore = "big fuzz"]
fn diff_lexer_5k() {
use abstracttui::text::DiffLexer;
let lexer = DiffLexer::new();
let atoms = [
"+",
"-",
" ",
"@@",
"@@ -1,2 +3,4 @@",
"@@@",
"---",
"--- a/f",
"+++",
"+++ b/f\t2026",
"diff --git a/x b/y",
"index 83db48f..bf3a1a5 100644",
"\\",
"\\ No newline at end of file",
"Binary files a and b differ",
"rename from x",
"text",
"fn main() {",
"é",
"漢",
"🎉",
"\u{1b}[31m",
"\u{0}",
"\t",
" ",
];
let mut rng = Rng::new(0x_D1FF_FACE);
for _ in 0..5_000 {
let n = 1 + rng.below(12);
let mut line = String::new();
for _ in 0..n {
line.push_str(atoms[rng.below(atoms.len())]);
}
let mut prev_end = 0usize;
for (r, _) in lexer.spans(&line) {
assert!(r.start >= prev_end && r.end <= line.len() && r.start <= r.end);
assert!(line.is_char_boundary(r.start) && line.is_char_boundary(r.end));
prev_end = r.end;
}
if !line.is_empty() {
let first = lexer.spans(&line);
assert_eq!(first.first().map(|(r, _)| r.start), Some(0), "{line:?}");
}
}
eprintln!("diff: 5000 hostile lines, 0 panics, all ranges valid");
}