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.

at master 270 lines 10 kB view raw
1import { test, testDeep, summary } from './helpers.js'; 2import { inspect } from 'node:util'; 3 4console.log('Async Iterator Tests\n'); 5 6test('AsyncIterator global exists', typeof AsyncIterator, 'function'); 7test('AsyncIterator prototype async iterator tag', AsyncIterator.prototype[Symbol.toStringTag], 'AsyncIterator'); 8 9async function* protoShapeAsyncGen() { 10 yield 1; 11} 12const AsyncGeneratorFunction = protoShapeAsyncGen.constructor; 13const protoShapeAsyncIter = protoShapeAsyncGen(); 14const ownAsyncGeneratorProto = Object.getPrototypeOf(protoShapeAsyncIter); 15const sharedAsyncGeneratorProto = Object.getPrototypeOf(ownAsyncGeneratorProto); 16const inheritedAsyncIteratorProto = Object.getPrototypeOf(sharedAsyncGeneratorProto); 17test('async generator function has prototype property', protoShapeAsyncGen.hasOwnProperty('prototype'), true); 18test('async generator function constructor name', AsyncGeneratorFunction.name, 'AsyncGeneratorFunction'); 19test('async generator function prototype chain', Object.getPrototypeOf(protoShapeAsyncGen), AsyncGeneratorFunction.prototype); 20test('async generator function prototype tag', AsyncGeneratorFunction.prototype[Symbol.toStringTag], 'AsyncGeneratorFunction'); 21test('async generator function inspect tag', inspect(protoShapeAsyncGen), '[AsyncGeneratorFunction: protoShapeAsyncGen]'); 22test('async generator function prototype prototype', AsyncGeneratorFunction.prototype.prototype, sharedAsyncGeneratorProto); 23test( 24 'new async generator function throws', 25 (() => { 26 try { 27 new protoShapeAsyncGen(); 28 return false; 29 } catch (err) { 30 return true; 31 } 32 })(), 33 true 34); 35test('async generator instance uses function prototype', ownAsyncGeneratorProto, protoShapeAsyncGen.prototype); 36test('async generator shared prototype tag', sharedAsyncGeneratorProto[Symbol.toStringTag], 'AsyncGenerator'); 37test('async generator shared prototype has next', sharedAsyncGeneratorProto.hasOwnProperty('next'), true); 38test('async generator shared prototype inherits AsyncIterator', inheritedAsyncIteratorProto, AsyncIterator.prototype); 39test('async generator Symbol.asyncIterator inherited from AsyncIterator', protoShapeAsyncIter[Symbol.asyncIterator](), protoShapeAsyncIter); 40test('async generator inherits AsyncIterator helpers', typeof protoShapeAsyncIter.map, 'function'); 41 42const dynamicAsyncGen = AsyncGeneratorFunction('yield 4;'); 43const dynamicAsyncIter = dynamicAsyncGen(); 44test('AsyncGeneratorFunction constructs async generator function', Object.getPrototypeOf(dynamicAsyncGen), AsyncGeneratorFunction.prototype); 45test('AsyncGeneratorFunction instance uses function prototype', Object.getPrototypeOf(dynamicAsyncIter), dynamicAsyncGen.prototype); 46test('AsyncGeneratorFunction yielded value', (await dynamicAsyncIter.next()).value, 4); 47test( 48 'strict async generator function expression has own prototype', 49 Function('"use strict"; return (async function* () {}).hasOwnProperty("prototype");')(), 50 true 51); 52 53class CustomAsyncIterator extends AsyncIterator { 54 constructor(values) { 55 super(); 56 this.values = values; 57 this.index = 0; 58 } 59 60 next() { 61 if (this.index >= this.values.length) return Promise.resolve({ done: true }); 62 return Promise.resolve({ done: false, value: this.values[this.index++] }); 63 } 64} 65 66const custom = new CustomAsyncIterator([4, 5]); 67test('class extends AsyncIterator', custom[Symbol.asyncIterator](), custom); 68test('class instance instanceof AsyncIterator', custom instanceof AsyncIterator, true); 69testDeep('custom async iterator toArray', await custom.toArray(), [4, 5]); 70 71const asyncSource = AsyncIterator.from( 72 (async function* () { 73 yield 1; 74 yield 2; 75 yield 3; 76 })() 77); 78test('AsyncIterator.from async iterable instanceof', asyncSource instanceof AsyncIterator, true); 79testDeep('AsyncIterator.from async iterable values', await asyncSource.toArray(), [1, 2, 3]); 80 81testDeep('AsyncIterator.from iterable values', await AsyncIterator.from([1, 2, 3]).toArray(), [1, 2, 3]); 82testDeep('AsyncIterator.from iterator values', await AsyncIterator.from([1, 2, 3].values()).toArray(), [1, 2, 3]); 83 84const promisedStep = await AsyncIterator.from([Promise.resolve(7)]).next(); 85test('AsyncIterator.from sync iterable awaits promised value', promisedStep.value, 7); 86test('AsyncIterator.from sync iterable promised value done', promisedStep.done, false); 87 88let rejectedValueCaught; 89try { 90 await AsyncIterator.from([Promise.reject(new Error('sync value rejection'))]).next(); 91} catch (e) { 92 rejectedValueCaught = e; 93} 94test('AsyncIterator.from sync iterable rejects promised value', rejectedValueCaught.message, 'sync value rejection'); 95 96const syncReturnSource = { 97 next() { 98 return { done: false, value: 0 }; 99 }, 100 return() { 101 return { done: true, value: Promise.resolve(7) }; 102 }, 103 [Symbol.iterator]() { 104 return this; 105 } 106}; 107test('AsyncIterator.from sync return awaits value', (await AsyncIterator.from(syncReturnSource).return()).value, 7); 108 109const syncThrowSource = { 110 next() { 111 return { done: false, value: 0 }; 112 }, 113 throw() { 114 return { done: true, value: Promise.resolve(8) }; 115 }, 116 [Symbol.iterator]() { 117 return this; 118 } 119}; 120test('AsyncIterator.from sync throw awaits value', (await AsyncIterator.from(syncThrowSource).throw(new Error('x'))).value, 8); 121 122testDeep( 123 'async iterator map', 124 await AsyncIterator.from([1, 2, 3]) 125 .map(x => x * x) 126 .toArray(), 127 [1, 4, 9] 128); 129testDeep( 130 'async iterator map awaits callback', 131 await AsyncIterator.from([1, 2]) 132 .map(async x => x + 1) 133 .toArray(), 134 [2, 3] 135); 136testDeep( 137 'async iterator filter', 138 await AsyncIterator.from([1, 2, 3, 4]) 139 .filter(x => x % 2) 140 .toArray(), 141 [1, 3] 142); 143testDeep( 144 'async iterator filter awaits callback', 145 await AsyncIterator.from([1, 2, 3, 4]) 146 .filter(async x => x > 2) 147 .toArray(), 148 [3, 4] 149); 150testDeep('async iterator take', await AsyncIterator.from([1, 2, 3]).take(2).toArray(), [1, 2]); 151testDeep('async iterator drop', await AsyncIterator.from([1, 2, 3]).drop(1).toArray(), [2, 3]); 152testDeep( 153 'async iterator flatMap sync inner', 154 await AsyncIterator.from([1, 2, 3]) 155 .flatMap(x => [x, 0]) 156 .toArray(), 157 [1, 0, 2, 0, 3, 0] 158); 159testDeep( 160 'async iterator flatMap awaits callback', 161 await AsyncIterator.from([1, 2]) 162 .flatMap(async x => [x, x + 10]) 163 .toArray(), 164 [1, 11, 2, 12] 165); 166 167async function* doubleAsync(x) { 168 yield x; 169 yield x * 2; 170} 171 172testDeep('async iterator flatMap async inner', await AsyncIterator.from([2, 3]).flatMap(doubleAsync).toArray(), [2, 4, 3, 6]); 173 174test('async iterator every true', await AsyncIterator.from([1, 2, 3]).every(x => typeof x === 'number'), true); 175test('async iterator every awaits callback', await AsyncIterator.from([1, 2, 3]).every(async x => x < 4), true); 176test('async iterator some true', await AsyncIterator.from([1, 2, 3]).some(x => x === 2), true); 177test('async iterator some awaits callback', await AsyncIterator.from([1, 2, 3]).some(async x => x === 3), true); 178test('async iterator find', await AsyncIterator.from([1, 2, 3]).find(x => x > 1), 2); 179test('async iterator find awaits callback', await AsyncIterator.from([1, 2, 3]).find(async x => x > 2), 3); 180 181function closableAsyncIterator(log) { 182 return { 183 index: 0, 184 next() { 185 return Promise.resolve({ done: false, value: ++this.index }); 186 }, 187 return() { 188 log.push('closed'); 189 return Promise.resolve({ done: true }); 190 }, 191 [Symbol.asyncIterator]() { 192 return this; 193 } 194 }; 195} 196 197let everyCloseLog = []; 198test('async iterator every closes on false', await AsyncIterator.from(closableAsyncIterator(everyCloseLog)).every(x => x < 1), false); 199testDeep('async iterator every close log', everyCloseLog, ['closed']); 200 201let someCloseLog = []; 202test('async iterator some closes on match', await AsyncIterator.from(closableAsyncIterator(someCloseLog)).some(x => x === 1), true); 203testDeep('async iterator some close log', someCloseLog, ['closed']); 204 205let findCloseLog = []; 206test('async iterator find closes on match', await AsyncIterator.from(closableAsyncIterator(findCloseLog)).find(x => x === 1), 1); 207testDeep('async iterator find close log', findCloseLog, ['closed']); 208 209let throwCloseLog = []; 210let throwCaught = false; 211try { 212 await AsyncIterator.from(closableAsyncIterator(throwCloseLog)).some(() => { 213 throw new Error('sync callback failure'); 214 }); 215} catch (err) { 216 throwCaught = err.message === 'sync callback failure'; 217} 218test('async iterator closes on sync callback throw', throwCaught, true); 219testDeep('async iterator sync callback throw close log', throwCloseLog, ['closed']); 220 221let rejectCloseLog = []; 222let rejectCaught = false; 223try { 224 await AsyncIterator.from(closableAsyncIterator(rejectCloseLog)).find(async () => { 225 throw new Error('async callback failure'); 226 }); 227} catch (err) { 228 rejectCaught = err.message === 'async callback failure'; 229} 230test('async iterator closes on async callback rejection', rejectCaught, true); 231testDeep('async iterator async callback rejection close log', rejectCloseLog, ['closed']); 232 233const syncAsyncLike = { 234 index: 0, 235 next() { 236 if (this.index >= 5000) return { done: true }; 237 return { done: false, value: this.index++ }; 238 }, 239 [Symbol.asyncIterator]() { 240 return this; 241 } 242}; 243test('async iterator handles sync next results without recursive stack growth', (await AsyncIterator.from(syncAsyncLike).toArray()).length, 5000); 244 245let forEachSum = 0; 246await AsyncIterator.from([1, 2, 3]).forEach(x => { 247 forEachSum += x; 248}); 249test('async iterator forEach', forEachSum, 6); 250 251let asyncForEachSum = 0; 252await AsyncIterator.from([1, 2, 3]).forEach(async x => { 253 asyncForEachSum += x; 254}); 255test('async iterator forEach awaits callback', asyncForEachSum, 6); 256 257test('async iterator reduce', await AsyncIterator.from([1, 2, 3]).reduce((a, b) => a + b), 6); 258test('async iterator reduce awaits callback', await AsyncIterator.from([1, 2, 3]).reduce(async (a, b) => a + b), 6); 259testDeep( 260 'async generator helpers inherited', 261 await (async function* () { 262 yield 2; 263 yield 4; 264 })() 265 .map(x => x + 1) 266 .toArray(), 267 [3, 5] 268); 269 270summary();