b0f139f389
This commit introduces a new `tester` module to the `ast` crate, enabling functional tests and performance benchmarks for MYC scripts. Functional tests are executed by reading `.myc` files from the `examples` directory, parsing expected output comments, and comparing them against the actual script execution results. Benchmarking involves measuring the execution time of scripts, calculating median durations, and comparing them against stored baselines. The commit also adds support for updating these baselines. The `ast.rs` CLI and the `main.rs` GUI application have been updated to expose these new testing and benchmarking features. The `Cargo.toml` and `Cargo.lock` files have been updated to include the `regex` dependency required for parsing benchmark comments.
124 lines
4.4 KiB
Rust
124 lines
4.4 KiB
Rust
#[cfg(test)]
|
|
mod tests {
|
|
use std::rc::Rc;
|
|
use std::cell::RefCell;
|
|
use crate::ast::parser::Parser;
|
|
use crate::ast::compiler::binder::Binder;
|
|
use crate::ast::vm::VM;
|
|
use crate::ast::types::Value;
|
|
use crate::ast::nodes::UntypedKind;
|
|
use crate::ast::environment::Environment;
|
|
use std::fs;
|
|
|
|
#[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 mut parser = Parser::new(source).expect("Failed to create parser");
|
|
let ast = parser.parse_expression().expect("Failed to parse");
|
|
|
|
let globals = Rc::new(RefCell::new(std::collections::HashMap::new()));
|
|
let mut binder = Binder::new(globals.clone());
|
|
let bound_ast = binder.bind(&ast).expect("Failed to bind");
|
|
|
|
let vm_globals = Rc::new(RefCell::new(Vec::new()));
|
|
let mut vm = VM::new(vm_globals);
|
|
let result = vm.run(&bound_ast);
|
|
|
|
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 entries = fs::read_dir("examples").expect("Could not read examples directory");
|
|
for entry in entries {
|
|
let entry = entry.expect("Invalid entry");
|
|
let path = entry.path();
|
|
if path.extension().map_or(false, |ext| ext == "myc") {
|
|
let content = fs::read_to_string(&path).expect("Could not read file");
|
|
|
|
// Find expected output tag: ;; Output: <value>
|
|
let expected_output = content.lines()
|
|
.find(|line| line.starts_with(";; Output:"))
|
|
.map(|line| line.replace(";; Output:", "").trim().to_string());
|
|
|
|
if let Some(expected) = expected_output {
|
|
let env = Environment::new();
|
|
let result = env.run_script(&content);
|
|
|
|
match result {
|
|
Ok(val) => {
|
|
let val_str = format!("{}", val);
|
|
assert_eq!(val_str, expected, "Example {:?} failed: expected {}, got {}", path, expected, val_str);
|
|
}
|
|
Err(e) => panic!("Example {:?} failed with error: {}", path, e),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|