use crate::exec; use crate::exec::Executor; use crate::forward; use crate::realm::Realm; #[test] fn empty_let_decl_undefined() { let scenario = r#" let a; a == undefined; "#; let pass = String::from("true"); assert_eq!(exec(scenario), pass); } #[test] fn semicolon_expression_stop() { let scenario = r#" var a = 1; + 1; a "#; let pass = String::from("1"); assert_eq!(exec(scenario), pass); } #[test] fn empty_var_decl_undefined() { let scenario = r#" let b; b == undefined; "#; let pass = String::from("true"); assert_eq!(exec(scenario), pass); } #[test] fn object_field_set() { let scenario = r#" let m = {}; m['key'] = 22; m['key'] "#; assert_eq!(exec(scenario), String::from("22")); } #[test] fn spread_with_arguments() { let realm = Realm::create(); let mut engine = Executor::new(realm); let scenario = r#" const a = [1, "test", 3, 4]; function foo(...a) { return arguments; } var result = foo(...a); "#; forward(&mut engine, scenario); let one = forward(&mut engine, "result[0]"); assert_eq!(one, String::from("1")); let two = forward(&mut engine, "result[1]"); assert_eq!(two, String::from("test")); let three = forward(&mut engine, "result[2]"); assert_eq!(three, String::from("3")); let four = forward(&mut engine, "result[3]"); assert_eq!(four, String::from("4")); } #[test] fn array_rest_with_arguments() { let realm = Realm::create(); let mut engine = Executor::new(realm); let scenario = r#" var b = [4, 5, 6] var a = [1, 2, 3, ...b]; "#; forward(&mut engine, scenario); let one = forward(&mut engine, "a"); assert_eq!(one, String::from("[ 1, 2, 3, 4, 5, 6 ]")); } #[test] fn array_field_set() { let element_changes = r#" let m = [1, 2, 3]; m[1] = 5; m[1] "#; assert_eq!(exec(element_changes), String::from("5")); let length_changes = r#" let m = [1, 2, 3]; m[10] = 52; m.length "#; assert_eq!(exec(length_changes), String::from("11")); let negative_index_wont_affect_length = r#" let m = [1, 2, 3]; m[-11] = 5; m.length "#; assert_eq!(exec(negative_index_wont_affect_length), String::from("3")); let non_num_key_wont_affect_length = r#" let m = [1, 2, 3]; m["magic"] = 5; m.length "#; assert_eq!(exec(non_num_key_wont_affect_length), String::from("3")); } #[test] fn test_tilde_operator() { let float = r#" let f = -1.2; ~f "#; assert_eq!(exec(float), String::from("0")); let numeric = r#" let f = 1789; ~f "#; assert_eq!(exec(numeric), String::from("-1790")); // TODO: enable test after we have NaN // let nan = r#" // var m = NaN; // ~m // "#; // assert_eq!(exec(nan), String::from("-1")); let object = r#" let m = {}; ~m "#; assert_eq!(exec(object), String::from("-1")); let boolean_true = r#" ~true "#; assert_eq!(exec(boolean_true), String::from("-2")); let boolean_false = r#" ~false "#; assert_eq!(exec(boolean_false), String::from("-1")); } #[test] fn test_early_return() { let early_return = r#" function early_return() { if (true) { return true; } return false; } early_return() "#; assert_eq!(exec(early_return), String::from("true")); let early_return = r#" function nested_fnct() { return "nested"; } function outer_fnct() { nested_fnct(); return "outer"; } outer_fnct() "#; assert_eq!(exec(early_return), String::from("outer")); } #[test] fn test_short_circuit_evaluation() { // OR operation assert_eq!(exec("true || true"), String::from("true")); assert_eq!(exec("true || false"), String::from("true")); assert_eq!(exec("false || true"), String::from("true")); assert_eq!(exec("false || false"), String::from("false")); // the second operand must NOT be evaluated if the first one resolve to `true`. let short_circuit_eval = r#" function add_one(counter) { counter.value += 1; return true; } let counter = { value: 0 }; let _ = add_one(counter) || add_one(counter); counter.value "#; assert_eq!(exec(short_circuit_eval), String::from("1")); // the second operand must be evaluated if the first one resolve to `false`. let short_circuit_eval = r#" function add_one(counter) { counter.value += 1; return false; } let counter = { value: 0 }; let _ = add_one(counter) || add_one(counter); counter.value "#; assert_eq!(exec(short_circuit_eval), String::from("2")); // AND operation assert_eq!(exec("true && true"), String::from("true")); assert_eq!(exec("true && false"), String::from("false")); assert_eq!(exec("false && true"), String::from("false")); assert_eq!(exec("false && false"), String::from("false")); // the second operand must be evaluated if the first one resolve to `true`. let short_circuit_eval = r#" function add_one(counter) { counter.value += 1; return true; } let counter = { value: 0 }; let _ = add_one(counter) && add_one(counter); counter.value "#; assert_eq!(exec(short_circuit_eval), String::from("2")); // the second operand must NOT be evaluated if the first one resolve to `false`. let short_circuit_eval = r#" function add_one(counter) { counter.value += 1; return false; } let counter = { value: 0 }; let _ = add_one(counter) && add_one(counter); counter.value "#; assert_eq!(exec(short_circuit_eval), String::from("1")); } #[test] fn test_do_while_loop() { let simple_one = r#" a = 0; do { a += 1; } while (a < 10); a "#; assert_eq!(exec(simple_one), String::from("10")); let multiline_statement = r#" pow = 0; b = 1; do { pow += 1; b *= 2; } while (pow < 8); b "#; assert_eq!(exec(multiline_statement), String::from("256")); let body_is_executed_at_least_once = r#" a = 0; do { a += 1; } while (false); a "#; assert_eq!(exec(body_is_executed_at_least_once), String::from("1")); }