#[cfg(test)] mod e2e_tests { use dmc_lib::compile_compilation_unit; use dmc_lib::constants_table::ConstantsTable; use dmc_lib::diagnostic::{Diagnostic, Diagnostics}; use dvm_lib::vm::constant::{Constant, StringConstant}; use dvm_lib::vm::value::Value; use dvm_lib::vm::{DvmContext, call}; use std::rc::Rc; const REGISTER_COUNT: usize = 16; fn report_diagnostics(diagnostics: &[Diagnostic]) -> ! { eprintln!( "{}", diagnostics .iter() .map(|d| format!("{:?}", d)) .collect::>() .join("\n") ); panic!("There were diagnostics."); } fn prepare_context(input: &str) -> Result { let mut constants_table = ConstantsTable::new(); let compile_compilation_unit_result = compile_compilation_unit(input, &mut constants_table); let mut dvm_context = DvmContext::new(); for (name, function) in compile_compilation_unit_result?.functions { dvm_context.functions_mut().insert(name, function); } for (name, content) in &constants_table.string_constants() { dvm_context.constants_mut().insert( name.clone(), Constant::String(StringConstant::new(name.clone(), content.clone())), ); } Ok(dvm_context) } fn get_result( dvm_context: &DvmContext, function_name: &str, arguments: &[Value], ) -> Option { call(&dvm_context, function_name, &arguments, REGISTER_COUNT) } fn assert_result(input: &str, function_name: &str, arguments: &[Value], expected_value: Value) { let context = match prepare_context(input) { Ok(context) => context, Err(diagnostics) => report_diagnostics(&diagnostics), }; match get_result(&context, function_name, arguments) { None => panic!("Call returned no value"), Some(result_value) => { assert_eq!(result_value, expected_value); } } } #[test] fn add_1_2() { assert_result( " fn add(a: Int, b: Int) -> Int a + b end ", "add", &vec![Value::Int(1), Value::Int(2)], Value::Int(3), ); } #[test] fn bunch_of_adding() { assert_result( " fn add(a: Int, b: Int) -> Int a + b end fn greetAndAdd(a: Int, b: Int) -> String \"Hello. \" + a + \" plus \" + b + \" is \" + add(a, b) end ", "greetAndAdd", &vec![Value::Int(1), Value::Int(2)], Value::String(Rc::from("Hello. 1 plus 2 is 3")), ); } #[test] fn simple_subtract() { assert_result("fn sub() -> Int 3 - 2 end", "sub", &vec![], Value::Int(1)) } #[test] fn add_two_doubles() { assert_result( "fn add(a: Double, b: Double) -> Double a + b end", "add", &vec![Value::Double(1.23), Value::Double(1.23)], Value::Double(1.23 + 1.23), ); } #[test] fn add_two_double_variables() { assert_result( " fn add() -> Double let a = 1.0 let b = 2.0 a + b end ", "add", &vec![Value::Double(1.0), Value::Double(2.0)], Value::Double(3.0), ); } #[test] fn subtract_two_doubles() { assert_result( "fn subtract(a: Double, b: Double) -> Double a - b end", "subtract", &vec![Value::Double(3.0), Value::Double(2.0)], Value::Double(1.0), ); } #[test] fn subtract_two_double_variables() { assert_result( " fn subtract() -> Double let a = 3.0 let b = 2.0 a - b end ", "subtract", &vec![], Value::Double(1.0), ) } #[test] fn simple_assign() { assert_result( " fn assign() -> Int let mut x = 21 x = x + x x end ", "assign", &vec![], Value::Int(42), ); } #[test] fn assign_field() -> Result<(), Vec> { let context = prepare_context( " class Foo mut bar = 21 ctor(_bar: Int) bar = _bar end end fn foo() -> Foo Foo(42) end ", )?; let result = get_result(&context, "foo", &vec![]); assert!(result.is_some()); let value = result.unwrap(); assert!(matches!(value, Value::Object(_))); let o = value.unwrap_object().borrow(); assert_eq!(o.fields().len(), 1); assert_eq!(o.fields()[0].unwrap_int(), 42); Ok(()) } #[test] fn generic_field_and_ctor_param() -> Result<(), Vec> { let context = prepare_context( " class Foo mut t: T ctor(_t: T) t = _t end end fn main() -> Foo Foo(42) end ", )?; let result = get_result(&context, "main", &vec![]); assert!(result.is_some()); let value = result.unwrap(); assert!(matches!(value, Value::Object(_))); let o = value.unwrap_object().borrow(); assert_eq!(o.fields().len(), 1); assert_eq!(o.fields()[0].unwrap_int(), 42); Ok(()) } }