use proptest::prelude::*;
use sema_reader::{read, read_many};
proptest! {
#[test]
fn reader_never_panics(input in "\\PC*") {
let _ = read(&input);
}
#[test]
fn reader_many_never_panics(input in "\\PC*") {
let _ = read_many(&input);
}
}
fn sema_atom() -> impl Strategy<Value = String> {
prop_oneof![
(-1000i64..1000).prop_map(|n| n.to_string()),
(-100.0f64..100.0).prop_map(|f| format!("{f:.2}")),
"[a-zA-Z0-9 _]{0,20}".prop_map(|s| format!("\"{s}\"")),
"[a-z][a-z0-9?!-]{0,10}",
"[a-z][a-z0-9-]{0,10}".prop_map(|s| format!(":{s}")),
Just("#t".to_string()),
Just("#f".to_string()),
Just("true".to_string()),
Just("false".to_string()),
Just("nil".to_string()),
]
}
fn sema_expr(depth: u32) -> impl Strategy<Value = String> {
if depth == 0 {
sema_atom().boxed()
} else {
prop_oneof![
sema_atom(),
prop::collection::vec(sema_expr(depth - 1), 0..5)
.prop_map(|items| format!("({})", items.join(" "))),
prop::collection::vec(sema_expr(depth - 1), 0..5)
.prop_map(|items| format!("[{}]", items.join(" "))),
sema_atom().prop_map(|a| format!("'{a}")),
]
.boxed()
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(500))]
#[test]
fn valid_sema_parses_ok(expr in sema_expr(3)) {
read(&expr).unwrap_or_else(|e| {
panic!("Failed to parse generated expr: {expr:?}\nError: {e}")
});
}
#[test]
fn multiple_exprs_parse(exprs in prop::collection::vec(sema_expr(2), 1..5)) {
let input = exprs.join(" ");
let result = read_many(&input).unwrap_or_else(|e| {
panic!("Failed to parse: {input:?}\nError: {e}")
});
assert!(!result.is_empty(), "should parse at least one expr from: {input:?}");
}
}
proptest! {
#[test]
fn delimiter_soup_never_panics(
input in prop::collection::vec(
prop_oneof![
Just("("),
Just(")"),
Just("["),
Just("]"),
Just("{"),
Just("}"),
Just(" "),
Just("1"),
Just(":a"),
Just("foo"),
],
0..50
).prop_map(|v| v.join(""))
) {
let _ = read_many(&input);
}
}
proptest! {
#[test]
fn string_escapes_never_panic(
content in prop::collection::vec(
prop_oneof![
Just("a".to_string()),
Just("\\n".to_string()),
Just("\\t".to_string()),
Just("\\\\".to_string()),
Just("\\\"".to_string()),
Just(" ".to_string()),
Just("\\z".to_string()), ],
0..20
).prop_map(|v| format!("\"{}\"", v.join("")))
) {
let _ = read(&content);
}
}
proptest! {
#[test]
fn numeric_strings_never_panic(
input in prop_oneof![
"-?[0-9]{1,20}", "-?[0-9]{1,10}\\.[0-9]{1,10}", "-?[0-9]{1,25}", ]
) {
let _ = read(&input);
}
}