diff options
Diffstat (limited to 'cli/js')
-rw-r--r-- | cli/js/lib.deno.ns.d.ts | 2 | ||||
-rw-r--r-- | cli/js/testing.ts | 61 | ||||
-rw-r--r-- | cli/js/tests/test_util.ts | 43 |
3 files changed, 63 insertions, 43 deletions
diff --git a/cli/js/lib.deno.ns.d.ts b/cli/js/lib.deno.ns.d.ts index 6453b4ab8..8f79487b7 100644 --- a/cli/js/lib.deno.ns.d.ts +++ b/cli/js/lib.deno.ns.d.ts @@ -18,6 +18,8 @@ declare namespace Deno { fn: TestFunction; name: string; skip?: boolean; + disableOpSanitizer?: boolean; + disableResourceSanitizer?: boolean; } /** Register a test which will be run when `deno test` is used on the command diff --git a/cli/js/testing.ts b/cli/js/testing.ts index ed34e08f7..0d21962c0 100644 --- a/cli/js/testing.ts +++ b/cli/js/testing.ts @@ -4,6 +4,9 @@ import { exit } from "./ops/os.ts"; import { Console, stringifyArgs } from "./web/console.ts"; import { stdout } from "./files.ts"; import { TextEncoder } from "./web/text_encoding.ts"; +import { metrics } from "./ops/runtime.ts"; +import { resources } from "./ops/resources.ts"; +import { assert } from "./util.ts"; const RED_FAILED = red("FAILED"); const GREEN_OK = green("ok"); @@ -15,12 +18,59 @@ function formatDuration(time = 0): string { return gray(italic(timeStr)); } +// Wrap `TestFunction` in additional assertion that makes sure +// the test case does not leak async "ops" - ie. number of async +// completed ops after the test is the same as number of dispatched +// ops. Note that "unref" ops are ignored since in nature that are +// optional. +function assertOps(fn: TestFunction): TestFunction { + return async function asyncOpSanitizer(): Promise<void> { + const pre = metrics(); + await fn(); + const post = metrics(); + // We're checking diff because one might spawn HTTP server in the background + // that will be a pending async op before test starts. + const dispatchedDiff = post.opsDispatchedAsync - pre.opsDispatchedAsync; + const completedDiff = post.opsCompletedAsync - pre.opsCompletedAsync; + assert( + dispatchedDiff === completedDiff, + `Test case is leaking async ops. +Before: + - dispatched: ${pre.opsDispatchedAsync} + - completed: ${pre.opsCompletedAsync} +After: + - dispatched: ${post.opsDispatchedAsync} + - completed: ${post.opsCompletedAsync}` + ); + }; +} + +// Wrap `TestFunction` in additional assertion that makes sure +// the test case does not "leak" resources - ie. resource table after +// the test has exactly the same contents as before the test. +function assertResources(fn: TestFunction): TestFunction { + return async function resourceSanitizer(): Promise<void> { + const pre = resources(); + await fn(); + const post = resources(); + + const preStr = JSON.stringify(pre, null, 2); + const postStr = JSON.stringify(post, null, 2); + const msg = `Test case is leaking resources. +Before: ${preStr} +After: ${postStr}`; + assert(preStr === postStr, msg); + }; +} + export type TestFunction = () => void | Promise<void>; export interface TestDefinition { fn: TestFunction; name: string; skip?: boolean; + disableOpSanitizer?: boolean; + disableResourceSanitizer?: boolean; } const TEST_REGISTRY: TestDefinition[] = []; @@ -56,7 +106,16 @@ export function test( if (!t.name) { throw new TypeError("The test name can't be empty"); } - testDef = { fn: t.fn, name: t.name, skip: Boolean(t.skip) }; + + testDef = { ...t, skip: Boolean(t.skip) }; + } + + if (testDef.disableOpSanitizer !== true) { + testDef.fn = assertOps(testDef.fn); + } + + if (testDef.disableResourceSanitizer !== true) { + testDef.fn = assertResources(testDef.fn); } TEST_REGISTRY.push(testDef); diff --git a/cli/js/tests/test_util.ts b/cli/js/tests/test_util.ts index 621d86bfd..851596b11 100644 --- a/cli/js/tests/test_util.ts +++ b/cli/js/tests/test_util.ts @@ -112,47 +112,6 @@ function normalizeTestPermissions(perms: UnitTestPermissions): Permissions { }; } -// Wrap `TestFunction` in additional assertion that makes sure -// the test case does not leak async "ops" - ie. number of async -// completed ops after the test is the same as number of dispatched -// ops. Note that "unref" ops are ignored since in nature that are -// optional. -function assertOps(fn: Deno.TestFunction): Deno.TestFunction { - return async function asyncOpSanitizer(): Promise<void> { - const pre = Deno.metrics(); - await fn(); - const post = Deno.metrics(); - // We're checking diff because one might spawn HTTP server in the background - // that will be a pending async op before test starts. - assertEquals( - post.opsDispatchedAsync - pre.opsDispatchedAsync, - post.opsCompletedAsync - pre.opsCompletedAsync, - `Test case is leaking async ops. - Before: - - dispatched: ${pre.opsDispatchedAsync} - - completed: ${pre.opsCompletedAsync} - After: - - dispatched: ${post.opsDispatchedAsync} - - completed: ${post.opsCompletedAsync}` - ); - }; -} - -// Wrap `TestFunction` in additional assertion that makes sure -// the test case does not "leak" resources - ie. resource table after -// the test has exactly the same contents as before the test. -function assertResources(fn: Deno.TestFunction): Deno.TestFunction { - return async function resourceSanitizer(): Promise<void> { - const pre = Deno.resources(); - await fn(); - const post = Deno.resources(); - const msg = `Test case is leaking resources. - Before: ${JSON.stringify(pre, null, 2)} - After: ${JSON.stringify(post, null, 2)}`; - assertEquals(pre, post, msg); - }; -} - interface UnitTestPermissions { read?: boolean; write?: boolean; @@ -209,7 +168,7 @@ export function unitTest( const unitTestDefinition: UnitTestDefinition = { name, - fn: assertResources(assertOps(fn)), + fn, skip: !!options.skip, perms: normalizedPerms }; |