mirror of
https://github.com/duhanbalci/dexpr.git
synced 2026-07-02 00:29:15 +00:00
refactor
This commit is contained in:
143
tests/data_driven_tests.rs
Normal file
143
tests/data_driven_tests.rs
Normal file
@@ -0,0 +1,143 @@
|
||||
use dexpr::{ast::value::Value, compiler::Compiler, parser, vm::VM};
|
||||
use indexmap::IndexMap;
|
||||
use rust_decimal::Decimal;
|
||||
use smol_str::SmolStr;
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct TestCase {
|
||||
name: String,
|
||||
code: String,
|
||||
#[serde(default)]
|
||||
globals: HashMap<String, ValueDef>,
|
||||
expected: ValueDef,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct ValueDef {
|
||||
#[serde(rename = "type")]
|
||||
typ: String,
|
||||
#[serde(default)]
|
||||
value: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
fn value_def_to_value(def: &ValueDef) -> Value {
|
||||
match def.typ.as_str() {
|
||||
"null" => Value::Null,
|
||||
"number" => {
|
||||
let s = def.value.as_ref().unwrap().as_str().unwrap();
|
||||
Value::Number(Decimal::from_str(s).unwrap())
|
||||
}
|
||||
"string" => {
|
||||
let s = def.value.as_ref().unwrap().as_str().unwrap();
|
||||
Value::String(s.into())
|
||||
}
|
||||
"boolean" => {
|
||||
let b = def.value.as_ref().unwrap().as_bool().unwrap();
|
||||
Value::Boolean(b)
|
||||
}
|
||||
"object" => {
|
||||
fn json_obj_to_value(obj: &serde_json::Map<String, serde_json::Value>) -> Value {
|
||||
let mut map = IndexMap::new();
|
||||
for (k, v) in obj {
|
||||
let val = match v {
|
||||
serde_json::Value::String(s) => {
|
||||
if let Ok(d) = Decimal::from_str(s) {
|
||||
Value::Number(d)
|
||||
} else {
|
||||
Value::String(SmolStr::from(s.as_str()))
|
||||
}
|
||||
}
|
||||
serde_json::Value::Bool(b) => Value::Boolean(*b),
|
||||
serde_json::Value::Object(nested) => json_obj_to_value(nested),
|
||||
_ => Value::String(SmolStr::from(v.to_string())),
|
||||
};
|
||||
map.insert(SmolStr::from(k.as_str()), val);
|
||||
}
|
||||
Value::Object(map)
|
||||
}
|
||||
let obj = def.value.as_ref().unwrap().as_object().unwrap();
|
||||
json_obj_to_value(obj)
|
||||
}
|
||||
other => panic!("Unknown type: {other}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_all_cases() {
|
||||
let json = include_str!("test_cases.json");
|
||||
let cases: Vec<TestCase> =
|
||||
serde_json::from_str(json).expect("Failed to parse test_cases.json");
|
||||
|
||||
let mut failures = Vec::new();
|
||||
let total = cases.len();
|
||||
|
||||
for case in &cases {
|
||||
let ast = match parser::program(&case.code) {
|
||||
Ok(ast) => ast,
|
||||
Err(e) => {
|
||||
failures.push(format!(
|
||||
"FAIL: {}\n code: {}\n error: parse failed: {e}",
|
||||
case.name,
|
||||
case.code.replace('\n', "\\n"),
|
||||
));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let mut compiler = Compiler::new();
|
||||
let bytecode = match compiler.compile(ast) {
|
||||
Ok(bc) => bc,
|
||||
Err(e) => {
|
||||
failures.push(format!(
|
||||
"FAIL: {}\n code: {}\n error: compile failed: {e}",
|
||||
case.name,
|
||||
case.code.replace('\n', "\\n"),
|
||||
));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let mut vm = VM::new(&bytecode);
|
||||
|
||||
for (name, def) in &case.globals {
|
||||
vm.set_global(name, value_def_to_value(def));
|
||||
}
|
||||
|
||||
let result = match vm.execute() {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
failures.push(format!(
|
||||
"FAIL: {}\n code: {}\n error: execute failed: {e}",
|
||||
case.name,
|
||||
case.code.replace('\n', "\\n"),
|
||||
));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let expected = value_def_to_value(&case.expected);
|
||||
|
||||
if result != expected {
|
||||
failures.push(format!(
|
||||
"FAIL: {}\n code: {}\n expected: {:?}\n got: {:?}",
|
||||
case.name,
|
||||
case.code.replace('\n', "\\n"),
|
||||
expected,
|
||||
result
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if !failures.is_empty() {
|
||||
panic!(
|
||||
"\n{} / {} test cases failed:\n\n{}\n",
|
||||
failures.len(),
|
||||
total,
|
||||
failures.join("\n\n")
|
||||
);
|
||||
}
|
||||
|
||||
eprintln!("All {total} test cases passed.");
|
||||
}
|
||||
Reference in New Issue
Block a user