text: 'use strict';
  
  if (self.importScripts) {
    self.importScripts('/resources/testharness.js');
    self.importScripts('../resources/test-utils.js');
    self.importScripts('../resources/recording-streams.js');
  }
  
  const error1 = new Error('error1!');
  error1.name = 'error1';
  
  promise_test(() => {
  
    const rs = recordingReadableStream({
      start(controller) {
        controller.close();
      }
    });
  
    const ws = recordingWritableStream();
  
    return rs.pipeTo(ws).then(value => {
      assert_equals(value, undefined, 'the promise must fulfill with undefined');
    })
    .then(() => {
      assert_array_equals(rs.events, []);
      assert_array_equals(ws.events, ['close']);
  
      return Promise.all([
        rs.getReader().closed,
        ws.getWriter().closed
      ]);
    });
  
  }, 'Closing must be propagated forward: starts closed; preventClose omitted; fulfilled close promise');
  
  promise_test(t => {
  
    const rs = recordingReadableStream({
      start(controller) {
        controller.close();
      }
    });
  
    const ws = recordingWritableStream({
      close() {
        throw error1;
      }
    });
  
    return promise_rejects(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error').then(() => {
      assert_array_equals(rs.events, []);
      assert_array_equals(ws.events, ['close']);
  
      return Promise.all([
        rs.getReader().closed,
        promise_rejects(t, error1, ws.getWriter().closed)
      ]);
    });
  
  }, 'Closing must be propagated forward: starts closed; preventClose omitted; rejected close promise');
  
  for (const falsy of [undefined, null, false, +0, -0, NaN, '']) {
    const stringVersion = Object.is(falsy, -0) ? '-0' : String(falsy);
  
    promise_test(() => {
  
      const rs = recordingReadableStream({
        start(controller) {
          controller.close();
        }
      });
  
      const ws = recordingWritableStream();
  
      return rs.pipeTo(ws, { preventClose: falsy }).then(value => {
        assert_equals(value, undefined, 'the promise must fulfill with undefined');
      })
      .then(() => {
        assert_array_equals(rs.events, []);
        assert_array_equals(ws.events, ['close']);
  
        return Promise.all([
          rs.getReader().closed,
          ws.getWriter().closed
        ]);
      });
  
    }, `Closing must be propagated forward: starts closed; preventClose = ${stringVersion} (falsy); fulfilled close ` +
       `promise`);
  }
  
  for (const truthy of [true, 'a', 1, Symbol(), { }]) {
    promise_test(() => {
  
      const rs = recordingReadableStream({
        start(controller) {
          controller.close();
        }
      });
  
      const ws = recordingWritableStream();
  
      return rs.pipeTo(ws, { preventClose: truthy }).then(value => {
        assert_equals(value, undefined, 'the promise must fulfill with undefined');
      })
      .then(() => {
        assert_array_equals(rs.events, []);
        assert_array_equals(ws.events, []);
  
        return rs.getReader().closed;
      });
  
    }, `Closing must be propagated forward: starts closed; preventClose = ${String(truthy)} (truthy)`);
  }
  
  promise_test(() => {
  
    const rs = recordingReadableStream({
      start(controller) {
        controller.close();
      }
    });
  
    const ws = recordingWritableStream();
  
    return rs.pipeTo(ws, { preventClose: true, preventAbort: true }).then(value => {
      assert_equals(value, undefined, 'the promise must fulfill with undefined');
    })
    .then(() => {
      assert_array_equals(rs.events, []);
      assert_array_equals(ws.events, []);
  
      return rs.getReader().closed;
    });
  
  }, 'Closing must be propagated forward: starts closed; preventClose = true, preventAbort = true');
  
  promise_test(() => {
  
    const rs = recordingReadableStream({
      start(controller) {
        controller.close();
      }
    });
  
    const ws = recordingWritableStream();
  
    return rs.pipeTo(ws, { preventClose: true, preventAbort: true, preventCancel: true }).then(value => {
      assert_equals(value, undefined, 'the promise must fulfill with undefined');
    })
    .then(() => {
      assert_array_equals(rs.events, []);
      assert_array_equals(ws.events, []);
  
      return rs.getReader().closed;
    });
  
  }, 'Closing must be propagated forward: starts closed; preventClose = true, preventAbort = true, preventCancel = true');
  
  promise_test(() => {
  
    const rs = recordingReadableStream();
  
    const ws = recordingWritableStream();
  
    const pipePromise = rs.pipeTo(ws);
  
    setTimeout(() => rs.controller.close());
  
    return pipePromise.then(value => {
      assert_equals(value, undefined, 'the promise must fulfill with undefined');
    })
    .then(() => {
      assert_array_equals(rs.eventsWithoutPulls, []);
      assert_array_equals(ws.events, ['close']);
  
      return Promise.all([
        rs.getReader().closed,
        ws.getWriter().closed
      ]);
    });
  
  }, 'Closing must be propagated forward: becomes closed asynchronously; preventClose omitted; fulfilled close promise');
  
  promise_test(t => {
  
    const rs = recordingReadableStream();
  
    const ws = recordingWritableStream({
      close() {
        throw error1;
      }
    });
  
    const pipePromise = promise_rejects(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error');
  
    setTimeout(() => rs.controller.close());
  
    return pipePromise.then(() => {
      assert_array_equals(rs.eventsWithoutPulls, []);
      assert_array_equals(ws.events, ['close']);
  
      return Promise.all([
        rs.getReader().closed,
        promise_rejects(t, error1, ws.getWriter().closed)
      ]);
    });
  
  }, 'Closing must be propagated forward: becomes closed asynchronously; preventClose omitted; rejected close promise');
  
  promise_test(() => {
  
    const rs = recordingReadableStream();
  
    const ws = recordingWritableStream();
  
    const pipePromise = rs.pipeTo(ws, { preventClose: true });
  
    setTimeout(() => rs.controller.close());
  
    return pipePromise.then(value => {
      assert_equals(value, undefined, 'the promise must fulfill with undefined');
    })
    .then(() => {
      assert_array_equals(rs.eventsWithoutPulls, []);
      assert_array_equals(ws.events, []);
  
      return rs.getReader().closed;
    });
  
  }, 'Closing must be propagated forward: becomes closed asynchronously; preventClose = true');
  
  promise_test(() => {
  
    const rs = recordingReadableStream();
  
    const ws = recordingWritableStream(undefined, new CountQueuingStrategy({ highWaterMark: 0 }));
  
    const pipePromise = rs.pipeTo(ws);
  
    setTimeout(() => rs.controller.close());
  
    return pipePromise.then(value => {
      assert_equals(value, undefined, 'the promise must fulfill with undefined');
    })
    .then(() => {
      assert_array_equals(rs.eventsWithoutPulls, []);
      assert_array_equals(ws.events, ['close']);
  
      return Promise.all([
        rs.getReader().closed,
        ws.getWriter().closed
      ]);
    });
  
  }, 'Closing must be propagated forward: becomes closed asynchronously; dest never desires chunks; ' +
     'preventClose omitted; fulfilled close promise');
  
  promise_test(t => {
  
    const rs = recordingReadableStream();
  
    const ws = recordingWritableStream({
      close() {
        throw error1;
      }
    }, new CountQueuingStrategy({ highWaterMark: 0 }));
  
    const pipePromise = promise_rejects(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error');
  
    setTimeout(() => rs.controller.close());
  
    return pipePromise.then(() => {
      assert_array_equals(rs.eventsWithoutPulls, []);
      assert_array_equals(ws.events, ['close']);
  
      return Promise.all([
        rs.getReader().closed,
        promise_rejects(t, error1, ws.getWriter().closed)
      ]);
    });
  
  }, 'Closing must be propagated forward: becomes closed asynchronously; dest never desires chunks; ' +
     'preventClose omitted; rejected close promise');
  
  promise_test(() => {
  
    const rs = recordingReadableStream();
  
    const ws = recordingWritableStream(undefined, new CountQueuingStrategy({ highWaterMark: 0 }));
  
    const pipePromise = rs.pipeTo(ws, { preventClose: true });
  
    setTimeout(() => rs.controller.close());
  
    return pipePromise.then(value => {
      assert_equals(value, undefined, 'the promise must fulfill with undefined');
    })
    .then(() => {
      assert_array_equals(rs.eventsWithoutPulls, []);
      assert_array_equals(ws.events, []);
  
      return rs.getReader().closed;
    });
  
  }, 'Closing must be propagated forward: becomes closed asynchronously; dest never desires chunks; ' +
     'preventClose = true');
  
  promise_test(() => {
  
    const rs = recordingReadableStream();
  
    const ws = recordingWritableStream();
  
    const pipePromise = rs.pipeTo(ws);
  
    setTimeout(() => {
      rs.controller.enqueue('Hello');
      setTimeout(() => rs.controller.close());
    }, 10);
  
    return pipePromise.then(value => {
      assert_equals(value, undefined, 'the promise must fulfill with undefined');
    })
    .then(() => {
      assert_array_equals(rs.eventsWithoutPulls, []);
      assert_array_equals(ws.events, ['write', 'Hello', 'close']);
  
      return Promise.all([
        rs.getReader().closed,
        ws.getWriter().closed
      ]);
    });
  
  }, 'Closing must be propagated forward: becomes closed after one chunk; preventClose omitted; fulfilled close promise');
  
  promise_test(t => {
  
    const rs = recordingReadableStream();
  
    const ws = recordingWritableStream({
      close() {
        throw error1;
      }
    });
  
    const pipePromise = promise_rejects(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error');
  
    setTimeout(() => {
      rs.controller.enqueue('Hello');
      setTimeout(() => rs.controller.close());
    }, 10);
  
    return pipePromise.then(() => {
      assert_array_equals(rs.eventsWithoutPulls, []);
      assert_array_equals(ws.events, ['write', 'Hello', 'close']);
  
      return Promise.all([
        rs.getReader().closed,
        promise_rejects(t, error1, ws.getWriter().closed)
      ]);
    });
  
  }, 'Closing must be propagated forward: becomes closed after one chunk; preventClose omitted; rejected close promise');
  
  promise_test(() => {
  
    const rs = recordingReadableStream();
  
    const ws = recordingWritableStream();
  
    const pipePromise = rs.pipeTo(ws, { preventClose: true });
  
    setTimeout(() => {
      rs.controller.enqueue('Hello');
      setTimeout(() => rs.controller.close());
    }, 10);
  
    return pipePromise.then(value => {
      assert_equals(value, undefined, 'the promise must fulfill with undefined');
    })
    .then(() => {
      assert_array_equals(rs.eventsWithoutPulls, []);
      assert_array_equals(ws.events, ['write', 'Hello']);
  
      return rs.getReader().closed;
    });
  
  }, 'Closing must be propagated forward: becomes closed after one chunk; preventClose = true');
  
  promise_test(() => {
  
    const rs = recordingReadableStream();
  
    let resolveWritePromise;
    const ws = recordingWritableStream({
      write() {
        return new Promise(resolve => {
          resolveWritePromise = resolve;
        });
      }
    });
  
    let pipeComplete = false;
    const pipePromise = rs.pipeTo(ws).then(() => {
      pipeComplete = true;
    });
  
    rs.controller.enqueue('a');
    rs.controller.close();
  
    // Flush async events and verify that no shutdown occurs.
    return flushAsyncEvents().then(() => {
      assert_array_equals(ws.events, ['write', 'a']); // no 'close'
      assert_equals(pipeComplete, false, 'the pipe must not be complete');
  
      resolveWritePromise();
  
      return pipePromise.then(() => {
        assert_array_equals(ws.events, ['write', 'a', 'close']);
      });
    });
  
  }, 'Closing must be propagated forward: shutdown must not occur until the final write completes');
  
  promise_test(() => {
  
    const rs = recordingReadableStream();
  
    let resolveWritePromise;
    const ws = recordingWritableStream({
      write() {
        return new Promise(resolve => {
          resolveWritePromise = resolve;
        });
      }
    });
  
    let pipeComplete = false;
    const pipePromise = rs.pipeTo(ws, { preventClose: true }).then(() => {
      pipeComplete = true;
    });
  
    rs.controller.enqueue('a');
    rs.controller.close();
  
    // Flush async events and verify that no shutdown occurs.
    return flushAsyncEvents().then(() => {
      assert_array_equals(ws.events, ['write', 'a'],
        'the chunk must have been written, but close must not have happened');
      assert_equals(pipeComplete, false, 'the pipe must not be complete');
  
      resolveWritePromise();
  
      return pipePromise;
    }).then(() => flushAsyncEvents()).then(() => {
      assert_array_equals(ws.events, ['write', 'a'],
        'the chunk must have been written, but close must not have happened');
    });
  
  }, 'Closing must be propagated forward: shutdown must not occur until the final write completes; preventClose = true');
  
  promise_test(() => {
  
    const rs = recordingReadableStream();
  
    let resolveWriteCalled;
    const writeCalledPromise = new Promise(resolve => {
      resolveWriteCalled = resolve;
    });
  
    let resolveWritePromise;
    const ws = recordingWritableStream({
      write() {
        resolveWriteCalled();
  
        return new Promise(resolve => {
          resolveWritePromise = resolve;
        });
      }
    }, new CountQueuingStrategy({ highWaterMark: 2 }));
  
    let pipeComplete = false;
    const pipePromise = rs.pipeTo(ws).then(() => {
      pipeComplete = true;
    });
  
    rs.controller.enqueue('a');
    rs.controller.enqueue('b');
  
    return writeCalledPromise.then(() => flushAsyncEvents()).then(() => {
      assert_array_equals(ws.events, ['write', 'a'],
        'the first chunk must have been written, but close must not have happened yet');
      assert_false(pipeComplete, 'the pipe should not complete while the first write is pending');
  
      rs.controller.close();
      resolveWritePromise();
    }).then(() => flushAsyncEvents()).then(() => {
      assert_array_equals(ws.events, ['write', 'a', 'write', 'b'],
        'the second chunk must have been written, but close must not have happened yet');
      assert_false(pipeComplete, 'the pipe should not complete while the second write is pending');
  
      resolveWritePromise();
      return pipePromise;
    }).then(() => {
      assert_array_equals(ws.events, ['write', 'a', 'write', 'b', 'close'],
        'all chunks must have been written and close must have happened');
    });
  
  }, 'Closing must be propagated forward: shutdown must not occur until the final write completes; becomes closed after first write');
  
  promise_test(() => {
  
    const rs = recordingReadableStream();
  
    let resolveWriteCalled;
    const writeCalledPromise = new Promise(resolve => {
      resolveWriteCalled = resolve;
    });
  
    let resolveWritePromise;
    const ws = recordingWritableStream({
      write() {
        resolveWriteCalled();
  
        return new Promise(resolve => {
          resolveWritePromise = resolve;
        });
      }
    }, new CountQueuingStrategy({ highWaterMark: 2 }));
  
    let pipeComplete = false;
    const pipePromise = rs.pipeTo(ws, { preventClose: true }).then(() => {
      pipeComplete = true;
    });
  
    rs.controller.enqueue('a');
    rs.controller.enqueue('b');
  
    return writeCalledPromise.then(() => flushAsyncEvents()).then(() => {
      assert_array_equals(ws.events, ['write', 'a'],
        'the first chunk must have been written, but close must not have happened');
      assert_false(pipeComplete, 'the pipe should not complete while the first write is pending');
  
      rs.controller.close();
      resolveWritePromise();
    }).then(() => flushAsyncEvents()).then(() => {
      assert_array_equals(ws.events, ['write', 'a', 'write', 'b'],
        'the second chunk must have been written, but close must not have happened');
      assert_false(pipeComplete, 'the pipe should not complete while the second write is pending');
  
      resolveWritePromise();
      return pipePromise;
    }).then(() => flushAsyncEvents()).then(() => {
      assert_array_equals(ws.events, ['write', 'a', 'write', 'b'],
        'all chunks must have been written, but close must not have happened');
    });
  
  }, 'Closing must be propagated forward: shutdown must not occur until the final write completes; becomes closed after first write; preventClose = true');
  
  done();
  