Adding tests for RTCQuicStream.

Bug: 874296
Change-Id: I33e79a0641e38b420a23d0ff42933cd26bc0b368
Reviewed-on: https://chromium-review.googlesource.com/c/1341153
Commit-Queue: Seth Hampson <shampson@chromium.org>
Reviewed-by: Steve Anton <steveanton@chromium.org>
Cr-Commit-Position: refs/heads/master@{#611242}
diff --git a/webrtc-quic/RTCQuicStream-helper.js b/webrtc-quic/RTCQuicStream-helper.js
index a87668f..9563e53 100644
--- a/webrtc-quic/RTCQuicStream-helper.js
+++ b/webrtc-quic/RTCQuicStream-helper.js
@@ -39,6 +39,43 @@
     const [ localQuicTransport, remoteQuicTransport ] =
         await makeTwoConnectedQuicTransports(t);
     const localStream = localQuicTransport.createStream();
+    localStream.finish();
+    const remoteWatcher =
+        new EventWatcher(t, remoteQuicTransport, 'quicstream');
+    const { stream: remoteStream } = await remoteWatcher.wait_for('quicstream');
+    remoteStream.finish();
+    await localStream.waitForReadable(localStream.maxReadBufferedAmount);
+    assert_object_equals(
+        localStream.readInto(new Uint8Array(10)),
+        { amount: 0, finished: true });
+    assert_equals(localStream.state, 'closed');
+    return test_func(t, localStream);
+  }, 'Stream closed by finish(), followed by reading remote finish: ' +
+      description);
+
+  promise_test(async t => {
+    const [ localQuicTransport, remoteQuicTransport ] =
+        await makeTwoConnectedQuicTransports(t);
+    const localStream = localQuicTransport.createStream();
+    localStream.write(new Uint8Array(1));
+    const remoteWatcher =
+        new EventWatcher(t, remoteQuicTransport, 'quicstream');
+    const { stream: remoteStream } = await remoteWatcher.wait_for('quicstream');
+    remoteStream.finish();
+    await localStream.waitForReadable(localStream.maxReadBufferedAmount);
+    assert_object_equals(
+        localStream.readInto(new Uint8Array(10)),
+        { amount: 0, finished: true });
+    localStream.finish();
+    assert_equals(localStream.state, 'closed');
+    return test_func(t, localStream);
+  }, 'Stream closed by by reading remote finish, followed by finish(): ' +
+      description);
+
+  promise_test(async t => {
+    const [ localQuicTransport, remoteQuicTransport ] =
+        await makeTwoConnectedQuicTransports(t);
+    const localStream = localQuicTransport.createStream();
     localQuicTransport.stop();
     assert_equals(localStream.state, 'closed');
     return test_func(t, localStream);
diff --git a/webrtc-quic/RTCQuicStream.https.html b/webrtc-quic/RTCQuicStream.https.html
index 08c3a54..1e24298 100644
--- a/webrtc-quic/RTCQuicStream.https.html
+++ b/webrtc-quic/RTCQuicStream.https.html
@@ -119,12 +119,72 @@
 
 promise_test(async t => {
   const [ localQuicTransport, remoteQuicTransport ] =
-    await makeTwoConnectedQuicTransports(t);
+      await makeTwoConnectedQuicTransports(t);
   const localStream = localQuicTransport.createStream();
-  const promise = localStream.waitForReadable(1);
+  localStream.finish();
+  const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
+  const { stream: remoteStream } = await remoteWatcher.wait_for('quicstream');
+  assert_equals(remoteStream.state, 'open');
+  const remoteStreamWatcher = new EventWatcher(t, remoteStream, 'statechange');
+  await remoteStream.waitForReadable(remoteStream.maxReadBufferedAmount);
+  assert_object_equals(
+      remoteStream.readInto(new Uint8Array(10)),
+      { amount: 0, finished: true } );
+  assert_equals(remoteStream.state, 'closing');
+}, 'waitForReadable() resolves with remote finish');
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
+  localStream.finish();
+  const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
+  const { stream: remoteStream } = await remoteWatcher.wait_for('quicstream');
+  await remoteStream.waitForReadable(remoteStream.maxReadBufferedAmount);
+  assert_object_equals(
+      remoteStream.readInto(new Uint8Array(10)),
+      { amount: 0, finished: true } );
+  assert_equals(remoteStream.state, 'closing');
+  remoteStream.finish()
+  assert_equals(remoteStream.state, 'closed');
+}, 'finish() on a stream that has already read out finish changes ' +
+    `state to 'closed'.`);
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
+  localStream.finish();
+  const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
+  const { stream: remoteStream } = await remoteWatcher.wait_for('quicstream');
+  remoteStream.finish();
+  assert_equals(localStream.state, 'closing');
+  await localStream.waitForReadable(localStream.maxReadBufferedAmount);
+  assert_object_equals(
+      localStream.readInto(new Uint8Array(10)),
+      { amount: 0, finished: true } );
+  assert_equals(localStream.state, 'closed');
+}, 'Reading out finish on stream that has already called finish()  ' +
+    `state to 'closed'.`);
+
+promise_test(async t => {
+    const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
+  localStream.finish();
+  const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
+  const { stream: remoteStream } = await remoteWatcher.wait_for('quicstream');
+  await remoteStream.waitForReadable(remoteStream.maxReadBufferedAmount);
+  assert_object_equals(
+      remoteStream.readInto(new Uint8Array(10)),
+      { amount: 0, finished: true } );
+  assert_equals(remoteStream.state, 'closing');
   localStream.reset();
-  await promise_rejects(t, 'InvalidStateError', promise);
-}, 'reset() rejects pending waitForReadable() promises.');
+  const remoteStreamWatcher = new EventWatcher(t, remoteStream, 'statechange');
+  await remoteStreamWatcher.wait_for('statechange');
+  assert_equals(remoteStream.state, 'closed');
+}, 'Reading out finish then a getting a remote reset fires a statechange event ' +
+    `to 'closed'.`);
 
 promise_test(async t => {
   const [ localQuicTransport, remoteQuicTransport ] =
@@ -214,6 +274,36 @@
   const [ localQuicTransport, remoteQuicTransport ] =
       await makeTwoConnectedQuicTransports(t);
   const localStream = localQuicTransport.createStream();
+  localStream.write(generateData(10));
+  const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
+  const { stream : remoteStream} = await remoteWatcher.wait_for('quicstream');
+  await remoteStream.waitForReadable(10);
+  assert_equals(10, remoteStream.readBufferedAmount);
+  remoteStream.reset();
+  assert_equals(0, remoteStream.readBufferedAmount);
+}, 'readBufferedAmount set to 0 after local reset().');
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
+  localStream.write(generateData(10));
+  const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
+  const { stream : remoteStream} = await remoteWatcher.wait_for('quicstream');
+  await remoteStream.waitForReadable(10);
+  assert_equals(10, remoteStream.readBufferedAmount);
+  localStream.reset();
+  const remoteStreamWatcher =
+        new EventWatcher(t, remoteStream, 'statechange');
+  await remoteStreamWatcher.wait_for('statechange');
+  assert_equals(remoteStream.state, 'closed');
+  assert_equals(0, remoteStream.readBufferedAmount);
+}, 'readBufferedAmount set to 0 after remote reset().');
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
   localStream.write(new Uint8Array(10));
   localStream.reset();
   assert_equals(localStream.writeBufferedAmount, 0);
