From 73d23ead7f1e59b3d0246333824134b13ecccb2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Juli=C3=A1n=20Espina?= Date: Sat, 5 Nov 2022 15:57:38 +0000 Subject: [PATCH] Fix async tests result values (#2406) So, there were some tests that weren't reporting the result of async evaluations correctly. This PR fixes this. It also ignores tests with the `IsHTMLDDA` feature, since we haven't implemented it. On another note, this also changes the symbols of the test suite to 'F' (failed) and '-' (ignored), which is clearer for colorless terminals. --- boa_tester/src/exec/mod.rs | 163 ++++++++++++++++++------------------- test_ignore.txt | 1 + 2 files changed, 81 insertions(+), 83 deletions(-) diff --git a/boa_tester/src/exec/mod.rs b/boa_tester/src/exec/mod.rs index cfb5e41629..2aa51abb09 100644 --- a/boa_tester/src/exec/mod.rs +++ b/boa_tester/src/exec/mod.rs @@ -15,7 +15,6 @@ use boa_engine::{ use boa_gc::{Cell, Finalize, Gc, Trace}; use colored::Colorize; use rayon::prelude::*; -use std::panic; impl TestSuite { /// Runs the test suite. @@ -169,27 +168,26 @@ impl Test { error_type: _, } )) { - let res = panic::catch_unwind(|| match self.expected_outcome { + let res = std::panic::catch_unwind(|| match self.expected_outcome { Outcome::Positive => { let mut context = Context::default(); + let async_result = AsyncResult::default(); + + if let Err(e) = self.set_up_env(harness, &mut context, async_result.clone()) { + return (false, e); + } - let callback_obj = CallbackObject::default(); // TODO: timeout - match self.set_up_env(harness, &mut context, callback_obj.clone()) { - Ok(_) => { - let res = context.eval(&test_content); - - let passed = res.is_ok() - && matches!(*callback_obj.result.borrow(), Some(true) | None); - let text = match res { - Ok(val) => val.display().to_string(), - Err(e) => format!("Uncaught {e}",), - }; - - (passed, text) - } - Err(e) => (false, e), + let value = match context.eval(&test_content) { + Ok(v) => v, + Err(e) => return (false, format!("Uncaught {e}")), + }; + + if let Err(e) = async_result.inner.borrow().as_ref() { + return (false, format!("Uncaught {e}")); } + + (true, value.display().to_string()) } Outcome::Negative { phase: Phase::Parse | Phase::Early, @@ -220,57 +218,47 @@ impl Test { error_type, } => { let mut context = Context::default(); - if let Err(e) = Parser::new(test_content.as_bytes()).parse_all(&mut context) { - (false, format!("Uncaught {e}")) - } else { - // TODO: timeout - match self.set_up_env(harness, &mut context, CallbackObject::default()) { - Ok(_) => match context.eval(&test_content) { - Ok(res) => (false, res.display().to_string()), - Err(e) => { - let passed = if let Ok(e) = e.try_native(&mut context) { - match &e.kind { - JsNativeErrorKind::Syntax - if error_type == ErrorType::SyntaxError => - { - true - } - JsNativeErrorKind::Reference - if error_type == ErrorType::ReferenceError => - { - true - } - JsNativeErrorKind::Range - if error_type == ErrorType::RangeError => - { - true - } - JsNativeErrorKind::Type - if error_type == ErrorType::TypeError => - { - true - } - _ => false, - } - } else { - e.as_opaque() - .expect("try_native cannot fail if e is not opaque") - .as_object() - .and_then(|o| o.get("constructor", &mut context).ok()) - .as_ref() - .and_then(JsValue::as_object) - .and_then(|o| o.get("name", &mut context).ok()) - .as_ref() - .and_then(JsValue::as_string) - .map(|s| s == error_type.as_str()) - .unwrap_or_default() - }; - - (passed, format!("Uncaught {e}")) - } - }, - Err(e) => (false, e), + if let Err(e) = self.set_up_env(harness, &mut context, AsyncResult::default()) { + return (false, e); + } + let code = match Parser::new(test_content.as_bytes()) + .parse_all(&mut context) + .map_err(Into::into) + .and_then(|stmts| context.compile(&stmts)) + { + Ok(code) => code, + Err(e) => return (false, format!("Uncaught {e}")), + }; + + // TODO: timeout + let e = match context.execute(code) { + Ok(res) => return (false, res.display().to_string()), + Err(e) => e, + }; + if let Ok(e) = e.try_native(&mut context) { + match &e.kind { + JsNativeErrorKind::Syntax if error_type == ErrorType::SyntaxError => {} + JsNativeErrorKind::Reference + if error_type == ErrorType::ReferenceError => {} + JsNativeErrorKind::Range if error_type == ErrorType::RangeError => {} + JsNativeErrorKind::Type if error_type == ErrorType::TypeError => {} + _ => return (false, format!("Uncaught {e}")), } + (true, format!("Uncaught {e}")) + } else { + let passed = e + .as_opaque() + .expect("try_native cannot fail if e is not opaque") + .as_object() + .and_then(|o| o.get("constructor", &mut context).ok()) + .as_ref() + .and_then(JsValue::as_object) + .and_then(|o| o.get("name", &mut context).ok()) + .as_ref() + .and_then(JsValue::as_string) + .map(|s| s == error_type.as_str()) + .unwrap_or_default(); + (passed, format!("Uncaught {e}")) } } }); @@ -308,7 +296,7 @@ impl Test { if matches!(result, (TestOutcomeResult::Passed, _)) { ".".green() } else { - ".".red() + "F".red() } ); } @@ -323,7 +311,7 @@ impl Test { "Ignored".yellow() ); } else { - print!("{}", ".".yellow()); + print!("{}", "-".yellow()); } (TestOutcomeResult::Ignored, String::new()) }; @@ -351,10 +339,10 @@ impl Test { &self, harness: &Harness, context: &mut Context, - callback_obj: CallbackObject, + async_result: AsyncResult, ) -> Result<(), String> { // Register the print() function. - Self::register_print_fn(context, callback_obj); + Self::register_print_fn(context, async_result); // add the $262 object. let _js262 = js262::init(context); @@ -392,10 +380,10 @@ impl Test { } /// Registers the print function in the context. - fn register_print_fn(context: &mut Context, callback_object: CallbackObject) { + fn register_print_fn(context: &mut Context, async_result: AsyncResult) { // We use `FunctionBuilder` to define a closure with additional captures. let js_function = - FunctionBuilder::closure_with_captures(context, test262_print, callback_object) + FunctionBuilder::closure_with_captures(context, test262_print, async_result) .name("print") .length(1) .build(); @@ -409,9 +397,17 @@ impl Test { } /// Object which includes the result of the async operation. -#[derive(Debug, Clone, Default, Trace, Finalize)] -struct CallbackObject { - result: Gc>>, +#[derive(Debug, Clone, Trace, Finalize)] +struct AsyncResult { + inner: Gc>>, +} + +impl Default for AsyncResult { + fn default() -> Self { + Self { + inner: Gc::new(Cell::new(Ok(()))), + } + } } /// `print()` function required by the test262 suite. @@ -419,14 +415,15 @@ struct CallbackObject { fn test262_print( _this: &JsValue, args: &[JsValue], - captures: &mut CallbackObject, - _context: &mut Context, + async_result: &mut AsyncResult, + context: &mut Context, ) -> JsResult { - if let Some(message) = args.get_or_undefined(0).as_string() { - *captures.result.borrow_mut() = - Some(message.to_std_string_escaped() == "Test262:AsyncTestComplete"); - } else { - *captures.result.borrow_mut() = Some(false); + let message = args + .get_or_undefined(0) + .to_string(context)? + .to_std_string_escaped(); + if message != "Test262:AsyncTestComplete" { + *async_result.inner.borrow_mut() = Err(message); } Ok(JsValue::undefined()) } diff --git a/test_ignore.txt b/test_ignore.txt index c30babae78..523fbd39d2 100644 --- a/test_ignore.txt +++ b/test_ignore.txt @@ -13,6 +13,7 @@ feature:Atomics feature:dynamic_import feature:decorators feature:array-grouping +feature:IsHTMLDDA // Non-implemented Intl features feature:intl-normative-optional