Bug 1487585 [wpt PR 12771] - RTCQuicTransport: start() implementation, a=testonly
authorSteve Anton <steveanton@chromium.org>
Fri, 05 Oct 2018 14:21:17 +0000
changeset 495803 29412641d601420c994a2c645d331b22c08f9173
parent 495802 4248f5d3fc599f024dde8c705fe9d809dc40e256
child 495804 b9b5f7cd0c5e1df2b7d04cba7495cb76a1c4ab91
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstestonly
bugs1487585, 12771, 874296, 1171904, 595191
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1487585 [wpt PR 12771] - RTCQuicTransport: start() implementation, a=testonly Automatic update from web-platform-testsRTCQuicTransport: start() implementation This change implements the RTCQuicTransport.start() method and associated events: onstatechange, onerror. Bug: 874296 Change-Id: I8cb5d525ff5dd9d7d0a07a306f72297febc9aaaa Reviewed-on: https://chromium-review.googlesource.com/1171904 Commit-Queue: Steve Anton <steveanton@chromium.org> Reviewed-by: Henrik Boström <hbos@chromium.org> Reviewed-by: Philip Jägenstedt <foolip@chromium.org> Cr-Commit-Position: refs/heads/master@{#595191} -- wpt-commits: 1931a2758eef0ba57f7f3f6608baf0082a4e1bb3 wpt-pr: 12771
testing/web-platform/tests/webrtc/RTCIceTransport-extension-helper.js
testing/web-platform/tests/webrtc/RTCIceTransport-extension.https.html
testing/web-platform/tests/webrtc/RTCQuicStream.https.html
testing/web-platform/tests/webrtc/RTCQuicTransport-helper.js
testing/web-platform/tests/webrtc/RTCQuicTransport.https.html
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webrtc/RTCIceTransport-extension-helper.js
@@ -0,0 +1,42 @@
+'use strict';
+
+// Construct an RTCIceTransport instance. The instance will automatically be
+// cleaned up when the test finishes.
+function makeIceTransport(t) {
+  const iceTransport = new RTCIceTransport();
+  t.add_cleanup(() => iceTransport.stop());
+  return iceTransport;
+}
+
+// Construct two RTCIceTransport instances, configure them to exchange
+// candidates, then gather() them.
+// Returns a 2-list: [ RTCIceTransport, RTCIceTransport ]
+function makeAndGatherTwoIceTransports(t) {
+  const localTransport = makeIceTransport(t);
+  const remoteTransport = makeIceTransport(t);
+  localTransport.onicecandidate = e => {
+    if (e.candidate) {
+      remoteTransport.addRemoteCandidate(e.candidate);
+    }
+  };
+  remoteTransport.onicecandidate = e => {
+    if (e.candidate) {
+      localTransport.addRemoteCandidate(e.candidate);
+    }
+  };
+  localTransport.gather({});
+  remoteTransport.gather({});
+  return [ localTransport, remoteTransport ];
+}
+
+// Construct two RTCIceTransport instances, configure them to exchange
+// candidates and parameters, then gather() and start() them.
+// Returns a 2-list:
+//     [ controlling RTCIceTransport,
+//       controlled RTCIceTransport ]
+function makeGatherAndStartTwoIceTransports(t) {
+  const [ localTransport, remoteTransport ] = makeAndGatherTwoIceTransports(t);
+  localTransport.start(remoteTransport.getLocalParameters(), 'controlling');
+  remoteTransport.start(localTransport.getLocalParameters(), 'controlled');
+  return [ localTransport, remoteTransport ];
+}
--- a/testing/web-platform/tests/webrtc/RTCIceTransport-extension.https.html
+++ b/testing/web-platform/tests/webrtc/RTCIceTransport-extension.https.html
@@ -1,19 +1,25 @@
 <!doctype html>
 <meta charset=utf-8>
 <title>RTCIceTransport-extensions.https.html</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="RTCIceTransport-extension-helper.js"></script>
 <script>
 'use strict';
 
 // These tests are based on the following extension specification:
 // https://w3c.github.io/webrtc-ice/
 
