mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
WIP
This commit is contained in:
parent
e6c455c613
commit
777c935603
11 changed files with 339 additions and 225 deletions
|
@ -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);
|
||||
|
|
|
@ -421,6 +421,7 @@ impl TestRun {
|
|||
}
|
||||
test::TestEvent::ForceEndReport => {}
|
||||
test::TestEvent::Sigint => {}
|
||||
_ => {} // FIXME
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in a new issue