mirror of
https://github.com/denoland/deno.git
synced 2024-11-28 16:20:57 -05:00
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.
This commit is contained in:
parent
2b35ddfc46
commit
67b1b0f183
10 changed files with 450 additions and 705 deletions
|
@ -2,21 +2,16 @@
|
|||
|
||||
const core = globalThis.Deno.core;
|
||||
const ops = core.ops;
|
||||
const internals = globalThis.__bootstrap.internals;
|
||||
import { setExitHandler } from "ext:runtime/30_os.js";
|
||||
import { Console } from "ext:deno_console/02_console.js";
|
||||
import { serializePermissions } from "ext:runtime/10_permissions.js";
|
||||
import { assert } from "ext:deno_web/00_infra.js";
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const {
|
||||
ArrayFrom,
|
||||
ArrayPrototypeFilter,
|
||||
ArrayPrototypeJoin,
|
||||
ArrayPrototypeMap,
|
||||
ArrayPrototypePush,
|
||||
ArrayPrototypeShift,
|
||||
ArrayPrototypeSort,
|
||||
BigInt,
|
||||
DateNow,
|
||||
Error,
|
||||
FunctionPrototype,
|
||||
|
@ -36,6 +31,7 @@ const {
|
|||
} = primordials;
|
||||
|
||||
const opSanitizerDelayResolveQueue = [];
|
||||
let hasSetOpSanitizerDelayMacrotask = false;
|
||||
|
||||
// Even if every resource is closed by the end of a test, there can be a delay
|
||||
// until the pending ops have all finished. This function returns a promise
|
||||
|
@ -47,6 +43,10 @@ const opSanitizerDelayResolveQueue = [];
|
|||
// before that, though, in order to give time for worker message ops to finish
|
||||
// (since timeouts of 0 don't queue tasks in the timer queue immediately).
|
||||
function opSanitizerDelay() {
|
||||
if (!hasSetOpSanitizerDelayMacrotask) {
|
||||
core.setMacrotaskCallback(handleOpSanitizerDelayMacrotask);
|
||||
hasSetOpSanitizerDelayMacrotask = true;
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
ArrayPrototypePush(opSanitizerDelayResolveQueue, resolve);
|
||||
|
@ -415,9 +415,28 @@ function assertExit(fn, isTest) {
|
|||
};
|
||||
}
|
||||
|
||||
function assertTestStepScopes(fn) {
|
||||
function wrapOuter(fn, desc) {
|
||||
return async function outerWrapped() {
|
||||
try {
|
||||
if (desc.ignore) {
|
||||
return "ignored";
|
||||
}
|
||||
return await fn(desc) ?? "ok";
|
||||
} catch (error) {
|
||||
return { failed: { jsError: core.destructureError(error) } };
|
||||
} finally {
|
||||
const state = MapPrototypeGet(testStates, desc.id);
|
||||
for (const childDesc of state.children) {
|
||||
stepReportResult(childDesc, { failed: "incomplete" }, 0);
|
||||
}
|
||||
state.completed = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function wrapInner(fn) {
|
||||
/** @param desc {TestDescription | TestStepDescription} */
|
||||
return async function testStepSanitizer(desc) {
|
||||
return async function innerWrapped(desc) {
|
||||
function getRunningStepDescs() {
|
||||
const results = [];
|
||||
let childDesc = desc;
|
||||
|
@ -458,11 +477,17 @@ function assertTestStepScopes(fn) {
|
|||
};
|
||||
}
|
||||
await fn(MapPrototypeGet(testStates, desc.id).context);
|
||||
let failedSteps = 0;
|
||||
for (const childDesc of MapPrototypeGet(testStates, desc.id).children) {
|
||||
if (!MapPrototypeGet(testStates, childDesc.id).completed) {
|
||||
const state = MapPrototypeGet(testStates, childDesc.id);
|
||||
if (!state.completed) {
|
||||
return { failed: "incompleteSteps" };
|
||||
}
|
||||
if (state.failed) {
|
||||
failedSteps++;
|
||||
}
|
||||
}
|
||||
return failedSteps == 0 ? null : { failed: { failedSteps } };
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -495,7 +520,6 @@ function withPermissions(fn, permissions) {
|
|||
* fn: TestFunction
|
||||
* origin: string,
|
||||
* location: TestLocation,
|
||||
* filteredOut: boolean,
|
||||
* ignore: boolean,
|
||||
* only: boolean.
|
||||
* sanitizeOps: boolean,
|
||||
|
@ -538,7 +562,6 @@ function withPermissions(fn, permissions) {
|
|||
* name: string,
|
||||
* fn: BenchFunction
|
||||
* origin: string,
|
||||
* filteredOut: boolean,
|
||||
* ignore: boolean,
|
||||
* only: boolean.
|
||||
* sanitizeExit: boolean,
|
||||
|
@ -546,14 +569,8 @@ function withPermissions(fn, permissions) {
|
|||
* }} BenchDescription
|
||||
*/
|
||||
|
||||
/** @type {TestDescription[]} */
|
||||
const testDescs = [];
|
||||
/** @type {Map<number, TestState | TestStepState>} */
|
||||
const testStates = new Map();
|
||||
/** @type {BenchDescription[]} */
|
||||
const benchDescs = [];
|
||||
let isTestSubcommand = false;
|
||||
let isBenchSubcommand = false;
|
||||
|
||||
// Main test function provided by Deno.
|
||||
function test(
|
||||
|
@ -561,7 +578,7 @@ function test(
|
|||
optionsOrFn,
|
||||
maybeFn,
|
||||
) {
|
||||
if (!isTestSubcommand) {
|
||||
if (typeof ops.op_register_test != "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -647,19 +664,17 @@ function test(
|
|||
|
||||
// Delete this prop in case the user passed it. It's used to detect steps.
|
||||
delete testDesc.parent;
|
||||
testDesc.origin = getTestOrigin();
|
||||
const jsError = core.destructureError(new Error());
|
||||
testDesc.location = {
|
||||
fileName: jsError.frames[1].fileName,
|
||||
lineNumber: jsError.frames[1].lineNumber,
|
||||
columnNumber: jsError.frames[1].columnNumber,
|
||||
};
|
||||
testDesc.fn = wrapTest(testDesc);
|
||||
|
||||
const { id, filteredOut } = ops.op_register_test(testDesc);
|
||||
const { id, origin } = ops.op_register_test(testDesc);
|
||||
testDesc.id = id;
|
||||
testDesc.filteredOut = filteredOut;
|
||||
|
||||
ArrayPrototypePush(testDescs, testDesc);
|
||||
testDesc.origin = origin;
|
||||
MapPrototypeSet(testStates, testDesc.id, {
|
||||
context: createTestContext(testDesc),
|
||||
children: [],
|
||||
|
@ -673,7 +688,7 @@ function bench(
|
|||
optionsOrFn,
|
||||
maybeFn,
|
||||
) {
|
||||
if (!isBenchSubcommand) {
|
||||
if (typeof ops.op_register_bench != "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -756,36 +771,13 @@ function bench(
|
|||
benchDesc = { ...defaults, ...nameOrFnOrOptions, fn, name };
|
||||
}
|
||||
|
||||
benchDesc.origin = getBenchOrigin();
|
||||
const AsyncFunction = (async () => {}).constructor;
|
||||
benchDesc.async = AsyncFunction === benchDesc.fn.constructor;
|
||||
benchDesc.fn = wrapBenchmark(benchDesc);
|
||||
|
||||
const { id, filteredOut } = ops.op_register_bench(benchDesc);
|
||||
const { id, origin } = ops.op_register_bench(benchDesc);
|
||||
benchDesc.id = id;
|
||||
benchDesc.filteredOut = filteredOut;
|
||||
|
||||
ArrayPrototypePush(benchDescs, benchDesc);
|
||||
}
|
||||
|
||||
async function runTest(desc) {
|
||||
if (desc.ignore) {
|
||||
return "ignored";
|
||||
}
|
||||
let testFn = wrapTestFnWithSanitizers(desc.fn, desc);
|
||||
if (!("parent" in desc) && desc.permissions) {
|
||||
testFn = withPermissions(
|
||||
testFn,
|
||||
desc.permissions,
|
||||
);
|
||||
}
|
||||
try {
|
||||
const result = await testFn(desc);
|
||||
if (result) return result;
|
||||
const failedSteps = failedChildStepsCount(desc);
|
||||
return failedSteps === 0 ? "ok" : { failed: { failedSteps } };
|
||||
} catch (error) {
|
||||
return { failed: { jsError: core.destructureError(error) } };
|
||||
}
|
||||
benchDesc.origin = origin;
|
||||
}
|
||||
|
||||
function compareMeasurements(a, b) {
|
||||
|
@ -808,8 +800,7 @@ function benchStats(n, highPrecision, avg, min, max, all) {
|
|||
};
|
||||
}
|
||||
|
||||
async function benchMeasure(timeBudget, desc) {
|
||||
const fn = desc.fn;
|
||||
async function benchMeasure(timeBudget, fn, async) {
|
||||
let n = 0;
|
||||
let avg = 0;
|
||||
let wavg = 0;
|
||||
|
@ -823,7 +814,7 @@ async function benchMeasure(timeBudget, desc) {
|
|||
let iterations = 20;
|
||||
let budget = 10 * 1e6;
|
||||
|
||||
if (!desc.async) {
|
||||
if (!async) {
|
||||
while (budget > 0 || iterations-- > 0) {
|
||||
const t1 = benchNow();
|
||||
|
||||
|
@ -854,7 +845,7 @@ async function benchMeasure(timeBudget, desc) {
|
|||
let iterations = 10;
|
||||
let budget = timeBudget * 1e6;
|
||||
|
||||
if (!desc.async) {
|
||||
if (!async) {
|
||||
while (budget > 0 || iterations-- > 0) {
|
||||
const t1 = benchNow();
|
||||
|
||||
|
@ -887,7 +878,7 @@ async function benchMeasure(timeBudget, desc) {
|
|||
let iterations = 10;
|
||||
let budget = timeBudget * 1e6;
|
||||
|
||||
if (!desc.async) {
|
||||
if (!async) {
|
||||
while (budget > 0 || iterations-- > 0) {
|
||||
const t1 = benchNow();
|
||||
for (let c = 0; c < lowPrecisionThresholdInNs; c++) fn();
|
||||
|
@ -920,173 +911,49 @@ async function benchMeasure(timeBudget, desc) {
|
|||
return benchStats(n, wavg > lowPrecisionThresholdInNs, avg, min, max, all);
|
||||
}
|
||||
|
||||
async function runBench(desc) {
|
||||
let token = null;
|
||||
/** Wrap a user benchmark function in one which returns a structured result. */
|
||||
function wrapBenchmark(desc) {
|
||||
const fn = desc.fn;
|
||||
return async function outerWrapped() {
|
||||
let token = null;
|
||||
const originalConsole = globalThis.console;
|
||||
|
||||
try {
|
||||
if (desc.permissions) {
|
||||
token = pledgePermissions(desc.permissions);
|
||||
}
|
||||
|
||||
if (desc.sanitizeExit) {
|
||||
setExitHandler((exitCode) => {
|
||||
assert(
|
||||
false,
|
||||
`Bench attempted to exit with exit code: ${exitCode}`,
|
||||
);
|
||||
try {
|
||||
globalThis.console = new Console((s) => {
|
||||
ops.op_dispatch_bench_event({ output: s });
|
||||
});
|
||||
|
||||
if (desc.permissions) {
|
||||
token = pledgePermissions(desc.permissions);
|
||||
}
|
||||
|
||||
if (desc.sanitizeExit) {
|
||||
setExitHandler((exitCode) => {
|
||||
assert(
|
||||
false,
|
||||
`Bench attempted to exit with exit code: ${exitCode}`,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const benchTimeInMs = 500;
|
||||
const stats = await benchMeasure(benchTimeInMs, fn, desc.async);
|
||||
|
||||
return { ok: stats };
|
||||
} catch (error) {
|
||||
return { failed: core.destructureError(error) };
|
||||
} finally {
|
||||
globalThis.console = originalConsole;
|
||||
if (bench.sanitizeExit) setExitHandler(null);
|
||||
if (token !== null) restorePermissions(token);
|
||||
}
|
||||
|
||||
const benchTimeInMs = 500;
|
||||
const stats = await benchMeasure(benchTimeInMs, desc);
|
||||
|
||||
return { ok: stats };
|
||||
} catch (error) {
|
||||
return { failed: core.destructureError(error) };
|
||||
} finally {
|
||||
if (bench.sanitizeExit) setExitHandler(null);
|
||||
if (token !== null) restorePermissions(token);
|
||||
}
|
||||
}
|
||||
|
||||
let origin = null;
|
||||
|
||||
function getTestOrigin() {
|
||||
if (origin == null) {
|
||||
origin = ops.op_get_test_origin();
|
||||
}
|
||||
return origin;
|
||||
}
|
||||
|
||||
function getBenchOrigin() {
|
||||
if (origin == null) {
|
||||
origin = ops.op_get_bench_origin();
|
||||
}
|
||||
return origin;
|
||||
};
|
||||
}
|
||||
|
||||
function benchNow() {
|
||||
return ops.op_bench_now();
|
||||
}
|
||||
|
||||
function enableTest() {
|
||||
isTestSubcommand = true;
|
||||
}
|
||||
|
||||
function enableBench() {
|
||||
isBenchSubcommand = true;
|
||||
}
|
||||
|
||||
async function runTests({
|
||||
shuffle = null,
|
||||
} = {}) {
|
||||
core.setMacrotaskCallback(handleOpSanitizerDelayMacrotask);
|
||||
|
||||
const origin = getTestOrigin();
|
||||
const only = ArrayPrototypeFilter(testDescs, (test) => test.only);
|
||||
const filtered = ArrayPrototypeFilter(
|
||||
only.length > 0 ? only : testDescs,
|
||||
(desc) => !desc.filteredOut,
|
||||
);
|
||||
|
||||
ops.op_dispatch_test_event({
|
||||
plan: {
|
||||
origin,
|
||||
total: filtered.length,
|
||||
filteredOut: testDescs.length - filtered.length,
|
||||
usedOnly: only.length > 0,
|
||||
},
|
||||
});
|
||||
|
||||
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 = 0x80000000n;
|
||||
const a = 1103515245n;
|
||||
const c = 12345n;
|
||||
|
||||
return function (max) {
|
||||
return state = ((a * state + c) % m) % BigInt(max);
|
||||
};
|
||||
}(BigInt(shuffle));
|
||||
|
||||
for (let i = filtered.length - 1; i > 0; i--) {
|
||||
const j = nextInt(i);
|
||||
[filtered[i], filtered[j]] = [filtered[j], filtered[i]];
|
||||
}
|
||||
}
|
||||
|
||||
for (const desc of filtered) {
|
||||
if (ops.op_tests_should_stop()) {
|
||||
break;
|
||||
}
|
||||
ops.op_dispatch_test_event({ wait: desc.id });
|
||||
const earlier = DateNow();
|
||||
const result = await runTest(desc);
|
||||
const elapsed = DateNow() - earlier;
|
||||
const state = MapPrototypeGet(testStates, desc.id);
|
||||
state.completed = true;
|
||||
for (const childDesc of state.children) {
|
||||
stepReportResult(childDesc, { failed: "incomplete" }, 0);
|
||||
}
|
||||
ops.op_dispatch_test_event({
|
||||
result: [desc.id, result, elapsed],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function runBenchmarks() {
|
||||
core.setMacrotaskCallback(handleOpSanitizerDelayMacrotask);
|
||||
|
||||
const origin = getBenchOrigin();
|
||||
const originalConsole = globalThis.console;
|
||||
|
||||
globalThis.console = new Console((s) => {
|
||||
ops.op_dispatch_bench_event({ output: s });
|
||||
});
|
||||
|
||||
const only = ArrayPrototypeFilter(benchDescs, (bench) => bench.only);
|
||||
const filtered = ArrayPrototypeFilter(
|
||||
only.length > 0 ? only : benchDescs,
|
||||
(desc) => !desc.filteredOut && !desc.ignore,
|
||||
);
|
||||
|
||||
let groups = new Set();
|
||||
// make sure ungrouped benchmarks are placed above grouped
|
||||
groups.add(undefined);
|
||||
|
||||
for (const desc of filtered) {
|
||||
desc.group ||= undefined;
|
||||
groups.add(desc.group);
|
||||
}
|
||||
|
||||
groups = ArrayFrom(groups);
|
||||
ArrayPrototypeSort(
|
||||
filtered,
|
||||
(a, b) => groups.indexOf(a.group) - groups.indexOf(b.group),
|
||||
);
|
||||
|
||||
ops.op_dispatch_bench_event({
|
||||
plan: {
|
||||
origin,
|
||||
total: filtered.length,
|
||||
usedOnly: only.length > 0,
|
||||
names: ArrayPrototypeMap(filtered, (desc) => desc.name),
|
||||
},
|
||||
});
|
||||
|
||||
for (const desc of filtered) {
|
||||
desc.baseline = !!desc.baseline;
|
||||
ops.op_dispatch_bench_event({ wait: desc.id });
|
||||
ops.op_dispatch_bench_event({
|
||||
result: [desc.id, await runBench(desc)],
|
||||
});
|
||||
}
|
||||
|
||||
globalThis.console = originalConsole;
|
||||
}
|
||||
|
||||
function getFullName(desc) {
|
||||
if ("parent" in desc) {
|
||||
return `${getFullName(desc.parent)} ... ${desc.name}`;
|
||||
|
@ -1108,13 +975,6 @@ function stepReportResult(desc, result, elapsed) {
|
|||
});
|
||||
}
|
||||
|
||||
function failedChildStepsCount(desc) {
|
||||
return ArrayPrototypeFilter(
|
||||
MapPrototypeGet(testStates, desc.id).children,
|
||||
(d) => MapPrototypeGet(testStates, d.id).failed,
|
||||
).length;
|
||||
}
|
||||
|
||||
/** @param desc {TestDescription | TestStepDescription} */
|
||||
function createTestContext(desc) {
|
||||
let parent;
|
||||
|
@ -1191,7 +1051,6 @@ function createTestContext(desc) {
|
|||
stepDesc.sanitizeOps ??= desc.sanitizeOps;
|
||||
stepDesc.sanitizeResources ??= desc.sanitizeResources;
|
||||
stepDesc.sanitizeExit ??= desc.sanitizeExit;
|
||||
stepDesc.origin = getTestOrigin();
|
||||
const jsError = core.destructureError(new Error());
|
||||
stepDesc.location = {
|
||||
fileName: jsError.frames[1].fileName,
|
||||
|
@ -1202,8 +1061,10 @@ function createTestContext(desc) {
|
|||
stepDesc.parent = desc;
|
||||
stepDesc.rootId = rootId;
|
||||
stepDesc.rootName = rootName;
|
||||
const { id } = ops.op_register_test_step(stepDesc);
|
||||
stepDesc.fn = wrapTest(stepDesc);
|
||||
const { id, origin } = ops.op_register_test_step(stepDesc);
|
||||
stepDesc.id = id;
|
||||
stepDesc.origin = origin;
|
||||
const state = {
|
||||
context: createTestContext(stepDesc),
|
||||
children: [],
|
||||
|
@ -1218,10 +1079,9 @@ function createTestContext(desc) {
|
|||
|
||||
ops.op_dispatch_test_event({ stepWait: stepDesc.id });
|
||||
const earlier = DateNow();
|
||||
const result = await runTest(stepDesc);
|
||||
const result = await stepDesc.fn(stepDesc);
|
||||
const elapsed = DateNow() - earlier;
|
||||
state.failed = !!result.failed;
|
||||
state.completed = true;
|
||||
stepReportResult(stepDesc, result, elapsed);
|
||||
return result == "ok";
|
||||
},
|
||||
|
@ -1229,37 +1089,29 @@ function createTestContext(desc) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Wrap a user test function in one which returns a structured result.
|
||||
* @template T {Function}
|
||||
* @param testFn {T}
|
||||
* @param opts {{
|
||||
* sanitizeOps: boolean,
|
||||
* sanitizeResources: boolean,
|
||||
* sanitizeExit: boolean,
|
||||
* }}
|
||||
* @param desc {TestDescription | TestStepDescription}
|
||||
* @returns {T}
|
||||
*/
|
||||
function wrapTestFnWithSanitizers(testFn, opts) {
|
||||
testFn = assertTestStepScopes(testFn);
|
||||
|
||||
if (opts.sanitizeOps) {
|
||||
function wrapTest(desc) {
|
||||
let testFn = wrapInner(desc.fn);
|
||||
if (desc.sanitizeOps) {
|
||||
testFn = assertOps(testFn);
|
||||
}
|
||||
if (opts.sanitizeResources) {
|
||||
if (desc.sanitizeResources) {
|
||||
testFn = assertResources(testFn);
|
||||
}
|
||||
if (opts.sanitizeExit) {
|
||||
if (desc.sanitizeExit) {
|
||||
testFn = assertExit(testFn, true);
|
||||
}
|
||||
return testFn;
|
||||
if (!("parent" in desc) && desc.permissions) {
|
||||
testFn = withPermissions(testFn, desc.permissions);
|
||||
}
|
||||
return wrapOuter(testFn, desc);
|
||||
}
|
||||
|
||||
internals.testing = {
|
||||
runTests,
|
||||
runBenchmarks,
|
||||
enableTest,
|
||||
enableBench,
|
||||
};
|
||||
|
||||
import { denoNs } from "ext:runtime/90_deno_ns.js";
|
||||
denoNs.bench = bench;
|
||||
denoNs.test = test;
|
||||
|
|
|
@ -10,13 +10,11 @@ use crate::lsp::client::Client;
|
|||
use crate::lsp::client::TestingNotification;
|
||||
use crate::lsp::config;
|
||||
use crate::lsp::logging::lsp_log;
|
||||
use crate::ops;
|
||||
use crate::proc_state;
|
||||
use crate::tools::test;
|
||||
use crate::tools::test::FailFastTracker;
|
||||
use crate::tools::test::TestEventSender;
|
||||
use crate::util::checksum;
|
||||
use crate::worker::create_main_worker_for_test_or_bench;
|
||||
|
||||
use deno_core::anyhow::anyhow;
|
||||
use deno_core::error::AnyError;
|
||||
|
@ -27,10 +25,7 @@ use deno_core::futures::StreamExt;
|
|||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::parking_lot::RwLock;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_runtime::deno_io::Stdio;
|
||||
use deno_runtime::deno_io::StdioPipe;
|
||||
use deno_runtime::permissions::Permissions;
|
||||
use deno_runtime::permissions::PermissionsContainer;
|
||||
use deno_runtime::tokio_util::run_local;
|
||||
use indexmap::IndexMap;
|
||||
use std::collections::HashMap;
|
||||
|
@ -147,42 +142,6 @@ impl LspTestFilter {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn test_specifier(
|
||||
ps: proc_state::ProcState,
|
||||
permissions: Permissions,
|
||||
specifier: ModuleSpecifier,
|
||||
mode: test::TestMode,
|
||||
sender: TestEventSender,
|
||||
fail_fast_tracker: FailFastTracker,
|
||||
token: CancellationToken,
|
||||
filter: test::TestFilter,
|
||||
) -> Result<(), AnyError> {
|
||||
if !token.is_cancelled() {
|
||||
let stdout = StdioPipe::File(sender.stdout());
|
||||
let stderr = StdioPipe::File(sender.stderr());
|
||||
let mut worker = create_main_worker_for_test_or_bench(
|
||||
&ps,
|
||||
specifier.clone(),
|
||||
PermissionsContainer::new(permissions),
|
||||
vec![ops::testing::deno_test::init_ops(
|
||||
sender,
|
||||
fail_fast_tracker,
|
||||
filter,
|
||||
)],
|
||||
Stdio {
|
||||
stdin: StdioPipe::Inherit,
|
||||
stdout,
|
||||
stderr,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
worker.run_lsp_test_specifier(mode).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TestRun {
|
||||
id: u32,
|
||||
|
@ -300,7 +259,6 @@ impl TestRun {
|
|||
Arc::new(RwLock::new(IndexMap::new()));
|
||||
let mut test_steps = IndexMap::new();
|
||||
|
||||
let tests_ = tests.clone();
|
||||
let join_handles = queue.into_iter().map(move |specifier| {
|
||||
let specifier = specifier.clone();
|
||||
let ps = ps.clone();
|
||||
|
@ -321,38 +279,30 @@ impl TestRun {
|
|||
.unwrap_or_default(),
|
||||
};
|
||||
let token = self.token.clone();
|
||||
let tests = tests_.clone();
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
if fail_fast_tracker.should_stop() {
|
||||
return Ok(());
|
||||
}
|
||||
let origin = specifier.to_string();
|
||||
let file_result = run_local(test_specifier(
|
||||
ps,
|
||||
permissions,
|
||||
specifier,
|
||||
test::TestMode::Executable,
|
||||
sender.clone(),
|
||||
fail_fast_tracker,
|
||||
token,
|
||||
filter,
|
||||
));
|
||||
let file_result = if token.is_cancelled() {
|
||||
Ok(())
|
||||
} else {
|
||||
run_local(test::test_specifier(
|
||||
&ps,
|
||||
permissions,
|
||||
specifier,
|
||||
sender.clone(),
|
||||
fail_fast_tracker,
|
||||
filter,
|
||||
))
|
||||
};
|
||||
if let Err(error) = file_result {
|
||||
if error.is::<JsError>() {
|
||||
sender.send(test::TestEvent::UncaughtError(
|
||||
origin.clone(),
|
||||
origin,
|
||||
Box::new(error.downcast::<JsError>().unwrap()),
|
||||
))?;
|
||||
for desc in tests.read().values() {
|
||||
if desc.origin == origin {
|
||||
sender.send(test::TestEvent::Result(
|
||||
desc.id,
|
||||
test::TestResult::Cancelled,
|
||||
0,
|
||||
))?
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(error);
|
||||
}
|
||||
|
@ -489,6 +439,7 @@ impl TestRun {
|
|||
.iter()
|
||||
.map(|s| s.as_str()),
|
||||
);
|
||||
args.push("--trace-ops");
|
||||
if self.workspace_settings.unstable && !args.contains(&"--unstable") {
|
||||
args.push("--unstable");
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ use std::time;
|
|||
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;
|
||||
|
@ -19,24 +21,26 @@ use uuid::Uuid;
|
|||
|
||||
use crate::tools::bench::BenchDescription;
|
||||
use crate::tools::bench::BenchEvent;
|
||||
use crate::tools::test::TestFilter;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct BenchContainer(
|
||||
pub Vec<(BenchDescription, v8::Global<v8::Function>)>,
|
||||
);
|
||||
|
||||
deno_core::extension!(deno_bench,
|
||||
ops = [
|
||||
op_pledge_test_permissions,
|
||||
op_restore_test_permissions,
|
||||
op_get_bench_origin,
|
||||
op_register_bench,
|
||||
op_dispatch_bench_event,
|
||||
op_bench_now,
|
||||
],
|
||||
options = {
|
||||
sender: UnboundedSender<BenchEvent>,
|
||||
filter: TestFilter,
|
||||
},
|
||||
state = |state, options| {
|
||||
state.put(options.sender);
|
||||
state.put(options.filter);
|
||||
state.put(BenchContainer::default());
|
||||
},
|
||||
customizer = |ext: &mut deno_core::ExtensionBuilder| {
|
||||
ext.force_op_registration();
|
||||
|
@ -90,51 +94,61 @@ pub fn op_restore_test_permissions(
|
|||
}
|
||||
}
|
||||
|
||||
#[op]
|
||||
fn op_get_bench_origin(state: &mut OpState) -> String {
|
||||
state.borrow::<ModuleSpecifier>().to_string()
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct BenchInfo {
|
||||
struct BenchInfo<'s> {
|
||||
#[serde(rename = "fn")]
|
||||
function: serde_v8::Value<'s>,
|
||||
name: String,
|
||||
origin: String,
|
||||
baseline: bool,
|
||||
group: Option<String>,
|
||||
ignore: bool,
|
||||
only: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct BenchRegisterResult {
|
||||
id: usize,
|
||||
filtered_out: bool,
|
||||
origin: String,
|
||||
}
|
||||
|
||||
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
#[op]
|
||||
fn op_register_bench(
|
||||
#[op(v8)]
|
||||
fn op_register_bench<'a>(
|
||||
scope: &mut v8::HandleScope<'a>,
|
||||
state: &mut OpState,
|
||||
info: BenchInfo,
|
||||
info: BenchInfo<'a>,
|
||||
) -> Result<BenchRegisterResult, AnyError> {
|
||||
let id = NEXT_ID.fetch_add(1, Ordering::SeqCst);
|
||||
let filter = state.borrow::<TestFilter>().clone();
|
||||
let filtered_out = !filter.includes(&info.name);
|
||||
let origin = state.borrow::<ModuleSpecifier>().to_string();
|
||||
let description = BenchDescription {
|
||||
id,
|
||||
name: info.name,
|
||||
origin: info.origin,
|
||||
origin: origin.clone(),
|
||||
baseline: info.baseline,
|
||||
group: info.group,
|
||||
ignore: info.ignore,
|
||||
only: info.only,
|
||||
};
|
||||
let function: v8::Local<v8::Function> = info.function.v8_value.try_into()?;
|
||||
let function = v8::Global::new(scope, function);
|
||||
state
|
||||
.borrow_mut::<BenchContainer>()
|
||||
.0
|
||||
.push((description.clone(), function));
|
||||
let sender = state.borrow::<UnboundedSender<BenchEvent>>().clone();
|
||||
sender.send(BenchEvent::Register(description)).ok();
|
||||
Ok(BenchRegisterResult { id, filtered_out })
|
||||
Ok(BenchRegisterResult { id, origin })
|
||||
}
|
||||
|
||||
#[op]
|
||||
fn op_dispatch_bench_event(state: &mut OpState, event: BenchEvent) {
|
||||
assert!(
|
||||
matches!(event, BenchEvent::Output(_)),
|
||||
"Only output events are expected from JS."
|
||||
);
|
||||
let sender = state.borrow::<UnboundedSender<BenchEvent>>().clone();
|
||||
sender.send(event).ok();
|
||||
}
|
||||
|
|
|
@ -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<v8::Function>)>,
|
||||
);
|
||||
|
||||
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<String, AnyError> {
|
||||
Ok(state.borrow::<ModuleSpecifier>().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<TestRegisterResult, AnyError> {
|
||||
let id = NEXT_ID.fetch_add(1, Ordering::SeqCst);
|
||||
let filter = state.borrow::<TestFilter>().clone();
|
||||
let filtered_out = !filter.includes(&info.name);
|
||||
let origin = state.borrow::<ModuleSpecifier>().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<v8::Function> = info.function.v8_value.try_into()?;
|
||||
let function = v8::Global::new(scope, function);
|
||||
state
|
||||
.borrow_mut::<TestContainer>()
|
||||
.0
|
||||
.push((description.clone(), function));
|
||||
let mut sender = state.borrow::<TestEventSender>().clone();
|
||||
sender.send(TestEvent::Register(description)).ok();
|
||||
Ok(TestRegisterResult { id, filtered_out })
|
||||
Ok(TestRegisterResult { id, origin })
|
||||
}
|
||||
|
||||
fn deserialize_parent<'de, D>(deserializer: D) -> Result<usize, D::Error>
|
||||
|
@ -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<TestRegisterResult, AnyError> {
|
||||
let id = NEXT_ID.fetch_add(1, Ordering::SeqCst);
|
||||
let origin = state.borrow::<ModuleSpecifier>().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::<TestEventSender>().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::<FailFastTracker>().add_failure();
|
||||
}
|
||||
assert!(
|
||||
matches!(event, TestEvent::StepWait(_) | TestEvent::StepResult(..)),
|
||||
"Only step wait/result events are expected from JS."
|
||||
);
|
||||
let mut sender = state.borrow::<TestEventSender>().clone();
|
||||
sender.send(event).ok();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[op]
|
||||
fn op_tests_should_stop(state: &mut OpState) -> bool {
|
||||
state.borrow::<FailFastTracker>().should_stop()
|
||||
}
|
||||
|
|
1
cli/tests/testdata/test/doc_only.out
vendored
1
cli/tests/testdata/test/doc_only.out
vendored
|
@ -1,5 +1,4 @@
|
|||
Check [WILDCARD]/test/doc_only/mod.ts$2-5.ts
|
||||
running 0 tests from ./test/doc_only/mod.ts
|
||||
|
||||
ok | 0 passed | 0 failed ([WILDCARD])
|
||||
|
||||
|
|
36
cli/tests/testdata/test/shuffle.out
vendored
36
cli/tests/testdata/test/shuffle.out
vendored
|
@ -2,38 +2,38 @@ Check [WILDCARD]/test/shuffle/bar_test.ts
|
|||
Check [WILDCARD]/test/shuffle/baz_test.ts
|
||||
Check [WILDCARD]/test/shuffle/foo_test.ts
|
||||
running 10 tests from ./test/shuffle/foo_test.ts
|
||||
test 2 ... ok ([WILDCARD])
|
||||
test 3 ... ok ([WILDCARD])
|
||||
test 6 ... ok ([WILDCARD])
|
||||
test 9 ... ok ([WILDCARD])
|
||||
test 8 ... ok ([WILDCARD])
|
||||
test 2 ... ok ([WILDCARD])
|
||||
test 7 ... ok ([WILDCARD])
|
||||
test 5 ... ok ([WILDCARD])
|
||||
test 4 ... ok ([WILDCARD])
|
||||
test 1 ... ok ([WILDCARD])
|
||||
test 8 ... ok ([WILDCARD])
|
||||
test 0 ... ok ([WILDCARD])
|
||||
test 9 ... ok ([WILDCARD])
|
||||
test 4 ... ok ([WILDCARD])
|
||||
test 6 ... ok ([WILDCARD])
|
||||
test 1 ... ok ([WILDCARD])
|
||||
running 10 tests from ./test/shuffle/baz_test.ts
|
||||
test 2 ... ok ([WILDCARD])
|
||||
test 3 ... ok ([WILDCARD])
|
||||
test 6 ... ok ([WILDCARD])
|
||||
test 9 ... ok ([WILDCARD])
|
||||
test 8 ... ok ([WILDCARD])
|
||||
test 2 ... ok ([WILDCARD])
|
||||
test 7 ... ok ([WILDCARD])
|
||||
test 5 ... ok ([WILDCARD])
|
||||
test 4 ... ok ([WILDCARD])
|
||||
test 1 ... ok ([WILDCARD])
|
||||
test 8 ... ok ([WILDCARD])
|
||||
test 0 ... ok ([WILDCARD])
|
||||
test 9 ... ok ([WILDCARD])
|
||||
test 4 ... ok ([WILDCARD])
|
||||
test 6 ... ok ([WILDCARD])
|
||||
test 1 ... ok ([WILDCARD])
|
||||
running 10 tests from ./test/shuffle/bar_test.ts
|
||||
test 2 ... ok ([WILDCARD])
|
||||
test 3 ... ok ([WILDCARD])
|
||||
test 6 ... ok ([WILDCARD])
|
||||
test 9 ... ok ([WILDCARD])
|
||||
test 8 ... ok ([WILDCARD])
|
||||
test 2 ... ok ([WILDCARD])
|
||||
test 7 ... ok ([WILDCARD])
|
||||
test 5 ... ok ([WILDCARD])
|
||||
test 4 ... ok ([WILDCARD])
|
||||
test 1 ... ok ([WILDCARD])
|
||||
test 8 ... ok ([WILDCARD])
|
||||
test 0 ... ok ([WILDCARD])
|
||||
test 9 ... ok ([WILDCARD])
|
||||
test 4 ... ok ([WILDCARD])
|
||||
test 6 ... ok ([WILDCARD])
|
||||
test 1 ... ok ([WILDCARD])
|
||||
|
||||
ok | 30 passed | 0 failed ([WILDCARD])
|
||||
|
||||
|
|
1
cli/tests/testdata/test/text.out
vendored
1
cli/tests/testdata/test/text.out
vendored
|
@ -1,4 +1,3 @@
|
|||
running 0 tests from ./test/text.md
|
||||
|
||||
ok | 0 passed | 0 failed ([WILDCARD])
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ use crate::util::file_watcher::ResolutionResult;
|
|||
use crate::util::fs::collect_specifiers;
|
||||
use crate::util::path::is_supported_ext;
|
||||
use crate::version::get_user_agent;
|
||||
use crate::worker::create_main_worker_for_test_or_bench;
|
||||
use crate::worker::create_custom_worker;
|
||||
|
||||
use deno_core::error::generic_error;
|
||||
use deno_core::error::AnyError;
|
||||
|
@ -24,11 +24,15 @@ use deno_core::futures::future;
|
|||
use deno_core::futures::stream;
|
||||
use deno_core::futures::FutureExt;
|
||||
use deno_core::futures::StreamExt;
|
||||
use deno_core::located_script_name;
|
||||
use deno_core::serde_v8;
|
||||
use deno_core::v8;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_runtime::permissions::Permissions;
|
||||
use deno_runtime::permissions::PermissionsContainer;
|
||||
use deno_runtime::tokio_util::run_local;
|
||||
use indexmap::IndexMap;
|
||||
use indexmap::IndexSet;
|
||||
use log::Level;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
@ -87,6 +91,8 @@ pub struct BenchDescription {
|
|||
pub origin: String,
|
||||
pub baseline: bool,
|
||||
pub group: Option<String>,
|
||||
pub ignore: bool,
|
||||
pub only: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
@ -433,20 +439,80 @@ async fn bench_specifier(
|
|||
ps: ProcState,
|
||||
permissions: Permissions,
|
||||
specifier: ModuleSpecifier,
|
||||
channel: UnboundedSender<BenchEvent>,
|
||||
options: BenchSpecifierOptions,
|
||||
sender: UnboundedSender<BenchEvent>,
|
||||
filter: TestFilter,
|
||||
) -> Result<(), AnyError> {
|
||||
let filter = options.filter;
|
||||
let mut worker = create_main_worker_for_test_or_bench(
|
||||
let mut worker = create_custom_worker(
|
||||
&ps,
|
||||
specifier,
|
||||
specifier.clone(),
|
||||
PermissionsContainer::new(permissions),
|
||||
vec![ops::bench::deno_bench::init_ops(channel, filter)],
|
||||
vec![ops::bench::deno_bench::init_ops(sender.clone())],
|
||||
Default::default(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
worker.run_bench_specifier().await
|
||||
// We execute the main module as a side module so that import.meta.main is not set.
|
||||
worker.execute_side_module_possibly_with_npm().await?;
|
||||
|
||||
let mut worker = worker.into_main_worker();
|
||||
worker.dispatch_load_event(located_script_name!())?;
|
||||
|
||||
let benchmarks = {
|
||||
let state_rc = worker.js_runtime.op_state();
|
||||
let mut state = state_rc.borrow_mut();
|
||||
std::mem::take(&mut state.borrow_mut::<ops::bench::BenchContainer>().0)
|
||||
};
|
||||
let (only, no_only): (Vec<_>, Vec<_>) =
|
||||
benchmarks.into_iter().partition(|(d, _)| d.only);
|
||||
let used_only = !only.is_empty();
|
||||
let benchmarks = if used_only { only } else { no_only };
|
||||
let mut benchmarks = benchmarks
|
||||
.into_iter()
|
||||
.filter(|(d, _)| filter.includes(&d.name) && !d.ignore)
|
||||
.collect::<Vec<_>>();
|
||||
let mut groups = IndexSet::<Option<String>>::new();
|
||||
// make sure ungrouped benchmarks are placed above grouped
|
||||
groups.insert(None);
|
||||
for (desc, _) in &benchmarks {
|
||||
groups.insert(desc.group.clone());
|
||||
}
|
||||
benchmarks.sort_by(|(d1, _), (d2, _)| {
|
||||
groups
|
||||
.get_index_of(&d1.group)
|
||||
.unwrap()
|
||||
.partial_cmp(&groups.get_index_of(&d2.group).unwrap())
|
||||
.unwrap()
|
||||
});
|
||||
sender.send(BenchEvent::Plan(BenchPlan {
|
||||
origin: specifier.to_string(),
|
||||
total: benchmarks.len(),
|
||||
used_only,
|
||||
names: benchmarks.iter().map(|(d, _)| d.name.clone()).collect(),
|
||||
}))?;
|
||||
for (desc, function) in benchmarks {
|
||||
sender.send(BenchEvent::Wait(desc.id))?;
|
||||
let promise = {
|
||||
let scope = &mut worker.js_runtime.handle_scope();
|
||||
let cb = function.open(scope);
|
||||
let this = v8::undefined(scope).into();
|
||||
let promise = cb.call(scope, this, &[]).unwrap();
|
||||
v8::Global::new(scope, promise)
|
||||
};
|
||||
let result = worker.js_runtime.resolve_value(promise).await?;
|
||||
let scope = &mut worker.js_runtime.handle_scope();
|
||||
let result = v8::Local::new(scope, result);
|
||||
let result = serde_v8::from_v8::<BenchResult>(scope, result)?;
|
||||
sender.send(BenchEvent::Result(desc.id, result))?;
|
||||
}
|
||||
|
||||
loop {
|
||||
if !worker.dispatch_beforeunload_event(located_script_name!())? {
|
||||
break;
|
||||
}
|
||||
worker.run_event_loop(false).await?;
|
||||
}
|
||||
worker.dispatch_unload_event(located_script_name!())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test a collection of specifiers with test modes concurrently.
|
||||
|
@ -468,10 +534,9 @@ async fn bench_specifiers(
|
|||
let specifier = specifier;
|
||||
let sender = sender.clone();
|
||||
let options = option_for_handles.clone();
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let future = bench_specifier(ps, permissions, specifier, sender, options);
|
||||
|
||||
let future =
|
||||
bench_specifier(ps, permissions, specifier, sender, options.filter);
|
||||
run_local(future)
|
||||
})
|
||||
});
|
||||
|
|
|
@ -17,7 +17,7 @@ use crate::util::fs::collect_specifiers;
|
|||
use crate::util::path::get_extension;
|
||||
use crate::util::path::is_supported_ext;
|
||||
use crate::util::path::mapped_specifier_for_tsc;
|
||||
use crate::worker::create_main_worker_for_test_or_bench;
|
||||
use crate::worker::create_custom_worker;
|
||||
|
||||
use deno_ast::swc::common::comments::CommentKind;
|
||||
use deno_ast::MediaType;
|
||||
|
@ -29,8 +29,11 @@ use deno_core::futures::future;
|
|||
use deno_core::futures::stream;
|
||||
use deno_core::futures::FutureExt;
|
||||
use deno_core::futures::StreamExt;
|
||||
use deno_core::located_script_name;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::serde_v8;
|
||||
use deno_core::url::Url;
|
||||
use deno_core::v8;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_runtime::deno_io::Stdio;
|
||||
use deno_runtime::deno_io::StdioPipe;
|
||||
|
@ -63,6 +66,7 @@ use std::sync::atomic::Ordering;
|
|||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
use std::time::SystemTime;
|
||||
use tokio::signal;
|
||||
use tokio::sync::mpsc::unbounded_channel;
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
|
@ -144,6 +148,8 @@ pub struct TestLocation {
|
|||
pub struct TestDescription {
|
||||
pub id: usize,
|
||||
pub name: String,
|
||||
pub ignore: bool,
|
||||
pub only: bool,
|
||||
pub origin: String,
|
||||
pub location: TestLocation,
|
||||
}
|
||||
|
@ -900,26 +906,24 @@ pub fn format_test_error(js_error: &JsError) -> String {
|
|||
|
||||
/// Test a single specifier as documentation containing test programs, an executable test module or
|
||||
/// both.
|
||||
async fn test_specifier(
|
||||
pub async fn test_specifier(
|
||||
ps: &ProcState,
|
||||
permissions: Permissions,
|
||||
specifier: ModuleSpecifier,
|
||||
mode: TestMode,
|
||||
sender: TestEventSender,
|
||||
mut sender: TestEventSender,
|
||||
fail_fast_tracker: FailFastTracker,
|
||||
options: TestSpecifierOptions,
|
||||
filter: TestFilter,
|
||||
) -> Result<(), AnyError> {
|
||||
if fail_fast_tracker.should_stop() {
|
||||
return Ok(());
|
||||
}
|
||||
let stdout = StdioPipe::File(sender.stdout());
|
||||
let stderr = StdioPipe::File(sender.stderr());
|
||||
let mut worker = create_main_worker_for_test_or_bench(
|
||||
let mut worker = create_custom_worker(
|
||||
ps,
|
||||
specifier,
|
||||
specifier.clone(),
|
||||
PermissionsContainer::new(permissions),
|
||||
vec![ops::testing::deno_test::init_ops(
|
||||
sender,
|
||||
fail_fast_tracker,
|
||||
options.filter,
|
||||
)],
|
||||
vec![ops::testing::deno_test::init_ops(sender.clone())],
|
||||
Stdio {
|
||||
stdin: StdioPipe::Inherit,
|
||||
stdout,
|
||||
|
@ -928,7 +932,119 @@ async fn test_specifier(
|
|||
)
|
||||
.await?;
|
||||
|
||||
worker.run_test_specifier(mode).await
|
||||
let mut coverage_collector = worker.maybe_setup_coverage_collector().await?;
|
||||
|
||||
// We execute the main module as a side module so that import.meta.main is not set.
|
||||
match worker.execute_side_module_possibly_with_npm().await {
|
||||
Ok(()) => {}
|
||||
Err(error) => {
|
||||
if error.is::<JsError>() {
|
||||
sender.send(TestEvent::UncaughtError(
|
||||
specifier.to_string(),
|
||||
Box::new(error.downcast::<JsError>().unwrap()),
|
||||
))?;
|
||||
return Ok(());
|
||||
} else {
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut worker = worker.into_main_worker();
|
||||
if ps.options.trace_ops() {
|
||||
worker.js_runtime.execute_script_static(
|
||||
located_script_name!(),
|
||||
"Deno[Deno.internal].core.enableOpCallTracing();",
|
||||
)?;
|
||||
}
|
||||
worker.dispatch_load_event(located_script_name!())?;
|
||||
|
||||
let tests = {
|
||||
let state_rc = worker.js_runtime.op_state();
|
||||
let mut state = state_rc.borrow_mut();
|
||||
std::mem::take(&mut state.borrow_mut::<ops::testing::TestContainer>().0)
|
||||
};
|
||||
let unfiltered = tests.len();
|
||||
let (only, no_only): (Vec<_>, Vec<_>) =
|
||||
tests.into_iter().partition(|(d, _)| d.only);
|
||||
let used_only = !only.is_empty();
|
||||
let tests = if used_only { only } else { no_only };
|
||||
let mut tests = tests
|
||||
.into_iter()
|
||||
.filter(|(d, _)| filter.includes(&d.name))
|
||||
.collect::<Vec<_>>();
|
||||
if let Some(seed) = ps.options.shuffle_tests() {
|
||||
tests.shuffle(&mut SmallRng::seed_from_u64(seed));
|
||||
}
|
||||
sender.send(TestEvent::Plan(TestPlan {
|
||||
origin: specifier.to_string(),
|
||||
total: tests.len(),
|
||||
filtered_out: unfiltered - tests.len(),
|
||||
used_only,
|
||||
}))?;
|
||||
let mut had_uncaught_error = false;
|
||||
for (desc, function) in tests {
|
||||
if fail_fast_tracker.should_stop() {
|
||||
break;
|
||||
}
|
||||
if desc.ignore {
|
||||
sender.send(TestEvent::Result(desc.id, TestResult::Ignored, 0))?;
|
||||
continue;
|
||||
}
|
||||
if had_uncaught_error {
|
||||
sender.send(TestEvent::Result(desc.id, TestResult::Cancelled, 0))?;
|
||||
continue;
|
||||
}
|
||||
sender.send(TestEvent::Wait(desc.id))?;
|
||||
let earlier = SystemTime::now();
|
||||
let promise = {
|
||||
let scope = &mut worker.js_runtime.handle_scope();
|
||||
let cb = function.open(scope);
|
||||
let this = v8::undefined(scope).into();
|
||||
let promise = cb.call(scope, this, &[]).unwrap();
|
||||
v8::Global::new(scope, promise)
|
||||
};
|
||||
let result = match worker.js_runtime.resolve_value(promise).await {
|
||||
Ok(r) => r,
|
||||
Err(error) => {
|
||||
if error.is::<JsError>() {
|
||||
sender.send(TestEvent::UncaughtError(
|
||||
specifier.to_string(),
|
||||
Box::new(error.downcast::<JsError>().unwrap()),
|
||||
))?;
|
||||
fail_fast_tracker.add_failure();
|
||||
sender.send(TestEvent::Result(desc.id, TestResult::Cancelled, 0))?;
|
||||
had_uncaught_error = true;
|
||||
continue;
|
||||
} else {
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
let scope = &mut worker.js_runtime.handle_scope();
|
||||
let result = v8::Local::new(scope, result);
|
||||
let result = serde_v8::from_v8::<TestResult>(scope, result)?;
|
||||
if matches!(result, TestResult::Failed(_)) {
|
||||
fail_fast_tracker.add_failure();
|
||||
}
|
||||
let elapsed = SystemTime::now().duration_since(earlier)?.as_millis();
|
||||
sender.send(TestEvent::Result(desc.id, result, elapsed as u64))?;
|
||||
}
|
||||
|
||||
loop {
|
||||
if !worker.dispatch_beforeunload_event(located_script_name!())? {
|
||||
break;
|
||||
}
|
||||
worker.run_event_loop(false).await?;
|
||||
}
|
||||
worker.dispatch_unload_event(located_script_name!())?;
|
||||
|
||||
if let Some(coverage_collector) = coverage_collector.as_mut() {
|
||||
worker
|
||||
.with_event_loop(coverage_collector.stop_collecting().boxed_local())
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn extract_files_from_regex_blocks(
|
||||
|
@ -1182,18 +1298,18 @@ static HAS_TEST_RUN_SIGINT_HANDLER: AtomicBool = AtomicBool::new(false);
|
|||
async fn test_specifiers(
|
||||
ps: &ProcState,
|
||||
permissions: &Permissions,
|
||||
specifiers_with_mode: Vec<(ModuleSpecifier, TestMode)>,
|
||||
specifiers: Vec<ModuleSpecifier>,
|
||||
options: TestSpecifierOptions,
|
||||
) -> Result<(), AnyError> {
|
||||
let log_level = ps.options.log_level();
|
||||
let specifiers_with_mode = if let Some(seed) = ps.options.shuffle_tests() {
|
||||
let specifiers = if let Some(seed) = ps.options.shuffle_tests() {
|
||||
let mut rng = SmallRng::seed_from_u64(seed);
|
||||
let mut specifiers_with_mode = specifiers_with_mode;
|
||||
specifiers_with_mode.sort_by_key(|(specifier, _)| specifier.clone());
|
||||
specifiers_with_mode.shuffle(&mut rng);
|
||||
specifiers_with_mode
|
||||
let mut specifiers = specifiers;
|
||||
specifiers.sort();
|
||||
specifiers.shuffle(&mut rng);
|
||||
specifiers
|
||||
} else {
|
||||
specifiers_with_mode
|
||||
specifiers
|
||||
};
|
||||
|
||||
let (sender, mut receiver) = unbounded_channel::<TestEvent>();
|
||||
|
@ -1207,44 +1323,23 @@ async fn test_specifiers(
|
|||
});
|
||||
HAS_TEST_RUN_SIGINT_HANDLER.store(true, Ordering::Relaxed);
|
||||
|
||||
let join_handles =
|
||||
specifiers_with_mode
|
||||
.into_iter()
|
||||
.map(move |(specifier, mode)| {
|
||||
let ps = ps.clone();
|
||||
let permissions = permissions.clone();
|
||||
let mut sender = sender.clone();
|
||||
let options = options.clone();
|
||||
let fail_fast_tracker = FailFastTracker::new(options.fail_fast);
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
if fail_fast_tracker.should_stop() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let origin = specifier.to_string();
|
||||
let file_result = run_local(test_specifier(
|
||||
&ps,
|
||||
permissions,
|
||||
specifier,
|
||||
mode,
|
||||
sender.clone(),
|
||||
fail_fast_tracker,
|
||||
options,
|
||||
));
|
||||
if let Err(error) = file_result {
|
||||
if error.is::<JsError>() {
|
||||
sender.send(TestEvent::UncaughtError(
|
||||
origin,
|
||||
Box::new(error.downcast::<JsError>().unwrap()),
|
||||
))?;
|
||||
} else {
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
let join_handles = specifiers.into_iter().map(move |specifier| {
|
||||
let ps = ps.clone();
|
||||
let permissions = permissions.clone();
|
||||
let sender = sender.clone();
|
||||
let options = options.clone();
|
||||
let fail_fast_tracker = FailFastTracker::new(options.fail_fast);
|
||||
tokio::task::spawn_blocking(move || {
|
||||
run_local(test_specifier(
|
||||
&ps,
|
||||
permissions,
|
||||
specifier,
|
||||
sender.clone(),
|
||||
fail_fast_tracker,
|
||||
options.filter,
|
||||
))
|
||||
})
|
||||
});
|
||||
|
||||
let join_stream = stream::iter(join_handles)
|
||||
.buffer_unordered(concurrent_jobs.get())
|
||||
|
@ -1310,7 +1405,7 @@ async fn test_specifiers(
|
|||
.push((description.clone(), failure.clone()));
|
||||
}
|
||||
TestResult::Cancelled => {
|
||||
unreachable!("should be handled in TestEvent::UncaughtError");
|
||||
summary.failed += 1;
|
||||
}
|
||||
}
|
||||
reporter.report_result(description, &result, elapsed);
|
||||
|
@ -1321,12 +1416,6 @@ async fn test_specifiers(
|
|||
reporter.report_uncaught_error(&origin, &error);
|
||||
summary.failed += 1;
|
||||
summary.uncaught_errors.push((origin.clone(), error));
|
||||
for desc in tests.values() {
|
||||
if desc.origin == origin && tests_with_result.insert(desc.id) {
|
||||
summary.failed += 1;
|
||||
reporter.report_result(desc, &TestResult::Cancelled, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TestEvent::StepRegister(description) => {
|
||||
|
@ -1360,6 +1449,8 @@ async fn test_specifiers(
|
|||
&tests,
|
||||
&test_steps,
|
||||
),
|
||||
ignore: false,
|
||||
only: false,
|
||||
origin: description.origin.clone(),
|
||||
location: description.location.clone(),
|
||||
},
|
||||
|
@ -1565,7 +1656,13 @@ pub async fn run_tests(
|
|||
test_specifiers(
|
||||
&ps,
|
||||
&permissions,
|
||||
specifiers_with_mode,
|
||||
specifiers_with_mode
|
||||
.into_iter()
|
||||
.filter_map(|(s, m)| match m {
|
||||
TestMode::Documentation => None,
|
||||
_ => Some(s),
|
||||
})
|
||||
.collect(),
|
||||
TestSpecifierOptions {
|
||||
concurrent_jobs: test_options.concurrent_jobs,
|
||||
fail_fast: test_options.fail_fast,
|
||||
|
@ -1729,7 +1826,13 @@ pub async fn run_tests_with_watch(
|
|||
test_specifiers(
|
||||
&ps,
|
||||
permissions,
|
||||
specifiers_with_mode,
|
||||
specifiers_with_mode
|
||||
.into_iter()
|
||||
.filter_map(|(s, m)| match m {
|
||||
TestMode::Documentation => None,
|
||||
_ => Some(s),
|
||||
})
|
||||
.collect(),
|
||||
TestSpecifierOptions {
|
||||
concurrent_jobs: test_options.concurrent_jobs,
|
||||
fail_fast: test_options.fail_fast,
|
||||
|
|
247
cli/worker.rs
247
cli/worker.rs
|
@ -5,14 +5,10 @@ use std::rc::Rc;
|
|||
use std::sync::Arc;
|
||||
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::ascii_str;
|
||||
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::serde_v8;
|
||||
use deno_core::v8;
|
||||
use deno_core::Extension;
|
||||
use deno_core::ModuleId;
|
||||
use deno_runtime::colors;
|
||||
|
@ -36,7 +32,6 @@ use crate::ops;
|
|||
use crate::proc_state::ProcState;
|
||||
use crate::tools;
|
||||
use crate::tools::coverage::CoverageCollector;
|
||||
use crate::tools::test::TestMode;
|
||||
use crate::util::checksum;
|
||||
use crate::version;
|
||||
|
||||
|
@ -45,11 +40,6 @@ pub struct CliMainWorker {
|
|||
is_main_cjs: bool,
|
||||
worker: MainWorker,
|
||||
ps: ProcState,
|
||||
|
||||
js_run_tests_callback: Option<v8::Global<v8::Function>>,
|
||||
js_run_benchmarks_callback: Option<v8::Global<v8::Function>>,
|
||||
js_enable_test_callback: Option<v8::Global<v8::Function>>,
|
||||
js_enable_bench_callback: Option<v8::Global<v8::Function>>,
|
||||
}
|
||||
|
||||
impl CliMainWorker {
|
||||
|
@ -176,114 +166,14 @@ impl CliMainWorker {
|
|||
executor.execute().await
|
||||
}
|
||||
|
||||
pub async fn run_test_specifier(
|
||||
&mut self,
|
||||
mode: TestMode,
|
||||
) -> Result<(), AnyError> {
|
||||
self.enable_test();
|
||||
|
||||
// Enable op call tracing in core to enable better debugging of op sanitizer
|
||||
// failures.
|
||||
if self.ps.options.trace_ops() {
|
||||
self.worker.js_runtime.execute_script_static(
|
||||
located_script_name!(),
|
||||
"Deno[Deno.internal].core.enableOpCallTracing();",
|
||||
)?;
|
||||
}
|
||||
|
||||
let mut maybe_coverage_collector =
|
||||
self.maybe_setup_coverage_collector().await?;
|
||||
|
||||
// We only execute the specifier as a module if it is tagged with TestMode::Module or
|
||||
// TestMode::Both.
|
||||
if mode != TestMode::Documentation {
|
||||
// We execute the module module as a side module so that import.meta.main is not set.
|
||||
self.execute_side_module_possibly_with_npm().await?;
|
||||
}
|
||||
|
||||
self.worker.dispatch_load_event(located_script_name!())?;
|
||||
self.run_tests(&self.ps.options.shuffle_tests()).await?;
|
||||
loop {
|
||||
if !self
|
||||
.worker
|
||||
.dispatch_beforeunload_event(located_script_name!())?
|
||||
{
|
||||
break;
|
||||
}
|
||||
self.worker.run_event_loop(false).await?;
|
||||
}
|
||||
|
||||
self.worker.dispatch_unload_event(located_script_name!())?;
|
||||
|
||||
if let Some(coverage_collector) = maybe_coverage_collector.as_mut() {
|
||||
self
|
||||
.worker
|
||||
.with_event_loop(coverage_collector.stop_collecting().boxed_local())
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn run_lsp_test_specifier(
|
||||
&mut self,
|
||||
mode: TestMode,
|
||||
) -> Result<(), AnyError> {
|
||||
self.enable_test();
|
||||
|
||||
self.worker.execute_script(
|
||||
located_script_name!(),
|
||||
ascii_str!("Deno[Deno.internal].core.enableOpCallTracing();"),
|
||||
)?;
|
||||
|
||||
if mode != TestMode::Documentation {
|
||||
// We execute the module module as a side module so that import.meta.main is not set.
|
||||
self.execute_side_module_possibly_with_npm().await?;
|
||||
}
|
||||
|
||||
self.worker.dispatch_load_event(located_script_name!())?;
|
||||
self.run_tests(&None).await?;
|
||||
loop {
|
||||
if !self
|
||||
.worker
|
||||
.dispatch_beforeunload_event(located_script_name!())?
|
||||
{
|
||||
break;
|
||||
}
|
||||
self.worker.run_event_loop(false).await?;
|
||||
}
|
||||
self.worker.dispatch_unload_event(located_script_name!())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn run_bench_specifier(&mut self) -> Result<(), AnyError> {
|
||||
self.enable_bench();
|
||||
|
||||
// We execute the module module as a side module so that import.meta.main is not set.
|
||||
self.execute_side_module_possibly_with_npm().await?;
|
||||
|
||||
self.worker.dispatch_load_event(located_script_name!())?;
|
||||
self.run_benchmarks().await?;
|
||||
loop {
|
||||
if !self
|
||||
.worker
|
||||
.dispatch_beforeunload_event(located_script_name!())?
|
||||
{
|
||||
break;
|
||||
}
|
||||
self.worker.run_event_loop(false).await?;
|
||||
}
|
||||
self.worker.dispatch_unload_event(located_script_name!())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn execute_main_module_possibly_with_npm(
|
||||
pub async fn execute_main_module_possibly_with_npm(
|
||||
&mut self,
|
||||
) -> Result<(), AnyError> {
|
||||
let id = self.worker.preload_main_module(&self.main_module).await?;
|
||||
self.evaluate_module_possibly_with_npm(id).await
|
||||
}
|
||||
|
||||
async fn execute_side_module_possibly_with_npm(
|
||||
pub async fn execute_side_module_possibly_with_npm(
|
||||
&mut self,
|
||||
) -> Result<(), AnyError> {
|
||||
let id = self.worker.preload_side_module(&self.main_module).await?;
|
||||
|
@ -325,7 +215,7 @@ impl CliMainWorker {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn maybe_setup_coverage_collector(
|
||||
pub async fn maybe_setup_coverage_collector(
|
||||
&mut self,
|
||||
) -> Result<Option<CoverageCollector>, AnyError> {
|
||||
if let Some(ref coverage_dir) = self.ps.options.coverage_dir() {
|
||||
|
@ -343,61 +233,6 @@ impl CliMainWorker {
|
|||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Run tests declared with `Deno.test()`. Test events will be dispatched
|
||||
/// by calling ops which are currently only implemented in the CLI crate.
|
||||
pub async fn run_tests(
|
||||
&mut self,
|
||||
shuffle: &Option<u64>,
|
||||
) -> Result<(), AnyError> {
|
||||
let promise = {
|
||||
let scope = &mut self.worker.js_runtime.handle_scope();
|
||||
let cb = self.js_run_tests_callback.as_ref().unwrap().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.worker.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.
|
||||
pub async fn run_benchmarks(&mut self) -> Result<(), AnyError> {
|
||||
let promise = {
|
||||
let scope = &mut self.worker.js_runtime.handle_scope();
|
||||
let cb = self
|
||||
.js_run_benchmarks_callback
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.open(scope);
|
||||
let this = v8::undefined(scope).into();
|
||||
let promise = cb.call(scope, this, &[]).unwrap();
|
||||
v8::Global::new(scope, promise)
|
||||
};
|
||||
self.worker.js_runtime.resolve_value(promise).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Enable `Deno.test()`. If this isn't called before executing user code,
|
||||
/// `Deno.test()` calls will noop.
|
||||
pub fn enable_test(&mut self) {
|
||||
let scope = &mut self.worker.js_runtime.handle_scope();
|
||||
let cb = self.js_enable_test_callback.as_ref().unwrap().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.
|
||||
pub fn enable_bench(&mut self) {
|
||||
let scope = &mut self.worker.js_runtime.handle_scope();
|
||||
let cb = self.js_enable_bench_callback.as_ref().unwrap().open(scope);
|
||||
let this = v8::undefined(scope).into();
|
||||
cb.call(scope, this, &[]).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_main_worker(
|
||||
|
@ -405,42 +240,16 @@ pub async fn create_main_worker(
|
|||
main_module: ModuleSpecifier,
|
||||
permissions: PermissionsContainer,
|
||||
) -> Result<CliMainWorker, AnyError> {
|
||||
create_main_worker_internal(
|
||||
ps,
|
||||
main_module,
|
||||
permissions,
|
||||
vec![],
|
||||
Default::default(),
|
||||
false,
|
||||
)
|
||||
.await
|
||||
create_custom_worker(ps, main_module, permissions, vec![], Default::default())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn create_main_worker_for_test_or_bench(
|
||||
ps: &ProcState,
|
||||
main_module: ModuleSpecifier,
|
||||
permissions: PermissionsContainer,
|
||||
custom_extensions: Vec<Extension>,
|
||||
stdio: deno_runtime::deno_io::Stdio,
|
||||
) -> Result<CliMainWorker, AnyError> {
|
||||
create_main_worker_internal(
|
||||
ps,
|
||||
main_module,
|
||||
permissions,
|
||||
custom_extensions,
|
||||
stdio,
|
||||
true,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn create_main_worker_internal(
|
||||
pub async fn create_custom_worker(
|
||||
ps: &ProcState,
|
||||
main_module: ModuleSpecifier,
|
||||
permissions: PermissionsContainer,
|
||||
mut custom_extensions: Vec<Extension>,
|
||||
stdio: deno_runtime::deno_io::Stdio,
|
||||
bench_or_test: bool,
|
||||
) -> Result<CliMainWorker, AnyError> {
|
||||
let (main_module, is_main_cjs) = if let Ok(package_ref) =
|
||||
NpmPackageReqReference::from_specifier(&main_module)
|
||||
|
@ -552,59 +361,17 @@ async fn create_main_worker_internal(
|
|||
stdio,
|
||||
};
|
||||
|
||||
let mut worker = MainWorker::bootstrap_from_options(
|
||||
let worker = MainWorker::bootstrap_from_options(
|
||||
main_module.clone(),
|
||||
permissions,
|
||||
options,
|
||||
);
|
||||
|
||||
let (
|
||||
js_run_tests_callback,
|
||||
js_run_benchmarks_callback,
|
||||
js_enable_test_callback,
|
||||
js_enable_bench_callback,
|
||||
) = if bench_or_test {
|
||||
let scope = &mut worker.js_runtime.handle_scope();
|
||||
let js_run_tests_callback = deno_core::JsRuntime::eval::<v8::Function>(
|
||||
scope,
|
||||
"Deno[Deno.internal].testing.runTests",
|
||||
)
|
||||
.unwrap();
|
||||
let js_run_benchmarks_callback =
|
||||
deno_core::JsRuntime::eval::<v8::Function>(
|
||||
scope,
|
||||
"Deno[Deno.internal].testing.runBenchmarks",
|
||||
)
|
||||
.unwrap();
|
||||
let js_enable_tests_callback = deno_core::JsRuntime::eval::<v8::Function>(
|
||||
scope,
|
||||
"Deno[Deno.internal].testing.enableTest",
|
||||
)
|
||||
.unwrap();
|
||||
let js_enable_bench_callback = deno_core::JsRuntime::eval::<v8::Function>(
|
||||
scope,
|
||||
"Deno[Deno.internal].testing.enableBench",
|
||||
)
|
||||
.unwrap();
|
||||
(
|
||||
Some(v8::Global::new(scope, js_run_tests_callback)),
|
||||
Some(v8::Global::new(scope, js_run_benchmarks_callback)),
|
||||
Some(v8::Global::new(scope, js_enable_tests_callback)),
|
||||
Some(v8::Global::new(scope, js_enable_bench_callback)),
|
||||
)
|
||||
} else {
|
||||
(None, None, None, None)
|
||||
};
|
||||
|
||||
Ok(CliMainWorker {
|
||||
main_module,
|
||||
is_main_cjs,
|
||||
worker,
|
||||
ps: ps.clone(),
|
||||
js_run_tests_callback,
|
||||
js_run_benchmarks_callback,
|
||||
js_enable_test_callback,
|
||||
js_enable_bench_callback,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue