]> git.proxmox.com Git - mirror_novnc.git/blob - tests/test.websock.js
Add getters/setter to websock
[mirror_novnc.git] / tests / test.websock.js
1 const expect = chai.expect;
2
3 import Websock from '../core/websock.js';
4 import FakeWebSocket from './fake.websocket.js';
5
6 describe('Websock', function () {
7 "use strict";
8
9 describe('Queue methods', function () {
10 let sock;
11 const RQ_TEMPLATE = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]);
12
13 beforeEach(function () {
14 sock = new Websock();
15 // skip init
16 sock._allocate_buffers();
17 sock._rQ.set(RQ_TEMPLATE);
18 sock._rQlen = RQ_TEMPLATE.length;
19 });
20 describe('rQlen', function () {
21 it('should return the length of the receive queue', function () {
22 sock.rQi = 0;
23
24 expect(sock.rQlen).to.equal(RQ_TEMPLATE.length);
25 });
26
27 it("should return the proper length if we read some from the receive queue", function () {
28 sock.rQi = 1;
29
30 expect(sock.rQlen).to.equal(RQ_TEMPLATE.length - 1);
31 });
32 });
33
34 describe('rQpeek8', function () {
35 it('should peek at the next byte without poping it off the queue', function () {
36 const bef_len = sock.rQlen;
37 const peek = sock.rQpeek8();
38 expect(sock.rQpeek8()).to.equal(peek);
39 expect(sock.rQlen).to.equal(bef_len);
40 });
41 });
42
43 describe('rQshift8()', function () {
44 it('should pop a single byte from the receive queue', function () {
45 const peek = sock.rQpeek8();
46 const bef_len = sock.rQlen;
47 expect(sock.rQshift8()).to.equal(peek);
48 expect(sock.rQlen).to.equal(bef_len - 1);
49 });
50 });
51
52 describe('rQshift16()', function () {
53 it('should pop two bytes from the receive queue and return a single number', function () {
54 const bef_len = sock.rQlen;
55 const expected = (RQ_TEMPLATE[0] << 8) + RQ_TEMPLATE[1];
56 expect(sock.rQshift16()).to.equal(expected);
57 expect(sock.rQlen).to.equal(bef_len - 2);
58 });
59 });
60
61 describe('rQshift32()', function () {
62 it('should pop four bytes from the receive queue and return a single number', function () {
63 const bef_len = sock.rQlen;
64 const expected = (RQ_TEMPLATE[0] << 24) +
65 (RQ_TEMPLATE[1] << 16) +
66 (RQ_TEMPLATE[2] << 8) +
67 RQ_TEMPLATE[3];
68 expect(sock.rQshift32()).to.equal(expected);
69 expect(sock.rQlen).to.equal(bef_len - 4);
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 () {
75 const bef_len = sock.rQlen;
76 const bef_rQi = sock.rQi;
77 const shifted = sock.rQshiftStr(3);
78 expect(shifted).to.be.a('string');
79 expect(shifted).to.equal(String.fromCharCode.apply(null, Array.prototype.slice.call(new Uint8Array(RQ_TEMPLATE.buffer, bef_rQi, 3))));
80 expect(sock.rQlen).to.equal(bef_len - 3);
81 });
82
83 it('should shift the entire rest of the queue off if no length is given', function () {
84 sock.rQshiftStr();
85 expect(sock.rQlen).to.equal(0);
86 });
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);
109 expect(sock.rQlen).to.equal(0);
110 });
111 });
112
113 describe('rQshiftBytes', function () {
114 it('should shift the given number of bytes of the receive queue and return an array', function () {
115 const bef_len = sock.rQlen;
116 const bef_rQi = sock.rQi;
117 const shifted = sock.rQshiftBytes(3);
118 expect(shifted).to.be.an.instanceof(Uint8Array);
119 expect(shifted).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, bef_rQi, 3));
120 expect(sock.rQlen).to.equal(bef_len - 3);
121 });
122
123 it('should shift the entire rest of the queue off if no length is given', function () {
124 sock.rQshiftBytes();
125 expect(sock.rQlen).to.equal(0);
126 });
127 });
128
129 describe('rQslice', function () {
130 beforeEach(function () {
131 sock.rQi = 0;
132 });
133
134 it('should not modify the receive queue', function () {
135 const bef_len = sock.rQlen;
136 sock.rQslice(0, 2);
137 expect(sock.rQlen).to.equal(bef_len);
138 });
139
140 it('should return an array containing the given slice of the receive queue', function () {
141 const sl = sock.rQslice(0, 2);
142 expect(sl).to.be.an.instanceof(Uint8Array);
143 expect(sl).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 0, 2));
144 });
145
146 it('should use the rest of the receive queue if no end is given', function () {
147 const sl = sock.rQslice(1);
148 expect(sl).to.have.length(RQ_TEMPLATE.length - 1);
149 expect(sl).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 1));
150 });
151
152 it('should take the current rQi in to account', function () {
153 sock.rQi = 1;
154 expect(sock.rQslice(0, 2)).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 1, 2));
155 });
156 });
157
158 describe('rQwait', function () {
159 beforeEach(function () {
160 sock.rQi = 0;
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 () {
172 sock.rQi = 5;
173 expect(sock.rQwait('hi', RQ_TEMPLATE.length, 4)).to.be.true;
174 expect(sock.rQi).to.equal(1);
175 });
176
177 it('should raise an error if we try to go back more than possible', function () {
178 sock.rQi = 5;
179 expect(() => sock.rQwait('hi', RQ_TEMPLATE.length, 6)).to.throw(Error);
180 });
181
182 it('should not reduce rQi if there are enough bytes', function () {
183 sock.rQi = 5;
184 sock.rQwait('hi', 1, 6);
185 expect(sock.rQi).to.equal(5);
186 });
187 });
188
189 describe('flush', function () {
190 beforeEach(function () {
191 sock._websocket = {
192 send: sinon.spy()
193 };
194 });
195
196 it('should actually send on the websocket', function () {
197 sock._websocket.bufferedAmount = 8;
198 sock._websocket.readyState = WebSocket.OPEN;
199 sock._sQ = new Uint8Array([1, 2, 3]);
200 sock._sQlen = 3;
201 const encoded = sock._encode_message();
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
208 it('should not call send if we do not have anything queued up', function () {
209 sock._sQlen = 0;
210 sock._websocket.bufferedAmount = 8;
211
212 sock.flush();
213
214 expect(sock._websocket.send).not.to.have.been.called;
215 });
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]);
225 const sq = sock.sQ;
226 expect(new Uint8Array(sq.buffer, sock._sQlen - 3, 3)).to.array.equal(new Uint8Array([1, 2, 3]));
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 () {
248 let old_WS;
249 before(function () {
250 old_WS = WebSocket;
251 });
252
253 let sock;
254 beforeEach(function () {
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';
264 });
265
266 describe('opening', function () {
267 it('should pick the correct protocols if none are given', function () {
268
269 });
270
271 it('should open the actual websocket', function () {
272 sock.open('ws://localhost:8675', 'binary');
273 expect(WebSocket).to.have.been.calledWith('ws://localhost:8675', 'binary');
274 });
275
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
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 () {
352 // eslint-disable-next-line no-global-assign
353 WebSocket = old_WS;
354 });
355 });
356
357 describe('WebSocket Receiving', function () {
358 let sock;
359 beforeEach(function () {
360 sock = new Websock();
361 sock._allocate_buffers();
362 });
363
364 it('should support adding binary Uint8Array data to the receive queue', function () {
365 const msg = { data: new Uint8Array([1, 2, 3]) };
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();
373 const msg = { data: new Uint8Array([1, 2, 3]).buffer };
374 sock._mode = 'binary';
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();
381 const msg = { data: new Uint8Array([]).buffer };
382 sock._mode = 'binary';
383 sock._recv_message(msg);
384 expect(sock._eventHandlers.message).not.to.have.been.called;
385 });
386
387 it('should compact the receive queue', function () {
388 // NB(sross): while this is an internal implementation detail, it's important to
389 // test, otherwise the receive queue could become very large very quickly
390 sock._rQ = new Uint8Array([0, 1, 2, 3, 4, 5, 0, 0, 0, 0]);
391 sock._rQlen = 6;
392 sock.rQi = 6;
393 sock._rQmax = 3;
394 const msg = { data: new Uint8Array([1, 2, 3]).buffer };
395 sock._mode = 'binary';
396 sock._recv_message(msg);
397 expect(sock._rQlen).to.equal(3);
398 expect(sock.rQi).to.equal(0);
399 });
400
401 it('should automatically resize the receive queue if the incoming message is too large', function () {
402 sock._rQ = new Uint8Array(20);
403 sock._rQlen = 0;
404 sock.rQi = 0;
405 sock._rQbufferSize = 20;
406 sock._rQmax = 2;
407 const msg = { data: new Uint8Array(30).buffer };
408 sock._mode = 'binary';
409 sock._recv_message(msg);
410 expect(sock._rQlen).to.equal(30);
411 expect(sock.rQi).to.equal(0);
412 expect(sock._rQ.length).to.equal(240); // keep the invariant that rQbufferSize / 8 >= rQlen
413 });
414 });
415
416 describe('Data encoding', function () {
417 before(function () { FakeWebSocket.replace(); });
418 after(function () { FakeWebSocket.restore(); });
419
420 describe('as binary data', function () {
421 let sock;
422 beforeEach(function () {
423 sock = new Websock();
424 sock.open('ws://', 'binary');
425 sock._websocket._open();
426 });
427
428 it('should only send the send queue up to the send queue length', function () {
429 sock._sQ = new Uint8Array([1, 2, 3, 4, 5]);
430 sock._sQlen = 3;
431 const res = sock._encode_message();
432 expect(res).to.array.equal(new Uint8Array([1, 2, 3]));
433 });
434
435 it('should properly pass the encoded data off to the actual WebSocket', function () {
436 sock.send([1, 2, 3]);
437 expect(sock._websocket._get_sent_data()).to.array.equal(new Uint8Array([1, 2, 3]));
438 });
439 });
440 });
441 });