mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
fix(coverage): pin the V8 test262 status list to Node's V8 version (#27674)
monitor-oxc's Test262 job fails on every run because `cargo coverage
runtime` no longer matches `runtime.snap`. The runtime suite skips the
cases V8's `test262.status` lists as failing, and it fetches that file
from V8's `main` branch at run time. On 2026-10-09 V8 landed
`03af3c3548` ("[scopes] Reject var arguments in parameter eval") and
removed the 88 `language/eval-code/direct/*declare-arguments*` entries.
Node 26.5 bundles V8 14.6.202, which predates that fix, so those 88
cases now run and fail with `Expected a SyntaxError to be thrown`.
The skip list exists to hide bugs of the engine that executes the cases,
and that engine is Node's V8, not V8 `main`. `main` is wrong in both
directions: it un-skips cases V8 fixed after Node's branch cut, and it
skips 236 entries for test262 files newer than the pinned checkout.
The list is now fetched at the V8 version reported by `node -p
process.versions.v8` (`14.6.202.34`, which V8 tags on GitHub), falling
back to `main` only when that tag does not exist. The gitignored cache
file carries the version in its name so a Node bump cannot serve a stale
list.
With the pinned list the snapshot only changes when the test262 pin or
`.node-version` moves. Regenerated against Node 26.5.0 it adds nothing
and drops: four `delete/super-property-uninitialized-this` cases that V8
fixed upstream after 14.6, the `typeof/symbol.js` timeout, and the two
`bigint-and-number-extremes` minify cases that #27656 fixed (the regular
CI does not run the runtime suite, so that PR could not update the
snapshot):
| skip list | cases | passed | new failures |
| --- | --- | --- | --- |
| V8 `main` today | 18369 | 17307 | 88 |
| V8 `14.6.202.34` | 18277 | 17306 | 0 |
Written with Claude Code; reviewed and tested by me.
This commit is contained in:
1 parent
6b5057bb1d
commit
b60f8a7c14
3 files changed
+56
-40
No files matched your search
@@ -1,8 +1,8 @@
|
||||
commit: 7ab7fafa
|
||||
|
||||
runtime Summary:
|
||||
AST Parsed : 18281/18281 (100.00%)
|
||||
Positive Passed: 17306/18281 (94.67%)
|
||||
AST Parsed : 18277/18277 (100.00%)
|
||||
Positive Passed: 17306/18277 (94.69%)
|
||||
minify Error: tasks/coverage/test262/test/language/computed-property-names/class/static/generator-prototype.js
|
||||
Test262Error: Expected a TypeError to be thrown but no exception was thrown at all
|
||||
|
||||
@@ -1074,18 +1074,9 @@ Test262Error: The expression should evaluate to the result Expected SameValue(«
|
||||
transform Error: tasks/coverage/test262/test/language/expressions/compound-assignment/left-hand-side-private-reference-accessor-property-sub.js
|
||||
Test262Error: The expression should evaluate to the result Expected SameValue(«undefined», «1») to be true
|
||||
|
||||
codegen Error: tasks/coverage/test262/test/language/expressions/delete/super-property-uninitialized-this.js
|
||||
Test262Error: Expected a ReferenceError but got a Test262Error
|
||||
|
||||
minify Error: tasks/coverage/test262/test/language/expressions/does-not-equals/S11.9.2_A7.8.js
|
||||
Test262Error: #7: (1 != {valueOf: function() {throw "error"}, toString: function() {return 1}}) throw "error"
|
||||
|
||||
minify Error: tasks/coverage/test262/test/language/expressions/does-not-equals/bigint-and-number-extremes.js
|
||||
Test262Error: The result of (0xfffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n != Number.MAX_VALUE) is false Expected SameValue(«true», «false») to be true
|
||||
|
||||
minify Error: tasks/coverage/test262/test/language/expressions/equals/bigint-and-number-extremes.js
|
||||
Test262Error: The result of (0xfffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n == Number.MAX_VALUE) is true Expected SameValue(«false», «true») to be true
|
||||
|
||||
minify Error: tasks/coverage/test262/test/language/expressions/equals/coerce-symbol-to-prim-err.js
|
||||
Test262Error: Expected a Test262Error to be thrown but no exception was thrown at all
|
||||
|
||||
@@ -1377,9 +1368,6 @@ TypeError: Cannot read properties of undefined (reading 'enumerable')
|
||||
minify Error: tasks/coverage/test262/test/language/expressions/optional-chaining/member-expression.js
|
||||
Test262Error: Expected SameValue(«"a"», «""») to be true
|
||||
|
||||
transform Error: tasks/coverage/test262/test/language/expressions/typeof/symbol.js
|
||||
timeout: global
|
||||
|
||||
minify Error: tasks/coverage/test262/test/language/function-code/block-decl-onlystrict.js
|
||||
TypeError: Cannot read properties of undefined (reading 'constructor')
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
v8_test262.status
|
||||
v8_test262*.status
|
||||
@@ -1,40 +1,68 @@
|
||||
use std::{fs, sync::OnceLock};
|
||||
use std::{fs, process::Command, sync::OnceLock};
|
||||
|
||||
use lazy_regex::Regex;
|
||||
use oxc_tasks_common::agent;
|
||||
|
||||
use crate::workspace_root;
|
||||
|
||||
/// Generate v8 test262 status file, which is used to skip failed tests
|
||||
/// see <https://chromium.googlesource.com/v8/v8/+/refs/heads/main/test/test262/test262.status>
|
||||
/// Cases V8 itself expects to fail, skipped so the runtime suite reports oxc's
|
||||
/// failures rather than the engine's.
|
||||
///
|
||||
/// The list is V8's `test262.status` at the V8 version bundled in the `node` on
|
||||
/// `PATH`, since that engine executes the cases. V8's `main` branch is wrong in
|
||||
/// both directions: it un-skips cases V8 fixed after Node's branch cut, and it
|
||||
/// skips cases for test262 files newer than the pinned checkout.
|
||||
///
|
||||
/// The parsed list is cached per V8 version in a gitignored file.
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if `node` cannot be run or the status file cannot be fetched.
|
||||
pub fn get_v8_test262_failure_paths() -> &'static Vec<String> {
|
||||
static STATUS: OnceLock<Vec<String>> = OnceLock::new();
|
||||
STATUS.get_or_init(|| {
|
||||
let path = workspace_root().join("src/runtime/v8_test262.status");
|
||||
|
||||
let version = node_v8_version();
|
||||
let path = workspace_root().join(format!("src/runtime/v8_test262_{version}.status"));
|
||||
if path.exists() {
|
||||
fs::read_to_string(&path).unwrap().lines().map(ToString::to_string).collect::<Vec<_>>()
|
||||
} else {
|
||||
let res = agent()
|
||||
.get("http://raw.githubusercontent.com/v8/v8/main/test/test262/test262.status")
|
||||
.call()
|
||||
.unwrap()
|
||||
.body_mut()
|
||||
.read_to_string()
|
||||
.unwrap();
|
||||
let mut tests = Regex::new(r"'(.+)': \[(FAIL|SKIP)\]")
|
||||
.unwrap()
|
||||
.captures_iter(&res)
|
||||
.filter_map(|capture| capture.get(1))
|
||||
.filter(|m| m.as_str() != "*")
|
||||
.map(|m| m.as_str().to_string())
|
||||
.collect::<Vec<_>>();
|
||||
tests.sort_unstable();
|
||||
tests.dedup();
|
||||
fs::write(path, tests.join("\n")).unwrap();
|
||||
tests
|
||||
return fs::read_to_string(&path).unwrap().lines().map(ToString::to_string).collect();
|
||||
}
|
||||
|
||||
let status = fetch_status(&version);
|
||||
let mut tests = Regex::new(r"'(.+)': \[(FAIL|SKIP)\]")
|
||||
.unwrap()
|
||||
.captures_iter(&status)
|
||||
.filter_map(|capture| capture.get(1))
|
||||
.filter(|m| m.as_str() != "*")
|
||||
.map(|m| m.as_str().to_string())
|
||||
.collect::<Vec<_>>();
|
||||
tests.sort_unstable();
|
||||
tests.dedup();
|
||||
fs::write(path, tests.join("\n")).unwrap();
|
||||
tests
|
||||
})
|
||||
}
|
||||
|
||||
/// `process.versions.v8` without Node's `-node.N` suffix, e.g. `14.6.202.34`.
|
||||
fn node_v8_version() -> String {
|
||||
let output = Command::new("node")
|
||||
.args(["-p", "process.versions.v8"])
|
||||
.output()
|
||||
.expect("Failed to run node - ensure Node.js is installed");
|
||||
assert!(output.status.success(), "`node -p process.versions.v8` failed");
|
||||
let version = String::from_utf8(output.stdout).unwrap();
|
||||
version.trim().split('-').next().unwrap().to_string()
|
||||
}
|
||||
|
||||
/// V8 tags every release, so the file at `version` is the list that shipped
|
||||
/// with Node's engine. A version without a tag falls back to `main`.
|
||||
fn fetch_status(version: &str) -> String {
|
||||
let url = |git_ref: &str| {
|
||||
format!("https://raw.githubusercontent.com/v8/v8/{git_ref}/test/test262/test262.status")
|
||||
};
|
||||
match agent().get(&url(version)).call() {
|
||||
Ok(mut res) => res.body_mut().read_to_string().unwrap(),
|
||||
Err(err) => {
|
||||
println!("V8 test262.status for {version} is unavailable ({err}), using main instead");
|
||||
agent().get(&url("main")).call().unwrap().body_mut().read_to_string().unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user