mirror of
https://github.com/denoland/deno.git
synced 2024-11-26 16:09:27 -05:00
250 lines
5.8 KiB
TypeScript
250 lines
5.8 KiB
TypeScript
|
// Copyright Node.js contributors. All rights reserved. MIT License.
|
||
|
import Readable from "./readable.ts";
|
||
|
import Stream from "./stream.ts";
|
||
|
import toReadableAsyncIterator from "./async_iterator.ts";
|
||
|
import { deferred } from "../../async/mod.ts";
|
||
|
import { assertEquals, assertThrowsAsync } from "../../testing/asserts.ts";
|
||
|
|
||
|
Deno.test("Stream to async iterator", async () => {
|
||
|
let destroyExecuted = 0;
|
||
|
const destroyExecutedExpected = 1;
|
||
|
const destroyExpectedExecutions = deferred();
|
||
|
|
||
|
class AsyncIteratorStream extends Stream {
|
||
|
constructor() {
|
||
|
super();
|
||
|
}
|
||
|
|
||
|
destroy() {
|
||
|
destroyExecuted++;
|
||
|
if (destroyExecuted == destroyExecutedExpected) {
|
||
|
destroyExpectedExecutions.resolve();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Symbol.asyncIterator] = Readable.prototype[Symbol.asyncIterator];
|
||
|
}
|
||
|
|
||
|
const stream = new AsyncIteratorStream();
|
||
|
|
||
|
queueMicrotask(() => {
|
||
|
stream.emit("data", "hello");
|
||
|
stream.emit("data", "world");
|
||
|
stream.emit("end");
|
||
|
});
|
||
|
|
||
|
let res = "";
|
||
|
|
||
|
for await (const d of stream) {
|
||
|
res += d;
|
||
|
}
|
||
|
assertEquals(res, "helloworld");
|
||
|
|
||
|
const destroyTimeout = setTimeout(
|
||
|
() => destroyExpectedExecutions.reject(),
|
||
|
1000,
|
||
|
);
|
||
|
await destroyExpectedExecutions;
|
||
|
clearTimeout(destroyTimeout);
|
||
|
assertEquals(destroyExecuted, destroyExecutedExpected);
|
||
|
});
|
||
|
|
||
|
Deno.test("Stream to async iterator throws on 'error' emitted", async () => {
|
||
|
let closeExecuted = 0;
|
||
|
const closeExecutedExpected = 1;
|
||
|
const closeExpectedExecutions = deferred();
|
||
|
|
||
|
let errorExecuted = 0;
|
||
|
const errorExecutedExpected = 1;
|
||
|
const errorExpectedExecutions = deferred();
|
||
|
|
||
|
class StreamImplementation extends Stream {
|
||
|
close() {
|
||
|
closeExecuted++;
|
||
|
if (closeExecuted == closeExecutedExpected) {
|
||
|
closeExpectedExecutions.resolve();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const stream = new StreamImplementation();
|
||
|
queueMicrotask(() => {
|
||
|
stream.emit("data", 0);
|
||
|
stream.emit("data", 1);
|
||
|
stream.emit("error", new Error("asd"));
|
||
|
});
|
||
|
|
||
|
toReadableAsyncIterator(stream)
|
||
|
.next()
|
||
|
.catch((err) => {
|
||
|
errorExecuted++;
|
||
|
if (errorExecuted == errorExecutedExpected) {
|
||
|
errorExpectedExecutions.resolve();
|
||
|
}
|
||
|
assertEquals(err.message, "asd");
|
||
|
});
|
||
|
|
||
|
const closeTimeout = setTimeout(
|
||
|
() => closeExpectedExecutions.reject(),
|
||
|
1000,
|
||
|
);
|
||
|
const errorTimeout = setTimeout(
|
||
|
() => errorExpectedExecutions.reject(),
|
||
|
1000,
|
||
|
);
|
||
|
await closeExpectedExecutions;
|
||
|
await errorExpectedExecutions;
|
||
|
clearTimeout(closeTimeout);
|
||
|
clearTimeout(errorTimeout);
|
||
|
assertEquals(closeExecuted, closeExecutedExpected);
|
||
|
assertEquals(errorExecuted, errorExecutedExpected);
|
||
|
});
|
||
|
|
||
|
Deno.test("Async iterator matches values of Readable", async () => {
|
||
|
const readable = new Readable({
|
||
|
objectMode: true,
|
||
|
read() {},
|
||
|
});
|
||
|
readable.push(0);
|
||
|
readable.push(1);
|
||
|
readable.push(null);
|
||
|
|
||
|
const iter = readable[Symbol.asyncIterator]();
|
||
|
|
||
|
assertEquals(
|
||
|
await iter.next().then(({ value }) => value),
|
||
|
0,
|
||
|
);
|
||
|
for await (const d of iter) {
|
||
|
assertEquals(d, 1);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
Deno.test("Async iterator throws on Readable destroyed sync", async () => {
|
||
|
const message = "kaboom from read";
|
||
|
|
||
|
const readable = new Readable({
|
||
|
objectMode: true,
|
||
|
read() {
|
||
|
this.destroy(new Error(message));
|
||
|
},
|
||
|
});
|
||
|
|
||
|
await assertThrowsAsync(
|
||
|
async () => {
|
||
|
// deno-lint-ignore no-empty
|
||
|
for await (const k of readable) {}
|
||
|
},
|
||
|
Error,
|
||
|
message,
|
||
|
);
|
||
|
});
|
||
|
|
||
|
Deno.test("Async iterator throws on Readable destroyed async", async () => {
|
||
|
const message = "kaboom";
|
||
|
const readable = new Readable({
|
||
|
read() {},
|
||
|
});
|
||
|
const iterator = readable[Symbol.asyncIterator]();
|
||
|
|
||
|
readable.destroy(new Error(message));
|
||
|
|
||
|
await assertThrowsAsync(
|
||
|
iterator.next.bind(iterator),
|
||
|
Error,
|
||
|
message,
|
||
|
);
|
||
|
});
|
||
|
|
||
|
Deno.test("Async iterator finishes the iterator when Readable destroyed", async () => {
|
||
|
const readable = new Readable({
|
||
|
read() {},
|
||
|
});
|
||
|
|
||
|
readable.destroy();
|
||
|
|
||
|
const { done } = await readable[Symbol.asyncIterator]().next();
|
||
|
assertEquals(done, true);
|
||
|
});
|
||
|
|
||
|
Deno.test("Async iterator finishes all item promises when Readable destroyed", async () => {
|
||
|
const r = new Readable({
|
||
|
objectMode: true,
|
||
|
read() {
|
||
|
},
|
||
|
});
|
||
|
|
||
|
const b = r[Symbol.asyncIterator]();
|
||
|
const c = b.next();
|
||
|
const d = b.next();
|
||
|
r.destroy();
|
||
|
assertEquals(await c, { done: true, value: undefined });
|
||
|
assertEquals(await d, { done: true, value: undefined });
|
||
|
});
|
||
|
|
||
|
Deno.test("Async iterator: 'next' is triggered by Readable push", async () => {
|
||
|
const max = 42;
|
||
|
let readed = 0;
|
||
|
let received = 0;
|
||
|
const readable = new Readable({
|
||
|
objectMode: true,
|
||
|
read() {
|
||
|
this.push("hello");
|
||
|
if (++readed === max) {
|
||
|
this.push(null);
|
||
|
}
|
||
|
},
|
||
|
});
|
||
|
|
||
|
for await (const k of readable) {
|
||
|
received++;
|
||
|
assertEquals(k, "hello");
|
||
|
}
|
||
|
|
||
|
assertEquals(readed, received);
|
||
|
});
|
||
|
|
||
|
Deno.test("Async iterator: 'close' called on forced iteration end", async () => {
|
||
|
let closeExecuted = 0;
|
||
|
const closeExecutedExpected = 1;
|
||
|
const closeExpectedExecutions = deferred();
|
||
|
|
||
|
class IndestructibleReadable extends Readable {
|
||
|
constructor() {
|
||
|
super({
|
||
|
autoDestroy: false,
|
||
|
read() {},
|
||
|
});
|
||
|
}
|
||
|
|
||
|
close() {
|
||
|
closeExecuted++;
|
||
|
if (closeExecuted == closeExecutedExpected) {
|
||
|
closeExpectedExecutions.resolve();
|
||
|
}
|
||
|
readable.emit("close");
|
||
|
}
|
||
|
|
||
|
// deno-lint-ignore ban-ts-comment
|
||
|
//@ts-ignore
|
||
|
destroy = null;
|
||
|
}
|
||
|
|
||
|
const readable = new IndestructibleReadable();
|
||
|
readable.push("asd");
|
||
|
readable.push("asd");
|
||
|
|
||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||
|
for await (const d of readable) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
const closeTimeout = setTimeout(
|
||
|
() => closeExpectedExecutions.reject(),
|
||
|
1000,
|
||
|
);
|
||
|
await closeExpectedExecutions;
|
||
|
clearTimeout(closeTimeout);
|
||
|
assertEquals(closeExecuted, closeExecutedExpected);
|
||
|
});
|