Files
RustAst/src/integration_test.rs
T
Michael Schimmel 26f0ed9531 Refactor example tests into dedicated function
Extracts the logic for running example tests into a new function
`run_functional_tests` in `src/utils/tester.rs`. This function is then
called from `src/integration_test.rs` to simplify the test setup and
improve code organization. The changes also adjust the benchmark
thresholds slightly.
2026-02-21 15:01:36 +01:00

180 lines
6.7 KiB
Rust

#[cfg(test)]
mod tests {
use crate::ast::parser::Parser;
use crate::ast::types::Value;
use crate::ast::nodes::UntypedKind;
use crate::ast::environment::Environment;
#[test]
fn test_parse_integer_constant() {
let source = "123";
let mut parser = Parser::new(source).expect("Failed to create parser");
let ast = parser.parse_expression().expect("Failed to parse");
if let UntypedKind::Constant(Value::Int(val)) = ast.kind {
assert_eq!(val, 123);
} else {
panic!("Expected Integer constant, got {:?}", ast.kind);
}
}
#[test]
fn test_parse_negative_integer() {
let source = "-42";
let mut parser = Parser::new(source).expect("Failed to create parser");
let ast = parser.parse_expression().expect("Failed to parse");
if let UntypedKind::Constant(Value::Int(val)) = ast.kind {
assert_eq!(val, -42);
} else {
panic!("Expected Integer constant, got {:?}", ast.kind);
}
}
#[test]
fn test_parse_float_constant() {
let source = "123.45";
let mut parser = Parser::new(source).expect("Failed to create parser");
let ast = parser.parse_expression().expect("Failed to parse");
if let UntypedKind::Constant(Value::Float(val)) = ast.kind {
assert_eq!(val, 123.45);
} else {
panic!("Expected Float constant, got {:?}", ast.kind);
}
}
#[test]
fn test_parse_negative_float() {
let source = "-10.5";
let mut parser = Parser::new(source).expect("Failed to create parser");
let ast = parser.parse_expression().expect("Failed to parse");
if let UntypedKind::Constant(Value::Float(val)) = ast.kind {
assert_eq!(val, -10.5);
} else {
panic!("Expected Float constant, got {:?}", ast.kind);
}
}
#[test]
fn test_closure_modification_from_source() {
let source = r#"
(do
(def x 10)
(def f (fn [] (assign x 20)))
(f)
x
)
"#;
let env = Environment::new();
let compiled = env.compile(source).expect("Failed to compile");
let linked = env.link(compiled);
let result = env.run(&linked);
match result {
Ok(Value::Int(val)) => assert_eq!(val, 20, "Closure should modify outer variable"),
Ok(val) => panic!("Expected Int(20), got {:?}", val),
Err(e) => panic!("VM Error: {}", e),
}
}
#[test]
fn test_examples() {
let results = crate::utils::tester::run_functional_tests();
for res in results {
assert!(res.success, "Example {} failed: {}", res.name, res.message);
}
}
#[test]
fn test_debug_mode_logging() {
let env = Environment::new();
let source = "(+ 10 20)";
let result = env.run_debug(source).expect("Failed to run debug");
let (val, logs) = result;
// 1. Check value
match val {
Ok(Value::Int(30)) => (),
_ => panic!("Expected Int(30), got {:?}", val),
}
// 2. Check logs (should have entries for + and constants)
assert!(!logs.is_empty(), "Logs should not be empty");
// Look for typical trace patterns
let has_call = logs.iter().any(|l| l.contains("CALL"));
let has_const = logs.iter().any(|l| l.contains("CONST(10)"));
let has_result = logs.iter().any(|l| l.contains("} -> 30"));
assert!(has_call, "Logs should contain CALL");
assert!(has_const, "Logs should contain CONST(10)");
assert!(has_result, "Logs should contain result 30");
}
#[test]
fn test_rtl_operators() {
let env = Environment::new();
// --- Arithmetic ---
assert_eq!(format!("{}", env.run_script("(+ 10 20)").unwrap()), "30");
assert_eq!(format!("{}", env.run_script("(- 20 10)").unwrap()), "10");
assert_eq!(format!("{}", env.run_script("(* 10 20)").unwrap()), "200");
assert_eq!(format!("{}", env.run_script("(/ 20 10)").unwrap()), "2");
assert_eq!(format!("{}", env.run_script("(// 20 3)").unwrap()), "6"); // 20 // 3 = 6
assert_eq!(format!("{}", env.run_script("(% 20 3)").unwrap()), "2"); // 20 % 3 = 2
// --- Logic / Bitwise ---
assert_eq!(format!("{}", env.run_script("(and true false)").unwrap()), "false");
assert_eq!(format!("{}", env.run_script("(or true false)").unwrap()), "true");
assert_eq!(format!("{}", env.run_script("(xor true false)").unwrap()), "true");
assert_eq!(format!("{}", env.run_script("(not true)").unwrap()), "false");
assert_eq!(format!("{}", env.run_script("(<< 1 2)").unwrap()), "4"); // 1 << 2 = 4
assert_eq!(format!("{}", env.run_script("(>> 4 1)").unwrap()), "2"); // 4 >> 1 = 2
assert_eq!(format!("{}", env.run_script("(and 3 1)").unwrap()), "1"); // 3 & 1 = 1
// --- Comparison ---
assert_eq!(format!("{}", env.run_script("(= 10 10)").unwrap()), "true");
assert_eq!(format!("{}", env.run_script("(= 10 20)").unwrap()), "false");
assert_eq!(format!("{}", env.run_script("(<> 10 20)").unwrap()), "true");
assert_eq!(format!("{}", env.run_script("(< 10 20)").unwrap()), "true");
assert_eq!(format!("{}", env.run_script("(> 10 20)").unwrap()), "false");
assert_eq!(format!("{}", env.run_script("(<= 10 10)").unwrap()), "true");
assert_eq!(format!("{}", env.run_script("(>= 10 10)").unwrap()), "true");
// --- NaN ---
assert_eq!(format!("{}", env.run_script("NaN").unwrap()), "NaN");
}
#[test]
fn test_date_parsing() {
let env = Environment::new();
let res = env.run_script("(date \"2023-01-01\")").unwrap();
if let Value::DateTime(_) = res {
// OK
} else {
panic!("Expected DateTime, got {:?}", res);
}
}
#[test]
fn test_dynamic_call_destructuring_underflow() {
let env = Environment::new();
let source = "(do
(def call-dynamic (fn [f data] (f data)))
(def data [10 [20 30]])
(def x (fn [[a [b c]]] (+ a (+ b c))))
(call-dynamic x data))";
let result = env.run_script(source);
if let Err(e) = &result {
panic!("Failed: {}", e);
}
assert_eq!(format!("{}", result.unwrap()), "60");
}
}