+// The following helper functions are called from
+// RTCIceTransport-extension-helper.js:
+//   makeIceTransport
+//   makeGatherAndStartTwoIceTransports
+
 function makeIceTransport(t) {
   const iceTransport = new RTCIceTransport();
   t.add_cleanup(() => iceTransport.stop());
   return iceTransport;
 }
 
 test(() => {
   const iceTransport = new RTCIceTransport();
@@ -235,32 +241,18 @@ test(t => {
   assert_equals(iceTransport.state, 'new');
   assert_array_equals(iceTransport.getRemoteCandidates(), []);
   assert_ice_parameters_equals(iceTransport.getRemoteParameters(),
       changedRemoteParameters);
 }, `start() flushes remote candidates and transitions state to 'new' if ` +
    'later called with different remote parameters');
 
 promise_test(async t => {
-  const localTransport = makeIceTransport(t);
-  const remoteTransport = makeIceTransport(t);
-  localTransport.onicecandidate = e => {
-    if (e.candidate) {
-      remoteTransport.addRemoteCandidate(e.candidate);
-    }
-  };
-  remoteTransport.onicecandidate = e => {
-    if (e.candidate) {
-      localTransport.addRemoteCandidate(e.candidate);
-    }
-  };
-  localTransport.gather({});
-  remoteTransport.gather({});
-  localTransport.start(remoteTransport.getLocalParameters(), 'controlling');
-  remoteTransport.start(localTransport.getLocalParameters(), 'controlled');
+  const [ localTransport, remoteTransport ] =
+      makeGatherAndStartTwoIceTransports(t);
   const localWatcher = new EventWatcher(t, localTransport, 'statechange');
   const remoteWatcher = new EventWatcher(t, remoteTransport, 'statechange');
   await Promise.all([
     localWatcher.wait_for('statechange').then(() => {
       assert_equals(localTransport.state, 'connected');
     }),
     remoteWatcher.wait_for('statechange').then(() => {
       assert_equals(remoteTransport.state, 'connected');
--- a/testing/web-platform/tests/webrtc/RTCQuicStream.https.html
+++ b/testing/web-platform/tests/webrtc/RTCQuicStream.https.html
@@ -1,43 +1,44 @@
 <!doctype html>
 <meta charset=utf-8>
 <title>RTCQuicStream.https.html</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="RTCIceTransport-extension-helper.js"></script>
 <script src="RTCQuicTransport-helper.js"></script>
 <script>
 'use strict';
 
 // These tests are based on the following specification:
 // https://w3c.github.io/webrtc-quic/
 
 // The following helper functions are called from RTCQuicTransport-helper.js:
-//   makeQuicTransport
+//   makeStandaloneQuicTransport
 
-test(t => {
-  const quicTransport = makeQuicTransport(t, []);
+promise_test(async t => {
+  const quicTransport = await makeStandaloneQuicTransport(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.readBufferedAmount, 0,
       'Expect read buffered amount to be 0.');
   assert_equals(quicStream.writeBufferedAmount, 0,
       'Expect write buffered amount to be 0.');
 }, 'createStream() returns an RTCQuicStream with initial properties set.');
 
-test(t => {
-  const quicTransport = makeQuicTransport(t, []);
+promise_test(async t => {
+  const quicTransport = await makeStandaloneQuicTransport(t);
   quicTransport.stop();
   assert_throws('InvalidStateError', () => quicTransport.createStream());
 }, 'createStream() throws if the transport is closed.');
 
-test(t => {
-  const quicTransport = makeQuicTransport(t, []);
+promise_test(async t => {
+  const quicTransport = await makeStandaloneQuicTransport(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.');
 
 </script>
--- a/testing/web-platform/tests/webrtc/RTCQuicTransport-helper.js
+++ b/testing/web-platform/tests/webrtc/RTCQuicTransport-helper.js
@@ -1,10 +1,82 @@
 'use strict';
 
-function makeQuicTransport(t, certificates) {
-  const iceTransport = new RTCIceTransport();
-  t.add_cleanup(() => iceTransport.stop());
+// This file depends on RTCIceTransport-extension-helper.js which should be
+// loaded from the main HTML file.
+// The following helper functions are called from
+// RTCIceTransport-extension-helper.js:
+//   makeIceTransport
+//   makeGatherAndStartTwoIceTransports
+
+// Return a promise to generate an RTCCertificate with the given keygen
+// algorithm or a default one if none provided.
+function generateCertificate(keygenAlgorithm) {
+  return RTCPeerConnection.generateCertificate({
+    name: 'ECDSA',
+    namedCurve: 'P-256',
+    ...keygenAlgorithm,
+  });
+}
+
+// Construct an RTCQuicTransport instance with the given RTCIceTransport
+// instance and the given certificates. The RTCQuicTransport instance will be
+// automatically cleaned up when the test finishes.
+function makeQuicTransport(t, iceTransport, certificates) {
   const quicTransport = new RTCQuicTransport(iceTransport, certificates);
   t.add_cleanup(() => quicTransport.stop());
   return quicTransport;
 }
 
+// Construct an RTCQuicTransport instance with a new RTCIceTransport instance
+// and a single, newly-generated certificate. The RTCQuicTransport and
+// RTCIceTransport instances will be automatically cleaned up when the test
+// finishes.
+async function makeStandaloneQuicTransport(t) {
+  const certificate = await generateCertificate();
+  return makeQuicTransport(t, makeIceTransport(t), [ certificate ]);
+}
+
+// Construct two RTCQuicTransport instances and each call start() with the other
+// transport's local parameters.
+// Returns a 2-list:
+//     [ server RTCQuicTransport,
+//       client RTCQuicTransport ]
+async function makeAndStartTwoQuicTransports(t) {
+  const [ localCertificate, remoteCertificate ] =
+      await Promise.all([ generateCertificate(), generateCertificate() ]);
+  const [ localIceTransport, remoteIceTransport ] =
+      makeGatherAndStartTwoIceTransports(t);
+  const localQuicTransport =
+      makeQuicTransport(t, localIceTransport, [ localCertificate ]);
+  const remoteQuicTransport =
+      makeQuicTransport(t, remoteIceTransport, [ remoteCertificate ]);
+  localQuicTransport.start(remoteQuicTransport.getLocalParameters());
+  remoteQuicTransport.start(localQuicTransport.getLocalParameters());
+  return [ localQuicTransport, remoteQuicTransport ];
+}
+
+// Construct two RTCQuicTransport instances and wait for them to connect.
+// Returns a 2-list:
+//     [ server RTCQuicTransport,
+//       client RTCQuicTransport ]
+async function makeTwoConnectedQuicTransports(t) {
+  // Returns a promise that resolves when the transport fires a 'statechange'
+  // event to 'connected'.
+  function waitForConnected(transport) {
+    return new Promise((resolve, reject) => {
+      const eventHandler = t.step_func(() => {
+        assert_equals(transport.state, 'connected');
+        transport.removeEventListener('statechange', eventHandler, false);
+        resolve();
+      });
+      transport.addEventListener('statechange', eventHandler, false);
+    });
+  }
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeAndStartTwoQuicTransports(t);
+  await Promise.all([
+    waitForConnected(localQuicTransport),
+    waitForConnected(remoteQuicTransport),
+  ]);
+  return [ localQuicTransport, remoteQuicTransport ];
+}
+
--- a/testing/web-platform/tests/webrtc/RTCQuicTransport.https.html
+++ b/testing/web-platform/tests/webrtc/RTCQuicTransport.https.html
@@ -1,91 +1,188 @@
 <!doctype html>
 <meta charset=utf-8>
 <title>RTCQuicTransport.https.html</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="RTCIceTransport-extension-helper.js"></script>
 <script src="RTCQuicTransport-helper.js"></script>
 <script>
 'use strict';
 
 // These tests are based on the following specification:
 // https://w3c.github.io/webrtc-quic/
 
+// The following helper functions are called from
+// RTCIceTransport-extension-helper.js:
+//   makeIceTransport
+//   makeAndGatherTwoIceTransports
+
 // The following helper functions are called from RTCQuicTransport-helper.js:
 //   makeQuicTransport
-
-function generateCertificate(keygenAlgorithm) {
-  return RTCPeerConnection.generateCertificate({
-    name: 'ECDSA',
-    namedCurve: 'P-256',
-    ...keygenAlgorithm,
-  });
-}
+//   makeStandaloneQuicTransport
+//   makeAndStartTwoQuicTransports
+//   makeTwoConnectedQuicTransports
 
-test(t => {
-  // Don't use the makeQuicTransport helper so that the transport property can
-  // be verified.
-  const iceTransport = new RTCIceTransport();
-  const quicTransport = new RTCQuicTransport(iceTransport, []);
-  t.add_cleanup(() => {
-    quicTransport.stop();
-    iceTransport.stop();
-  });
+promise_test(async t => {
+  const certificate = await generateCertificate();
+  const iceTransport = makeIceTransport(t);
+  const quicTransport = makeQuicTransport(t, iceTransport, [ certificate ]);
   assert_equals(quicTransport.transport, iceTransport,
       'Expect transport to be the same as the one passed in the constructor.');
   assert_equals(quicTransport.state, 'new', `Expect state to be 'new'.`);
   assert_object_equals(quicTransport.getLocalParameters(),
-      { role: 'auto', fingerprints: [] },
+      { role: 'auto', fingerprints: certificate.getFingerprints() },
       'Expect local parameters to be initialized.');
   assert_equals(quicTransport.getRemoteParameters(), null,
       'Expect no remote parameters.');
-  assert_array_equals(quicTransport.getCertificates(), [],
-      'Expect not certificates.');
+  assert_array_equals(quicTransport.getCertificates(), [ certificate ],
+      'Expect one certificate.');
   assert_array_equals(quicTransport.getRemoteCertificates(), [],
       'Expect no remote certificates.');
 }, 'RTCQuicTransport initial properties are set.');
 
 promise_test(async t => {
   const [ firstCertificate, secondCertificate ] =
       await Promise.all([ generateCertificate(), generateCertificate() ]);
   const quicTransport =
-      makeQuicTransport(t, [ firstCertificate, secondCertificate ]);
+      makeQuicTransport(t, makeIceTransport(t),
+          [ firstCertificate, secondCertificate ]);
   assert_array_equals(quicTransport.getCertificates(),
       [ firstCertificate, secondCertificate ]);
 }, 'getCertificates() returns the certificates passed in the constructor.');
 
 promise_test(async t => {
   const [ firstCertificate, secondCertificate ] =
       await Promise.all([ generateCertificate(), generateCertificate() ]);
   const quicTransport =
-      makeQuicTransport(t, [ firstCertificate, secondCertificate ]);
+      makeQuicTransport(t, makeIceTransport(t),
+          [ firstCertificate, secondCertificate ]);
   assert_object_equals(quicTransport.getLocalParameters(), {
     role: 'auto',
-    fingerprints: [ firstCertificate.getFingerprints()[0],
-        secondCertificate.getFingerprints()[0] ],
+    fingerprints:
+        [ firstCertificate.getFingerprints()[0],
+            secondCertificate.getFingerprints()[0] ],
   });
   assert_array_equals(quicTransport.getCertificates(),
       [ firstCertificate, secondCertificate ]);
 }, 'getLocalParameters() has fingerprints for all certificates passed in the ' +
     'constructor.');
 
 promise_test(async t => {
   const expiredCertificate = await generateCertificate({ expires: 0 });
   assert_throws(new TypeError(),
-      () => makeQuicTransport(t, [ expiredCertificate ]));
+      () => makeQuicTransport(t, makeIceTransport(t), [ expiredCertificate ]));
 }, 'RTCQuicTransport constructor throws if passed an expired certificate.');
 
-test(t => {
-  const iceTransport = new RTCIceTransport();
+promise_test(async t => {
+  const certificate = await generateCertificate();
+  const iceTransport = makeIceTransport(t);
   iceTransport.stop();
   assert_throws('InvalidStateError',
-      () => new RTCQuicTransport(iceTransport, []));
+      () => makeQuicTransport(t, iceTransport, [ certificate ]));
 }, 'RTCQuicTransport constructor throws if passed a closed RTCIceTransport.');
 
-test(t => {
-  const quicTransport = makeQuicTransport(t, []);
+promise_test(async t => {
+  const certificate = await generateCertificate();
+  const iceTransport = makeIceTransport(t);
+  const firstQuicTransport =
+      makeQuicTransport(t, iceTransport, [ certificate ]);
+  assert_throws('InvalidStateError',
+      () => makeQuicTransport(t, iceTransport, [ certificate ]));
+}, 'RTCQuicTransport constructor throws if passed an RTCIceTransport that ' +
+    'already has an active RTCQuicTransport.');
+
+promise_test(async t => {
+  const quicTransport = await makeStandaloneQuicTransport(t);
   quicTransport.stop();
   assert_equals(quicTransport.state, 'closed');
 }, `stop() changes state to 'closed'.`);
 
+promise_test(async t => {
+  const quicTransport = await makeStandaloneQuicTransport(t);
+  quicTransport.transport.stop();
+  assert_equals(quicTransport.state, 'closed');
+}, `RTCIceTransport.stop() changes RTCQuicTransport.state to 'closed'.`);
+
+promise_test(async t => {
+  const quicTransport = await makeStandaloneQuicTransport(t);
+  quicTransport.start(quicTransport.getLocalParameters());
+  assert_equals(quicTransport.state, 'new');
+}, 'start() with a non-started RTCIceTransport does not change state.');
+
+promise_test(async t => {
+  const certificate = await generateCertificate();
+  const [ localIceTransport, remoteIceTransport ] =
+      makeAndGatherTwoIceTransports(t);
+  const quicTransport =
+      makeQuicTransport(t, localIceTransport, [ certificate ]);
+  quicTransport.start(quicTransport.getLocalParameters());
+  const iceTransportWatcher =
+      new EventWatcher(t, remoteIceTransport, 'icecandidate');
+  await iceTransportWatcher.wait_for('icecandidate');
+  localIceTransport.start(remoteIceTransport.getLocalParameters(),
+      'controlling');
+  assert_equals(quicTransport.state, 'connecting');
+}, 'start() with a non-started RTCIceTransport later changes state to ' +
+    `'connecting' once the RTCIceTransport.start() is called.`);
+
+promise_test(async t => {
+  const certificate = await generateCertificate();
+  const [ localIceTransport, remoteIceTransport ] =
+      makeAndGatherTwoIceTransports(t);
+  const quicTransport =
+      makeQuicTransport(t, localIceTransport, [ certificate ]);
+  const iceTransportWatcher =
+      new EventWatcher(t, remoteIceTransport, 'icecandidate');
+  await iceTransportWatcher.wait_for('icecandidate');
+  localIceTransport.start(remoteIceTransport.getLocalParameters());
+  quicTransport.start(quicTransport.getLocalParameters());
+  assert_equals(quicTransport.state, 'connecting');
+}, `start() with a started RTCIceTransport changes state to 'connecting'.`);
+
+promise_test(async t => {
+  const quicTransport = await makeStandaloneQuicTransport(t);
+  quicTransport.stop();
+  assert_throws('InvalidStateError',
+      () => quicTransport.start(quicTransport.getLocalParameters()));
+}, 'start() throws if called after stop().');
+
+promise_test(async t => {
+  const quicTransport = await makeStandaloneQuicTransport(t);
+  quicTransport.transport.stop();
+  assert_throws('InvalidStateError',
+      () => quicTransport.start(quicTransport.getLocalParameters()));
+}, 'start() throws if called after the RTCIceTransport has stopped.');
+
+promise_test(async t => {
+  const quicTransport = await makeStandaloneQuicTransport(t);
+  quicTransport.start(quicTransport.getLocalParameters());
+  assert_throws('InvalidStateError',
+      () => quicTransport.start(quicTransport.getLocalParameters()));
+}, 'start() throws if called twice.');
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeAndStartTwoQuicTransports(t);
+  const localWatcher = new EventWatcher(t, localQuicTransport, 'statechange');
+  const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'statechange');
+  await Promise.all([
+    localWatcher.wait_for('statechange').then(() => {
+      assert_equals(localQuicTransport.state, 'connected');
+    }),
+    remoteWatcher.wait_for('statechange').then(() => {
+      assert_equals(remoteQuicTransport.state, 'connected');
+    }),
+  ]);
+}, 'Two RTCQuicTransports connect to each other.');
+
+promise_test(async t => {
+  const [ localQuicTransport, remoteQuicTransport ] =
+      await makeTwoConnectedQuicTransports(t);
+  localQuicTransport.stop();
+  const remoteWatcher = new EventWatcher(t, remoteQuicTransport, 'statechange');
+  await remoteWatcher.wait_for('statechange');
+  assert_equals(remoteQuicTransport.state, 'closed');
+}, `stop() fires a statechange event to 'closed' on the remote transport`);
+
 </script>