From 6e8618ae0f55bcaa4cfaaa579b4e21f9f74b117d Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Thu, 13 Apr 2023 18:43:23 +0100 Subject: refactor(cli): move runTests() and runBenchmarks() to rust (#18563) Stores the test/bench functions in rust op state during registration. The functions are wrapped in JS first so that they return a directly convertible `TestResult`/`BenchResult`. Test steps are still mostly handled in JS since they are pretty much invoked by the user. Allows removing a bunch of infrastructure for communicating between JS and rust. Allows using rust utilities for things like shuffling tests (`Vec::shuffle`). We can progressively move op and resource sanitization to rust as well. Fixes #17122. Fixes #17312. --- cli/ops/testing.rs | 79 +++++++++++++++++++++++++----------------------------- 1 file changed, 37 insertions(+), 42 deletions(-) (limited to 'cli/ops/testing.rs') diff --git a/cli/ops/testing.rs b/cli/ops/testing.rs index 8b5c95fea..e36d7e611 100644 --- a/cli/ops/testing.rs +++ b/cli/ops/testing.rs @@ -1,17 +1,16 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use crate::tools::test::FailFastTracker; use crate::tools::test::TestDescription; use crate::tools::test::TestEvent; use crate::tools::test::TestEventSender; -use crate::tools::test::TestFilter; use crate::tools::test::TestLocation; -use crate::tools::test::TestResult; use crate::tools::test::TestStepDescription; use deno_core::error::generic_error; use deno_core::error::AnyError; use deno_core::op; +use deno_core::serde_v8; +use deno_core::v8; use deno_core::ModuleSpecifier; use deno_core::OpState; use deno_runtime::permissions::create_child_permissions; @@ -24,25 +23,25 @@ use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; use uuid::Uuid; +#[derive(Default)] +pub(crate) struct TestContainer( + pub Vec<(TestDescription, v8::Global)>, +); + deno_core::extension!(deno_test, ops = [ op_pledge_test_permissions, op_restore_test_permissions, - op_get_test_origin, op_register_test, op_register_test_step, op_dispatch_test_event, - op_tests_should_stop, ], options = { sender: TestEventSender, - fail_fast_tracker: FailFastTracker, - filter: TestFilter, }, state = |state, options| { state.put(options.sender); - state.put(options.fail_fast_tracker); - state.put(options.filter); + state.put(TestContainer::default()); }, customizer = |ext: &mut deno_core::ExtensionBuilder| { ext.force_op_registration(); @@ -95,16 +94,14 @@ pub fn op_restore_test_permissions( } } -#[op] -fn op_get_test_origin(state: &mut OpState) -> Result { - Ok(state.borrow::().to_string()) -} - -#[derive(Debug, Deserialize)] +#[derive(Deserialize)] #[serde(rename_all = "camelCase")] -struct TestInfo { +struct TestInfo<'s> { + #[serde(rename = "fn")] + function: serde_v8::Value<'s>, name: String, - origin: String, + ignore: bool, + only: bool, location: TestLocation, } @@ -112,28 +109,36 @@ struct TestInfo { #[serde(rename_all = "camelCase")] struct TestRegisterResult { id: usize, - filtered_out: bool, + origin: String, } static NEXT_ID: AtomicUsize = AtomicUsize::new(0); -#[op] -fn op_register_test( +#[op(v8)] +fn op_register_test<'a>( + scope: &mut v8::HandleScope<'a>, state: &mut OpState, - info: TestInfo, + info: TestInfo<'a>, ) -> Result { let id = NEXT_ID.fetch_add(1, Ordering::SeqCst); - let filter = state.borrow::().clone(); - let filtered_out = !filter.includes(&info.name); + let origin = state.borrow::().to_string(); let description = TestDescription { id, name: info.name, - origin: info.origin, + ignore: info.ignore, + only: info.only, + origin: origin.clone(), location: info.location, }; + let function: v8::Local = info.function.v8_value.try_into()?; + let function = v8::Global::new(scope, function); + state + .borrow_mut::() + .0 + .push((description.clone(), function)); let mut sender = state.borrow::().clone(); sender.send(TestEvent::Register(description)).ok(); - Ok(TestRegisterResult { id, filtered_out }) + Ok(TestRegisterResult { id, origin }) } fn deserialize_parent<'de, D>(deserializer: D) -> Result @@ -151,7 +156,6 @@ where #[serde(rename_all = "camelCase")] struct TestStepInfo { name: String, - origin: String, location: TestLocation, level: usize, #[serde(rename = "parent")] @@ -167,10 +171,11 @@ fn op_register_test_step( info: TestStepInfo, ) -> Result { let id = NEXT_ID.fetch_add(1, Ordering::SeqCst); + let origin = state.borrow::().to_string(); let description = TestStepDescription { id, name: info.name, - origin: info.origin, + origin: origin.clone(), location: info.location, level: info.level, parent_id: info.parent_id, @@ -179,10 +184,7 @@ fn op_register_test_step( }; let mut sender = state.borrow::().clone(); sender.send(TestEvent::StepRegister(description)).ok(); - Ok(TestRegisterResult { - id, - filtered_out: false, - }) + Ok(TestRegisterResult { id, origin }) } #[op] @@ -190,18 +192,11 @@ fn op_dispatch_test_event( state: &mut OpState, event: TestEvent, ) -> Result<(), AnyError> { - if matches!( - event, - TestEvent::Result(_, TestResult::Cancelled | TestResult::Failed(_), _) - ) { - state.borrow::().add_failure(); - } + assert!( + matches!(event, TestEvent::StepWait(_) | TestEvent::StepResult(..)), + "Only step wait/result events are expected from JS." + ); let mut sender = state.borrow::().clone(); sender.send(event).ok(); Ok(()) } - -#[op] -fn op_tests_should_stop(state: &mut OpState) -> bool { - state.borrow::().should_stop() -} -- cgit v1.2.3