@@ -223,6 +313,26 @@
   const [ localQuicTransport, remoteQuicTransport ] =
       await makeTwoConnectedQuicTransports(t);
   const localStream = localQuicTransport.createStream();
+  localStream.write(new Uint8Array(1));
+  const remoteWatcher =
+      new EventWatcher(t, remoteQuicTransport, 'quicstream');
+  const { stream: remoteStream } = await remoteWatcher.wait_for('quicstream');
+  remoteStream.finish();
+  await localStream.waitForReadable(localStream.maxReadBufferedAmount);
+  assert_object_equals(
+      localStream.readInto(new Uint8Array(10)),
+      { amount: 0, finished: true });
+  localStream.write(new Uint8Array(10));
+  assert_equals(localStream.writeBufferedAmount, 10);
+  localStream.finish();
+  assert_equals(localStream.writeBufferedAmount, 0);
+}, 'writeBufferedAmount set to 0 after reading remote finish, followed ' +
+    'by finish().');
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
   localStream.write(new Uint8Array(10));
   localQuicTransport.stop();
   assert_equals(localStream.writeBufferedAmount, 0);
@@ -251,7 +361,7 @@
   await localStream.waitForWriteBufferedAmountBelow(
       localStream.maxWriteBufferedAmount);
 }, 'waitForWriteBufferedAmountBelow(maxWriteBufferedAmount) resolves ' +
