From 4f8dea100e751d550a4a40d11b142fc9a7c4a5a8 Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Fri, 2 Sep 2022 18:44:45 +0100 Subject: [PATCH] refactor(test): grab runTests() and runBenchmarks() from __bootstrap (#15420) --- .../testdata/test/steps/failing_steps.out | 4 +- cli/worker.rs | 49 ++-------- core/runtime.rs | 2 +- runtime/js/40_testing.js | 43 ++++----- runtime/worker.rs | 94 +++++++++++++++++++ 5 files changed, 128 insertions(+), 64 deletions(-) diff --git a/cli/tests/testdata/test/steps/failing_steps.out b/cli/tests/testdata/test/steps/failing_steps.out index d8c2bdf8df..1e5f2f64df 100644 --- a/cli/tests/testdata/test/steps/failing_steps.out +++ b/cli/tests/testdata/test/steps/failing_steps.out @@ -38,12 +38,12 @@ failing step in failing test ... FAILED ([WILDCARD]) nested failure => ./test/steps/failing_steps.ts:[WILDCARD] error: Error: 1 test step failed. at runTest (deno:runtime/js/40_testing.js:[WILDCARD]) - at async Object.runTests (deno:runtime/js/40_testing.js:[WILDCARD]) + at async runTests (deno:runtime/js/40_testing.js:[WILDCARD]) multiple test step failures => ./test/steps/failing_steps.ts:[WILDCARD] error: Error: 2 test steps failed. at runTest (deno:runtime/js/40_testing.js:[WILDCARD]) - at async Object.runTests (deno:runtime/js/40_testing.js:[WILDCARD]) + at async runTests (deno:runtime/js/40_testing.js:[WILDCARD]) failing step in failing test => ./test/steps/failing_steps.ts:[WILDCARD] error: Error: Fail test. diff --git a/cli/worker.rs b/cli/worker.rs index d7c185fd95..1c4c6475a3 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -7,7 +7,6 @@ use deno_core::error::AnyError; use deno_core::futures::task::LocalFutureObj; use deno_core::futures::FutureExt; use deno_core::located_script_name; -use deno_core::serde_json::json; use deno_core::Extension; use deno_core::ModuleId; use deno_graph::source::ResolveResponse; @@ -220,10 +219,7 @@ impl CliMainWorker { &mut self, mode: TestMode, ) -> Result<(), AnyError> { - self.worker.js_runtime.execute_script( - &located_script_name!(), - r#"Deno[Deno.internal].enableTestAndBench()"#, - )?; + self.worker.enable_test(); // Enable op call tracing in core to enable better debugging of op sanitizer // failures. @@ -273,17 +269,10 @@ impl CliMainWorker { } self.worker.dispatch_load_event(&located_script_name!())?; - - let test_result = self.worker.js_runtime.execute_script( - &located_script_name!(), - &format!( - r#"Deno[Deno.internal].runTests({})"#, - json!({ "shuffle": self.ps.options.shuffle_tests() }), - ), - )?; - - self.worker.js_runtime.resolve_value(test_result).await?; - + self + .worker + .run_tests(&self.ps.options.shuffle_tests()) + .await?; loop { if !self .worker @@ -309,10 +298,7 @@ impl CliMainWorker { &mut self, mode: TestMode, ) -> Result<(), AnyError> { - self.worker.js_runtime.execute_script( - &located_script_name!(), - r#"Deno[Deno.internal].enableTestAndBench()"#, - )?; + self.worker.enable_test(); self .worker @@ -328,14 +314,7 @@ impl CliMainWorker { } self.worker.dispatch_load_event(&located_script_name!())?; - - let test_result = self.worker.js_runtime.execute_script( - &located_script_name!(), - r#"Deno[Deno.internal].runTests()"#, - )?; - - self.worker.js_runtime.resolve_value(test_result).await?; - + self.worker.run_tests(&None).await?; loop { if !self .worker @@ -350,10 +329,7 @@ impl CliMainWorker { } pub async fn run_bench_specifier(&mut self) -> Result<(), AnyError> { - self.worker.js_runtime.execute_script( - &located_script_name!(), - r#"Deno[Deno.internal].enableTestAndBench()"#, - )?; + self.worker.enable_bench(); if self.ps.options.compat() { self.worker.execute_side_module(&compat::GLOBAL_URL).await?; @@ -383,14 +359,7 @@ impl CliMainWorker { } self.worker.dispatch_load_event(&located_script_name!())?; - - let bench_result = self.worker.js_runtime.execute_script( - &located_script_name!(), - r#"Deno[Deno.internal].runBenchmarks()"#, - )?; - - self.worker.js_runtime.resolve_value(bench_result).await?; - + self.worker.run_benchmarks().await?; loop { if !self .worker diff --git a/core/runtime.rs b/core/runtime.rs index b1d8f3d807..5b3b0ce501 100644 --- a/core/runtime.rs +++ b/core/runtime.rs @@ -629,7 +629,7 @@ impl JsRuntime { .ok() } - pub(crate) fn grab_global<'s, T>( + pub fn grab_global<'s, T>( scope: &mut v8::HandleScope<'s>, path: &str, ) -> Option> diff --git a/runtime/js/40_testing.js b/runtime/js/40_testing.js index 276fef6a63..e3a6ce3243 100644 --- a/runtime/js/40_testing.js +++ b/runtime/js/40_testing.js @@ -16,6 +16,7 @@ ArrayPrototypePush, ArrayPrototypeShift, ArrayPrototypeSort, + BigInt, DateNow, Error, FunctionPrototype, @@ -600,7 +601,8 @@ const testStates = new Map(); /** @type {BenchDescription[]} */ const benchDescs = []; - let isTestOrBenchSubcommand = false; + let isTestSubcommand = false; + let isBenchSubcommand = false; // Main test function provided by Deno. function test( @@ -608,7 +610,7 @@ optionsOrFn, maybeFn, ) { - if (!isTestOrBenchSubcommand) { + if (!isTestSubcommand) { return; } @@ -727,7 +729,7 @@ optionsOrFn, maybeFn, ) { - if (!isTestOrBenchSubcommand) { + if (!isBenchSubcommand) { return; } @@ -1032,11 +1034,12 @@ return ops.op_bench_now(); } - // This function is called by Rust side if we're in `deno test` or - // `deno bench` subcommand. If this function is not called then `Deno.test()` - // and `Deno.bench()` become noops. - function enableTestAndBench() { - isTestOrBenchSubcommand = true; + function enableTest() { + isTestSubcommand = true; + } + + function enableBench() { + isBenchSubcommand = true; } async function runTests({ @@ -1062,15 +1065,16 @@ if (shuffle !== null) { // http://en.wikipedia.org/wiki/Linear_congruential_generator + // Use BigInt for everything because the random seed is u64. const nextInt = (function (state) { - const m = 0x80000000; - const a = 1103515245; - const c = 12345; + const m = 0x80000000n; + const a = 1103515245n; + const c = 12345n; return function (max) { - return state = ((a * state + c) % m) % max; + return state = ((a * state + c) % m) % BigInt(max); }; - }(shuffle)); + }(BigInt(shuffle))); for (let i = filtered.length - 1; i > 0; i--) { const j = nextInt(i); @@ -1390,15 +1394,12 @@ return testFn; } - window.__bootstrap.internals = { - ...window.__bootstrap.internals ?? {}, - enableTestAndBench, - runTests, - runBenchmarks, - }; - window.__bootstrap.testing = { - test, bench, + enableBench, + enableTest, + runBenchmarks, + runTests, + test, }; })(this); diff --git a/runtime/worker.rs b/runtime/worker.rs index 79b1b5537f..632805e6d4 100644 --- a/runtime/worker.rs +++ b/runtime/worker.rs @@ -11,6 +11,9 @@ use deno_core::error::AnyError; use deno_core::error::JsError; use deno_core::futures::Future; use deno_core::located_script_name; +use deno_core::serde_json::json; +use deno_core::serde_v8; +use deno_core::v8; use deno_core::CompiledWasmModuleStore; use deno_core::Extension; use deno_core::GetErrorClassFn; @@ -59,6 +62,10 @@ pub struct MainWorker { pub js_runtime: JsRuntime, should_break_on_first_statement: bool, exit_code: ExitCode, + js_run_tests_callback: v8::Global, + js_run_benchmarks_callback: v8::Global, + js_enable_test_callback: v8::Global, + js_enable_bench_callback: v8::Global, } pub struct WorkerOptions { @@ -86,6 +93,15 @@ pub struct WorkerOptions { pub stdio: Stdio, } +fn grab_cb( + scope: &mut v8::HandleScope, + path: &str, +) -> v8::Global { + let cb = JsRuntime::grab_global::(scope, path) + .unwrap_or_else(|| panic!("{} must be defined", path)); + v8::Global::new(scope, cb) +} + impl MainWorker { pub fn bootstrap_from_options( main_module: ModuleSpecifier, @@ -198,10 +214,29 @@ impl MainWorker { ); } + let ( + js_run_tests_callback, + js_run_benchmarks_callback, + js_enable_test_callback, + js_enable_bench_callback, + ) = { + let scope = &mut js_runtime.handle_scope(); + ( + grab_cb(scope, "__bootstrap.testing.runTests"), + grab_cb(scope, "__bootstrap.testing.runBenchmarks"), + grab_cb(scope, "__bootstrap.testing.enableTest"), + grab_cb(scope, "__bootstrap.testing.enableBench"), + ) + }; + Self { js_runtime, should_break_on_first_statement: options.should_break_on_first_statement, exit_code, + js_run_tests_callback, + js_run_benchmarks_callback, + js_enable_test_callback, + js_enable_bench_callback, } } @@ -289,6 +324,65 @@ impl MainWorker { self.evaluate_module(id).await } + /// Run tests declared with `Deno.test()`. Test events will be dispatched + /// by calling ops which are currently only implemented in the CLI crate. + // TODO(nayeemrmn): Move testing ops to deno_runtime and redesign/unhide. + #[doc(hidden)] + pub async fn run_tests( + &mut self, + shuffle: &Option, + ) -> Result<(), AnyError> { + let promise = { + let scope = &mut self.js_runtime.handle_scope(); + let cb = self.js_run_tests_callback.open(scope); + let this = v8::undefined(scope).into(); + let options = + serde_v8::to_v8(scope, json!({ "shuffle": shuffle })).unwrap(); + let promise = cb.call(scope, this, &[options]).unwrap(); + v8::Global::new(scope, promise) + }; + self.js_runtime.resolve_value(promise).await?; + Ok(()) + } + + /// Run benches declared with `Deno.bench()`. Bench events will be dispatched + /// by calling ops which are currently only implemented in the CLI crate. + // TODO(nayeemrmn): Move benchmark ops to deno_runtime and redesign/unhide. + #[doc(hidden)] + pub async fn run_benchmarks(&mut self) -> Result<(), AnyError> { + let promise = { + let scope = &mut self.js_runtime.handle_scope(); + let cb = self.js_run_benchmarks_callback.open(scope); + let this = v8::undefined(scope).into(); + let promise = cb.call(scope, this, &[]).unwrap(); + v8::Global::new(scope, promise) + }; + self.js_runtime.resolve_value(promise).await?; + Ok(()) + } + + /// Enable `Deno.test()`. If this isn't called before executing user code, + /// `Deno.test()` calls will noop. + // TODO(nayeemrmn): Move testing ops to deno_runtime and redesign/unhide. + #[doc(hidden)] + pub fn enable_test(&mut self) { + let scope = &mut self.js_runtime.handle_scope(); + let cb = self.js_enable_test_callback.open(scope); + let this = v8::undefined(scope).into(); + cb.call(scope, this, &[]).unwrap(); + } + + /// Enable `Deno.bench()`. If this isn't called before executing user code, + /// `Deno.bench()` calls will noop. + // TODO(nayeemrmn): Move benchmark ops to deno_runtime and redesign/unhide. + #[doc(hidden)] + pub fn enable_bench(&mut self) { + let scope = &mut self.js_runtime.handle_scope(); + let cb = self.js_enable_bench_callback.open(scope); + let this = v8::undefined(scope).into(); + cb.call(scope, this, &[]).unwrap(); + } + fn wait_for_inspector_session(&mut self) { if self.should_break_on_first_statement { self