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