diff --git a/tasks/coverage/snapshots/estree_acorn_jsx.snap b/tasks/coverage/snapshots/estree_acorn_jsx.snap index a4813d5694de1..99d94764547a0 100644 --- a/tasks/coverage/snapshots/estree_acorn_jsx.snap +++ b/tasks/coverage/snapshots/estree_acorn_jsx.snap @@ -1,4 +1,4 @@ -commit: 389cb2a7 +commit: a53e1b22 estree_acorn_jsx Summary: AST Parsed : 39/39 (100.00%) diff --git a/tasks/coverage/src/lib.rs b/tasks/coverage/src/lib.rs index c596ef6327587..3eec2dc66d37d 100644 --- a/tasks/coverage/src/lib.rs +++ b/tasks/coverage/src/lib.rs @@ -69,11 +69,18 @@ pub struct MiscFile { pub should_fail: bool, } +pub struct AcornJsxFile { + pub path: PathBuf, + pub code: String, + pub should_fail: bool, +} + pub struct TestData { pub test262: Vec, pub babel: Vec, pub typescript: Vec, pub misc: Vec, + pub acorn_jsx: Vec, } // ================================ @@ -298,6 +305,7 @@ const TEST262_PATH: &str = "test262/test"; const BABEL_PATH: &str = "babel/packages/babel-parser/test/fixtures"; const TYPESCRIPT_PATH: &str = "typescript/tests/cases"; const MISC_PATH: &str = "misc"; +const ESTREE_ACORN_JSX_PATH: &str = "estree-conformance/tests/acorn-jsx"; impl AppArgs { pub fn run_all(&self) { @@ -407,6 +415,12 @@ impl AppArgs { pub fn run_estree(&self, data: &TestData) { self.run_tool("estree_test262", TEST262_PATH, &data.test262, tools::run_estree_test262); + self.run_tool( + "estree_acorn_jsx", + ESTREE_ACORN_JSX_PATH, + &data.acorn_jsx, + tools::run_estree_acorn_jsx, + ); self.run_tool( "estree_typescript", TYPESCRIPT_PATH, diff --git a/tasks/coverage/src/load.rs b/tasks/coverage/src/load.rs index 844ef297af2ab..710665e1f1c09 100644 --- a/tasks/coverage/src/load.rs +++ b/tasks/coverage/src/load.rs @@ -14,17 +14,22 @@ use rayon::prelude::*; use walkdir::WalkDir; use crate::{ - BabelFile, MiscFile, Test262File, TestData, TypeScriptFile, babel, test262, typescript, - workspace_root, + AcornJsxFile, BabelFile, MiscFile, Test262File, TestData, TypeScriptFile, babel, test262, + typescript, workspace_root, }; impl TestData { pub fn load(filter: Option<&str>) -> Self { - let ((test262, babel), (typescript, misc)) = rayon::join( + let ((test262, babel), (typescript, (misc, acorn_jsx))) = rayon::join( || rayon::join(|| load_test262(filter), || load_babel(filter)), - || rayon::join(|| load_typescript(filter), || load_misc(filter)), + || { + rayon::join( + || load_typescript(filter), + || rayon::join(|| load_misc(filter), || load_acorn_jsx(filter)), + ) + }, ); - Self { test262, babel, typescript, misc } + Self { test262, babel, typescript, misc, acorn_jsx } } } @@ -256,3 +261,16 @@ fn load_misc(filter: Option<&str>) -> Vec { files } + +fn load_acorn_jsx(filter: Option<&str>) -> Vec { + let skip_path = |path: &Path| path.extension().is_none_or(|ext| ext != "jsx"); + + walk_and_read(Path::new("estree-conformance/tests/acorn-jsx"), filter, skip_path) + .into_par_iter() + .map(|(path, code)| { + let should_fail = + path.parent().and_then(Path::file_name).is_some_and(|name| name == "fail"); + AcornJsxFile { path, code, should_fail } + }) + .collect() +} diff --git a/tasks/coverage/src/tools.rs b/tasks/coverage/src/tools.rs index be31cc0d7d043..1d48e82f919e6 100644 --- a/tasks/coverage/src/tools.rs +++ b/tasks/coverage/src/tools.rs @@ -1,6 +1,6 @@ //! Tool runner functions for coverage testing -use std::{borrow::Cow, path::Path, sync::Arc}; +use std::{borrow::Cow, fs, path::Path, sync::Arc}; use oxc::{ allocator::Allocator, @@ -19,8 +19,9 @@ use oxc_formatter::{ use rayon::prelude::*; use crate::{ - BabelFile, CoverageResult, Driver, MiscFile, Test262File, TestResult, TypeScriptFile, - test262::TestFlag, typescript::constants::TS_IGNORE_SUPPRESSIBLE_ERRORS, workspace_root, + AcornJsxFile, BabelFile, CoverageResult, Driver, MiscFile, Test262File, TestResult, + TypeScriptFile, test262::TestFlag, typescript::constants::TS_IGNORE_SUPPRESSIBLE_ERRORS, + workspace_root, }; // ================================ @@ -699,8 +700,6 @@ pub fn run_minifier_babel(files: &[BabelFile]) -> Vec { // ESTree // ================================ -use std::fs; - pub fn run_estree_test262(files: &[Test262File]) -> Vec { files .par_iter() @@ -714,7 +713,7 @@ pub fn run_estree_test262(files: &[Test262File]) -> Vec { if f.path.starts_with("test262/test/language/comments/hashbang/") { return false; } - // Check if acorn json exists + // Skip tests where no Acorn JSON file let acorn_path = workspace_root() .join("estree-conformance/tests") .join(&f.path) @@ -758,8 +757,66 @@ pub fn run_estree_test262(files: &[Test262File]) -> Vec { .collect() } +pub fn run_estree_acorn_jsx(files: &[AcornJsxFile]) -> Vec { + files + .par_iter() + .map(|f| { + let source_type = SourceType::default().with_module(true).with_jsx(true); + let allocator = Allocator::new(); + let ret = Parser::new(&allocator, &f.code, source_type).parse(); + let is_parse_error = ret.panicked || !ret.errors.is_empty(); + + if is_parse_error { + let error = + ret.errors.first().map_or_else(|| "Panicked".to_string(), ToString::to_string); + let result = if f.should_fail { + TestResult::CorrectError(error, ret.panicked) + } else { + TestResult::ParseError(error, ret.panicked) + }; + return CoverageResult { path: f.path.clone(), should_fail: f.should_fail, result }; + } + + if f.should_fail { + return CoverageResult { + path: f.path.clone(), + should_fail: true, + result: TestResult::IncorrectlyPassed, + }; + } + + let mut program = ret.program; + Utf8ToUtf16::new(&f.code).convert_program_with_ascending_order_checks(&mut program); + + let acorn_json_path = workspace_root().join(&f.path).with_extension("json"); + let acorn_json = match fs::read_to_string(&acorn_json_path) { + Ok(acorn_json) => acorn_json, + Err(error) => { + return CoverageResult { + path: f.path.clone(), + should_fail: false, + result: TestResult::GenericError( + "Error reading Acorn JSON", + error.to_string(), + ), + }; + } + }; + let oxc_json = program.to_pretty_estree_js_json(false); + + let result = if oxc_json == acorn_json { + TestResult::Passed + } else { + TestResult::Mismatch("Mismatch", oxc_json, acorn_json) + }; + + CoverageResult { path: f.path.clone(), should_fail: false, result } + }) + .collect() +} + pub fn run_estree_typescript(files: &[TypeScriptFile]) -> Vec { - // Skip paths for TypeScript estree tests + // Skip paths for TypeScript ESTree tests const SKIP_PATHS: &[&str] = &[ // Skip cases which are failing in parser conformance tests "typescript/tests/cases/compiler/arrayFromAsync.ts", @@ -788,7 +845,7 @@ pub fn run_estree_typescript(files: &[TypeScriptFile]) -> Vec { if f.path.to_str().is_some_and(|p| SKIP_PATHS.contains(&p)) { return false; } - // Check if estree file exists + // Skip tests where no expected ESTree file exists let ext = f.path.extension().and_then(|e| e.to_str()).unwrap_or(""); let estree_path = workspace_root() .join("estree-conformance/tests")