Skip to content

Commit

Permalink
Fix async tests result values (#2406)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jedel1043 committed Nov 5, 2022
1 parent 91235c7 commit 73d23ea
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 83 deletions.
163 changes: 80 additions & 83 deletions boa_tester/src/exec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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}"))
}
}
});
Expand Down Expand Up @@ -308,7 +296,7 @@ impl Test {
if matches!(result, (TestOutcomeResult::Passed, _)) {
".".green()
} else {
".".red()
"F".red()
}
);
}
Expand All @@ -323,7 +311,7 @@ impl Test {
"Ignored".yellow()
);
} else {
print!("{}", ".".yellow());
print!("{}", "-".yellow());
}
(TestOutcomeResult::Ignored, String::new())
};
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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();
Expand All @@ -409,24 +397,33 @@ impl Test {
}

/// Object which includes the result of the async operation.
#[derive(Debug, Clone, Default, Trace, Finalize)]
struct CallbackObject {
result: Gc<Cell<Option<bool>>>,
#[derive(Debug, Clone, Trace, Finalize)]
struct AsyncResult {
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.
#[allow(clippy::unnecessary_wraps)]
fn test262_print(
_this: &JsValue,
args: &[JsValue],
captures: &mut CallbackObject,
_context: &mut Context,
async_result: &mut AsyncResult,
context: &mut Context,
) -> JsResult<JsValue> {
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())
}
1 change: 1 addition & 0 deletions test_ignore.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ feature:Atomics
feature:dynamic_import
feature:decorators
feature:array-grouping
feature:IsHTMLDDA

// Non-implemented Intl features
feature:intl-normative-optional
Expand Down

0 comments on commit 73d23ea

Please sign in to comment.