1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-21 15:04:11 -05:00
This commit is contained in:
Marvin Hagemeister 2024-10-10 13:54:58 +02:00
parent e6c455c613
commit 777c935603
11 changed files with 339 additions and 225 deletions

View file

@ -1,5 +1,7 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// @ts-check
import { core, primordials } from "ext:core/mod.js";
import { escapeName, withPermissions } from "ext:cli/40_test_common.js";
@ -7,14 +9,18 @@ import { escapeName, withPermissions } from "ext:cli/40_test_common.js";
const {
op_register_test_step,
op_register_test,
op_register_test_group,
op_test_group_pop,
op_register_test_group_lifecycle,
op_test_group_register,
op_test_group_event_start,
op_test_group_event_end,
op_register_test_run_fn,
op_test_event_step_result_failed,
op_test_event_step_result_ignored,
op_test_event_step_result_ok,
op_test_event_step_wait,
op_test_event_start,
op_test_event_result_ok,
op_test_event_result_ignored,
op_test_event_result_failed,
op_test_get_origin,
} = core.ops;
const {
@ -44,11 +50,11 @@ const DenoNs = globalThis.Deno;
* origin: string,
* location: TestLocation,
* ignore: boolean,
* only: boolean.
* only: boolean,
* sanitizeOps: boolean,
* sanitizeResources: boolean,
* sanitizeExit: boolean,
* permissions: PermissionOptions,
* permissions: Deno.PermissionOptions,
* }} TestDescription
*
* @typedef {{
@ -86,9 +92,9 @@ const DenoNs = globalThis.Deno;
* fn: BenchFunction
* origin: string,
* ignore: boolean,
* only: boolean.
* only: boolean,
* sanitizeExit: boolean,
* permissions: PermissionOptions,
* permissions: Deno.PermissionOptions,
* }} BenchDescription
*/
@ -149,7 +155,7 @@ function wrapOuter(fn, desc) {
}
function wrapInner(fn) {
/** @param desc {TestDescription | TestStepDescription} */
/** @param {TestDescription | TestStepDescription} desc */
return async function innerWrapped(desc) {
function getRunningStepDescs() {
const results = [];
@ -210,7 +216,16 @@ function wrapInner(fn) {
const registerTestIdRetBuf = new Uint32Array(1);
const registerTestIdRetBufU8 = new Uint8Array(registerTestIdRetBuf.buffer);
// As long as we're using one isolate per test, we can cache the origin since it won't change
const registerTestGroupIdRetBuf = new Uint32Array(1);
const registerTestGroupIdRetBufU8 = new Uint8Array(
registerTestGroupIdRetBuf.buffer,
);
/**
* As long as we're using one isolate per test, we can cache the origin
* since it won't change.
* @type {string | undefined}
*/
let cachedOrigin = undefined;
function testInner(
@ -384,7 +399,7 @@ function stepReportResult(desc, result, elapsed) {
}
}
/** @param desc {TestDescription | TestStepDescription} */
/** @param {TestDescription | TestStepDescription} desc */
function createTestContext(desc) {
let parent;
let level;
@ -416,8 +431,8 @@ function createTestContext(desc) {
*/
origin: desc.origin,
/**
* @param nameOrFnOrOptions {string | TestStepDefinition | ((t: TestContext) => void | Promise<void>)}
* @param maybeFn {((t: TestContext) => void | Promise<void>) | undefined}
* @param {string | TestStepDescription | ((t: TestContext) => void | Promise<void>)} nameOrFnOrOptions
* @param {((t: TestContext) => void | Promise<void>) | undefined} maybeFn
*/
async step(nameOrFnOrOptions, maybeFn) {
if (MapPrototypeGet(testStates, desc.id).completed) {
@ -504,9 +519,8 @@ function createTestContext(desc) {
/**
* Wrap a user test function in one which returns a structured result.
* @template T {Function}
* @param testFn {T}
* @param desc {TestDescription | TestStepDescription}
* @template {Function} T
* @param {TestDescription | TestStepDescription} desc
* @returns {T}
*/
function wrapTest(desc) {
@ -522,14 +536,34 @@ function wrapTest(desc) {
globalThis.Deno.test = test;
/** @typedef {{ name: string, fn: () => any, only: boolean, ignore: boolean }} BddTest */
/** @typedef {() => unknown | Promise<unknown>} TestLifecycleFn */
/** @typedef {{ name: string, ignore: boolean, only: boolean, children: Array<TestGroup | BddTest>, beforeAll: TestLifecycleFn | null, afterAll: TestLifecycleFn | null, beforeEach: TestLifecycleFn | null, afterEach: TestLifecycleFn | null}} TestGroup */
/**
* @typedef {{
* id: number,
* name: string,
* fn: () => any,
* only: boolean,
* ignore: boolean
* }} BddTest
*
* @typedef {() => unknown | Promise<unknown>} TestLifecycleFn
*
* @typedef {{
* id: number,
* name: string,
* ignore: boolean,
* only: boolean,
* children: Array<TestGroup | BddTest>,
* beforeAll: TestLifecycleFn | null,
* afterAll: TestLifecycleFn | null,
* beforeEach: TestLifecycleFn | null,
* afterEach: TestLifecycleFn | null
* }} TestGroup
*/
/** @type {TestGroup} */
const ROOT_TEST_GROUP = {
name: "__<root>__",
id: 0,
name: "__DENO_TEST_ROOT__",
ignore: false,
only: false,
children: [],
@ -538,6 +572,16 @@ const ROOT_TEST_GROUP = {
afterAll: null,
afterEach: null,
};
// No-op if we're not running in `deno test` subcommand.
if (typeof op_register_test === "function") {
op_test_group_register(
registerTestGroupIdRetBufU8,
ROOT_TEST_GROUP.name,
true,
);
ROOT_TEST_GROUP.id = registerTestGroupIdRetBuf[0];
}
/** @type {{ hasOnly: boolean, stack: TestGroup[], total: number }} */
const BDD_CONTEXT = {
hasOnly: false,
@ -547,14 +591,16 @@ const BDD_CONTEXT = {
/**
* @param {string} name
* @param {fn: () => any} fn
* @param {() => any} fn
* @param {boolean} ignore
* @param {boolean} only
*/
function itInner(name, fn, ignore, only) {
// No-op if we're not running in `deno test` subcommand.
if (typeof op_register_test !== "function") {
return;
if (
!ignore && BDD_CONTEXT.stack.length > 1 &&
BDD_CONTEXT.stack.some((x) => x.ignore)
) {
ignore = true;
}
if (cachedOrigin == undefined) {
@ -577,12 +623,13 @@ function itInner(name, fn, ignore, only) {
/** @type {BddTest} */
const testDef = {
id: 0,
name,
fn: testFn,
ignore,
only,
};
BDD_CONTEXT.stack.at(-1).children.push(testDef);
getGroupParent().children.push(testDef);
BDD_CONTEXT.total++;
op_register_test(
@ -597,6 +644,8 @@ function itInner(name, fn, ignore, only) {
location.columnNumber,
registerTestIdRetBufU8,
);
testDef.id = registerTestIdRetBuf[0];
}
/**
@ -623,6 +672,18 @@ it.ignore = (name, fn) => {
};
it.skip = it.ignore;
/** @type {(x: TestGroup | BddTest) => x is TestGroup} */
function isTestGroup(x) {
return "beforeAll" in x;
}
/**
* @returns {TestGroup}
*/
function getGroupParent() {
return /** @type {TestGroup} */ (BDD_CONTEXT.stack.at(-1));
}
/**
* @param {string} name
* @param {() => void} fn
@ -635,9 +696,13 @@ function describeInner(name, fn, ignore, only) {
return;
}
const parent = BDD_CONTEXT.stack.at(-1);
op_test_group_register(registerTestGroupIdRetBufU8, name, false);
const id = registerTestGroupIdRetBuf[0];
const parent = getGroupParent();
/** @type {TestGroup} */
const group = {
id,
name,
ignore,
only,
@ -653,6 +718,43 @@ function describeInner(name, fn, ignore, only) {
try {
fn();
} finally {
let allIgnore = true;
let onlyChildCount = 0;
for (let i = 0; i < group.children.length; i++) {
const child = group.children[i];
if (!child.ignore) allIgnore = false;
if (!isTestGroup(child) && child.only) {
onlyChildCount++;
}
}
if (!group.ignore) {
group.ignore = allIgnore;
}
if (!group.ignore) {
if (onlyChildCount > 0) {
group.only = true;
if (onlyChildCount < group.children.length - 1) {
for (let i = 0; i < group.children.length; i++) {
const child = group.children[i];
if (!isTestGroup(child) && !child.only) {
child.ignore = true;
}
}
}
} else if (group.only) {
for (let i = 0; i < group.children.length; i++) {
const child = group.children[i];
child.only = true;
}
}
}
BDD_CONTEXT.stack.pop();
}
}
@ -685,27 +787,27 @@ describe.skip = describe.ignore;
* @param {() => any} fn
*/
function beforeAll(fn) {
BDD_CONTEXT.stack.at(-1).beforeAll = fn;
getGroupParent().beforeAll = fn;
}
/**
* @param {() => any} fn
*/
function afterAll(fn) {
BDD_CONTEXT.stack.at(-1).afterAll = fn;
getGroupParent().afterAll = fn;
}
/**
* @param {() => any} fn
*/
function beforeEach(fn) {
BDD_CONTEXT.stack.at(-1).beforeEach = fn;
getGroupParent().beforeEach = fn;
}
/**
* @param {() => any} fn
*/
function afterEach(fn) {
BDD_CONTEXT.stack.at(-1).afterEach = fn;
getGroupParent().afterEach = fn;
}
globalThis.before = beforeAll;
@ -719,68 +821,109 @@ globalThis.describe = describe;
/**
* This function is called from Rust.
* @param {bigint} seed
* @param {number} seed
* @param {...any} rest
*/
async function runTests(seed, ...rest) {
if (BDD_CONTEXT.hasOnly) {
ROOT_TEST_GROUP.only = ROOT_TEST_GROUP.children.some((child) => child.only);
}
console.log("RUN TESTS", seed, rest, ROOT_TEST_GROUP);
// Filter tests
try {
await runGroup(seed, ROOT_TEST_GROUP);
} finally {
//
}
}
/**
* @param {bigint} seed
* @param {number} seed
* @param {TestGroup} group
*/
async function runGroup(seed, group) {
// Bail out if group has no tests or sub groups
op_test_group_event_start(group.id);
/** @type {BddTest[]} */
const tests = [];
/** @type {TestGroup[]} */
const groups = [];
if (seed > 0 && group.children.length > 1) {
shuffle(group.children, seed);
}
for (let i = 0; i < group.children[i]; i++) {
// Sort tests:
// - non-ignored tests first (might be shuffled earlier)
// - ignored tests second
// - groups last
group.children.sort(sortTestItems);
// Short circuit if the whole group is ignored
if (group.ignore) {
// FIXME
return;
}
try {
if (group.beforeAll !== null) {
await group.beforeAll();
}
for (let i = 0; i < group.children.length; i++) {
const child = group.children[i];
if ("beforeAll" in child) {
groups.push(child);
if (group.beforeEach !== null) {
await group.beforeEach();
}
if (isTestGroup(child)) {
await runGroup(seed, child);
} else if (child.ignore) {
op_test_event_result_ignored(child.id);
} else {
tests.push(child);
op_test_event_start(child.id);
const start = DateNow();
try {
await child.fn();
const elapsed = DateNow() - start;
op_test_event_result_ok(child.id, elapsed);
} catch (err) {
const elapsed = DateNow() - start;
op_test_event_result_failed(child.id, elapsed);
}
}
if (seed > 0) {
shuffle(tests, seed);
shuffle(groups, seed);
if (group.afterEach !== null) {
await group.afterEach();
}
}
await group.beforeAll?.();
for (let i = 0; i < tests.length; i++) {
const test = tests[i];
await group.beforeEach?.();
await test.fn();
await group.afterEach?.();
if (group.afterAll !== null) {
await group.afterAll();
}
for (let i = 0; i < groups.length; i++) {
const childGroup = groups[i];
await group.beforeEach?.();
await runGroup(seed, childGroup);
await group.afterEach?.();
} finally {
op_test_group_event_end(group.id);
}
}
await group.afterAll?.();
/**
* @param {TestGroup | BddTest} a
* @param {TestGroup | BddTest} b
*/
function sortTestItems(a, b) {
const isAGroup = isTestGroup(a);
const isBGroup = isTestGroup(b);
if (isAGroup && isBGroup) return 0;
if (isAGroup && !isBGroup) return -1;
if (!isAGroup && isBGroup) return 1;
if (a.ignore && b.ignore) return 0;
if (a.ignore && !b.ignore) return -1;
if (!a.ignore && b.ignore) return 1;
return 0;
}
/**
* @template T
* @param {T[]} arr
* @param {bigint} seed
* @param {number} seed
*/
function shuffle(arr, seed) {
let m = arr.length;
@ -788,13 +931,22 @@ function shuffle(arr, seed) {
let i;
while (m) {
i = Math.floor(seed * m--);
i = Math.floor(randomize(seed) * m--);
t = arr[m];
arr[m] = arr[i];
arr[i] = t;
}
}
/**
* @param {number} seed
* @returns {number}
*/
function randomize(seed) {
const x = Math.sin(seed++) * 10000;
return x - Math.floor(x);
}
// No-op if we're not running in `deno test` subcommand.
if (typeof op_register_test === "function") {
op_register_test_run_fn(runTests);

View file

@ -421,6 +421,7 @@ impl TestRun {
}
test::TestEvent::ForceEndReport => {}
test::TestEvent::Sigint => {}
_ => {} // FIXME
}
}

View file

@ -5,9 +5,9 @@ use crate::tools::test::TestDescription;
use crate::tools::test::TestEvent;
use crate::tools::test::TestEventSender;
use crate::tools::test::TestFailure;
use crate::tools::test::TestGroup;
use crate::tools::test::TestGroupLifecycleFn;
use crate::tools::test::TestGroupDescription;
use crate::tools::test::TestLocation;
use crate::tools::test::TestResult;
use crate::tools::test::TestStepDescription;
use crate::tools::test::TestStepResult;
@ -30,10 +30,14 @@ deno_core::extension!(deno_test,
op_restore_test_permissions,
op_register_test,
op_register_test_step,
op_register_test_group,
op_test_group_pop,
op_register_test_group_lifecycle,
op_register_test_run_fn,
op_test_group_register,
op_test_group_event_start,
op_test_group_event_end,
op_test_event_start,
op_test_event_result_ignored,
op_test_event_result_ok,
op_test_event_result_failed,
op_test_get_origin,
op_test_event_step_wait,
op_test_event_step_result_ok,
@ -93,7 +97,7 @@ pub fn op_restore_test_permissions(
}
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
static NEXT_GROUP_ID: AtomicUsize = AtomicUsize::new(1);
static NEXT_GROUP_ID: AtomicUsize = AtomicUsize::new(0);
#[allow(clippy::too_many_arguments)]
#[op2]
@ -120,7 +124,6 @@ fn op_register_test(
let origin = state.borrow::<ModuleSpecifier>().to_string();
let description = TestDescription {
id,
parent_id: 0,
name,
ignore,
only,
@ -140,35 +143,37 @@ fn op_register_test(
}
#[op2(fast)]
fn op_register_test_group(
fn op_test_group_register(
state: &mut OpState,
#[buffer] ret_buf: &mut [u8],
#[string] name: String,
ignore: bool,
only: bool,
is_root: bool,
) -> Result<(), AnyError> {
let id = NEXT_GROUP_ID.fetch_add(1, Ordering::SeqCst);
let container = state.borrow_mut::<TestContainer>();
let group = TestGroup {
id,
parent_id: 0,
name,
ignore,
only,
children: vec![],
after_all: None,
after_each: None,
before_all: None,
before_each: None,
};
container.register_group(group);
let sender = state.borrow_mut::<TestEventSender>();
let description = TestGroupDescription { id, name, is_root };
sender.send(TestEvent::GroupRegister(description)).ok();
ret_buf.copy_from_slice(&(id as u32).to_le_bytes());
Ok(())
}
#[op2(fast)]
fn op_test_group_pop(state: &mut OpState) -> Result<(), AnyError> {
let container = state.borrow_mut::<TestContainer>();
container.group_pop();
fn op_test_group_event_start(
state: &mut OpState,
#[smi] id: usize,
) -> Result<(), AnyError> {
let sender = state.borrow_mut::<TestEventSender>();
sender.send(TestEvent::GroupWait(id)).ok();
Ok(())
}
#[op2(fast)]
fn op_test_group_event_end(
state: &mut OpState,
#[smi] id: usize,
) -> Result<(), AnyError> {
let sender = state.borrow_mut::<TestEventSender>();
sender.send(TestEvent::GroupResult(id)).ok();
Ok(())
}
@ -182,43 +187,6 @@ fn op_register_test_run_fn(
Ok(())
}
#[allow(clippy::too_many_arguments)]
#[op2]
fn op_register_test_group_lifecycle(
state: &mut OpState,
#[smi] kind: u32,
#[global] function: v8::Global<v8::Function>,
#[string] file_name: String,
#[smi] line_number: u32,
#[smi] column_number: u32,
) -> Result<(), AnyError> {
let container = state.borrow_mut::<TestContainer>();
let lifecycle = TestGroupLifecycleFn {
function,
location: TestLocation {
column_number,
line_number,
file_name,
},
};
// Keep in sync with the JS side
if let Some(last_id) = container.stack.last() {
let last: &mut TestGroup =
container.groups.get_mut::<usize>(*last_id).unwrap();
match kind {
1 => last.before_all = Some(lifecycle),
2 => last.before_each = Some(lifecycle),
3 => last.after_all = Some(lifecycle),
4 => last.after_each = Some(lifecycle),
_ => panic!("Unknown test group lifecycle kind"),
}
}
Ok(())
}
#[op2]
#[string]
fn op_test_get_origin(state: &mut OpState) -> String {
@ -260,6 +228,46 @@ fn op_register_test_step(
Ok(id)
}
#[op2(fast)]
fn op_test_event_start(state: &mut OpState, #[smi] id: usize) {
let sender = state.borrow_mut::<TestEventSender>();
sender.send(TestEvent::Wait(id)).ok();
}
#[op2(fast)]
fn op_test_event_result_ignored(state: &mut OpState, #[smi] id: usize) {
let sender = state.borrow_mut::<TestEventSender>();
sender
.send(TestEvent::Result(id, TestResult::Ignored, 0))
.ok();
}
#[op2(fast)]
fn op_test_event_result_ok(
state: &mut OpState,
#[smi] id: usize,
#[smi] duration: u64,
) {
let sender = state.borrow_mut::<TestEventSender>();
sender
.send(TestEvent::Result(id, TestResult::Ok, duration))
.ok();
}
#[op2(fast)]
fn op_test_event_result_failed(
state: &mut OpState,
#[smi] id: usize,
#[smi] duration: u64,
) {
// FIXME: Placeholder
let failure = TestFailure::IncompleteSteps;
let sender = state.borrow_mut::<TestEventSender>();
sender
.send(TestEvent::Result(id, TestResult::Failed(failure), duration))
.ok();
}
#[op2(fast)]
fn op_test_event_step_wait(state: &mut OpState, #[smi] id: usize) {
let sender = state.borrow_mut::<TestEventSender>();

View file

@ -226,34 +226,15 @@ pub(crate) struct TestContainer {
has_tests: bool,
pub run_fn: Option<v8::Global<v8::Function>>,
pub has_only: bool,
pub groups: Vec<TestGroup>,
pub stack: Vec<usize>,
pub tests: (TestDescriptions, Vec<v8::Global<v8::Function>>),
}
impl TestContainer {
pub fn new() -> Self {
let root = TestGroup {
id: 0,
parent_id: 0,
children: vec![],
ignore: false,
name: "<root>".to_string(),
only: false,
after_all: None,
after_each: None,
before_all: None,
before_each: None,
};
let stack = vec![root.id];
Self {
has_tests: false,
run_fn: None,
groups: vec![root],
has_only: false,
stack,
tests: (
TestDescriptions {
..Default::default()
@ -265,7 +246,7 @@ impl TestContainer {
pub fn register(
&mut self,
mut description: TestDescription,
description: TestDescription,
function: v8::Global<v8::Function>,
) {
self.has_tests = true;
@ -274,35 +255,10 @@ impl TestContainer {
self.has_only = true
}
if let Some(last_id) = self.stack.last() {
description.parent_id = *last_id;
let last: &mut TestGroup =
self.groups.get_mut::<usize>(*last_id).unwrap();
last.children.push(TestGroupChild::Test(description.id));
}
self.tests.0.tests.insert(description.id, description);
self.tests.1.push(function);
}
pub fn register_group(&mut self, mut group: TestGroup) {
if let Some(last_id) = self.stack.last() {
group.parent_id = *last_id;
let last: &mut TestGroup =
self.groups.get_mut::<usize>(*last_id).unwrap();
last.children.push(TestGroupChild::Group(group.id));
}
self.stack.push(group.id);
self.groups.push(group);
}
pub fn group_pop(&mut self) {
self.stack.pop();
}
pub fn is_empty(&self) -> bool {
self.has_tests
}
@ -336,7 +292,6 @@ impl<'a> IntoIterator for &'a TestDescriptions {
#[serde(rename_all = "camelCase")]
pub struct TestDescription {
pub id: usize,
pub parent_id: usize,
pub name: String,
pub ignore: bool,
pub only: bool,
@ -346,6 +301,14 @@ pub struct TestDescription {
pub sanitize_resources: bool,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Eq, Hash)]
#[serde(rename_all = "camelCase")]
pub struct TestGroupDescription {
pub id: usize,
pub name: String,
pub is_root: bool,
}
/// May represent a failure of a test or test step.
#[derive(Debug, Clone, PartialEq, Deserialize, Eq, Hash)]
#[serde(rename_all = "camelCase")]
@ -367,32 +330,6 @@ impl From<&TestDescription> for TestFailureDescription {
}
}
#[derive(Debug, Clone)]
pub struct TestGroupLifecycleFn {
pub function: v8::Global<v8::Function>,
pub location: TestLocation,
}
#[derive(Debug, Default, Clone)]
pub struct TestGroup {
pub id: usize,
pub parent_id: usize,
pub name: String,
pub ignore: bool,
pub only: bool,
pub children: Vec<TestGroupChild>,
pub before_all: Option<TestGroupLifecycleFn>,
pub before_each: Option<TestGroupLifecycleFn>,
pub after_all: Option<TestGroupLifecycleFn>,
pub after_each: Option<TestGroupLifecycleFn>,
}
#[derive(Debug, Clone)]
pub enum TestGroupChild {
Group(usize),
Test(usize),
}
#[derive(Debug, Default, Clone, PartialEq)]
pub struct TestFailureFormatOptions {
pub hide_stacktraces: bool,
@ -561,6 +498,9 @@ pub enum TestStdioStream {
pub enum TestEvent {
Register(Arc<TestDescriptions>),
Plan(TestPlan),
GroupRegister(TestGroupDescription),
GroupWait(usize),
GroupResult(usize),
Wait(usize),
Output(Vec<u8>),
Slow(usize, u64),
@ -881,12 +821,6 @@ pub fn send_test_event(
)
}
enum TestRunItem {
Lifecycle(usize),
Group(usize),
Test(usize),
}
pub async fn run_tests_for_worker(
worker: &mut MainWorker,
specifier: &ModuleSpecifier,
@ -898,9 +832,9 @@ pub async fn run_tests_for_worker(
let tc =
std::mem::take(&mut *state_rc.borrow_mut().borrow_mut::<TestContainer>());
eprintln!("{:#?}", tc.stack);
let tests: Arc<TestDescriptions> = tc.tests.0.into();
send_test_event(&state_rc, TestEvent::Register(tests.clone()))?;
let to_run: Vec<TestRunItem> = vec![];
if let Some(function) = &tc.run_fn {
let seed = if let Some(seed) = options.shuffle {
seed
@ -911,14 +845,16 @@ pub async fn run_tests_for_worker(
let args = {
let scope = &mut worker.js_runtime.handle_scope();
let seed_value: v8::Local<v8::Value> =
v8::BigInt::new_from_u64(scope, seed as u64).into();
v8::Number::new(scope, seed as f64).into();
[v8::Global::new(scope, seed_value)]
};
let call = worker.js_runtime.call_with_args(&function, &args);
// FIXME: result
let result = worker
.js_runtime
.with_event_loop_promise(call, PollEventLoopOptions::default())
.await;
return Ok(());
}
if let Some(seed) = options.shuffle {
@ -926,11 +862,7 @@ pub async fn run_tests_for_worker(
}
// FILTER
eprintln!("sorted, {:#?}", tc.stack);
let test_functions = tc.tests.1;
let tests: Arc<TestDescriptions> = tc.tests.0.into();
send_test_event(&state_rc, TestEvent::Register(tests.clone()))?;
let res = run_tests_for_worker_inner(
worker,
specifier,
@ -1401,6 +1333,9 @@ pub async fn report_tests(
while let Some((_, event)) = receiver.recv().await {
match event {
TestEvent::GroupRegister(description) => {
reporter.report_register_group(&description);
}
TestEvent::Register(description) => {
for (_, description) in description.into_iter() {
reporter.report_register(description);
@ -1489,6 +1424,7 @@ pub async fn report_tests(
}
std::process::exit(130);
}
_ => {}
}
}

View file

@ -13,6 +13,12 @@ impl CompoundTestReporter {
}
impl TestReporter for CompoundTestReporter {
fn report_register_group(&mut self, description: &TestGroupDescription) {
for reporter in &mut self.test_reporters {
reporter.report_register_group(description)
}
}
fn report_register(&mut self, description: &TestDescription) {
for reporter in &mut self.test_reporters {
reporter.report_register(description);

View file

@ -88,6 +88,7 @@ fn fmt_cancelled() -> String {
#[allow(clippy::print_stdout)]
impl TestReporter for DotTestReporter {
fn report_register_group(&mut self, _description: &TestGroupDescription) {}
fn report_register(&mut self, _description: &TestDescription) {}
fn report_plan(&mut self, plan: &TestPlan) {

View file

@ -80,6 +80,8 @@ impl JunitTestReporter {
}
impl TestReporter for JunitTestReporter {
fn report_register_group(&mut self, _description: &TestGroupDescription) {}
fn report_register(&mut self, description: &TestDescription) {
let mut case = quick_junit::TestCase::new(
description.name.clone(),

View file

@ -17,6 +17,7 @@ pub use tap::TapTestReporter;
pub trait TestReporter {
fn report_register(&mut self, description: &TestDescription);
fn report_register_group(&mut self, description: &TestGroupDescription);
fn report_plan(&mut self, plan: &TestPlan);
fn report_wait(&mut self, description: &TestDescription);
fn report_slow(&mut self, description: &TestDescription, elapsed: u64);

View file

@ -167,6 +167,8 @@ impl PrettyTestReporter {
}
impl TestReporter for PrettyTestReporter {
fn report_register_group(&mut self, _description: &TestGroupDescription) {}
fn report_register(&mut self, _description: &TestDescription) {}
fn report_plan(&mut self, plan: &TestPlan) {
self.write_output_end();

View file

@ -123,6 +123,7 @@ impl TapTestReporter {
#[allow(clippy::print_stdout)]
impl TestReporter for TapTestReporter {
fn report_register_group(&mut self, _description: &TestGroupDescription) {}
fn report_register(&mut self, _description: &TestDescription) {}
fn report_plan(&mut self, plan: &TestPlan) {

View file

@ -476,6 +476,10 @@ const NOT_IMPORTED_OPS = [
"op_test_event_step_result_ignored",
"op_test_event_step_result_ok",
"op_test_event_step_wait",
"op_test_event_start",
"op_test_event_result_ignored",
"op_test_event_result_ok",
"op_test_event_result_failed",
"op_test_op_sanitizer_collect",
"op_test_op_sanitizer_finish",
"op_test_op_sanitizer_get_async_message",
@ -483,9 +487,9 @@ const NOT_IMPORTED_OPS = [
"op_restore_test_permissions",
"op_register_test_step",
"op_register_test",
"op_register_test_group",
"op_test_group_pop",
"op_register_test_group_lifecycle",
"op_test_group_register",
"op_test_group_event_start",
"op_test_group_event_end",
"op_register_test_run_fn",
"op_test_get_origin",
"op_pledge_test_permissions",