| <!doctype html> |
| <meta charset=utf-8> |
| <meta name="timeout" content="long"> |
| <title>RTCDataChannel.prototype.bufferedAmount</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: |
| // createDataChannelPair |
| // awaitMessage |
| |
| /* |
| 6.2. RTCDataChannel |
| interface RTCDataChannel : EventTarget { |
| ... |
| readonly attribute unsigned long bufferedAmount; |
| void send(USVString data); |
| void send(Blob data); |
| void send(ArrayBuffer data); |
| void send(ArrayBufferView data); |
| }; |
| |
| bufferedAmount |
| The bufferedAmount attribute must return the number of bytes of application |
| data (UTF-8 text and binary data) that have been queued using send() but that, |
| as of the last time the event loop started executing a task, had not yet been |
| transmitted to the network. (This thus includes any text sent during the |
| execution of the current task, regardless of whether the user agent is able |
| to transmit text asynchronously with script execution.) This does not include |
| framing overhead incurred by the protocol, or buffering done by the operating |
| system or network hardware. The value of the [[BufferedAmount]] slot will only |
| increase with each call to the send() method as long as the [[ReadyState]] slot |
| is open; however, the slot does not reset to zero once the channel closes. When |
| the underlying data transport sends data from its queue, the user agent MUST |
| queue a task that reduces [[BufferedAmount]] with the number of bytes that was |
| sent. |
| |
| |
| [WebMessaging] |
| interface MessageEvent : Event { |
| readonly attribute any data; |
| ... |
| }; |
| */ |
| |
| // Simple ASCII encoded string |
| const helloString = 'hello'; |
| // ASCII encoded buffer representation of the string |
| const helloBuffer = Uint8Array.of(0x68, 0x65, 0x6c, 0x6c, 0x6f); |
| const helloBlob = new Blob([helloBuffer]); |
| |
| // Unicode string with multiple code units |
| const unicodeString = '世界你好'; |
| // UTF-8 encoded buffer representation of the string |
| const unicodeBuffer = Uint8Array.of( |
| 0xe4, 0xb8, 0x96, 0xe7, 0x95, 0x8c, |
| 0xe4, 0xbd, 0xa0, 0xe5, 0xa5, 0xbd); |
| |
| for (const options of [{}, {negotiated: true, id: 0}]) { |
| const mode = `${options.negotiated? "negotiated " : ""}datachannel`; |
| |
| /* |
| Ensure .bufferedAmount is 0 initially for both sides. |
| */ |
| promise_test(async (t) => { |
| const [dc1, dc2] = await createDataChannelPair(t, options); |
| |
| assert_equals(dc1.bufferedAmount, 0, 'Expect bufferedAmount to be 0'); |
| assert_equals(dc2.bufferedAmount, 0, 'Expect bufferedAmount to be 0'); |
| }, `${mode} bufferedAmount initial value should be 0 for both peers`); |
| |
| /* |
| 6.2. send() |
| 3. Execute the sub step that corresponds to the type of the methods argument: |
| |
| string object |
| Let data be the object and increase the bufferedAmount attribute |
| by the number of bytes needed to express data as UTF-8. |
| */ |
| promise_test(async (t) => { |
| const [dc1, dc2] = await createDataChannelPair(t, options); |
| |
| dc1.send(unicodeString); |
| assert_equals(dc1.bufferedAmount, unicodeBuffer.byteLength, |
| 'Expect bufferedAmount to be the byte length of the unicode string'); |
| |
| await awaitMessage(dc2); |
| assert_equals(dc1.bufferedAmount, 0, |
| 'Expect sender bufferedAmount to be reduced after message is sent'); |
| }, `${mode} bufferedAmount should increase to byte length of encoded` + |
| `unicode string sent`); |
| |
| /* |
| 6.2. send() |
| 3. Execute the sub step that corresponds to the type of the methods argument: |
| ArrayBuffer object |
| Let data be the data stored in the buffer described by the ArrayBuffer |
| object and increase the bufferedAmount attribute by the length of the |
| ArrayBuffer in bytes. |
| */ |
| promise_test(async (t) => { |
| const [dc1, dc2] = await createDataChannelPair(t, options); |
| |
| dc1.send(helloBuffer.buffer); |
| assert_equals(dc1.bufferedAmount, helloBuffer.byteLength, |
| 'Expect bufferedAmount to increase to byte length of sent buffer'); |
| |
| await awaitMessage(dc2); |
| assert_equals(dc1.bufferedAmount, 0, |
| 'Expect sender bufferedAmount to be reduced after message is sent'); |
| }, `${mode} bufferedAmount should increase to byte length of buffer sent`); |
| |
| /* |
| 6.2. send() |
| 3. Execute the sub step that corresponds to the type of the methods argument: |
| Blob object |
| Let data be the raw data represented by the Blob object and increase |
| the bufferedAmount attribute by the size of data, in bytes. |
| */ |
| promise_test(async (t) => { |
| const [dc1, dc2] = await createDataChannelPair(t, options); |
| |
| dc1.send(helloBlob); |
| assert_equals(dc1.bufferedAmount, helloBlob.size, |
| 'Expect bufferedAmount to increase to size of sent blob'); |
| |
| await awaitMessage(dc2); |
| assert_equals(dc1.bufferedAmount, 0, |
| 'Expect sender bufferedAmount to be reduced after message is sent'); |
| }, `${mode} bufferedAmount should increase to size of blob sent`); |
| |
| // Test sending 3 messages: helloBuffer, unicodeString, helloBlob |
| promise_test(async (t) => { |
| const resolver = new Resolver(); |
| let messageCount = 0; |
| |
| const [dc1, dc2] = await createDataChannelPair(t, options); |
| dc2.onmessage = t.step_func(() => { |
| if (++messageCount === 3) { |
| assert_equals(dc1.bufferedAmount, 0, |
| 'Expect sender bufferedAmount to be reduced after message is sent'); |
| resolver.resolve(); |
| } |
| }); |
| |
| dc1.send(helloBuffer); |
| assert_equals(dc1.bufferedAmount, helloString.length, |
| 'Expect bufferedAmount to be the total length of all messages queued to send'); |
| |
| dc1.send(unicodeString); |
| assert_equals(dc1.bufferedAmount, |
| helloString.length + unicodeBuffer.byteLength, |
| 'Expect bufferedAmount to be the total length of all messages queued to send'); |
| |
| dc1.send(helloBlob); |
| assert_equals(dc1.bufferedAmount, |
| helloString.length*2 + unicodeBuffer.byteLength, |
| 'Expect bufferedAmount to be the total length of all messages queued to send'); |
| |
| await resolver; |
| }, `${mode} bufferedAmount should increase by byte length for each message sent`); |
| |
| promise_test(async (t) => { |
| const [dc1] = await createDataChannelPair(t, options); |
| |
| dc1.send(helloBuffer.buffer); |
| assert_equals(dc1.bufferedAmount, helloBuffer.byteLength, |
| 'Expect bufferedAmount to increase to byte length of sent buffer'); |
| |
| dc1.close(); |
| assert_equals(dc1.bufferedAmount, helloBuffer.byteLength, |
| 'Expect bufferedAmount to not decrease immediately after closing the channel'); |
| }, `${mode} bufferedAmount should not decrease immediately after initiating closure`); |
| |
| promise_test(async (t) => { |
| const pc1 = new RTCPeerConnection(); |
| t.add_cleanup(() => pc1.close()); |
| const [dc1] = await createDataChannelPair(t, options, pc1); |
| |
| dc1.send(helloBuffer.buffer); |
| assert_equals(dc1.bufferedAmount, helloBuffer.byteLength, |
| 'Expect bufferedAmount to increase to byte length of sent buffer'); |
| |
| pc1.close(); |
| assert_equals(dc1.bufferedAmount, helloBuffer.byteLength, |
| 'Expect bufferedAmount to not decrease after closing the peer connection'); |
| }, `${mode} bufferedAmount should not decrease after closing the peer connection`); |
| |
| promise_test(async t => { |
| const [channel1, channel2] = await createDataChannelPair(t, options); |
| channel1.addEventListener('bufferedamountlow', t.step_func_done(() => { |
| assert_true(channel1.bufferedAmount <= channel1.bufferedAmountLowThreshold); |
| })); |
| const eventWatcher = new EventWatcher(t, channel1, ['bufferedamountlow']); |
| channel1.send(helloString); |
| await eventWatcher.wait_for(['bufferedamountlow']); |
| }, `${mode} bufferedamountlow event fires after send() is complete`); |
| |
| promise_test(async t => { |
| const [channel1, channel2] = await createDataChannelPair(t, options); |
| channel1.send(helloString); |
| assert_equals(channel1.bufferedAmount, helloString.length); |
| await awaitMessage(channel2); |
| assert_equals(channel1.bufferedAmount, 0); |
| }, `${mode} bufferedamount is data.length on send(data)`); |
| |
| promise_test(async t => { |
| const [channel1, channel2] = await createDataChannelPair(t, options); |
| channel1.send(helloString); |
| assert_equals(channel1.bufferedAmount, helloString.length); |
| assert_equals(channel1.bufferedAmount, helloString.length); |
| }, `${mode} bufferedamount returns the same amount if no more data is`); |
| |
| promise_test(async t => { |
| const [channel1, channel2] = await createDataChannelPair(t, options); |
| let eventFireCount = 0; |
| channel1.addEventListener('bufferedamountlow', t.step_func(() => { |
| assert_true(channel1.bufferedAmount <= channel1.bufferedAmountLowThreshold); |
| assert_equals(++eventFireCount, 1); |
| })); |
| const eventWatcher = new EventWatcher(t, channel1, ['bufferedamountlow']); |
| channel1.send(helloString); |
| assert_equals(channel1.bufferedAmount, helloString.length); |
| channel1.send(helloString); |
| assert_equals(channel1.bufferedAmount, 2 * helloString.length); |
| await eventWatcher.wait_for(['bufferedamountlow']); |
| }, `${mode} bufferedamountlow event fires only once after multiple` + |
| ` consecutive send() calls`); |
| |
| promise_test(async t => { |
| const [channel1, channel2] = await createDataChannelPair(t, options); |
| const eventWatcher = new EventWatcher(t, channel1, ['bufferedamountlow']); |
| channel1.send(helloString); |
| assert_equals(channel1.bufferedAmount, helloString.length); |
| await eventWatcher.wait_for(['bufferedamountlow']); |
| assert_equals(await awaitMessage(channel2), helloString); |
| channel1.send(helloString); |
| assert_equals(channel1.bufferedAmount, helloString.length); |
| await eventWatcher.wait_for(['bufferedamountlow']); |
| assert_equals(await awaitMessage(channel2), helloString); |
| }, `${mode} bufferedamountlow event fires after each sent message`); |
| } |
| </script> |