errors.any.js (13372B)
1 // META: global=window,worker,shadowrealm 2 // META: script=../resources/test-utils.js 3 'use strict'; 4 5 const thrownError = new Error('bad things are happening!'); 6 thrownError.name = 'error1'; 7 8 promise_test(t => { 9 const ts = new TransformStream({ 10 transform() { 11 throw thrownError; 12 }, 13 cancel: t.unreached_func('cancel should not be called') 14 }); 15 16 const reader = ts.readable.getReader(); 17 18 const writer = ts.writable.getWriter(); 19 20 return Promise.all([ 21 promise_rejects_exactly(t, thrownError, writer.write('a'), 22 'writable\'s write should reject with the thrown error'), 23 promise_rejects_exactly(t, thrownError, reader.read(), 24 'readable\'s read should reject with the thrown error'), 25 promise_rejects_exactly(t, thrownError, reader.closed, 26 'readable\'s closed should be rejected with the thrown error'), 27 promise_rejects_exactly(t, thrownError, writer.closed, 28 'writable\'s closed should be rejected with the thrown error') 29 ]); 30 }, 'TransformStream errors thrown in transform put the writable and readable in an errored state'); 31 32 promise_test(t => { 33 const ts = new TransformStream({ 34 transform() { 35 }, 36 flush() { 37 throw thrownError; 38 }, 39 cancel: t.unreached_func('cancel should not be called') 40 }); 41 42 const reader = ts.readable.getReader(); 43 44 const writer = ts.writable.getWriter(); 45 46 return Promise.all([ 47 writer.write('a'), 48 promise_rejects_exactly(t, thrownError, writer.close(), 49 'writable\'s close should reject with the thrown error'), 50 promise_rejects_exactly(t, thrownError, reader.read(), 51 'readable\'s read should reject with the thrown error'), 52 promise_rejects_exactly(t, thrownError, reader.closed, 53 'readable\'s closed should be rejected with the thrown error'), 54 promise_rejects_exactly(t, thrownError, writer.closed, 55 'writable\'s closed should be rejected with the thrown error') 56 ]); 57 }, 'TransformStream errors thrown in flush put the writable and readable in an errored state'); 58 59 test(t => { 60 new TransformStream({ 61 start(c) { 62 c.enqueue('a'); 63 c.error(new Error('generic error')); 64 assert_throws_js(TypeError, () => c.enqueue('b'), 'enqueue() should throw'); 65 }, 66 cancel: t.unreached_func('cancel should not be called') 67 }); 68 }, 'errored TransformStream should not enqueue new chunks'); 69 70 promise_test(t => { 71 const ts = new TransformStream({ 72 start() { 73 return flushAsyncEvents().then(() => { 74 throw thrownError; 75 }); 76 }, 77 transform: t.unreached_func('transform should not be called'), 78 flush: t.unreached_func('flush should not be called'), 79 cancel: t.unreached_func('cancel should not be called') 80 }); 81 82 const writer = ts.writable.getWriter(); 83 const reader = ts.readable.getReader(); 84 return Promise.all([ 85 promise_rejects_exactly(t, thrownError, writer.write('a'), 'writer should reject with thrownError'), 86 promise_rejects_exactly(t, thrownError, writer.close(), 'close() should reject with thrownError'), 87 promise_rejects_exactly(t, thrownError, reader.read(), 'reader should reject with thrownError') 88 ]); 89 }, 'TransformStream transformer.start() rejected promise should error the stream'); 90 91 promise_test(t => { 92 const controllerError = new Error('start failure'); 93 controllerError.name = 'controllerError'; 94 const ts = new TransformStream({ 95 start(c) { 96 return flushAsyncEvents() 97 .then(() => { 98 c.error(controllerError); 99 throw new Error('ignored error'); 100 }); 101 }, 102 transform: t.unreached_func('transform should never be called if start() fails'), 103 flush: t.unreached_func('flush should never be called if start() fails'), 104 cancel: t.unreached_func('cancel should never be called if start() fails') 105 }); 106 107 const writer = ts.writable.getWriter(); 108 const reader = ts.readable.getReader(); 109 return Promise.all([ 110 promise_rejects_exactly(t, controllerError, writer.write('a'), 'writer should reject with controllerError'), 111 promise_rejects_exactly(t, controllerError, writer.close(), 'close should reject with same error'), 112 promise_rejects_exactly(t, controllerError, reader.read(), 'reader should reject with same error') 113 ]); 114 }, 'when controller.error is followed by a rejection, the error reason should come from controller.error'); 115 116 test(() => { 117 assert_throws_js(URIError, () => new TransformStream({ 118 start() { throw new URIError('start thrown error'); }, 119 transform() {} 120 }), 'constructor should throw'); 121 }, 'TransformStream constructor should throw when start does'); 122 123 test(() => { 124 const strategy = { 125 size() { throw new URIError('size thrown error'); } 126 }; 127 128 assert_throws_js(URIError, () => new TransformStream({ 129 start(c) { 130 c.enqueue('a'); 131 }, 132 transform() {} 133 }, undefined, strategy), 'constructor should throw the same error strategy.size throws'); 134 }, 'when strategy.size throws inside start(), the constructor should throw the same error'); 135 136 test(() => { 137 const controllerError = new URIError('controller.error'); 138 139 let controller; 140 const strategy = { 141 size() { 142 controller.error(controllerError); 143 throw new Error('redundant error'); 144 } 145 }; 146 147 assert_throws_js(URIError, () => new TransformStream({ 148 start(c) { 149 controller = c; 150 c.enqueue('a'); 151 }, 152 transform() {} 153 }, undefined, strategy), 'the first error should be thrown'); 154 }, 'when strategy.size calls controller.error() then throws, the constructor should throw the first error'); 155 156 promise_test(t => { 157 const ts = new TransformStream(); 158 const writer = ts.writable.getWriter(); 159 const closedPromise = writer.closed; 160 return Promise.all([ 161 ts.readable.cancel(thrownError), 162 promise_rejects_exactly(t, thrownError, closedPromise, 'closed should throw a TypeError') 163 ]); 164 }, 'cancelling the readable side should error the writable'); 165 166 promise_test(t => { 167 let controller; 168 const ts = new TransformStream({ 169 start(c) { 170 controller = c; 171 } 172 }); 173 const writer = ts.writable.getWriter(); 174 const reader = ts.readable.getReader(); 175 const writePromise = writer.write('a'); 176 const closePromise = writer.close(); 177 controller.error(thrownError); 178 return Promise.all([ 179 promise_rejects_exactly(t, thrownError, reader.closed, 'reader.closed should reject'), 180 promise_rejects_exactly(t, thrownError, writePromise, 'writePromise should reject'), 181 promise_rejects_exactly(t, thrownError, closePromise, 'closePromise should reject')]); 182 }, 'it should be possible to error the readable between close requested and complete'); 183 184 promise_test(t => { 185 const ts = new TransformStream({ 186 transform(chunk, controller) { 187 controller.enqueue(chunk); 188 controller.terminate(); 189 throw thrownError; 190 } 191 }, undefined, { highWaterMark: 1 }); 192 const writePromise = ts.writable.getWriter().write('a'); 193 const closedPromise = ts.readable.getReader().closed; 194 return Promise.all([ 195 promise_rejects_exactly(t, thrownError, writePromise, 'write() should reject'), 196 promise_rejects_exactly(t, thrownError, closedPromise, 'reader.closed should reject') 197 ]); 198 }, 'an exception from transform() should error the stream if terminate has been requested but not completed'); 199 200 promise_test(t => { 201 const ts = new TransformStream(); 202 const writer = ts.writable.getWriter(); 203 // The microtask following transformer.start() hasn't completed yet, so the abort is queued and not notified to the 204 // TransformStream yet. 205 const abortPromise = writer.abort(thrownError); 206 const cancelPromise = ts.readable.cancel(new Error('cancel reason')); 207 return Promise.all([ 208 abortPromise, 209 cancelPromise, 210 promise_rejects_exactly(t, thrownError, writer.closed, 'writer.closed should reject'), 211 ]); 212 }, 'abort should set the close reason for the writable when it happens before cancel during start, and cancel should ' + 213 'reject'); 214 215 promise_test(t => { 216 let resolveTransform; 217 const transformPromise = new Promise(resolve => { 218 resolveTransform = resolve; 219 }); 220 const ts = new TransformStream({ 221 transform() { 222 return transformPromise; 223 } 224 }, undefined, { highWaterMark: 2 }); 225 const writer = ts.writable.getWriter(); 226 return delay(0).then(() => { 227 const writePromise = writer.write(); 228 const abortPromise = writer.abort(thrownError); 229 const cancelPromise = ts.readable.cancel(new Error('cancel reason')); 230 resolveTransform(); 231 return Promise.all([ 232 writePromise, 233 abortPromise, 234 cancelPromise, 235 promise_rejects_exactly(t, thrownError, writer.closed, 'writer.closed should reject with thrownError')]); 236 }); 237 }, 'abort should set the close reason for the writable when it happens before cancel during underlying sink write, ' + 238 'but cancel should still succeed'); 239 240 const ignoredError = new Error('ignoredError'); 241 ignoredError.name = 'ignoredError'; 242 243 promise_test(t => { 244 const ts = new TransformStream({ 245 start(controller) { 246 controller.error(thrownError); 247 controller.error(ignoredError); 248 } 249 }); 250 return promise_rejects_exactly(t, thrownError, ts.writable.abort(), 'abort() should reject with thrownError'); 251 }, 'controller.error() should do nothing the second time it is called'); 252 253 promise_test(t => { 254 let controller; 255 const ts = new TransformStream({ 256 start(c) { 257 controller = c; 258 } 259 }); 260 const cancelPromise = ts.readable.cancel(ignoredError); 261 controller.error(thrownError); 262 return Promise.all([ 263 cancelPromise, 264 promise_rejects_exactly(t, thrownError, ts.writable.getWriter().closed, 'closed should reject with thrownError') 265 ]); 266 }, 'controller.error() should close writable immediately after readable.cancel()'); 267 268 promise_test(t => { 269 let controller; 270 const ts = new TransformStream({ 271 start(c) { 272 controller = c; 273 } 274 }); 275 return ts.readable.cancel(thrownError).then(() => { 276 controller.error(ignoredError); 277 return promise_rejects_exactly(t, thrownError, ts.writable.getWriter().closed, 'closed should reject with thrownError'); 278 }); 279 }, 'controller.error() should do nothing after readable.cancel() resolves'); 280 281 promise_test(t => { 282 let controller; 283 const ts = new TransformStream({ 284 start(c) { 285 controller = c; 286 } 287 }); 288 return ts.writable.abort(thrownError).then(() => { 289 controller.error(ignoredError); 290 return promise_rejects_exactly(t, thrownError, ts.writable.getWriter().closed, 'closed should reject with thrownError'); 291 }); 292 }, 'controller.error() should do nothing after writable.abort() has completed'); 293 294 promise_test(t => { 295 let controller; 296 const ts = new TransformStream({ 297 start(c) { 298 controller = c; 299 }, 300 transform() { 301 throw thrownError; 302 } 303 }, undefined, { highWaterMark: Infinity }); 304 const writer = ts.writable.getWriter(); 305 return promise_rejects_exactly(t, thrownError, writer.write(), 'write() should reject').then(() => { 306 controller.error(); 307 return promise_rejects_exactly(t, thrownError, writer.closed, 'closed should reject with thrownError'); 308 }); 309 }, 'controller.error() should do nothing after a transformer method has thrown an exception'); 310 311 promise_test(t => { 312 let controller; 313 let calls = 0; 314 const ts = new TransformStream({ 315 start(c) { 316 controller = c; 317 }, 318 transform() { 319 ++calls; 320 } 321 }, undefined, { highWaterMark: 1 }); 322 return delay(0).then(() => { 323 // Create backpressure. 324 controller.enqueue('a'); 325 const writer = ts.writable.getWriter(); 326 // transform() will not be called until backpressure is relieved. 327 const writePromise = writer.write('b'); 328 assert_equals(calls, 0, 'transform() should not have been called'); 329 controller.error(thrownError); 330 // Now backpressure has been relieved and the write can proceed. 331 return promise_rejects_exactly(t, thrownError, writePromise, 'write() should reject').then(() => { 332 assert_equals(calls, 0, 'transform() should not be called'); 333 }); 334 }); 335 }, 'erroring during write with backpressure should result in the write failing'); 336 337 promise_test(t => { 338 const ts = new TransformStream({}, undefined, { highWaterMark: 0 }); 339 return delay(0).then(() => { 340 const writer = ts.writable.getWriter(); 341 // write should start synchronously 342 const writePromise = writer.write(0); 343 // The underlying sink's abort() is not called until the write() completes. 344 const abortPromise = writer.abort(thrownError); 345 // Perform a read to relieve backpressure and permit the write() to complete. 346 const readPromise = ts.readable.getReader().read(); 347 return Promise.all([ 348 promise_rejects_exactly(t, thrownError, readPromise, 'read() should reject'), 349 promise_rejects_exactly(t, thrownError, writePromise, 'write() should reject'), 350 abortPromise 351 ]); 352 }); 353 }, 'a write() that was waiting for backpressure should reject if the writable is aborted'); 354 355 promise_test(t => { 356 const ts = new TransformStream(); 357 ts.writable.abort(thrownError); 358 const reader = ts.readable.getReader(); 359 return promise_rejects_exactly(t, thrownError, reader.read(), 'read() should reject with thrownError'); 360 }, 'the readable should be errored with the reason passed to the writable abort() method');