diff options
Diffstat (limited to 'testing/web-platform/tests/webrtc/RTCPeerConnection-createDataChannel.html')
-rw-r--r-- | testing/web-platform/tests/webrtc/RTCPeerConnection-createDataChannel.html | 758 |
1 files changed, 758 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-createDataChannel.html b/testing/web-platform/tests/webrtc/RTCPeerConnection-createDataChannel.html new file mode 100644 index 0000000000..7ad8bf7d46 --- /dev/null +++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-createDataChannel.html @@ -0,0 +1,758 @@ +<!doctype html> +<meta charset=utf-8> +<meta name="timeout" content="long"> +<title>RTCPeerConnection.prototype.createDataChannel</title> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src="RTCPeerConnection-helper.js"></script> +<script> +'use strict'; + +const stopTracks = (...streams) => { + streams.forEach(stream => stream.getTracks().forEach(track => track.stop())); +}; + +// Test is based on the following revision: +// https://rawgit.com/w3c/webrtc-pc/1cc5bfc3ff18741033d804c4a71f7891242fb5b3/webrtc.html + +/* + 6.1. RTCPeerConnection Interface Extensions + + partial interface RTCPeerConnection { + [...] + RTCDataChannel createDataChannel(USVString label, + optional RTCDataChannelInit dataChannelDict); + [...] + }; + + 6.2. RTCDataChannel + + interface RTCDataChannel : EventTarget { + readonly attribute USVString label; + readonly attribute boolean ordered; + readonly attribute unsigned short? maxPacketLifeTime; + readonly attribute unsigned short? maxRetransmits; + readonly attribute USVString protocol; + readonly attribute boolean negotiated; + readonly attribute unsigned short? id; + readonly attribute RTCDataChannelState readyState; + readonly attribute unsigned long bufferedAmount; + attribute unsigned long bufferedAmountLowThreshold; + [...] + attribute DOMString binaryType; + [...] + }; + + dictionary RTCDataChannelInit { + boolean ordered = true; + unsigned short maxPacketLifeTime; + unsigned short maxRetransmits; + USVString protocol = ""; + boolean negotiated = false; + [EnforceRange] + unsigned short id; + }; + */ + +test(t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + assert_equals(pc.createDataChannel.length, 1); + assert_throws_js(TypeError, () => pc.createDataChannel()); +}, 'createDataChannel with no argument should throw TypeError'); + +/* + 6.2. createDataChannel + 2. If connection's [[isClosed]] slot is true, throw an InvalidStateError. + */ +test(t => { + const pc = new RTCPeerConnection(); + pc.close(); + assert_equals(pc.signalingState, 'closed', 'signaling state'); + assert_throws_dom('InvalidStateError', () => pc.createDataChannel('')); +}, 'createDataChannel with closed connection should throw InvalidStateError'); + +/* + 6.1. createDataChannel + 4. Let channel have a [[DataChannelLabel]] internal slot initialized to the value of the + first argument. + 6. Let options be the second argument. + 7. Let channel have an [[MaxPacketLifeTime]] internal slot initialized to + option's maxPacketLifeTime member, if present, otherwise null. + 8. Let channel have a [[ReadyState]] internal slot initialized to "connecting". + 9. Let channel have a [[BufferedAmount]] internal slot initialized to 0. + 10. Let channel have an [[MaxRetransmits]] internal slot initialized to + option's maxRetransmits member, if present, otherwise null. + 11. Let channel have an [[Ordered]] internal slot initialized to option's + ordered member. + 12. Let channel have a [[DataChannelProtocol]] internal slot initialized to option's + protocol member. + 14. Let channel have a [[Negotiated]] internal slot initialized to option's negotiated + member. + 15. Let channel have an [[DataChannelId]] internal slot initialized to option's id + member, if it is present and [[Negotiated]] is true, otherwise null. + 21. If the [[DataChannelId]] slot is null (due to no ID being passed into + createDataChannel, or [[Negotiated]] being false), and the DTLS role of the SCTP + transport has already been negotiated, then initialize [[DataChannelId]] to a value + generated by the user agent, according to [RTCWEB-DATA-PROTOCOL], and skip + to the next step. If no available ID could be generated, or if the value of the + [[DataChannelId]] slot is being used by an existing RTCDataChannel, throw an + OperationError exception. + + Note + If the [[DataChannelId]] slot is null after this step, it will be populated once + the DTLS role is determined during the process of setting an RTCSessionDescription. + 22. If channel is the first RTCDataChannel created on connection, update the + negotiation-needed flag for connection. + + + 6.2. RTCDataChannel + + A RTCDataChannel, created with createDataChannel or dispatched via a + RTCDataChannelEvent, MUST initially be in the connecting state + + bufferedAmountLowThreshold + [...] The bufferedAmountLowThreshold is initially zero on each new RTCDataChannel, + but the application may change its value at any time. + + binaryType + [...] When a RTCDataChannel object is created, the binaryType attribute MUST + be initialized to the string "blob". + */ +test(t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + const dc = pc.createDataChannel(''); + + assert_true(dc instanceof RTCDataChannel, 'is RTCDataChannel'); + assert_equals(dc.label, ''); + assert_equals(dc.ordered, true); + assert_equals(dc.maxPacketLifeTime, null); + assert_equals(dc.maxRetransmits, null); + assert_equals(dc.protocol, ''); + assert_equals(dc.negotiated, false); + // Since no offer/answer exchange has occurred yet, the DTLS role is unknown + // and so the ID should be null. + assert_equals(dc.id, null); + assert_equals(dc.readyState, 'connecting'); + assert_equals(dc.bufferedAmount, 0); + assert_equals(dc.bufferedAmountLowThreshold, 0); + assert_equals(dc.binaryType, 'blob'); +}, 'createDataChannel attribute default values'); + +test(t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + const dc = pc.createDataChannel('test', { + ordered: false, + maxRetransmits: 1, + // Note: maxPacketLifeTime is not set in this test. + protocol: 'custom', + negotiated: true, + id: 3 + }); + + assert_true(dc instanceof RTCDataChannel, 'is RTCDataChannel'); + assert_equals(dc.label, 'test'); + assert_equals(dc.ordered, false); + assert_equals(dc.maxPacketLifeTime, null); + assert_equals(dc.maxRetransmits, 1); + assert_equals(dc.protocol, 'custom'); + assert_equals(dc.negotiated, true); + assert_equals(dc.id, 3); + assert_equals(dc.readyState, 'connecting'); + assert_equals(dc.bufferedAmount, 0); + assert_equals(dc.bufferedAmountLowThreshold, 0); + assert_equals(dc.binaryType, 'blob'); + + const dc2 = pc.createDataChannel('test2', { + ordered: false, + maxPacketLifeTime: 42 + }); + assert_equals(dc2.label, 'test2'); + assert_equals(dc2.maxPacketLifeTime, 42); + assert_equals(dc2.maxRetransmits, null); +}, 'createDataChannel with provided parameters should initialize attributes to provided values'); + +/* + 6.2. createDataChannel + 4. Let channel have a [[DataChannelLabel]] internal slot initialized to the value of the + first argument. + + [ECMA262] 7.1.12. ToString(argument) + undefined -> "undefined" + null -> "null" + + [WebIDL] 3.10.15. Convert a DOMString to a sequence of Unicode scalar values + */ +const labels = [ + ['"foo"', 'foo', 'foo'], + ['null', null, 'null'], + ['undefined', undefined, 'undefined'], + ['lone surrogate', '\uD800', '\uFFFD'], +]; +for (const [description, label, expected] of labels) { + test(t => { + const pc = new RTCPeerConnection; + t.add_cleanup(() => pc.close()); + + const dc = pc.createDataChannel(label); + assert_equals(dc.label, expected); + }, `createDataChannel with label ${description} should succeed`); +} + +/* + 6.2. RTCDataChannel + createDataChannel + 11. Let channel have an [[Ordered]] internal slot initialized to option's + ordered member. + */ +test(t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + const dc = pc.createDataChannel('', { ordered: false }); + assert_equals(dc.ordered, false); +}, 'createDataChannel with ordered false should succeed'); + +// true as the default value of a boolean is confusing because null is converted +// to false while undefined is converted to true. +test(t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + const dc1 = pc.createDataChannel('', { ordered: null }); + assert_equals(dc1.ordered, false); + const dc2 = pc.createDataChannel('', { ordered: undefined }); + assert_equals(dc2.ordered, true); +}, 'createDataChannel with ordered null/undefined should succeed'); + +/* + 6.2. RTCDataChannel + createDataChannel + 7. Let channel have an [[MaxPacketLifeTime]] internal slot initialized to + option's maxPacketLifeTime member, if present, otherwise null. + */ +test(t => { + const pc = new RTCPeerConnection; + t.add_cleanup(() => pc.close()); + + const dc = pc.createDataChannel('', { maxPacketLifeTime: 0 }); + assert_equals(dc.maxPacketLifeTime, 0); +}, 'createDataChannel with maxPacketLifeTime 0 should succeed'); + +/* + 6.2. RTCDataChannel + createDataChannel + 10. Let channel have an [[MaxRetransmits]] internal slot initialized to + option's maxRetransmits member, if present, otherwise null. + */ +test(t => { + const pc = new RTCPeerConnection; + t.add_cleanup(() => pc.close()); + + const dc = pc.createDataChannel('', { maxRetransmits: 0 }); + assert_equals(dc.maxRetransmits, 0); +}, 'createDataChannel with maxRetransmits 0 should succeed'); + +/* + 6.2. createDataChannel + 18. If both [[MaxPacketLifeTime]] and [[MaxRetransmits]] attributes are set (not null), + throw a TypeError. + */ +test(t => { + const pc = new RTCPeerConnection; + t.add_cleanup(() => pc.close()); + + pc.createDataChannel('', { + maxPacketLifeTime: undefined, + maxRetransmits: undefined + }); +}, 'createDataChannel with both maxPacketLifeTime and maxRetransmits undefined should succeed'); + +test(t => { + const pc = new RTCPeerConnection; + t.add_cleanup(() => pc.close()); + + assert_throws_js(TypeError, () => pc.createDataChannel('', { + maxPacketLifeTime: 0, + maxRetransmits: 0 + })); + assert_throws_js(TypeError, () => pc.createDataChannel('', { + maxPacketLifeTime: 42, + maxRetransmits: 42 + })); +}, 'createDataChannel with both maxPacketLifeTime and maxRetransmits should throw TypeError'); + +/* + 6.2. RTCDataChannel + createDataChannel + 12. Let channel have a [[DataChannelProtocol]] internal slot initialized to option's + protocol member. + */ +const protocols = [ + ['"foo"', 'foo', 'foo'], + ['null', null, 'null'], + ['undefined', undefined, ''], + ['lone surrogate', '\uD800', '\uFFFD'], +]; +for (const [description, protocol, expected] of protocols) { + test(t => { + const pc = new RTCPeerConnection; + t.add_cleanup(() => pc.close()); + + const dc = pc.createDataChannel('', { protocol }); + assert_equals(dc.protocol, expected); + }, `createDataChannel with protocol ${description} should succeed`); +} + +/* + 6.2. RTCDataChannel + createDataChannel + 20. If [[DataChannelId]] is equal to 65535, which is greater than the maximum allowed + ID of 65534 but still qualifies as an unsigned short, throw a TypeError. + */ +for (const id of [0, 1, 65534, 65535]) { + test((t) => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + const dc = pc.createDataChannel('', { id }); + assert_equals(dc.id, null); + }, `createDataChannel with id ${id} and negotiated not set should succeed, but not set the channel's id`); +} + +for (const id of [0, 1, 65534]) { + test(t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + const dc = pc.createDataChannel('', { 'negotiated': true, 'id': id }); + assert_equals(dc.id, id); + }, `createDataChannel with id ${id} and negotiated true should succeed, and set the channel's id`); +} + +for (const id of [-1, 65536]) { + test((t) => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + assert_throws_js(TypeError, () => pc.createDataChannel('', { id })); + }, `createDataChannel with id ${id} and negotiated not set should throw TypeError`); +} + +for (const id of [-1, 65535, 65536]) { + test(t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + assert_throws_js(TypeError, () => pc.createDataChannel('', + { 'negotiated': true, 'id': id })); + }, `createDataChannel with id ${id} should throw TypeError`); +} + +/* + 6.2. createDataChannel + 5. If [[DataChannelLabel]] is longer than 65535 bytes, throw a TypeError. + */ +test(t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + assert_throws_js(TypeError, () => + pc.createDataChannel('l'.repeat(65536))); + + assert_throws_js(TypeError, () => + pc.createDataChannel('l'.repeat(65536), { + negotiated: true, + id: 42 + })); +}, 'createDataChannel with too long label should throw TypeError'); + +test(t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + assert_throws_js(TypeError, () => + pc.createDataChannel('\u00b5'.repeat(32768))); + + assert_throws_js(TypeError, () => + pc.createDataChannel('\u00b5'.repeat(32768), { + negotiated: true, + id: 42 + })); +}, 'createDataChannel with too long label (2 byte unicode) should throw TypeError'); + +/* + 6.2. label + [...] Scripts are allowed to create multiple RTCDataChannel objects with the same label. + [...] + */ +test(t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + const label = 'test'; + + pc.createDataChannel(label); + pc.createDataChannel(label); +}, 'createDataChannel with same label used twice should not throw'); + +/* + 6.2. createDataChannel + 13. If [[DataChannelProtocol]] is longer than 65535 bytes long, throw a TypeError. + */ + +test(t => { + const pc = new RTCPeerConnection; + t.add_cleanup(() => pc.close()); + const channel = pc.createDataChannel('', { negotiated: true, id: 42 }); + assert_equals(channel.negotiated, true); +}, 'createDataChannel with negotiated true and id should succeed'); + +test(t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + assert_throws_js(TypeError, () => + pc.createDataChannel('', { + protocol: 'p'.repeat(65536) + })); + + assert_throws_js(TypeError, () => + pc.createDataChannel('', { + protocol: 'p'.repeat(65536), + negotiated: true, + id: 42 + })); +}, 'createDataChannel with too long protocol should throw TypeError'); + +test(t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + assert_throws_js(TypeError, () => + pc.createDataChannel('', { + protocol: '\u00b6'.repeat(32768) + })); + + assert_throws_js(TypeError, () => + pc.createDataChannel('', { + protocol: '\u00b6'.repeat(32768), + negotiated: true, + id: 42 + })); +}, 'createDataChannel with too long protocol (2 byte unicode) should throw TypeError'); + +test(t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + const label = 'l'.repeat(65535); + const protocol = 'p'.repeat(65535); + + const dc = pc.createDataChannel(label, { + protocol: protocol + }); + + assert_equals(dc.label, label); + assert_equals(dc.protocol, protocol); +}, 'createDataChannel with maximum length label and protocol should succeed'); + +/* + 6.2 createDataChannel + 15. Let channel have an [[DataChannelId]] internal slot initialized to option's id member, + if it is present and [[Negotiated]] is true, otherwise null. + + NOTE + This means the id member will be ignored if the data channel is negotiated in-band; this + is intentional. Data channels negotiated in-band should have IDs selected based on the + DTLS role, as specified in [RTCWEB-DATA-PROTOCOL]. + */ +test(t => { + const pc = new RTCPeerConnection; + t.add_cleanup(() => pc.close()); + + const dc = pc.createDataChannel('', { + negotiated: false, + }); + assert_equals(dc.negotiated, false, 'Expect dc.negotiated to be false'); +}, 'createDataChannel with negotiated false should succeed'); + +test(t => { + const pc = new RTCPeerConnection; + t.add_cleanup(() => pc.close()); + + const dc = pc.createDataChannel('', { + negotiated: false, + id: 42 + }); + assert_equals(dc.negotiated, false, 'Expect dc.negotiated to be false'); + assert_equals(dc.id, null, 'Expect dc.id to be ignored (null)'); +}, 'createDataChannel with negotiated false and id 42 should ignore the id'); + +/* + 6.2. createDataChannel + 16. If [[Negotiated]] is true and [[DataChannelId]] is null, throw a TypeError. + */ +test(t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + assert_throws_js(TypeError, () => + pc.createDataChannel('test', { + negotiated: true + })); +}, 'createDataChannel with negotiated true and id not defined should throw TypeError'); + +/* + 4.4.1.6. Set the RTCSessionSessionDescription + 2.2.6. If description is of type "answer" or "pranswer", then run the + following steps: + 3. If description negotiates the DTLS role of the SCTP transport, and there is an + RTCDataChannel with a null id, then generate an ID according to + [RTCWEB-DATA-PROTOCOL]. [...] + + 6.1. createDataChannel + 21. If the [[DataChannelId]] slot is null (due to no ID being passed into + createDataChannel, or [[Negotiated]] being false), and the DTLS role of the SCTP + transport has already been negotiated, then initialize [[DataChannelId]] to a value + generated by the user agent, according to [RTCWEB-DATA-PROTOCOL], and skip + to the next step. If no available ID could be generated, or if the value of the + [[DataChannelId]] slot is being used by an existing RTCDataChannel, throw an + OperationError exception. + + Note + If the [[DataChannelId]] slot is null after this step, it will be populated once + the DTLS role is determined during the process of setting an RTCSessionDescription. + */ +promise_test(async t => { + const pc1 = new RTCPeerConnection(); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + t.add_cleanup(() => pc2.close()); + + const negotiatedDc = pc1.createDataChannel('negotiated-channel', { + negotiated: true, + id: 42, + }); + assert_equals(negotiatedDc.id, 42, 'Expect negotiatedDc.id to be 42'); + + const dc1 = pc1.createDataChannel('channel'); + assert_equals(dc1.id, null, 'Expect initial id to be null'); + + const offer = await pc1.createOffer(); + await Promise.all([pc1.setLocalDescription(offer), pc2.setRemoteDescription(offer)]); + const answer = await pc2.createAnswer(); + await pc1.setRemoteDescription(answer); + + assert_not_equals(dc1.id, null, + 'Expect dc1.id to be assigned after remote description has been set'); + + assert_greater_than_equal(dc1.id, 0, + 'Expect dc1.id to be set to valid unsigned short'); + + assert_less_than(dc1.id, 65535, + 'Expect dc1.id to be set to valid unsigned short'); + + const dc2 = pc1.createDataChannel('channel'); + + assert_not_equals(dc2.id, null, + 'Expect dc2.id to be assigned after remote description has been set'); + + assert_greater_than_equal(dc2.id, 0, + 'Expect dc2.id to be set to valid unsigned short'); + + assert_less_than(dc2.id, 65535, + 'Expect dc2.id to be set to valid unsigned short'); + + assert_not_equals(dc2, dc1, + 'Expect channels created from same label to be different'); + + assert_equals(dc2.label, dc1.label, + 'Expect different channels can have the same label but different id'); + + assert_not_equals(dc2.id, dc1.id, + 'Expect different channels can have the same label but different id'); + + assert_equals(negotiatedDc.id, 42, + 'Expect negotiatedDc.id to be 42 after remote description has been set'); +}, 'Channels created (after setRemoteDescription) should have id assigned'); + +test(t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + + const dc1 = pc.createDataChannel('channel-1', { + negotiated: true, + id: 42, + }); + assert_equals(dc1.id, 42, + 'Expect dc1.id to be 42'); + + const dc2 = pc.createDataChannel('channel-2', { + negotiated: true, + id: 43, + }); + assert_equals(dc2.id, 43, + 'Expect dc2.id to be 43'); + + assert_throws_dom('OperationError', () => + pc.createDataChannel('channel-3', { + negotiated: true, + id: 42, + })); + +}, 'Reusing a data channel id that is in use should throw OperationError'); + +// We've seen implementations behaving differently before and after the connection has been +// established. +promise_test(async t => { + const pc1 = new RTCPeerConnection(); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + t.add_cleanup(() => pc2.close()); + + const dc1 = pc1.createDataChannel('channel-1', { + negotiated: true, + id: 42, + }); + assert_equals(dc1.id, 42, 'Expect dc1.id to be 42'); + + const dc2 = pc1.createDataChannel('channel-2', { + negotiated: true, + id: 43, + }); + assert_equals(dc2.id, 43, 'Expect dc2.id to be 43'); + + const offer = await pc1.createOffer(); + await Promise.all([pc1.setLocalDescription(offer), pc2.setRemoteDescription(offer)]); + const answer = await pc2.createAnswer(); + await pc1.setRemoteDescription(answer); + + assert_equals(dc1.id, 42, 'Expect dc1.id to be 42'); + + assert_equals(dc2.id, 43, 'Expect dc2.id to be 43'); + + assert_throws_dom('OperationError', () => + pc1.createDataChannel('channel-3', { + negotiated: true, + id: 42, + })); +}, 'Reusing a data channel id that is in use (after setRemoteDescription) should throw ' + + 'OperationError'); + +promise_test(async t => { + const pc1 = new RTCPeerConnection(); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + t.add_cleanup(() => pc2.close()); + + const dc1 = pc1.createDataChannel('channel-1'); + + const offer = await pc1.createOffer(); + await Promise.all([pc1.setLocalDescription(offer), pc2.setRemoteDescription(offer)]); + const answer = await pc2.createAnswer(); + await pc1.setRemoteDescription(answer); + + assert_not_equals(dc1.id, null, + 'Expect dc1.id to be assigned after remote description has been set'); + + assert_throws_dom('OperationError', () => + pc1.createDataChannel('channel-2', { + negotiated: true, + id: dc1.id, + })); +}, 'Reusing a data channel id that is in use (after setRemoteDescription, negotiated via DCEP) ' + + 'should throw OperationError'); + + +for (const options of [{}, {negotiated: true, id: 0}]) { + const mode = `${options.negotiated? "negotiated " : ""}datachannel`; + + // Based on https://bugzilla.mozilla.org/show_bug.cgi?id=1441723 + promise_test(async t => { + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + + await createDataChannelPair(t, options, pc1); + + const dc = pc1.createDataChannel(''); + assert_equals(dc.readyState, 'connecting', 'Channel should be in the connecting state'); + }, `New ${mode} should be in the connecting state after creation ` + + `(after connection establishment)`); + + promise_test(async t => { + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + const stream = await getNoiseStream({audio: true, video: true}); + t.add_cleanup(() => stopTracks(stream)); + const audio = stream.getAudioTracks()[0]; + const video = stream.getVideoTracks()[0]; + pc1.addTrack(audio, stream); + pc1.addTrack(video, stream); + await createDataChannelPair(t, options, pc1); + }, `addTrack, then creating ${mode}, should negotiate properly`); + + promise_test(async t => { + const pc1 = new RTCPeerConnection({bundlePolicy: "max-bundle"}); + t.add_cleanup(() => pc1.close()); + const stream = await getNoiseStream({audio: true, video: true}); + t.add_cleanup(() => stopTracks(stream)); + const audio = stream.getAudioTracks()[0]; + const video = stream.getVideoTracks()[0]; + pc1.addTrack(audio, stream); + pc1.addTrack(video, stream); + await createDataChannelPair(t, options, pc1); + }, `addTrack, then creating ${mode}, should negotiate properly when max-bundle is used`); + +/* +This test is disabled until https://github.com/w3c/webrtc-pc/issues/2562 +has been resolved; it presupposes that stopping the first transceiver +breaks the transport. + + promise_test(async t => { + const pc1 = new RTCPeerConnection({bundlePolicy: "max-bundle"}); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + t.add_cleanup(() => pc2.close()); + const stream = await getNoiseStream({audio: true, video: true}); + t.add_cleanup(() => stopTracks(stream)); + const audio = stream.getAudioTracks()[0]; + const video = stream.getVideoTracks()[0]; + pc1.addTrack(audio, stream); + pc1.addTrack(video, stream); + const [dc1, dc2] = await createDataChannelPair(t, options, pc1, pc2); + + pc2.getTransceivers()[0].stop(); + const dc1Closed = new Promise(r => dc1.onclose = r); + await exchangeOfferAnswer(pc1, pc2); + await dc1Closed; + }, `Stopping the bundle-tag when there is a ${mode} in the bundle ` + + `should kill the DataChannel`); +*/ +} + +/* + Untestable + 6.1. createDataChannel + 19. If a setting, either [[MaxPacketLifeTime]] or [[MaxRetransmits]], has been set to + indicate unreliable mode, and that value exceeds the maximum value supported + by the user agent, the value MUST be set to the user agents maximum value. + + 23. Return channel and continue the following steps in parallel. + 24. Create channel's associated underlying data transport and configure + it according to the relevant properties of channel. + + Tested in RTCPeerConnection-onnegotiationneeded.html + 22. If channel is the first RTCDataChannel created on connection, update the + negotiation-needed flag for connection. + + Tested in RTCDataChannel-id.html + - Odd/even rules for '.id' + + Tested in RTCDataChannel-dcep.html + - Transmission of '.label' and further options +*/ +</script> |