From 94b56b35c02f27003a355002108177b3f0f30d85 Mon Sep 17 00:00:00 2001 From: Wodann Date: Sat, 22 Aug 2020 13:38:15 +0200 Subject: [PATCH] refactor: move test utility functions to separate crate --- crates/mun_runtime/Cargo.toml | 1 + crates/mun_runtime/tests/hot_reloading.rs | 14 +- crates/mun_runtime/tests/marshalling.rs | 138 +++++++++++++------- crates/mun_runtime/tests/memory.rs | 105 ++++++++++----- crates/mun_runtime/tests/runtime.rs | 14 +- crates/mun_runtime/tests/util.rs | 143 +------------------- crates/mun_test/Cargo.toml | 19 +++ crates/mun_test/LICENSE-APACHE | 1 + crates/mun_test/LICENSE-MIT | 1 + crates/mun_test/README.md | 1 + crates/mun_test/src/driver.rs | 152 ++++++++++++++++++++++ crates/mun_test/src/lib.rs | 8 ++ 12 files changed, 361 insertions(+), 236 deletions(-) create mode 100644 crates/mun_test/Cargo.toml create mode 100644 crates/mun_test/LICENSE-APACHE create mode 100644 crates/mun_test/LICENSE-MIT create mode 100644 crates/mun_test/README.md create mode 100644 crates/mun_test/src/driver.rs create mode 100644 crates/mun_test/src/lib.rs diff --git a/crates/mun_runtime/Cargo.toml b/crates/mun_runtime/Cargo.toml index dfb40793d..8393375a1 100644 --- a/crates/mun_runtime/Cargo.toml +++ b/crates/mun_runtime/Cargo.toml @@ -27,6 +27,7 @@ rustc-hash = "1.1" compiler = { path="../mun_compiler", package = "mun_compiler" } criterion = "0.3" mlua = { package ="mlua", version="0.2", default-features = false, features=["vendored", "luajit"] } +mun_test = { path = "../mun_test" } tempfile = "3" termcolor = "1.1" wasmer-runtime = "0.16" diff --git a/crates/mun_runtime/tests/hot_reloading.rs b/crates/mun_runtime/tests/hot_reloading.rs index cc545ec2c..49a09e94b 100644 --- a/crates/mun_runtime/tests/hot_reloading.rs +++ b/crates/mun_runtime/tests/hot_reloading.rs @@ -1,15 +1,17 @@ #[macro_use] mod util; -use util::*; +use mun_test::CompileAndRunTestDriver; #[test] fn hotreloadable() { - let mut driver = TestDriver::new( + let mut driver = CompileAndRunTestDriver::new( r" pub fn main() -> i32 { 5 } ", - ); + |builder| builder, + ) + .expect("Failed to build test driver"); assert_invoke_eq!(i32, 5, driver, "main"); let runtime = driver.runtime(); @@ -24,7 +26,7 @@ fn hotreloadable() { #[test] fn hotreload_struct_decl() { - let mut driver = TestDriver::new( + let mut driver = CompileAndRunTestDriver::new( r#" struct(gc) Args { n: i32, @@ -39,7 +41,9 @@ fn hotreload_struct_decl() { Args { n: 3, foo: Bar { m: 1.0 }, } } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); let runtime = driver.runtime(); driver.update( diff --git a/crates/mun_runtime/tests/marshalling.rs b/crates/mun_runtime/tests/marshalling.rs index c4ca3e221..4e223eb7c 100644 --- a/crates/mun_runtime/tests/marshalling.rs +++ b/crates/mun_runtime/tests/marshalling.rs @@ -1,37 +1,46 @@ use mun_runtime::{invoke_fn, ArgumentReflection, Marshal, ReturnTypeReflection, StructRef}; +use mun_test::CompileAndRunTestDriver; + #[macro_use] mod util; -use util::*; - #[test] fn compile_and_run() { - let mut driver = TestDriver::new( + let driver = CompileAndRunTestDriver::new( r" pub fn main() {} ", - ); + |builder| builder, + ) + .expect("Failed to build test driver"); + assert_invoke_eq!((), (), driver, "main"); } #[test] fn return_value() { - let mut driver = TestDriver::new( + let driver = CompileAndRunTestDriver::new( r" pub fn main()->i32 { 3 } ", - ); + |builder| builder, + ) + .expect("Failed to build test driver"); + assert_invoke_eq!(i32, 3, driver, "main"); } #[test] fn arguments() { - let mut driver = TestDriver::new( + let driver = CompileAndRunTestDriver::new( r" pub fn main(a:i32, b:i32)->i32 { a+b } ", - ); + |builder| builder, + ) + .expect("Failed to build test driver"); + let a: i32 = 52; let b: i32 = 746; assert_invoke_eq!(i32, a + b, driver, "main", a, b); @@ -39,12 +48,14 @@ fn arguments() { #[test] fn dispatch_table() { - let mut driver = TestDriver::new( + let driver = CompileAndRunTestDriver::new( r" pub fn add(a:i32, b:i32)->i32 { a+b } pub fn main(a:i32, b:i32)->i32 { add(a,b) } ", - ); + |builder| builder, + ) + .expect("Failed to build test driver"); let a: i32 = 52; let b: i32 = 746; @@ -57,7 +68,7 @@ fn dispatch_table() { #[test] fn booleans() { - let mut driver = TestDriver::new( + let driver = CompileAndRunTestDriver::new( r#" pub fn equal(a:i64, b:i64)->bool { a==b } pub fn equalf(a:f64, b:f64)->bool { a==b } @@ -72,7 +83,10 @@ fn booleans() { pub fn greater_equal(a:i64, b:i64)->bool { a>=b } pub fn greater_equalf(a:f64, b:f64)->bool { a>=b } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); + assert_invoke_eq!(bool, false, driver, "equal", 52i64, 764i64); assert_invoke_eq!(bool, true, driver, "equal", 64i64, 64i64); assert_invoke_eq!(bool, false, driver, "equalf", 52f64, 764f64); @@ -101,7 +115,7 @@ fn booleans() { #[test] fn fibonacci() { - let mut driver = TestDriver::new( + let driver = CompileAndRunTestDriver::new( r#" pub fn fibonacci(n:i64)->i64 { if n <= 1 { @@ -111,7 +125,9 @@ fn fibonacci() { } } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); assert_invoke_eq!(i64, 5, driver, "fibonacci", 5i64); assert_invoke_eq!(i64, 89, driver, "fibonacci", 11i64); @@ -120,7 +136,7 @@ fn fibonacci() { #[test] fn fibonacci_loop() { - let mut driver = TestDriver::new( + let driver = CompileAndRunTestDriver::new( r#" pub fn fibonacci(n:i64)->i64 { let a = 0; @@ -137,7 +153,9 @@ fn fibonacci_loop() { } } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); assert_invoke_eq!(i64, 5, driver, "fibonacci", 5i64); assert_invoke_eq!(i64, 89, driver, "fibonacci", 11i64); @@ -147,7 +165,7 @@ fn fibonacci_loop() { #[test] fn fibonacci_loop_break() { - let mut driver = TestDriver::new( + let driver = CompileAndRunTestDriver::new( r#" pub fn fibonacci(n:i64)->i64 { let a = 0; @@ -164,7 +182,9 @@ fn fibonacci_loop_break() { } } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); assert_invoke_eq!(i64, 5, driver, "fibonacci", 5i64); assert_invoke_eq!(i64, 89, driver, "fibonacci", 11i64); @@ -174,7 +194,7 @@ fn fibonacci_loop_break() { #[test] fn fibonacci_while() { - let mut driver = TestDriver::new( + let driver = CompileAndRunTestDriver::new( r#" pub fn fibonacci(n:i64)->i64 { let a = 0; @@ -189,7 +209,9 @@ fn fibonacci_while() { a } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); assert_invoke_eq!(i64, 5, driver, "fibonacci", 5i64); assert_invoke_eq!(i64, 89, driver, "fibonacci", 11i64); @@ -199,7 +221,7 @@ fn fibonacci_while() { #[test] fn true_is_true() { - let mut driver = TestDriver::new( + let driver = CompileAndRunTestDriver::new( r#" pub fn test_true()->bool { true @@ -209,7 +231,10 @@ fn true_is_true() { false } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); + assert_invoke_eq!(bool, true, driver, "test_true"); assert_invoke_eq!(bool, false, driver, "test_false"); } @@ -219,7 +244,7 @@ fn compiler_valid_utf8() { use std::ffi::CStr; use std::slice; - let mut driver = TestDriver::new( + let driver = CompileAndRunTestDriver::new( r#" struct Foo { a: i32, @@ -227,7 +252,9 @@ fn compiler_valid_utf8() { pub fn foo(n:Foo)->bool { false } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); let runtime = driver.runtime(); let runtime_ref = runtime.borrow(); @@ -267,7 +294,7 @@ fn compiler_valid_utf8() { #[test] fn fields() { - let mut driver = TestDriver::new( + let driver = CompileAndRunTestDriver::new( r#" struct(gc) Foo { a:i32, b:i32 }; pub fn main(foo:i32)->bool { @@ -278,13 +305,16 @@ fn fields() { result.a == a.a } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); + assert_invoke_eq!(bool, true, driver, "main", 48); } #[test] fn field_crash() { - let mut driver = TestDriver::new( + let driver = CompileAndRunTestDriver::new( r#" struct(gc) Foo { a: i32 }; @@ -293,13 +323,16 @@ fn field_crash() { b.a } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); + assert_invoke_eq!(i32, 15, driver, "main", 10); } #[test] fn marshal_struct() { - let mut driver = TestDriver::new( + let driver = CompileAndRunTestDriver::new( r#" struct(value) Foo { a: i32, b: bool }; struct Bar(i32, bool); @@ -322,7 +355,9 @@ fn marshal_struct() { Baz(foo_new(foo_a, foo_b)) } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); struct TestData(T, T); @@ -496,27 +531,32 @@ fn extern_fn() { a + b + 9 } - let mut driver = TestDriver::new( + let driver = CompileAndRunTestDriver::new( r#" extern fn add(a: i32, b: i32) -> i32; pub fn main() -> i32 { add(3,4) } "#, + |builder| builder.insert_fn("add", add_int as extern "C" fn(i32, i32) -> i32), ) - .insert_fn("add", add_int as extern "C" fn(i32, i32) -> i32); + .expect("Failed to build test driver"); + assert_invoke_eq!(i32, 16, driver, "main"); } #[test] #[should_panic] fn extern_fn_missing() { - let mut driver = TestDriver::new( + let driver = CompileAndRunTestDriver::new( r#" extern fn add(a: i32, b: i32) -> i32; pub fn main() -> i32 { add(3,4) } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); + assert_invoke_eq!(isize, 16, driver, "main"); } @@ -526,14 +566,13 @@ fn extern_fn_invalid_signature() { 0 } - let result = TestDriver::new( + let result = CompileAndRunTestDriver::new( r#" extern fn add(a: i32, b: i32) -> i32; pub fn main() -> i32 { add(3,4) } "#, - ) - .insert_fn("add", add_int as extern "C" fn() -> i32) - .spawn(); + |builder| builder.insert_fn("add", add_int as extern "C" fn() -> i32), + ); assert!(result.is_err()); } @@ -545,19 +584,21 @@ fn extern_fn_invalid_sig() { 3 } - let mut driver = TestDriver::new( + let driver = CompileAndRunTestDriver::new( r#" extern fn add(a: i32, b: i32) -> i32; pub fn main() -> i32 { add(3,4) } "#, + |builder| builder.insert_fn("add", add_int as extern "C" fn(i8, isize) -> isize), ) - .insert_fn("add", add_int as extern "C" fn(i8, isize) -> isize); + .expect("Failed to build test driver"); + assert_invoke_eq!(isize, 16, driver, "main"); } #[test] fn test_primitive_types() { - let mut driver = TestDriver::new( + let driver = CompileAndRunTestDriver::new( r#" struct Primitives { a:u8, @@ -580,7 +621,9 @@ fn test_primitive_types() { Primitives { a:a, b:b, c:c, d:d, e:e, f:f, g:g, h:h, i:i, j:j, k:k, l:l } } "#, - ); + |builder| builder + ) + .expect("Failed to build test driver"); fn test_field< 't, @@ -643,13 +686,14 @@ fn can_add_external_without_return() { println!("{}", a); } - let mut driver = TestDriver::new( + let driver = CompileAndRunTestDriver::new( r#" extern fn foo(a: i32,); pub fn main(){ foo(3); } "#, + |builder| builder.insert_fn("foo", foo as extern "C" fn(i32) -> ()), ) - .insert_fn("foo", foo as extern "C" fn(i32) -> ()); + .expect("Failed to build test driver"); let runtime = driver.runtime(); let runtime_ref = runtime.borrow(); @@ -659,7 +703,7 @@ fn can_add_external_without_return() { #[test] fn signed_and_unsigned_rem() { - let mut driver = TestDriver::new( + let driver = CompileAndRunTestDriver::new( r#" pub fn signed() -> i32 { (0 - 2) % 5 @@ -669,7 +713,9 @@ fn signed_and_unsigned_rem() { 2 % 5 } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); assert_invoke_eq!(i32, -2, driver, "signed"); assert_invoke_eq!(i32, 2, driver, "unsigned"); diff --git a/crates/mun_runtime/tests/memory.rs b/crates/mun_runtime/tests/memory.rs index 0d9fbe03a..ef9fe7c1f 100644 --- a/crates/mun_runtime/tests/memory.rs +++ b/crates/mun_runtime/tests/memory.rs @@ -1,13 +1,12 @@ use mun_runtime::{invoke_fn, StructRef}; +use mun_test::CompileAndRunTestDriver; #[macro_use] mod util; -use util::*; - #[test] fn gc_trace() { - let mut driver = TestDriver::new( + let driver = CompileAndRunTestDriver::new( r#" pub struct Foo { quz: f64, @@ -27,7 +26,9 @@ fn gc_trace() { } } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); let runtime = driver.runtime(); let runtime_ref = runtime.borrow(); @@ -46,7 +47,7 @@ fn gc_trace() { #[test] fn map_struct_insert_field1() { - let mut driver = TestDriver::new( + let mut driver = CompileAndRunTestDriver::new( r#" struct Foo { b: i64, @@ -57,7 +58,9 @@ fn map_struct_insert_field1() { Foo { b, c } } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); let runtime = driver.runtime(); let runtime_ref = runtime.borrow(); @@ -84,7 +87,7 @@ fn map_struct_insert_field1() { #[test] fn map_struct_insert_field2() { - let mut driver = TestDriver::new( + let mut driver = CompileAndRunTestDriver::new( r#" struct Foo { a: i64, @@ -95,7 +98,9 @@ fn map_struct_insert_field2() { Foo { a, c } } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); let runtime = driver.runtime(); let runtime_ref = runtime.borrow(); @@ -122,7 +127,7 @@ fn map_struct_insert_field2() { #[test] fn map_struct_insert_field3() { - let mut driver = TestDriver::new( + let mut driver = CompileAndRunTestDriver::new( r#" struct Foo { a: i64, @@ -133,7 +138,9 @@ fn map_struct_insert_field3() { Foo { a, b } } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); let runtime = driver.runtime(); let runtime_ref = runtime.borrow(); @@ -160,7 +167,7 @@ fn map_struct_insert_field3() { #[test] fn map_struct_remove_field1() { - let mut driver = TestDriver::new( + let mut driver = CompileAndRunTestDriver::new( r#" struct Foo { a: f64, @@ -172,7 +179,9 @@ fn map_struct_remove_field1() { Foo { a, b, c } } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); let runtime = driver.runtime(); let runtime_ref = runtime.borrow(); @@ -196,7 +205,7 @@ fn map_struct_remove_field1() { #[test] fn map_struct_remove_field2() { - let mut driver = TestDriver::new( + let mut driver = CompileAndRunTestDriver::new( r#" struct Foo { a: f64, @@ -208,7 +217,9 @@ fn map_struct_remove_field2() { Foo { a, b, c } } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); let runtime = driver.runtime(); let runtime_ref = runtime.borrow(); @@ -232,7 +243,7 @@ fn map_struct_remove_field2() { #[test] fn map_struct_remove_field3() { - let mut driver = TestDriver::new( + let mut driver = CompileAndRunTestDriver::new( r#" struct Foo { a: i64, @@ -244,7 +255,9 @@ fn map_struct_remove_field3() { Foo { a, b, c } } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); let runtime = driver.runtime(); let runtime_ref = runtime.borrow(); @@ -268,7 +281,7 @@ fn map_struct_remove_field3() { #[test] fn map_struct_cast_fields1() { - let mut driver = TestDriver::new( + let mut driver = CompileAndRunTestDriver::new( r#" struct Foo( u8, @@ -282,7 +295,9 @@ fn map_struct_cast_fields1() { Foo(a, b, c, d, e) } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); let runtime = driver.runtime(); let runtime_ref = runtime.borrow(); @@ -316,7 +331,7 @@ fn map_struct_cast_fields1() { #[test] fn map_struct_cast_fields2() { - let mut driver = TestDriver::new( + let mut driver = CompileAndRunTestDriver::new( r#" struct Foo( i16, @@ -326,7 +341,9 @@ fn map_struct_cast_fields2() { Foo(a) } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); let runtime = driver.runtime(); let runtime_ref = runtime.borrow(); @@ -349,7 +366,7 @@ fn map_struct_cast_fields2() { #[test] fn map_struct_swap_fields1() { - let mut driver = TestDriver::new( + let mut driver = CompileAndRunTestDriver::new( r#" struct Foo { a: f64, @@ -361,7 +378,9 @@ fn map_struct_swap_fields1() { Foo { a, b, c } } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); let runtime = driver.runtime(); let runtime_ref = runtime.borrow(); @@ -389,7 +408,7 @@ fn map_struct_swap_fields1() { #[test] fn map_struct_swap_fields2() { - let mut driver = TestDriver::new( + let mut driver = CompileAndRunTestDriver::new( r#" struct Foo { a: f64, @@ -402,7 +421,9 @@ fn map_struct_swap_fields2() { Foo { a, b, c, d } } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); let runtime = driver.runtime(); let runtime_ref = runtime.borrow(); @@ -433,7 +454,7 @@ fn map_struct_swap_fields2() { #[test] fn map_struct_rename_field1() { - let mut driver = TestDriver::new( + let mut driver = CompileAndRunTestDriver::new( r#" struct Foo { a: i64, @@ -445,7 +466,9 @@ fn map_struct_rename_field1() { Foo { a, b, c } } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); let runtime = driver.runtime(); let runtime_ref = runtime.borrow(); @@ -473,7 +496,7 @@ fn map_struct_rename_field1() { #[test] fn map_struct_rename_field2() { - let mut driver = TestDriver::new( + let mut driver = CompileAndRunTestDriver::new( r#" struct Foo { a: i64, @@ -485,7 +508,9 @@ fn map_struct_rename_field2() { Foo { a, b, c } } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); let runtime = driver.runtime(); let runtime_ref = runtime.borrow(); @@ -513,7 +538,7 @@ fn map_struct_rename_field2() { #[test] fn map_struct_all() { - let mut driver = TestDriver::new( + let mut driver = CompileAndRunTestDriver::new( r#" struct Foo { a: i32, @@ -526,7 +551,9 @@ fn map_struct_all() { Foo { a, b, c, d } } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); let runtime = driver.runtime(); let runtime_ref = runtime.borrow(); @@ -558,7 +585,7 @@ fn map_struct_all() { #[test] fn delete_used_struct() { - let mut driver = TestDriver::new( + let mut driver = CompileAndRunTestDriver::new( r#" struct Foo { a: i64, @@ -570,7 +597,9 @@ fn delete_used_struct() { Foo { a, b, c } } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); let runtime = driver.runtime(); let runtime_ref = runtime.borrow(); @@ -609,7 +638,7 @@ fn delete_used_struct() { #[test] fn nested_structs() { - let mut driver = TestDriver::new( + let mut driver = CompileAndRunTestDriver::new( r#" struct(gc) GcStruct(f32, f32); struct(value) ValueStruct(f32, f32); @@ -633,7 +662,9 @@ fn nested_structs() { ValueWrapper(a, b) } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); let runtime = driver.runtime(); let runtime_ref = runtime.borrow(); @@ -833,7 +864,7 @@ fn nested_structs() { #[test] fn insert_struct() { - let mut driver = TestDriver::new( + let mut driver = CompileAndRunTestDriver::new( r#" struct Foo { a: i64, @@ -844,7 +875,9 @@ fn insert_struct() { Foo { a, c } } "#, - ); + |builder| builder, + ) + .expect("Failed to build test driver"); let runtime = driver.runtime(); let runtime_ref = runtime.borrow(); diff --git a/crates/mun_runtime/tests/runtime.rs b/crates/mun_runtime/tests/runtime.rs index 61ed820f9..6f72730a3 100644 --- a/crates/mun_runtime/tests/runtime.rs +++ b/crates/mun_runtime/tests/runtime.rs @@ -1,20 +1,19 @@ -mod util; - +use mun_test::CompileAndRunTestDriver; use std::io; -use util::*; #[test] fn error_assembly_not_linkable() { - let mut driver = TestDriver::new( + let driver = CompileAndRunTestDriver::new( r" extern fn dependency() -> i32; pub fn main() -> i32 { dependency() } ", + |builder| builder, ); assert_eq!( - format!("{}", driver.spawn().unwrap_err()), + format!("{}", driver.unwrap_err()), format!( "{}", io::Error::new( @@ -27,7 +26,7 @@ fn error_assembly_not_linkable() { #[test] fn arg_missing_bug() { - let mut driver = TestDriver::new( + let driver = CompileAndRunTestDriver::new( r" pub fn fibonacci_n() -> i64 { let n = arg(); @@ -45,7 +44,8 @@ fn arg_missing_bug() { fibonacci(n - 1) + fibonacci(n - 2) } }", + |builder| builder, ); - driver.spawn().unwrap() + driver.unwrap(); } diff --git a/crates/mun_runtime/tests/util.rs b/crates/mun_runtime/tests/util.rs index 0a78c0a04..ed69fc164 100644 --- a/crates/mun_runtime/tests/util.rs +++ b/crates/mun_runtime/tests/util.rs @@ -1,145 +1,4 @@ -#![allow(dead_code, unused_macros)] - -use compiler::{Config, DisplayColor, Driver, FileId, PathOrInline, RelativePathBuf}; -use mun_runtime::{IntoFunctionDefinition, Runtime, RuntimeBuilder}; -use std::{ - cell::{Ref, RefCell}, - io::Cursor, - path::PathBuf, - rc::Rc, - thread::sleep, - time::Duration, -}; - -/// Implements a compiler and runtime in one that can invoke functions. Use of the TestDriver -/// enables quick testing of Mun constructs in the runtime with hot-reloading support. -pub(crate) struct TestDriver { - _temp_dir: tempfile::TempDir, - out_path: PathBuf, - file_id: FileId, - driver: Driver, - runtime: RuntimeOrBuilder, -} - -enum RuntimeOrBuilder { - Runtime(Rc>), - Builder(RuntimeBuilder), - Pending, -} - -impl RuntimeOrBuilder { - pub fn spawn(&mut self) -> Result<(), anyhow::Error> { - let previous = std::mem::replace(self, RuntimeOrBuilder::Pending); - let runtime = match previous { - RuntimeOrBuilder::Runtime(runtime) => runtime, - RuntimeOrBuilder::Builder(builder) => builder.spawn()?, - _ => unreachable!(), - }; - *self = RuntimeOrBuilder::Runtime(runtime); - Ok(()) - } -} - -impl TestDriver { - /// Construct a new TestDriver from a single Mun source - pub fn new(text: &str) -> Self { - let temp_dir = tempfile::TempDir::new().unwrap(); - let config = Config { - out_dir: Some(temp_dir.path().to_path_buf()), - display_color: DisplayColor::Disable, - ..Config::default() - }; - let input = PathOrInline::Inline { - rel_path: RelativePathBuf::from("main.mun"), - contents: text.to_owned(), - }; - let (mut driver, file_id) = Driver::with_file(config, input).unwrap(); - let mut compiler_errors: Vec = Vec::new(); - if driver - .emit_diagnostics(&mut Cursor::new(&mut compiler_errors)) - .unwrap() - { - panic!( - "compiler errors:\n{}", - String::from_utf8(compiler_errors) - .expect("compiler errors are not UTF-8 formatted") - ) - } - let out_path = driver.assembly_output_path(file_id); - driver.write_assembly(file_id, true).unwrap(); - let builder = RuntimeBuilder::new(&out_path); - TestDriver { - _temp_dir: temp_dir, - driver, - out_path, - file_id, - runtime: RuntimeOrBuilder::Builder(builder), - } - } - - /// Spawns a `Runtime` from the `RuntimeBuilder`, if it hadn't already been spawned. - pub fn spawn(&mut self) -> Result<(), anyhow::Error> { - self.runtime.spawn().map(|_| ()) - } - - /// Updates the text of the Mun source and ensures that the generated assembly has been - /// reloaded. - /// - /// A reference to the borrowed `runtime` is used as an argument to ensure that the runtime was - /// spawned prior to calling update AND to allow moving of the existing borrow inside the update - /// function. This obviates the necessity for `update` to use the `Runtime`. - pub fn update(&mut self, runtime: Ref<'_, Runtime>, text: &str) { - self.driver.set_file_text(self.file_id, text); - let mut compiler_errors: Vec = Vec::new(); - if self - .driver - .emit_diagnostics(&mut Cursor::new(&mut compiler_errors)) - .unwrap() - { - panic!( - "compiler errors:\n{}", - String::from_utf8(compiler_errors) - .expect("compiler errors are not UTF-8 formatted") - ) - } - let out_path = self.driver.assembly_output_path(self.file_id); - self.driver.write_assembly(self.file_id, true).unwrap(); - assert_eq!( - &out_path, &self.out_path, - "recompiling did not result in the same assembly" - ); - let start_time = std::time::Instant::now(); - drop(runtime); - while !self.runtime().borrow_mut().update() { - let now = std::time::Instant::now(); - if now - start_time > std::time::Duration::from_secs(10) { - panic!("runtime did not update after recompilation within 10secs"); - } else { - sleep(Duration::from_millis(1)); - } - } - } - - /// Adds a custom user function to the dispatch table. - pub fn insert_fn, F: IntoFunctionDefinition>(mut self, name: S, func: F) -> Self { - self.runtime = match self.runtime { - RuntimeOrBuilder::Builder(builder) => { - RuntimeOrBuilder::Builder(builder.insert_fn(name, func)) - } - _ => unreachable!(), - }; - self - } - - /// Returns the `Runtime` used by this instance - pub fn runtime(&mut self) -> Rc> { - self.runtime.spawn().unwrap(); - match &mut self.runtime { - RuntimeOrBuilder::Runtime(r) => r.clone(), - _ => unreachable!(), - } - } -} +#![allow(unused_macros)] macro_rules! assert_invoke_eq { ($ExpectedType:ty, $ExpectedResult:expr, $Driver:expr, $($Arg:tt)+) => { diff --git a/crates/mun_test/Cargo.toml b/crates/mun_test/Cargo.toml new file mode 100644 index 000000000..3ec92c20a --- /dev/null +++ b/crates/mun_test/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "mun_test" +version = "0.1.0" +authors = ["The Mun Team "] +edition = "2018" +description = "Functionality for testing Mun code" +documentation = "https://docs.mun-lang.org/v0.2" +readme = "README.md" +homepage = "https://mun-lang.org" +repository = "https://github.com/mun-lang/mun" +license = "MIT OR Apache-2.0" +keywords = ["game", "hot-reloading", "language", "mun", "scripting"] +categories = ["game-development", "mun"] + +[dependencies] +anyhow = "1.0" +compiler = { path = "../mun_compiler", package = "mun_compiler" } +runtime = { path = "../mun_runtime", package = "mun_runtime" } +tempfile = "3" diff --git a/crates/mun_test/LICENSE-APACHE b/crates/mun_test/LICENSE-APACHE new file mode 100644 index 000000000..e5ee4a2d4 --- /dev/null +++ b/crates/mun_test/LICENSE-APACHE @@ -0,0 +1 @@ +../../LICENSE-APACHE diff --git a/crates/mun_test/LICENSE-MIT b/crates/mun_test/LICENSE-MIT new file mode 100644 index 000000000..4d694a09b --- /dev/null +++ b/crates/mun_test/LICENSE-MIT @@ -0,0 +1 @@ +../../LICENSE-MIT diff --git a/crates/mun_test/README.md b/crates/mun_test/README.md new file mode 100644 index 000000000..fe8400541 --- /dev/null +++ b/crates/mun_test/README.md @@ -0,0 +1 @@ +../../README.md \ No newline at end of file diff --git a/crates/mun_test/src/driver.rs b/crates/mun_test/src/driver.rs new file mode 100644 index 000000000..877613515 --- /dev/null +++ b/crates/mun_test/src/driver.rs @@ -0,0 +1,152 @@ +use compiler::{Config, DisplayColor, Driver, FileId, PathOrInline, RelativePathBuf}; +use runtime::{Runtime, RuntimeBuilder}; +use std::{ + cell::{Ref, RefCell}, + io::Cursor, + path::{Path, PathBuf}, + rc::Rc, + thread::sleep, + time::{Duration, Instant}, +}; + +/// Implements a compiler that generates and temporarily stores a `*.munlib` library +/// corresponding to a single source file. +pub struct CompileTestDriver { + _temp_dir: tempfile::TempDir, + out_path: PathBuf, + file_id: FileId, + driver: Driver, +} + +impl CompileTestDriver { + /// Constructs a new `CompileTestDriver` from a single Mun source. + pub fn new(text: &str) -> Self { + let temp_dir = tempfile::TempDir::new().unwrap(); + let config = Config { + out_dir: Some(temp_dir.path().to_path_buf()), + display_color: DisplayColor::Disable, + ..Config::default() + }; + let input = PathOrInline::Inline { + rel_path: RelativePathBuf::from("main.mun"), + contents: text.to_owned(), + }; + let (mut driver, file_id) = Driver::with_file(config, input).unwrap(); + let mut compiler_errors: Vec = Vec::new(); + if driver + .emit_diagnostics(&mut Cursor::new(&mut compiler_errors)) + .unwrap() + { + panic!( + "compiler errors:\n{}", + String::from_utf8(compiler_errors) + .expect("compiler errors are not UTF-8 formatted") + ) + } + let out_path = driver.assembly_output_path(file_id); + driver.write_assembly(file_id, true).unwrap(); + CompileTestDriver { + _temp_dir: temp_dir, + driver, + out_path, + file_id, + } + } + + /// Updates the text of the Mun source and ensures that the generated assembly has been + /// recompiled. + pub fn update(&mut self, text: &str) { + self.driver.set_file_text(self.file_id, text); + let mut compiler_errors: Vec = Vec::new(); + if self + .driver + .emit_diagnostics(&mut Cursor::new(&mut compiler_errors)) + .unwrap() + { + panic!( + "compiler errors:\n{}", + String::from_utf8(compiler_errors) + .expect("compiler errors are not UTF-8 formatted") + ) + } + let out_path = self.driver.assembly_output_path(self.file_id); + self.driver.write_assembly(self.file_id, true).unwrap(); + assert_eq!( + &out_path, &self.out_path, + "recompiling did not result in the same assembly" + ); + } + + /// Returns the path to the generated `*.munlib` library. + pub fn lib_path(&self) -> &Path { + &self.out_path + } +} + +impl std::fmt::Debug for CompileTestDriver { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CompilerTestDriver") + .field("_temp_dir", &self._temp_dir) + .field("out_path", &self.out_path) + .field("file_id", &self.file_id) + .finish() + } +} + +/// Implements a compiler that generates, temporarily stores, and hot reloads a +/// `*.munlib` library corresponding to a single source file. +/// +/// This allows testing of Mun constructs that depend on hot-reloading. +pub struct CompileAndRunTestDriver { + driver: CompileTestDriver, + runtime: Rc>, +} + +impl std::fmt::Debug for CompileAndRunTestDriver { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CompileAndRunTestDriver") + .field("driver", &self.driver) + .finish() + } +} + +impl CompileAndRunTestDriver { + /// Constructs a `CompileAndRunTestDriver` from a single Mun source file and a `config_fn` that + /// allows modification of a [`RuntimeBuilder`]. + pub fn new( + text: &str, + config_fn: impl FnOnce(RuntimeBuilder) -> RuntimeBuilder, + ) -> Result { + let driver = CompileTestDriver::new(text); + let builder = RuntimeBuilder::new(driver.lib_path()); + let runtime = config_fn(builder).spawn()?; + + Ok(Self { driver, runtime }) + } + + /// Updates the text of the Mun source and ensures that the generated assembly has been + /// reloaded. + /// + /// A reference to the borrowed `runtime` is used as an argument to allow moving of the + /// existing borrow inside the update function. This obviates the necessity for `update` to use + /// the `Runtime`. + pub fn update(&mut self, runtime: Ref<'_, Runtime>, text: &str) { + self.driver.update(text); + + let start_time = Instant::now(); + drop(runtime); + while !self.runtime().borrow_mut().update() { + let now = Instant::now(); + if now - start_time > Duration::from_secs(10) { + panic!("runtime did not update after recompilation within 10 seconds"); + } else { + sleep(Duration::from_millis(1)); + } + } + } + + /// Returns the `Runtime` used by the driver. + pub fn runtime(&self) -> Rc> { + self.runtime.clone() + } +} diff --git a/crates/mun_test/src/lib.rs b/crates/mun_test/src/lib.rs new file mode 100644 index 000000000..ed5b2a772 --- /dev/null +++ b/crates/mun_test/src/lib.rs @@ -0,0 +1,8 @@ +//! Mun Test +//! +//! Mun Test contains shared functionality for testing Mun crates. +#![warn(missing_docs)] + +mod driver; + +pub use driver::*;