Browse Source

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.
pull/2410/head
José Julián Espina 2 years ago
parent
commit
73d23ead7f
  1. 163
      boa_tester/src/exec/mod.rs
  2. 1
      test_ignore.txt

163
boa_tester/src/exec/mod.rs

@ -15,7 +15,6 @@ use boa_engine::{
use boa_gc::{Cell, Finalize, Gc, Trace}; use boa_gc::{Cell, Finalize, Gc, Trace};
use colored::Colorize; use colored::Colorize;
use rayon::prelude::*; use rayon::prelude::*;
use std::panic;
impl TestSuite { impl TestSuite {
/// Runs the test suite. /// Runs the test suite.
@ -169,27 +168,26 @@ impl Test {
error_type: _, error_type: _,
} }
)) { )) {
let res = panic::catch_unwind(|| match self.expected_outcome { let res = std::panic::catch_unwind(|| match self.expected_outcome {
Outcome::Positive => { Outcome::Positive => {
let mut context = Context::default(); 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 // TODO: timeout
match self.set_up_env(harness, &mut context, callback_obj.clone()) { let value = match context.eval(&test_content) {
Ok(_) => { Ok(v) => v,
let res = context.eval(&test_content); Err(e) => return (false, format!("Uncaught {e}")),
};
let passed = res.is_ok()
&& matches!(*callback_obj.result.borrow(), Some(true) | None); if let Err(e) = async_result.inner.borrow().as_ref() {
let text = match res { return (false, format!("Uncaught {e}"));
Ok(val) => val.display().to_string(),
Err(e) => format!("Uncaught {e}",),
};
(passed, text)
}
Err(e) => (false, e),
} }
(true, value.display().to_string())
} }
Outcome::Negative { Outcome::Negative {
phase: Phase::Parse | Phase::Early, phase: Phase::Parse | Phase::Early,
@ -220,57 +218,47 @@ impl Test {
error_type, error_type,
} => { } => {
let mut context = Context::default(); let mut context = Context::default();
if let Err(e) = Parser::new(test_content.as_bytes()).parse_all(&mut context) { if let Err(e) = self.set_up_env(harness, &mut context, AsyncResult::default()) {
(false, format!("Uncaught {e}")) return (false, e);
} else { }
// TODO: timeout let code = match Parser::new(test_content.as_bytes())
match self.set_up_env(harness, &mut context, CallbackObject::default()) { .parse_all(&mut context)
Ok(_) => match context.eval(&test_content) { .map_err(Into::into)
Ok(res) => (false, res.display().to_string()), .and_then(|stmts| context.compile(&stmts))
Err(e) => { {
let passed = if let Ok(e) = e.try_native(&mut context) { Ok(code) => code,
match &e.kind { Err(e) => return (false, format!("Uncaught {e}")),
JsNativeErrorKind::Syntax };
if error_type == ErrorType::SyntaxError =>
{ // TODO: timeout
true let e = match context.execute(code) {
} Ok(res) => return (false, res.display().to_string()),
JsNativeErrorKind::Reference Err(e) => e,
if error_type == ErrorType::ReferenceError => };
{ if let Ok(e) = e.try_native(&mut context) {
true match &e.kind {
} JsNativeErrorKind::Syntax if error_type == ErrorType::SyntaxError => {}
JsNativeErrorKind::Range JsNativeErrorKind::Reference
if error_type == ErrorType::RangeError => if error_type == ErrorType::ReferenceError => {}
{ JsNativeErrorKind::Range if error_type == ErrorType::RangeError => {}
true JsNativeErrorKind::Type if error_type == ErrorType::TypeError => {}
} _ => return (false, format!("Uncaught {e}")),
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),
} }
(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, _)) { if matches!(result, (TestOutcomeResult::Passed, _)) {
".".green() ".".green()
} else { } else {
".".red() "F".red()
} }
); );
} }
@ -323,7 +311,7 @@ impl Test {
"Ignored".yellow() "Ignored".yellow()
); );
} else { } else {
print!("{}", ".".yellow()); print!("{}", "-".yellow());
} }
(TestOutcomeResult::Ignored, String::new()) (TestOutcomeResult::Ignored, String::new())
}; };
@ -351,10 +339,10 @@ impl Test {
&self, &self,
harness: &Harness, harness: &Harness,
context: &mut Context, context: &mut Context,
callback_obj: CallbackObject, async_result: AsyncResult,
) -> Result<(), String> { ) -> Result<(), String> {
// Register the print() function. // Register the print() function.
Self::register_print_fn(context, callback_obj); Self::register_print_fn(context, async_result);
// add the $262 object. // add the $262 object.
let _js262 = js262::init(context); let _js262 = js262::init(context);
@ -392,10 +380,10 @@ impl Test {
} }
/// Registers the print function in the context. /// 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. // We use `FunctionBuilder` to define a closure with additional captures.
let js_function = let js_function =
FunctionBuilder::closure_with_captures(context, test262_print, callback_object) FunctionBuilder::closure_with_captures(context, test262_print, async_result)
.name("print") .name("print")
.length(1) .length(1)
.build(); .build();
@ -409,9 +397,17 @@ impl Test {
} }
/// Object which includes the result of the async operation. /// Object which includes the result of the async operation.
#[derive(Debug, Clone, Default, Trace, Finalize)] #[derive(Debug, Clone, Trace, Finalize)]
struct CallbackObject { struct AsyncResult {
result: Gc<Cell<Option<bool>>>, inner: Gc<Cell<Result<(), String>>>,
}
impl Default for AsyncResult {
fn default() -> Self {
Self {
inner: Gc::new(Cell::new(Ok(()))),
}
}
} }
/// `print()` function required by the test262 suite. /// `print()` function required by the test262 suite.
@ -419,14 +415,15 @@ struct CallbackObject {
fn test262_print( fn test262_print(
_this: &JsValue, _this: &JsValue,
args: &[JsValue], args: &[JsValue],
captures: &mut CallbackObject, async_result: &mut AsyncResult,
_context: &mut Context, context: &mut Context,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
if let Some(message) = args.get_or_undefined(0).as_string() { let message = args
*captures.result.borrow_mut() = .get_or_undefined(0)
Some(message.to_std_string_escaped() == "Test262:AsyncTestComplete"); .to_string(context)?
} else { .to_std_string_escaped();
*captures.result.borrow_mut() = Some(false); if message != "Test262:AsyncTestComplete" {
*async_result.inner.borrow_mut() = Err(message);
} }
Ok(JsValue::undefined()) Ok(JsValue::undefined())
} }

1
test_ignore.txt

@ -13,6 +13,7 @@ feature:Atomics
feature:dynamic_import feature:dynamic_import
feature:decorators feature:decorators
feature:array-grouping feature:array-grouping
feature:IsHTMLDDA
// Non-implemented Intl features // Non-implemented Intl features
feature:intl-normative-optional feature:intl-normative-optional

Loading…
Cancel
Save