-  'immediately.');
+    'immediately.');
 
 promise_test(async t => {
   const [ localQuicTransport, remoteQuicTransport ] =
@@ -265,7 +375,21 @@
       promise_rejects(t, 'InvalidStateError', promise1),
       promise_rejects(t, 'InvalidStateError', promise2)]);
 }, 'Pending waitForWriteBufferedAmountBelow() promises rejected after ' +
-  'finish().');
+    'finish().');
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
+  localStream.write(new Uint8Array(localStream.maxWriteBufferedAmount));
+  localStream.finish();
+  const promise1 = localStream.waitForWriteBufferedAmountBelow(0);
+  const promise2 = localStream.waitForWriteBufferedAmountBelow(0);
+  await Promise.all([
+      promise_rejects(t, 'InvalidStateError', promise1),
+      promise_rejects(t, 'InvalidStateError', promise2)]);
+}, 'waitForWriteBufferedAmountBelow() promises immediately rejected after ' +
+    'finish().');
 
 promise_test(async t => {
   const [ localQuicTransport, remoteQuicTransport ] =
@@ -279,7 +403,21 @@
       promise_rejects(t, 'InvalidStateError', promise1),
       promise_rejects(t, 'InvalidStateError', promise2)]);
 }, 'Pending waitForWriteBufferedAmountBelow() promises rejected after ' +
-  'reset().');
+    'reset().');
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
+  localStream.write(new Uint8Array(localStream.maxWriteBufferedAmount));
+  const promise1 = localStream.waitForWriteBufferedAmountBelow(0);
+  const promise2 = localStream.waitForWriteBufferedAmountBelow(0);
+  localQuicTransport.stop();
+  await Promise.all([
+      promise_rejects(t, 'InvalidStateError', promise1),
+      promise_rejects(t, 'InvalidStateError', promise2)]);
+}, 'Pending waitForWriteBufferedAmountBelow() promises rejected after ' +
+    'RTCQuicTransport stop().');
 
 closed_stream_test(async (t, stream) => {
   await promise_rejects(t, 'InvalidStateError',
@@ -292,7 +430,7 @@
   const localStream = localQuicTransport.createStream();
   assert_object_equals(
       localStream.readInto(new Uint8Array(10)),
-      { amount: 0, finished: false });
+      { amount: 0, finished: false } );
 }, 'readInto() on new local stream returns amount 0.');
 
 closed_stream_test(async (t, stream) => {
@@ -311,7 +449,7 @@
   const readBuffer = new Uint8Array(3);
   assert_object_equals(
       remoteStream.readInto(readBuffer),
-      { amount: 1, finished: false });
+      { amount: 1, finished: false } );
   assert_array_equals(readBuffer, [ 65, 0, 0 ]);
   assert_equals(remoteStream.readBufferedAmount, 0);
 }, 'Read 1 byte.');
@@ -341,6 +479,30 @@
   const [ localQuicTransport, remoteQuicTransport ] =
       await makeTwoConnectedQuicTransports(t);
   const localStream = localQuicTransport.createStream();
+  const data = generateData(10);
+  localStream.write(data);
+  localStream.finish();
+  const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
+  const { stream: remoteStream } = await remoteWatcher.wait_for('quicstream');
+  await remoteStream.waitForReadable(data.length + 1);
+  const readBuffer = new Uint8Array(5);
+  assert_object_equals(
+      remoteStream.readInto(readBuffer),
+      { amount: 5, finished: false} );
+  assert_array_equals(
+      readBuffer, data.subarray(0, 5));
+  const finReadBuffer = new Uint8Array(5);
+  assert_object_equals(
+      remoteStream.readInto(finReadBuffer),
+      { amount: 5, finished: true} );
+  assert_array_equals(
+    finReadBuffer, data.subarray(5, data.length));
+}, 'readInto() reads out finish after reading all data.');
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
   const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
   writeGeneratedData(localStream, localStream.maxReadBufferedAmount);
   const { stream: remoteStream } = await remoteWatcher.wait_for('quicstream');
