Implement RTCQuicTransport.onquicstream and stream reset/finish

This CL implements the RTCQuicTransport createStream method and the
corresponding quicstream event hooked up to the QUIC adapters. It
also implements RTCQuicStream.reset/finish and statechange event to
allow end-to-end testing.

Bug: 874296
Change-Id: I331d37f3e21c606697b8768bf9eea59c90487163
Reviewed-on: https://chromium-review.googlesource.com/c/1217846
Commit-Queue: Steve Anton <steveanton@chromium.org>
Reviewed-by: Kentaro Hara <haraken@chromium.org>
Reviewed-by: Henrik Boström <hbos@chromium.org>
Cr-Commit-Position: refs/heads/master@{#596068}
diff --git a/webrtc/RTCQuicStream.https.html b/webrtc/RTCQuicStream.https.html
index 3302545..68c88e2 100644
--- a/webrtc/RTCQuicStream.https.html
+++ b/webrtc/RTCQuicStream.https.html
@@ -13,13 +13,14 @@
 
 // The following helper functions are called from RTCQuicTransport-helper.js:
 //   makeStandaloneQuicTransport
+//   makeTwoConnectedQuicTransports
 
 promise_test(async t => {
-  const quicTransport = await makeStandaloneQuicTransport(t);
+  const [ quicTransport, ] = await makeTwoConnectedQuicTransports(t);
   const quicStream = quicTransport.createStream();
   assert_equals(quicStream.transport, quicTransport,
       'Expect transport to be set to the creating RTCQuicTransport.');
-  assert_equals(quicStream.state, 'new', `Expect state to be 'new'.`);
+  assert_equals(quicStream.state, 'open', `Expect state to be 'open'.`);
   assert_equals(quicStream.readBufferedAmount, 0,
       'Expect read buffered amount to be 0.');
   assert_equals(quicStream.writeBufferedAmount, 0,
@@ -28,17 +29,130 @@
 
 promise_test(async t => {
   const quicTransport = await makeStandaloneQuicTransport(t);
+  assert_throws('InvalidStateError', () => quicTransport.createStream());
+}, 'createStream() throws if the transport is not connected.');
+
+promise_test(async t => {
+  const quicTransport = await makeStandaloneQuicTransport(t);
   quicTransport.stop();
   assert_throws('InvalidStateError', () => quicTransport.createStream());
 }, 'createStream() throws if the transport is closed.');
 
 promise_test(async t => {
-  const quicTransport = await makeStandaloneQuicTransport(t);
+  const [ quicTransport, ] = await makeTwoConnectedQuicTransports(t);
   const firstQuicStream = quicTransport.createStream();
   const secondQuicStream = quicTransport.createStream();
   quicTransport.stop();
   assert_equals(firstQuicStream.state, 'closed');
   assert_equals(secondQuicStream.state, 'closed');
-}, 'RTCQuicTransport.stop() closes all streams.');
+}, 'RTCQuicTransport.stop() closes all local streams.');
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const firstLocalStream = localQuicTransport.createStream();
+  firstLocalStream.finish();
+  const secondLocalStream = localQuicTransport.createStream();
+  secondLocalStream.finish();
+  const remoteWatcher =
+      new EventWatcher(t, remoteQuicTransport, [ 'quicstream', 'statechange' ]);
+  const { stream: firstRemoteStream } =
+      await remoteWatcher.wait_for('quicstream');
+  const { stream: secondRemoteStream } =
+      await remoteWatcher.wait_for('quicstream');
+  localQuicTransport.stop();
+  await remoteWatcher.wait_for('statechange');
+  assert_equals(firstRemoteStream.state, 'closed');
+  assert_equals(secondRemoteStream.state, 'closed');
+}, 'RTCQuicTransport.stop() closes all remote streams.');
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
+  localStream.finish();
+  assert_equals(localStream.state, 'closing');
+}, `finish() changes state to 'closing'.`);
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
+  localStream.finish();
+  localStream.finish();
+  assert_equals(localStream.state, 'closing');
+}, `finish() twice does not change state.`);
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
+  localStream.reset();
+  assert_equals(localStream.state, 'closed');
+}, `reset() changes state to 'closed'.`);
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
+  localStream.finish();
+  localStream.reset();
+  assert_equals(localStream.state, 'closed');
+}, `reset() following 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');
+  assert_equals(remoteStream.state, 'open');
+  const remoteStreamWatcher = new EventWatcher(t, remoteStream, 'statechange');
+  await remoteStreamWatcher.wait_for('statechange');
+  assert_equals(remoteStream.state, 'closing');
+}, 'createStream() followed by finish() fires a quicstream event followed by ' +
+    `a statechange event to 'closing' on the remote side.`);
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
+  localStream.reset();
+  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 remoteStreamWatcher.wait_for('statechange');
+  assert_equals(remoteStream.state, 'closed');
+}, 'createStream() followed by reset() fires a quicstream event followed ' +
+    `by a statechange event to 'closed' on the remote side.`);
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  remoteQuicTransport.onquicstream = ({ stream }) => stream.reset();
+  const localStream = localQuicTransport.createStream();
+  localStream.finish();
+  const localWatcher = new EventWatcher(t, localStream, 'statechange');
+  await localWatcher.wait_for('statechange');
+  assert_equals(localStream.state, 'closed');
+}, 'finish() on a remote stream that has already finished fires a ' +
+    `statechange event to 'closed' on the remote side.`);
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  const localStream = localQuicTransport.createStream();
+  localStream.finish();
+  localStream.reset();
+  const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'quicstream');
+  const { stream: remoteStream } = await remoteWatcher.wait_for('quicstream');
+  const remoteStreamWatcher = new EventWatcher(t, remoteStream, 'statechange');
+  await remoteStreamWatcher.wait_for('statechange');
+  assert_equals(remoteStream.state, 'closing');
+  await remoteStreamWatcher.wait_for('statechange');
+  assert_equals(remoteStream.state, 'closed');
+}, 'finish() then reset() fires two statechange events on the remote side.');
 
 </script>
diff --git a/webrtc/RTCQuicTransport-helper.js b/webrtc/RTCQuicTransport-helper.js
index 3ea19d7..7e28fea 100644
--- a/webrtc/RTCQuicTransport-helper.js
+++ b/webrtc/RTCQuicTransport-helper.js
@@ -79,4 +79,3 @@
   ]);
   return [ localQuicTransport, remoteQuicTransport ];
 }
-
diff --git a/webrtc/RTCQuicTransport.https.html b/webrtc/RTCQuicTransport.https.html
index ec79bc2..3bcc93d 100644
--- a/webrtc/RTCQuicTransport.https.html
+++ b/webrtc/RTCQuicTransport.https.html
@@ -17,6 +17,7 @@
 //   makeAndGatherTwoIceTransports
 
 // The following helper functions are called from RTCQuicTransport-helper.js:
+//   generateCertificate
 //   makeQuicTransport
 //   makeStandaloneQuicTransport
 //   makeAndStartTwoQuicTransports