// META: global=window,worker,shadowrealm
// META: script=../resources/recording-streams.js
'use strict';

const error1 = new Error('error1!');
error1.name = 'error1';

promise_test(() => {

  const rs = recordingReadableStream();

  const ws = recordingWritableStream();
  const writer = ws.getWriter();
  writer.close();
  writer.releaseLock();

  return rs.pipeTo(ws).then(
    () => assert_unreached('the promise must not fulfill'),
    err => {
      assert_equals(err.name, 'TypeError', 'the promise must reject with a TypeError');

      assert_array_equals(rs.eventsWithoutPulls, ['cancel', err]);
      assert_array_equals(ws.events, ['close']);

      return Promise.all([
        rs.getReader().closed,
        ws.getWriter().closed
      ]);
    }
  );

}, 'Closing must be propagated backward: starts closed; preventCancel omitted; fulfilled cancel promise');

promise_test(t => {

  // Our recording streams do not deal well with errors generated by the system, so give them some help
  let recordedError;
  const rs = recordingReadableStream({
    cancel(cancelErr) {
      recordedError = cancelErr;
      throw error1;
    }
  });

  const ws = recordingWritableStream();
  const writer = ws.getWriter();
  writer.close();
  writer.releaseLock();

  return promise_rejects_exactly(t, error1, rs.pipeTo(ws), 'pipeTo must reject with the same error').then(() => {
    assert_equals(recordedError.name, 'TypeError', 'the cancel reason must be a TypeError');

    assert_array_equals(rs.eventsWithoutPulls, ['cancel', recordedError]);
    assert_array_equals(ws.events, ['close']);

    return Promise.all([
      rs.getReader().closed,
      ws.getWriter().closed
    ]);
  });

}, 'Closing must be propagated backward: starts closed; preventCancel omitted; rejected cancel 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();

    const ws = recordingWritableStream();
    const writer = ws.getWriter();
    writer.close();
    writer.releaseLock();

    return rs.pipeTo(ws, { preventCancel: falsy }).then(
      () => assert_unreached('the promise must not fulfill'),
      err => {
        assert_equals(err.name, 'TypeError', 'the promise must reject with a TypeError');

        assert_array_equals(rs.eventsWithoutPulls, ['cancel', err]);
        assert_array_equals(ws.events, ['close']);

        return Promise.all([
          rs.getReader().closed,
          ws.getWriter().closed
        ]);
      }
    );

  }, `Closing must be propagated backward: starts closed; preventCancel = ${stringVersion} (falsy); fulfilled cancel ` +
     `promise`);
}

for (const truthy of [true, 'a', 1, Symbol(), { }]) {
  promise_test(t => {

    const rs = recordingReadableStream();

    const ws = recordingWritableStream();
    const writer = ws.getWriter();
    writer.close();
    writer.releaseLock();

    return promise_rejects_js(t, TypeError, rs.pipeTo(ws, { preventCancel: truthy })).then(() => {
      assert_array_equals(rs.eventsWithoutPulls, []);
      assert_array_equals(ws.events, ['close']);

      return ws.getWriter().closed;
    });

  }, `Closing must be propagated backward: starts closed; preventCancel = ${String(truthy)} (truthy)`);
}

promise_test(t => {

  const rs = recordingReadableStream();

  const ws = recordingWritableStream();
  const writer = ws.getWriter();
  writer.close();
  writer.releaseLock();

  return promise_rejects_js(t, TypeError, rs.pipeTo(ws, { preventCancel: true, preventAbort: true }))
    .then(() => {
      assert_array_equals(rs.eventsWithoutPulls, []);
      assert_array_equals(ws.events, ['close']);

      return ws.getWriter().closed;
    });

}, 'Closing must be propagated backward: starts closed; preventCancel = true, preventAbort = true');

promise_test(t => {

  const rs = recordingReadableStream();

  const ws = recordingWritableStream();
  const writer = ws.getWriter();
  writer.close();
  writer.releaseLock();

  return promise_rejects_js(t, TypeError,
                         rs.pipeTo(ws, { preventCancel: true, preventAbort: true, preventClose: true }))
  .then(() => {
    assert_array_equals(rs.eventsWithoutPulls, []);
    assert_array_equals(ws.events, ['close']);

    return ws.getWriter().closed;
  });

}, 'Closing must be propagated backward: starts closed; preventCancel = true, preventAbort = true, preventClose ' +
   '= true');
