mirror of
https://github.com/denoland/deno.git
synced 2025-09-27 20:59:10 +00:00
perf: move "cli/js/40_testing.js" out of main snapshot (#21212)
Closes https://github.com/denoland/deno/issues/21136 --------- Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com>
This commit is contained in:
parent
7d8f0ae038
commit
89424f8e4a
7 changed files with 1269 additions and 1233 deletions
|
@ -320,8 +320,16 @@ impl DenoSubcommand {
|
||||||
matches!(self, Self::Run(_))
|
matches!(self, Self::Run(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_test_or_jupyter(&self) -> bool {
|
// Returns `true` if the subcommand depends on testing infrastructure.
|
||||||
matches!(self, Self::Test(_) | Self::Jupyter(_))
|
pub fn needs_test(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
Self::Test(_)
|
||||||
|
| Self::Jupyter(_)
|
||||||
|
| Self::Repl(_)
|
||||||
|
| Self::Bench(_)
|
||||||
|
| Self::Lsp
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -331,7 +331,6 @@ deno_core::extension!(
|
||||||
esm_entry_point = "ext:cli/99_main.js",
|
esm_entry_point = "ext:cli/99_main.js",
|
||||||
esm = [
|
esm = [
|
||||||
dir "js",
|
dir "js",
|
||||||
"40_testing.js",
|
|
||||||
"99_main.js"
|
"99_main.js"
|
||||||
],
|
],
|
||||||
customizer = |ext: &mut deno_core::Extension| {
|
customizer = |ext: &mut deno_core::Extension| {
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
* }, { raw: true });
|
* }, { raw: true });
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
{
|
(() => {
|
||||||
const internals = Deno[Deno.internal];
|
const internals = Deno[Deno.internal];
|
||||||
const core = internals.core;
|
const core = internals.core;
|
||||||
|
|
||||||
|
@ -428,4 +428,4 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
internals.enableJupyter = enableJupyter;
|
internals.enableJupyter = enableJupyter;
|
||||||
}
|
})();
|
||||||
|
|
|
@ -3,36 +3,36 @@
|
||||||
// Do not use primordials because we do not want to depend on the __bootstrap
|
// Do not use primordials because we do not want to depend on the __bootstrap
|
||||||
// namespace.
|
// namespace.
|
||||||
//
|
//
|
||||||
// deno-lint-ignore-file prefer-primordials
|
// deno-lint-ignore-file
|
||||||
|
(() => {
|
||||||
|
const internals = Deno[Deno.internal];
|
||||||
|
const core = internals.core;
|
||||||
|
const ops = core.ops;
|
||||||
|
|
||||||
const core = globalThis.Deno.core;
|
const {
|
||||||
const ops = core.ops;
|
|
||||||
|
|
||||||
const internals = globalThis.__bootstrap.internals;
|
|
||||||
const {
|
|
||||||
setExitHandler,
|
setExitHandler,
|
||||||
Console,
|
Console,
|
||||||
serializePermissions,
|
serializePermissions,
|
||||||
} = internals;
|
} = internals;
|
||||||
|
|
||||||
const opSanitizerDelayResolveQueue = [];
|
const opSanitizerDelayResolveQueue = [];
|
||||||
let hasSetOpSanitizerDelayMacrotask = false;
|
let hasSetOpSanitizerDelayMacrotask = false;
|
||||||
|
|
||||||
// Even if every resource is closed by the end of a test, there can be a delay
|
// 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
|
// until the pending ops have all finished. This function returns a promise
|
||||||
// that resolves when it's (probably) fine to run the op sanitizer.
|
// that resolves when it's (probably) fine to run the op sanitizer.
|
||||||
//
|
//
|
||||||
// This is implemented by adding a macrotask callback that runs after the
|
// This is implemented by adding a macrotask callback that runs after the
|
||||||
// all ready async ops resolve, and the timer macrotask. Using just a macrotask
|
// all ready async ops resolve, and the timer macrotask. Using just a macrotask
|
||||||
// callback without delaying is sufficient, because when the macrotask callback
|
// callback without delaying is sufficient, because when the macrotask callback
|
||||||
// runs after async op dispatch, we know that all async ops that can currently
|
// runs after async op dispatch, we know that all async ops that can currently
|
||||||
// return `Poll::Ready` have done so, and have been dispatched to JS.
|
// return `Poll::Ready` have done so, and have been dispatched to JS.
|
||||||
//
|
//
|
||||||
// Worker ops are an exception to this, because there is no way for the user to
|
// Worker ops are an exception to this, because there is no way for the user to
|
||||||
// await shutdown of the worker from the thread calling `worker.terminate()`.
|
// await shutdown of the worker from the thread calling `worker.terminate()`.
|
||||||
// Because of this, we give extra leeway for worker ops to complete, by waiting
|
// Because of this, we give extra leeway for worker ops to complete, by waiting
|
||||||
// for a whole millisecond if there are pending worker ops.
|
// for a whole millisecond if there are pending worker ops.
|
||||||
function opSanitizerDelay(hasPendingWorkerOps) {
|
function opSanitizerDelay(hasPendingWorkerOps) {
|
||||||
if (!hasSetOpSanitizerDelayMacrotask) {
|
if (!hasSetOpSanitizerDelayMacrotask) {
|
||||||
core.setMacrotaskCallback(handleOpSanitizerDelayMacrotask);
|
core.setMacrotaskCallback(handleOpSanitizerDelayMacrotask);
|
||||||
hasSetOpSanitizerDelayMacrotask = true;
|
hasSetOpSanitizerDelayMacrotask = true;
|
||||||
|
@ -47,21 +47,21 @@ function opSanitizerDelay(hasPendingWorkerOps) {
|
||||||
}, hasPendingWorkerOps ? 1 : 0);
|
}, hasPendingWorkerOps ? 1 : 0);
|
||||||
});
|
});
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleOpSanitizerDelayMacrotask() {
|
function handleOpSanitizerDelayMacrotask() {
|
||||||
const resolve = opSanitizerDelayResolveQueue.shift();
|
const resolve = opSanitizerDelayResolveQueue.shift();
|
||||||
if (resolve) {
|
if (resolve) {
|
||||||
resolve();
|
resolve();
|
||||||
return opSanitizerDelayResolveQueue.length === 0;
|
return opSanitizerDelayResolveQueue.length === 0;
|
||||||
}
|
}
|
||||||
return undefined; // we performed no work, so can skip microtasks checkpoint
|
return undefined; // we performed no work, so can skip microtasks checkpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
// An async operation to $0 was started in this test, but never completed. This is often caused by not $1.
|
// An async operation to $0 was started in this test, but never completed. This is often caused by not $1.
|
||||||
// An async operation to $0 was started in this test, but never completed. Async operations should not complete in a test if they were not started in that test.
|
// An async operation to $0 was started in this test, but never completed. Async operations should not complete in a test if they were not started in that test.
|
||||||
// deno-fmt-ignore
|
// deno-fmt-ignore
|
||||||
const OP_DETAILS = {
|
const OP_DETAILS = {
|
||||||
"op_blob_read_part": ["read from a Blob or File", "awaiting the result of a Blob or File read"],
|
"op_blob_read_part": ["read from a Blob or File", "awaiting the result of a Blob or File read"],
|
||||||
"op_broadcast_recv": ["receive a message from a BroadcastChannel", "closing the BroadcastChannel"],
|
"op_broadcast_recv": ["receive a message from a BroadcastChannel", "closing the BroadcastChannel"],
|
||||||
"op_broadcast_send": ["send a message to a BroadcastChannel", "closing the BroadcastChannel"],
|
"op_broadcast_send": ["send a message to a BroadcastChannel", "closing the BroadcastChannel"],
|
||||||
|
@ -133,24 +133,24 @@ const OP_DETAILS = {
|
||||||
"op_ws_send_binary_ab": ["send a message on a WebSocket", "closing a `WebSocket` or `WebSocketStream`"],
|
"op_ws_send_binary_ab": ["send a message on a WebSocket", "closing a `WebSocket` or `WebSocketStream`"],
|
||||||
"op_ws_send_ping": ["send a message on a WebSocket", "closing a `WebSocket` or `WebSocketStream`"],
|
"op_ws_send_ping": ["send a message on a WebSocket", "closing a `WebSocket` or `WebSocketStream`"],
|
||||||
"op_ws_send_pong": ["send a message on a WebSocket", "closing a `WebSocket` or `WebSocketStream`"],
|
"op_ws_send_pong": ["send a message on a WebSocket", "closing a `WebSocket` or `WebSocketStream`"],
|
||||||
};
|
};
|
||||||
|
|
||||||
let opIdHostRecvMessage = -1;
|
let opIdHostRecvMessage = -1;
|
||||||
let opIdHostRecvCtrl = -1;
|
let opIdHostRecvCtrl = -1;
|
||||||
let opNames = null;
|
let opNames = null;
|
||||||
|
|
||||||
function populateOpNames() {
|
function populateOpNames() {
|
||||||
opNames = core.ops.op_op_names();
|
opNames = core.ops.op_op_names();
|
||||||
opIdHostRecvMessage = opNames.indexOf("op_host_recv_message");
|
opIdHostRecvMessage = opNames.indexOf("op_host_recv_message");
|
||||||
opIdHostRecvCtrl = opNames.indexOf("op_host_recv_ctrl");
|
opIdHostRecvCtrl = opNames.indexOf("op_host_recv_ctrl");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap test function in additional assertion that makes sure
|
// Wrap test function in additional assertion that makes sure
|
||||||
// the test case does not leak async "ops" - ie. number of async
|
// the test case does not leak async "ops" - ie. number of async
|
||||||
// completed ops after the test is the same as number of dispatched
|
// completed ops after the test is the same as number of dispatched
|
||||||
// ops. Note that "unref" ops are ignored since in nature that are
|
// ops. Note that "unref" ops are ignored since in nature that are
|
||||||
// optional.
|
// optional.
|
||||||
function assertOps(fn) {
|
function assertOps(fn) {
|
||||||
/** @param desc {TestDescription | TestStepDescription} */
|
/** @param desc {TestDescription | TestStepDescription} */
|
||||||
return async function asyncOpSanitizer(desc) {
|
return async function asyncOpSanitizer(desc) {
|
||||||
if (opNames === null) populateOpNames();
|
if (opNames === null) populateOpNames();
|
||||||
|
@ -262,11 +262,13 @@ function assertOps(fn) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { failed: { leakedOps: [details, core.isOpCallTracingEnabled()] } };
|
return {
|
||||||
|
failed: { leakedOps: [details, core.isOpCallTracingEnabled()] },
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function prettyResourceNames(name) {
|
function prettyResourceNames(name) {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "fsFile":
|
case "fsFile":
|
||||||
return ["A file", "opened", "closed"];
|
return ["A file", "opened", "closed"];
|
||||||
|
@ -331,9 +333,9 @@ function prettyResourceNames(name) {
|
||||||
default:
|
default:
|
||||||
return [`A "${name}" resource`, "created", "cleaned up"];
|
return [`A "${name}" resource`, "created", "cleaned up"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function resourceCloseHint(name) {
|
function resourceCloseHint(name) {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "fsFile":
|
case "fsFile":
|
||||||
return "Close the file handle by calling `file.close()`.";
|
return "Close the file handle by calling `file.close()`.";
|
||||||
|
@ -398,12 +400,12 @@ function resourceCloseHint(name) {
|
||||||
default:
|
default:
|
||||||
return "Close the resource before the end of the test.";
|
return "Close the resource before the end of the test.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap test function in additional assertion that makes sure
|
// Wrap test function in additional assertion that makes sure
|
||||||
// the test case does not "leak" resources - ie. resource table after
|
// the test case does not "leak" resources - ie. resource table after
|
||||||
// the test has exactly the same contents as before the test.
|
// the test has exactly the same contents as before the test.
|
||||||
function assertResources(fn) {
|
function assertResources(fn) {
|
||||||
/** @param desc {TestDescription | TestStepDescription} */
|
/** @param desc {TestDescription | TestStepDescription} */
|
||||||
return async function resourceSanitizer(desc) {
|
return async function resourceSanitizer(desc) {
|
||||||
const pre = core.resources();
|
const pre = core.resources();
|
||||||
|
@ -440,11 +442,11 @@ function assertResources(fn) {
|
||||||
}
|
}
|
||||||
return { failed: { leakedResources: details } };
|
return { failed: { leakedResources: details } };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap test function in additional assertion that makes sure
|
// Wrap test function in additional assertion that makes sure
|
||||||
// that the test case does not accidentally exit prematurely.
|
// that the test case does not accidentally exit prematurely.
|
||||||
function assertExit(fn, isTest) {
|
function assertExit(fn, isTest) {
|
||||||
return async function exitSanitizer(...params) {
|
return async function exitSanitizer(...params) {
|
||||||
setExitHandler((exitCode) => {
|
setExitHandler((exitCode) => {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -461,9 +463,9 @@ function assertExit(fn, isTest) {
|
||||||
setExitHandler(null);
|
setExitHandler(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function wrapOuter(fn, desc) {
|
function wrapOuter(fn, desc) {
|
||||||
return async function outerWrapped() {
|
return async function outerWrapped() {
|
||||||
try {
|
try {
|
||||||
if (desc.ignore) {
|
if (desc.ignore) {
|
||||||
|
@ -480,9 +482,9 @@ function wrapOuter(fn, desc) {
|
||||||
state.completed = true;
|
state.completed = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function wrapInner(fn) {
|
function wrapInner(fn) {
|
||||||
/** @param desc {TestDescription | TestStepDescription} */
|
/** @param desc {TestDescription | TestStepDescription} */
|
||||||
return async function innerWrapped(desc) {
|
return async function innerWrapped(desc) {
|
||||||
function getRunningStepDescs() {
|
function getRunningStepDescs() {
|
||||||
|
@ -520,7 +522,9 @@ function wrapInner(fn) {
|
||||||
|
|
||||||
if (usesSanitizer(desc) && runningStepDescs.length > 0) {
|
if (usesSanitizer(desc) && runningStepDescs.length > 0) {
|
||||||
return {
|
return {
|
||||||
failed: { hasSanitizersAndOverlaps: runningStepDescs.map(getFullName) },
|
failed: {
|
||||||
|
hasSanitizersAndOverlaps: runningStepDescs.map(getFullName),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
await fn(testStates.get(desc.id).context);
|
await fn(testStates.get(desc.id).context);
|
||||||
|
@ -536,19 +540,19 @@ function wrapInner(fn) {
|
||||||
}
|
}
|
||||||
return failedSteps == 0 ? null : { failed: { failedSteps } };
|
return failedSteps == 0 ? null : { failed: { failedSteps } };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function pledgePermissions(permissions) {
|
function pledgePermissions(permissions) {
|
||||||
return ops.op_pledge_test_permissions(
|
return ops.op_pledge_test_permissions(
|
||||||
serializePermissions(permissions),
|
serializePermissions(permissions),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function restorePermissions(token) {
|
function restorePermissions(token) {
|
||||||
ops.op_restore_test_permissions(token);
|
ops.op_restore_test_permissions(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
function withPermissions(fn, permissions) {
|
function withPermissions(fn, permissions) {
|
||||||
return async function applyPermissions(...params) {
|
return async function applyPermissions(...params) {
|
||||||
const token = pledgePermissions(permissions);
|
const token = pledgePermissions(permissions);
|
||||||
|
|
||||||
|
@ -558,22 +562,22 @@ function withPermissions(fn, permissions) {
|
||||||
restorePermissions(token);
|
restorePermissions(token);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const ESCAPE_ASCII_CHARS = [
|
const ESCAPE_ASCII_CHARS = [
|
||||||
["\b", "\\b"],
|
["\b", "\\b"],
|
||||||
["\f", "\\f"],
|
["\f", "\\f"],
|
||||||
["\t", "\\t"],
|
["\t", "\\t"],
|
||||||
["\n", "\\n"],
|
["\n", "\\n"],
|
||||||
["\r", "\\r"],
|
["\r", "\\r"],
|
||||||
["\v", "\\v"],
|
["\v", "\\v"],
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
function escapeName(name) {
|
function escapeName(name) {
|
||||||
// Check if we need to escape a character
|
// Check if we need to escape a character
|
||||||
for (let i = 0; i < name.length; i++) {
|
for (let i = 0; i < name.length; i++) {
|
||||||
const ch = name.charCodeAt(i);
|
const ch = name.charCodeAt(i);
|
||||||
|
@ -588,9 +592,9 @@ function escapeName(name) {
|
||||||
|
|
||||||
// We didn't need to escape anything, return original string
|
// We didn't need to escape anything, return original string
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* id: number,
|
* id: number,
|
||||||
* name: string,
|
* name: string,
|
||||||
|
@ -646,27 +650,27 @@ function escapeName(name) {
|
||||||
* }} BenchDescription
|
* }} BenchDescription
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @type {Map<number, TestState | TestStepState>} */
|
/** @type {Map<number, TestState | TestStepState>} */
|
||||||
const testStates = new Map();
|
const testStates = new Map();
|
||||||
/** @type {number | null} */
|
/** @type {number | null} */
|
||||||
let currentBenchId = null;
|
let currentBenchId = null;
|
||||||
// These local variables are used to track time measurements at
|
// These local variables are used to track time measurements at
|
||||||
// `BenchContext::{start,end}` calls. They are global instead of using a state
|
// `BenchContext::{start,end}` calls. They are global instead of using a state
|
||||||
// map to minimise the overhead of assigning them.
|
// map to minimise the overhead of assigning them.
|
||||||
/** @type {number | null} */
|
/** @type {number | null} */
|
||||||
let currentBenchUserExplicitStart = null;
|
let currentBenchUserExplicitStart = null;
|
||||||
/** @type {number | null} */
|
/** @type {number | null} */
|
||||||
let currentBenchUserExplicitEnd = null;
|
let currentBenchUserExplicitEnd = null;
|
||||||
|
|
||||||
const registerTestIdRetBuf = new Uint32Array(1);
|
const registerTestIdRetBuf = new Uint32Array(1);
|
||||||
const registerTestIdRetBufU8 = new Uint8Array(registerTestIdRetBuf.buffer);
|
const registerTestIdRetBufU8 = new Uint8Array(registerTestIdRetBuf.buffer);
|
||||||
|
|
||||||
function testInner(
|
function testInner(
|
||||||
nameOrFnOrOptions,
|
nameOrFnOrOptions,
|
||||||
optionsOrFn,
|
optionsOrFn,
|
||||||
maybeFn,
|
maybeFn,
|
||||||
overrides = {},
|
overrides = {},
|
||||||
) {
|
) {
|
||||||
if (typeof ops.op_register_test != "function") {
|
if (typeof ops.op_register_test != "function") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -777,37 +781,37 @@ function testInner(
|
||||||
children: [],
|
children: [],
|
||||||
completed: false,
|
completed: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main test function provided by Deno.
|
// Main test function provided by Deno.
|
||||||
function test(
|
function test(
|
||||||
nameOrFnOrOptions,
|
nameOrFnOrOptions,
|
||||||
optionsOrFn,
|
optionsOrFn,
|
||||||
maybeFn,
|
maybeFn,
|
||||||
) {
|
) {
|
||||||
return testInner(nameOrFnOrOptions, optionsOrFn, maybeFn);
|
return testInner(nameOrFnOrOptions, optionsOrFn, maybeFn);
|
||||||
}
|
}
|
||||||
|
|
||||||
test.ignore = function (nameOrFnOrOptions, optionsOrFn, maybeFn) {
|
test.ignore = function (nameOrFnOrOptions, optionsOrFn, maybeFn) {
|
||||||
return testInner(nameOrFnOrOptions, optionsOrFn, maybeFn, { ignore: true });
|
return testInner(nameOrFnOrOptions, optionsOrFn, maybeFn, { ignore: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
test.only = function (
|
test.only = function (
|
||||||
nameOrFnOrOptions,
|
nameOrFnOrOptions,
|
||||||
optionsOrFn,
|
optionsOrFn,
|
||||||
maybeFn,
|
maybeFn,
|
||||||
) {
|
) {
|
||||||
return testInner(nameOrFnOrOptions, optionsOrFn, maybeFn, { only: true });
|
return testInner(nameOrFnOrOptions, optionsOrFn, maybeFn, { only: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
let registeredWarmupBench = false;
|
let registeredWarmupBench = false;
|
||||||
|
|
||||||
// Main bench function provided by Deno.
|
// Main bench function provided by Deno.
|
||||||
function bench(
|
function bench(
|
||||||
nameOrFnOrOptions,
|
nameOrFnOrOptions,
|
||||||
optionsOrFn,
|
optionsOrFn,
|
||||||
maybeFn,
|
maybeFn,
|
||||||
) {
|
) {
|
||||||
if (typeof ops.op_register_bench != "function") {
|
if (typeof ops.op_register_bench != "function") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -919,16 +923,24 @@ function bench(
|
||||||
const { id, origin } = ops.op_register_bench(benchDesc);
|
const { id, origin } = ops.op_register_bench(benchDesc);
|
||||||
benchDesc.id = id;
|
benchDesc.id = id;
|
||||||
benchDesc.origin = origin;
|
benchDesc.origin = origin;
|
||||||
}
|
}
|
||||||
|
|
||||||
function compareMeasurements(a, b) {
|
function compareMeasurements(a, b) {
|
||||||
if (a > b) return 1;
|
if (a > b) return 1;
|
||||||
if (a < b) return -1;
|
if (a < b) return -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function benchStats(n, highPrecision, usedExplicitTimers, avg, min, max, all) {
|
function benchStats(
|
||||||
|
n,
|
||||||
|
highPrecision,
|
||||||
|
usedExplicitTimers,
|
||||||
|
avg,
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
all,
|
||||||
|
) {
|
||||||
return {
|
return {
|
||||||
n,
|
n,
|
||||||
min,
|
min,
|
||||||
|
@ -941,9 +953,9 @@ function benchStats(n, highPrecision, usedExplicitTimers, avg, min, max, all) {
|
||||||
highPrecision,
|
highPrecision,
|
||||||
usedExplicitTimers,
|
usedExplicitTimers,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function benchMeasure(timeBudget, fn, async, context) {
|
async function benchMeasure(timeBudget, fn, async, context) {
|
||||||
let n = 0;
|
let n = 0;
|
||||||
let avg = 0;
|
let avg = 0;
|
||||||
let wavg = 0;
|
let wavg = 0;
|
||||||
|
@ -1103,10 +1115,10 @@ async function benchMeasure(timeBudget, fn, async, context) {
|
||||||
max,
|
max,
|
||||||
all,
|
all,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @param desc {BenchDescription} */
|
/** @param desc {BenchDescription} */
|
||||||
function createBenchContext(desc) {
|
function createBenchContext(desc) {
|
||||||
return {
|
return {
|
||||||
[Symbol.toStringTag]: "BenchContext",
|
[Symbol.toStringTag]: "BenchContext",
|
||||||
name: desc.name,
|
name: desc.name,
|
||||||
|
@ -1118,7 +1130,9 @@ function createBenchContext(desc) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (currentBenchUserExplicitStart != null) {
|
if (currentBenchUserExplicitStart != null) {
|
||||||
throw new TypeError("BenchContext::start() has already been invoked.");
|
throw new TypeError(
|
||||||
|
"BenchContext::start() has already been invoked.",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
currentBenchUserExplicitStart = benchNow();
|
currentBenchUserExplicitStart = benchNow();
|
||||||
},
|
},
|
||||||
|
@ -1135,10 +1149,10 @@ function createBenchContext(desc) {
|
||||||
currentBenchUserExplicitEnd = end;
|
currentBenchUserExplicitEnd = end;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Wrap a user benchmark function in one which returns a structured result. */
|
/** Wrap a user benchmark function in one which returns a structured result. */
|
||||||
function wrapBenchmark(desc) {
|
function wrapBenchmark(desc) {
|
||||||
const fn = desc.fn;
|
const fn = desc.fn;
|
||||||
return async function outerWrapped() {
|
return async function outerWrapped() {
|
||||||
let token = null;
|
let token = null;
|
||||||
|
@ -1164,7 +1178,12 @@ function wrapBenchmark(desc) {
|
||||||
|
|
||||||
const benchTimeInMs = 500;
|
const benchTimeInMs = 500;
|
||||||
const context = createBenchContext(desc);
|
const context = createBenchContext(desc);
|
||||||
const stats = await benchMeasure(benchTimeInMs, fn, desc.async, context);
|
const stats = await benchMeasure(
|
||||||
|
benchTimeInMs,
|
||||||
|
fn,
|
||||||
|
desc.async,
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
|
||||||
return { ok: stats };
|
return { ok: stats };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -1178,24 +1197,24 @@ function wrapBenchmark(desc) {
|
||||||
if (token !== null) restorePermissions(token);
|
if (token !== null) restorePermissions(token);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function benchNow() {
|
function benchNow() {
|
||||||
return ops.op_bench_now();
|
return ops.op_bench_now();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFullName(desc) {
|
function getFullName(desc) {
|
||||||
if ("parent" in desc) {
|
if ("parent" in desc) {
|
||||||
return `${getFullName(desc.parent)} ... ${desc.name}`;
|
return `${getFullName(desc.parent)} ... ${desc.name}`;
|
||||||
}
|
}
|
||||||
return desc.name;
|
return desc.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
function usesSanitizer(desc) {
|
function usesSanitizer(desc) {
|
||||||
return desc.sanitizeResources || desc.sanitizeOps || desc.sanitizeExit;
|
return desc.sanitizeResources || desc.sanitizeOps || desc.sanitizeExit;
|
||||||
}
|
}
|
||||||
|
|
||||||
function stepReportResult(desc, result, elapsed) {
|
function stepReportResult(desc, result, elapsed) {
|
||||||
const state = testStates.get(desc.id);
|
const state = testStates.get(desc.id);
|
||||||
for (const childDesc of state.children) {
|
for (const childDesc of state.children) {
|
||||||
stepReportResult(childDesc, { failed: "incomplete" }, 0);
|
stepReportResult(childDesc, { failed: "incomplete" }, 0);
|
||||||
|
@ -1207,10 +1226,10 @@ function stepReportResult(desc, result, elapsed) {
|
||||||
} else {
|
} else {
|
||||||
ops.op_test_event_step_result_failed(desc.id, result.failed, elapsed);
|
ops.op_test_event_step_result_failed(desc.id, result.failed, elapsed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @param desc {TestDescription | TestStepDescription} */
|
/** @param desc {TestDescription | TestStepDescription} */
|
||||||
function createTestContext(desc) {
|
function createTestContext(desc) {
|
||||||
let parent;
|
let parent;
|
||||||
let level;
|
let level;
|
||||||
let rootId;
|
let rootId;
|
||||||
|
@ -1254,7 +1273,9 @@ function createTestContext(desc) {
|
||||||
|
|
||||||
let stepDesc;
|
let stepDesc;
|
||||||
if (typeof nameOrFnOrOptions === "string") {
|
if (typeof nameOrFnOrOptions === "string") {
|
||||||
if (!Object.prototype.isPrototypeOf.call(Function.prototype, maybeFn)) {
|
if (
|
||||||
|
!Object.prototype.isPrototypeOf.call(Function.prototype, maybeFn)
|
||||||
|
) {
|
||||||
throw new TypeError("Expected function for second argument.");
|
throw new TypeError("Expected function for second argument.");
|
||||||
}
|
}
|
||||||
stepDesc = {
|
stepDesc = {
|
||||||
|
@ -1324,16 +1345,16 @@ function createTestContext(desc) {
|
||||||
return result == "ok";
|
return result == "ok";
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrap a user test function in one which returns a structured result.
|
* Wrap a user test function in one which returns a structured result.
|
||||||
* @template T {Function}
|
* @template T {Function}
|
||||||
* @param testFn {T}
|
* @param testFn {T}
|
||||||
* @param desc {TestDescription | TestStepDescription}
|
* @param desc {TestDescription | TestStepDescription}
|
||||||
* @returns {T}
|
* @returns {T}
|
||||||
*/
|
*/
|
||||||
function wrapTest(desc) {
|
function wrapTest(desc) {
|
||||||
let testFn = wrapInner(desc.fn);
|
let testFn = wrapInner(desc.fn);
|
||||||
if (desc.sanitizeOps) {
|
if (desc.sanitizeOps) {
|
||||||
testFn = assertOps(testFn);
|
testFn = assertOps(testFn);
|
||||||
|
@ -1348,8 +1369,8 @@ function wrapTest(desc) {
|
||||||
testFn = withPermissions(testFn, desc.permissions);
|
testFn = withPermissions(testFn, desc.permissions);
|
||||||
}
|
}
|
||||||
return wrapOuter(testFn, desc);
|
return wrapOuter(testFn, desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
import { denoNs } from "ext:runtime/90_deno_ns.js";
|
globalThis.Deno.bench = bench;
|
||||||
denoNs.bench = bench;
|
globalThis.Deno.test = test;
|
||||||
denoNs.test = test;
|
})();
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
import "ext:cli/40_testing.js";
|
|
||||||
import "ext:cli/runtime/js/99_main.js";
|
import "ext:cli/runtime/js/99_main.js";
|
||||||
|
|
|
@ -639,9 +639,13 @@ impl CliMainWorkerFactory {
|
||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
|
|
||||||
if self.shared.subcommand.is_test_or_jupyter() {
|
if self.shared.subcommand.needs_test() {
|
||||||
worker.js_runtime.execute_script_static(
|
worker.js_runtime.execute_script_static(
|
||||||
"40_jupyter.js",
|
"ext:cli/40_testing.js",
|
||||||
|
include_str!("js/40_testing.js"),
|
||||||
|
)?;
|
||||||
|
worker.js_runtime.execute_script_static(
|
||||||
|
"ext:cli/40_jupyter.js",
|
||||||
include_str!("js/40_jupyter.js"),
|
include_str!("js/40_jupyter.js"),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -448,6 +448,11 @@ const finalDenoNs = {
|
||||||
resources: core.resources,
|
resources: core.resources,
|
||||||
close: core.close,
|
close: core.close,
|
||||||
...denoNs,
|
...denoNs,
|
||||||
|
// Deno.test and Deno.bench are noops here, but kept for compatibility; so
|
||||||
|
// that they don't cause errors when used outside of `deno test`/`deno bench`
|
||||||
|
// contexts.
|
||||||
|
test: () => {},
|
||||||
|
bench: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue