test_async_iterable.html (13088B)
1 <!-- Any copyright is dedicated to the Public Domain. 2 - http://creativecommons.org/publicdomain/zero/1.0/ --> 3 <!DOCTYPE HTML> 4 <html> 5 <head> 6 <title>Test Async Iterable Interface</title> 7 <script src="/tests/SimpleTest/SimpleTest.js"></script> 8 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> 9 </head> 10 <body> 11 <script class="testbody" type="application/javascript"> 12 13 add_task(async function init() { 14 await SpecialPowers.pushPrefEnv({set: [["dom.expose_test_interfaces", true]]}); 15 }); 16 17 const singleValues = Array(10).fill(0).map((_, i) => i * 9 % 7); 18 19 async function check_single_result_values(values, multiplier = 1) { 20 is(values.length, 10, `AsyncIterableSingle: should return 10 elements`); 21 for (let i = 0; i < 10; i++) { 22 let expected = singleValues[i] * multiplier; 23 is(values[i], expected, 24 `AsyncIterableSingle: should be ${expected}, get ${values[i]}`); 25 } 26 } 27 28 async function check_single_result(itr, multiplier = 1) { 29 let values = []; 30 for await (let v of itr) { 31 values.push(v); 32 } 33 check_single_result_values(values, multiplier); 34 } 35 36 async function test_data_single() { 37 info(`AsyncIterableSingle: Testing simple iterable creation and functionality`); 38 39 // eslint-disable-next-line no-undef 40 let itr = new TestInterfaceAsyncIterableSingle({ failToInit: true }); 41 let initFailed = false; 42 try { 43 itr.values(); 44 } catch (e) { 45 initFailed = true; 46 } 47 ok(initFailed, 48 "AsyncIterableSingle: A failure in asynchronous iterator initialization " + 49 "steps should propagate to the caller of the asynchronous iterator's " + 50 "constructor."); 51 52 // eslint-disable-next-line no-undef 53 itr = new TestInterfaceAsyncIterableSingle(); 54 is(itr.values, itr[Symbol.asyncIterator], 55 `AsyncIterableSingle: Should be using @@asyncIterator for 'values'`); 56 57 await check_single_result(itr); 58 await check_single_result(itr.values()); 59 60 // eslint-disable-next-line no-undef 61 itr = new TestInterfaceAsyncIterableSingleWithArgs(); 62 is(itr.values, itr[Symbol.asyncIterator], 63 `AsyncIterableSingleWithArgs: Should be using @@asyncIterator for 'values'`); 64 65 await check_single_result(itr, 1); 66 await check_single_result(itr.values({ multiplier: 2 }), 2); 67 68 // eslint-disable-next-line no-undef 69 itr = new TestInterfaceAsyncIterableSingle(); 70 let itrValues = itr.values(); 71 let values = []; 72 for (let i = 0; i < 10; ++i) { 73 values.push(itrValues.next()); 74 } 75 check_single_result_values(await Promise.all(values).then(v => v.map(w => w.value))); 76 77 // Test that there is only one ongoing promise at a time. 78 // Async iterables return a promise that is then resolved with the iterator 79 // value. We create an array of unresolved promises here, one promise for 80 // every result that we expect from the iterator. We pass that array of 81 // promises to the .value() method of the 82 // TestInterfaceAsyncIterableSingleWithArgs, and it will chain the resolving 83 // of each resulting iterator value on the corresponding promise from this 84 // array. We then resolve the promises in the array one by one in reverse 85 // order. This tries to make sure that the iterator always resolves the 86 // promises in the order of iteration. 87 let unblockers = []; 88 let blockingPromises = []; 89 for (let i = 0; i < 10; ++i) { 90 let unblocker; 91 let promise = new Promise((resolve) => { 92 unblocker = resolve; 93 }); 94 unblockers.push(unblocker); 95 blockingPromises.push(promise); 96 } 97 98 // eslint-disable-next-line no-undef 99 itr = new TestInterfaceAsyncIterableSingleWithArgs(); 100 itrValues = itr.values({ blockingPromises }); 101 values = []; 102 for (let i = 0; i < 10; ++i) { 103 values.push(itrValues.next()); 104 } 105 unblockers.reverse(); 106 for (let unblocker of unblockers) { 107 unblocker(); 108 } 109 110 check_single_result_values(await Promise.all(values).then(v => v.map(w => w.value))); 111 112 // eslint-disable-next-line no-undef 113 itr = new TestInterfaceAsyncIterableSingleWithArgs(); 114 115 let callCount = itr.returnCallCount; 116 117 let i = 0; 118 for await (let v of itr) { 119 if (++i > 1) { 120 break; 121 } 122 values.push(v); 123 } 124 125 is(itr.returnCallCount, callCount + 1, 126 `AsyncIterableSingle: breaking out of for-await-of loop should call "return"`); 127 is(itr.returnLastCalledWith, undefined, 128 `AsyncIterableSingleWithArgs: the asynchronous iterator return algorithm should be called with the argument that was passed in.`); 129 130 // eslint-disable-next-line no-undef 131 itr = new TestInterfaceAsyncIterableSingleWithArgs(); 132 133 async function * yieldFromIterator () { 134 yield * itr 135 } 136 137 let yieldingIterator = yieldFromIterator(); 138 139 let result = await yieldingIterator.next(); 140 is(result.value, singleValues[0], 141 `AsyncIterableSingle: should be ${singleValues[0]}, get ${result.value}`); 142 result = await yieldingIterator.next(); 143 is(result.value, singleValues[1], 144 `AsyncIterableSingle: should be ${singleValues[1]}, get ${result.value}`); 145 146 result = await yieldingIterator.return("abcd"); 147 is(typeof result, "object", 148 `AsyncIterableSingleWithArgs: "return("abcd")" should return { done: true, value: "abcd" }`); 149 is(result.done, true, 150 `AsyncIterableSingleWithArgs: "return("abcd")" should return { done: true, value: "abcd" }`); 151 is(result.value, "abcd", 152 `AsyncIterableSingleWithArgs: "return("abcd")" should return { done: true, value: "abcd" }`); 153 is(itr.returnLastCalledWith, "abcd", 154 `AsyncIterableSingleWithArgs: the asynchronous iterator return algorithm should be called with the argument that was passed in.`); 155 156 result = await yieldingIterator.return("efgh"); 157 is(typeof result, "object", 158 `AsyncIterableSingleWithArgs: "return("efgh")" should return { done: true, value: "efgh" }`); 159 is(result.done, true, 160 `AsyncIterableSingleWithArgs: "return("efgh")" should return { done: true, value: "efgh" }`); 161 is(result.value, "efgh", 162 `AsyncIterableSingleWithArgs: "return("efgh")" should return { done: true, value: "efgh" }`); 163 is(itr.returnLastCalledWith, "abcd", 164 `AsyncIterableSingleWithArgs: the asynchronous iterator return algorithm shouldn't be called if the iterator's 'is finished' flag is true already.`); 165 166 // eslint-disable-next-line no-undef 167 itr = new TestInterfaceAsyncIterableSingleWithArgs(); 168 itrValues = itr.values({ failNextAfter: 1 }); 169 await itrValues.next().then(({ value, done }) => { 170 is(value, singleValues[0], "First value is correct"); 171 ok(!done, "Expecting more values"); 172 return itrValues.next(); 173 }).then(() => { 174 ok(false, "Second call to next() should convert failure to a rejected promise."); 175 return itrValues.next(); 176 }).catch(() => { 177 ok(true, "Second call to next() should convert failure to a rejected promise."); 178 return itrValues.next(); 179 }).then(({ done }) => { 180 ok(done, "An earlier failure in next() should set the async iterator's 'is finished' flag to true."); 181 }).catch(() => { 182 ok(false, "An earlier failure in next() shouldn't cause subsequent calls to return a rejected promise."); 183 }); 184 185 // eslint-disable-next-line no-undef 186 itr = new TestInterfaceAsyncIterableSingleWithArgs(); 187 itrValues = itr.values({ throwFromNext: true }); 188 await itrValues.next().then(() => { 189 ok(false, "Should have rejected from the exception"); 190 }).catch(() => { 191 ok(true, "Should have rejected from the exception"); 192 }); 193 194 // eslint-disable-next-line no-undef 195 itr = new TestInterfaceAsyncIterableSingleWithArgs(); 196 itrValues = itr.values({ throwFromReturn: () => { throw new DOMException("Throw from return", "InvalidStateError"); } }); 197 await itrValues.return().then(() => { 198 ok(false, "Should have rejected from the exception"); 199 }).catch(() => { 200 ok(true, "Should have rejected from the exception"); 201 }); 202 } 203 204 async function test_data_double() { 205 info(`AsyncIterableDouble: Testing simple iterable creation and functionality`); 206 207 // eslint-disable-next-line no-undef 208 let itr = new TestInterfaceAsyncIterableDouble(); 209 is(itr.entries, itr[Symbol.asyncIterator], 210 `AsyncIterableDouble: Should be using @@asyncIterator for 'entries'`); 211 212 let elements = [["a", "b"], ["c", "d"], ["e", "f"]]; 213 let key_itr = itr.keys(); 214 let value_itr = itr.values(); 215 let entries_itr = itr.entries(); 216 let key = await key_itr.next(); 217 let value = await value_itr.next(); 218 let entry = await entries_itr.next(); 219 for (let i = 0; i < 3; ++i) { 220 is(key.value, elements[i][0], `AsyncIterableDouble: Key.value should be ${elements[i][0]}, got ${key.value}`); 221 is(key.done, false, `AsyncIterableDouble: Key.done should be false, got ${key.done}`); 222 is(value.value, elements[i][1], `AsyncIterableDouble: Value.value should be ${elements[i][1]}, got ${value.value}`); 223 is(value.done, false, `AsyncIterableDouble: Value.done should be false, got ${value.done}`); 224 is(entry.value[0], elements[i][0], `AsyncIterableDouble: Entry.value[0] should be ${elements[i][0]}, got ${entry.value[0]}`); 225 is(entry.value[1], elements[i][1], `AsyncIterableDouble: Entry.value[1] should be ${elements[i][1]}, got ${entry.value[1]}`); 226 is(entry.done, false, `AsyncIterableDouble: Entry.done should be false, got ${entry.done}`); 227 228 key = await key_itr.next(); 229 value = await value_itr.next(); 230 entry = await entries_itr.next(); 231 } 232 is(key.value, undefined, `AsyncIterableDouble: Key.value should be ${undefined}, got ${key.value}`); 233 is(key.done, true, `AsyncIterableDouble: Key.done should be true, got ${key.done}`); 234 is(value.value, undefined, `AsyncIterableDouble: Value.value should be ${undefined}, got ${value.value}`); 235 is(value.done, true, `AsyncIterableDouble: Value.done should be true, got ${value.done}`); 236 is(entry.value, undefined, `AsyncIterableDouble: Entry.value should be ${undefined}, got ${entry.value}`); 237 is(entry.done, true, `AsyncIterableDouble: Entry.done should be true, got ${entry.done}`); 238 239 let idx = 0; 240 for await (let [itrkey, itrvalue] of itr) { 241 is(itrkey, elements[idx][0], `AsyncIterableDouble: Looping at ${idx} should have key ${elements[idx][0]}, got ${key}`); 242 is(itrvalue, elements[idx][1], `AsyncIterableDouble: Looping at ${idx} should have value ${elements[idx][1]}, got ${value}`); 243 ++idx; 244 } 245 is(idx, 3, `AsyncIterableDouble: Should have 3 loops of for-await-of, got ${idx}`); 246 } 247 248 async function test_data_double_union() { 249 info(`AsyncIterableDoubleUnion: Testing simple iterable creation and functionality`); 250 251 // eslint-disable-next-line no-undef 252 let itr = new TestInterfaceAsyncIterableDoubleUnion(); 253 is(itr.entries, itr[Symbol.asyncIterator], 254 `AsyncIterableDoubleUnion: Should be using @@asyncIterator for 'entries'`); 255 256 let elements = [["long", 1], ["string", "a"]]; 257 let key_itr = itr.keys(); 258 let value_itr = itr.values(); 259 let entries_itr = itr.entries(); 260 let key = await key_itr.next(); 261 let value = await value_itr.next(); 262 let entry = await entries_itr.next(); 263 for (let i = 0; i < 2; ++i) { 264 is(key.value, elements[i][0], `AsyncIterableDoubleUnion: Key.value should be ${elements[i][0]}, got ${key.value}`); 265 is(key.done, false, `AsyncIterableDoubleUnion: Key.done should be false, got ${key.done}`); 266 is(value.value, elements[i][1], `AsyncIterableDoubleUnion: Value.value should be ${elements[i][1]}, got ${value.value}`); 267 is(value.done, false, `AsyncIterableDoubleUnion: Value.done should be false, got ${value.done}`); 268 is(entry.value[0], elements[i][0], `AsyncIterableDoubleUnion: Entry.value[0] should be ${elements[i][0]}, got ${entry.value[0]}`); 269 is(entry.value[1], elements[i][1], `AsyncIterableDoubleUnion: Entry.value[1] should be ${elements[i][1]}, got ${entry.value[1]}`); 270 is(entry.done, false, `AsyncIterableDoubleUnion: Entry.done should be false, got ${entry.done}`); 271 272 key = await key_itr.next(); 273 value = await value_itr.next(); 274 entry = await entries_itr.next(); 275 } 276 is(key.value, undefined, `AsyncIterableDoubleUnion: Key.value should be ${undefined}, got ${key.value}`); 277 is(key.done, true, `AsyncIterableDoubleUnion: Key.done should be true, got ${key.done}`); 278 is(value.value, undefined, `AsyncIterableDoubleUnion: Value.value should be ${undefined}, got ${value.value}`); 279 is(value.done, true, `AsyncIterableDoubleUnion: Value.done should be true, got ${value.done}`); 280 is(entry.value, undefined, `AsyncIterableDoubleUnion: Entry.value should be ${undefined}, got ${entry.value}`); 281 is(entry.done, true, `AsyncIterableDoubleUnion: Entry.done should be true, got ${entry.done}`); 282 283 let idx = 0; 284 for await (let [itrkey, itrvalue] of itr) { 285 is(itrkey, elements[idx][0], `AsyncIterableDoubleUnion: Looping at ${idx} should have key ${elements[idx][0]}, got ${key}`); 286 is(itrvalue, elements[idx][1], `AsyncIterableDoubleUnion: Looping at ${idx} should have value ${elements[idx][1]}, got ${value}`); 287 ++idx; 288 } 289 is(idx, 2, `AsyncIterableDoubleUnion: Should have 2 loops of for-await-of, got ${idx}`); 290 } 291 292 add_task(async function do_tests() { 293 await test_data_single(); 294 await test_data_double(); 295 await test_data_double_union(); 296 }); 297 298 </script> 299 </body> 300 </html>