use ruchy::runtime::interpreter::{Interpreter, Value};
use std::rc::Rc;
fn eval_expr(code: &str) -> Result<Value, Box<dyn std::error::Error>> {
let mut interpreter = Interpreter::new();
interpreter.eval_string(code).map_err(|e| e.into())
}
fn test_pattern_match(expr: &str, expected: &str) {
let code = format!("match {} {{ {} }}", expr, expected);
let result = eval_expr(&code);
assert!(result.is_ok(), "Pattern match failed for: {}", code);
}
#[cfg(test)]
mod pattern_guard_tests {
use super::*;
#[test]
fn test_simple_pattern_guard() {
let code = r#"
match 5 {
x if x > 3 => "big",
x => "small"
}
"#;
let result = eval_expr(code).unwrap();
assert_eq!(result, Value::String(Rc::new("big".to_string())));
}
#[test]
fn test_pattern_guard_false_continues() {
let code = r#"
match 2 {
x if x > 5 => "big",
x if x > 0 => "positive",
_ => "negative"
}
"#;
let result = eval_expr(code).unwrap();
assert_eq!(result, Value::String(Rc::new("positive".to_string())));
}
#[test]
fn test_pattern_guard_with_destructuring() {
let code = r#"
match (3, 4) {
(x, y) if x + y > 5 => "sum_big",
(x, y) => "sum_small"
}
"#;
let result = eval_expr(code).unwrap();
assert_eq!(result, Value::String(Rc::new("sum_big".to_string())));
}
#[test]
fn test_multiple_guards_same_pattern() {
let code = r#"
match 10 {
x if x < 5 => "small",
x if x < 15 => "medium",
x => "large"
}
"#;
let result = eval_expr(code).unwrap();
assert_eq!(result, Value::String(Rc::new("medium".to_string())));
}
#[test]
fn test_guard_with_external_variable() {
let code = r#"
let threshold = 10;
match 15 {
x if x > threshold => "above",
x => "below"
}
"#;
let result = eval_expr(code).unwrap();
assert_eq!(result, Value::String(Rc::new("above".to_string())));
}
}
#[cfg(test)]
mod destructuring_tests {
use super::*;
#[test]
fn test_nested_tuple_destructuring() {
let code = r#"
match ((1, 2), (3, 4)) {
((a, b), (c, d)) => a + b + c + d
}
"#;
let result = eval_expr(code).unwrap();
assert_eq!(result, Value::Integer(10));
}
#[test]
fn test_array_destructuring_with_rest() {
let code = r#"
match [1, 2, 3, 4, 5] {
[first, second, ..rest] => first + second,
[] => 0
}
"#;
let result = eval_expr(code).unwrap();
assert_eq!(result, Value::Integer(3));
}
#[test]
fn test_struct_destructuring() {
let code = r#"
struct Point { x: i32, y: i32 }
let p = Point { x: 3, y: 4 };
match p {
Point { x, y } => x * x + y * y
}
"#;
let result = eval_expr(code).unwrap();
assert_eq!(result, Value::Integer(25));
}
#[test]
fn test_struct_partial_destructuring() {
let code = r#"
struct User { name: String, age: i32, email: String }
let user = User { name: "Alice", age: 30, email: "alice@example.com" };
match user {
User { name, .. } => name
}
"#;
let result = eval_expr(code).unwrap();
assert_eq!(result, Value::String(Rc::new("Alice".to_string())));
}
}
#[cfg(test)]
mod exhaustiveness_tests {
use super::*;
#[test]
fn test_enum_exhaustiveness_complete() {
let code = r#"
enum Color { Red, Green, Blue }
let color = Color::Red;
match color {
Color::Red => "red",
Color::Green => "green",
Color::Blue => "blue"
}
"#;
let result = eval_expr(code).unwrap();
assert_eq!(result, Value::String(Rc::new("red".to_string())));
}
#[test]
#[should_panic(expected = "Non-exhaustive match")]
fn test_enum_exhaustiveness_incomplete() {
let code = r#"
enum Color { Red, Green, Blue }
let color = Color::Blue;
match color {
Color::Red => "red",
Color::Green => "green"
// Missing Blue case - should fail exhaustiveness check
}
"#;
eval_expr(code).unwrap();
}
#[test]
fn test_boolean_exhaustiveness() {
let code = r#"
match true {
true => "yes",
false => "no"
}
"#;
let result = eval_expr(code).unwrap();
assert_eq!(result, Value::String(Rc::new("yes".to_string())));
}
#[test]
fn test_integer_range_coverage() {
let code = r#"
match 5 {
x if x < 0 => "negative",
0 => "zero",
x if x <= 10 => "small_positive",
_ => "large_positive"
}
"#;
let result = eval_expr(code).unwrap();
assert_eq!(result, Value::String(Rc::new("small_positive".to_string())));
}
}
#[cfg(test)]
mod nested_pattern_tests {
use super::*;
#[test]
fn test_deeply_nested_tuples() {
let code = r#"
match (1, (2, (3, 4))) {
(a, (b, (c, d))) => a + b + c + d
}
"#;
let result = eval_expr(code).unwrap();
assert_eq!(result, Value::Integer(10));
}
#[test]
fn test_mixed_nested_patterns() {
let code = r#"
match ([1, 2], (3, 4)) {
([a, b], (c, d)) => (a + b) * (c + d)
}
"#;
let result = eval_expr(code).unwrap();
assert_eq!(result, Value::Integer(21));
}
#[test]
fn test_nested_guards() {
let code = r#"
match ((5, 3), (2, 8)) {
((a, b), (c, d)) if a > b && c < d => "condition_met",
((a, b), (c, d)) => "condition_not_met"
}
"#;
let result = eval_expr(code).unwrap();
assert_eq!(result, Value::String(Rc::new("condition_met".to_string())));
}
}
#[cfg(test)]
mod or_pattern_tests {
use super::*;
#[test]
fn test_simple_or_pattern() {
let code = r#"
match 2 {
1 | 2 | 3 => "low",
4 | 5 | 6 => "medium",
_ => "high"
}
"#;
let result = eval_expr(code).unwrap();
assert_eq!(result, Value::String(Rc::new("low".to_string())));
}
#[test]
fn test_or_pattern_with_guards() {
let code = r#"
match 5 {
x @ (2 | 4 | 6) if x > 3 => "even_and_big",
x @ (1 | 3 | 5) if x > 3 => "odd_and_big",
_ => "other"
}
"#;
let result = eval_expr(code).unwrap();
assert_eq!(result, Value::String(Rc::new("odd_and_big".to_string())));
}
}
#[cfg(test)]
mod range_pattern_tests {
use super::*;
#[test]
fn test_inclusive_range_pattern() {
let code = r#"
match 5 {
1..=5 => "low_range",
6..=10 => "high_range",
_ => "outside"
}
"#;
let result = eval_expr(code).unwrap();
assert_eq!(result, Value::String(Rc::new("low_range".to_string())));
}
#[test]
fn test_exclusive_range_pattern() {
let code = r#"
match 5 {
1..5 => "low_range",
5..10 => "high_range",
_ => "outside"
}
"#;
let result = eval_expr(code).unwrap();
assert_eq!(result, Value::String(Rc::new("high_range".to_string())));
}
}
#[cfg(test)]
mod at_binding_tests {
use super::*;
#[test]
fn test_at_binding_simple() {
let code = r#"
match 42 {
x @ 42 => x,
_ => 0
}
"#;
let result = eval_expr(code).unwrap();
assert_eq!(result, Value::Integer(42));
}
#[test]
fn test_at_binding_with_guard() {
let code = r#"
match 15 {
x @ y if y > 10 => x * 2,
x => x
}
"#;
let result = eval_expr(code).unwrap();
assert_eq!(result, Value::Integer(30));
}
}
#[cfg(test)]
mod property_tests {
use super::*;
use quickcheck_macros::quickcheck;
#[quickcheck]
fn prop_wildcard_always_matches(value: i32) -> bool {
let code = format!("match {} {{ _ => true }}", value);
match eval_expr(&code) {
Ok(Value::Bool(true)) => true,
_ => false,
}
}
#[quickcheck]
fn prop_literal_pattern_matches_itself(value: i32) -> bool {
let code = format!("match {} {{ {} => true, _ => false }}", value, value);
match eval_expr(&code) {
Ok(Value::Bool(true)) => true,
_ => false,
}
}
#[quickcheck]
fn prop_guard_evaluation_consistent(value: i32) -> quickcheck::TestResult {
if value.abs() > 1000 {
return quickcheck::TestResult::discard();
}
let code = format!("match {} {{ x if x > 0 => true, _ => false }}", value);
let result = eval_expr(&code).unwrap();
let expected = Value::Bool(value > 0);
quickcheck::TestResult::from_bool(result == expected)
}
}