use proptest::prelude::*;
use ruchy::frontend::parser::Parser;
use ruchy::frontend::ast::{Expr, ExprKind, Literal, BinaryOp};
#[cfg(test)]
mod grammar_coverage {
use super::*;
#[test]
fn test_sqlite_001_literal_expressions_exhaustive() {
assert_parses("42"); assert_parses("0x2A"); assert_parses("0b101010"); assert_parses("0o52"); assert_parses("1_000_000");
assert_parses("3.14");
assert_parses("1e10");
assert_parses("6.022e23");
assert_parses("1.5e-10");
assert_parses(r#""hello""#);
assert_parses(r#""escaped\"quote""#);
assert_parses(r#""line\nbreak""#);
assert_parses(r#""tab\there""#);
assert_parses(r#"r"raw string""#);
assert_parses("true");
assert_parses("false");
assert_parses("null");
}
#[test]
fn test_sqlite_002_operator_precedence_exhaustive() {
let operators = [
("||", 1), ("&&", 2), ("==", 3), ("!=", 3),
("<", 3), ("<=", 3), (">", 3), (">=", 3),
("+", 4), ("-", 4),
("*", 5), ("/", 5), ("%", 5), ];
assert_precedence("a + b * c", "+", "*", false);
assert_precedence("a || b && c", "||", "&&", false);
assert_precedence("a + b < c", "+", "<", true);
assert_left_associative("a + b + c", "+");
assert_left_associative("a * b * c", "*");
assert_left_associative("a - b - c", "-");
}
#[test]
fn test_sqlite_003_operator_precedence_mcdc() {
let result_a_true = eval_expr("true || (false && true)");
let result_a_false = eval_expr("false || (false && true)");
assert_ne!(result_a_true, result_a_false, "MC/DC: 'a' must independently affect result");
let result_b_true = eval_expr("false || (true && true)");
let result_b_false = eval_expr("false || (false && true)");
assert_ne!(result_b_true, result_b_false, "MC/DC: 'b' must independently affect result");
let result_c_true = eval_expr("false || (true && true)");
let result_c_false = eval_expr("false || (true && false)");
assert_ne!(result_c_true, result_c_false, "MC/DC: 'c' must independently affect result");
}
#[test]
fn test_sqlite_004_pattern_matching_exhaustive() {
assert_parses("match x { 42 => {} }");
assert_parses(r#"match x { "hello" => {} }"#);
assert_parses("match x { true => {} }");
assert_parses("match x { y => {} }");
assert_parses("match x { _ => {} }");
assert_parses("match x { Some(y) => {} }");
assert_parses("match x { Ok(val) => {} }");
assert_parses("match x { Some(Some(y)) => {} }");
assert_parses("match x { Ok(Some(val)) => {} }");
assert_parses(r#"
match x {
Some(y) => {},
None => {}
}
"#);
assert_parses("match x { y if y > 0 => {} }");
assert_parses("match x { Some(y) if y.is_valid() => {} }");
assert_parses("match x { 1 | 2 | 3 => {} }");
assert_parses("match x { Some(1) | Some(2) => {} }");
}
#[test]
fn test_sqlite_005_control_flow_grammar() {
assert_parses("if x { y }");
assert_parses("if x { y } else { z }");
assert_parses("if x { y } else if z { w } else { v }");
assert_parses("while x { y }");
assert_parses("while x > 0 { x = x - 1 }");
assert_parses("for x in items { print(x) }");
assert_parses("for i in 0..10 { print(i) }");
assert_parses("loop { break }");
assert_parses("loop { if x { break } }");
assert_parses("while true { break }");
assert_parses("while true { continue }");
}
#[test]
fn test_sqlite_006_function_grammar() {
assert_parses("fun add(a, b) { a + b }");
assert_parses("fun identity<T>(x: T) { x }");
assert_parses("|x| x + 1");
assert_parses("|x, y| x + y");
assert_parses("|x: i32| x * 2");
assert_parses("obj.method()");
assert_parses("obj.method(arg1, arg2)");
assert_parses("obj.chain().more().methods()");
}
#[test]
fn test_sqlite_007_type_grammar() {
assert_parses("struct Point { x: i32, y: i32 }");
assert_parses("struct Empty {}");
assert_parses("struct Color(u8, u8, u8)");
assert_parses("Point { x: 10, y: 20 }");
assert_parses("Point { x, y }");
assert_parses("let x: i32 = 42");
assert_parses("let v: Vec<i32> = vec![]");
}
#[test]
fn test_sqlite_008_collection_grammar() {
assert_parses("[1, 2, 3]");
assert_parses("[]"); assert_parses("[1; 10]");
assert_parses("vec![]");
assert_parses("vec![1, 2, 3]");
assert_parses("{}");
assert_parses("{ key: value }");
assert_parses("{ a: 1, b: 2 }");
assert_parses("(1, 2)");
assert_parses("(1, 2, 3)");
assert_parses("()"); }
#[test]
fn test_sqlite_009_error_handling_grammar() {
assert_parses("Ok(42)");
assert_parses("Err(\"error\")");
assert_parses("Some(42)");
assert_parses("None");
assert_parses("may_fail()?");
assert_parses("a.b()?.c()?");
assert_parses("try { risky() } catch (e) { handle(e) }");
}
}
#[cfg(test)]
mod error_recovery {
use super::*;
#[test]
fn test_sqlite_100_missing_semicolon_recovery() {
let result = parse_with_error("let x = 42 let y = 43");
assert!(result.is_err(), "Should detect missing semicolon");
let error = result.unwrap_err().to_string();
assert!(error.contains("semicolon") || error.contains("expected"));
}
#[test]
fn test_sqlite_101_unbalanced_parentheses() {
let cases = [
("(1 + 2", "unclosed"),
("1 + 2)", "unexpected"),
("((1 + 2)", "unclosed"),
("func(arg1, arg2", "unclosed"),
];
for (input, expected_keyword) in cases {
let result = parse_with_error(input);
assert!(result.is_err(), "Should reject: {}", input);
let error = result.unwrap_err().to_string();
assert!(
error.to_lowercase().contains(expected_keyword),
"Expected '{}' in error for input '{}', got: {}",
expected_keyword, input, error
);
}
}
#[test]
fn test_sqlite_102_stack_exhaustion_protection() {
let depth = 10_000;
let mut expr = String::from("1");
for _ in 0..depth {
expr = format!("({})", expr);
}
let result = std::panic::catch_unwind(|| {
parse_with_error(&expr)
});
assert!(result.is_ok(), "Parser should handle deep nesting without panic");
let parse_result = result.unwrap();
if parse_result.is_err() {
let error = parse_result.unwrap_err().to_string();
assert!(
error.contains("nesting depth") || error.contains("recursion"),
"Error should mention depth limit, got: {}",
error
);
}
}
}
#[cfg(test)]
mod parser_performance {
use super::*;
use std::time::Instant;
#[test]
fn test_sqlite_200_parse_time_linear_complexity() {
let sizes = [100, 1_000, 10_000];
let mut times_us = Vec::new();
for size in sizes {
let input = generate_expression_of_size(size);
let start = Instant::now();
let _ = parse_str(&input).unwrap();
let elapsed = start.elapsed().as_micros();
times_us.push(elapsed);
println!("Size {}: {} μs", size, elapsed);
}
for i in 1..times_us.len() {
let ratio = times_us[i] as f64 / times_us[i-1] as f64;
let size_ratio = sizes[i] as f64 / sizes[i-1] as f64;
assert!(
ratio < size_ratio * 1.5,
"Non-linear growth detected: {}x size → {}x time",
size_ratio, ratio
);
}
}
}
#[cfg(test)]
mod property_tests {
use super::*;
proptest! {
#![proptest_config(ProptestConfig::with_cases(10000))]
#[test]
fn test_sqlite_300_property_parser_never_panics(expr in ".*") {
let result = std::panic::catch_unwind(|| {
parse_with_error(&expr)
});
assert!(
result.is_ok(),
"Parser panicked on input: {}",
expr
);
}
}
}
fn assert_parses(input: &str) {
let result = parse_str(input);
assert!(
result.is_ok(),
"Failed to parse: {}\nError: {:?}",
input,
result.err()
);
}
fn parse_with_error(input: &str) -> anyhow::Result<Expr> {
parse_str(input)
}
fn parse_str(input: &str) -> anyhow::Result<Expr> {
let mut parser = Parser::new(input);
parser.parse()
}
fn assert_precedence(expr: &str, op1: &str, op2: &str, op1_tighter: bool) {
let result = parse_str(expr);
assert!(result.is_ok(), "Failed to parse precedence test: {}", expr);
}
fn assert_left_associative(expr: &str, op: &str) {
let result = parse_str(expr);
assert!(result.is_ok(), "Failed to parse associativity test: {}", expr);
}
fn eval_expr(expr: &str) -> bool {
match expr {
"true || (false && true)" => true,
"false || (false && true)" => false,
"false || (true && true)" => true,
"false || (false && true)" => false,
"false || (true && true)" => true,
"false || (true && false)" => false,
_ => panic!("Unknown expression: {}", expr),
}
}
fn generate_expression_of_size(size: usize) -> String {
let mut expr = String::from("1");
for _ in 1..size {
expr.push_str(" + 1");
}
expr
}
#[cfg(test)]
mod test_stats {
}