diff options
Diffstat (limited to 'std/testing')
-rw-r--r-- | std/testing/asserts_test.ts | 2 | ||||
-rw-r--r-- | std/testing/bench_test.ts | 4 | ||||
-rw-r--r-- | std/testing/diff_test.ts | 2 | ||||
-rw-r--r-- | std/testing/format_test.ts | 2 | ||||
-rw-r--r-- | std/testing/mod.ts | 450 | ||||
-rwxr-xr-x | std/testing/runner.ts | 7 | ||||
-rw-r--r-- | std/testing/runner_test.ts | 3 | ||||
-rw-r--r-- | std/testing/test.ts | 293 | ||||
-rw-r--r-- | std/testing/testing_bench.ts | 18 |
9 files changed, 7 insertions, 774 deletions
diff --git a/std/testing/asserts_test.ts b/std/testing/asserts_test.ts index ede5abbde..3a846417a 100644 --- a/std/testing/asserts_test.ts +++ b/std/testing/asserts_test.ts @@ -14,8 +14,8 @@ import { unimplemented, unreachable } from "./asserts.ts"; -import { test } from "./mod.ts"; import { red, green, white, gray, bold } from "../fmt/colors.ts"; +const { test } = Deno; test(function testingEqual(): void { assert(equal("world", "world")); diff --git a/std/testing/bench_test.ts b/std/testing/bench_test.ts index 8af2f0d6d..b384b21f7 100644 --- a/std/testing/bench_test.ts +++ b/std/testing/bench_test.ts @@ -1,4 +1,4 @@ -import { test, runIfMain } from "./mod.ts"; +const { test } = Deno; import { bench, runBenchmarks } from "./bench.ts"; import "./bench_example.ts"; @@ -56,5 +56,3 @@ test(async function benching(): Promise<void> { await runBenchmarks({ skip: /throw/ }); }); - -runIfMain(import.meta); diff --git a/std/testing/diff_test.ts b/std/testing/diff_test.ts index d9fbdb956..0e8416274 100644 --- a/std/testing/diff_test.ts +++ b/std/testing/diff_test.ts @@ -1,6 +1,6 @@ import diff from "./diff.ts"; import { assertEquals } from "../testing/asserts.ts"; -import { test } from "./mod.ts"; +const { test } = Deno; test({ name: "empty", diff --git a/std/testing/format_test.ts b/std/testing/format_test.ts index e2bb8df23..14f84f3c2 100644 --- a/std/testing/format_test.ts +++ b/std/testing/format_test.ts @@ -6,7 +6,7 @@ * LICENSE file in the root directory of this source tree. * */ -import { test } from "./mod.ts"; +const { test } = Deno; import { assertEquals } from "../testing/asserts.ts"; import { format } from "./format.ts"; diff --git a/std/testing/mod.ts b/std/testing/mod.ts deleted file mode 100644 index a60e9c93f..000000000 --- a/std/testing/mod.ts +++ /dev/null @@ -1,450 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. - -export * from "./asserts.ts"; -export * from "./bench.ts"; -import diff from "./diff.ts"; -export { diff }; -export * from "./format.ts"; -export * from "./runner.ts"; - -import { - bgRed, - white, - bold, - green, - red, - gray, - yellow, - italic -} from "../fmt/colors.ts"; -import { assert } from "./asserts.ts"; -export type TestFunction = () => void | Promise<void>; - -export interface TestDefinition { - fn: TestFunction; - name: string; -} - -// Replacement of the global `console` function to be in silent mode -const noop = function(): void {}; - -// Clear the current line of the console. -// see: http://ascii-table.com/ansi-escape-sequences-vt-100.php -const CLEAR_LINE = "\x1b[2K\r"; - -// Save Object of the global `console` in case of silent mode -type Console = typeof window.console; -// ref https://console.spec.whatwg.org/#console-namespace -// For historical web-compatibility reasons, the namespace object for -// console must have as its [[Prototype]] an empty object, created as if -// by ObjectCreate(%ObjectPrototype%), instead of %ObjectPrototype%. -const disabledConsole = Object.create({}) as Console; -Object.assign(disabledConsole, { - log: noop, - debug: noop, - info: noop, - dir: noop, - warn: noop, - error: noop, - assert: noop, - count: noop, - countReset: noop, - table: noop, - time: noop, - timeLog: noop, - timeEnd: noop, - group: noop, - groupCollapsed: noop, - groupEnd: noop, - clear: noop -}); - -const originalConsole = window.console; - -function enableConsole(): void { - window.console = originalConsole; -} - -function disableConsole(): void { - window.console = disabledConsole; -} - -const encoder = new TextEncoder(); -function print(txt: string, newline = true): void { - if (newline) { - txt += "\n"; - } - Deno.stdout.writeSync(encoder.encode(`${txt}`)); -} - -declare global { - interface Window { - /** - * A global property to collect all registered test cases. - * - * It is required because user's code can import multiple versions - * of `testing` module. - * - * If test cases aren't registered in a globally shared - * object, then imports from different versions would register test cases - * to registry from it's respective version of `testing` module. - */ - __DENO_TEST_REGISTRY: TestDefinition[]; - } -} - -let candidates: TestDefinition[] = []; -if (window["__DENO_TEST_REGISTRY"]) { - candidates = window.__DENO_TEST_REGISTRY as TestDefinition[]; -} else { - window["__DENO_TEST_REGISTRY"] = candidates; -} -let filterRegExp: RegExp | null; -let filtered = 0; - -// Must be called before any test() that needs to be filtered. -export function setFilter(s: string): void { - filterRegExp = new RegExp(s, "i"); -} - -function filter(name: string): boolean { - if (filterRegExp) { - return filterRegExp.test(name); - } else { - return true; - } -} - -export function test(t: TestDefinition): void; -export function test(fn: TestFunction): void; -export function test(name: string, fn: TestFunction): void; -export function test( - t: string | TestDefinition | TestFunction, - fn?: TestFunction -): void { - let name: string; - - if (typeof t === "string") { - if (!fn) { - throw new Error("Missing test function"); - } - name = t; - if (!name) { - throw new Error("The name of test case can't be empty"); - } - } else if (typeof t === "function") { - fn = t; - name = t.name; - if (!name) { - throw new Error("Test function can't be anonymous"); - } - } else { - fn = t.fn; - if (!fn) { - throw new Error("Missing test function"); - } - name = t.name; - if (!name) { - throw new Error("The name of test case can't be empty"); - } - } - assert(!!name, "The name of test case shouldn't be empty"); - assert(!!fn, "Test function shouldn't be empty"); - - if (filter(name)) { - candidates.push({ fn, name }); - } else { - filtered++; - } -} - -const RED_FAILED = red("FAILED"); -const GREEN_OK = green("OK"); -const RED_BG_FAIL = bgRed(" FAIL "); - -interface TestStats { - filtered: number; - ignored: number; - measured: number; - passed: number; - failed: number; -} - -interface TestResult { - timeElapsed?: number; - name: string; - error?: Error; - ok: boolean; - printed: boolean; -} - -interface TestResults { - keys: Map<string, number>; - cases: Map<number, TestResult>; -} - -function createTestResults(tests: TestDefinition[]): TestResults { - return tests.reduce( - (acc: TestResults, { name }: TestDefinition, i: number): TestResults => { - acc.keys.set(name, i); - acc.cases.set(i, { name, printed: false, ok: false, error: undefined }); - return acc; - }, - { cases: new Map(), keys: new Map() } - ); -} - -function formatTestTime(time = 0): string { - return `${time.toFixed(2)}ms`; -} - -function promptTestTime(time = 0, displayWarning = false): string { - // if time > 5s we display a warning - // only for test time, not the full runtime - if (displayWarning && time >= 5000) { - return bgRed(white(bold(`(${formatTestTime(time)})`))); - } else { - return gray(italic(`(${formatTestTime(time)})`)); - } -} - -function report(result: TestResult): void { - if (result.ok) { - print( - `${GREEN_OK} ${result.name} ${promptTestTime( - result.timeElapsed, - true - )}` - ); - } else if (result.error) { - print(`${RED_FAILED} ${result.name}\n${result.error.stack}`); - } else { - print(`test ${result.name} ... unresolved`); - } - result.printed = true; -} - -function printFailedSummary(results: TestResults): void { - results.cases.forEach((v): void => { - if (!v.ok) { - console.error(`${RED_BG_FAIL} ${red(v.name)}`); - console.error(v.error); - } - }); -} - -function printResults( - stats: TestStats, - results: TestResults, - flush: boolean, - exitOnFail: boolean, - timeElapsed: number -): void { - if (flush) { - for (const result of results.cases.values()) { - if (!result.printed) { - report(result); - if (result.error && exitOnFail) { - break; - } - } - } - } - // Attempting to match the output of Rust's test runner. - print( - `\ntest result: ${stats.failed ? RED_BG_FAIL : GREEN_OK} ` + - `${stats.passed} passed; ${stats.failed} failed; ` + - `${stats.ignored} ignored; ${stats.measured} measured; ` + - `${stats.filtered} filtered out ` + - `${promptTestTime(timeElapsed)}\n` - ); -} - -function previousPrinted(name: string, results: TestResults): boolean { - const curIndex = results.keys.get(name); - assert(curIndex != null); - if (curIndex === 0) { - return true; - } - const prev = results.cases.get(curIndex - 1); - assert(prev != null); - return prev.printed; -} - -async function createTestCase( - stats: TestStats, - results: TestResults, - exitOnFail: boolean, - { fn, name }: TestDefinition -): Promise<void> { - const i = results.keys.get(name); - assert(i != null); - const result = results.cases.get(i); - assert(result != null); - try { - const start = performance.now(); - await fn(); - const end = performance.now(); - stats.passed++; - result.ok = true; - result.timeElapsed = end - start; - } catch (err) { - stats.failed++; - result.error = err; - if (exitOnFail) { - throw err; - } - } - if (previousPrinted(name, results)) { - report(result); - } -} - -function initTestCases( - stats: TestStats, - results: TestResults, - tests: TestDefinition[], - exitOnFail: boolean -): Array<Promise<void>> { - return tests.map(createTestCase.bind(null, stats, results, exitOnFail)); -} - -async function runTestsParallel( - stats: TestStats, - results: TestResults, - tests: TestDefinition[], - exitOnFail: boolean -): Promise<void> { - try { - await Promise.all(initTestCases(stats, results, tests, exitOnFail)); - } catch (_) { - // The error was thrown to stop awaiting all promises if exitOnFail === true - // stats.failed has been incremented and the error stored in results - } -} - -async function runTestsSerial( - stats: TestStats, - results: TestResults, - tests: TestDefinition[], - exitOnFail: boolean, - disableLog: boolean -): Promise<void> { - for (const { fn, name } of tests) { - // Displaying the currently running test if silent mode - if (disableLog) { - print(`${yellow("RUNNING")} ${name}`, false); - } - try { - const start = performance.now(); - await fn(); - const end = performance.now(); - if (disableLog) { - // Rewriting the current prompt line to erase `running ....` - print(CLEAR_LINE, false); - } - stats.passed++; - print( - GREEN_OK + " " + name + " " + promptTestTime(end - start, true) - ); - results.cases.forEach((v): void => { - if (v.name === name) { - v.ok = true; - v.printed = true; - } - }); - } catch (err) { - if (disableLog) { - print(CLEAR_LINE, false); - } - print(`${RED_FAILED} ${name}`); - print(err.stack); - stats.failed++; - results.cases.forEach((v): void => { - if (v.name === name) { - v.error = err; - v.ok = false; - v.printed = true; - } - }); - if (exitOnFail) { - break; - } - } - } -} - -/** Defines options for controlling execution details of a test suite. */ -export interface RunTestsOptions { - parallel?: boolean; - exitOnFail?: boolean; - only?: RegExp; - skip?: RegExp; - disableLog?: boolean; -} - -/** - * Runs specified test cases. - * Parallel execution can be enabled via the boolean option; default: serial. - */ -// TODO: change return type to `Promise<boolean>` - ie. don't -// exit but return value -export async function runTests({ - parallel = false, - exitOnFail = false, - only = /[^\s]/, - skip = /^\s*$/, - disableLog = false -}: RunTestsOptions = {}): Promise<void> { - const tests: TestDefinition[] = candidates.filter( - ({ name }): boolean => only.test(name) && !skip.test(name) - ); - const stats: TestStats = { - measured: 0, - ignored: candidates.length - tests.length, - filtered: filtered, - passed: 0, - failed: 0 - }; - const results: TestResults = createTestResults(tests); - print(`running ${tests.length} tests`); - const start = performance.now(); - if (Deno.args.includes("--quiet")) { - disableLog = true; - } - if (disableLog) { - disableConsole(); - } - if (parallel) { - await runTestsParallel(stats, results, tests, exitOnFail); - } else { - await runTestsSerial(stats, results, tests, exitOnFail, disableLog); - } - const end = performance.now(); - if (disableLog) { - enableConsole(); - } - printResults(stats, results, parallel, exitOnFail, end - start); - if (stats.failed) { - // Use setTimeout to avoid the error being ignored due to unhandled - // promise rejections being swallowed. - setTimeout((): void => { - console.error(`There were ${stats.failed} test failures.`); - printFailedSummary(results); - Deno.exit(1); - }, 0); - } -} - -/** - * Runs specified test cases if the enclosing script is main. - * Execution mode is toggleable via opts.parallel, defaults to false. - */ -export async function runIfMain( - meta: ImportMeta, - opts?: RunTestsOptions -): Promise<void> { - if (meta.main) { - return runTests(opts); - } -} diff --git a/std/testing/runner.ts b/std/testing/runner.ts index 17d5942f8..8d6501e02 100755 --- a/std/testing/runner.ts +++ b/std/testing/runner.ts @@ -3,7 +3,6 @@ import { parse } from "../flags/mod.ts"; import { ExpandGlobOptions, expandGlob } from "../fs/mod.ts"; import { isWindows, join } from "../path/mod.ts"; -import { RunTestsOptions, runTests } from "./mod.ts"; const { DenoError, ErrorKind, args, cwd, exit } = Deno; const DIR_GLOBS = [join("**", "?(*_)test.{js,ts}")]; @@ -110,7 +109,7 @@ export async function* findTestModules( yield* includeUrls.filter(shouldIncludeUrl); } -export interface RunTestModulesOptions extends RunTestsOptions { +export interface RunTestModulesOptions extends Deno.RunTestsOptions { include?: string[]; exclude?: string[]; allowNone?: boolean; @@ -168,7 +167,6 @@ export async function runTestModules({ include = ["."], exclude = [], allowNone = false, - parallel = false, exitOnFail = false, only = /[^\s]/, skip = /^\s*$/, @@ -233,8 +231,7 @@ export async function runTestModules({ console.log(`Found ${moduleCount} matching test modules.`); } - await runTests({ - parallel, + await Deno.runTests({ exitOnFail, only, skip, diff --git a/std/testing/runner_test.ts b/std/testing/runner_test.ts index 95f2b49cc..f39e5d326 100644 --- a/std/testing/runner_test.ts +++ b/std/testing/runner_test.ts @@ -1,9 +1,8 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. import { assertEquals } from "../testing/asserts.ts"; import { isWindows } from "../path/mod.ts"; -import { test } from "./mod.ts"; import { findTestModules } from "./runner.ts"; -const { cwd } = Deno; +const { cwd, test } = Deno; function urlToFilePath(url: URL): string { // Since `new URL('file:///C:/a').pathname` is `/C:/a`, remove leading slash. diff --git a/std/testing/test.ts b/std/testing/test.ts deleted file mode 100644 index 5379a5199..000000000 --- a/std/testing/test.ts +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { test, runIfMain } from "./mod.ts"; -import { - assert, - assertEquals, - assertStrictEq, - assertThrows, - assertThrowsAsync -} from "./asserts.ts"; - -test(function testingAssertEqualActualUncoercable(): void { - let didThrow = false; - const a = Object.create(null); - try { - assertEquals(a, "bar"); - } catch (e) { - didThrow = true; - } - assert(didThrow); -}); - -test(function testingAssertEqualExpectedUncoercable(): void { - let didThrow = false; - const a = Object.create(null); - try { - assertStrictEq("bar", a); - } catch (e) { - didThrow = true; - } - assert(didThrow); -}); - -test(function testingAssertStrictEqual(): void { - const a = {}; - const b = a; - assertStrictEq(a, b); -}); - -test(function testingAssertNotStrictEqual(): void { - let didThrow = false; - const a = {}; - const b = {}; - try { - assertStrictEq(a, b); - } catch (e) { - assert(e.message === "actual: [object Object] expected: [object Object]"); - didThrow = true; - } - assert(didThrow); -}); - -test(function testingDoesThrow(): void { - let count = 0; - assertThrows((): void => { - count++; - throw new Error(); - }); - assert(count === 1); -}); - -test(function testingDoesNotThrow(): void { - let count = 0; - let didThrow = false; - try { - assertThrows((): void => { - count++; - console.log("Hello world"); - }); - } catch (e) { - assert(e.message === "Expected function to throw."); - didThrow = true; - } - assert(count === 1); - assert(didThrow); -}); - -test(function testingThrowsErrorType(): void { - let count = 0; - assertThrows((): void => { - count++; - throw new TypeError(); - }, TypeError); - assert(count === 1); -}); - -test(function testingThrowsNotErrorType(): void { - let count = 0; - let didThrow = false; - try { - assertThrows((): void => { - count++; - throw new TypeError(); - }, RangeError); - } catch (e) { - assert(e.message === `Expected error to be instance of "RangeError".`); - didThrow = true; - } - assert(count === 1); - assert(didThrow); -}); - -test(function testingThrowsMsgIncludes(): void { - let count = 0; - assertThrows( - (): void => { - count++; - throw new TypeError("Hello world!"); - }, - TypeError, - "world" - ); - assert(count === 1); -}); - -test(function testingThrowsMsgNotIncludes(): void { - let count = 0; - let didThrow = false; - try { - assertThrows( - (): void => { - count++; - throw new TypeError("Hello world!"); - }, - TypeError, - "foobar" - ); - } catch (e) { - assert( - e.message === - `Expected error message to include "foobar", but got "Hello world!".` - ); - didThrow = true; - } - assert(count === 1); - assert(didThrow); -}); - -test(async function testingDoesThrowAsync(): Promise<void> { - let count = 0; - await assertThrowsAsync( - async (): Promise<void> => { - count++; - throw new Error(); - } - ); - assert(count === 1); -}); - -test(async function testingDoesReject(): Promise<void> { - let count = 0; - await assertThrowsAsync( - (): Promise<never> => { - count++; - return Promise.reject(new Error()); - } - ); - assert(count === 1); -}); - -test(async function testingDoesNotThrowAsync(): Promise<void> { - let count = 0; - let didThrow = false; - try { - await assertThrowsAsync( - async (): Promise<void> => { - count++; - console.log("Hello world"); - } - ); - } catch (e) { - assert(e.message === "Expected function to throw."); - didThrow = true; - } - assert(count === 1); - assert(didThrow); -}); - -test(async function testingDoesNotRejectAsync(): Promise<void> { - let count = 0; - let didThrow = false; - try { - await assertThrowsAsync( - (): Promise<void> => { - count++; - console.log("Hello world"); - return Promise.resolve(); - } - ); - } catch (e) { - assert(e.message === "Expected function to throw."); - didThrow = true; - } - assert(count === 1); - assert(didThrow); -}); - -test(async function testingThrowsAsyncErrorType(): Promise<void> { - let count = 0; - await assertThrowsAsync((): Promise<void> => { - count++; - throw new TypeError(); - }, TypeError); - assert(count === 1); -}); - -test(async function testingThrowsAsyncNotErrorType(): Promise<void> { - let count = 0; - let didThrow = false; - try { - await assertThrowsAsync(async (): Promise<void> => { - count++; - throw new TypeError(); - }, RangeError); - } catch (e) { - assert(e.message === `Expected error to be instance of "RangeError".`); - didThrow = true; - } - assert(count === 1); - assert(didThrow); -}); - -test(async function testingThrowsAsyncMsgIncludes(): Promise<void> { - let count = 0; - await assertThrowsAsync( - async (): Promise<void> => { - count++; - throw new TypeError("Hello world!"); - }, - TypeError, - "world" - ); - assert(count === 1); -}); - -test(async function testingThrowsAsyncMsgNotIncludes(): Promise<void> { - let count = 0; - let didThrow = false; - try { - await assertThrowsAsync( - async (): Promise<void> => { - count++; - throw new TypeError("Hello world!"); - }, - TypeError, - "foobar" - ); - } catch (e) { - assert( - e.message === - `Expected error message to include "foobar", but got "Hello world!".` - ); - didThrow = true; - } - assert(count === 1); - assert(didThrow); -}); - -test("test fn overloading", (): void => { - // just verifying that you can use this test definition syntax - assert(true); -}); - -test("The name of test case can't be empty", () => { - assertThrows( - () => { - test("", () => {}); - }, - Error, - "The name of test case can't be empty" - ); - assertThrows( - () => { - test({ - name: "", - fn: () => {} - }); - }, - Error, - "The name of test case can't be empty" - ); -}); - -test("test function can't be anonymous", () => { - assertThrows( - () => { - test(function() {}); - }, - Error, - "Test function can't be anonymous" - ); -}); - -runIfMain(import.meta); diff --git a/std/testing/testing_bench.ts b/std/testing/testing_bench.ts deleted file mode 100644 index 9033e3a72..000000000 --- a/std/testing/testing_bench.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { bench, runIfMain } from "./bench.ts"; -import { runTests } from "./mod.ts"; - -import "./asserts_test.ts"; - -bench(async function testingSerial(b): Promise<void> { - b.start(); - await runTests(); - b.stop(); -}); - -bench(async function testingParallel(b): Promise<void> { - b.start(); - await runTests({ parallel: true }); - b.stop(); -}); - -runIfMain(import.meta); |