MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

add coverage for async iter

+218
+105
tests/test_async_iterable_stream_cleanup.cjs
··· 1 + function assert(condition, message) { 2 + if (!condition) throw new Error(message); 3 + } 4 + 5 + function assertEq(actual, expected, message) { 6 + if (actual !== expected) { 7 + throw new Error(`${message}: expected ${expected}, got ${actual}`); 8 + } 9 + } 10 + 11 + function createAsyncIterableStream(source, trace) { 12 + const stream = source.pipeThrough(new TransformStream()); 13 + 14 + stream[Symbol.asyncIterator] = function() { 15 + const reader = this.getReader(); 16 + let finished = false; 17 + 18 + async function cleanup(cancelStream) { 19 + if (finished) return; 20 + finished = true; 21 + 22 + try { 23 + if (cancelStream) { 24 + trace.push('cleanup-before-cancel'); 25 + await reader.cancel(); 26 + trace.push('cleanup-after-cancel'); 27 + } 28 + } finally { 29 + trace.push('cleanup-before-releaseLock'); 30 + reader.releaseLock(); 31 + trace.push('cleanup-after-releaseLock'); 32 + } 33 + } 34 + 35 + return { 36 + async next() { 37 + const result = await reader.read(); 38 + if (result.done) { 39 + await cleanup(false); 40 + return { done: true, value: void 0 }; 41 + } 42 + 43 + trace.push(`next:${result.value}`); 44 + return { done: false, value: result.value }; 45 + }, 46 + 47 + async return() { 48 + trace.push('return-start'); 49 + await cleanup(true); 50 + trace.push('return-end'); 51 + return { done: true, value: void 0 }; 52 + }, 53 + }; 54 + }; 55 + 56 + return stream; 57 + } 58 + 59 + async function main() { 60 + const trace = []; 61 + const source = new ReadableStream({ 62 + start(controller) { 63 + controller.enqueue('alpha'); 64 + controller.enqueue('beta'); 65 + controller.close(); 66 + }, 67 + cancel() { 68 + trace.push('source-cancel'); 69 + }, 70 + }); 71 + 72 + const stream = createAsyncIterableStream(source, trace); 73 + 74 + assert(typeof stream[Symbol.asyncIterator] === 'function', 'stream should expose @@asyncIterator'); 75 + 76 + for await (const chunk of stream) { 77 + trace.push(`chunk:${chunk}`); 78 + break; 79 + } 80 + 81 + trace.push('loop-exit'); 82 + 83 + assertEq( 84 + trace.join(','), 85 + [ 86 + 'next:alpha', 87 + 'chunk:alpha', 88 + 'return-start', 89 + 'cleanup-before-cancel', 90 + 'cleanup-after-cancel', 91 + 'cleanup-before-releaseLock', 92 + 'cleanup-after-releaseLock', 93 + 'return-end', 94 + 'loop-exit', 95 + ].join(','), 96 + 'for-await cleanup should finish after cancel/releaseLock' 97 + ); 98 + 99 + console.log('async iterable stream cleanup survives cancel/releaseLock'); 100 + } 101 + 102 + main().catch((err) => { 103 + console.error(err && err.stack ? err.stack : String(err)); 104 + throw err; 105 + });
+74
tests/test_async_materialized_stack_slice.cjs
··· 1 + function assertEq(actual, expected, message) { 2 + if (actual !== expected) { 3 + throw new Error(`${message}: expected ${expected}, got ${actual}`); 4 + } 5 + } 6 + 7 + async function main() { 8 + const trace = []; 9 + 10 + async function leaf(label) { 11 + const local = `leaf-${label}`; 12 + trace.push(`leaf-before:${local}`); 13 + const resumed = await new Promise((resolve) => setTimeout(() => resolve(label.toUpperCase()), 0)); 14 + trace.push(`leaf-after:${local}:${resumed}`); 15 + return `${local}:${resumed}`; 16 + } 17 + 18 + async function middle(label) { 19 + const local = `middle-${label}`; 20 + trace.push(`middle-enter:${local}`); 21 + try { 22 + const value = await leaf(label); 23 + trace.push(`middle-after:${local}:${value}`); 24 + return `${local}:${value}`; 25 + } finally { 26 + trace.push(`middle-finally:${local}`); 27 + } 28 + } 29 + 30 + async function top(label) { 31 + const local = `top-${label}`; 32 + trace.push(`top-enter:${local}`); 33 + try { 34 + const value = await middle(label); 35 + trace.push(`top-after:${local}:${value}`); 36 + return `${local}:${value}`; 37 + } finally { 38 + trace.push(`top-finally:${local}`); 39 + await Promise.resolve(local); 40 + trace.push(`top-finally-after-await:${local}`); 41 + } 42 + } 43 + 44 + const result = await top('x'); 45 + 46 + assertEq( 47 + result, 48 + 'top-x:middle-x:leaf-x:X', 49 + 'nested async result should preserve caller stack state' 50 + ); 51 + 52 + assertEq( 53 + trace.join(','), 54 + [ 55 + 'top-enter:top-x', 56 + 'middle-enter:middle-x', 57 + 'leaf-before:leaf-x', 58 + 'leaf-after:leaf-x:X', 59 + 'middle-after:middle-x:leaf-x:X', 60 + 'middle-finally:middle-x', 61 + 'top-after:top-x:middle-x:leaf-x:X', 62 + 'top-finally:top-x', 63 + 'top-finally-after-await:top-x', 64 + ].join(','), 65 + 'materialized slice should preserve frames, handlers, and locals above the await' 66 + ); 67 + 68 + console.log('nested async stack slice resumes with handlers and locals intact'); 69 + } 70 + 71 + main().catch((err) => { 72 + console.error(err && err.stack ? err.stack : String(err)); 73 + throw err; 74 + });
+39
tests/test_async_promise_job_resume.cjs
··· 1 + function assertEq(actual, expected, message) { 2 + if (actual !== expected) { 3 + throw new Error(`${message}: expected ${expected}, got ${actual}`); 4 + } 5 + } 6 + 7 + async function leaf(iteration) { 8 + const local = `leaf-${iteration}`; 9 + const payload = await Promise.resolve(iteration) 10 + .then((value) => Promise.resolve(value + 1)) 11 + .then((value) => ({ value, local })); 12 + return `${payload.local}:${payload.value}`; 13 + } 14 + 15 + async function middle(iteration) { 16 + const local = `middle-${iteration}`; 17 + const value = await Promise.resolve().then(() => leaf(iteration)); 18 + return `${local}:${value}`; 19 + } 20 + 21 + async function runOnce(iteration) { 22 + const local = `outer-${iteration}`; 23 + return `${local}:${await middle(iteration)}`; 24 + } 25 + 26 + async function main() { 27 + for (let i = 0; i < 128; i++) { 28 + const actual = await runOnce(i); 29 + const expected = `outer-${i}:middle-${i}:leaf-${i}:${i + 1}`; 30 + assertEq(actual, expected, `promise-job resume ${i}`); 31 + } 32 + 33 + console.log('promise-job async resumes stay stable across repeated microtask churn'); 34 + } 35 + 36 + main().catch((err) => { 37 + console.error(err && err.stack ? err.stack : String(err)); 38 + throw err; 39 + });