use std::path::PathBuf;
fn corpus() -> Vec<(String, String)> {
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("..")
.join("..")
.join("spec");
let mut out = Vec::new();
let entries = std::fs::read_dir(&root)
.unwrap_or_else(|e| panic!("spec dir {} unreadable: {e}", root.display()));
for e in entries.filter_map(Result::ok) {
let p = e.path();
if p.extension().and_then(|s| s.to_str()) != Some("b") {
continue;
}
let name = p
.file_name()
.and_then(|s| s.to_str())
.unwrap_or("?")
.to_string();
if let Ok(src) = std::fs::read_to_string(&p) {
out.push((name, src));
}
}
out.sort();
assert!(
!out.is_empty(),
"spec corpus is EMPTY — this test would pass vacuously. Fix the path \
rather than deleting the assert; a totality test over zero inputs is \
worse than none, because it reports safety it never checked."
);
out
}
#[test]
fn no_prefix_of_the_spec_corpus_panics_the_parser() {
let mut checked = 0usize;
for (name, src) in corpus() {
for end in 0..=src.len() {
let Some(slice) = src.get(..end) else {
continue;
};
let r = std::panic::catch_unwind(|| {
let _ = blue_lang_syntax::lex(slice);
let _ = blue_lang_syntax::parse_program(slice);
});
assert!(
r.is_ok(),
"PANIC on a {end}-byte prefix of {name}. A parser must return \
Err, never abort — this input is what an LSP sees on every \
keystroke.\n---\n{slice}\n---"
);
checked += 1;
}
}
assert!(
checked > 1000,
"only {checked} prefixes exercised — the corpus shrank or the skip \
logic is over-eager, and a shrunken corpus silently weakens this gate"
);
}
#[test]
fn hostile_inputs_return_rather_than_abort() {
let deep_parens = "(".repeat(2_000);
let deep_blocks = "def a\n".repeat(2_000);
let cases: Vec<(&str, &str)> = vec![
("empty", ""),
("nul", "\0"),
("lone-open-paren", "("),
("lone-close-paren", ")"),
("unterminated-string", "\"abc"),
("unterminated-string-escape", "\"abc\\"),
("dangling-def", "def"),
("dangling-def-name", "def foo"),
("def-without-end", "def foo\n 1"),
("lone-end", "end"),
("lone-equals", "="),
("trailing-operator", "1 +"),
("leading-operator", "+ 1"),
("bare-comment", "#"),
("crlf", "def a\r\n1\r\nend\r\n"),
("tabs-only", "\t\t\t"),
("unicode-ident", "def λ\n1\nend"),
("emoji", "🔥"),
("deep-parens", &deep_parens),
("deep-blocks", &deep_blocks),
];
for (label, src) in cases {
let r = std::panic::catch_unwind(|| {
let _ = blue_lang_syntax::lex(src);
let _ = blue_lang_syntax::parse_program(src);
});
assert!(
r.is_ok(),
"PANIC on hostile input `{label}` — must return Err instead"
);
}
}
#[test]
fn every_spec_file_parses_and_its_forms_are_stable() {
for (name, src) in corpus() {
let Ok(forms) = blue_lang_syntax::parse_program(&src) else {
eprintln!("note: {name} does not parse; totality still holds");
continue;
};
assert!(
!forms.is_empty(),
"{name} parsed to ZERO forms — an empty parse of a non-empty spec \
file means the parser silently consumed the program"
);
let printed = format!("{forms:?}");
let r = std::panic::catch_unwind(|| {
let _ = blue_lang_syntax::parse_program(&printed);
});
assert!(r.is_ok(), "PANIC re-parsing the printed forms of {name}");
}
}
#[test]
fn parser_nesting_depth_is_bounded_and_the_bound_is_measured() {
for depth in [1usize, 8, 32, 64, 128] {
let src = "(".repeat(depth);
let r = std::panic::catch_unwind(|| {
let _ = blue_lang_syntax::parse_program(&src);
});
assert!(
r.is_ok(),
"parser panicked at nesting depth {depth}, which was previously \
known-good — this is a REGRESSION in the safe range"
);
}
}
#[test]
fn depth_beyond_the_limit_is_an_err_not_an_abort() {
for n in [
blue_lang_syntax::MAX_EXPR_DEPTH + 1,
blue_lang_syntax::MAX_EXPR_DEPTH * 10,
2_000,
] {
let src = "(".repeat(n);
let err = blue_lang_syntax::parse_program(&src)
.expect_err("input past MAX_EXPR_DEPTH must be rejected, not accepted");
let msg = err.to_string();
assert!(
msg.contains("nests deeper than"),
"the bound must name ITSELF so the operator knows this is a limit \
and not a syntax error they should go hunting for — got: {msg}"
);
}
}
#[test]
fn the_depth_limit_does_not_reject_realistic_nesting() {
for n in [1usize, 8, 32, 64, 128, blue_lang_syntax::MAX_EXPR_DEPTH - 2] {
let src = format!("{}1{}", "(".repeat(n), ")".repeat(n));
assert!(
blue_lang_syntax::parse_program(&src).is_ok(),
"well-formed nesting at depth {n} must PARSE — a limit that \
rejects valid programs is a bug, not a safeguard"
);
}
}
#[test]
fn the_depth_bound_is_taken_from_the_argument_not_the_constant() {
let shallow = format!("{}1{}", "(".repeat(40), ")".repeat(40));
assert!(
blue_lang_syntax::parse_program(&shallow).is_ok(),
"precondition: the DEFAULT bound accepts depth 40"
);
let err = blue_lang_syntax::parse_program_with_depth(&shallow, 8)
.expect_err("a caller-supplied bound of 8 must reject depth 40");
assert!(
err.to_string().contains("nests deeper than 8"),
"the message must name the bound that was ACTUALLY applied, not the \
constant — got: {err}"
);
let deep_n = blue_lang_syntax::MAX_EXPR_DEPTH + 32;
let deep = format!("{}1{}", "(".repeat(deep_n), ")".repeat(deep_n));
assert!(
blue_lang_syntax::parse_program(&deep).is_err(),
"precondition: the DEFAULT bound refuses depth MAX + 32"
);
assert!(
blue_lang_syntax::parse_program_with_depth(&deep, deep_n * 4).is_ok(),
"a caller-supplied bound above the constant must ADMIT what the \
constant refuses — otherwise the argument is ignored"
);
}
#[test]
fn the_default_entry_points_delegate_to_the_bounded_ones() {
for src in ["1 + 2", "def f(x)\n x\nend", "{a: 1, b: [2, 3]}"] {
assert_eq!(
blue_lang_syntax::parse_program(src),
blue_lang_syntax::parse_program_with_depth(src, blue_lang_syntax::MAX_EXPR_DEPTH),
"parse_program must BE parse_program_with_depth(_, MAX_EXPR_DEPTH)"
);
assert_eq!(
blue_lang_syntax::parse_program_spanned(src),
blue_lang_syntax::parse_program_spanned_with_depth(
src,
blue_lang_syntax::MAX_EXPR_DEPTH
),
);
}
}