| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 1 | <!doctype html> |
| 2 | <meta charset=utf-8> |
| Byron Campen [:bwc] | bb9278b | 2019-07-10 15:11:08 | [diff] [blame] | 3 | <meta name="timeout" content="long"> |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 4 | <title>RTCDataChannel.prototype.bufferedAmount</title> |
| 5 | <script src="/resources/testharness.js"></script> |
| 6 | <script src="/resources/testharnessreport.js"></script> |
| 7 | <script src="RTCPeerConnection-helper.js"></script> |
| 8 | <script> |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 9 | 'use strict'; |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 10 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 11 | // Test is based on the following revision: |
| 12 | // https://rawgit.com/w3c/webrtc-pc/1cc5bfc3ff18741033d804c4a71f7891242fb5b3/webrtc.html |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 13 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 14 | // The following helper functions are called from RTCPeerConnection-helper.js: |
| 15 | // createDataChannelPair |
| 16 | // awaitMessage |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 17 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 18 | /* |
| 19 | 6.2. RTCDataChannel |
| 20 | interface RTCDataChannel : EventTarget { |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 21 | ... |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 22 | readonly attribute unsigned long bufferedAmount; |
| 23 | void send(USVString data); |
| 24 | void send(Blob data); |
| 25 | void send(ArrayBuffer data); |
| 26 | void send(ArrayBufferView data); |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 27 | }; |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 28 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 29 | bufferedAmount |
| 30 | The bufferedAmount attribute must return the number of bytes of application |
| 31 | data (UTF-8 text and binary data) that have been queued using send() but that, |
| 32 | as of the last time the event loop started executing a task, had not yet been |
| 33 | transmitted to the network. (This thus includes any text sent during the |
| 34 | execution of the current task, regardless of whether the user agent is able |
| 35 | to transmit text asynchronously with script execution.) This does not include |
| 36 | framing overhead incurred by the protocol, or buffering done by the operating |
| 37 | system or network hardware. The value of the [[BufferedAmount]] slot will only |
| 38 | increase with each call to the send() method as long as the [[ReadyState]] slot |
| 39 | is open; however, the slot does not reset to zero once the channel closes. When |
| 40 | the underlying data transport sends data from its queue, the user agent MUST |
| 41 | queue a task that reduces [[BufferedAmount]] with the number of bytes that was |
| 42 | sent. |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 43 | |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 44 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 45 | [WebMessaging] |
| 46 | interface MessageEvent : Event { |
| 47 | readonly attribute any data; |
| 48 | ... |
| 49 | }; |
| 50 | */ |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 51 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 52 | // Simple ASCII encoded string |
| 53 | const helloString = 'hello'; |
| 54 | // ASCII encoded buffer representation of the string |
| 55 | const helloBuffer = Uint8Array.of(0x68, 0x65, 0x6c, 0x6c, 0x6f); |
| 56 | const helloBlob = new Blob([helloBuffer]); |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 57 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 58 | // Unicode string with multiple code units |
| 59 | const unicodeString = '世界你好'; |
| 60 | // UTF-8 encoded buffer representation of the string |
| 61 | const unicodeBuffer = Uint8Array.of( |
| 62 | 0xe4, 0xb8, 0x96, 0xe7, 0x95, 0x8c, |
| 63 | 0xe4, 0xbd, 0xa0, 0xe5, 0xa5, 0xbd); |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 64 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 65 | /* |
| 66 | Ensure .bufferedAmount is 0 initially for both sides. |
| 67 | */ |
| 68 | promise_test(async (t) => { |
| 69 | const pc1 = new RTCPeerConnection(); |
| 70 | const pc2 = new RTCPeerConnection(); |
| 71 | t.add_cleanup(() => pc1.close()); |
| 72 | t.add_cleanup(() => pc2.close()); |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 73 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 74 | const [dc1, dc2] = await createDataChannelPair(pc1, pc2); |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 75 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 76 | assert_equals(dc1.bufferedAmount, 0, 'Expect bufferedAmount to be 0'); |
| 77 | assert_equals(dc2.bufferedAmount, 0, 'Expect bufferedAmount to be 0'); |
| 78 | }, 'bufferedAmount initial value should be 0 for both peers'); |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 79 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 80 | /* |
| 81 | 6.2. send() |
| 82 | 3. Execute the sub step that corresponds to the type of the methods argument: |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 83 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 84 | string object |
| 85 | Let data be the object and increase the bufferedAmount attribute |
| 86 | by the number of bytes needed to express data as UTF-8. |
| 87 | */ |
| 88 | promise_test(async (t) => { |
| 89 | const pc1 = new RTCPeerConnection(); |
| 90 | const pc2 = new RTCPeerConnection(); |
| 91 | t.add_cleanup(() => pc1.close()); |
| 92 | t.add_cleanup(() => pc2.close()); |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 93 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 94 | const [dc1, dc2] = await createDataChannelPair(pc1, pc2); |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 95 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 96 | dc1.send(unicodeString); |
| 97 | assert_equals(dc1.bufferedAmount, unicodeBuffer.byteLength, |
| 98 | 'Expect bufferedAmount to be the byte length of the unicode string'); |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 99 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 100 | await awaitMessage(dc2); |
| 101 | assert_equals(dc1.bufferedAmount, 0, |
| 102 | 'Expect sender bufferedAmount to be reduced after message is sent'); |
| 103 | }, 'bufferedAmount should increase to byte length of encoded unicode string sent'); |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 104 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 105 | /* |
| 106 | 6.2. send() |
| 107 | 3. Execute the sub step that corresponds to the type of the methods argument: |
| 108 | ArrayBuffer object |
| 109 | Let data be the data stored in the buffer described by the ArrayBuffer |
| 110 | object and increase the bufferedAmount attribute by the length of the |
| 111 | ArrayBuffer in bytes. |
| 112 | */ |
| 113 | promise_test(async (t) => { |
| 114 | const pc1 = new RTCPeerConnection(); |
| 115 | const pc2 = new RTCPeerConnection(); |
| 116 | t.add_cleanup(() => pc1.close()); |
| 117 | t.add_cleanup(() => pc2.close()); |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 118 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 119 | const [dc1, dc2] = await createDataChannelPair(pc1, pc2); |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 120 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 121 | dc1.send(helloBuffer.buffer); |
| 122 | assert_equals(dc1.bufferedAmount, helloBuffer.byteLength, |
| 123 | 'Expect bufferedAmount to increase to byte length of sent buffer'); |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 124 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 125 | await awaitMessage(dc2); |
| 126 | assert_equals(dc1.bufferedAmount, 0, |
| 127 | 'Expect sender bufferedAmount to be reduced after message is sent'); |
| 128 | }, 'bufferedAmount should increase to byte length of buffer sent'); |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 129 | |
| youennf | 0e97d09 | 2019-03-23 18:02:56 | [diff] [blame] | 130 | /* |
| 131 | 6.2. send() |
| 132 | 3. Execute the sub step that corresponds to the type of the methods argument: |
| 133 | Blob object |
| 134 | Let data be the raw data represented by the Blob object and increase |
| 135 | the bufferedAmount attribute by the size of data, in bytes. |
| 136 | */ |
| 137 | promise_test(async (t) => { |
| 138 | const pc1 = new RTCPeerConnection(); |
| 139 | const pc2 = new RTCPeerConnection(); |
| 140 | t.add_cleanup(() => pc1.close()); |
| 141 | t.add_cleanup(() => pc2.close()); |
| 142 | |
| 143 | const [dc1, dc2] = await createDataChannelPair(pc1, pc2); |
| 144 | |
| 145 | dc1.send(helloBlob); |
| 146 | assert_equals(dc1.bufferedAmount, helloBlob.size, |
| 147 | 'Expect bufferedAmount to increase to size of sent blob'); |
| 148 | |
| 149 | await awaitMessage(dc2); |
| 150 | assert_equals(dc1.bufferedAmount, 0, |
| 151 | 'Expect sender bufferedAmount to be reduced after message is sent'); |
| 152 | }, 'bufferedAmount should increase to size of blob sent'); |
| 153 | |
| 154 | // Test sending 3 messages: helloBuffer, unicodeString, helloBlob |
| 155 | promise_test(async (t) => { |
| 156 | const resolver = new Resolver(); |
| 157 | const pc1 = new RTCPeerConnection(); |
| 158 | const pc2 = new RTCPeerConnection(); |
| 159 | t.add_cleanup(() => pc1.close()); |
| 160 | t.add_cleanup(() => pc2.close()); |
| 161 | |
| 162 | let messageCount = 0; |
| 163 | |
| 164 | const [dc1, dc2] = await createDataChannelPair(pc1, pc2); |
| 165 | const onMessage = t.step_func(() => { |
| 166 | if (++messageCount === 3) { |
| 167 | assert_equals(dc1.bufferedAmount, 0, |
| 168 | 'Expect sender bufferedAmount to be reduced after message is sent'); |
| 169 | resolver.resolve(); |
| 170 | } |
| 171 | }); |
| 172 | |
| 173 | dc2.addEventListener('message', onMessage); |
| 174 | |
| 175 | dc1.send(helloBuffer); |
| 176 | assert_equals(dc1.bufferedAmount, helloString.length, |
| 177 | 'Expect bufferedAmount to be the total length of all messages queued to send'); |
| 178 | |
| 179 | dc1.send(unicodeString); |
| 180 | assert_equals(dc1.bufferedAmount, |
| 181 | helloString.length + unicodeBuffer.byteLength, |
| 182 | 'Expect bufferedAmount to be the total length of all messages queued to send'); |
| 183 | |
| 184 | dc1.send(helloBlob); |
| 185 | assert_equals(dc1.bufferedAmount, |
| 186 | helloString.length*2 + unicodeBuffer.byteLength, |
| 187 | 'Expect bufferedAmount to be the total length of all messages queued to send'); |
| 188 | |
| 189 | await resolver; |
| 190 | }, 'bufferedAmount should increase by byte length for each message sent'); |
| 191 | |
| 192 | promise_test(async (t) => { |
| 193 | const pc1 = new RTCPeerConnection(); |
| 194 | const pc2 = new RTCPeerConnection(); |
| 195 | t.add_cleanup(() => pc1.close()); |
| 196 | t.add_cleanup(() => pc2.close()); |
| 197 | |
| 198 | const [dc1] = await createDataChannelPair(pc1, pc2); |
| 199 | |
| 200 | dc1.send(helloBuffer.buffer); |
| 201 | assert_equals(dc1.bufferedAmount, helloBuffer.byteLength, |
| 202 | 'Expect bufferedAmount to increase to byte length of sent buffer'); |
| 203 | |
| 204 | dc1.close(); |
| 205 | assert_equals(dc1.bufferedAmount, helloBuffer.byteLength, |
| 206 | 'Expect bufferedAmount to not decrease immediately after closing the channel'); |
| 207 | }, 'bufferedAmount should not decrease immediately after initiating closure'); |
| 208 | |
| 209 | promise_test(async (t) => { |
| 210 | const pc1 = new RTCPeerConnection(); |
| 211 | const pc2 = new RTCPeerConnection(); |
| 212 | t.add_cleanup(() => pc1.close()); |
| 213 | t.add_cleanup(() => pc2.close()); |
| 214 | |
| 215 | const [dc1] = await createDataChannelPair(pc1, pc2); |
| 216 | |
| 217 | dc1.send(helloBuffer.buffer); |
| 218 | assert_equals(dc1.bufferedAmount, helloBuffer.byteLength, |
| 219 | 'Expect bufferedAmount to increase to byte length of sent buffer'); |
| 220 | |
| 221 | pc1.close(); |
| 222 | assert_equals(dc1.bufferedAmount, helloBuffer.byteLength, |
| 223 | 'Expect bufferedAmount to not decrease after closing the peer connection'); |
| 224 | }, 'bufferedAmount should not decrease after closing the peer connection'); |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 225 | |
| Marina Ciocea | 791052b | 2019-03-12 06:56:04 | [diff] [blame] | 226 | promise_test(async t => { |
| 227 | const [channel1, channel2] = await createDataChannelPair(); |
| 228 | channel1.addEventListener('bufferedamountlow', t.step_func_done(() => { |
| 229 | assert_true(channel1.bufferedAmount <= channel1.bufferedAmountLowThreshold); |
| 230 | })); |
| 231 | const eventWatcher = new EventWatcher(t, channel1, ['bufferedamountlow']); |
| 232 | channel1.send(helloString); |
| 233 | await eventWatcher.wait_for(['bufferedamountlow']); |
| 234 | }, 'Data channel bufferedamountlow event fires after send() is complete'); |
| 235 | |
| 236 | promise_test(async t => { |
| 237 | const [channel1, channel2] = await createDataChannelPair(); |
| 238 | channel1.send(helloString); |
| 239 | assert_equals(channel1.bufferedAmount, helloString.length); |
| 240 | await awaitMessage(channel2); |
| 241 | assert_equals(channel1.bufferedAmount, 0); |
| 242 | }, 'Data channel bufferedamount is data.length on send(data)'); |
| 243 | |
| 244 | promise_test(async t => { |
| 245 | const [channel1, channel2] = await createDataChannelPair(); |
| 246 | channel1.send(helloString); |
| 247 | assert_equals(channel1.bufferedAmount, helloString.length); |
| 248 | assert_equals(channel1.bufferedAmount, helloString.length); |
| 249 | }, 'Data channel bufferedamount returns the same amount if no more data is' + |
| 250 | ' sent on the channel'); |
| 251 | |
| 252 | promise_test(async t => { |
| 253 | const [channel1, channel2] = await createDataChannelPair(); |
| 254 | let eventFireCount = 0; |
| 255 | channel1.addEventListener('bufferedamountlow', t.step_func(() => { |
| 256 | assert_true(channel1.bufferedAmount <= channel1.bufferedAmountLowThreshold); |
| 257 | assert_equals(++eventFireCount, 1); |
| 258 | })); |
| 259 | const eventWatcher = new EventWatcher(t, channel1, ['bufferedamountlow']); |
| 260 | channel1.send(helloString); |
| 261 | assert_equals(channel1.bufferedAmount, helloString.length); |
| 262 | channel1.send(helloString); |
| 263 | assert_equals(channel1.bufferedAmount, 2 * helloString.length); |
| 264 | await eventWatcher.wait_for(['bufferedamountlow']); |
| 265 | }, 'Data channel bufferedamountlow event fires only once after multiple' + |
| 266 | ' consecutive send() calls'); |
| 267 | |
| 268 | promise_test(async t => { |
| 269 | const [channel1, channel2] = await createDataChannelPair(); |
| 270 | const eventWatcher = new EventWatcher(t, channel1, ['bufferedamountlow']); |
| 271 | channel1.send(helloString); |
| 272 | assert_equals(channel1.bufferedAmount, helloString.length); |
| 273 | await eventWatcher.wait_for(['bufferedamountlow']); |
| 274 | assert_equals(await awaitMessage(channel2), helloString); |
| 275 | channel1.send(helloString); |
| 276 | assert_equals(channel1.bufferedAmount, helloString.length); |
| 277 | await eventWatcher.wait_for(['bufferedamountlow']); |
| 278 | assert_equals(await awaitMessage(channel2), helloString); |
| 279 | }, 'Data channel bufferedamountlow event fires after each sent message'); |
| 280 | |
| Soares Chen | 17354db | 2017-06-13 18:39:04 | [diff] [blame] | 281 | </script> |