]> git.proxmox.com Git - mirror_novnc.git/blame - tests/test.websock.js
Add deflator helper class for deflating data
[mirror_novnc.git] / tests / test.websock.js
CommitLineData
2b5f94fa 1const expect = chai.expect;
2cccf753 2
dfae3209
SR
3import Websock from '../core/websock.js';
4import FakeWebSocket from './fake.websocket.js';
5
2c5491e1 6describe('Websock', function () {
2cccf753
SR
7 "use strict";
8
9 describe('Queue methods', function () {
2b5f94fa
JD
10 let sock;
11 const RQ_TEMPLATE = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]);
2cccf753
SR
12
13 beforeEach(function () {
14 sock = new Websock();
38781d93
SR
15 // skip init
16 sock._allocate_buffers();
17 sock._rQ.set(RQ_TEMPLATE);
18 sock._rQlen = RQ_TEMPLATE.length;
2cccf753
SR
19 });
20 describe('rQlen', function () {
21 it('should return the length of the receive queue', function () {
8a189a62 22 sock.rQi = 0;
2cccf753 23
8a189a62 24 expect(sock.rQlen).to.equal(RQ_TEMPLATE.length);
2cccf753
SR
25 });
26
27 it("should return the proper length if we read some from the receive queue", function () {
8a189a62 28 sock.rQi = 1;
2cccf753 29
8a189a62 30 expect(sock.rQlen).to.equal(RQ_TEMPLATE.length - 1);
2cccf753
SR
31 });
32 });
33
34 describe('rQpeek8', function () {
35 it('should peek at the next byte without poping it off the queue', function () {
8a189a62 36 const bef_len = sock.rQlen;
2b5f94fa 37 const peek = sock.rQpeek8();
2cccf753 38 expect(sock.rQpeek8()).to.equal(peek);
8a189a62 39 expect(sock.rQlen).to.equal(bef_len);
2cccf753
SR
40 });
41 });
42
8a189a62 43 describe('rQshift8()', function () {
2cccf753 44 it('should pop a single byte from the receive queue', function () {
2b5f94fa 45 const peek = sock.rQpeek8();
8a189a62 46 const bef_len = sock.rQlen;
2cccf753 47 expect(sock.rQshift8()).to.equal(peek);
8a189a62 48 expect(sock.rQlen).to.equal(bef_len - 1);
2cccf753
SR
49 });
50 });
51
8a189a62 52 describe('rQshift16()', function () {
2cccf753 53 it('should pop two bytes from the receive queue and return a single number', function () {
8a189a62 54 const bef_len = sock.rQlen;
2b5f94fa 55 const expected = (RQ_TEMPLATE[0] << 8) + RQ_TEMPLATE[1];
2cccf753 56 expect(sock.rQshift16()).to.equal(expected);
8a189a62 57 expect(sock.rQlen).to.equal(bef_len - 2);
2cccf753
SR
58 });
59 });
60
8a189a62 61 describe('rQshift32()', function () {
2cccf753 62 it('should pop four bytes from the receive queue and return a single number', function () {
8a189a62 63 const bef_len = sock.rQlen;
2b5f94fa 64 const expected = (RQ_TEMPLATE[0] << 24) +
2cccf753
SR
65 (RQ_TEMPLATE[1] << 16) +
66 (RQ_TEMPLATE[2] << 8) +
67 RQ_TEMPLATE[3];
68 expect(sock.rQshift32()).to.equal(expected);
8a189a62 69 expect(sock.rQlen).to.equal(bef_len - 4);
2cccf753
SR
70 });
71 });
72
73 describe('rQshiftStr', function () {
74 it('should shift the given number of bytes off of the receive queue and return a string', function () {
8a189a62
JD
75 const bef_len = sock.rQlen;
76 const bef_rQi = sock.rQi;
2b5f94fa 77 const shifted = sock.rQshiftStr(3);
2cccf753 78 expect(shifted).to.be.a('string');
38781d93 79 expect(shifted).to.equal(String.fromCharCode.apply(null, Array.prototype.slice.call(new Uint8Array(RQ_TEMPLATE.buffer, bef_rQi, 3))));
8a189a62 80 expect(sock.rQlen).to.equal(bef_len - 3);
2cccf753
SR
81 });
82
83 it('should shift the entire rest of the queue off if no length is given', function () {
84 sock.rQshiftStr();
8a189a62 85 expect(sock.rQlen).to.equal(0);
2cccf753 86 });
178b92d3
SM
87
88 it('should be able to handle very large strings', function () {
89 const BIG_LEN = 500000;
90 const RQ_BIG = new Uint8Array(BIG_LEN);
91 let expected = "";
92 let letterCode = 'a'.charCodeAt(0);
93 for (let i = 0; i < BIG_LEN; i++) {
94 RQ_BIG[i] = letterCode;
95 expected += String.fromCharCode(letterCode);
96
97 if (letterCode < 'z'.charCodeAt(0)) {
98 letterCode++;
99 } else {
100 letterCode = 'a'.charCodeAt(0);
101 }
102 }
103 sock._rQ.set(RQ_BIG);
104 sock._rQlen = RQ_BIG.length;
105
106 const shifted = sock.rQshiftStr();
107
108 expect(shifted).to.be.equal(expected);
8a189a62 109 expect(sock.rQlen).to.equal(0);
178b92d3 110 });
2cccf753
SR
111 });
112
113 describe('rQshiftBytes', function () {
114 it('should shift the given number of bytes of the receive queue and return an array', function () {
8a189a62
JD
115 const bef_len = sock.rQlen;
116 const bef_rQi = sock.rQi;
2b5f94fa 117 const shifted = sock.rQshiftBytes(3);
38781d93
SR
118 expect(shifted).to.be.an.instanceof(Uint8Array);
119 expect(shifted).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, bef_rQi, 3));
8a189a62 120 expect(sock.rQlen).to.equal(bef_len - 3);
2cccf753
SR
121 });
122
123 it('should shift the entire rest of the queue off if no length is given', function () {
124 sock.rQshiftBytes();
8a189a62 125 expect(sock.rQlen).to.equal(0);
2cccf753
SR
126 });
127 });
128
129 describe('rQslice', function () {
130 beforeEach(function () {
8a189a62 131 sock.rQi = 0;
2cccf753
SR
132 });
133
134 it('should not modify the receive queue', function () {
8a189a62 135 const bef_len = sock.rQlen;
2cccf753 136 sock.rQslice(0, 2);
8a189a62 137 expect(sock.rQlen).to.equal(bef_len);
2cccf753
SR
138 });
139
140 it('should return an array containing the given slice of the receive queue', function () {
2b5f94fa 141 const sl = sock.rQslice(0, 2);
38781d93
SR
142 expect(sl).to.be.an.instanceof(Uint8Array);
143 expect(sl).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 0, 2));
2cccf753
SR
144 });
145
146 it('should use the rest of the receive queue if no end is given', function () {
2b5f94fa 147 const sl = sock.rQslice(1);
2cccf753 148 expect(sl).to.have.length(RQ_TEMPLATE.length - 1);
38781d93 149 expect(sl).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 1));
2cccf753
SR
150 });
151
152 it('should take the current rQi in to account', function () {
8a189a62 153 sock.rQi = 1;
38781d93 154 expect(sock.rQslice(0, 2)).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 1, 2));
2cccf753
SR
155 });
156 });
157
158 describe('rQwait', function () {
159 beforeEach(function () {
8a189a62 160 sock.rQi = 0;
2cccf753
SR
161 });
162
163 it('should return true if there are not enough bytes in the receive queue', function () {
164 expect(sock.rQwait('hi', RQ_TEMPLATE.length + 1)).to.be.true;
165 });
166
167 it('should return false if there are enough bytes in the receive queue', function () {
168 expect(sock.rQwait('hi', RQ_TEMPLATE.length)).to.be.false;
169 });
170
171 it('should return true and reduce rQi by "goback" if there are not enough bytes', function () {
8a189a62 172 sock.rQi = 5;
2cccf753 173 expect(sock.rQwait('hi', RQ_TEMPLATE.length, 4)).to.be.true;
8a189a62 174 expect(sock.rQi).to.equal(1);
2cccf753
SR
175 });
176
177 it('should raise an error if we try to go back more than possible', function () {
8a189a62 178 sock.rQi = 5;
651c23ec 179 expect(() => sock.rQwait('hi', RQ_TEMPLATE.length, 6)).to.throw(Error);
2cccf753
SR
180 });
181
182 it('should not reduce rQi if there are enough bytes', function () {
8a189a62 183 sock.rQi = 5;
2cccf753 184 sock.rQwait('hi', 1, 6);
8a189a62 185 expect(sock.rQi).to.equal(5);
2cccf753
SR
186 });
187 });
188
189 describe('flush', function () {
190 beforeEach(function () {
191 sock._websocket = {
192 send: sinon.spy()
193 };
194 });
195
c4482d2d 196 it('should actually send on the websocket', function () {
2cccf753 197 sock._websocket.bufferedAmount = 8;
0ae5c54a 198 sock._websocket.readyState = WebSocket.OPEN;
9ff86fb7
SR
199 sock._sQ = new Uint8Array([1, 2, 3]);
200 sock._sQlen = 3;
2b5f94fa 201 const encoded = sock._encode_message();
2cccf753
SR
202
203 sock.flush();
204 expect(sock._websocket.send).to.have.been.calledOnce;
205 expect(sock._websocket.send).to.have.been.calledWith(encoded);
206 });
207
2cccf753 208 it('should not call send if we do not have anything queued up', function () {
9ff86fb7 209 sock._sQlen = 0;
2cccf753
SR
210 sock._websocket.bufferedAmount = 8;
211
212 sock.flush();
213
214 expect(sock._websocket.send).not.to.have.been.called;
215 });
2cccf753
SR
216 });
217
218 describe('send', function () {
219 beforeEach(function () {
220 sock.flush = sinon.spy();
221 });
222
223 it('should add to the send queue', function () {
224 sock.send([1, 2, 3]);
8a189a62 225 const sq = sock.sQ;
9ff86fb7 226 expect(new Uint8Array(sq.buffer, sock._sQlen - 3, 3)).to.array.equal(new Uint8Array([1, 2, 3]));
2cccf753
SR
227 });
228
229 it('should call flush', function () {
230 sock.send([1, 2, 3]);
231 expect(sock.flush).to.have.been.calledOnce;
232 });
233 });
234
235 describe('send_string', function () {
236 beforeEach(function () {
237 sock.send = sinon.spy();
238 });
239
240 it('should call send after converting the string to an array', function () {
241 sock.send_string("\x01\x02\x03");
242 expect(sock.send).to.have.been.calledWith([1, 2, 3]);
243 });
244 });
245 });
246
247 describe('lifecycle methods', function () {
2b5f94fa 248 let old_WS;
2cccf753 249 before(function () {
7b536961 250 old_WS = WebSocket;
2cccf753
SR
251 });
252
2b5f94fa 253 let sock;
2cccf753 254 beforeEach(function () {
8727f598
JD
255 sock = new Websock();
256 // eslint-disable-next-line no-global-assign
257 WebSocket = sinon.spy();
258 WebSocket.OPEN = old_WS.OPEN;
259 WebSocket.CONNECTING = old_WS.CONNECTING;
260 WebSocket.CLOSING = old_WS.CLOSING;
261 WebSocket.CLOSED = old_WS.CLOSED;
262
263 WebSocket.prototype.binaryType = 'arraybuffer';
2cccf753
SR
264 });
265
266 describe('opening', function () {
6786fd87 267 it('should pick the correct protocols if none are given', function () {
2cccf753
SR
268
269 });
270
271 it('should open the actual websocket', function () {
38781d93
SR
272 sock.open('ws://localhost:8675', 'binary');
273 expect(WebSocket).to.have.been.calledWith('ws://localhost:8675', 'binary');
2cccf753
SR
274 });
275
2cccf753
SR
276 // it('should initialize the event handlers')?
277 });
278
279 describe('closing', function () {
280 beforeEach(function () {
281 sock.open('ws://');
282 sock._websocket.close = sinon.spy();
283 });
284
285 it('should close the actual websocket if it is open', function () {
286 sock._websocket.readyState = WebSocket.OPEN;
287 sock.close();
288 expect(sock._websocket.close).to.have.been.calledOnce;
289 });
290
291 it('should close the actual websocket if it is connecting', function () {
292 sock._websocket.readyState = WebSocket.CONNECTING;
293 sock.close();
294 expect(sock._websocket.close).to.have.been.calledOnce;
295 });
296
297 it('should not try to close the actual websocket if closing', function () {
298 sock._websocket.readyState = WebSocket.CLOSING;
299 sock.close();
300 expect(sock._websocket.close).not.to.have.been.called;
301 });
302
303 it('should not try to close the actual websocket if closed', function () {
304 sock._websocket.readyState = WebSocket.CLOSED;
305 sock.close();
306 expect(sock._websocket.close).not.to.have.been.called;
307 });
308
309 it('should reset onmessage to not call _recv_message', function () {
310 sinon.spy(sock, '_recv_message');
311 sock.close();
312 sock._websocket.onmessage(null);
313 try {
314 expect(sock._recv_message).not.to.have.been.called;
315 } finally {
316 sock._recv_message.restore();
317 }
318 });
319 });
320
321 describe('event handlers', function () {
322 beforeEach(function () {
323 sock._recv_message = sinon.spy();
324 sock.on('open', sinon.spy());
325 sock.on('close', sinon.spy());
326 sock.on('error', sinon.spy());
327 sock.open('ws://');
328 });
329
330 it('should call _recv_message on a message', function () {
331 sock._websocket.onmessage(null);
332 expect(sock._recv_message).to.have.been.calledOnce;
333 });
334
2cccf753
SR
335 it('should call the open event handler on opening', function () {
336 sock._websocket.onopen();
337 expect(sock._eventHandlers.open).to.have.been.calledOnce;
338 });
339
340 it('should call the close event handler on closing', function () {
341 sock._websocket.onclose();
342 expect(sock._eventHandlers.close).to.have.been.calledOnce;
343 });
344
345 it('should call the error event handler on error', function () {
346 sock._websocket.onerror();
347 expect(sock._eventHandlers.error).to.have.been.calledOnce;
348 });
349 });
350
351 after(function () {
8727f598 352 // eslint-disable-next-line no-global-assign
2cccf753
SR
353 WebSocket = old_WS;
354 });
355 });
356
357 describe('WebSocket Receiving', function () {
2b5f94fa 358 let sock;
2cccf753 359 beforeEach(function () {
7b536961
PO
360 sock = new Websock();
361 sock._allocate_buffers();
2cccf753
SR
362 });
363
364 it('should support adding binary Uint8Array data to the receive queue', function () {
2b5f94fa 365 const msg = { data: new Uint8Array([1, 2, 3]) };
2cccf753
SR
366 sock._mode = 'binary';
367 sock._recv_message(msg);
368 expect(sock.rQshiftStr(3)).to.equal('\x01\x02\x03');
369 });
370
371 it('should call the message event handler if present', function () {
372 sock._eventHandlers.message = sinon.spy();
2b5f94fa 373 const msg = { data: new Uint8Array([1, 2, 3]).buffer };
38781d93 374 sock._mode = 'binary';
2cccf753
SR
375 sock._recv_message(msg);
376 expect(sock._eventHandlers.message).to.have.been.calledOnce;
377 });
378
379 it('should not call the message event handler if there is nothing in the receive queue', function () {
380 sock._eventHandlers.message = sinon.spy();
2b5f94fa 381 const msg = { data: new Uint8Array([]).buffer };
38781d93 382 sock._mode = 'binary';
2cccf753
SR
383 sock._recv_message(msg);
384 expect(sock._eventHandlers.message).not.to.have.been.called;
385 });
386
7d755d10
JAD
387 it('should compact the receive queue when a message handler empties it', function () {
388 sock._eventHandlers.message = () => { sock.rQi = sock._rQlen; };
38781d93
SR
389 sock._rQ = new Uint8Array([0, 1, 2, 3, 4, 5, 0, 0, 0, 0]);
390 sock._rQlen = 6;
8a189a62 391 sock.rQi = 6;
2b5f94fa 392 const msg = { data: new Uint8Array([1, 2, 3]).buffer };
38781d93 393 sock._mode = 'binary';
2cccf753 394 sock._recv_message(msg);
7d755d10 395 expect(sock._rQlen).to.equal(0);
8a189a62 396 expect(sock.rQi).to.equal(0);
2cccf753
SR
397 });
398
7d755d10
JAD
399 it('should compact the receive queue when we reach the end of the buffer', function () {
400 sock._rQ = new Uint8Array(20);
401 sock._rQbufferSize = 20;
402 sock._rQlen = 20;
403 sock.rQi = 10;
404 const msg = { data: new Uint8Array([1, 2]).buffer };
405 sock._mode = 'binary';
406 sock._recv_message(msg);
407 expect(sock._rQlen).to.equal(12);
408 expect(sock.rQi).to.equal(0);
409 });
410
411 it('should automatically resize the receive queue if the incoming message is larger than the buffer', function () {
40037b6a
SR
412 sock._rQ = new Uint8Array(20);
413 sock._rQlen = 0;
8a189a62 414 sock.rQi = 0;
40037b6a 415 sock._rQbufferSize = 20;
2b5f94fa 416 const msg = { data: new Uint8Array(30).buffer };
40037b6a
SR
417 sock._mode = 'binary';
418 sock._recv_message(msg);
419 expect(sock._rQlen).to.equal(30);
8a189a62 420 expect(sock.rQi).to.equal(0);
40037b6a
SR
421 expect(sock._rQ.length).to.equal(240); // keep the invariant that rQbufferSize / 8 >= rQlen
422 });
7d755d10
JAD
423
424 it('should automatically resize the receive queue if the incoming message is larger than 1/8th of the buffer and we reach the end of the buffer', function () {
425 sock._rQ = new Uint8Array(20);
426 sock._rQlen = 16;
427 sock.rQi = 16;
428 sock._rQbufferSize = 20;
429 const msg = { data: new Uint8Array(6).buffer };
430 sock._mode = 'binary';
431 sock._recv_message(msg);
432 expect(sock._rQlen).to.equal(6);
433 expect(sock.rQi).to.equal(0);
434 expect(sock._rQ.length).to.equal(48);
435 });
2cccf753
SR
436 });
437
438 describe('Data encoding', function () {
439 before(function () { FakeWebSocket.replace(); });
440 after(function () { FakeWebSocket.restore(); });
441
442 describe('as binary data', function () {
2b5f94fa 443 let sock;
2cccf753
SR
444 beforeEach(function () {
445 sock = new Websock();
446 sock.open('ws://', 'binary');
447 sock._websocket._open();
448 });
449
9ff86fb7
SR
450 it('should only send the send queue up to the send queue length', function () {
451 sock._sQ = new Uint8Array([1, 2, 3, 4, 5]);
452 sock._sQlen = 3;
2b5f94fa 453 const res = sock._encode_message();
9ff86fb7 454 expect(res).to.array.equal(new Uint8Array([1, 2, 3]));
2cccf753
SR
455 });
456
457 it('should properly pass the encoded data off to the actual WebSocket', function () {
458 sock.send([1, 2, 3]);
9ff86fb7 459 expect(sock._websocket._get_sent_data()).to.array.equal(new Uint8Array([1, 2, 3]));
2cccf753
SR
460 });
461 });
2cccf753
SR
462 });
463});