1// META: global=window,worker 2// META: script=../resources/test-utils.js 3// META: script=../resources/recording-streams.js 4'use strict'; 5 6const error1 = new Error('error1!'); 7error1.name = 'error1'; 8 9const error2 = new Error('error2!'); 10error2.name = 'error2'; 11 12promise_test(t => { 13 14 const rs = recordingReadableStream(); 15 16 const ws = recordingWritableStream({ 17 start() { 18 return Promise.reject(error1); 19 } 20 }); 21 22 return promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error') 23 .then(() => { 24 assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]); 25 assert_array_equals(ws.events, []); 26 }); 27 28}, 'Errors must be propagated backward: starts errored; preventCancel omitted; fulfilled cancel promise'); 29 30promise_test(t => { 31 32 const rs = recordingReadableStream(); 33 34 const ws = recordingWritableStream({ 35 write() { 36 return Promise.reject(error1); 37 } 38 }); 39 40 const writer = ws.getWriter(); 41 42 return promise_rejects_exactly(t, error1, writer.write('Hello'), 'writer.write() must reject with the write error') 43 .then(() => promise_rejects_exactly(t, error1, writer.closed, 'writer.closed must reject with the write error')) 44 .then(() => { 45 writer.releaseLock(); 46 47 return promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the write error') 48 .then(() => { 49 assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]); 50 assert_array_equals(ws.events, ['write', 'Hello']); 51 }); 52 }); 53 54}, 'Errors must be propagated backward: becomes errored before piping due to write; preventCancel omitted; ' + 55 'fulfilled cancel promise'); 56 57promise_test(t => { 58 59 const rs = recordingReadableStream({ 60 cancel() { 61 throw error2; 62 } 63 }); 64 65 const ws = recordingWritableStream({ 66 write() { 67 return Promise.reject(error1); 68 } 69 }); 70 71 const writer = ws.getWriter(); 72 73 return promise_rejects_exactly(t, error1, writer.write('Hello'), 'writer.write() must reject with the write error') 74 .then(() => promise_rejects_exactly(t, error1, writer.closed, 'writer.closed must reject with the write error')) 75 .then(() => { 76 writer.releaseLock(); 77 78 return promise_rejects_exactly(t, error2, rs.pipeTo(ws), 'pipeTo must reject with the cancel error') 79 .then(() => { 80 assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]); 81 assert_array_equals(ws.events, ['write', 'Hello']); 82 }); 83 }); 84 85}, 'Errors must be propagated backward: becomes errored before piping due to write; preventCancel omitted; rejected ' + 86 'cancel promise'); 87 88for (const falsy of [undefined, null, false, +0, -0, NaN, '']) { 89 const stringVersion = Object.is(falsy, -0) ? '-0' : String(falsy); 90 91 promise_test(t => { 92 93 const rs = recordingReadableStream(); 94 95 const ws = recordingWritableStream({ 96 write() { 97 return Promise.reject(error1); 98 } 99 }); 100 101 const writer = ws.getWriter(); 102 103 return promise_rejects_exactly(t, error1, writer.write('Hello'), 'writer.write() must reject with the write error') 104 .then(() => promise_rejects_exactly(t, error1, writer.closed, 'writer.closed must reject with the write error')) 105 .then(() => { 106 writer.releaseLock(); 107 108 return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: falsy }), 109 'pipeTo must reject with the write error') 110 .then(() => { 111 assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]); 112 assert_array_equals(ws.events, ['write', 'Hello']); 113 }); 114 }); 115 116 }, `Errors must be propagated backward: becomes errored before piping due to write; preventCancel = ` + 117 `${stringVersion} (falsy); fulfilled cancel promise`); 118} 119 120for (const truthy of [true, 'a', 1, Symbol(), { }]) { 121 promise_test(t => { 122 123 const rs = recordingReadableStream(); 124 125 const ws = recordingWritableStream({ 126 write() { 127 return Promise.reject(error1); 128 } 129 }); 130 131 const writer = ws.getWriter(); 132 133 return promise_rejects_exactly(t, error1, writer.write('Hello'), 'writer.write() must reject with the write error') 134 .then(() => promise_rejects_exactly(t, error1, writer.closed, 'writer.closed must reject with the write error')) 135 .then(() => { 136 writer.releaseLock(); 137 138 return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: truthy }), 139 'pipeTo must reject with the write error') 140 .then(() => { 141 assert_array_equals(rs.eventsWithoutPulls, []); 142 assert_array_equals(ws.events, ['write', 'Hello']); 143 }); 144 }); 145 146 }, `Errors must be propagated backward: becomes errored before piping due to write; preventCancel = ` + 147 `${String(truthy)} (truthy)`); 148} 149 150promise_test(t => { 151 152 const rs = recordingReadableStream(); 153 154 const ws = recordingWritableStream({ 155 write() { 156 return Promise.reject(error1); 157 } 158 }); 159 160 const writer = ws.getWriter(); 161 162 return promise_rejects_exactly(t, error1, writer.write('Hello'), 'writer.write() must reject with the write error') 163 .then(() => promise_rejects_exactly(t, error1, writer.closed, 'writer.closed must reject with the write error')) 164 .then(() => { 165 writer.releaseLock(); 166 167 return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: true, preventAbort: true }), 168 'pipeTo must reject with the write error') 169 .then(() => { 170 assert_array_equals(rs.eventsWithoutPulls, []); 171 assert_array_equals(ws.events, ['write', 'Hello']); 172 }); 173 }); 174 175}, 'Errors must be propagated backward: becomes errored before piping due to write, preventCancel = true; ' + 176 'preventAbort = true'); 177 178promise_test(t => { 179 180 const rs = recordingReadableStream(); 181 182 const ws = recordingWritableStream({ 183 write() { 184 return Promise.reject(error1); 185 } 186 }); 187 188 const writer = ws.getWriter(); 189 190 return promise_rejects_exactly(t, error1, writer.write('Hello'), 'writer.write() must reject with the write error') 191 .then(() => promise_rejects_exactly(t, error1, writer.closed, 'writer.closed must reject with the write error')) 192 .then(() => { 193 writer.releaseLock(); 194 195 return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: true, preventAbort: true, preventClose: true }), 196 'pipeTo must reject with the write error') 197 .then(() => { 198 assert_array_equals(rs.eventsWithoutPulls, []); 199 assert_array_equals(ws.events, ['write', 'Hello']); 200 }); 201 }); 202 203}, 'Errors must be propagated backward: becomes errored before piping due to write; preventCancel = true, ' + 204 'preventAbort = true, preventClose = true'); 205 206promise_test(t => { 207 208 const rs = recordingReadableStream({ 209 start(controller) { 210 controller.enqueue('Hello'); 211 } 212 }); 213 214 const ws = recordingWritableStream({ 215 write() { 216 throw error1; 217 } 218 }); 219 220 return promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error').then(() => { 221 assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]); 222 assert_array_equals(ws.events, ['write', 'Hello']); 223 }); 224 225}, 'Errors must be propagated backward: becomes errored during piping due to write; preventCancel omitted; fulfilled ' + 226 'cancel promise'); 227 228promise_test(t => { 229 230 const rs = recordingReadableStream({ 231 start(controller) { 232 controller.enqueue('Hello'); 233 }, 234 cancel() { 235 throw error2; 236 } 237 }); 238 239 const ws = recordingWritableStream({ 240 write() { 241 throw error1; 242 } 243 }); 244 245 return promise_rejects_exactly(t, error2, rs.pipeTo(ws), 'pipeTo must reject with the cancel error').then(() => { 246 assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]); 247 assert_array_equals(ws.events, ['write', 'Hello']); 248 }); 249 250}, 'Errors must be propagated backward: becomes errored during piping due to write; preventCancel omitted; rejected ' + 251 'cancel promise'); 252 253promise_test(t => { 254 255 const rs = recordingReadableStream({ 256 start(controller) { 257 controller.enqueue('Hello'); 258 } 259 }); 260 261 const ws = recordingWritableStream({ 262 write() { 263 throw error1; 264 } 265 }); 266 267 return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: true }), 'pipeTo must reject with the same error') 268 .then(() => { 269 assert_array_equals(rs.eventsWithoutPulls, []); 270 assert_array_equals(ws.events, ['write', 'Hello']); 271 }); 272 273}, 'Errors must be propagated backward: becomes errored during piping due to write; preventCancel = true'); 274 275promise_test(t => { 276 277 const rs = recordingReadableStream({ 278 start(controller) { 279 controller.enqueue('a'); 280 controller.enqueue('b'); 281 controller.enqueue('c'); 282 } 283 }); 284 285 const ws = recordingWritableStream({ 286 write() { 287 if (ws.events.length > 2) { 288 return delay(0).then(() => { 289 throw error1; 290 }); 291 } 292 return undefined; 293 } 294 }); 295 296 return promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error').then(() => { 297 assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]); 298 assert_array_equals(ws.events, ['write', 'a', 'write', 'b']); 299 }); 300 301}, 'Errors must be propagated backward: becomes errored during piping due to write, but async; preventCancel = ' + 302 'false; fulfilled cancel promise'); 303 304promise_test(t => { 305 306 const rs = recordingReadableStream({ 307 start(controller) { 308 controller.enqueue('a'); 309 controller.enqueue('b'); 310 controller.enqueue('c'); 311 }, 312 cancel() { 313 throw error2; 314 } 315 }); 316 317 const ws = recordingWritableStream({ 318 write() { 319 if (ws.events.length > 2) { 320 return delay(0).then(() => { 321 throw error1; 322 }); 323 } 324 return undefined; 325 } 326 }); 327 328 return promise_rejects_exactly(t, error2, rs.pipeTo(ws), 'pipeTo must reject with the cancel error').then(() => { 329 assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]); 330 assert_array_equals(ws.events, ['write', 'a', 'write', 'b']); 331 }); 332 333}, 'Errors must be propagated backward: becomes errored during piping due to write, but async; preventCancel = ' + 334 'false; rejected cancel promise'); 335 336promise_test(t => { 337 338 const rs = recordingReadableStream({ 339 start(controller) { 340 controller.enqueue('a'); 341 controller.enqueue('b'); 342 controller.enqueue('c'); 343 } 344 }); 345 346 const ws = recordingWritableStream({ 347 write() { 348 if (ws.events.length > 2) { 349 return delay(0).then(() => { 350 throw error1; 351 }); 352 } 353 return undefined; 354 } 355 }); 356 357 return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: true }), 'pipeTo must reject with the same error') 358 .then(() => { 359 assert_array_equals(rs.eventsWithoutPulls, []); 360 assert_array_equals(ws.events, ['write', 'a', 'write', 'b']); 361 }); 362 363}, 'Errors must be propagated backward: becomes errored during piping due to write, but async; preventCancel = true'); 364 365promise_test(t => { 366 367 const rs = recordingReadableStream(); 368 369 const ws = recordingWritableStream(); 370 371 const pipePromise = promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error'); 372 373 t.step_timeout(() => ws.controller.error(error1), 10); 374 375 return pipePromise.then(() => { 376 assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]); 377 assert_array_equals(ws.events, []); 378 }); 379 380}, 'Errors must be propagated backward: becomes errored after piping; preventCancel omitted; fulfilled cancel promise'); 381 382promise_test(t => { 383 384 const rs = recordingReadableStream({ 385 cancel() { 386 throw error2; 387 } 388 }); 389 390 const ws = recordingWritableStream(); 391 392 const pipePromise = promise_rejects_exactly(t, error2, rs.pipeTo(ws), 'pipeTo must reject with the cancel error'); 393 394 t.step_timeout(() => ws.controller.error(error1), 10); 395 396 return pipePromise.then(() => { 397 assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]); 398 assert_array_equals(ws.events, []); 399 }); 400 401}, 'Errors must be propagated backward: becomes errored after piping; preventCancel omitted; rejected cancel promise'); 402 403promise_test(t => { 404 405 const rs = recordingReadableStream(); 406 407 const ws = recordingWritableStream(); 408 409 const pipePromise = promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: true }), 410 'pipeTo must reject with the same error'); 411 412 t.step_timeout(() => ws.controller.error(error1), 10); 413 414 return pipePromise.then(() => { 415 assert_array_equals(rs.eventsWithoutPulls, []); 416 assert_array_equals(ws.events, []); 417 }); 418 419}, 'Errors must be propagated backward: becomes errored after piping; preventCancel = true'); 420 421promise_test(t => { 422 423 const rs = recordingReadableStream({ 424 start(controller) { 425 controller.enqueue('a'); 426 controller.enqueue('b'); 427 controller.enqueue('c'); 428 controller.close(); 429 } 430 }); 431 432 const ws = recordingWritableStream({ 433 write(chunk) { 434 if (chunk === 'c') { 435 return Promise.reject(error1); 436 } 437 return undefined; 438 } 439 }); 440 441 return promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error').then(() => { 442 assert_array_equals(rs.eventsWithoutPulls, []); 443 assert_array_equals(ws.events, ['write', 'a', 'write', 'b', 'write', 'c']); 444 }); 445 446}, 'Errors must be propagated backward: becomes errored after piping due to last write; source is closed; ' + 447 'preventCancel omitted (but cancel is never called)'); 448 449promise_test(t => { 450 451 const rs = recordingReadableStream({ 452 start(controller) { 453 controller.enqueue('a'); 454 controller.enqueue('b'); 455 controller.enqueue('c'); 456 controller.close(); 457 } 458 }); 459 460 const ws = recordingWritableStream({ 461 write(chunk) { 462 if (chunk === 'c') { 463 return Promise.reject(error1); 464 } 465 return undefined; 466 } 467 }); 468 469 return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: true }), 'pipeTo must reject with the same error') 470 .then(() => { 471 assert_array_equals(rs.eventsWithoutPulls, []); 472 assert_array_equals(ws.events, ['write', 'a', 'write', 'b', 'write', 'c']); 473 }); 474 475}, 'Errors must be propagated backward: becomes errored after piping due to last write; source is closed; ' + 476 'preventCancel = true'); 477 478promise_test(t => { 479 480 const rs = recordingReadableStream(); 481 482 const ws = recordingWritableStream(undefined, new CountQueuingStrategy({ highWaterMark: 0 })); 483 484 const pipePromise = promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error'); 485 486 t.step_timeout(() => ws.controller.error(error1), 10); 487 488 return pipePromise.then(() => { 489 assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]); 490 assert_array_equals(ws.events, []); 491 }); 492 493}, 'Errors must be propagated backward: becomes errored after piping; dest never desires chunks; preventCancel = ' + 494 'false; fulfilled cancel promise'); 495 496promise_test(t => { 497 498 const rs = recordingReadableStream({ 499 cancel() { 500 throw error2; 501 } 502 }); 503 504 const ws = recordingWritableStream(undefined, new CountQueuingStrategy({ highWaterMark: 0 })); 505 506 const pipePromise = promise_rejects_exactly(t, error2, rs.pipeTo(ws), 'pipeTo must reject with the cancel error'); 507 508 t.step_timeout(() => ws.controller.error(error1), 10); 509 510 return pipePromise.then(() => { 511 assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]); 512 assert_array_equals(ws.events, []); 513 }); 514 515}, 'Errors must be propagated backward: becomes errored after piping; dest never desires chunks; preventCancel = ' + 516 'false; rejected cancel promise'); 517 518promise_test(t => { 519 520 const rs = recordingReadableStream(); 521 522 const ws = recordingWritableStream(undefined, new CountQueuingStrategy({ highWaterMark: 0 })); 523 524 const pipePromise = promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: true }), 525 'pipeTo must reject with the same error'); 526 527 t.step_timeout(() => ws.controller.error(error1), 10); 528 529 return pipePromise.then(() => { 530 assert_array_equals(rs.eventsWithoutPulls, []); 531 assert_array_equals(ws.events, []); 532 }); 533 534}, 'Errors must be propagated backward: becomes errored after piping; dest never desires chunks; preventCancel = ' + 535 'true'); 536 537promise_test(() => { 538 539 const rs = recordingReadableStream(); 540 541 const ws = recordingWritableStream(); 542 543 ws.abort(error1); 544 545 return rs.pipeTo(ws).then( 546 () => assert_unreached('the promise must not fulfill'), 547 err => { 548 assert_equals(err, error1, 'the promise must reject with error1'); 549 550 assert_array_equals(rs.eventsWithoutPulls, ['cancel', err]); 551 assert_array_equals(ws.events, ['abort', error1]); 552 } 553 ); 554 555}, 'Errors must be propagated backward: becomes errored before piping via abort; preventCancel omitted; fulfilled ' + 556 'cancel promise'); 557 558promise_test(t => { 559 560 const rs = recordingReadableStream({ 561 cancel() { 562 throw error2; 563 } 564 }); 565 566 const ws = recordingWritableStream(); 567 568 ws.abort(error1); 569 570 return promise_rejects_exactly(t, error2, rs.pipeTo(ws), 'pipeTo must reject with the cancel error') 571 .then(() => { 572 return ws.getWriter().closed.then( 573 () => assert_unreached('the promise must not fulfill'), 574 err => { 575 assert_equals(err, error1, 'the promise must reject with error1'); 576 577 assert_array_equals(rs.eventsWithoutPulls, ['cancel', err]); 578 assert_array_equals(ws.events, ['abort', error1]); 579 } 580 ); 581 }); 582 583}, 'Errors must be propagated backward: becomes errored before piping via abort; preventCancel omitted; rejected ' + 584 'cancel promise'); 585 586promise_test(t => { 587 588 const rs = recordingReadableStream(); 589 590 const ws = recordingWritableStream(); 591 592 ws.abort(error1); 593 594 return promise_rejects_exactly(t, error1, rs.pipeTo(ws, { preventCancel: true })).then(() => { 595 assert_array_equals(rs.eventsWithoutPulls, []); 596 assert_array_equals(ws.events, ['abort', error1]); 597 }); 598 599}, 'Errors must be propagated backward: becomes errored before piping via abort; preventCancel = true'); 600 601promise_test(t => { 602 603 const rs = recordingReadableStream(); 604 605 let resolveWriteCalled; 606 const writeCalledPromise = new Promise(resolve => { 607 resolveWriteCalled = resolve; 608 }); 609 610 const ws = recordingWritableStream({ 611 write() { 612 resolveWriteCalled(); 613 return flushAsyncEvents(); 614 } 615 }); 616 617 const pipePromise = rs.pipeTo(ws); 618 619 rs.controller.enqueue('a'); 620 621 return writeCalledPromise.then(() => { 622 ws.controller.error(error1); 623 624 return promise_rejects_exactly(t, error1, pipePromise); 625 }).then(() => { 626 assert_array_equals(rs.eventsWithoutPulls, ['cancel', error1]); 627 assert_array_equals(ws.events, ['write', 'a']); 628 }); 629 630}, 'Errors must be propagated backward: erroring via the controller errors once pending write completes'); 631