diff options
Diffstat (limited to 'testing/web-platform/tests/webrtc/RTCPeerConnection-ondatachannel.html')
-rw-r--r-- | testing/web-platform/tests/webrtc/RTCPeerConnection-ondatachannel.html | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-ondatachannel.html b/testing/web-platform/tests/webrtc/RTCPeerConnection-ondatachannel.html new file mode 100644 index 0000000000..08f206fb02 --- /dev/null +++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-ondatachannel.html @@ -0,0 +1,374 @@ +<!doctype html> +<meta charset=utf-8> +<meta name="timeout" content="long"> +<title>RTCPeerConnection.prototype.ondatachannel</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="RTCPeerConnection-helper.js"></script> +<script> +'use strict'; + +// Test is based on the following revision: +// https://rawgit.com/w3c/webrtc-pc/1cc5bfc3ff18741033d804c4a71f7891242fb5b3/webrtc.html + +// The following helper functions are called from RTCPeerConnection-helper.js: +// exchangeIceCandidates +// exchangeOfferAnswer +// createDataChannelPair + +/* + 6.2. RTCDataChannel + When an underlying data transport is to be announced (the other peer created a channel with + negotiated unset or set to false), the user agent of the peer that did not initiate the + creation process MUST queue a task to run the following steps: + 2. Let channel be a newly created RTCDataChannel object. + 7. Set channel's [[ReadyState]] to open (but do not fire the open event, yet). + 8. Fire a datachannel event named datachannel with channel at the RTCPeerConnection object. + + 6.3. RTCDataChannelEvent + Firing a datachannel event named e with an RTCDataChannel channel means that an event with the + name e, which does not bubble (except where otherwise stated) and is not cancelable (except + where otherwise stated), and which uses the RTCDataChannelEvent interface with the channel + attribute set to channel, MUST be created and dispatched at the given target. + + interface RTCDataChannelEvent : Event { + readonly attribute RTCDataChannel channel; + }; + */ +promise_test(async (t) => { + const resolver = new Resolver(); + const pc1 = new RTCPeerConnection(); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + t.add_cleanup(() => pc2.close()); + + let eventCount = 0; + + pc2.ondatachannel = t.step_func((event) => { + eventCount++; + assert_equals(eventCount, 1, + 'Expect data channel event to fire exactly once'); + + assert_true(event instanceof RTCDataChannelEvent, + 'Expect event to be instance of RTCDataChannelEvent'); + + assert_equals(event.bubbles, false); + assert_equals(event.cancelable, false); + + const dc = event.channel; + assert_true(dc instanceof RTCDataChannel, + 'Expect channel to be instance of RTCDataChannel'); + + // The channel should be in the 'open' state already. + // See: https://github.com/w3c/webrtc-pc/pull/1851 + assert_equals(dc.readyState, 'open', + 'Expect channel ready state to be open'); + + resolver.resolve(); + }); + + pc1.createDataChannel('fire-me!'); + + exchangeIceCandidates(pc1, pc2); + await exchangeOfferAnswer(pc1, pc2); + + await resolver; +}, 'Data channel event should fire when new data channel is announced to the remote peer'); + +/* + Since the channel should be in the 'open' state when dispatching via the 'datachannel' event, + we should be able to send data in the event handler. + */ +promise_test(async (t) => { + const resolver = new Resolver(); + const pc1 = new RTCPeerConnection(); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + t.add_cleanup(() => pc2.close()); + + const message = 'meow meow!'; + + pc2.ondatachannel = t.step_func((event) => { + const dc2 = event.channel; + dc2.send(message); + }); + + const dc1 = pc1.createDataChannel('fire-me!'); + dc1.onmessage = t.step_func((event) => { + assert_equals(event.data, message, + 'Received data should be equal to sent data'); + + resolver.resolve(); + }); + + exchangeIceCandidates(pc1, pc2); + await exchangeOfferAnswer(pc1, pc2); + + await resolver; +}, 'Should be able to send data in a datachannel event handler'); + +/* + 6.2. RTCDataChannel + When an underlying data transport is to be announced (the other peer created a channel with + negotiated unset or set to false), the user agent of the peer that did not initiate the + creation process MUST queue a task to run the following steps: + 8. Fire a datachannel event named datachannel with channel at the RTCPeerConnection object. + 9. If the channel's [[ReadyState]] is still open, announce the data channel as open. + */ +promise_test(async (t) => { + const resolver = new Resolver(); + const pc1 = new RTCPeerConnection(); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + t.add_cleanup(() => pc2.close()); + + pc2.ondatachannel = t.step_func((event) => { + const dc = event.channel; + dc.onopen = t.step_func(() => { + assert_unreached('Open event should not fire'); + }); + + // This should prevent triggering the 'open' event + dc.close(); + + // Wait a bit to ensure the 'open' event does NOT fire + t.step_timeout(() => resolver.resolve(), 500); + }); + + pc1.createDataChannel('fire-me!'); + + exchangeIceCandidates(pc1, pc2); + await exchangeOfferAnswer(pc1, pc2); + + await resolver; +}, 'Open event should not be raised when closing the channel in the datachannel event'); + +// Added this test as a result of the discussion in +// https://github.com/w3c/webrtc-pc/pull/1851#discussion_r185976747 +promise_test(async (t) => { + const resolver = new Resolver(); + const pc1 = new RTCPeerConnection(); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + t.add_cleanup(() => pc2.close()); + + pc2.ondatachannel = t.step_func((event) => { + const dc = event.channel; + dc.onopen = t.step_func((event) => { + resolver.resolve(); + }); + + // This should NOT prevent triggering the 'open' event since it enqueues at least two tasks + t.step_timeout(() => { + t.step_timeout(() => { + dc.close() + }, 1); + }, 1); + }); + + pc1.createDataChannel('fire-me!'); + + exchangeIceCandidates(pc1, pc2); + await exchangeOfferAnswer(pc1, pc2); + + await resolver; +}, 'Open event should be raised when closing the channel in the datachannel event after ' + + 'enqueuing a task'); + + +/* + Combination of the two tests above (send and close). + */ +promise_test(async (t) => { + const resolver = new Resolver(); + const pc1 = new RTCPeerConnection(); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + t.add_cleanup(() => pc2.close()); + + const message = 'meow meow!'; + + pc2.ondatachannel = t.step_func((event) => { + const dc2 = event.channel; + dc2.onopen = t.step_func(() => { + assert_unreached('Open event should not fire'); + }); + + // This should send but still prevent triggering the 'open' event + dc2.send(message); + dc2.close(); + }); + + const dc1 = pc1.createDataChannel('fire-me!'); + dc1.onmessage = t.step_func((event) => { + assert_equals(event.data, message, + 'Received data should be equal to sent data'); + + resolver.resolve(); + }); + + exchangeIceCandidates(pc1, pc2); + await exchangeOfferAnswer(pc1, pc2); + + await resolver; +}, 'Open event should not be raised when sending and immediately closing the channel in the ' + + 'datachannel event'); + +/* + 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; + ... + }; + + When an underlying data transport is to be announced (the other peer created a channel with + negotiated unset or set to false), the user agent of the peer that did not initiate the + creation process MUST queue a task to run the following steps: + 2. Let channel be a newly created RTCDataChannel object. + 3. Let configuration be an information bundle received from the other peer as a part of the + process to establish the underlying data transport described by the WebRTC DataChannel + Protocol specification [RTCWEB-DATA-PROTOCOL]. + 4. Initialize channel's [[DataChannelLabel]], [[Ordered]], [[MaxPacketLifeTime]], + [[MaxRetransmits]], [[DataChannelProtocol]], and [[DataChannelId]] internal slots to the + corresponding values in configuration. + 5. Initialize channel's [[Negotiated]] internal slot to false. + 7. Set channel's [[ReadyState]] slot to connecting. + 8. Fire a datachannel event named datachannel with channel at the RTCPeerConnection object. + + Note: More exhaustive tests are defined in RTCDataChannel-dcep + */ + +promise_test(async (t) => { + const resolver = new Resolver(); + const pc1 = new RTCPeerConnection(); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + t.add_cleanup(() => pc2.close()); + + const dc1 = pc1.createDataChannel('test', { + ordered: false, + maxRetransmits: 1, + protocol: 'custom' + }); + + assert_equals(dc1.label, 'test'); + assert_equals(dc1.ordered, false); + assert_equals(dc1.maxPacketLifeTime, null); + assert_equals(dc1.maxRetransmits, 1); + assert_equals(dc1.protocol, 'custom'); + assert_equals(dc1.negotiated, false); + + pc2.ondatachannel = t.step_func((event) => { + const dc2 = event.channel; + assert_true(dc2 instanceof RTCDataChannel, + 'Expect channel to be instance of RTCDataChannel'); + + assert_equals(dc2.label, 'test'); + assert_equals(dc2.ordered, false); + assert_equals(dc2.maxPacketLifeTime, null); + assert_equals(dc2.maxRetransmits, 1); + assert_equals(dc2.protocol, 'custom'); + assert_equals(dc2.negotiated, false); + assert_equals(dc2.id, dc1.id); + + resolver.resolve(); + }); + + exchangeIceCandidates(pc1, pc2); + await exchangeOfferAnswer(pc1, pc2); + + await resolver; +}, 'In-band negotiated channel created on remote peer should match the same configuration as local ' + + 'peer'); + +promise_test(async (t) => { + const resolver = new Resolver(); + const pc1 = new RTCPeerConnection(); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + t.add_cleanup(() => pc2.close()); + + const dc1 = pc1.createDataChannel(''); + + assert_equals(dc1.label, ''); + assert_equals(dc1.ordered, true); + assert_equals(dc1.maxPacketLifeTime, null); + assert_equals(dc1.maxRetransmits, null); + assert_equals(dc1.protocol, ''); + assert_equals(dc1.negotiated, false); + + pc2.ondatachannel = t.step_func((event) => { + const dc2 = event.channel; + assert_true(dc2 instanceof RTCDataChannel, + 'Expect channel to be instance of RTCDataChannel'); + + assert_equals(dc2.label, ''); + assert_equals(dc2.ordered, true); + assert_equals(dc2.maxPacketLifeTime, null); + assert_equals(dc2.maxRetransmits, null); + assert_equals(dc2.protocol, ''); + assert_equals(dc2.negotiated, false); + assert_equals(dc2.id, dc1.id); + + resolver.resolve(); + }); + + exchangeIceCandidates(pc1, pc2); + await exchangeOfferAnswer(pc1, pc2); + + await resolver; +}, 'In-band negotiated channel created on remote peer should match the same (default) ' + + 'configuration as local peer'); + +/* + 6.2. RTCDataChannel + Dictionary RTCDataChannelInit Members + negotiated + The default value of false tells the user agent to announce the + channel in-band and instruct the other peer to dispatch a corresponding + RTCDataChannel object. If set to true, it is up to the application + to negotiate the channel and create a RTCDataChannel object with the + same id at the other peer. + */ +promise_test(async (t) => { + const resolver = new Resolver(); + const pc1 = new RTCPeerConnection(); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + t.add_cleanup(() => pc2.close()); + + pc2.ondatachannel = t.unreached_func('datachannel event should not be fired'); + + pc1.createDataChannel('test', { + negotiated: true, + id: 42 + }); + + exchangeIceCandidates(pc1, pc2); + await exchangeOfferAnswer(pc1, pc2); + + // Wait a bit to ensure the 'datachannel' event does NOT fire + t.step_timeout(() => resolver.resolve(), 500); + await resolver; +}, 'Negotiated channel should not fire datachannel event on remote peer'); + +/* + Non-testable + 6.2. RTCDataChannel + When an underlying data transport is to be announced + 1. If the associated RTCPeerConnection object's [[isClosed]] slot + is true, abort these steps. + + The above step is not testable because to reach it we would have to + close the peer connection just between receiving the in-band negotiated data + channel via DCEP and firing the datachannel event. + */ +</script> |