diff options
Diffstat (limited to 'std/testing')
-rw-r--r-- | std/testing/README.md | 248 | ||||
-rw-r--r-- | std/testing/_diff.ts | 228 | ||||
-rw-r--r-- | std/testing/_diff_test.ts | 111 | ||||
-rw-r--r-- | std/testing/asserts.ts | 617 | ||||
-rw-r--r-- | std/testing/asserts_test.ts | 927 | ||||
-rw-r--r-- | std/testing/bench.ts | 363 | ||||
-rw-r--r-- | std/testing/bench_example.ts | 32 | ||||
-rw-r--r-- | std/testing/bench_test.ts | 379 |
8 files changed, 0 insertions, 2905 deletions
diff --git a/std/testing/README.md b/std/testing/README.md deleted file mode 100644 index 2cff96a6d..000000000 --- a/std/testing/README.md +++ /dev/null @@ -1,248 +0,0 @@ -# Testing - -This module provides a few basic utilities to make testing easier and consistent -in Deno. - -## Usage - -`testing/asserts.ts` module provides range of assertion helpers. If the -assertion is false an `AssertionError` will be thrown which will result in -pretty-printed diff of failing assertion. - -- `equal()` - Deep comparison function, where `actual` and `expected` are - compared deeply, and if they vary, `equal` returns `false`. -- `assert()` - Expects a boolean value, throws if the value is `false`. -- `assertEquals()` - Uses the `equal` comparison and throws if the `actual` and - `expected` are not equal. -- `assertNotEquals()` - Uses the `equal` comparison and throws if the `actual` - and `expected` are equal. -- `assertStrictEquals()` - Compares `actual` and `expected` strictly, therefore - for non-primitives the values must reference the same instance. -- `assertStringIncludes()` - Make an assertion that `actual` includes - `expected`. -- `assertMatch()` - Make an assertion that `actual` match RegExp `expected`. -- `assertNotMatch()` - Make an assertion that `actual` not match RegExp - `expected`. -- `assertArrayIncludes()` - Make an assertion that `actual` array includes the - `expected` values. -- `assertObjectMatch()` - Make an assertion that `actual` object match - `expected` subset object -- `assertThrows()` - Expects the passed `fn` to throw. If `fn` does not throw, - this function does. Also compares any errors thrown to an optional expected - `Error` class and checks that the error `.message` includes an optional - string. -- `assertThrowsAsync()` - Expects the passed `fn` to be async and throw (or - return a `Promise` that rejects). If the `fn` does not throw or reject, this - function will throw asynchronously. Also compares any errors thrown to an - optional expected `Error` class and checks that the error `.message` includes - an optional string. -- `unimplemented()` - Use this to stub out methods that will throw when invoked. -- `unreachable()` - Used to assert unreachable code. - -Basic usage: - -```ts -import { assertEquals } from "https://deno.land/std@$STD_VERSION/testing/asserts.ts"; - -Deno.test({ - name: "testing example", - fn(): void { - assertEquals("world", "world"); - assertEquals({ hello: "world" }, { hello: "world" }); - }, -}); -``` - -Short syntax (named function instead of object): - -```ts -Deno.test("example", function (): void { - assertEquals("world", "world"); - assertEquals({ hello: "world" }, { hello: "world" }); -}); -``` - -Using `assertStrictEquals()`: - -```ts -Deno.test("isStrictlyEqual", function (): void { - const a = {}; - const b = a; - assertStrictEquals(a, b); -}); - -// This test fails -Deno.test("isNotStrictlyEqual", function (): void { - const a = {}; - const b = {}; - assertStrictEquals(a, b); -}); -``` - -Using `assertThrows()`: - -```ts -Deno.test("doesThrow", function (): void { - assertThrows((): void => { - throw new TypeError("hello world!"); - }); - assertThrows((): void => { - throw new TypeError("hello world!"); - }, TypeError); - assertThrows( - (): void => { - throw new TypeError("hello world!"); - }, - TypeError, - "hello", - ); -}); - -// This test will not pass. -Deno.test("fails", function (): void { - assertThrows((): void => { - console.log("Hello world"); - }); -}); -``` - -Using `assertThrowsAsync()`: - -```ts -Deno.test("doesThrow", async function (): Promise<void> { - await assertThrowsAsync( - async (): Promise<void> => { - throw new TypeError("hello world!"); - }, - ); - await assertThrowsAsync(async (): Promise<void> => { - throw new TypeError("hello world!"); - }, TypeError); - await assertThrowsAsync( - async (): Promise<void> => { - throw new TypeError("hello world!"); - }, - TypeError, - "hello", - ); - await assertThrowsAsync( - async (): Promise<void> => { - return Promise.reject(new Error()); - }, - ); -}); - -// This test will not pass. -Deno.test("fails", async function (): Promise<void> { - await assertThrowsAsync( - async (): Promise<void> => { - console.log("Hello world"); - }, - ); -}); -``` - -## Benching - -With this module you can benchmark your code and get information on how is it -performing. - -### Basic usage: - -Benchmarks can be registered using the `bench` function, where you can define a -code, that should be benchmarked. `b.start()` has to be called at the start of -the part you want to benchmark and `b.stop()` at the end of it, otherwise an -error will be thrown. - -After that simply calling `runBenchmarks()` will benchmark all registered -benchmarks and log the results in the commandline. - -```ts -import { - bench, - runBenchmarks, -} from "https://deno.land/std@$STD_VERSION/testing/bench.ts"; - -bench(function forIncrementX1e9(b): void { - b.start(); - for (let i = 0; i < 1e9; i++); - b.stop(); -}); - -runBenchmarks(); -``` - -Averaging execution time over multiple runs: - -```ts -bench({ - name: "runs100ForIncrementX1e6", - runs: 100, - func(b): void { - b.start(); - for (let i = 0; i < 1e6; i++); - b.stop(); - }, -}); -``` - -Running specific benchmarks using regular expressions: - -```ts -runBenchmarks({ only: /desired/, skip: /exceptions/ }); -``` - -### Processing benchmark results - -`runBenchmarks()` returns a `Promise<BenchmarkRunResult>`, so you can process -the benchmarking results yourself. It contains detailed results of each -benchmark's run as `BenchmarkResult` s. - -```ts -runBenchmarks() - .then((results: BenchmarkRunResult) => { - console.log(results); - }) - .catch((error: Error) => { - // ... errors if benchmark was badly constructed. - }); -``` - -### Processing benchmarking progress - -`runBenchmarks()` accepts an optional progress handler callback function, so you -can get information on the progress of the running benchmarking. - -Using `{ silent: true }` means you wont see the default progression logs in the -commandline. - -```ts -runBenchmarks({ silent: true }, (p: BenchmarkRunProgress) => { - // initial progress data. - if (p.state === ProgressState.BenchmarkingStart) { - console.log( - `Starting benchmarking. Queued: ${p.queued.length}, filtered: ${p.filtered}`, - ); - } - // ... -}); -``` - -#### Benching API - -##### `bench(benchmark: BenchmarkDefinition | BenchmarkFunction): void` - -Registers a benchmark that will be run once `runBenchmarks` is called. - -##### `runBenchmarks(opts?: BenchmarkRunOptions, progressCb?: (p: BenchmarkRunProgress) => void | Promise<void>): Promise<BenchmarkRunResult>` - -Runs all registered benchmarks serially. Filtering can be applied by setting -`BenchmarkRunOptions.only` and/or `BenchmarkRunOptions.skip` to regular -expressions matching benchmark names. Default progression logs can be turned off -with the `BenchmarkRunOptions.silent` flag. - -##### `clearBenchmarks(opts?: BenchmarkClearOptions): void` - -Clears all registered benchmarks, so calling `runBenchmarks()` after it wont run -them. Filtering can be applied by setting `BenchmarkRunOptions.only` and/or -`BenchmarkRunOptions.skip` to regular expressions matching benchmark names. diff --git a/std/testing/_diff.ts b/std/testing/_diff.ts deleted file mode 100644 index 7d659acd2..000000000 --- a/std/testing/_diff.ts +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// This module is browser compatible. - -interface FarthestPoint { - y: number; - id: number; -} - -export enum DiffType { - removed = "removed", - common = "common", - added = "added", -} - -export interface DiffResult<T> { - type: DiffType; - value: T; -} - -const REMOVED = 1; -const COMMON = 2; -const ADDED = 3; - -function createCommon<T>(A: T[], B: T[], reverse?: boolean): T[] { - const common = []; - if (A.length === 0 || B.length === 0) return []; - for (let i = 0; i < Math.min(A.length, B.length); i += 1) { - if ( - A[reverse ? A.length - i - 1 : i] === B[reverse ? B.length - i - 1 : i] - ) { - common.push(A[reverse ? A.length - i - 1 : i]); - } else { - return common; - } - } - return common; -} - -/** - * Renders the differences between the actual and expected values - * @param A Actual value - * @param B Expected value - */ -export function diff<T>(A: T[], B: T[]): Array<DiffResult<T>> { - const prefixCommon = createCommon(A, B); - const suffixCommon = createCommon( - A.slice(prefixCommon.length), - B.slice(prefixCommon.length), - true, - ).reverse(); - A = suffixCommon.length - ? A.slice(prefixCommon.length, -suffixCommon.length) - : A.slice(prefixCommon.length); - B = suffixCommon.length - ? B.slice(prefixCommon.length, -suffixCommon.length) - : B.slice(prefixCommon.length); - const swapped = B.length > A.length; - [A, B] = swapped ? [B, A] : [A, B]; - const M = A.length; - const N = B.length; - if (!M && !N && !suffixCommon.length && !prefixCommon.length) return []; - if (!N) { - return [ - ...prefixCommon.map( - (c): DiffResult<typeof c> => ({ type: DiffType.common, value: c }), - ), - ...A.map( - (a): DiffResult<typeof a> => ({ - type: swapped ? DiffType.added : DiffType.removed, - value: a, - }), - ), - ...suffixCommon.map( - (c): DiffResult<typeof c> => ({ type: DiffType.common, value: c }), - ), - ]; - } - const offset = N; - const delta = M - N; - const size = M + N + 1; - const fp = new Array(size).fill({ y: -1 }); - /** - * INFO: - * This buffer is used to save memory and improve performance. - * The first half is used to save route and last half is used to save diff - * type. - * This is because, when I kept new uint8array area to save type,performance - * worsened. - */ - const routes = new Uint32Array((M * N + size + 1) * 2); - const diffTypesPtrOffset = routes.length / 2; - let ptr = 0; - let p = -1; - - function backTrace<T>( - A: T[], - B: T[], - current: FarthestPoint, - swapped: boolean, - ): Array<{ - type: DiffType; - value: T; - }> { - const M = A.length; - const N = B.length; - const result = []; - let a = M - 1; - let b = N - 1; - let j = routes[current.id]; - let type = routes[current.id + diffTypesPtrOffset]; - while (true) { - if (!j && !type) break; - const prev = j; - if (type === REMOVED) { - result.unshift({ - type: swapped ? DiffType.removed : DiffType.added, - value: B[b], - }); - b -= 1; - } else if (type === ADDED) { - result.unshift({ - type: swapped ? DiffType.added : DiffType.removed, - value: A[a], - }); - a -= 1; - } else { - result.unshift({ type: DiffType.common, value: A[a] }); - a -= 1; - b -= 1; - } - j = routes[prev]; - type = routes[prev + diffTypesPtrOffset]; - } - return result; - } - - function createFP( - slide: FarthestPoint, - down: FarthestPoint, - k: number, - M: number, - ): FarthestPoint { - if (slide && slide.y === -1 && down && down.y === -1) { - return { y: 0, id: 0 }; - } - if ( - (down && down.y === -1) || - k === M || - (slide && slide.y) > (down && down.y) + 1 - ) { - const prev = slide.id; - ptr++; - routes[ptr] = prev; - routes[ptr + diffTypesPtrOffset] = ADDED; - return { y: slide.y, id: ptr }; - } else { - const prev = down.id; - ptr++; - routes[ptr] = prev; - routes[ptr + diffTypesPtrOffset] = REMOVED; - return { y: down.y + 1, id: ptr }; - } - } - - function snake<T>( - k: number, - slide: FarthestPoint, - down: FarthestPoint, - _offset: number, - A: T[], - B: T[], - ): FarthestPoint { - const M = A.length; - const N = B.length; - if (k < -N || M < k) return { y: -1, id: -1 }; - const fp = createFP(slide, down, k, M); - while (fp.y + k < M && fp.y < N && A[fp.y + k] === B[fp.y]) { - const prev = fp.id; - ptr++; - fp.id = ptr; - fp.y += 1; - routes[ptr] = prev; - routes[ptr + diffTypesPtrOffset] = COMMON; - } - return fp; - } - - while (fp[delta + offset].y < N) { - p = p + 1; - for (let k = -p; k < delta; ++k) { - fp[k + offset] = snake( - k, - fp[k - 1 + offset], - fp[k + 1 + offset], - offset, - A, - B, - ); - } - for (let k = delta + p; k > delta; --k) { - fp[k + offset] = snake( - k, - fp[k - 1 + offset], - fp[k + 1 + offset], - offset, - A, - B, - ); - } - fp[delta + offset] = snake( - delta, - fp[delta - 1 + offset], - fp[delta + 1 + offset], - offset, - A, - B, - ); - } - return [ - ...prefixCommon.map( - (c): DiffResult<typeof c> => ({ type: DiffType.common, value: c }), - ), - ...backTrace(A, B, fp[delta + offset], swapped), - ...suffixCommon.map( - (c): DiffResult<typeof c> => ({ type: DiffType.common, value: c }), - ), - ]; -} diff --git a/std/testing/_diff_test.ts b/std/testing/_diff_test.ts deleted file mode 100644 index ec40c191c..000000000 --- a/std/testing/_diff_test.ts +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { diff } from "./_diff.ts"; -import { assertEquals } from "../testing/asserts.ts"; - -Deno.test({ - name: "empty", - fn(): void { - assertEquals(diff([], []), []); - }, -}); - -Deno.test({ - name: '"a" vs "b"', - fn(): void { - assertEquals(diff(["a"], ["b"]), [ - { type: "removed", value: "a" }, - { type: "added", value: "b" }, - ]); - }, -}); - -Deno.test({ - name: '"a" vs "a"', - fn(): void { - assertEquals(diff(["a"], ["a"]), [{ type: "common", value: "a" }]); - }, -}); - -Deno.test({ - name: '"a" vs ""', - fn(): void { - assertEquals(diff(["a"], []), [{ type: "removed", value: "a" }]); - }, -}); - -Deno.test({ - name: '"" vs "a"', - fn(): void { - assertEquals(diff([], ["a"]), [{ type: "added", value: "a" }]); - }, -}); - -Deno.test({ - name: '"a" vs "a, b"', - fn(): void { - assertEquals(diff(["a"], ["a", "b"]), [ - { type: "common", value: "a" }, - { type: "added", value: "b" }, - ]); - }, -}); - -Deno.test({ - name: '"strength" vs "string"', - fn(): void { - assertEquals(diff(Array.from("strength"), Array.from("string")), [ - { type: "common", value: "s" }, - { type: "common", value: "t" }, - { type: "common", value: "r" }, - { type: "removed", value: "e" }, - { type: "added", value: "i" }, - { type: "common", value: "n" }, - { type: "common", value: "g" }, - { type: "removed", value: "t" }, - { type: "removed", value: "h" }, - ]); - }, -}); - -Deno.test({ - name: '"strength" vs ""', - fn(): void { - assertEquals(diff(Array.from("strength"), Array.from("")), [ - { type: "removed", value: "s" }, - { type: "removed", value: "t" }, - { type: "removed", value: "r" }, - { type: "removed", value: "e" }, - { type: "removed", value: "n" }, - { type: "removed", value: "g" }, - { type: "removed", value: "t" }, - { type: "removed", value: "h" }, - ]); - }, -}); - -Deno.test({ - name: '"" vs "strength"', - fn(): void { - assertEquals(diff(Array.from(""), Array.from("strength")), [ - { type: "added", value: "s" }, - { type: "added", value: "t" }, - { type: "added", value: "r" }, - { type: "added", value: "e" }, - { type: "added", value: "n" }, - { type: "added", value: "g" }, - { type: "added", value: "t" }, - { type: "added", value: "h" }, - ]); - }, -}); - -Deno.test({ - name: '"abc", "c" vs "abc", "bcd", "c"', - fn(): void { - assertEquals(diff(["abc", "c"], ["abc", "bcd", "c"]), [ - { type: "common", value: "abc" }, - { type: "added", value: "bcd" }, - { type: "common", value: "c" }, - ]); - }, -}); diff --git a/std/testing/asserts.ts b/std/testing/asserts.ts deleted file mode 100644 index 3daf0d83f..000000000 --- a/std/testing/asserts.ts +++ /dev/null @@ -1,617 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// This module is browser compatible. Do not rely on good formatting of values -// for AssertionError messages in browsers. - -import { bold, gray, green, red, stripColor, white } from "../fmt/colors.ts"; -import { diff, DiffResult, DiffType } from "./_diff.ts"; - -const CAN_NOT_DISPLAY = "[Cannot display]"; - -interface Constructor { - // deno-lint-ignore no-explicit-any - new (...args: any[]): any; -} - -export class AssertionError extends Error { - constructor(message: string) { - super(message); - this.name = "AssertionError"; - } -} - -/** - * Converts the input into a string. Objects, Sets and Maps are sorted so as to - * make tests less flaky - * @param v Value to be formatted - */ -export function _format(v: unknown): string { - return globalThis.Deno - ? Deno.inspect(v, { - depth: Infinity, - sorted: true, - trailingComma: true, - compact: false, - iterableLimit: Infinity, - }) - : `"${String(v).replace(/(?=["\\])/g, "\\")}"`; -} - -/** - * Colors the output of assertion diffs - * @param diffType Difference type, either added or removed - */ -function createColor(diffType: DiffType): (s: string) => string { - switch (diffType) { - case DiffType.added: - return (s: string): string => green(bold(s)); - case DiffType.removed: - return (s: string): string => red(bold(s)); - default: - return white; - } -} - -/** - * Prefixes `+` or `-` in diff output - * @param diffType Difference type, either added or removed - */ -function createSign(diffType: DiffType): string { - switch (diffType) { - case DiffType.added: - return "+ "; - case DiffType.removed: - return "- "; - default: - return " "; - } -} - -function buildMessage(diffResult: ReadonlyArray<DiffResult<string>>): string[] { - const messages: string[] = []; - messages.push(""); - messages.push(""); - messages.push( - ` ${gray(bold("[Diff]"))} ${red(bold("Actual"))} / ${ - green(bold("Expected")) - }`, - ); - messages.push(""); - messages.push(""); - diffResult.forEach((result: DiffResult<string>): void => { - const c = createColor(result.type); - messages.push(c(`${createSign(result.type)}${result.value}`)); - }); - messages.push(""); - - return messages; -} - -function isKeyedCollection(x: unknown): x is Set<unknown> { - return [Symbol.iterator, "size"].every((k) => k in (x as Set<unknown>)); -} - -/** - * Deep equality comparison used in assertions - * @param c actual value - * @param d expected value - */ -export function equal(c: unknown, d: unknown): boolean { - const seen = new Map(); - return (function compare(a: unknown, b: unknown): boolean { - // Have to render RegExp & Date for string comparison - // unless it's mistreated as object - if ( - a && - b && - ((a instanceof RegExp && b instanceof RegExp) || - (a instanceof URL && b instanceof URL)) - ) { - return String(a) === String(b); - } - if (a instanceof Date && b instanceof Date) { - const aTime = a.getTime(); - const bTime = b.getTime(); - // Check for NaN equality manually since NaN is not - // equal to itself. - if (Number.isNaN(aTime) && Number.isNaN(bTime)) { - return true; - } - return a.getTime() === b.getTime(); - } - if (Object.is(a, b)) { - return true; - } - if (a && typeof a === "object" && b && typeof b === "object") { - if (seen.get(a) === b) { - return true; - } - if (Object.keys(a || {}).length !== Object.keys(b || {}).length) { - return false; - } - if (isKeyedCollection(a) && isKeyedCollection(b)) { - if (a.size !== b.size) { - return false; - } - - let unmatchedEntries = a.size; - - for (const [aKey, aValue] of a.entries()) { - for (const [bKey, bValue] of b.entries()) { - /* Given that Map keys can be references, we need - * to ensure that they are also deeply equal */ - if ( - (aKey === aValue && bKey === bValue && compare(aKey, bKey)) || - (compare(aKey, bKey) && compare(aValue, bValue)) - ) { - unmatchedEntries--; - } - } - } - - return unmatchedEntries === 0; - } - const merged = { ...a, ...b }; - for (const key in merged) { - type Key = keyof typeof merged; - if (!compare(a && a[key as Key], b && b[key as Key])) { - return false; - } - } - seen.set(a, b); - return true; - } - return false; - })(c, d); -} - -/** Make an assertion, error will be thrown if `expr` does not have truthy value. */ -export function assert(expr: unknown, msg = ""): asserts expr { - if (!expr) { - throw new AssertionError(msg); - } -} - -/** - * Make an assertion that `actual` and `expected` are equal, deeply. If not - * deeply equal, then throw. - * - * Type parameter can be specified to ensure values under comparison have the same type. - * For example: - *```ts - *assertEquals<number>(1, 2) - *``` - */ -export function assertEquals( - actual: unknown, - expected: unknown, - msg?: string, -): void; -export function assertEquals<T>(actual: T, expected: T, msg?: string): void; -export function assertEquals( - actual: unknown, - expected: unknown, - msg?: string, -): void { - if (equal(actual, expected)) { - return; - } - let message = ""; - const actualString = _format(actual); - const expectedString = _format(expected); - try { - const diffResult = diff( - actualString.split("\n"), - expectedString.split("\n"), - ); - const diffMsg = buildMessage(diffResult).join("\n"); - message = `Values are not equal:\n${diffMsg}`; - } catch (e) { - message = `\n${red(CAN_NOT_DISPLAY)} + \n\n`; - } - if (msg) { - message = msg; - } - throw new AssertionError(message); -} - -/** - * Make an assertion that `actual` and `expected` are not equal, deeply. - * If not then throw. - * - * Type parameter can be specified to ensure values under comparison have the same type. - * For example: - *```ts - *assertNotEquals<number>(1, 2) - *``` - */ -export function assertNotEquals( - actual: unknown, - expected: unknown, - msg?: string, -): void; -export function assertNotEquals<T>(actual: T, expected: T, msg?: string): void; -export function assertNotEquals( - actual: unknown, - expected: unknown, - msg?: string, -): void { - if (!equal(actual, expected)) { - return; - } - let actualString: string; - let expectedString: string; - try { - actualString = String(actual); - } catch (e) { - actualString = "[Cannot display]"; - } - try { - expectedString = String(expected); - } catch (e) { - expectedString = "[Cannot display]"; - } - if (!msg) { - msg = `actual: ${actualString} expected: ${expectedString}`; - } - throw new AssertionError(msg); -} - -/** - * Make an assertion that `actual` and `expected` are strictly equal. If - * not then throw. - * ```ts - * assertStrictEquals(1, 2) - * ``` - */ -export function assertStrictEquals( - actual: unknown, - expected: unknown, - msg?: string, -): void; -export function assertStrictEquals<T>( - actual: T, - expected: T, - msg?: string, -): void; -export function assertStrictEquals( - actual: unknown, - expected: unknown, - msg?: string, -): void { - if (actual === expected) { - return; - } - - let message: string; - - if (msg) { - message = msg; - } else { - const actualString = _format(actual); - const expectedString = _format(expected); - - if (actualString === expectedString) { - const withOffset = actualString - .split("\n") - .map((l) => ` ${l}`) - .join("\n"); - message = - `Values have the same structure but are not reference-equal:\n\n${ - red(withOffset) - }\n`; - } else { - try { - const diffResult = diff( - actualString.split("\n"), - expectedString.split("\n"), - ); - const diffMsg = buildMessage(diffResult).join("\n"); - message = `Values are not strictly equal:\n${diffMsg}`; - } catch (e) { - message = `\n${red(CAN_NOT_DISPLAY)} + \n\n`; - } - } - } - - throw new AssertionError(message); -} - -/** - * Make an assertion that `actual` and `expected` are not strictly equal. - * If the values are strictly equal then throw. - * ```ts - * assertNotStrictEquals(1, 1) - * ``` - */ -export function assertNotStrictEquals( - actual: unknown, - expected: unknown, - msg?: string, -): void; -export function assertNotStrictEquals<T>( - actual: T, - expected: T, - msg?: string, -): void; -export function assertNotStrictEquals( - actual: unknown, - expected: unknown, - msg?: string, -): void { - if (actual !== expected) { - return; - } - - throw new AssertionError( - msg ?? `Expected "actual" to be strictly unequal to: ${_format(actual)}\n`, - ); -} - -/** - * Make an assertion that actual is not null or undefined. If not - * then thrown. - */ -export function assertExists( - actual: unknown, - msg?: string, -): void { - if (actual === undefined || actual === null) { - if (!msg) { - msg = - `actual: "${actual}" expected to match anything but null or undefined`; - } - throw new AssertionError(msg); - } -} - -/** - * Make an assertion that actual includes expected. If not - * then thrown. - */ -export function assertStringIncludes( - actual: string, - expected: string, - msg?: string, -): void { - if (!actual.includes(expected)) { - if (!msg) { - msg = `actual: "${actual}" expected to contain: "${expected}"`; - } - throw new AssertionError(msg); - } -} - -/** - * Make an assertion that `actual` includes the `expected` values. - * If not then an error will be thrown. - * - * Type parameter can be specified to ensure values under comparison have the same type. - * For example: - *```ts - *assertArrayIncludes<number>([1, 2], [2]) - *``` - */ -export function assertArrayIncludes( - actual: ArrayLike<unknown>, - expected: ArrayLike<unknown>, - msg?: string, -): void; -export function assertArrayIncludes<T>( - actual: ArrayLike<T>, - expected: ArrayLike<T>, - msg?: string, -): void; -export function assertArrayIncludes( - actual: ArrayLike<unknown>, - expected: ArrayLike<unknown>, - msg?: string, -): void { - const missing: unknown[] = []; - for (let i = 0; i < expected.length; i++) { - let found = false; - for (let j = 0; j < actual.length; j++) { - if (equal(expected[i], actual[j])) { - found = true; - break; - } - } - if (!found) { - missing.push(expected[i]); - } - } - if (missing.length === 0) { - return; - } - if (!msg) { - msg = `actual: "${_format(actual)}" expected to include: "${ - _format(expected) - }"\nmissing: ${_format(missing)}`; - } - throw new AssertionError(msg); -} - -/** - * Make an assertion that `actual` match RegExp `expected`. If not - * then thrown - */ -export function assertMatch( - actual: string, - expected: RegExp, - msg?: string, -): void { - if (!expected.test(actual)) { - if (!msg) { - msg = `actual: "${actual}" expected to match: "${expected}"`; - } - throw new AssertionError(msg); - } -} - -/** - * Make an assertion that `actual` not match RegExp `expected`. If match - * then thrown - */ -export function assertNotMatch( - actual: string, - expected: RegExp, - msg?: string, -): void { - if (expected.test(actual)) { - if (!msg) { - msg = `actual: "${actual}" expected to not match: "${expected}"`; - } - throw new AssertionError(msg); - } -} - -/** - * Make an assertion that `actual` object is a subset of `expected` object, deeply. - * If not, then throw. - */ -export function assertObjectMatch( - actual: Record<PropertyKey, unknown>, - expected: Record<PropertyKey, unknown>, -): void { - type loose = Record<PropertyKey, unknown>; - const seen = new WeakMap(); - return assertEquals( - (function filter(a: loose, b: loose): loose { - // Prevent infinite loop with circular references with same filter - if ((seen.has(a)) && (seen.get(a) === b)) { - return a; - } - seen.set(a, b); - // Filter keys and symbols which are present in both actual and expected - const filtered = {} as loose; - const entries = [ - ...Object.getOwnPropertyNames(a), - ...Object.getOwnPropertySymbols(a), - ] - .filter((key) => key in b) - .map((key) => [key, a[key as string]]) as Array<[string, unknown]>; - // Build filtered object and filter recursively on nested objects references - for (const [key, value] of entries) { - if (typeof value === "object") { - const subset = (b as loose)[key]; - if ((typeof subset === "object") && (subset)) { - filtered[key] = filter(value as loose, subset as loose); - continue; - } - } - filtered[key] = value; - } - return filtered; - })(actual, expected), - expected, - ); -} - -/** - * Forcefully throws a failed assertion - */ -export function fail(msg?: string): void { - // eslint-disable-next-line @typescript-eslint/no-use-before-define - assert(false, `Failed assertion${msg ? `: ${msg}` : "."}`); -} - -/** - * Executes a function, expecting it to throw. If it does not, then it - * throws. An error class and a string that should be included in the - * error message can also be asserted. - */ -export function assertThrows<T = void>( - fn: () => T, - ErrorClass?: Constructor, - msgIncludes = "", - msg?: string, -): Error { - let doesThrow = false; - let error = null; - try { - fn(); - } catch (e) { - if (e instanceof Error === false) { - throw new AssertionError("A non-Error object was thrown."); - } - if (ErrorClass && !(e instanceof ErrorClass)) { - msg = - `Expected error to be instance of "${ErrorClass.name}", but was "${e.constructor.name}"${ - msg ? `: ${msg}` : "." - }`; - throw new AssertionError(msg); - } - if ( - msgIncludes && - !stripColor(e.message).includes(stripColor(msgIncludes)) - ) { - msg = - `Expected error message to include "${msgIncludes}", but got "${e.message}"${ - msg ? `: ${msg}` : "." - }`; - throw new AssertionError(msg); - } - doesThrow = true; - error = e; - } - if (!doesThrow) { - msg = `Expected function to throw${msg ? `: ${msg}` : "."}`; - throw new AssertionError(msg); - } - return error; -} - -/** - * Executes a function which returns a promise, expecting it to throw or reject. - * If it does not, then it throws. An error class and a string that should be - * included in the error message can also be asserted. - */ -export async function assertThrowsAsync<T = void>( - fn: () => Promise<T>, - ErrorClass?: Constructor, - msgIncludes = "", - msg?: string, -): Promise<Error> { - let doesThrow = false; - let error = null; - try { - await fn(); - } catch (e) { - if (e instanceof Error === false) { - throw new AssertionError("A non-Error object was thrown or rejected."); - } - if (ErrorClass && !(e instanceof ErrorClass)) { - msg = - `Expected error to be instance of "${ErrorClass.name}", but got "${e.name}"${ - msg ? `: ${msg}` : "." - }`; - throw new AssertionError(msg); - } - if ( - msgIncludes && - !stripColor(e.message).includes(stripColor(msgIncludes)) - ) { - msg = - `Expected error message to include "${msgIncludes}", but got "${e.message}"${ - msg ? `: ${msg}` : "." - }`; - throw new AssertionError(msg); - } - doesThrow = true; - error = e; - } - if (!doesThrow) { - msg = `Expected function to throw${msg ? `: ${msg}` : "."}`; - throw new AssertionError(msg); - } - return error; -} - -/** Use this to stub out methods that will throw when invoked. */ -export function unimplemented(msg?: string): never { - throw new AssertionError(msg || "unimplemented"); -} - -/** Use this to assert unreachable code. */ -export function unreachable(): never { - throw new AssertionError("unreachable"); -} diff --git a/std/testing/asserts_test.ts b/std/testing/asserts_test.ts deleted file mode 100644 index 14ffd2962..000000000 --- a/std/testing/asserts_test.ts +++ /dev/null @@ -1,927 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { - _format, - assert, - assertArrayIncludes, - assertEquals, - assertExists, - AssertionError, - assertMatch, - assertNotEquals, - assertNotMatch, - assertNotStrictEquals, - assertObjectMatch, - assertStrictEquals, - assertStringIncludes, - assertThrows, - assertThrowsAsync, - equal, - fail, - unimplemented, - unreachable, -} from "./asserts.ts"; -import { bold, gray, green, red, stripColor, yellow } from "../fmt/colors.ts"; - -Deno.test("testingEqual", function (): void { - assert(equal("world", "world")); - assert(!equal("hello", "world")); - assert(equal(5, 5)); - assert(!equal(5, 6)); - assert(equal(NaN, NaN)); - assert(equal({ hello: "world" }, { hello: "world" })); - assert(!equal({ world: "hello" }, { hello: "world" })); - assert( - equal( - { hello: "world", hi: { there: "everyone" } }, - { hello: "world", hi: { there: "everyone" } }, - ), - ); - assert( - !equal( - { hello: "world", hi: { there: "everyone" } }, - { hello: "world", hi: { there: "everyone else" } }, - ), - ); - assert(equal(/deno/, /deno/)); - assert(!equal(/deno/, /node/)); - assert(equal(new Date(2019, 0, 3), new Date(2019, 0, 3))); - assert(!equal(new Date(2019, 0, 3), new Date(2019, 1, 3))); - assert( - !equal( - new Date(2019, 0, 3, 4, 20, 1, 10), - new Date(2019, 0, 3, 4, 20, 1, 20), - ), - ); - assert(equal(new Date("Invalid"), new Date("Invalid"))); - assert(!equal(new Date("Invalid"), new Date(2019, 0, 3))); - assert(!equal(new Date("Invalid"), new Date(2019, 0, 3, 4, 20, 1, 10))); - assert(equal(new Set([1]), new Set([1]))); - assert(!equal(new Set([1]), new Set([2]))); - assert(equal(new Set([1, 2, 3]), new Set([3, 2, 1]))); - assert(equal(new Set([1, new Set([2, 3])]), new Set([new Set([3, 2]), 1]))); - assert(!equal(new Set([1, 2]), new Set([3, 2, 1]))); - assert(!equal(new Set([1, 2, 3]), new Set([4, 5, 6]))); - assert(equal(new Set("denosaurus"), new Set("denosaurussss"))); - assert(equal(new Map(), new Map())); - assert( - equal( - new Map([ - ["foo", "bar"], - ["baz", "baz"], - ]), - new Map([ - ["foo", "bar"], - ["baz", "baz"], - ]), - ), - ); - assert( - equal( - new Map([["foo", new Map([["bar", "baz"]])]]), - new Map([["foo", new Map([["bar", "baz"]])]]), - ), - ); - assert( - equal( - new Map([["foo", { bar: "baz" }]]), - new Map([["foo", { bar: "baz" }]]), - ), - ); - assert( - equal( - new Map([ - ["foo", "bar"], - ["baz", "qux"], - ]), - new Map([ - ["baz", "qux"], - ["foo", "bar"], - ]), - ), - ); - assert(equal(new Map([["foo", ["bar"]]]), new Map([["foo", ["bar"]]]))); - assert(!equal(new Map([["foo", "bar"]]), new Map([["bar", "baz"]]))); - assert( - !equal( - new Map([["foo", "bar"]]), - new Map([ - ["foo", "bar"], - ["bar", "baz"], - ]), - ), - ); - assert( - !equal( - new Map([["foo", new Map([["bar", "baz"]])]]), - new Map([["foo", new Map([["bar", "qux"]])]]), - ), - ); - assert(equal(new Map([[{ x: 1 }, true]]), new Map([[{ x: 1 }, true]]))); - assert(!equal(new Map([[{ x: 1 }, true]]), new Map([[{ x: 1 }, false]]))); - assert(!equal(new Map([[{ x: 1 }, true]]), new Map([[{ x: 2 }, true]]))); - assert(equal([1, 2, 3], [1, 2, 3])); - assert(equal([1, [2, 3]], [1, [2, 3]])); - assert(!equal([1, 2, 3, 4], [1, 2, 3])); - assert(!equal([1, 2, 3, 4], [1, 2, 3])); - assert(!equal([1, 2, 3, 4], [1, 4, 2, 3])); - assert(equal(new Uint8Array([1, 2, 3, 4]), new Uint8Array([1, 2, 3, 4]))); - assert(!equal(new Uint8Array([1, 2, 3, 4]), new Uint8Array([2, 1, 4, 3]))); - assert( - equal(new URL("https://example.test"), new URL("https://example.test")), - ); - assert( - !equal( - new URL("https://example.test"), - new URL("https://example.test/with-path"), - ), - ); -}); - -Deno.test("testingNotEquals", function (): void { - const a = { foo: "bar" }; - const b = { bar: "foo" }; - assertNotEquals(a, b); - assertNotEquals("Denosaurus", "Tyrannosaurus"); - assertNotEquals( - new Date(2019, 0, 3, 4, 20, 1, 10), - new Date(2019, 0, 3, 4, 20, 1, 20), - ); - assertNotEquals( - new Date("invalid"), - new Date(2019, 0, 3, 4, 20, 1, 20), - ); - let didThrow; - try { - assertNotEquals("Raptor", "Raptor"); - didThrow = false; - } catch (e) { - assert(e instanceof AssertionError); - didThrow = true; - } - assertEquals(didThrow, true); -}); - -Deno.test("testingAssertExists", function (): void { - assertExists("Denosaurus"); - assertExists(false); - assertExists(0); - assertExists(""); - assertExists(-0); - assertExists(0); - assertExists(NaN); - let didThrow; - try { - assertExists(undefined); - didThrow = false; - } catch (e) { - assert(e instanceof AssertionError); - didThrow = true; - } - assertEquals(didThrow, true); - didThrow = false; - try { - assertExists(null); - didThrow = false; - } catch (e) { - assert(e instanceof AssertionError); - didThrow = true; - } - assertEquals(didThrow, true); -}); - -Deno.test("testingAssertStringContains", function (): void { - assertStringIncludes("Denosaurus", "saur"); - assertStringIncludes("Denosaurus", "Deno"); - assertStringIncludes("Denosaurus", "rus"); - let didThrow; - try { - assertStringIncludes("Denosaurus", "Raptor"); - didThrow = false; - } catch (e) { - assert(e instanceof AssertionError); - didThrow = true; - } - assertEquals(didThrow, true); -}); - -Deno.test("testingArrayContains", function (): void { - const fixture = ["deno", "iz", "luv"]; - const fixtureObject = [{ deno: "luv" }, { deno: "Js" }]; - assertArrayIncludes(fixture, ["deno"]); - assertArrayIncludes(fixtureObject, [{ deno: "luv" }]); - assertArrayIncludes( - Uint8Array.from([1, 2, 3, 4]), - Uint8Array.from([1, 2, 3]), - ); - assertThrows( - (): void => assertArrayIncludes(fixtureObject, [{ deno: "node" }]), - AssertionError, - `actual: "[ - { - deno: "luv", - }, - { - deno: "Js", - }, -]" expected to include: "[ - { - deno: "node", - }, -]" -missing: [ - { - deno: "node", - }, -]`, - ); -}); - -Deno.test("testingAssertStringContainsThrow", function (): void { - let didThrow = false; - try { - assertStringIncludes("Denosaurus from Jurassic", "Raptor"); - } catch (e) { - assert( - e.message === - `actual: "Denosaurus from Jurassic" expected to contain: "Raptor"`, - ); - assert(e instanceof AssertionError); - didThrow = true; - } - assert(didThrow); -}); - -Deno.test("testingAssertStringMatching", function (): void { - assertMatch("foobar@deno.com", RegExp(/[a-zA-Z]+@[a-zA-Z]+.com/)); -}); - -Deno.test("testingAssertStringMatchingThrows", function (): void { - let didThrow = false; - try { - assertMatch("Denosaurus from Jurassic", RegExp(/Raptor/)); - } catch (e) { - assert( - e.message === - `actual: "Denosaurus from Jurassic" expected to match: "/Raptor/"`, - ); - assert(e instanceof AssertionError); - didThrow = true; - } - assert(didThrow); -}); - -Deno.test("testingAssertStringNotMatching", function (): void { - assertNotMatch("foobar.deno.com", RegExp(/[a-zA-Z]+@[a-zA-Z]+.com/)); -}); - -Deno.test("testingAssertStringNotMatchingThrows", function (): void { - let didThrow = false; - try { - assertNotMatch("Denosaurus from Jurassic", RegExp(/from/)); - } catch (e) { - assert( - e.message === - `actual: "Denosaurus from Jurassic" expected to not match: "/from/"`, - ); - assert(e instanceof AssertionError); - didThrow = true; - } - assert(didThrow); -}); - -Deno.test("testingAssertObjectMatching", function (): void { - const sym = Symbol("foo"); - const a = { foo: true, bar: false }; - const b = { ...a, baz: a }; - const c = { ...b, qux: b }; - const d = { corge: c, grault: c }; - const e = { foo: true } as { [key: string]: unknown }; - e.bar = e; - const f = { [sym]: true, bar: false }; - // Simple subset - assertObjectMatch(a, { - foo: true, - }); - // Subset with another subset - assertObjectMatch(b, { - foo: true, - baz: { bar: false }, - }); - // Subset with multiple subsets - assertObjectMatch(c, { - foo: true, - baz: { bar: false }, - qux: { - baz: { foo: true }, - }, - }); - // Subset with same object reference as subset - assertObjectMatch(d, { - corge: { - foo: true, - qux: { bar: false }, - }, - grault: { - bar: false, - qux: { foo: true }, - }, - }); - // Subset with circular reference - assertObjectMatch(e, { - foo: true, - bar: { - bar: { - bar: { - foo: true, - }, - }, - }, - }); - // Subset with same symbol - assertObjectMatch(f, { - [sym]: true, - }); - // Missing key - { - let didThrow; - try { - assertObjectMatch({ - foo: true, - }, { - foo: true, - bar: false, - }); - didThrow = false; - } catch (e) { - assert(e instanceof AssertionError); - didThrow = true; - } - assertEquals(didThrow, true); - } - // Simple subset - { - let didThrow; - try { - assertObjectMatch(a, { - foo: false, - }); - didThrow = false; - } catch (e) { - assert(e instanceof AssertionError); - didThrow = true; - } - assertEquals(didThrow, true); - } - // Subset with another subset - { - let didThrow; - try { - assertObjectMatch(b, { - foo: true, - baz: { bar: true }, - }); - didThrow = false; - } catch (e) { - assert(e instanceof AssertionError); - didThrow = true; - } - assertEquals(didThrow, true); - } - // Subset with multiple subsets - { - let didThrow; - try { - assertObjectMatch(c, { - foo: true, - baz: { bar: false }, - qux: { - baz: { foo: false }, - }, - }); - didThrow = false; - } catch (e) { - assert(e instanceof AssertionError); - didThrow = true; - } - assertEquals(didThrow, true); - } - // Subset with same object reference as subset - { - let didThrow; - try { - assertObjectMatch(d, { - corge: { - foo: true, - qux: { bar: true }, - }, - grault: { - bar: false, - qux: { foo: false }, - }, - }); - didThrow = false; - } catch (e) { - assert(e instanceof AssertionError); - didThrow = true; - } - assertEquals(didThrow, true); - } - // Subset with circular reference - { - let didThrow; - try { - assertObjectMatch(e, { - foo: true, - bar: { - bar: { - bar: { - foo: false, - }, - }, - }, - }); - didThrow = false; - } catch (e) { - assert(e instanceof AssertionError); - didThrow = true; - } - assertEquals(didThrow, true); - } - // Subset with symbol key but with string key subset - { - let didThrow; - try { - assertObjectMatch(f, { - foo: true, - }); - didThrow = false; - } catch (e) { - assert(e instanceof AssertionError); - didThrow = true; - } - assertEquals(didThrow, true); - } -}); - -Deno.test("testingAssertsUnimplemented", function (): void { - let didThrow = false; - try { - unimplemented(); - } catch (e) { - assert(e.message === "unimplemented"); - assert(e instanceof AssertionError); - didThrow = true; - } - assert(didThrow); -}); - -Deno.test("testingAssertsUnreachable", function (): void { - let didThrow = false; - try { - unreachable(); - } catch (e) { - assert(e.message === "unreachable"); - assert(e instanceof AssertionError); - didThrow = true; - } - assert(didThrow); -}); - -Deno.test("testingAssertFail", function (): void { - assertThrows(fail, AssertionError, "Failed assertion."); - assertThrows( - (): void => { - fail("foo"); - }, - AssertionError, - "Failed assertion: foo", - ); -}); - -Deno.test("testingAssertFailWithWrongErrorClass", function (): void { - assertThrows( - (): void => { - //This next assertThrows will throw an AssertionError due to the wrong - //expected error class - assertThrows( - (): void => { - fail("foo"); - }, - TypeError, - "Failed assertion: foo", - ); - }, - AssertionError, - `Expected error to be instance of "TypeError", but was "AssertionError"`, - ); -}); - -Deno.test("testingAssertThrowsWithReturnType", () => { - assertThrows(() => { - throw new Error(); - }); -}); - -Deno.test("testingAssertThrowsAsyncWithReturnType", () => { - assertThrowsAsync(() => { - throw new Error(); - }); -}); - -const createHeader = (): string[] => [ - "", - "", - ` ${gray(bold("[Diff]"))} ${red(bold("Actual"))} / ${ - green( - bold("Expected"), - ) - }`, - "", - "", -]; - -const added: (s: string) => string = (s: string): string => - green(bold(stripColor(s))); -const removed: (s: string) => string = (s: string): string => - red(bold(stripColor(s))); - -Deno.test({ - name: "pass case", - fn(): void { - assertEquals({ a: 10 }, { a: 10 }); - assertEquals(true, true); - assertEquals(10, 10); - assertEquals("abc", "abc"); - assertEquals({ a: 10, b: { c: "1" } }, { a: 10, b: { c: "1" } }); - assertEquals(new Date("invalid"), new Date("invalid")); - }, -}); - -Deno.test({ - name: "failed with number", - fn(): void { - assertThrows( - (): void => assertEquals(1, 2), - AssertionError, - [ - "Values are not equal:", - ...createHeader(), - removed(`- ${yellow("1")}`), - added(`+ ${yellow("2")}`), - "", - ].join("\n"), - ); - }, -}); - -Deno.test({ - name: "failed with number vs string", - fn(): void { - assertThrows( - (): void => assertEquals(1, "1"), - AssertionError, - [ - "Values are not equal:", - ...createHeader(), - removed(`- ${yellow("1")}`), - added(`+ "1"`), - ].join("\n"), - ); - }, -}); - -Deno.test({ - name: "failed with array", - fn(): void { - assertThrows( - (): void => assertEquals([1, "2", 3], ["1", "2", 3]), - AssertionError, - ` - [ -- 1, -+ "1", - "2", - 3, - ]`, - ); - }, -}); - -Deno.test({ - name: "failed with object", - fn(): void { - assertThrows( - (): void => assertEquals({ a: 1, b: "2", c: 3 }, { a: 1, b: 2, c: [3] }), - AssertionError, - ` - { - a: 1, -+ b: 2, -+ c: [ -+ 3, -+ ], -- b: "2", -- c: 3, - }`, - ); - }, -}); - -Deno.test({ - name: "failed with date", - fn(): void { - assertThrows( - (): void => - assertEquals( - new Date(2019, 0, 3, 4, 20, 1, 10), - new Date(2019, 0, 3, 4, 20, 1, 20), - ), - AssertionError, - [ - "Values are not equal:", - ...createHeader(), - removed(`- ${new Date(2019, 0, 3, 4, 20, 1, 10).toISOString()}`), - added(`+ ${new Date(2019, 0, 3, 4, 20, 1, 20).toISOString()}`), - "", - ].join("\n"), - ); - assertThrows( - (): void => - assertEquals( - new Date("invalid"), - new Date(2019, 0, 3, 4, 20, 1, 20), - ), - AssertionError, - [ - "Values are not equal:", - ...createHeader(), - removed(`- ${new Date("invalid")}`), - added(`+ ${new Date(2019, 0, 3, 4, 20, 1, 20).toISOString()}`), - "", - ].join("\n"), - ); - }, -}); - -Deno.test({ - name: "strict pass case", - fn(): void { - assertStrictEquals(true, true); - assertStrictEquals(10, 10); - assertStrictEquals("abc", "abc"); - - const xs = [1, false, "foo"]; - const ys = xs; - assertStrictEquals(xs, ys); - - const x = { a: 1 }; - const y = x; - assertStrictEquals(x, y); - }, -}); - -Deno.test({ - name: "strict failed with structure diff", - fn(): void { - assertThrows( - (): void => assertStrictEquals({ a: 1, b: 2 }, { a: 1, c: [3] }), - AssertionError, - ` - { - a: 1, -+ c: [ -+ 3, -+ ], -- b: 2, - }`, - ); - }, -}); - -Deno.test({ - name: "strict failed with reference diff", - fn(): void { - assertThrows( - (): void => assertStrictEquals({ a: 1, b: 2 }, { a: 1, b: 2 }), - AssertionError, - `Values have the same structure but are not reference-equal: - - { - a: 1, - b: 2, - }`, - ); - }, -}); - -Deno.test({ - name: "strictly unequal pass case", - fn(): void { - assertNotStrictEquals(true, false); - assertNotStrictEquals(10, 11); - assertNotStrictEquals("abc", "xyz"); - assertNotStrictEquals(1, "1"); - - const xs = [1, false, "foo"]; - const ys = [1, true, "bar"]; - assertNotStrictEquals(xs, ys); - - const x = { a: 1 }; - const y = { a: 2 }; - assertNotStrictEquals(x, y); - }, -}); - -Deno.test({ - name: "strictly unequal fail case", - fn(): void { - assertThrows(() => assertNotStrictEquals(1, 1), AssertionError); - }, -}); - -Deno.test({ - name: "assert* functions with specified type parameter", - fn(): void { - assertEquals<string>("hello", "hello"); - assertNotEquals<number>(1, 2); - assertArrayIncludes<boolean>([true, false], [true]); - const value = { x: 1 }; - assertStrictEquals<typeof value>(value, value); - // deno-lint-ignore ban-types - assertNotStrictEquals<object>(value, { x: 1 }); - }, -}); - -Deno.test("Assert Throws Non-Error Fail", () => { - assertThrows( - () => { - assertThrows( - () => { - throw "Panic!"; - }, - String, - "Panic!", - ); - }, - AssertionError, - "A non-Error object was thrown.", - ); - - assertThrows( - () => { - assertThrows(() => { - throw null; - }); - }, - AssertionError, - "A non-Error object was thrown.", - ); - - assertThrows( - () => { - assertThrows(() => { - throw undefined; - }); - }, - AssertionError, - "A non-Error object was thrown.", - ); -}); - -Deno.test("Assert Throws Async Non-Error Fail", () => { - assertThrowsAsync( - () => { - return assertThrowsAsync( - () => { - return Promise.reject("Panic!"); - }, - String, - "Panic!", - ); - }, - AssertionError, - "A non-Error object was thrown or rejected.", - ); - - assertThrowsAsync( - () => { - return assertThrowsAsync(() => { - return Promise.reject(null); - }); - }, - AssertionError, - "A non-Error object was thrown or rejected.", - ); - - assertThrowsAsync( - () => { - return assertThrowsAsync(() => { - return Promise.reject(undefined); - }); - }, - AssertionError, - "A non-Error object was thrown or rejected.", - ); - - assertThrowsAsync( - () => { - return assertThrowsAsync(() => { - throw undefined; - }); - }, - AssertionError, - "A non-Error object was thrown or rejected.", - ); -}); - -Deno.test("assertEquals diff for differently ordered objects", () => { - assertThrows( - () => { - assertEquals( - { - aaaaaaaaaaaaaaaaaaaaaaaa: 0, - bbbbbbbbbbbbbbbbbbbbbbbb: 0, - ccccccccccccccccccccccc: 0, - }, - { - ccccccccccccccccccccccc: 1, - aaaaaaaaaaaaaaaaaaaaaaaa: 0, - bbbbbbbbbbbbbbbbbbbbbbbb: 0, - }, - ); - }, - AssertionError, - ` - { - aaaaaaaaaaaaaaaaaaaaaaaa: 0, - bbbbbbbbbbbbbbbbbbbbbbbb: 0, -- ccccccccccccccccccccccc: 0, -+ ccccccccccccccccccccccc: 1, - }`, - ); -}); - -// Check that the diff formatter overrides some default behaviours of -// `Deno.inspect()` which are problematic for diffing. -Deno.test("assert diff formatting", () => { - // Wraps objects into multiple lines even when they are small. Prints trailing - // commas. - assertEquals( - stripColor(_format({ a: 1, b: 2 })), - `{ - a: 1, - b: 2, -}`, - ); - - // Same for nested small objects. - assertEquals( - stripColor(_format([{ x: { a: 1, b: 2 }, y: ["a", "b"] }])), - `[ - { - x: { - a: 1, - b: 2, - }, - y: [ - "a", - "b", - ], - }, -]`, - ); - - // Grouping is disabled. - assertEquals( - stripColor(_format(["i", "i", "i", "i", "i", "i", "i"])), - `[ - "i", - "i", - "i", - "i", - "i", - "i", - "i", -]`, - ); -}); - -Deno.test("Assert Throws Parent Error", () => { - assertThrows( - () => { - throw new AssertionError("Fail!"); - }, - Error, - "Fail!", - ); -}); - -Deno.test("Assert Throws Async Parent Error", () => { - assertThrowsAsync( - () => { - throw new AssertionError("Fail!"); - }, - Error, - "Fail!", - ); -}); diff --git a/std/testing/bench.ts b/std/testing/bench.ts deleted file mode 100644 index 159a70cf7..000000000 --- a/std/testing/bench.ts +++ /dev/null @@ -1,363 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assert } from "../_util/assert.ts"; -import { deepAssign } from "../_util/deep_assign.ts"; - -interface BenchmarkClock { - start: number; - stop: number; - for?: string; -} - -/** Provides methods for starting and stopping a benchmark clock. */ -export interface BenchmarkTimer { - start: () => void; - stop: () => void; -} - -/** Defines a benchmark through a named function. */ -export interface BenchmarkFunction { - (b: BenchmarkTimer): void | Promise<void>; - name: string; -} - -/** Defines a benchmark definition with configurable runs. */ -export interface BenchmarkDefinition { - func: BenchmarkFunction; - name: string; - /** Defines how many times the provided `func` should be benchmarked in succession */ - runs?: number; -} - -/** Defines runBenchmark's run constraints by matching benchmark names. */ -export interface BenchmarkRunOptions { - /** Only benchmarks which name match this regexp will be run*/ - only?: RegExp; - /** Benchmarks which name match this regexp will be skipped */ - skip?: RegExp; - /** Setting it to true prevents default benchmarking progress logs to the commandline*/ - silent?: boolean; -} - -/** Defines clearBenchmark's constraints by matching benchmark names. */ -export interface BenchmarkClearOptions { - /** Only benchmarks which name match this regexp will be removed */ - only?: RegExp; - /** Benchmarks which name match this regexp will be kept */ - skip?: RegExp; -} - -/** Defines the result of a single benchmark */ -export interface BenchmarkResult { - /** The name of the benchmark */ - name: string; - /** The total time it took to run a given bechmark */ - totalMs: number; - /** Times the benchmark was run in succession. */ - runsCount: number; - /** The average time of running the benchmark in milliseconds. */ - measuredRunsAvgMs: number; - /** The individual measurements in milliseconds it took to run the benchmark.*/ - measuredRunsMs: number[]; -} - -/** Defines the result of a `runBenchmarks` call */ -export interface BenchmarkRunResult { - /** How many benchmark were ignored by the provided `only` and `skip` */ - filtered: number; - /** The individual results for each benchmark that was run */ - results: BenchmarkResult[]; -} - -/** Defines the current progress during the run of `runBenchmarks` */ -export interface BenchmarkRunProgress extends BenchmarkRunResult { - /** List of the queued benchmarks to run with their name and their run count */ - queued?: Array<{ name: string; runsCount: number }>; - /** The currently running benchmark with its name, run count and the already finished measurements in milliseconds */ - running?: { name: string; runsCount: number; measuredRunsMs: number[] }; - /** Indicates in which state benchmarking currently is */ - state?: ProgressState; -} - -/** Defines the states `BenchmarkRunProgress` can be in */ -export enum ProgressState { - BenchmarkingStart = "benchmarking_start", - BenchStart = "bench_start", - BenchPartialResult = "bench_partial_result", - BenchResult = "bench_result", - BenchmarkingEnd = "benchmarking_end", -} - -export class BenchmarkRunError extends Error { - benchmarkName?: string; - constructor(msg: string, benchmarkName?: string) { - super(msg); - this.name = "BenchmarkRunError"; - this.benchmarkName = benchmarkName; - } -} - -function red(text: string): string { - return Deno.noColor ? text : `\x1b[31m${text}\x1b[0m`; -} - -function blue(text: string): string { - return Deno.noColor ? text : `\x1b[34m${text}\x1b[0m`; -} - -function verifyOr1Run(runs?: number): number { - return runs && runs >= 1 && runs !== Infinity ? Math.floor(runs) : 1; -} - -function assertTiming(clock: BenchmarkClock): void { - // NaN indicates that a benchmark has not been timed properly - if (!clock.stop) { - throw new BenchmarkRunError( - `Running benchmarks FAILED during benchmark named [${clock.for}]. The benchmark timer's stop method must be called`, - clock.for, - ); - } else if (!clock.start) { - throw new BenchmarkRunError( - `Running benchmarks FAILED during benchmark named [${clock.for}]. The benchmark timer's start method must be called`, - clock.for, - ); - } else if (clock.start > clock.stop) { - throw new BenchmarkRunError( - `Running benchmarks FAILED during benchmark named [${clock.for}]. The benchmark timer's start method must be called before its stop method`, - clock.for, - ); - } -} - -function createBenchmarkTimer(clock: BenchmarkClock): BenchmarkTimer { - return { - start(): void { - clock.start = performance.now(); - }, - stop(): void { - if (isNaN(clock.start)) { - throw new BenchmarkRunError( - `Running benchmarks FAILED during benchmark named [${clock.for}]. The benchmark timer's start method must be called before its stop method`, - clock.for, - ); - } - clock.stop = performance.now(); - }, - }; -} - -const candidates: BenchmarkDefinition[] = []; - -/** Registers a benchmark as a candidate for the runBenchmarks executor. */ -export function bench( - benchmark: BenchmarkDefinition | BenchmarkFunction, -): void { - if (!benchmark.name) { - throw new Error("The benchmark function must not be anonymous"); - } - if (typeof benchmark === "function") { - candidates.push({ name: benchmark.name, runs: 1, func: benchmark }); - } else { - candidates.push({ - name: benchmark.name, - runs: verifyOr1Run(benchmark.runs), - func: benchmark.func, - }); - } -} - -/** Clears benchmark candidates which name matches `only` and doesn't match `skip`. - * Removes all candidates if options were not provided */ -export function clearBenchmarks({ - only = /[^\s]/, - skip = /$^/, -}: BenchmarkClearOptions = {}): void { - const keep = candidates.filter( - ({ name }): boolean => !only.test(name) || skip.test(name), - ); - candidates.splice(0, candidates.length); - candidates.push(...keep); -} - -/** - * Runs all registered and non-skipped benchmarks serially. - * - * @param [progressCb] provides the possibility to get updates of the current progress during the run of the benchmarking - * @returns results of the benchmarking - */ -export async function runBenchmarks( - { only = /[^\s]/, skip = /^\s*$/, silent }: BenchmarkRunOptions = {}, - progressCb?: (progress: BenchmarkRunProgress) => void | Promise<void>, -): Promise<BenchmarkRunResult> { - // Filtering candidates by the "only" and "skip" constraint - const benchmarks: BenchmarkDefinition[] = candidates.filter( - ({ name }): boolean => only.test(name) && !skip.test(name), - ); - // Init main counters and error flag - const filtered = candidates.length - benchmarks.length; - let failError: Error | undefined = undefined; - // Setting up a shared benchmark clock and timer - const clock: BenchmarkClock = { start: NaN, stop: NaN }; - const b = createBenchmarkTimer(clock); - - // Init progress data - const progress: BenchmarkRunProgress = { - // bench.run is already ensured with verifyOr1Run on register - queued: benchmarks.map((bench) => ({ - name: bench.name, - runsCount: bench.runs!, - })), - results: [], - filtered, - state: ProgressState.BenchmarkingStart, - }; - - // Publish initial progress data - await publishProgress(progress, ProgressState.BenchmarkingStart, progressCb); - - if (!silent) { - console.log( - "running", - benchmarks.length, - `benchmark${benchmarks.length === 1 ? " ..." : "s ..."}`, - ); - } - - // Iterating given benchmark definitions (await-in-loop) - for (const { name, runs = 0, func } of benchmarks) { - if (!silent) { - // See https://github.com/denoland/deno/pull/1452 about groupCollapsed - console.groupCollapsed(`benchmark ${name} ... `); - } - - // Provide the benchmark name for clock assertions - clock.for = name; - - // Remove benchmark from queued - assert(progress.queued); - const queueIndex = progress.queued.findIndex( - (queued) => queued.name === name && queued.runsCount === runs, - ); - if (queueIndex != -1) { - progress.queued.splice(queueIndex, 1); - } - // Init the progress of the running benchmark - progress.running = { name, runsCount: runs, measuredRunsMs: [] }; - // Publish starting of a benchmark - await publishProgress(progress, ProgressState.BenchStart, progressCb); - - // Trying benchmark.func - let result = ""; - try { - // Averaging runs - let pendingRuns = runs; - let totalMs = 0; - - // Would be better 2 not run these serially - while (true) { - // b is a benchmark timer interfacing an unset (NaN) benchmark clock - await func(b); - // Making sure the benchmark was started/stopped properly - assertTiming(clock); - - // Calculate length of run - const measuredMs = clock.stop - clock.start; - - // Summing up - totalMs += measuredMs; - // Adding partial result - progress.running.measuredRunsMs.push(measuredMs); - // Publish partial benchmark results - await publishProgress( - progress, - ProgressState.BenchPartialResult, - progressCb, - ); - - // Resetting the benchmark clock - clock.start = clock.stop = NaN; - // Once all ran - if (!--pendingRuns) { - result = runs == 1 - ? `${totalMs}ms` - : `${runs} runs avg: ${totalMs / runs}ms`; - // Adding results - progress.results.push({ - name, - totalMs, - runsCount: runs, - measuredRunsAvgMs: totalMs / runs, - measuredRunsMs: progress.running.measuredRunsMs, - }); - // Clear currently running - delete progress.running; - // Publish results of the benchmark - await publishProgress( - progress, - ProgressState.BenchResult, - progressCb, - ); - break; - } - } - } catch (err) { - failError = err; - - if (!silent) { - console.groupEnd(); - console.error(red(err.stack)); - } - - break; - } - - if (!silent) { - // Reporting - console.log(blue(result)); - console.groupEnd(); - } - - // Resetting the benchmark clock - clock.start = clock.stop = NaN; - delete clock.for; - } - - // Indicate finished running - delete progress.queued; - // Publish final result in Cb too - await publishProgress(progress, ProgressState.BenchmarkingEnd, progressCb); - - if (!silent) { - // Closing results - console.log( - `benchmark result: ${failError ? red("FAIL") : blue("DONE")}. ` + - `${progress.results.length} measured; ${filtered} filtered`, - ); - } - - // Throw error if there was a failing benchmark - if (failError) { - throw failError; - } - - const benchmarkRunResult = { - filtered, - results: progress.results, - }; - - return benchmarkRunResult; -} - -async function publishProgress( - progress: BenchmarkRunProgress, - state: ProgressState, - progressCb?: (progress: BenchmarkRunProgress) => void | Promise<void>, -): Promise<void> { - progressCb && (await progressCb(cloneProgressWithState(progress, state))); -} - -function cloneProgressWithState( - progress: BenchmarkRunProgress, - state: ProgressState, -): BenchmarkRunProgress { - return deepAssign({}, progress, { state }) as BenchmarkRunProgress; -} diff --git a/std/testing/bench_example.ts b/std/testing/bench_example.ts deleted file mode 100644 index 5af277f1c..000000000 --- a/std/testing/bench_example.ts +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// https://deno.land/std/testing/bench.ts -import { bench, BenchmarkTimer, runBenchmarks } from "./bench.ts"; - -// Basic -bench(function forIncrementX1e9(b: BenchmarkTimer): void { - b.start(); - for (let i = 0; i < 1e9; i++); - b.stop(); -}); - -// Reporting average measured time for $runs runs of func -bench({ - name: "runs100ForIncrementX1e6", - runs: 100, - func(b): void { - b.start(); - for (let i = 0; i < 1e6; i++); - b.stop(); - }, -}); - -// Itsabug -bench(function throwing(b): void { - b.start(); - // Throws bc the timer's stop method is never called -}); - -// Bench control -if (import.meta.main) { - runBenchmarks({ skip: /throw/ }); -} diff --git a/std/testing/bench_test.ts b/std/testing/bench_test.ts deleted file mode 100644 index 6ce7796ca..000000000 --- a/std/testing/bench_test.ts +++ /dev/null @@ -1,379 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { - bench, - BenchmarkRunError, - BenchmarkRunProgress, - clearBenchmarks, - ProgressState, - runBenchmarks, -} from "./bench.ts"; -import { - assert, - assertEquals, - assertThrows, - assertThrowsAsync, -} from "./asserts.ts"; - -Deno.test({ - name: "benching", - - fn: async function (): Promise<void> { - bench(function forIncrementX1e3(b): void { - b.start(); - for (let i = 0; i < 1e3; i++); - b.stop(); - }); - - bench(function forDecrementX1e3(b): void { - b.start(); - for (let i = 1e3; i > 0; i--); - b.stop(); - }); - - bench(async function forAwaitFetchDenolandX10(b): Promise<void> { - b.start(); - for (let i = 0; i < 10; i++) { - const r = await fetch("https://deno.land/"); - await r.text(); - } - b.stop(); - }); - - bench(async function promiseAllFetchDenolandX10(b): Promise<void> { - const urls = new Array(10).fill("https://deno.land/"); - b.start(); - await Promise.all( - urls.map( - async (denoland: string): Promise<void> => { - const r = await fetch(denoland); - await r.text(); - }, - ), - ); - b.stop(); - }); - - bench({ - name: "runs100ForIncrementX1e6", - runs: 100, - func(b): void { - b.start(); - for (let i = 0; i < 1e6; i++); - b.stop(); - }, - }); - - bench(function throwing(b): void { - b.start(); - // Throws bc the timer's stop method is never called - }); - - const benchResult = await runBenchmarks({ skip: /throw/ }); - - assertEquals(benchResult.filtered, 1); - assertEquals(benchResult.results.length, 5); - - const resultWithSingleRunsFiltered = benchResult.results.filter( - ({ name }) => name === "forDecrementX1e3", - ); - assertEquals(resultWithSingleRunsFiltered.length, 1); - - const resultWithSingleRuns = resultWithSingleRunsFiltered[0]; - assert(!!resultWithSingleRuns.runsCount); - assert(!!resultWithSingleRuns.measuredRunsAvgMs); - assert(!!resultWithSingleRuns.measuredRunsMs); - assertEquals(resultWithSingleRuns.runsCount, 1); - assertEquals(resultWithSingleRuns.measuredRunsMs.length, 1); - - const resultWithMultipleRunsFiltered = benchResult.results.filter( - ({ name }) => name === "runs100ForIncrementX1e6", - ); - assertEquals(resultWithMultipleRunsFiltered.length, 1); - - const resultWithMultipleRuns = resultWithMultipleRunsFiltered[0]; - assert(!!resultWithMultipleRuns.runsCount); - assert(!!resultWithMultipleRuns.measuredRunsAvgMs); - assert(!!resultWithMultipleRuns.measuredRunsMs); - assertEquals(resultWithMultipleRuns.runsCount, 100); - assertEquals(resultWithMultipleRuns.measuredRunsMs.length, 100); - - clearBenchmarks(); - }, -}); - -Deno.test({ - name: "Bench without name should throw", - fn() { - assertThrows( - (): void => { - bench(() => {}); - }, - Error, - "The benchmark function must not be anonymous", - ); - }, -}); - -Deno.test({ - name: "Bench without stop should throw", - fn: async function (): Promise<void> { - await assertThrowsAsync( - async (): Promise<void> => { - bench(function benchWithoutStop(b): void { - b.start(); - // Throws bc the timer's stop method is never called - }); - await runBenchmarks({ only: /benchWithoutStop/, silent: true }); - }, - BenchmarkRunError, - "The benchmark timer's stop method must be called", - ); - }, -}); - -Deno.test({ - name: "Bench without start should throw", - fn: async function (): Promise<void> { - await assertThrowsAsync( - async (): Promise<void> => { - bench(function benchWithoutStart(b): void { - b.stop(); - // Throws bc the timer's start method is never called - }); - await runBenchmarks({ only: /benchWithoutStart/, silent: true }); - }, - BenchmarkRunError, - "The benchmark timer's start method must be called", - ); - }, -}); - -Deno.test({ - name: "Bench with stop before start should throw", - fn: async function (): Promise<void> { - await assertThrowsAsync( - async (): Promise<void> => { - bench(function benchStopBeforeStart(b): void { - b.stop(); - b.start(); - // Throws bc the timer's stop is called before start - }); - await runBenchmarks({ only: /benchStopBeforeStart/, silent: true }); - }, - BenchmarkRunError, - "The benchmark timer's start method must be called before its stop method", - ); - }, -}); - -Deno.test({ - name: "clearBenchmarks should clear all candidates", - fn: async function (): Promise<void> { - dummyBench("test"); - - clearBenchmarks(); - const benchingResults = await runBenchmarks({ silent: true }); - - assertEquals(benchingResults.filtered, 0); - assertEquals(benchingResults.results.length, 0); - }, -}); - -Deno.test({ - name: "clearBenchmarks with only as option", - fn: async function (): Promise<void> { - // to reset candidates - clearBenchmarks(); - - dummyBench("test"); - dummyBench("onlyclear"); - - clearBenchmarks({ only: /only/ }); - const benchingResults = await runBenchmarks({ silent: true }); - - assertEquals(benchingResults.filtered, 0); - assertEquals(benchingResults.results.length, 1); - assertEquals(benchingResults.results[0].name, "test"); - }, -}); - -Deno.test({ - name: "clearBenchmarks with skip as option", - fn: async function (): Promise<void> { - // to reset candidates - clearBenchmarks(); - - dummyBench("test"); - dummyBench("skipclear"); - - clearBenchmarks({ skip: /skip/ }); - const benchingResults = await runBenchmarks({ silent: true }); - - assertEquals(benchingResults.filtered, 0); - assertEquals(benchingResults.results.length, 1); - assertEquals(benchingResults.results[0].name, "skipclear"); - }, -}); - -Deno.test({ - name: "clearBenchmarks with only and skip as option", - fn: async function (): Promise<void> { - // to reset candidates - clearBenchmarks(); - - dummyBench("test"); - dummyBench("clearonly"); - dummyBench("clearskip"); - dummyBench("clearonly"); - - clearBenchmarks({ only: /clear/, skip: /skip/ }); - const benchingResults = await runBenchmarks({ silent: true }); - - assertEquals(benchingResults.filtered, 0); - assertEquals(benchingResults.results.length, 2); - assert(!!benchingResults.results.find(({ name }) => name === "test")); - assert(!!benchingResults.results.find(({ name }) => name === "clearskip")); - }, -}); - -Deno.test({ - name: "progressCallback of runBenchmarks", - fn: async function (): Promise<void> { - clearBenchmarks(); - dummyBench("skip"); - dummyBench("single"); - dummyBench("multiple", 2); - - const progressCallbacks: BenchmarkRunProgress[] = []; - - const benchingResults = await runBenchmarks( - { skip: /skip/, silent: true }, - (progress) => { - progressCallbacks.push(progress); - }, - ); - - let pc = 0; - // Assert initial progress before running - let progress = progressCallbacks[pc++]; - assert(progress.queued); - assertEquals(progress.state, ProgressState.BenchmarkingStart); - assertEquals(progress.filtered, 1); - assertEquals(progress.queued.length, 2); - assertEquals(progress.running, undefined); - assertEquals(progress.results, []); - - // Assert start of bench "single" - progress = progressCallbacks[pc++]; - assertEquals(progress.state, ProgressState.BenchStart); - assertEquals(progress.filtered, 1); - assert(progress.queued); - assertEquals(progress.queued.length, 1); - assert(!!progress.queued.find(({ name }) => name == "multiple")); - assertEquals(progress.running, { - name: "single", - runsCount: 1, - measuredRunsMs: [], - }); - assertEquals(progress.results, []); - - // Assert running result of bench "single" - progress = progressCallbacks[pc++]; - assertEquals(progress.state, ProgressState.BenchPartialResult); - assert(progress.queued); - assertEquals(progress.queued.length, 1); - assertEquals(progress.running!.measuredRunsMs.length, 1); - assertEquals(progress.results.length, 0); - - // Assert result of bench "single" - progress = progressCallbacks[pc++]; - assertEquals(progress.state, ProgressState.BenchResult); - assert(progress.queued); - assertEquals(progress.queued.length, 1); - assertEquals(progress.running, undefined); - assertEquals(progress.results.length, 1); - assert(!!progress.results.find(({ name }) => name == "single")); - - // Assert start of bench "multiple" - progress = progressCallbacks[pc++]; - assertEquals(progress.state, ProgressState.BenchStart); - assert(progress.queued); - assertEquals(progress.queued.length, 0); - assertEquals(progress.running, { - name: "multiple", - runsCount: 2, - measuredRunsMs: [], - }); - assertEquals(progress.results.length, 1); - - // Assert first result of bench "multiple" - progress = progressCallbacks[pc++]; - assertEquals(progress.state, ProgressState.BenchPartialResult); - assert(progress.queued); - assertEquals(progress.queued.length, 0); - assertEquals(progress.running!.measuredRunsMs.length, 1); - assertEquals(progress.results.length, 1); - - // Assert second result of bench "multiple" - progress = progressCallbacks[pc++]; - assertEquals(progress.state, ProgressState.BenchPartialResult); - assert(progress.queued); - assertEquals(progress.queued.length, 0); - assertEquals(progress.running!.measuredRunsMs.length, 2); - assertEquals(progress.results.length, 1); - - // Assert finish of bench "multiple" - progress = progressCallbacks[pc++]; - assertEquals(progress.state, ProgressState.BenchResult); - assert(progress.queued); - assertEquals(progress.queued.length, 0); - assertEquals(progress.running, undefined); - assertEquals(progress.results.length, 2); - assert(!!progress.results.find(({ name }) => name == "single")); - const resultOfMultiple = progress.results.filter( - ({ name }) => name == "multiple", - ); - assertEquals(resultOfMultiple.length, 1); - assert(!!resultOfMultiple[0].measuredRunsMs); - assert(!isNaN(resultOfMultiple[0].measuredRunsAvgMs)); - assertEquals(resultOfMultiple[0].measuredRunsMs.length, 2); - - // The last progress should equal the final result from promise except the state property - progress = progressCallbacks[pc++]; - assertEquals(progress.state, ProgressState.BenchmarkingEnd); - delete progress.state; - assertEquals(progress, benchingResults); - }, -}); - -Deno.test({ - name: "async progressCallback", - fn: async function (): Promise<void> { - clearBenchmarks(); - dummyBench("single"); - - const asyncCallbacks = []; - - await runBenchmarks({ silent: true }, (progress) => { - return new Promise((resolve) => { - queueMicrotask(() => { - asyncCallbacks.push(progress); - resolve(); - }); - }); - }); - - assertEquals(asyncCallbacks.length, 5); - }, -}); - -function dummyBench(name: string, runs = 1): void { - bench({ - name, - runs, - func(b) { - b.start(); - b.stop(); - }, - }); -} |