mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 17:34:47 -05:00
Update CLI for unit_test_runner.ts (#4352)
* drop server guard before unit test result check To prevent cascading test failures when js_unit_test http server guard is dropped before asserting that tests were successful. This is really a band-aid and doesn't solve underlying issue with http server. * Update CLI for unit_test_runner.ts * Change cli/js/tests/unit_test_runner.ts command line interface to work in 3 modes: - "one-off" - run tests that match permissions of currently running process - "master" - run tests for all possible permission combinations, by spawning subprocesses running in "worker" mode and communicating via TCP socket; requires elevated permissions - "worker" - run tests for set of permissions provided by CLI arg; requires elevated permissions to setup TCP connection to "master"; after initial setup process drops permissions to given set * Support filtering of tests by string passed after "--" CLI arg * Update cli/js/tests/README.md
This commit is contained in:
parent
0f6acf2753
commit
d6bbbdda75
4 changed files with 171 additions and 31 deletions
|
@ -37,11 +37,58 @@ ways:
|
||||||
- sanitization of async ops - ensuring that tests don't leak async ops by
|
- sanitization of async ops - ensuring that tests don't leak async ops by
|
||||||
ensuring that all started async ops are done before test finishes
|
ensuring that all started async ops are done before test finishes
|
||||||
|
|
||||||
`unit_test_runner.ts` is main script used to run unit tests.
|
## Running tests
|
||||||
|
|
||||||
|
`unit_test_runner.ts` is the main script used to run unit tests.
|
||||||
|
|
||||||
Runner discoveres required permissions combinations by loading
|
Runner discoveres required permissions combinations by loading
|
||||||
`cli/js/tests/unit_tests.ts` and going through all registered instances of
|
`cli/js/tests/unit_tests.ts` and going through all registered instances of
|
||||||
`unitTest`. For each discovered permission combination a new Deno process is
|
`unitTest`.
|
||||||
created with respective `--allow-*` flags which loads
|
|
||||||
`cli/js/tests/unit_tests.ts` and executes all `unitTest` that match runtime
|
There are three ways to run `unit_test_runner.ts`:
|
||||||
permissions.
|
|
||||||
|
- run tests matching current process permissions
|
||||||
|
|
||||||
|
```
|
||||||
|
// run tests that don't require any permissions
|
||||||
|
target/debug/deno unit_test_runner.ts
|
||||||
|
|
||||||
|
// run tests with "net" permission
|
||||||
|
target/debug/deno --allow-net unit_test_runner.ts
|
||||||
|
|
||||||
|
target/debug/deno --allow-net --allow-read unit_test_runner.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
- run all tests - "master" mode, that spawns worker processes for each
|
||||||
|
discovered permission combination:
|
||||||
|
|
||||||
|
```
|
||||||
|
target/debug/deno -A unit_test_runner.ts --master
|
||||||
|
```
|
||||||
|
|
||||||
|
By default all output of worker processes is discarded; for debug purposes
|
||||||
|
`--verbose` flag can be provided to preserve output from worker
|
||||||
|
|
||||||
|
```
|
||||||
|
target/debug/deno -A unit_test_runner.ts --master --verbose
|
||||||
|
```
|
||||||
|
|
||||||
|
- "worker" mode; communicates with parent using TCP socket on provided address;
|
||||||
|
after initial setup drops permissions to specified set. It shouldn't be used
|
||||||
|
directly, only be "master" process.
|
||||||
|
|
||||||
|
```
|
||||||
|
target/debug/deno -A unit_test_runner.ts --worker --addr=127.0.0.1:4500 --perms=net,write,run
|
||||||
|
```
|
||||||
|
|
||||||
|
### Filtering
|
||||||
|
|
||||||
|
Runner supports basic test filtering by name:
|
||||||
|
|
||||||
|
```
|
||||||
|
target/debug/deno unit_test_runner.ts -- netAccept
|
||||||
|
|
||||||
|
target/debug/deno -A unit_test_runner.ts --master -- netAccept
|
||||||
|
```
|
||||||
|
|
||||||
|
Filter string must be specified after "--" argument
|
||||||
|
|
|
@ -13,6 +13,7 @@ export {
|
||||||
fail
|
fail
|
||||||
} from "../../../std/testing/asserts.ts";
|
} from "../../../std/testing/asserts.ts";
|
||||||
export { readLines } from "../../../std/io/bufio.ts";
|
export { readLines } from "../../../std/io/bufio.ts";
|
||||||
|
export { parse as parseArgs } from "../../../std/flags/mod.ts";
|
||||||
|
|
||||||
export interface Permissions {
|
export interface Permissions {
|
||||||
read: boolean;
|
read: boolean;
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||||
import "./unit_tests.ts";
|
import "./unit_tests.ts";
|
||||||
import {
|
import {
|
||||||
assert,
|
|
||||||
readLines,
|
readLines,
|
||||||
permissionCombinations,
|
permissionCombinations,
|
||||||
Permissions,
|
Permissions,
|
||||||
registerUnitTests,
|
registerUnitTests,
|
||||||
SocketReporter,
|
SocketReporter,
|
||||||
fmtPerms
|
fmtPerms,
|
||||||
|
parseArgs
|
||||||
} from "./test_util.ts";
|
} from "./test_util.ts";
|
||||||
|
|
||||||
interface PermissionSetTestResult {
|
interface PermissionSetTestResult {
|
||||||
|
@ -44,17 +44,15 @@ async function dropWorkerPermissions(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function workerRunnerMain(args: string[]): Promise<void> {
|
async function workerRunnerMain(
|
||||||
const addrArg = args.find(e => e.includes("--addr"));
|
addrStr: string,
|
||||||
assert(typeof addrArg === "string", "Missing --addr argument");
|
permsStr: string,
|
||||||
const addrStr = addrArg.split("=")[1];
|
filter?: string
|
||||||
|
): Promise<void> {
|
||||||
const [hostname, port] = addrStr.split(":");
|
const [hostname, port] = addrStr.split(":");
|
||||||
const addr = { hostname, port: Number(port) };
|
const addr = { hostname, port: Number(port) };
|
||||||
|
|
||||||
let perms: Deno.PermissionName[] = [];
|
let perms: Deno.PermissionName[] = [];
|
||||||
const permsArg = args.find(e => e.includes("--perms"));
|
|
||||||
assert(typeof permsArg === "string", "Missing --perms argument");
|
|
||||||
const permsStr = permsArg.split("=")[1];
|
|
||||||
if (permsStr.length > 0) {
|
if (permsStr.length > 0) {
|
||||||
perms = permsStr.split(",") as Deno.PermissionName[];
|
perms = permsStr.split(",") as Deno.PermissionName[];
|
||||||
}
|
}
|
||||||
|
@ -69,13 +67,19 @@ async function workerRunnerMain(args: string[]): Promise<void> {
|
||||||
await Deno.runTests({
|
await Deno.runTests({
|
||||||
failFast: false,
|
failFast: false,
|
||||||
exitOnFail: false,
|
exitOnFail: false,
|
||||||
reporter: socketReporter
|
reporter: socketReporter,
|
||||||
|
only: filter
|
||||||
});
|
});
|
||||||
// Notify parent process we're done
|
// Notify parent process we're done
|
||||||
socketReporter.close();
|
socketReporter.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function spawnWorkerRunner(addr: string, perms: Permissions): Deno.Process {
|
function spawnWorkerRunner(
|
||||||
|
verbose: boolean,
|
||||||
|
addr: string,
|
||||||
|
perms: Permissions,
|
||||||
|
filter?: string
|
||||||
|
): Deno.Process {
|
||||||
// run subsequent tests using same deno executable
|
// run subsequent tests using same deno executable
|
||||||
const permStr = Object.keys(perms)
|
const permStr = Object.keys(perms)
|
||||||
.filter((permName): boolean => {
|
.filter((permName): boolean => {
|
||||||
|
@ -88,25 +92,33 @@ function spawnWorkerRunner(addr: string, perms: Permissions): Deno.Process {
|
||||||
"run",
|
"run",
|
||||||
"-A",
|
"-A",
|
||||||
"cli/js/tests/unit_test_runner.ts",
|
"cli/js/tests/unit_test_runner.ts",
|
||||||
"--",
|
|
||||||
"--worker",
|
"--worker",
|
||||||
`--addr=${addr}`,
|
`--addr=${addr}`,
|
||||||
`--perms=${permStr}`
|
`--perms=${permStr}`
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
args.push("--");
|
||||||
|
args.push(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ioMode = verbose ? "inherit" : "null";
|
||||||
|
|
||||||
const p = Deno.run({
|
const p = Deno.run({
|
||||||
args,
|
args,
|
||||||
stdin: "null",
|
stdin: ioMode,
|
||||||
stdout: "piped",
|
stdout: ioMode,
|
||||||
stderr: "null"
|
stderr: ioMode
|
||||||
});
|
});
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runTestsForPermissionSet(
|
async function runTestsForPermissionSet(
|
||||||
|
verbose: boolean,
|
||||||
reporter: Deno.ConsoleTestReporter,
|
reporter: Deno.ConsoleTestReporter,
|
||||||
perms: Permissions
|
perms: Permissions,
|
||||||
|
filter?: string
|
||||||
): Promise<PermissionSetTestResult> {
|
): Promise<PermissionSetTestResult> {
|
||||||
const permsFmt = fmtPerms(perms);
|
const permsFmt = fmtPerms(perms);
|
||||||
console.log(`Running tests for: ${permsFmt}`);
|
console.log(`Running tests for: ${permsFmt}`);
|
||||||
|
@ -114,7 +126,7 @@ async function runTestsForPermissionSet(
|
||||||
const addrStr = `${addr.hostname}:${addr.port}`;
|
const addrStr = `${addr.hostname}:${addr.port}`;
|
||||||
const workerListener = Deno.listen(addr);
|
const workerListener = Deno.listen(addr);
|
||||||
|
|
||||||
const workerProcess = spawnWorkerRunner(addrStr, perms);
|
const workerProcess = spawnWorkerRunner(verbose, addrStr, perms, filter);
|
||||||
|
|
||||||
// Wait for worker subprocess to go online
|
// Wait for worker subprocess to go online
|
||||||
const conn = await workerListener.accept();
|
const conn = await workerListener.accept();
|
||||||
|
@ -182,7 +194,10 @@ async function runTestsForPermissionSet(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function masterRunnerMain(): Promise<void> {
|
async function masterRunnerMain(
|
||||||
|
verbose: boolean,
|
||||||
|
filter?: string
|
||||||
|
): Promise<void> {
|
||||||
console.log(
|
console.log(
|
||||||
"Discovered permission combinations for tests:",
|
"Discovered permission combinations for tests:",
|
||||||
permissionCombinations.size
|
permissionCombinations.size
|
||||||
|
@ -196,7 +211,12 @@ async function masterRunnerMain(): Promise<void> {
|
||||||
const consoleReporter = new Deno.ConsoleTestReporter();
|
const consoleReporter = new Deno.ConsoleTestReporter();
|
||||||
|
|
||||||
for (const perms of permissionCombinations.values()) {
|
for (const perms of permissionCombinations.values()) {
|
||||||
const result = await runTestsForPermissionSet(consoleReporter, perms);
|
const result = await runTestsForPermissionSet(
|
||||||
|
verbose,
|
||||||
|
consoleReporter,
|
||||||
|
perms,
|
||||||
|
filter
|
||||||
|
);
|
||||||
testResults.add(result);
|
testResults.add(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,16 +244,87 @@ async function masterRunnerMain(): Promise<void> {
|
||||||
console.log("Unit tests passed");
|
console.log("Unit tests passed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const HELP = `Unit test runner
|
||||||
|
|
||||||
|
Run tests matching current process permissions:
|
||||||
|
|
||||||
|
deno --allow-write unit_test_runner.ts
|
||||||
|
|
||||||
|
deno --allow-net --allow-hrtime unit_test_runner.ts
|
||||||
|
|
||||||
|
deno --allow-write unit_test_runner.ts -- testWriteFile
|
||||||
|
|
||||||
|
Run "master" process that creates "worker" processes
|
||||||
|
for each discovered permission combination:
|
||||||
|
|
||||||
|
deno -A unit_test_runner.ts --master
|
||||||
|
|
||||||
|
Run worker process for given permissions:
|
||||||
|
|
||||||
|
deno -A unit_test_runner.ts --worker --perms=net,read,write --addr=127.0.0.1:4500
|
||||||
|
|
||||||
|
|
||||||
|
OPTIONS:
|
||||||
|
--master
|
||||||
|
Run in master mode, spawning worker processes for
|
||||||
|
each discovered permission combination
|
||||||
|
|
||||||
|
--worker
|
||||||
|
Run in worker mode, requires "perms" and "addr" flags,
|
||||||
|
should be run with "-A" flag; after setup worker will
|
||||||
|
drop permissions to required set specified in "perms"
|
||||||
|
|
||||||
|
--perms=<perm_name>...
|
||||||
|
Set of permissions this process should run tests with,
|
||||||
|
|
||||||
|
--addr=<addr>
|
||||||
|
Address of TCP socket for reporting
|
||||||
|
|
||||||
|
ARGS:
|
||||||
|
-- <filter>...
|
||||||
|
Run only tests with names matching filter, must
|
||||||
|
be used after "--"
|
||||||
|
`;
|
||||||
|
|
||||||
|
function assertOrHelp(expr: unknown): asserts expr {
|
||||||
|
if (!expr) {
|
||||||
|
console.log(HELP);
|
||||||
|
Deno.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function main(): Promise<void> {
|
async function main(): Promise<void> {
|
||||||
const args = Deno.args;
|
const args = parseArgs(Deno.args, {
|
||||||
|
boolean: ["master", "worker", "verbose"],
|
||||||
|
"--": true
|
||||||
|
});
|
||||||
|
|
||||||
const isWorker = args.includes("--worker");
|
if (args.help) {
|
||||||
|
console.log(HELP);
|
||||||
if (isWorker) {
|
return;
|
||||||
return await workerRunnerMain(args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return await masterRunnerMain();
|
const filter = args["--"][0];
|
||||||
|
|
||||||
|
// Master mode
|
||||||
|
if (args.master) {
|
||||||
|
return await masterRunnerMain(args.verbose, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Worker mode
|
||||||
|
if (args.worker) {
|
||||||
|
assertOrHelp(typeof args.addr === "string");
|
||||||
|
assertOrHelp(typeof args.perms === "string");
|
||||||
|
return await workerRunnerMain(args.addr, args.perms, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Running tests matching current process permissions
|
||||||
|
await registerUnitTests();
|
||||||
|
await Deno.runTests({
|
||||||
|
failFast: false,
|
||||||
|
exitOnFail: true,
|
||||||
|
only: filter
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
main();
|
main();
|
||||||
|
|
|
@ -274,12 +274,13 @@ fn js_unit_tests() {
|
||||||
.arg("--reload")
|
.arg("--reload")
|
||||||
.arg("-A")
|
.arg("-A")
|
||||||
.arg("cli/js/tests/unit_test_runner.ts")
|
.arg("cli/js/tests/unit_test_runner.ts")
|
||||||
|
.arg("--master")
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("failed to spawn script");
|
.expect("failed to spawn script");
|
||||||
let status = deno.wait().expect("failed to wait for the child process");
|
let status = deno.wait().expect("failed to wait for the child process");
|
||||||
|
drop(g);
|
||||||
assert_eq!(Some(0), status.code());
|
assert_eq!(Some(0), status.code());
|
||||||
assert!(status.success());
|
assert!(status.success());
|
||||||
drop(g);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Add table
Reference in a new issue