0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-10-31 09:14:20 -04:00
denoland-deno/cli/tests/unit/test_util.ts
Casper Beyer 6abf126c2a
chore: remove std directory (#9361)
This removes the std folder from the tree.

Various parts of the tests are pretty tightly dependent 
on std (47 direct imports and 75 indirect imports, not 
counting the cli tests that use them as fixtures) so I've 
added std as a submodule for now.
2021-02-02 12:05:46 +01:00

365 lines
8.7 KiB
TypeScript

// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
import {
assert,
assertEquals,
} from "../../../test_util/std/testing/asserts.ts";
import * as colors from "../../../test_util/std/fmt/colors.ts";
export { colors };
import { resolve } from "../../../test_util/std/path/mod.ts";
export {
assert,
assertEquals,
assertMatch,
assertNotEquals,
assertStrictEquals,
assertStringIncludes,
assertThrows,
assertThrowsAsync,
fail,
unreachable,
} from "../../../test_util/std/testing/asserts.ts";
export { deferred } from "../../../test_util/std/async/deferred.ts";
export { readLines } from "../../../test_util/std/io/bufio.ts";
export { parse as parseArgs } from "../../../test_util/std/flags/mod.ts";
export interface Permissions {
read: boolean;
write: boolean;
net: boolean;
env: boolean;
run: boolean;
plugin: boolean;
hrtime: boolean;
}
export function fmtPerms(perms: Permissions): string {
const p = Object.keys(perms)
.filter((e): boolean => perms[e as keyof Permissions] === true)
.map((key) => `--allow-${key}`);
if (p.length) {
return p.join(" ");
}
return "<no permissions>";
}
const isGranted = async (name: Deno.PermissionName): Promise<boolean> =>
(await Deno.permissions.query({ name })).state === "granted";
export 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"),
};
}
export 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);
}
}
export async function registerUnitTests(): Promise<void> {
const processPerms = await getProcessPermissions();
const onlyTests = REGISTERED_UNIT_TESTS.filter(({ only }) => only);
const unitTests = onlyTests.length > 0 ? onlyTests : REGISTERED_UNIT_TESTS;
for (const unitTestDefinition of unitTests) {
if (!permissionsMatch(processPerms, unitTestDefinition.perms)) {
continue;
}
Deno.test(unitTestDefinition);
}
}
function normalizeTestPermissions(perms: UnitTestPermissions): Permissions {
return {
read: !!perms.read,
write: !!perms.write,
net: !!perms.net,
run: !!perms.run,
env: !!perms.env,
plugin: !!perms.plugin,
hrtime: !!perms.hrtime,
};
}
interface UnitTestPermissions {
read?: boolean;
write?: boolean;
net?: boolean;
env?: boolean;
run?: boolean;
plugin?: boolean;
hrtime?: boolean;
}
interface UnitTestOptions {
ignore?: boolean;
only?: boolean;
perms?: UnitTestPermissions;
}
interface UnitTestDefinition extends Deno.TestDefinition {
ignore: boolean;
only: boolean;
perms: Permissions;
}
type TestFunction = () => void | Promise<void>;
export const REGISTERED_UNIT_TESTS: UnitTestDefinition[] = [];
export function unitTest(fn: TestFunction): void;
export function unitTest(options: UnitTestOptions, fn: TestFunction): void;
export function unitTest(
optionsOrFn: UnitTestOptions | TestFunction,
maybeFn?: TestFunction,
): void {
assert(optionsOrFn, "At least one argument is required");
let options: UnitTestOptions;
let name: string;
let fn: 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");
}
const normalizedPerms = normalizeTestPermissions(options.perms || {});
registerPermCombination(normalizedPerms);
const unitTestDefinition: UnitTestDefinition = {
name,
fn,
ignore: !!options.ignore,
only: !!options.only,
perms: normalizedPerms,
};
REGISTERED_UNIT_TESTS.push(unitTestDefinition);
}
const encoder = new TextEncoder();
// Replace functions with null, errors with their stack strings, and JSONify.
// deno-lint-ignore no-explicit-any
function serializeTestMessage(message: any): string {
return JSON.stringify({
start: message.start && {
...message.start,
tests: message.start.tests.map((test: Deno.TestDefinition) => ({
...test,
fn: null,
})),
},
testStart: message.testStart && { ...message.testStart, fn: null },
testEnd: message.testEnd && {
...message.testEnd,
error: String(message.testEnd.error?.stack),
},
end: message.end && {
...message.end,
// deno-lint-ignore no-explicit-any
results: message.end.results.map((result: any) => ({
...result,
error: result.error?.stack,
})),
},
});
}
export async function reportToConn(
conn: Deno.Conn,
// deno-lint-ignore no-explicit-any
message: any,
): Promise<void> {
const line = serializeTestMessage(message);
const encodedMsg = encoder.encode(line + (message.end == null ? "\n" : ""));
await Deno.writeAll(conn, encodedMsg);
if (message.end != null) {
conn.closeWrite();
}
}
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,
},
),
);
});
/*
* Ensure all unit test files (e.g. xxx_test.ts) are present as imports in
* cli/tests/unit/unit_tests.ts as it is easy to miss this out
*/
unitTest(
{ perms: { read: true } },
function assertAllUnitTestFilesImported(): void {
const directoryTestFiles = [...Deno.readDirSync("./cli/tests/unit/")]
.map((k) => k.name)
.filter(
(file) =>
file!.endsWith(".ts") &&
!file!.endsWith("unit_tests.ts") &&
!file!.endsWith("test_util.ts") &&
!file!.endsWith("unit_test_runner.ts"),
);
const unitTestsFile: Uint8Array = Deno.readFileSync(
"./cli/tests/unit/unit_tests.ts",
);
const importLines = new TextDecoder("utf-8")
.decode(unitTestsFile)
.split("\n")
.filter((line) => line.startsWith("import"));
const importedTestFiles = importLines.map(
(relativeFilePath) => relativeFilePath.match(/\/([^\/]+)";/)![1],
);
directoryTestFiles.forEach((dirFile) => {
if (!importedTestFiles.includes(dirFile!)) {
throw new Error(
"cil/tests/unit/unit_tests.ts is missing import of test file: cli/js/" +
dirFile,
);
}
});
},
);
export function pathToAbsoluteFileUrl(path: string): URL {
path = resolve(path);
return new URL(`file://${Deno.build.os === "windows" ? "/" : ""}${path}`);
}