@@ -348,7 +510,7 @@
   const readBuffer = new Uint8Array(localStream.maxReadBufferedAmount);
   assert_object_equals(
       remoteStream.readInto(readBuffer),
-      { amount: localStream.maxReadBufferedAmount, finished: false });
+      { amount: localStream.maxReadBufferedAmount, finished: false } );
   assert_array_equals(
       readBuffer, generateData(localStream.maxReadBufferedAmount));
 }, 'Read maxReadBufferedAmount bytes all at once.');
@@ -357,6 +519,19 @@
   const [ localQuicTransport, remoteQuicTransport ] =
       await makeTwoConnectedQuicTransports(t);
   const localStream = localQuicTransport.createStream();
+  localStream.finish();
+  const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
+  const { stream: remoteStream } = await remoteWatcher.wait_for('quicstream');
+  await remoteStream.waitForReadable(remoteStream.maxReadBufferedAmount);
+  assert_object_equals(
+      remoteStream.readInto(new Uint8Array(10)),
+      { amount: 0, finished: true } );
+}, 'waitForReadable() resolves with finish().');
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
   const writeData = generateData(10);
   localStream.write(writeData);
   localStream.finish();
@@ -366,9 +541,9 @@
   assert_equals(remoteStream.readBufferedAmount, 10);
   const readBuffer = new Uint8Array(10);
   assert_object_equals(
-    remoteStream.readInto(readBuffer), { amount: 10, finished: true });
+      remoteStream.readInto(readBuffer), { amount: 10, finished: true } );
   assert_array_equals(readBuffer, writeData);
-}, 'waitForReadable() resolves early if remote fin is received.');
+}, 'waitForReadable() resolves early if remote finish is received.');
 
 promise_test(async t => {
   const [ localQuicTransport, remoteQuicTransport ] =
@@ -377,7 +552,27 @@
   await promise_rejects(t, new TypeError(),
       localStream.waitForReadable(localStream.maxReadBufferedAmount + 1));
 }, 'waitForReadable() rejects with TypeError if amount is more than ' +
-  'maxReadBufferedAmount.');
+    'maxReadBufferedAmount.');
+
+promise_test(async t => {
+    const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
+  localStream.finish();
+  const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
+  const { stream: remoteStream } = await remoteWatcher.wait_for('quicstream');
+  await remoteStream.waitForReadable(remoteStream.maxReadBufferedAmount);
+  assert_object_equals(
+      remoteStream.readInto(new Uint8Array(10)),
+      { amount: 0, finished: true } );
+
+  const promise1 = remoteStream.waitForReadable(10);
+  const promise2 = remoteStream.waitForReadable(10);
+  await Promise.all([
+      promise_rejects(t, 'InvalidStateError', promise1),
+      promise_rejects(t, 'InvalidStateError', promise2)]);
+}, 'waitForReadable() promises immediately rejected with InvalidStateError ' +
+    'after finish is read out.');
 
 promise_test(async t => {
   const [ localQuicTransport, remoteQuicTransport ] =
@@ -406,6 +601,35 @@
       promise_rejects(t, 'InvalidStateError', promise2)]);
 }, 'Pending waitForReadable() promises rejected after remote reset().');
 
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
+  const promise1 = localStream.waitForReadable(10);
+  const promise2 = localStream.waitForReadable(10);
+  localQuicTransport.stop();
+  await Promise.all([
+      promise_rejects(t, 'InvalidStateError', promise1),
+      promise_rejects(t, 'InvalidStateError', promise2)]);
+}, 'Pending waitForReadable() promises rejected after RTCQuicTransport ' +
+   'stop().');
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
+  localStream.write(new Uint8Array(1));
+  const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
+  const { stream : remoteStream} = await remoteWatcher.wait_for('quicstream');
+  const promise1 = remoteStream.waitForReadable(10);
+  const promise2 = remoteStream.waitForReadable(10);
+  localQuicTransport.stop();
+  await Promise.all([
+      promise_rejects(t, 'InvalidStateError', promise1),
+      promise_rejects(t, 'InvalidStateError', promise2)]);
+}, 'Pending waitForReadable() promises rejected after remote RTCQuicTransport ' +
+    'stop().');
+
 closed_stream_test(async (t, stream) => {
   await promise_rejects(t, 'InvalidStateError',
       stream.waitForReadable(1));