summaryrefslogtreecommitdiff
path: root/cli/js/test_util.ts
diff options
context:
space:
mode:
Diffstat (limited to 'cli/js/test_util.ts')
-rw-r--r--cli/js/test_util.ts411
1 files changed, 0 insertions, 411 deletions
diff --git a/cli/js/test_util.ts b/cli/js/test_util.ts
deleted file mode 100644
index fa17a2f87..000000000
--- a/cli/js/test_util.ts
+++ /dev/null
@@ -1,411 +0,0 @@
-// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
-//
-// We want to test many ops in deno which have different behavior depending on
-// the permissions set. These tests can specify which permissions they expect,
-// which appends a special string like "permW1N0" to the end of the test name.
-// Here we run several copies of deno with different permissions, filtering the
-// tests by the special string. permW1N0 means allow-write but not allow-net.
-// See tools/unit_tests.py for more details.
-
-import { readLines } from "../../std/io/bufio.ts";
-import { assert, assertEquals } from "../../std/testing/asserts.ts";
-export {
- assert,
- assertThrows,
- assertEquals,
- assertMatch,
- assertNotEquals,
- assertStrictEq,
- assertStrContains,
- unreachable,
- fail
-} from "../../std/testing/asserts.ts";
-
-interface TestPermissions {
- read?: boolean;
- write?: boolean;
- net?: boolean;
- env?: boolean;
- run?: boolean;
- plugin?: boolean;
- hrtime?: boolean;
-}
-
-export interface Permissions {
- read: boolean;
- write: boolean;
- net: boolean;
- env: boolean;
- run: boolean;
- plugin: boolean;
- hrtime: boolean;
-}
-
-const isGranted = async (name: Deno.PermissionName): Promise<boolean> =>
- (await Deno.permissions.query({ name })).state === "granted";
-
-async function getProcessPermissions(): Promise<Permissions> {
- return {
- run: await isGranted("run"),
- read: await isGranted("read"),
- write: await isGranted("write"),
- net: await isGranted("net"),
- env: await isGranted("env"),
- plugin: await isGranted("plugin"),
- hrtime: await isGranted("hrtime")
- };
-}
-
-const processPerms = await getProcessPermissions();
-
-function permissionsMatch(
- processPerms: Permissions,
- requiredPerms: Permissions
-): boolean {
- for (const permName in processPerms) {
- if (
- processPerms[permName as keyof Permissions] !==
- requiredPerms[permName as keyof Permissions]
- ) {
- return false;
- }
- }
-
- return true;
-}
-
-export const permissionCombinations: Map<string, Permissions> = new Map();
-
-function permToString(perms: Permissions): string {
- const r = perms.read ? 1 : 0;
- const w = perms.write ? 1 : 0;
- const n = perms.net ? 1 : 0;
- const e = perms.env ? 1 : 0;
- const u = perms.run ? 1 : 0;
- const p = perms.plugin ? 1 : 0;
- const h = perms.hrtime ? 1 : 0;
- return `permR${r}W${w}N${n}E${e}U${u}P${p}H${h}`;
-}
-
-function registerPermCombination(perms: Permissions): void {
- const key = permToString(perms);
- if (!permissionCombinations.has(key)) {
- permissionCombinations.set(key, perms);
- }
-}
-
-function normalizeTestPermissions(perms: TestPermissions): Permissions {
- return {
- read: !!perms.read,
- write: !!perms.write,
- net: !!perms.net,
- run: !!perms.run,
- env: !!perms.env,
- plugin: !!perms.plugin,
- hrtime: !!perms.hrtime
- };
-}
-
-// Wrap `TestFunction` in additional assertion that makes sure
-// the test case does not leak async "ops" - ie. number of async
-// completed ops after the test is the same as number of dispatched
-// ops. Note that "unref" ops are ignored since in nature that are
-// optional.
-function assertOps(fn: Deno.TestFunction): Deno.TestFunction {
- return async function asyncOpSanitizer(): Promise<void> {
- const pre = Deno.metrics();
- await fn();
- const post = Deno.metrics();
- // We're checking diff because one might spawn HTTP server in the background
- // that will be a pending async op before test starts.
- assertEquals(
- post.opsDispatchedAsync - pre.opsDispatchedAsync,
- post.opsCompletedAsync - pre.opsCompletedAsync,
- `Test case is leaking async ops.
- Before:
- - dispatched: ${pre.opsDispatchedAsync}
- - completed: ${pre.opsCompletedAsync}
- After:
- - dispatched: ${post.opsDispatchedAsync}
- - completed: ${post.opsCompletedAsync}`
- );
- };
-}
-
-// Wrap `TestFunction` in additional assertion that makes sure
-// the test case does not "leak" resources - ie. resource table after
-// the test has exactly the same contents as before the test.
-function assertResources(fn: Deno.TestFunction): Deno.TestFunction {
- return async function resourceSanitizer(): Promise<void> {
- const pre = Deno.resources();
- await fn();
- const post = Deno.resources();
- const msg = `Test case is leaking resources.
- Before: ${JSON.stringify(pre, null, 2)}
- After: ${JSON.stringify(post, null, 2)}`;
- assertEquals(pre, post, msg);
- };
-}
-
-interface UnitTestOptions {
- skip?: boolean;
- perms?: TestPermissions;
-}
-
-export function unitTest(fn: Deno.TestFunction): void;
-export function unitTest(options: UnitTestOptions, fn: Deno.TestFunction): void;
-export function unitTest(
- optionsOrFn: UnitTestOptions | Deno.TestFunction,
- maybeFn?: Deno.TestFunction
-): void {
- assert(optionsOrFn, "At least one argument is required");
-
- let options: UnitTestOptions;
- let name: string;
- let fn: Deno.TestFunction;
-
- if (typeof optionsOrFn === "function") {
- options = {};
- fn = optionsOrFn;
- name = fn.name;
- assert(name, "Missing test function name");
- } else {
- options = optionsOrFn;
- assert(maybeFn, "Missing test function definition");
- assert(
- typeof maybeFn === "function",
- "Second argument should be test function definition"
- );
- fn = maybeFn;
- name = fn.name;
- assert(name, "Missing test function name");
- }
-
- if (options.skip) {
- return;
- }
-
- const normalizedPerms = normalizeTestPermissions(options.perms || {});
- registerPermCombination(normalizedPerms);
- if (!permissionsMatch(processPerms, normalizedPerms)) {
- return;
- }
-
- const testDefinition: Deno.TestDefinition = {
- name,
- fn: assertResources(assertOps(fn))
- };
- Deno.test(testDefinition);
-}
-
-function extractNumber(re: RegExp, str: string): number | undefined {
- const match = str.match(re);
-
- if (match) {
- return Number.parseInt(match[1]);
- }
-}
-
-export async function parseUnitTestOutput(
- reader: Deno.Reader,
- print: boolean
-): Promise<{ actual?: number; expected?: number; resultOutput?: string }> {
- let expected, actual, result;
-
- for await (const line of readLines(reader)) {
- if (!expected) {
- // expect "running 30 tests"
- expected = extractNumber(/running (\d+) tests/, line);
- } else if (line.indexOf("test result:") !== -1) {
- result = line;
- }
-
- if (print) {
- console.log(line);
- }
- }
-
- // Check that the number of expected tests equals what was reported at the
- // bottom.
- if (result) {
- // result should be a string like this:
- // "test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; ..."
- actual = extractNumber(/(\d+) passed/, result);
- }
-
- return { actual, expected, resultOutput: result };
-}
-
-export interface ResolvableMethods<T> {
- resolve: (value?: T | PromiseLike<T>) => void;
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- reject: (reason?: any) => void;
-}
-
-export type Resolvable<T> = Promise<T> & ResolvableMethods<T>;
-
-export function createResolvable<T>(): Resolvable<T> {
- let methods: ResolvableMethods<T>;
- const promise = new Promise<T>((resolve, reject): void => {
- methods = { resolve, reject };
- });
- // TypeScript doesn't know that the Promise callback occurs synchronously
- // therefore use of not null assertion (`!`)
- return Object.assign(promise, methods!) as Resolvable<T>;
-}
-
-unitTest(function permissionsMatches(): void {
- assert(
- permissionsMatch(
- {
- read: true,
- write: false,
- net: false,
- env: false,
- run: false,
- plugin: false,
- hrtime: false
- },
- normalizeTestPermissions({ read: true })
- )
- );
-
- assert(
- permissionsMatch(
- {
- read: false,
- write: false,
- net: false,
- env: false,
- run: false,
- plugin: false,
- hrtime: false
- },
- normalizeTestPermissions({})
- )
- );
-
- assertEquals(
- permissionsMatch(
- {
- read: false,
- write: true,
- net: true,
- env: true,
- run: true,
- plugin: true,
- hrtime: true
- },
- normalizeTestPermissions({ read: true })
- ),
- false
- );
-
- assertEquals(
- permissionsMatch(
- {
- read: true,
- write: false,
- net: true,
- env: false,
- run: false,
- plugin: false,
- hrtime: false
- },
- normalizeTestPermissions({ read: true })
- ),
- false
- );
-
- assert(
- permissionsMatch(
- {
- read: true,
- write: true,
- net: true,
- env: true,
- run: true,
- plugin: true,
- hrtime: true
- },
- {
- read: true,
- write: true,
- net: true,
- env: true,
- run: true,
- plugin: true,
- hrtime: true
- }
- )
- );
-});
-
-unitTest(
- { perms: { read: true } },
- async function parsingUnitTestOutput(): Promise<void> {
- const cwd = Deno.cwd();
- const testDataPath = `${cwd}/tools/testdata/`;
-
- let result;
-
- // This is an example of a successful unit test output.
- const f1 = await Deno.open(`${testDataPath}/unit_test_output1.txt`);
- result = await parseUnitTestOutput(f1, false);
- assertEquals(result.actual, 96);
- assertEquals(result.expected, 96);
- f1.close();
-
- // This is an example of a silently dying unit test.
- const f2 = await Deno.open(`${testDataPath}/unit_test_output2.txt`);
- result = await parseUnitTestOutput(f2, false);
- assertEquals(result.actual, undefined);
- assertEquals(result.expected, 96);
- f2.close();
-
- // This is an example of compiling before successful unit tests.
- const f3 = await Deno.open(`${testDataPath}/unit_test_output3.txt`);
- result = await parseUnitTestOutput(f3, false);
- assertEquals(result.actual, 96);
- assertEquals(result.expected, 96);
- f3.close();
-
- // Check what happens on empty output.
- const f = new Deno.Buffer(new TextEncoder().encode("\n\n\n"));
- result = await parseUnitTestOutput(f, false);
- assertEquals(result.actual, undefined);
- assertEquals(result.expected, undefined);
- }
-);
-
-/*
- * Ensure all unit test files (e.g. xxx_test.ts) are present as imports in
- * cli/js/unit_tests.ts as it is easy to miss this out
- */
-unitTest(
- { perms: { read: true } },
- async function assertAllUnitTestFilesImported(): Promise<void> {
- const directoryTestFiles = Deno.readdirSync("./cli/js")
- .map(k => k.name)
- .filter(file => file!.endsWith("_test.ts"));
- const unitTestsFile: Uint8Array = Deno.readFileSync(
- "./cli/js/unit_tests.ts"
- );
- const importLines = new TextDecoder("utf-8")
- .decode(unitTestsFile)
- .split("\n")
- .filter(line => line.startsWith("import") && line.includes("_test.ts"));
- const importedTestFiles = importLines.map(
- relativeFilePath => relativeFilePath.match(/\/([^\/]+)";/)![1]
- );
-
- directoryTestFiles.forEach(dirFile => {
- if (!importedTestFiles.includes(dirFile!)) {
- throw new Error(
- "cil/js/unit_tests.ts is missing import of test file: cli/js/" +
- dirFile
- );
- }
- });
- }
-);