diff --git a/boa/src/builtins/symbol/tests.rs b/boa/src/builtins/symbol/tests.rs index db1d362fa8..e1a6c3f693 100644 --- a/boa/src/builtins/symbol/tests.rs +++ b/boa/src/builtins/symbol/tests.rs @@ -1,4 +1,4 @@ -use crate::{forward, forward_val, Context}; +use crate::{check_output, forward, forward_val, Context, TestAction}; #[test] fn call_symbol_and_check_return_type() { @@ -24,7 +24,6 @@ fn print_symbol_expect_description() { #[test] fn symbol_access() { - let mut context = Context::new(); let init = r#" var x = {}; var sym1 = Symbol("Hello"); @@ -32,8 +31,10 @@ fn symbol_access() { x[sym1] = 10; x[sym2] = 20; "#; - forward_val(&mut context, init).unwrap(); - assert_eq!(forward(&mut context, "x[sym1]"), "10"); - assert_eq!(forward(&mut context, "x[sym2]"), "20"); - assert_eq!(forward(&mut context, "x['Symbol(Hello)']"), "undefined"); + check_output(&[ + TestAction::Execute(init), + TestAction::TestEq("x[sym1]", "10"), + TestAction::TestEq("x[sym2]", "20"), + TestAction::TestEq("x['Symbol(Hello)']", "undefined"), + ]); } diff --git a/boa/src/exec/tests.rs b/boa/src/exec/tests.rs index cb7c810c39..c650cf533b 100644 --- a/boa/src/exec/tests.rs +++ b/boa/src/exec/tests.rs @@ -1,4 +1,6 @@ -use crate::{builtins::Number, exec, forward, forward_val, Context, JsValue}; +use crate::{ + builtins::Number, check_output, exec, forward, forward_val, Context, JsValue, TestAction, +}; #[test] fn function_declaration_returns_undefined() { @@ -118,8 +120,6 @@ fn object_field_set() { #[test] fn spread_with_arguments() { - let mut context = Context::new(); - let scenario = r#" const a = [1, "test", 3, 4]; function foo(...a) { @@ -128,31 +128,27 @@ fn spread_with_arguments() { var result = foo(...a); "#; - forward(&mut context, scenario); - let one = forward(&mut context, "result[0]"); - assert_eq!(one, String::from("1")); - - let two = forward(&mut context, "result[1]"); - assert_eq!(two, String::from("\"test\"")); - let three = forward(&mut context, "result[2]"); - assert_eq!(three, String::from("3")); - - let four = forward(&mut context, "result[3]"); - assert_eq!(four, String::from("4")); + check_output(&[ + TestAction::Execute(scenario), + TestAction::TestEq("result[0]", "1"), + TestAction::TestEq("result[1]", "\"test\""), + TestAction::TestEq("result[2]", "3"), + TestAction::TestEq("result[3]", "4"), + ]); } #[test] fn array_rest_with_arguments() { - let mut context = Context::new(); - let scenario = r#" var b = [4, 5, 6] var a = [1, 2, 3, ...b]; "#; - forward(&mut context, scenario); - let one = forward(&mut context, "a"); - assert_eq!(one, String::from("[ 1, 2, 3, 4, 5, 6 ]")); + + check_output(&[ + TestAction::Execute(scenario), + TestAction::TestEq("a", "[ 1, 2, 3, 4, 5, 6 ]"), + ]); } #[test] @@ -722,8 +718,6 @@ mod in_operator { #[test] fn should_type_error_when_rhs_not_object() { - let mut context = Context::new(); - let scenario = r#" var x = false; try { @@ -733,14 +727,14 @@ mod in_operator { } "#; - forward(&mut context, scenario); - assert_eq!(forward(&mut context, "x"), "true"); + check_output(&[ + TestAction::Execute(scenario), + TestAction::TestEq("x", "true"), + ]); } #[test] fn should_set_this_value() { - let mut context = Context::new(); - let scenario = r#" function Foo() { this.a = "a"; @@ -749,22 +743,24 @@ mod in_operator { var bar = new Foo(); "#; - forward(&mut context, scenario); - assert_eq!(forward(&mut context, "bar.a"), "\"a\""); - assert_eq!(forward(&mut context, "bar.b"), "\"b\""); + check_output(&[ + TestAction::Execute(scenario), + TestAction::TestEq("bar.a", "\"a\""), + TestAction::TestEq("bar.b", "\"b\""), + ]); } #[test] fn should_type_error_when_new_is_not_constructor() { - let mut context = Context::new(); - let scenario = r#" const a = ""; new a(); "#; - let result = forward(&mut context, scenario); - assert_eq!(result, "Uncaught \"TypeError\": \"a is not a constructor\""); + check_output(&[TestAction::TestEq( + &scenario, + "Uncaught \"TypeError\": \"a is not a constructor\"", + )]); } #[test] @@ -1223,45 +1219,38 @@ fn number_object_access_benchmark() { #[test] fn not_a_function() { - let mut context = Context::new(); let init = r#" let a = {}; let b = true; "#; - forward(&mut context, init); - let scenario = r#" + let scenario1 = r#" try { a(); } catch(e) { e.toString() } "#; - assert_eq!( - forward(&mut context, scenario), - "\"TypeError: not a function\"" - ); - let scenario = r#" + let scenario2 = r#" try { a.a(); } catch(e) { e.toString() } "#; - assert_eq!( - forward(&mut context, scenario), - "\"TypeError: not a function\"" - ); - let scenario = r#" + let scenario3 = r#" try { b(); } catch(e) { e.toString() } "#; - assert_eq!( - forward(&mut context, scenario), - "\"TypeError: not a function\"" - ); + + check_output(&[ + TestAction::Execute(init), + TestAction::TestEq(scenario1, "\"TypeError: not a function\""), + TestAction::TestEq(scenario2, "\"TypeError: not a function\""), + TestAction::TestEq(scenario3, "\"TypeError: not a function\""), + ]); } #[test] @@ -1339,12 +1328,12 @@ fn multicharacter_bitwise_assignment_to_non_assignable() { #[test] fn assign_to_array_decl() { - let mut context = Context::new(); - - assert!(forward(&mut context, "[1] = [2]").starts_with("Uncaught \"SyntaxError\": ")); - assert!(forward(&mut context, "[3, 5] = [7, 8]").starts_with("Uncaught \"SyntaxError\": ")); - assert!(forward(&mut context, "[6, 8] = [2]").starts_with("Uncaught \"SyntaxError\": ")); - assert!(forward(&mut context, "[6] = [2, 9]").starts_with("Uncaught \"SyntaxError\": ")); + check_output(&[ + TestAction::TestStartsWith("[1] = [2]", "Uncaught \"SyntaxError\": "), + TestAction::TestStartsWith("[3, 5] = [7, 8]", "Uncaught \"SyntaxError\": "), + TestAction::TestStartsWith("[6, 8] = [2]", "Uncaught \"SyntaxError\": "), + TestAction::TestStartsWith("[6] = [2, 9]", "Uncaught \"SyntaxError\": "), + ]); } #[test] @@ -1407,11 +1396,10 @@ fn test_strict_mode_octal() { var n = 023; "#; - let mut context = Context::new(); - - let string = dbg!(forward(&mut context, scenario)); - - assert!(string.starts_with("Uncaught \"SyntaxError\": ")); + check_output(&[TestAction::TestStartsWith( + scenario, + "Uncaught \"SyntaxError\": ", + )]); } #[test] @@ -1428,11 +1416,10 @@ fn test_strict_mode_with() { } "#; - let mut context = Context::new(); - - let string = dbg!(forward(&mut context, scenario)); - - assert!(string.starts_with("Uncaught \"SyntaxError\": ")); + check_output(&[TestAction::TestStartsWith( + scenario, + "Uncaught \"SyntaxError\": ", + )]); } #[test] @@ -1446,11 +1433,10 @@ fn test_strict_mode_delete() { delete x; "#; - let mut context = Context::new(); - - let string = dbg!(forward(&mut context, scenario)); - - assert!(string.starts_with("Uncaught \"SyntaxError\": ")); + check_output(&[TestAction::TestStartsWith( + scenario, + "Uncaught \"SyntaxError\": ", + )]); } #[test] @@ -1494,11 +1480,10 @@ fn test_strict_mode_func_decl_in_block() { if (a < b) { function f() {} } "#; - let mut context = Context::new(); - - let string = dbg!(forward(&mut context, scenario)); - - assert!(string.starts_with("Uncaught \"SyntaxError\": ")); + check_output(&[TestAction::TestStartsWith( + scenario, + "Uncaught \"SyntaxError\": ", + )]); } #[test] @@ -1511,11 +1496,10 @@ fn test_strict_mode_dup_func_parameters() { function f(a, b, b) {} "#; - let mut context = Context::new(); - - let string = dbg!(forward(&mut context, scenario)); - - assert!(string.starts_with("Uncaught \"SyntaxError\": ")); + check_output(&[TestAction::TestStartsWith( + scenario, + "Uncaught \"SyntaxError\": ", + )]); } #[test] diff --git a/boa/src/lib.rs b/boa/src/lib.rs index c7f31081ed..175194ceb7 100644 --- a/boa/src/lib.rs +++ b/boa/src/lib.rs @@ -159,3 +159,47 @@ pub(crate) fn exec>(src: T) -> String { Err(error) => error.display().to_string(), } } + +#[cfg(test)] +pub(crate) enum TestAction { + Execute(&'static str), + TestEq(&'static str, &'static str), + TestStartsWith(&'static str, &'static str), +} + +/// Create a clean Context, call "forward" for each action, and optionally +/// assert equality of the returned value or if returned value starts with +/// expected string. +#[cfg(test)] +#[track_caller] +pub(crate) fn check_output(actions: &[TestAction]) { + let mut context = Context::new(); + + let mut i = 1; + for action in actions { + match action { + TestAction::Execute(src) => { + forward(&mut context, src); + } + TestAction::TestEq(case, expected) => { + assert_eq!( + &forward(&mut context, case), + expected, + "Test case {} ('{}')", + i, + case + ); + i += 1; + } + TestAction::TestStartsWith(case, expected) => { + assert!( + &forward(&mut context, case).starts_with(expected), + "Test case {} ('{}')", + i, + case + ); + i += 1; + } + } + } +} diff --git a/boa/src/syntax/ast/node/iteration/tests.rs b/boa/src/syntax/ast/node/iteration/tests.rs index 36154b8bdd..a6b4c8c2f7 100644 --- a/boa/src/syntax/ast/node/iteration/tests.rs +++ b/boa/src/syntax/ast/node/iteration/tests.rs @@ -1,4 +1,4 @@ -use crate::{exec, forward, Context}; +use crate::{check_output, exec, TestAction}; #[test] fn while_loop_late_break() { @@ -194,87 +194,84 @@ fn do_while_loop_continue() { #[test] fn for_of_loop_declaration() { - let mut context = Context::new(); let scenario = r#" var result = 0; for (i of [1, 2, 3]) { result = i; } "#; - context.eval(scenario).unwrap(); - assert_eq!(&forward(&mut context, "result"), "3"); - assert_eq!(&forward(&mut context, "i"), "3"); + check_output(&[ + TestAction::Execute(scenario), + TestAction::TestEq("result", "3"), + TestAction::TestEq("i", "3"), + ]); } #[test] fn for_of_loop_var() { - let mut context = Context::new(); let scenario = r#" var result = 0; for (var i of [1, 2, 3]) { result = i; } "#; - context.eval(scenario).unwrap(); - assert_eq!(&forward(&mut context, "result"), "3"); - assert_eq!(&forward(&mut context, "i"), "3"); + check_output(&[ + TestAction::Execute(scenario), + TestAction::TestEq("result", "3"), + TestAction::TestEq("i", "3"), + ]); } #[test] fn for_of_loop_let() { - let mut context = Context::new(); let scenario = r#" var result = 0; for (let i of [1, 2, 3]) { result = i; } "#; - context.eval(scenario).unwrap(); - assert_eq!(&forward(&mut context, "result"), "3"); - assert_eq!( - &forward( - &mut context, + check_output(&[ + TestAction::Execute(scenario), + TestAction::TestEq("result", "3"), + TestAction::TestEq( r#" try { i } catch(e) { e.toString() } - "# + "#, + "\"ReferenceError: i is not defined\"", ), - "\"ReferenceError: i is not defined\"" - ); + ]); } #[test] fn for_of_loop_const() { - let mut context = Context::new(); let scenario = r#" var result = 0; for (let i of [1, 2, 3]) { result = i; } "#; - context.eval(scenario).unwrap(); - assert_eq!(&forward(&mut context, "result"), "3"); - assert_eq!( - &forward( - &mut context, + check_output(&[ + TestAction::Execute(scenario), + TestAction::TestEq("result", "3"), + TestAction::TestEq( r#" try { i } catch(e) { e.toString() } - "# + "#, + "\"ReferenceError: i is not defined\"", ), - "\"ReferenceError: i is not defined\"" - ); + ]); } #[test] fn for_of_loop_break() { - let mut context = Context::new(); let scenario = r#" var result = 0; for (var i of [1, 2, 3]) { @@ -283,14 +280,15 @@ fn for_of_loop_break() { result = i } "#; - context.eval(scenario).unwrap(); - assert_eq!(&forward(&mut context, "result"), "1"); - assert_eq!(&forward(&mut context, "i"), "2"); + check_output(&[ + TestAction::Execute(scenario), + TestAction::TestEq("result", "1"), + TestAction::TestEq("i", "2"), + ]); } #[test] fn for_of_loop_continue() { - let mut context = Context::new(); let scenario = r#" var result = 0; for (var i of [1, 2, 3]) { @@ -299,14 +297,15 @@ fn for_of_loop_continue() { result = i } "#; - context.eval(scenario).unwrap(); - assert_eq!(&forward(&mut context, "result"), "2"); - assert_eq!(&forward(&mut context, "i"), "3"); + check_output(&[ + TestAction::Execute(scenario), + TestAction::TestEq("result", "2"), + TestAction::TestEq("i", "3"), + ]); } #[test] fn for_of_loop_return() { - let mut context = Context::new(); let scenario = r#" function foo() { for (i of [1, 2, 3]) { @@ -315,8 +314,10 @@ fn for_of_loop_return() { } } "#; - context.eval(scenario).unwrap(); - assert_eq!(&forward(&mut context, "foo()"), "2"); + check_output(&[ + TestAction::Execute(scenario), + TestAction::TestEq("foo()", "2"), + ]); } #[test] @@ -356,8 +357,6 @@ fn for_loop_continue_label() { #[test] fn for_in_declaration() { - let mut context = Context::new(); - let init = r#" let result = []; let obj = { a: "a", b: 2}; @@ -366,21 +365,17 @@ fn for_in_declaration() { result = result.concat([i]); } "#; - eprintln!("{}", forward(&mut context, init)); - - assert_eq!( - forward( - &mut context, - "result.length === 2 && result.includes('a') && result.includes('b')" + check_output(&[ + TestAction::Execute(init), + TestAction::TestEq( + "result.length === 2 && result.includes('a') && result.includes('b')", + "true", ), - "true" - ); + ]); } #[test] fn for_in_var_object() { - let mut context = Context::new(); - let init = r#" let result = []; let obj = { a: "a", b: 2}; @@ -388,21 +383,17 @@ fn for_in_var_object() { result = result.concat([i]); } "#; - eprintln!("{}", forward(&mut context, init)); - - assert_eq!( - forward( - &mut context, - "result.length === 2 && result.includes('a') && result.includes('b')" + check_output(&[ + TestAction::Execute(init), + TestAction::TestEq( + "result.length === 2 && result.includes('a') && result.includes('b')", + "true", ), - "true" - ); + ]); } #[test] fn for_in_var_array() { - let mut context = Context::new(); - let init = r#" let result = []; let arr = ["a", "b"]; @@ -410,21 +401,17 @@ fn for_in_var_array() { result = result.concat([i]); } "#; - eprintln!("{}", forward(&mut context, init)); - - assert_eq!( - forward( - &mut context, - "result.length === 2 && result.includes('0') && result.includes('1')" + check_output(&[ + TestAction::Execute(init), + TestAction::TestEq( + "result.length === 2 && result.includes('0') && result.includes('1')", + "true", ), - "true" - ); + ]); } #[test] fn for_in_let_object() { - let mut context = Context::new(); - let init = r#" let result = []; let obj = { a: "a", b: 2}; @@ -432,21 +419,17 @@ fn for_in_let_object() { result = result.concat([i]); } "#; - eprintln!("{}", forward(&mut context, init)); - - assert_eq!( - forward( - &mut context, - "result.length === 2 && result.includes('a') && result.includes('b')" + check_output(&[ + TestAction::Execute(init), + TestAction::TestEq( + "result.length === 2 && result.includes('a') && result.includes('b')", + "true", ), - "true" - ); + ]); } #[test] fn for_in_const_array() { - let mut context = Context::new(); - let init = r#" let result = []; let arr = ["a", "b"]; @@ -454,15 +437,13 @@ fn for_in_const_array() { result = result.concat([i]); } "#; - eprintln!("{}", forward(&mut context, init)); - - assert_eq!( - forward( - &mut context, - "result.length === 2 && result.includes('0') && result.includes('1')" + check_output(&[ + TestAction::Execute(init), + TestAction::TestEq( + "result.length === 2 && result.includes('0') && result.includes('1')", + "true", ), - "true" - ); + ]); } #[test] diff --git a/boa/src/value/tests.rs b/boa/src/value/tests.rs index ba3fbcab1c..ee44ffcd9b 100644 --- a/boa/src/value/tests.rs +++ b/boa/src/value/tests.rs @@ -1,7 +1,7 @@ #![allow(clippy::float_cmp)] use super::*; -use crate::{forward, forward_val, Context}; +use crate::{check_output, forward, forward_val, Context, TestAction}; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; @@ -64,63 +64,45 @@ fn number_is_true() { // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness #[test] fn abstract_equality_comparison() { - let mut context = Context::new(); - - assert_eq!(forward(&mut context, "undefined == undefined"), "true"); - assert_eq!(forward(&mut context, "null == null"), "true"); - assert_eq!(forward(&mut context, "true == true"), "true"); - assert_eq!(forward(&mut context, "false == false"), "true"); - assert_eq!(forward(&mut context, "'foo' == 'foo'"), "true"); - assert_eq!(forward(&mut context, "0 == 0"), "true"); - assert_eq!(forward(&mut context, "+0 == -0"), "true"); - assert_eq!(forward(&mut context, "+0 == 0"), "true"); - assert_eq!(forward(&mut context, "-0 == 0"), "true"); - assert_eq!(forward(&mut context, "0 == false"), "true"); - assert_eq!(forward(&mut context, "'' == false"), "true"); - assert_eq!(forward(&mut context, "'' == 0"), "true"); - assert_eq!(forward(&mut context, "'17' == 17"), "true"); - assert_eq!(forward(&mut context, "[1,2] == '1,2'"), "true"); - assert_eq!(forward(&mut context, "new String('foo') == 'foo'"), "true"); - assert_eq!(forward(&mut context, "null == undefined"), "true"); - assert_eq!(forward(&mut context, "undefined == null"), "true"); - assert_eq!(forward(&mut context, "null == false"), "false"); - assert_eq!(forward(&mut context, "[] == ![]"), "true"); - assert_eq!( - forward( - &mut context, - "a = { foo: 'bar' }; b = { foo: 'bar'}; a == b" - ), - "false" - ); - assert_eq!( - forward(&mut context, "new String('foo') == new String('foo')"), - "false" - ); - assert_eq!(forward(&mut context, "0 == null"), "false"); - - assert_eq!(forward(&mut context, "0 == '-0'"), "true"); - assert_eq!(forward(&mut context, "0 == '+0'"), "true"); - assert_eq!(forward(&mut context, "'+0' == 0"), "true"); - assert_eq!(forward(&mut context, "'-0' == 0"), "true"); - - assert_eq!(forward(&mut context, "0 == NaN"), "false"); - assert_eq!(forward(&mut context, "'foo' == NaN"), "false"); - assert_eq!(forward(&mut context, "NaN == NaN"), "false"); - - assert_eq!( - forward( - &mut context, - "Number.POSITIVE_INFINITY === Number.POSITIVE_INFINITY" + check_output(&[ + TestAction::TestEq("undefined == undefined", "true"), + TestAction::TestEq("null == null", "true"), + TestAction::TestEq("true == true", "true"), + TestAction::TestEq("false == false", "true"), + TestAction::TestEq("'foo' == 'foo'", "true"), + TestAction::TestEq("0 == 0", "true"), + TestAction::TestEq("+0 == -0", "true"), + TestAction::TestEq("+0 == 0", "true"), + TestAction::TestEq("-0 == 0", "true"), + TestAction::TestEq("0 == false", "true"), + TestAction::TestEq("'' == false", "true"), + TestAction::TestEq("'' == 0", "true"), + TestAction::TestEq("'17' == 17", "true"), + TestAction::TestEq("[1,2] == '1,2'", "true"), + TestAction::TestEq("new String('foo') == 'foo'", "true"), + TestAction::TestEq("null == undefined", "true"), + TestAction::TestEq("undefined == null", "true"), + TestAction::TestEq("null == false", "false"), + TestAction::TestEq("[] == ![]", "true"), + TestAction::TestEq("a = { foo: 'bar' }; b = { foo: 'bar'}; a == b", "false"), + TestAction::TestEq("new String('foo') == new String('foo')", "false"), + TestAction::TestEq("0 == null", "false"), + TestAction::TestEq("0 == '-0'", "true"), + TestAction::TestEq("0 == '+0'", "true"), + TestAction::TestEq("'+0' == 0", "true"), + TestAction::TestEq("'-0' == 0", "true"), + TestAction::TestEq("0 == NaN", "false"), + TestAction::TestEq("'foo' == NaN", "false"), + TestAction::TestEq("NaN == NaN", "false"), + TestAction::TestEq( + "Number.POSITIVE_INFINITY === Number.POSITIVE_INFINITY", + "true", ), - "true" - ); - assert_eq!( - forward( - &mut context, - "Number.NEGAVIVE_INFINITY === Number.NEGAVIVE_INFINITY" + TestAction::TestEq( + "Number.NEGATIVE_INFINITY === Number.NEGATIVE_INFINITY", + "true", ), - "true" - ); + ]); } /// Helper function to get the hash of a `Value`. @@ -608,20 +590,20 @@ fn to_integer_or_infinity() { #[test] fn test_accessors() { - let mut context = Context::new(); let src = r#" let arr = []; let a = { get b() { return "c" }, set b(value) { arr = arr.concat([value]) }} ; a.b = "a"; "#; - context.eval(src).unwrap(); - assert_eq!(forward(&mut context, "a.b"), r#""c""#); - assert_eq!(forward(&mut context, "arr"), r#"[ "a" ]"#); + check_output(&[ + TestAction::Execute(src), + TestAction::TestEq("a.b", r#""c""#), + TestAction::TestEq("arr", r#"[ "a" ]"#), + ]); } #[test] fn to_primitive() { - let mut context = Context::new(); let src = r#" let a = {}; a[Symbol.toPrimitive] = function() { @@ -629,8 +611,10 @@ fn to_primitive() { }; let primitive = a + 0; "#; - context.eval(src).unwrap(); - assert_eq!(forward(&mut context, "primitive"), "42"); + check_output(&[ + TestAction::Execute(src), + TestAction::TestEq("primitive", "42"), + ]); } /// Test cyclic conversions that previously caused stack overflows