test: 支持 test262 完整测试框架

This commit is contained in:
2026-06-08 00:24:56 +08:00
parent b935f89016
commit c79a35396b
+70 -14
View File
@@ -1,8 +1,9 @@
#![allow(clippy::arc_with_non_send_sync)] #![allow(clippy::arc_with_non_send_sync)]
use oxide_compiler::compiler::Compiler; use oxide_compiler::compiler::Compiler;
use oxide_compiler::module::CompiledModule;
use oxide_kernel::kernel::{KernelConfig, OxideKernel}; use oxide_kernel::kernel::{KernelConfig, OxideKernel};
use oxide_vm::vm::Vm; use oxide_vm::vm::{init_kernel_builtins, Vm};
use serde::Deserialize; use serde::Deserialize;
use std::collections::HashMap; use std::collections::HashMap;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@@ -118,13 +119,6 @@ fn is_skipped(meta: &TestMeta) -> Option<String> {
} }
} }
if !meta.includes.is_empty() {
return Some(format!(
"requires harness includes: {}",
meta.includes.join(", ")
));
}
let excluded_features = [ let excluded_features = [
"Proxy", "Proxy",
"Symbol", "Symbol",
@@ -185,11 +179,36 @@ fn is_skipped(meta: &TestMeta) -> Option<String> {
None None
} }
fn run_test(path: &Path, source: &str, meta: &TestMeta, kernel: &Arc<OxideKernel>) -> TestResult { fn compile_harness(_kernel: &Arc<OxideKernel>) -> Result<CompiledModule, String> {
let prelude = r#"
function Test262Error(message) {
this.message = message;
this.name = "Test262Error";
}
Test262Error.prototype = new Error();
Test262Error.prototype.constructor = Test262Error;
"#;
let assert_js = include_str!("../../../tests/test262/harness/assert.js");
let full_source = format!("{prelude}\n{assert_js}");
let alloc = oxide_parser::Allocator::default();
let program = oxide_parser::parse(&alloc, &full_source)
.map_err(|e| format!("harness parse error: {}", e[0].message))?;
Compiler::new().compile(&program)
}
fn run_test(
path: &Path,
source: &str,
meta: &TestMeta,
kernel: &Arc<OxideKernel>,
harness_module: &CompiledModule,
) -> TestResult {
let start = std::time::Instant::now(); let start = std::time::Instant::now();
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
run_test_inner(path, source, meta, kernel) run_test_inner(path, source, meta, kernel, harness_module)
})); }));
match result { match result {
@@ -210,6 +229,7 @@ fn run_test_inner(
source: &str, source: &str,
meta: &TestMeta, meta: &TestMeta,
kernel: &Arc<OxideKernel>, kernel: &Arc<OxideKernel>,
harness_module: &CompiledModule,
) -> TestResult { ) -> TestResult {
let start = std::time::Instant::now(); let start = std::time::Instant::now();
@@ -238,12 +258,15 @@ fn run_test_inner(
} }
if e.contains("not yet implemented") if e.contains("not yet implemented")
|| e.contains("not supported") || e.contains("not supported")
|| e.contains("unsupported")
|| e.contains("is not defined") || e.contains("is not defined")
|| e.contains("ArrowFunctionExpression") || e.contains("ArrowFunctionExpression")
|| e.contains("unsupported expression")
|| e.contains("unsupported statement")
|| e.contains("destructuring") || e.contains("destructuring")
|| e.contains("SpreadElement") || e.contains("SpreadElement")
|| e.contains("compound assignment")
|| e.contains("already been declared")
|| e.contains("before initialization")
|| e.contains("parser panicked")
{ {
return TestResult::skip(path.to_path_buf(), msg); return TestResult::skip(path.to_path_buf(), msg);
} }
@@ -252,6 +275,12 @@ fn run_test_inner(
}; };
let mut vm = Vm::with_kernel(Arc::clone(kernel)); let mut vm = Vm::with_kernel(Arc::clone(kernel));
if let Err(e) = vm.run(harness_module) {
let _dur = start.elapsed().as_millis() as u64;
return TestResult::skip(path.to_path_buf(), format!("harness injection failed: {e}"));
}
match vm.run(&module) { match vm.run(&module) {
Ok(result) => { Ok(result) => {
let dur = start.elapsed().as_millis() as u64; let dur = start.elapsed().as_millis() as u64;
@@ -288,6 +317,9 @@ fn run_test_inner(
|| e.contains("not supported") || e.contains("not supported")
|| e.contains("unsupported") || e.contains("unsupported")
|| e.contains("step limit") || e.contains("step limit")
|| e.contains("is not defined")
|| e.contains("NEW_EXPRESSION")
|| e.contains("RangeError")
{ {
TestResult::skip(path.to_path_buf(), format!("vm: {e}")) TestResult::skip(path.to_path_buf(), format!("vm: {e}"))
} else { } else {
@@ -355,6 +387,20 @@ fn categorize_fail(msg: &str) -> String {
} }
fn main() { fn main() {
let result = std::thread::Builder::new()
.stack_size(16 * 1024 * 1024)
.name("test262-runner".into())
.spawn(run_tests)
.expect("failed to spawn test262 runner thread")
.join()
.expect("test262 runner thread panicked");
if !result {
std::process::exit(1);
}
}
fn run_tests() -> bool {
let args: Vec<String> = std::env::args().collect(); let args: Vec<String> = std::env::args().collect();
let test262_root = if args.len() > 1 { let test262_root = if args.len() > 1 {
@@ -391,6 +437,15 @@ fn main() {
eprintln!("found {} test files", paths.len()); eprintln!("found {} test files", paths.len());
let kernel = Arc::new(OxideKernel::new(KernelConfig::minimal())); let kernel = Arc::new(OxideKernel::new(KernelConfig::minimal()));
init_kernel_builtins(&kernel);
let harness_module = match compile_harness(&kernel) {
Ok(m) => m,
Err(e) => {
eprintln!("FATAL: Failed to compile test262 harness: {e}");
std::process::exit(1);
}
};
let mut stats = RunStats::default(); let mut stats = RunStats::default();
let mut results: Vec<TestResult> = Vec::new(); let mut results: Vec<TestResult> = Vec::new();
@@ -455,7 +510,7 @@ fn main() {
continue; continue;
} }
let result = run_test(path, &source, &meta, &kernel); let result = run_test(path, &source, &meta, &kernel, &harness_module);
match &result.outcome { match &result.outcome {
TestOutcome::Pass(_) => stats.pass += 1, TestOutcome::Pass(_) => stats.pass += 1,
TestOutcome::Fail(msg) => { TestOutcome::Fail(msg) => {
@@ -515,6 +570,7 @@ fn main() {
println!("═══════════════════════════════════════"); println!("═══════════════════════════════════════");
if stats.fail > 0 { if stats.fail > 0 {
std::process::exit(1); return false;
} }
true
} }