Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(async): improve test coverage #4567

Merged
merged 4 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions async/_util.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.

// This `reason` comes from `AbortSignal` thus must be `any`.
// deno-lint-ignore no-explicit-any
export function createAbortError(reason?: any): DOMException {
return new DOMException(
reason ? `Aborted: ${reason}` : "Aborted",
"AbortError",
);
}

export function exponentialBackoffWithJitter(
cap: number,
base: number,
Expand Down
32 changes: 30 additions & 2 deletions async/_util_test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { exponentialBackoffWithJitter } from "./_util.ts";
import { assertEquals } from "../assert/mod.ts";
import { createAbortError, exponentialBackoffWithJitter } from "./_util.ts";
import { assertEquals, assertInstanceOf } from "../assert/mod.ts";

// test util to ensure deterministic results during testing of backoff function by polyfilling Math.random
function prngMulberry32(seed: number) {
Expand Down Expand Up @@ -50,3 +50,31 @@ Deno.test("exponentialBackoffWithJitter()", () => {
assertEquals(results as typeof row, row);
}
});

Deno.test("createAbortError()", () => {
const error = createAbortError();
assertInstanceOf(error, DOMException);
assertEquals(error.name, "AbortError");
assertEquals(error.message, "Aborted");
});

Deno.test("createAbortError() handles aborted signal with reason", () => {
const c = new AbortController();
c.abort("Expected Reason");
const error = createAbortError(c.signal.reason);
assertInstanceOf(error, DOMException);
assertEquals(error.name, "AbortError");
assertEquals(error.message, "Aborted: Expected Reason");
});

Deno.test("createAbortError() handles aborted signal without reason", () => {
const c = new AbortController();
c.abort();
const error = createAbortError(c.signal.reason);
assertInstanceOf(error, DOMException);
assertEquals(error.name, "AbortError");
assertEquals(
error.message,
"Aborted: AbortError: The signal has been aborted",
);
});
11 changes: 2 additions & 9 deletions async/abortable.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.

import { createAbortError } from "./_util.ts";

/**
* Make {@linkcode Promise} abortable with the given signal.
*
Expand Down Expand Up @@ -145,12 +147,3 @@ export async function* abortableAsyncIterable<T>(
yield value;
}
}

// This `reason` comes from `AbortSignal` thus must be `any`.
// deno-lint-ignore no-explicit-any
function createAbortError(reason?: any): DOMException {
return new DOMException(
reason ? `Aborted: ${reason}` : "Aborted",
"AbortError",
);
}
10 changes: 5 additions & 5 deletions async/debounce_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { assertEquals, assertStrictEquals } from "../assert/mod.ts";
import { debounce, type DebouncedFunction } from "./debounce.ts";
import { delay } from "./delay.ts";

Deno.test("debounce() handles called", async function () {
Deno.test("debounce() handles called", async () => {
let called = 0;
const d = debounce(() => called++, 100);
d();
Expand All @@ -16,7 +16,7 @@ Deno.test("debounce() handles called", async function () {
assertEquals(d.pending, false);
});

Deno.test("debounce() handles cancelled", async function () {
Deno.test("debounce() handles cancelled", async () => {
let called = 0;
const d = debounce(() => called++, 100);
d();
Expand All @@ -30,7 +30,7 @@ Deno.test("debounce() handles cancelled", async function () {
assertEquals(d.pending, false);
});

Deno.test("debounce() handles flush", function () {
Deno.test("debounce() handles flush", () => {
let called = 0;
const d = debounce(() => called++, 100);
d();
Expand All @@ -43,7 +43,7 @@ Deno.test("debounce() handles flush", function () {
assertEquals(d.pending, false);
});

Deno.test("debounce() handles params and context", async function () {
Deno.test("debounce() handles params and context", async () => {
const params: Array<string | number> = [];
const d: DebouncedFunction<[string, number]> = debounce(
function (param1: string, param2: number) {
Expand All @@ -66,7 +66,7 @@ Deno.test("debounce() handles params and context", async function () {
assertEquals(d.pending, false);
});

Deno.test("debounce() handles number and string types", async function () {
Deno.test("debounce() handles number and string types", async () => {
const params: Array<string> = [];
const fn = (param: string) => params.push(param);
const d: DebouncedFunction<[string]> = debounce(fn, 100);
Expand Down
2 changes: 1 addition & 1 deletion async/delay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export interface DelayOptions {
* ```
*/
export function delay(ms: number, options: DelayOptions = {}): Promise<void> {
const { signal, persistent } = options;
const { signal, persistent = true } = options;
if (signal?.aborted) return Promise.reject(signal.reason);
return new Promise((resolve, reject) => {
const abort = () => {
Expand Down
46 changes: 40 additions & 6 deletions async/delay_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@
import { delay } from "./delay.ts";
import {
assert,
assertEquals,
assertInstanceOf,
assertRejects,
assertStrictEquals,
} from "../assert/mod.ts";
import { assertSpyCalls, stub } from "../testing/mock.ts";

// https://dom.spec.whatwg.org/#interface-AbortSignal
function assertIsDefaultAbortReason(reason: unknown) {
assertInstanceOf(reason, DOMException);
assertStrictEquals(reason.name, "AbortError");
}

Deno.test("delay()", async function () {
Deno.test("delay()", async () => {
const start = new Date();
const delayedPromise = delay(100);
const result = await delayedPromise;
Expand All @@ -22,7 +24,7 @@ Deno.test("delay()", async function () {
assert(diff >= 100);
});

Deno.test("delay() handles abort", async function () {
Deno.test("delay() handles abort", async () => {
const start = new Date();
const abort = new AbortController();
const { signal } = abort;
Expand All @@ -34,7 +36,7 @@ Deno.test("delay() handles abort", async function () {
assertIsDefaultAbortReason(cause);
});

Deno.test("delay() checks abort reason", async function (ctx) {
Deno.test("delay() checks abort reason", async (ctx) => {
async function assertRejectsReason(reason: unknown) {
const start = new Date();
const abort = new AbortController();
Expand Down Expand Up @@ -69,7 +71,7 @@ Deno.test("delay() checks abort reason", async function (ctx) {
});
});

Deno.test("delay() handles non-aborted signal", async function () {
Deno.test("delay() handles non-aborted signal", async () => {
const start = new Date();
const abort = new AbortController();
const { signal } = abort;
Expand All @@ -80,7 +82,7 @@ Deno.test("delay() handles non-aborted signal", async function () {
assert(diff >= 100);
});

Deno.test("delay() handles aborted signal after delay", async function () {
Deno.test("delay() handles aborted signal after delay", async () => {
const start = new Date();
const abort = new AbortController();
const { signal } = abort;
Expand All @@ -92,7 +94,7 @@ Deno.test("delay() handles aborted signal after delay", async function () {
assert(diff >= 100);
});

Deno.test("delay() handles already aborted signal", async function () {
Deno.test("delay() handles already aborted signal", async () => {
const start = new Date();
const abort = new AbortController();
abort.abort();
Expand All @@ -103,3 +105,35 @@ Deno.test("delay() handles already aborted signal", async function () {
assert(diff < 100);
assertIsDefaultAbortReason(cause);
});

Deno.test("delay() handles persitent option", async () => {
using unrefTimer = stub(Deno, "unrefTimer");
await delay(100, { persistent: false });
assertSpyCalls(unrefTimer, 1);
});

Deno.test("delay() handles persistent option with reference error", async () => {
using unrefTimer = stub(Deno, "unrefTimer", () => {
throw new ReferenceError();
});
await delay(100, { persistent: false });
assertSpyCalls(unrefTimer, 1);
});

Deno.test({
name: "delay() handles persistent option with error",
async fn() {
using unrefTimer = stub(Deno, "unrefTimer", () => {
throw new Error("Error!");
});
try {
await delay(100, { persistent: false });
} catch (e) {
assert(e instanceof Error);
assertEquals(e.message, "Error!");
assertSpyCalls(unrefTimer, 1);
}
},
sanitizeResources: false,
sanitizeOps: false,
});
1 change: 0 additions & 1 deletion async/mux_async_iterator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ export class MuxAsyncIterator<T> implements AsyncIterable<T> {
for (const e of this.#throws) {
throw e;
}
this.#throws.length = 0;
}
// Clear the `yields` list and reset the `signal` promise.
this.#yields.length = 0;
Expand Down
60 changes: 46 additions & 14 deletions async/mux_async_iterator_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class CustomAsyncIterable {
}
}

Deno.test("MuxAsyncIterator()", async function () {
Deno.test("MuxAsyncIterator()", async () => {
const mux = new MuxAsyncIterator<number>();
mux.add(gen123());
mux.add(gen456());
Expand All @@ -34,24 +34,56 @@ Deno.test("MuxAsyncIterator()", async function () {
assertEquals(results, new Set([1, 2, 3, 4, 5, 6]));
});

Deno.test("MuxAsyncIterator() takes async iterable as source", async function () {
Deno.test("MuxAsyncIterator() works with no iterables", async () => {
const mux = new MuxAsyncIterator<number>();
const results = new Set(await Array.fromAsync(mux));
assertEquals(results.size, 0);
assertEquals(results, new Set([]));
});

Deno.test("MuxAsyncIterator() clears iterables after successful iteration", async () => {
const mux = new MuxAsyncIterator<number>();
mux.add(gen123());
mux.add(gen456());
const results = new Set(await Array.fromAsync(mux));
assertEquals(results.size, 6);
assertEquals(results, new Set([1, 2, 3, 4, 5, 6]));
mux.add(gen123());
const results2 = new Set(await Array.fromAsync(mux));
assertEquals(results2.size, 3);
assertEquals(results2, new Set([1, 2, 3]));
});

Deno.test("MuxAsyncIterator() takes async iterable as source", async () => {
const mux = new MuxAsyncIterator<number>();
mux.add(new CustomAsyncIterable());
const results = new Set(await Array.fromAsync(mux));
assertEquals(results.size, 3);
assertEquals(results, new Set([1, 2, 3]));
});

Deno.test({
name: "MuxAsyncIterator() throws when the source throws",
async fn() {
const mux = new MuxAsyncIterator<number>();
mux.add(gen123());
mux.add(genThrows());
await assertRejects(
async () => await Array.fromAsync(mux),
Error,
"something went wrong",
);
},
Deno.test("MuxAsyncIterator() throws when the source throws", async () => {
const mux = new MuxAsyncIterator<number>();
mux.add(gen123());
mux.add(genThrows());
await assertRejects(
async () => await Array.fromAsync(mux),
Error,
"something went wrong",
);
});

Deno.test("MuxAsyncIterator() doesn't clear iterables after throwing", async () => {
const mux = new MuxAsyncIterator<number>();
mux.add(genThrows());
await assertRejects(
async () => await Array.fromAsync(mux),
Error,
"something went wrong",
);
await assertRejects(
async () => await Array.fromAsync(mux),
Error,
"something went wrong",
);
});
4 changes: 2 additions & 2 deletions async/pool_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
assertStringIncludes,
} from "../assert/mod.ts";

Deno.test("pooledMap()", async function () {
Deno.test("pooledMap()", async () => {
const start = new Date();
const results = pooledMap(
2,
Expand Down Expand Up @@ -66,7 +66,7 @@ Deno.test("pooledMap() returns ordered items", async () => {
assertEquals(returned, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
});

Deno.test("pooledMap() checks browser compat", async function () {
Deno.test("pooledMap() checks browser compat", async () => {
// Simulates the environment where Symbol.asyncIterator is not available
const asyncIterFunc = ReadableStream.prototype[Symbol.asyncIterator];
// deno-lint-ignore no-explicit-any
Expand Down
Loading