'use strict'; const { mustCall, mustNotCall } = require('../common'); const { Readable } = require('stream'); const { strictEqual } = require('assert'); async function asyncSupport() { const finallyMustCall = mustCall(); const bodyMustCall = mustCall(); async function* infiniteGenerate() { try { while (true) yield 'a'; } finally { finallyMustCall(); } } const stream = Readable.from(infiniteGenerate()); for await (const chunk of stream) { bodyMustCall(); strictEqual(chunk, 'a'); break; } } async function syncSupport() { const finallyMustCall = mustCall(); const bodyMustCall = mustCall(); function* infiniteGenerate() { try { while (true) yield 'a'; } finally { finallyMustCall(); } } const stream = Readable.from(infiniteGenerate()); for await (const chunk of stream) { bodyMustCall(); strictEqual(chunk, 'a'); break; } } async function syncPromiseSupport() { const returnMustBeAwaited = mustCall(); const bodyMustCall = mustCall(); function* infiniteGenerate() { try { while (true) yield Promise.resolve('a'); } finally { // eslint-disable-next-line no-unsafe-finally return { then(cb) { returnMustBeAwaited(); cb(); } }; } } const stream = Readable.from(infiniteGenerate()); for await (const chunk of stream) { bodyMustCall(); strictEqual(chunk, 'a'); break; } } async function syncRejectedSupport() { const returnMustBeAwaited = mustCall(); const bodyMustNotCall = mustNotCall(); const catchMustCall = mustCall(); const secondNextMustNotCall = mustNotCall(); function* generate() { try { yield Promise.reject('a'); secondNextMustNotCall(); } finally { // eslint-disable-next-line no-unsafe-finally return { then(cb) { returnMustBeAwaited(); cb(); } }; } } const stream = Readable.from(generate()); try { for await (const chunk of stream) { bodyMustNotCall(chunk); } } catch { catchMustCall(); } } async function noReturnAfterThrow() { const returnMustNotCall = mustNotCall(); const bodyMustNotCall = mustNotCall(); const catchMustCall = mustCall(); const nextMustCall = mustCall(); const stream = Readable.from({ [Symbol.asyncIterator]() { return this; }, async next() { nextMustCall(); throw new Error('a'); }, async return() { returnMustNotCall(); return { done: true }; }, }); try { for await (const chunk of stream) { bodyMustNotCall(chunk); } } catch { catchMustCall(); } } async function closeStreamWhileNextIsPending() { const finallyMustCall = mustCall(); const dataMustCall = mustCall(); let resolveDestroy; const destroyed = new Promise((resolve) => { resolveDestroy = mustCall(resolve); }); let resolveYielded; const yielded = new Promise((resolve) => { resolveYielded = mustCall(resolve); }); async function* infiniteGenerate() { try { while (true) { yield 'a'; resolveYielded(); await destroyed; } } finally { finallyMustCall(); } } const stream = Readable.from(infiniteGenerate()); stream.on('data', (data) => { dataMustCall(); strictEqual(data, 'a'); }); yielded.then(() => { stream.destroy(); resolveDestroy(); }); } async function closeAfterNullYielded() { const finallyMustCall = mustCall(); const dataMustCall = mustCall(3); function* generate() { try { yield 'a'; yield 'a'; yield 'a'; } finally { finallyMustCall(); } } const stream = Readable.from(generate()); stream.on('data', (chunk) => { dataMustCall(); strictEqual(chunk, 'a'); }); } Promise.all([ asyncSupport(), syncSupport(), syncPromiseSupport(), syncRejectedSupport(), noReturnAfterThrow(), closeStreamWhileNextIsPending(), closeAfterNullYielded(), ]).then(mustCall());