mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
feat: CLI/test262 全管线接入结构化日志
This commit is contained in:
Generated
+2
@@ -1084,6 +1084,7 @@ dependencies = [
|
||||
"oxide_code_cache",
|
||||
"oxide_compiler",
|
||||
"oxide_kernel",
|
||||
"oxide_log",
|
||||
"oxide_parser",
|
||||
"oxide_types",
|
||||
"oxide_vm",
|
||||
@@ -1166,6 +1167,7 @@ dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_compiler",
|
||||
"oxide_kernel",
|
||||
"oxide_log",
|
||||
"oxide_parser",
|
||||
"oxide_vm",
|
||||
"serde",
|
||||
|
||||
@@ -19,6 +19,7 @@ oxide_compiler = { path = "../oxide_compiler" }
|
||||
oxide_vm = { path = "../oxide_vm" }
|
||||
oxide_kernel = { path = "../oxide_kernel" }
|
||||
oxide_api = { path = "../oxide_api" }
|
||||
oxide_log = { path = "../oxide_log" }
|
||||
clap.workspace = true
|
||||
rustyline.workspace = true
|
||||
ansi_term.workspace = true
|
||||
|
||||
@@ -8,11 +8,15 @@ use std::sync::Arc;
|
||||
use ansi_term::Colour::Red;
|
||||
use clap::{Parser, Subcommand};
|
||||
use oxide_compiler::compiler::{compiled_module_hash, Compiler};
|
||||
use oxide_compiler::compiler_error;
|
||||
use oxide_kernel::kernel::{KernelConfig, KernelCore};
|
||||
use oxide_kernel::shape_forge::{ShapeForge, EMPTY_SHAPE_ID};
|
||||
use oxide_kernel::string_forge::PermInterner;
|
||||
use oxide_kernel::{kernel_error, kernel_info};
|
||||
use oxide_log::{Level, SUBSYSTEM_COUNT};
|
||||
use oxide_parser::Allocator;
|
||||
use oxide_types::object::JsObject;
|
||||
use oxide_vm::vm_error;
|
||||
use oxide_vm::vm_pool::VmPool;
|
||||
use oxide_vm::JsValue;
|
||||
|
||||
@@ -70,12 +74,12 @@ fn main() -> ExitCode {
|
||||
|
||||
match cli.command {
|
||||
Some(Commands::Eval { code }) => {
|
||||
let kernel = make_kernel();
|
||||
let kernel = make_kernel(cli.verbose, cli.quiet);
|
||||
let pool = make_pool(&kernel);
|
||||
eval(&code, &kernel, &pool)
|
||||
}
|
||||
Some(Commands::Run { file }) => {
|
||||
let kernel = make_kernel();
|
||||
let kernel = make_kernel(cli.verbose, cli.quiet);
|
||||
let pool = make_pool(&kernel);
|
||||
run(&file, &kernel, &pool)
|
||||
}
|
||||
@@ -89,7 +93,7 @@ fn main() -> ExitCode {
|
||||
update_baseline,
|
||||
leak_check_interval,
|
||||
}) => {
|
||||
let kernel = make_kernel();
|
||||
let kernel = make_kernel(false, false);
|
||||
let pool = make_pool(&kernel);
|
||||
let config = bench::BenchConfig {
|
||||
mode: mode.unwrap_or_else(|| "js".to_string()),
|
||||
@@ -107,8 +111,16 @@ fn main() -> ExitCode {
|
||||
}
|
||||
}
|
||||
|
||||
fn make_kernel() -> Arc<KernelCore> {
|
||||
KernelCore::new(KernelConfig::standard())
|
||||
fn make_kernel(verbose: bool, quiet: bool) -> Arc<KernelCore> {
|
||||
let mut config = KernelConfig::standard();
|
||||
if verbose {
|
||||
config.log_levels = [Level::Info; SUBSYSTEM_COUNT];
|
||||
} else if quiet {
|
||||
config.log_levels = [Level::Off; SUBSYSTEM_COUNT];
|
||||
} else {
|
||||
config.log_levels = [Level::Error; SUBSYSTEM_COUNT];
|
||||
}
|
||||
KernelCore::new(config)
|
||||
}
|
||||
|
||||
fn make_pool(kernel: &Arc<KernelCore>) -> Arc<VmPool> {
|
||||
@@ -121,6 +133,7 @@ fn eval(code: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>) -> ExitCode {
|
||||
Ok(p) => p,
|
||||
Err(errors) => {
|
||||
for err in &errors {
|
||||
compiler_error!("parse error: {}", err);
|
||||
eprintln!("{}", Red.paint(err.to_string()));
|
||||
}
|
||||
return ExitCode::FAILURE;
|
||||
@@ -132,6 +145,7 @@ fn eval(code: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>) -> ExitCode {
|
||||
let module = match kernel.code_forge().get_or_insert_with(hash, || compiler.compile(&program)) {
|
||||
Ok(m) => m,
|
||||
Err(err) => {
|
||||
compiler_error!("compile error: {}", err);
|
||||
eprintln!("{}", Red.paint(err));
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
@@ -144,6 +158,7 @@ fn eval(code: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>) -> ExitCode {
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
Err(err) => {
|
||||
vm_error!("runtime error: {}", err);
|
||||
eprintln!("{}", Red.paint(err));
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
@@ -226,6 +241,7 @@ fn run(file: &str, kernel: &Arc<KernelCore>, pool: &Arc<VmPool>) -> ExitCode {
|
||||
match fs::read_to_string(file) {
|
||||
Ok(source) => eval(&source, kernel, pool),
|
||||
Err(err) => {
|
||||
kernel_error!("cannot read {}: {}", file, err);
|
||||
eprintln!("{}", Red.paint(format!("Cannot read {file}: {err}")));
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
@@ -239,11 +255,13 @@ fn compile(expr: Option<String>, file: Option<String>) -> ExitCode {
|
||||
match fs::read_to_string(&path) {
|
||||
Ok(s) => s,
|
||||
Err(err) => {
|
||||
kernel_error!("cannot read {}: {}", path, err);
|
||||
eprintln!("{}", Red.paint(format!("Cannot read {path}: {err}")));
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
kernel_error!("compile requires -e '<code>' or a file path");
|
||||
eprintln!("{}", Red.paint("compile requires -e '<code>' or a file path"));
|
||||
return ExitCode::FAILURE;
|
||||
};
|
||||
@@ -253,6 +271,7 @@ fn compile(expr: Option<String>, file: Option<String>) -> ExitCode {
|
||||
Ok(p) => p,
|
||||
Err(errors) => {
|
||||
for err in &errors {
|
||||
compiler_error!("parse error: {}", err);
|
||||
eprintln!("{}", Red.paint(err.to_string()));
|
||||
}
|
||||
return ExitCode::FAILURE;
|
||||
@@ -266,6 +285,7 @@ fn compile(expr: Option<String>, file: Option<String>) -> ExitCode {
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
Err(err) => {
|
||||
compiler_error!("compile error: {}", err);
|
||||
eprintln!("{}", Red.paint(err));
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
@@ -291,12 +311,13 @@ fn repl() -> ExitCode {
|
||||
let mut rl = match DefaultEditor::new() {
|
||||
Ok(editor) => editor,
|
||||
Err(err) => {
|
||||
kernel_error!("failed to start REPL: {}", err);
|
||||
eprintln!("{}", Red.paint(format!("Failed to start REPL: {err}")));
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
};
|
||||
|
||||
let kernel = make_kernel();
|
||||
let kernel = make_kernel(false, false);
|
||||
let pool = make_pool(&kernel);
|
||||
let mut source = String::new();
|
||||
let mut input_buf = String::new();
|
||||
@@ -347,6 +368,7 @@ fn repl() -> ExitCode {
|
||||
return ExitCode::SUCCESS;
|
||||
}
|
||||
Err(err) => {
|
||||
vm_error!("REPL error: {}", err);
|
||||
eprintln!("{}", Red.paint(format!("REPL error: {err}")));
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
@@ -355,6 +377,7 @@ fn repl() -> ExitCode {
|
||||
}
|
||||
fn not_implemented(command: &str) -> ExitCode {
|
||||
use ansi_term::Colour::Yellow;
|
||||
kernel_info!("command not yet implemented: {}", command);
|
||||
eprintln!("{}", Yellow.paint(format!("'{command}' is not yet implemented")));
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ oxide_compiler = { path = "../oxide_compiler" }
|
||||
oxide_vm = { path = "../oxide_vm" }
|
||||
oxide_parser = { path = "../oxide_parser" }
|
||||
oxide_kernel = { path = "../oxide_kernel" }
|
||||
oxide_log = { path = "../oxide_log" }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_yaml = "0.9"
|
||||
walkdir = "2"
|
||||
|
||||
@@ -12,6 +12,9 @@ use std::sync::{Arc, OnceLock, RwLock};
|
||||
use std::time::{Duration, Instant};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
mod test262_log;
|
||||
use oxide_log::{Level, LogConfig, Output, SUBSYSTEM_COUNT};
|
||||
|
||||
// Thread-local that records the path currently being executed.
|
||||
// Written before every test; read by the panic hook to identify the crash file.
|
||||
std::thread_local! {
|
||||
@@ -1063,8 +1066,10 @@ fn main() {
|
||||
std::panic::set_hook(Box::new(|info| {
|
||||
let path = CURRENT_TEST_PATH.with(|p| p.borrow().clone());
|
||||
if !path.is_empty() {
|
||||
test262_error!("CRASH in test: {}", path);
|
||||
eprintln!("CRASH in test: {path}");
|
||||
}
|
||||
test262_error!("panic: {}", info);
|
||||
eprintln!("panic: {info}");
|
||||
}));
|
||||
|
||||
@@ -1086,11 +1091,17 @@ fn run_tests() -> bool {
|
||||
let config = match RunConfig::parse(&args) {
|
||||
Ok(config) => config,
|
||||
Err(msg) => {
|
||||
test262_error!("config error: {}", msg);
|
||||
eprintln!("{msg}");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
oxide_log::init(&LogConfig {
|
||||
output: Output::Stderr,
|
||||
levels: [Level::Info; SUBSYSTEM_COUNT],
|
||||
});
|
||||
|
||||
let test262_root = if let Some(root) = config.test262_root.clone() {
|
||||
root
|
||||
} else {
|
||||
@@ -1104,6 +1115,7 @@ fn run_tests() -> bool {
|
||||
};
|
||||
|
||||
if !test262_root.exists() {
|
||||
test262_error!("test262 not found at {}", test262_root.display());
|
||||
eprintln!(
|
||||
"test262 not found at: {}\n\
|
||||
Run: git submodule add https://github.com/tc39/test262.git tests/test262\n\
|
||||
@@ -1116,11 +1128,14 @@ fn run_tests() -> bool {
|
||||
|
||||
let filter = config.filter.clone().map(|f| f.replace('\\', "/"));
|
||||
|
||||
test262_info!("discovering tests in: {}", test262_root.display());
|
||||
eprintln!("discovering tests in: {}", test262_root.display());
|
||||
if config.no_skip {
|
||||
test262_info!("no-skip mode enabled");
|
||||
eprintln!("no-skip mode: capability filters disabled; unsupported results count as failures");
|
||||
}
|
||||
let paths = discover_tests(&test262_root);
|
||||
test262_info!("found {} test files", paths.len());
|
||||
eprintln!("found {} test files", paths.len());
|
||||
|
||||
let total = paths.len();
|
||||
@@ -1144,6 +1159,7 @@ fn run_tests() -> bool {
|
||||
let log_running_tests = std::env::var_os("OXIDE_TEST262_RUNNING_LOG").is_some();
|
||||
let heartbeat_path: Option<PathBuf> = std::env::var_os("OXIDE_TEST262_HEARTBEAT").map(PathBuf::from);
|
||||
|
||||
test262_info!("running on {} worker thread(s)", workers);
|
||||
eprintln!("running on {workers} worker thread(s)");
|
||||
|
||||
let skip_until = std::env::var("OXIDE_SKIP_UNTIL")
|
||||
@@ -1170,16 +1186,19 @@ fn run_tests() -> bool {
|
||||
let allow_fail_exit = std::env::var_os("OXIDE_TEST262_ALLOW_FAIL_EXIT").is_some();
|
||||
|
||||
if config.supervise && !is_chunk_child && filter.is_none() {
|
||||
test262_info!("supervised mode enabled");
|
||||
eprintln!("supervised mode enabled: child-window execution with per-test timeout + auto-resume");
|
||||
return run_supervised(&args, skip_until, end_index, config.no_skip, &paths);
|
||||
}
|
||||
|
||||
if let Some(chunk_size) = chunk_size {
|
||||
if !is_chunk_child && filter.is_none() {
|
||||
test262_info!("chunked mode enabled: {} test(s) per child", chunk_size);
|
||||
eprintln!("chunked mode enabled: {chunk_size} test(s) per child process");
|
||||
return run_chunked(&args, skip_until, end_index, chunk_size);
|
||||
}
|
||||
}
|
||||
test262_info!("kernel reset batch: {} test(s)", kernel_batch);
|
||||
eprintln!("kernel reset batch: {kernel_batch} test(s)");
|
||||
let cursor = AtomicUsize::new(skip_until);
|
||||
let progress = AtomicUsize::new(skip_until);
|
||||
@@ -1220,6 +1239,7 @@ fn run_tests() -> bool {
|
||||
// diagnosing OS-level crashes that need the last stderr line.
|
||||
CURRENT_TEST_PATH.with(|p| *p.borrow_mut() = path_str.clone());
|
||||
if log_running_tests {
|
||||
test262_debug!("running: {}", path_str);
|
||||
eprintln!(" [{tid:?}] running: {path_str}");
|
||||
}
|
||||
if let Some(hb) = heartbeat_ref {
|
||||
@@ -1240,6 +1260,7 @@ fn run_tests() -> bool {
|
||||
tests_since_kernel_reset = 0;
|
||||
}
|
||||
if done % 500 == 0 || done == total {
|
||||
test262_info!("progress: {}/{} ({}%)", done, total, done * 100 / total);
|
||||
eprintln!(" progress: {done}/{total} ({}%)", done * 100 / total);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#[macro_export]
|
||||
macro_rules! test262_error {
|
||||
($($arg:tt)*) => {
|
||||
oxide_log::__log_event!("oxide::test262", oxide_log::tracing::Level::ERROR, $($arg)*)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! test262_info {
|
||||
($($arg:tt)*) => {
|
||||
oxide_log::__log_event!("oxide::test262", oxide_log::tracing::Level::INFO, $($arg)*)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! test262_debug {
|
||||
($($arg:tt)*) => {
|
||||
oxide_log::__log_event!("oxide::test262", oxide_log::tracing::Level::DEBUG, $($arg)*)
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user