use proptest::prelude::*;
fn token_fragment() -> impl Strategy<Value = String> {
prop_oneof![
Just("def".to_string()),
Just("end".to_string()),
Just("do".to_string()),
Just("if".to_string()),
Just("else".to_string()),
Just("(".to_string()),
Just(")".to_string()),
Just("[".to_string()),
Just("]".to_string()),
Just("=".to_string()),
Just("+".to_string()),
Just("*".to_string()),
Just(".".to_string()),
Just(",".to_string()),
Just("\n".to_string()),
Just(" ".to_string()),
Just("#".to_string()),
Just("\"".to_string()),
Just("1".to_string()),
Just("x".to_string()),
Just("🔥".to_string()),
Just("日本".to_string()),
]
}
fn plausible_source() -> impl Strategy<Value = String> {
prop::collection::vec(token_fragment(), 0..40).prop_map(|v| v.concat())
}
proptest! {
#![proptest_config(ProptestConfig { cases: 512, ..ProptestConfig::default() })]
#[test]
fn parser_is_total_over_plausible_source(src in plausible_source()) {
let r = std::panic::catch_unwind(|| {
let _ = blue_lang_syntax::lex(&src);
let _ = blue_lang_syntax::parse_program(&src);
});
prop_assert!(r.is_ok(), "PANIC on generated source:\n---\n{src}\n---");
}
#[test]
fn parser_is_total_over_arbitrary_utf8(src in ".*") {
let r = std::panic::catch_unwind(|| {
let _ = blue_lang_syntax::parse_program(&src);
});
prop_assert!(r.is_ok(), "PANIC on arbitrary source:\n---\n{src}\n---");
}
#[test]
fn every_prefix_of_generated_source_is_total(src in plausible_source()) {
for end in 0..=src.len() {
let Some(slice) = src.get(..end) else { continue };
let r = std::panic::catch_unwind(|| {
let _ = blue_lang_syntax::parse_program(slice);
});
prop_assert!(r.is_ok(), "PANIC on {end}-byte prefix of:\n---\n{src}\n---");
}
}
#[test]
fn format_preserves_meaning_over_generated_source(src in plausible_source()) {
let Ok(before) = blue_lang_syntax::parse_program(&src) else {
return Ok(()); };
let Ok(formatted) = blue_lang_fmt::format_source(&src) else {
return Ok(());
};
let after = blue_lang_syntax::parse_program(&formatted).map_err(|e| {
TestCaseError::fail(format!(
"formatter emitted source the PARSER REJECTS — that is \
corruption, not a style choice: {e}\n--- in ---\n{src}\n\
--- out ---\n{formatted}\n"
))
})?;
prop_assert_eq!(
format!("{before:?}"),
format!("{after:?}"),
"format() CHANGED THE MEANING of a program.\n--- in ---\n{}\n\
--- out ---\n{}\n",
src,
formatted
);
}
#[test]
fn formatting_is_idempotent_over_generated_source(src in plausible_source()) {
let Ok(once) = blue_lang_fmt::format_source(&src) else {
return Ok(());
};
let Ok(twice) = blue_lang_fmt::format_source(&once) else {
return Err(TestCaseError::fail(
"format() output does not re-format — the formatter does not \
accept its own emission",
));
};
prop_assert_eq!(once, twice, "format(format(x)) != format(x)");
}
}