diff --git a/webrtc/README.md b/webrtc/README.md
new file mode 100644
index 00000000000000..4477e4f375fdc0
--- /dev/null
+++ b/webrtc/README.md
@@ -0,0 +1,12 @@
+# WebRTC
+
+This directory contains the WebRTC test suite.
+
+## Acknowledgements
+
+Some data channel tests are based on the [data channel conformance test
+suite][nplab-webrtc-dc-playground] of the Network Programming Lab of the Münster
+University of Applied Sciences. We would like to thank Peter Titz, Felix Weinrank and Timo
+Völker for agreeing to contribute their test cases to this repository.
+
+[nplab-webrtc-dc-playground]: https://github.com/nplab/WebRTC-Data-Channel-Playground/tree/master/conformance-tests
diff --git a/webrtc/RTCDataChannel-bufferedAmount.html b/webrtc/RTCDataChannel-bufferedAmount.html
index f4a7104a4e5f33..b982b29b3b957e 100644
--- a/webrtc/RTCDataChannel-bufferedAmount.html
+++ b/webrtc/RTCDataChannel-bufferedAmount.html
@@ -5,166 +5,222 @@
+
diff --git a/webrtc/RTCDataChannelEvent-constructor.html b/webrtc/RTCDataChannelEvent-constructor.html
index 997f65c43801a9..03211eccb920e1 100644
--- a/webrtc/RTCDataChannelEvent-constructor.html
+++ b/webrtc/RTCDataChannelEvent-constructor.html
@@ -4,6 +4,9 @@
diff --git a/webrtc/RTCPeerConnection-createDataChannel.html b/webrtc/RTCPeerConnection-createDataChannel.html
index 450a25002ed221..ae74b62d42c3de 100644
--- a/webrtc/RTCPeerConnection-createDataChannel.html
+++ b/webrtc/RTCPeerConnection-createDataChannel.html
@@ -3,44 +3,51 @@
RTCPeerConnection.prototype.createDataChannel
+
diff --git a/webrtc/RTCPeerConnection-helper.js b/webrtc/RTCPeerConnection-helper.js
index 0b25df26190c14..310f028c605cbf 100644
--- a/webrtc/RTCPeerConnection-helper.js
+++ b/webrtc/RTCPeerConnection-helper.js
@@ -261,51 +261,93 @@ function listenForSSRCs(t, receiver) {
// end points to open.
function createDataChannelPair(
pc1=new RTCPeerConnection(),
- pc2=new RTCPeerConnection())
+ pc2=new RTCPeerConnection(),
+ options={})
{
- const channel1 = pc1.createDataChannel('');
+ options = Object.assign({}, {
+ channelLabel: '',
+ channelOptions: undefined,
+ doSignaling: true
+ }, options);
+
+ let channel1Options;
+ let channel2Options = null;
+ if (options.channelOptions instanceof Array) {
+ [channel1Options, channel2Options] = options.channelOptions;
+ } else {
+ channel1Options = options.channelOptions;
+ }
- exchangeIceCandidates(pc1, pc2);
+ const channel1 = pc1.createDataChannel(options.channelLabel, channel1Options);
return new Promise((resolve, reject) => {
let channel2;
let opened1 = false;
let opened2 = false;
+ function cleanup() {
+ channel1.removeEventListener('open', onOpen1);
+ channel2.removeEventListener('open', onOpen2);
+ channel1.removeEventListener('error', onError);
+ channel2.removeEventListener('error', onError);
+ }
+
function onBothOpened() {
+ cleanup();
resolve([channel1, channel2]);
}
+ function onError(...args) {
+ cleanup();
+ reject(...args);
+ }
+
function onOpen1() {
opened1 = true;
- if(opened2) onBothOpened();
+ if (opened2) {
+ onBothOpened();
+ }
}
function onOpen2() {
opened2 = true;
- if(opened1) onBothOpened();
+ if (opened1) {
+ onBothOpened();
+ }
}
- function onDataChannel(event) {
- channel2 = event.channel;
- channel2.addEventListener('error', reject);
+ function onDataChannelPairFound() {
+ channel2.addEventListener('error', onError, { once: true });
const { readyState } = channel2;
- if(readyState === 'open') {
+ if (readyState === 'open') {
onOpen2();
- } else if(readyState === 'connecting') {
- channel2.addEventListener('open', onOpen2);
+ } else if (readyState === 'connecting') {
+ channel2.addEventListener('open', onOpen2, { once: true });
} else {
- reject(new Error(`Unexpected ready state ${readyState}`));
+ onError(new Error(`Unexpected ready state ${readyState}`));
}
}
- channel1.addEventListener('open', onOpen1);
- channel1.addEventListener('error', reject);
+ function onDataChannel(event) {
+ channel2 = event.channel;
+ onDataChannelPairFound();
+ }
+
+ channel1.addEventListener('open', onOpen1, { once: true });
+ channel1.addEventListener('error', onError, { once: true });
- pc2.addEventListener('datachannel', onDataChannel);
+ if (channel2Options !== null) {
+ channel2 = pc2.createDataChannel(options.channelLabel, channel2Options);
+ onDataChannelPairFound();
+ } else {
+ pc2.addEventListener('datachannel', onDataChannel);
+ }
- doSignalingHandshake(pc1, pc2);
+ if (options.doSignaling) {
+ exchangeIceCandidates(pc1, pc2);
+ doSignalingHandshake(pc1, pc2, options);
+ }
});
}
diff --git a/webrtc/RTCPeerConnection-ondatachannel.html b/webrtc/RTCPeerConnection-ondatachannel.html
index 1070ee701d2336..711027439b3ab4 100644
--- a/webrtc/RTCPeerConnection-ondatachannel.html
+++ b/webrtc/RTCPeerConnection-ondatachannel.html
@@ -5,180 +5,376 @@
diff --git a/webrtc/RTCSctpTransport-constructor.html b/webrtc/RTCSctpTransport-constructor.html
index 28dae055532490..e837d9b127b909 100644
--- a/webrtc/RTCSctpTransport-constructor.html
+++ b/webrtc/RTCSctpTransport-constructor.html
@@ -5,85 +5,121 @@
diff --git a/webrtc/RTCSctpTransport-maxMessageSize.html b/webrtc/RTCSctpTransport-maxMessageSize.html
index 9163a66af151e3..99767611509197 100644
--- a/webrtc/RTCSctpTransport-maxMessageSize.html
+++ b/webrtc/RTCSctpTransport-maxMessageSize.html
@@ -9,10 +9,13 @@
'use strict';
// This test has an assert_unreached() that requires that the variable
-// canSendSize (initiated below) is greater than 2, if non-zero. The reason
-// is that we need two non-zero values that are less that that value for
-// testing with predictable results. This is a bit unfortunate but shouldn't
-// have any practical impact.
+// canSendSize (initiated below) must be 0 or greater than 2. The reason
+// is that we need two non-zero values for testing the following two cases:
+//
+// * if remote MMS `1` < canSendSize it should result in `1`.
+// * renegotiation of the above case with remoteMMS `2` should result in `2`.
+//
+// This is a bit unfortunate but shouldn't have any practical impact.
// Helper class to read SDP attributes and generate SDPs with modified attribute values
class SDPAttributeHelper {
@@ -39,148 +42,165 @@
}
const mmsAttributeHelper = new SDPAttributeHelper('max-message-size', '\\d+');
-let canSendSize;
-const remoteValue1 = 1;
-const remoteValue2 = 2;
+let canSendSize = null;
+const remoteSize1 = 1;
+const remoteSize2 = 2;
-promise_test(t => {
+promise_test(async (t) => {
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
- assert_equals(pc.sctp, null);
- let maxMessageSize;
-
- return generateDataChannelOffer(pc)
- .then((offer) => {
- assert_not_equals(mmsAttributeHelper.getValue(offer.sdp), null,
- 'SDP should have max-message-size attribute');
-
- offer = { type: 'offer', sdp: mmsAttributeHelper.sdpWithValue(offer.sdp, 0) };
- return pc.setRemoteDescription(offer);
- })
- .then(() => pc.createAnswer())
- .then((answer) => pc.setLocalDescription(answer))
- .then(() => {
- assert_not_equals(pc.sctp, null);
- canSendSize = pc.sctp.maxMessageSize == Number.POSITIVE_INFINITY ? 0 : pc.sctp.maxMessageSize;
- if (canSendSize != 0 && canSendSize < remoteValue2) {
- assert_unreached('This test needs two values that are less than canSendSize (unless it is zero)');
- }
- });
+
+ assert_equals(pc.sctp, null, 'RTCSctpTransport must be null');
+
+ let offer = await generateDataChannelOffer(pc);
+ assert_not_equals(mmsAttributeHelper.getValue(offer.sdp), null,
+ 'SDP should have max-message-size attribute');
+ offer = { type: 'offer', sdp: mmsAttributeHelper.sdpWithValue(offer.sdp, 0) };
+ await pc.setRemoteDescription(offer);
+ const answer = await pc.createAnswer();
+ await pc.setLocalDescription(answer);
+
+ assert_not_equals(pc.sctp, null, 'RTCSctpTransport must be available');
+ canSendSize = pc.sctp.maxMessageSize === Number.POSITIVE_INFINITY ? 0 : pc.sctp.maxMessageSize;
+ if (canSendSize !== 0 && canSendSize < remoteSize2) {
+ assert_unreached(
+ 'This test needs canSendSize to be 0 or > 2 for further "below" and "above" tests');
+ }
}, 'Determine the local side send limitation (canSendSize) by offering a max-message-size of 0');
-promise_test(t => {
+promise_test(async (t) => {
+ assert_not_equals(canSendSize, null, 'canSendSize needs to be determined');
+
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
- assert_equals(pc.sctp, null);
-
- return generateDataChannelOffer(pc)
- .then((offer) => {
- assert_not_equals(mmsAttributeHelper.getValue(offer.sdp), null,
- 'SDP should have max-message-size attribute');
-
- // Remove the max-message-size SDP attribute
- offer = { type: 'offer', sdp: mmsAttributeHelper.sdpWithoutAttribute(offer.sdp) };
- return pc.setRemoteDescription(offer)
- })
- .then(() => pc.createAnswer())
- .then((answer) => pc.setLocalDescription(answer))
- .then(() => {
- assert_not_equals(pc.sctp, null);
- // Test outcome depends on canSendSize value
- if (canSendSize) {
- assert_equals(pc.sctp.maxMessageSize, Math.min(65536, canSendSize),
- 'Missing SDP attribute and a non-zero canSendSize should give an maxMessageSize of min(65536, canSendSize)');
- } else {
- assert_equals(pc.sctp.maxMessageSize, 65536,
- 'Missing SDP attribute and a canSendSize of 0 should give an maxMessageSize of 65536');
- }
- });
+
+ assert_equals(pc.sctp, null, 'RTCSctpTransport must be null');
+
+ let offer = await generateDataChannelOffer(pc);
+ assert_not_equals(mmsAttributeHelper.getValue(offer.sdp), null,
+ 'SDP should have max-message-size attribute');
+
+ // Remove the max-message-size SDP attribute
+ offer = { type: 'offer', sdp: mmsAttributeHelper.sdpWithoutAttribute(offer.sdp) };
+ await pc.setRemoteDescription(offer);
+ const answer = await pc.createAnswer();
+ await pc.setLocalDescription(answer);
+
+ assert_not_equals(pc.sctp, null, 'RTCSctpTransport must be available');
+ // Test outcome depends on canSendSize value
+ if (canSendSize !== 0) {
+ assert_equals(pc.sctp.maxMessageSize, Math.min(65536, canSendSize),
+ 'Missing SDP attribute and a non-zero canSendSize should give an maxMessageSize of min(65536, canSendSize)');
+ } else {
+ assert_equals(pc.sctp.maxMessageSize, 65536,
+ 'Missing SDP attribute and a canSendSize of 0 should give an maxMessageSize of 65536');
+ }
}, 'Remote offer SDP missing max-message-size attribute');
-promise_test(t => {
+promise_test(async (t) => {
+ assert_not_equals(canSendSize, null, 'canSendSize needs to be determined');
+
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
- assert_equals(pc.sctp, null);
-
- return generateDataChannelOffer(pc)
- .then((offer) => {
- assert_not_equals(mmsAttributeHelper.getValue(offer.sdp), null,
- 'SDP should have max-message-size attribute');
-
- offer = { type: 'offer', sdp: mmsAttributeHelper.sdpWithValue(offer.sdp, remoteValue1) };
- return pc.setRemoteDescription(offer);
- })
- .then(() => pc.createAnswer())
- .then((answer) => pc.setLocalDescription(answer))
- .then(() => {
- assert_not_equals(pc.sctp, null);
- assert_equals(pc.sctp.maxMessageSize, remoteValue1,
- 'maxMessageSize should be the value provided by the remote peer (as long as it is less than canSendSize)');
- });
+
+ assert_equals(pc.sctp, null, 'RTCSctpTransport must be null');
+
+ let offer = await generateDataChannelOffer(pc);
+ assert_not_equals(mmsAttributeHelper.getValue(offer.sdp), null,
+ 'SDP should have max-message-size attribute');
+
+ offer = { type: 'offer', sdp: mmsAttributeHelper.sdpWithValue(offer.sdp, remoteSize1) };
+ await pc.setRemoteDescription(offer);
+ const answer = await pc.createAnswer();
+ await pc.setLocalDescription(answer);
+
+ assert_not_equals(pc.sctp, null, 'RTCSctpTransport must be available');
+ assert_equals(pc.sctp.maxMessageSize, remoteSize1,
+ 'maxMessageSize should be the value provided by the remote peer (as long as it is less than canSendSize)');
}, 'max-message-size with a (non-zero) value provided by the remote peer');
-promise_test(t => {
+promise_test(async (t) => {
+ assert_not_equals(canSendSize, null, 'canSendSize needs to be determined');
+
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
- assert_equals(pc.sctp, null);
-
- return generateDataChannelOffer(pc)
- .then((offer) => {
- assert_not_equals(mmsAttributeHelper.getValue(offer.sdp), null,
- 'SDP should have max-message-size attribute');
-
- offer = { type: 'offer', sdp: mmsAttributeHelper.sdpWithValue(offer.sdp, remoteValue1) };
- return pc.setRemoteDescription(offer)
- })
- .then(() => pc.createAnswer())
- .then((answer) => pc.setLocalDescription(answer))
- .then(() => {
- assert_not_equals(pc.sctp, null);
- assert_equals(pc.sctp.maxMessageSize, remoteValue1,
- 'maxMessageSize should be the value provided by the remote peer (as long as it is less than canSendSize)');
- })
- .then(() => pc.createOffer()) // Start new O/A exchange that updates max-message-size
- .then((offer) => {
- offer = { type: 'offer', sdp: mmsAttributeHelper.sdpWithValue(offer.sdp, remoteValue2)};
- return pc.setRemoteDescription(offer)
- })
- .then(() => pc.createAnswer())
- .then((answer) => pc.setLocalDescription(answer))
- .then(() => {
- assert_not_equals(pc.sctp, null);
- assert_equals(pc.sctp.maxMessageSize, remoteValue2,
- 'maxMessageSize should be the new value provided by the remote peer (as long as it is less than canSendSize)');
- })
- ;
-}, 'Renegotiate max-message-size with a (non-zero) value provided by the remote peer');
-
-promise_test(t => {
+
+ assert_equals(pc.sctp, null, 'RTCSctpTransport must be null');
+
+ let offer = await generateDataChannelOffer(pc);
+ assert_not_equals(mmsAttributeHelper.getValue(offer.sdp), null,
+ 'SDP should have max-message-size attribute');
+
+ offer = { type: 'offer', sdp: mmsAttributeHelper.sdpWithValue(offer.sdp, remoteSize1) };
+ await pc.setRemoteDescription(offer);
+ let answer = await pc.createAnswer();
+ await pc.setLocalDescription(answer);
+
+ assert_not_equals(pc.sctp, null, 'RTCSctpTransport must be available');
+ assert_equals(pc.sctp.maxMessageSize, remoteSize1,
+ 'maxMessageSize should be the value provided by the remote peer (as long as it is less than canSendSize)');
+
+ // Start new O/A exchange that updates max-message-size to remoteSize2
+ offer = await pc.createOffer();
+ offer = { type: 'offer', sdp: mmsAttributeHelper.sdpWithValue(offer.sdp, remoteSize2)};
+ await pc.setRemoteDescription(offer);
+ answer = await pc.createAnswer();
+ await pc.setLocalDescription(answer);
+
+ assert_not_equals(pc.sctp, null, 'RTCSctpTransport must be available');
+ assert_equals(pc.sctp.maxMessageSize, remoteSize2,
+ 'maxMessageSize should be the new value provided by the remote peer (as long as it is less than canSendSize)');
+
+ // Start new O/A exchange that updates max-message-size to zero
+ offer = await pc.createOffer();
+ offer = { type: 'offer', sdp: mmsAttributeHelper.sdpWithValue(offer.sdp, 0)};
+ await pc.setRemoteDescription(offer);
+ answer = await pc.createAnswer();
+ await pc.setLocalDescription(answer);
+
+ assert_not_equals(pc.sctp, null, 'RTCSctpTransport must be available');
+ assert_equals(pc.sctp.maxMessageSize, canSendSize,
+ 'maxMessageSize should be canSendSize');
+
+ // Start new O/A exchange that updates max-message-size to remoteSize1 again
+ offer = await pc.createOffer();
+ offer = { type: 'offer', sdp: mmsAttributeHelper.sdpWithValue(offer.sdp, remoteSize1)};
+ await pc.setRemoteDescription(offer);
+ answer = await pc.createAnswer();
+ await pc.setLocalDescription(answer);
+
+ assert_not_equals(pc.sctp, null, 'RTCSctpTransport must be available');
+ assert_equals(pc.sctp.maxMessageSize, remoteSize1,
+ 'maxMessageSize should be the new value provided by the remote peer (as long as it is less than canSendSize)');
+}, 'Renegotiate max-message-size with various values provided by the remote peer');
+
+promise_test(async (t) => {
+ assert_not_equals(canSendSize, null, 'canSendSize needs to be determined');
+
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
- assert_equals(pc.sctp, null);
- const largerThanCanSendSize = canSendSize + 1;
-
- return generateDataChannelOffer(pc)
- .then((offer) => {
- assert_not_equals(mmsAttributeHelper.getValue(offer.sdp), null,
- 'SDP should have max-message-size attribute');
-
- offer = { type: 'offer', sdp: mmsAttributeHelper.sdpWithValue(offer.sdp, largerThanCanSendSize) };
- return pc.setRemoteDescription(offer)
- })
- .then(() => pc.createAnswer())
- .then((answer) => pc.setLocalDescription(answer))
- .then(() => {
- assert_not_equals(pc.sctp, null);
- // Test outcome depends on canSendSize value
- if (canSendSize) {
- assert_equals(pc.sctp.maxMessageSize, canSendSize,
- 'A remote value larger than a non-zero canSendSize should limit maxMessageSize to canSendSize');
- } else {
- assert_equals(pc.sctp.maxMessageSize, 65536,
- 'A canSendSize of zero should let the remote value set maxMessageSize');
- }
- });
+
+ assert_equals(pc.sctp, null, 'RTCSctpTransport must be null');
+ const largerThanCanSendSize = canSendSize === 0 ? 0 : canSendSize + 1;
+
+ let offer = await generateDataChannelOffer(pc);
+ assert_not_equals(mmsAttributeHelper.getValue(offer.sdp), null,
+ 'SDP should have max-message-size attribute');
+
+ offer = { type: 'offer', sdp: mmsAttributeHelper.sdpWithValue(offer.sdp, largerThanCanSendSize) };
+ await pc.setRemoteDescription(offer);
+ const answer = await pc.createAnswer();
+ await pc.setLocalDescription(answer);
+
+ assert_not_equals(pc.sctp, null, 'RTCSctpTransport must be available');
+ // Test outcome depends on canSendSize value
+ if (canSendSize !== 0) {
+ assert_equals(pc.sctp.maxMessageSize, canSendSize,
+ 'A remote value larger than a non-zero canSendSize should limit maxMessageSize to canSendSize');
+ } else {
+ assert_equals(pc.sctp.maxMessageSize, Number.POSITIVE_INFINITY,
+ 'A remote value of zero and canSendSize zero should result in "infinity"');
+ }
}, 'max-message-size with a (non-zero) value larger than canSendSize provided by the remote peer');
diff --git a/webrtc/historical.html b/webrtc/historical.html
index ffa28be5bca307..ae7a29dec0c184 100644
--- a/webrtc/historical.html
+++ b/webrtc/historical.html
@@ -4,9 +4,14 @@