diff options
Diffstat (limited to 'cli/js')
-rw-r--r-- | cli/js/net_test.ts | 2 | ||||
-rw-r--r-- | cli/js/performance_test.ts | 7 | ||||
-rw-r--r-- | cli/js/signal_test.ts | 47 | ||||
-rw-r--r-- | cli/js/test_util.ts | 30 | ||||
-rw-r--r-- | cli/js/timers.ts | 8 | ||||
-rw-r--r-- | cli/js/timers_test.ts | 66 | ||||
-rw-r--r-- | cli/js/unit_tests.ts | 1 | ||||
-rw-r--r-- | cli/js/workers_test.ts | 93 |
8 files changed, 102 insertions, 152 deletions
diff --git a/cli/js/net_test.ts b/cli/js/net_test.ts index 8a53a1c74..41d012c56 100644 --- a/cli/js/net_test.ts +++ b/cli/js/net_test.ts @@ -54,7 +54,7 @@ testPerm({ net: true }, async function netTcpConcurrentAccept(): Promise<void> { const p1 = listener.accept().catch(checkErr); await Promise.race([p, p1]); listener.close(); - await [p, p1]; + await Promise.all([p, p1]); assertEquals(acceptErrCount, 1); }); diff --git a/cli/js/performance_test.ts b/cli/js/performance_test.ts index 7e7f63d8c..227514954 100644 --- a/cli/js/performance_test.ts +++ b/cli/js/performance_test.ts @@ -1,10 +1,13 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { testPerm, assert } from "./test_util.ts"; +import { testPerm, assert, createResolvable } from "./test_util.ts"; -testPerm({ hrtime: false }, function now(): void { +testPerm({ hrtime: false }, async function performanceNow(): Promise<void> { + const resolvable = createResolvable(); const start = performance.now(); setTimeout((): void => { const end = performance.now(); assert(end - start >= 10); + resolvable.resolve(); }, 10); + await resolvable; }); diff --git a/cli/js/signal_test.ts b/cli/js/signal_test.ts index 1c8658477..a0588b987 100644 --- a/cli/js/signal_test.ts +++ b/cli/js/signal_test.ts @@ -4,7 +4,8 @@ import { testPerm, assert, assertEquals, - assertThrows + assertThrows, + createResolvable } from "./test_util.ts"; function defer(n: number): Promise<void> { @@ -101,15 +102,13 @@ if (Deno.build.os === "win") { ); }); } else { - testPerm({ run: true, net: true }, async function signalStreamTest(): Promise< - void - > { + testPerm({ run: true }, async function signalStreamTest(): Promise<void> { + const resolvable = createResolvable(); // This prevents the program from exiting. const t = setInterval(() => {}, 1000); let c = 0; const sig = Deno.signal(Deno.Signal.SIGUSR1); - setTimeout(async () => { await defer(20); for (const _ of Array(3)) { @@ -118,6 +117,7 @@ if (Deno.build.os === "win") { await defer(20); } sig.dispose(); + resolvable.resolve(); }); for await (const _ of sig) { @@ -126,25 +126,32 @@ if (Deno.build.os === "win") { assertEquals(c, 3); - clearTimeout(t); + clearInterval(t); + // Defer for a moment to allow async op from `setInterval` to resolve; + // for more explanation see `FIXME` in `cli/js/timers.ts::setGlobalTimeout` + await defer(20); + await resolvable; }); - testPerm( - { run: true, net: true }, - async function signalPromiseTest(): Promise<void> { - // This prevents the program from exiting. - const t = setInterval(() => {}, 1000); + testPerm({ run: true }, async function signalPromiseTest(): Promise<void> { + const resolvable = createResolvable(); + // This prevents the program from exiting. + const t = setInterval(() => {}, 1000); - const sig = Deno.signal(Deno.Signal.SIGUSR1); - setTimeout(() => { - Deno.kill(Deno.pid, Deno.Signal.SIGUSR1); - }, 20); - await sig; - sig.dispose(); + const sig = Deno.signal(Deno.Signal.SIGUSR1); + setTimeout(() => { + Deno.kill(Deno.pid, Deno.Signal.SIGUSR1); + resolvable.resolve(); + }, 20); + await sig; + sig.dispose(); - clearTimeout(t); - } - ); + clearInterval(t); + // Defer for a moment to allow async op from `setInterval` to resolve; + // for more explanation see `FIXME` in `cli/js/timers.ts::setGlobalTimeout` + await defer(20); + await resolvable; + }); testPerm({ run: true }, async function signalShorthandsTest(): Promise<void> { let s: Deno.SignalStream; diff --git a/cli/js/test_util.ts b/cli/js/test_util.ts index 6f52c01df..027610dd9 100644 --- a/cli/js/test_util.ts +++ b/cli/js/test_util.ts @@ -107,10 +107,36 @@ function normalizeTestPermissions(perms: TestPermissions): 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(): Promise<void> { + return async function resourceSanitizer(): Promise<void> { const preResources = Deno.resources(); await fn(); const postResources = Deno.resources(); @@ -131,7 +157,7 @@ export function testPerm(perms: TestPermissions, fn: Deno.TestFunction): void { return; } - Deno.test(fn.name, assertResources(fn)); + Deno.test(fn.name, assertResources(assertOps(fn))); } export function test(fn: Deno.TestFunction): void { diff --git a/cli/js/timers.ts b/cli/js/timers.ts index a92896efd..2b29f2a55 100644 --- a/cli/js/timers.ts +++ b/cli/js/timers.ts @@ -43,6 +43,14 @@ async function setGlobalTimeout(due: number, now: number): Promise<void> { // Send message to the backend. globalTimeoutDue = due; pendingEvents++; + // FIXME(bartlomieju): this is problematic, because `clearGlobalTimeout` + // is synchronous. That means that timer is cancelled, but this promise is still pending + // until next turn of event loop. This leads to "leaking of async ops" in tests; + // because `clearTimeout/clearInterval` might be the last statement in test function + // `opSanitizer` will immediately complain that there is pending op going on, unless + // some timeout/defer is put in place to allow promise resolution. + // Ideally `clearGlobalTimeout` doesn't return until this op is resolved, but + // I'm not if that's possible. await sendAsync("op_global_timer", { timeout }); pendingEvents--; // eslint-disable-next-line @typescript-eslint/no-use-before-define diff --git a/cli/js/timers_test.ts b/cli/js/timers_test.ts index 06367fc6d..8cb505ee4 100644 --- a/cli/js/timers_test.ts +++ b/cli/js/timers_test.ts @@ -1,5 +1,11 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { test, assert, assertEquals, assertNotEquals } from "./test_util.ts"; +import { + test, + createResolvable, + assert, + assertEquals, + assertNotEquals +} from "./test_util.ts"; function deferred(): { promise: Promise<{}>; @@ -178,8 +184,6 @@ test(async function timeoutCallbackThis(): Promise<void> { }); test(async function timeoutBindThis(): Promise<void> { - function noop(): void {} - const thisCheckPassed = [null, undefined, window, globalThis]; const thisCheckFailed = [ @@ -194,41 +198,37 @@ test(async function timeoutBindThis(): Promise<void> { Object.prototype ]; - thisCheckPassed.forEach( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (thisArg: any): void => { - let hasThrown = 0; - try { - setTimeout.call(thisArg, noop, 1); - hasThrown = 1; - } catch (err) { - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } + for (const thisArg of thisCheckPassed) { + const resolvable = createResolvable(); + let hasThrown = 0; + try { + setTimeout.call(thisArg, () => resolvable.resolve(), 1); + hasThrown = 1; + } catch (err) { + if (err instanceof TypeError) { + hasThrown = 2; + } else { + hasThrown = 3; } - assertEquals(hasThrown, 1); } - ); + await resolvable; + assertEquals(hasThrown, 1); + } - thisCheckFailed.forEach( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (thisArg: any): void => { - let hasThrown = 0; - try { - setTimeout.call(thisArg, noop, 1); - hasThrown = 1; - } catch (err) { - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } + for (const thisArg of thisCheckFailed) { + let hasThrown = 0; + try { + setTimeout.call(thisArg, () => {}, 1); + hasThrown = 1; + } catch (err) { + if (err instanceof TypeError) { + hasThrown = 2; + } else { + hasThrown = 3; } - assertEquals(hasThrown, 2); } - ); + assertEquals(hasThrown, 2); + } }); test(async function clearTimeoutShouldConvertToNumber(): Promise<void> { diff --git a/cli/js/unit_tests.ts b/cli/js/unit_tests.ts index f20e2f137..5093ce0b2 100644 --- a/cli/js/unit_tests.ts +++ b/cli/js/unit_tests.ts @@ -62,7 +62,6 @@ import "./utime_test.ts"; import "./write_file_test.ts"; import "./performance_test.ts"; import "./version_test.ts"; -import "./workers_test.ts"; if (import.meta.main) { await Deno.runTests(); diff --git a/cli/js/workers_test.ts b/cli/js/workers_test.ts deleted file mode 100644 index 5b8b1ef97..000000000 --- a/cli/js/workers_test.ts +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { - test, - testPerm, - assert, - assertEquals, - createResolvable -} from "./test_util.ts"; - -test(async function workersBasic(): Promise<void> { - const promise = createResolvable(); - const jsWorker = new Worker("../tests/subdir/test_worker.js", { - type: "module", - name: "jsWorker" - }); - const tsWorker = new Worker("../tests/subdir/test_worker.ts", { - type: "module", - name: "tsWorker" - }); - - tsWorker.onmessage = (e): void => { - assertEquals(e.data, "Hello World"); - promise.resolve(); - }; - - jsWorker.onmessage = (e): void => { - assertEquals(e.data, "Hello World"); - tsWorker.postMessage("Hello World"); - }; - - jsWorker.onerror = (e: Event): void => { - e.preventDefault(); - jsWorker.postMessage("Hello World"); - }; - - jsWorker.postMessage("Hello World"); - await promise; -}); - -test(async function nestedWorker(): Promise<void> { - const promise = createResolvable(); - - const nestedWorker = new Worker("../tests/subdir/nested_worker.js", { - type: "module", - name: "nested" - }); - - nestedWorker.onmessage = (e): void => { - assert(e.data.type !== "error"); - promise.resolve(); - }; - - nestedWorker.postMessage("Hello World"); - await promise; -}); - -test(async function workerThrowsWhenExecuting(): Promise<void> { - const promise = createResolvable(); - - const throwingWorker = new Worker("../tests/subdir/throwing_worker.js", { - type: "module" - }); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - throwingWorker.onerror = (e: any): void => { - e.preventDefault(); - assert(/Uncaught Error: Thrown error/.test(e.message)); - promise.resolve(); - }; - - await promise; -}); - -testPerm({ net: true }, async function workerCanUseFetch(): Promise<void> { - const promise = createResolvable(); - - const fetchingWorker = new Worker("../tests/subdir/fetching_worker.js", { - type: "module" - }); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - fetchingWorker.onerror = (e: any): void => { - e.preventDefault(); - promise.reject(e.message); - }; - - fetchingWorker.onmessage = (e): void => { - assert(e.data === "Done!"); - promise.resolve(); - }; - - await promise; -}); |