1 // requires local modules: util, websock, rfb, input/util, input/keysym, input/keysymdef, input/devices, inflator, des, display
2 // requires test modules: fake.websocket, assertions
3 /* jshint expr: true */
4 var assert
= chai
.assert
;
5 var expect
= chai
.expect
;
7 function make_rfb (extra_opts
) {
12 extra_opts
.target
= extra_opts
.target
|| document
.createElement('canvas');
13 return new RFB(extra_opts
);
16 var push8 = function (arr
, num
) {
21 var push16 = function (arr
, num
) {
23 arr
.push((num
>> 8) & 0xFF,
27 var push32 = function (arr
, num
) {
29 arr
.push((num
>> 24) & 0xFF,
35 describe('Remote Frame Buffer Protocol Client', function() {
37 before(FakeWebSocket
.replace
);
38 after(FakeWebSocket
.restore
);
41 this.clock
= sinon
.useFakeTimers();
42 // Use a single set of buffers instead of reallocating to
44 var sock
= new Websock();
45 var _sQ
= new Uint8Array(sock
._sQbufferSize
);
46 var rQ
= new Uint8Array(sock
._rQbufferSize
);
48 Websock
.prototype._old_allocate_buffers
= Websock
.prototype._allocate_buffers
;
49 Websock
.prototype._allocate_buffers = function () {
57 Websock
.prototype._allocate_buffers
= Websock
.prototype._old_allocate_buffers
;
61 describe('Public API Basic Behavior', function () {
63 beforeEach(function () {
67 describe('#connect', function () {
68 beforeEach(function () { client
._updateConnectionState
= sinon
.spy(); });
70 it('should set the current state to "connecting"', function () {
71 client
.connect('host', 8675);
72 expect(client
._updateConnectionState
).to
.have
.been
.calledOnce
;
73 expect(client
._updateConnectionState
).to
.have
.been
.calledWith('connecting');
76 it('should not try to connect if we are missing a host', function () {
77 client
._fail
= sinon
.spy();
78 client
._rfb_connection_state
= '';
79 client
.connect(undefined, 8675);
80 expect(client
._fail
).to
.have
.been
.calledOnce
;
81 expect(client
._updateConnectionState
).to
.not
.have
.been
.called
;
82 expect(client
._rfb_connection_state
).to
.equal('');
85 it('should not try to connect if we are missing a port', function () {
86 client
._fail
= sinon
.spy();
87 client
._rfb_connection_state
= '';
88 client
.connect('abc');
89 expect(client
._fail
).to
.have
.been
.calledOnce
;
90 expect(client
._updateConnectionState
).to
.not
.have
.been
.called
;
91 expect(client
._rfb_connection_state
).to
.equal('');
95 describe('#disconnect', function () {
96 beforeEach(function () { client
._updateConnectionState
= sinon
.spy(); });
98 it('should set the current state to "disconnecting"', function () {
100 expect(client
._updateConnectionState
).to
.have
.been
.calledOnce
;
101 expect(client
._updateConnectionState
).to
.have
.been
.calledWith('disconnecting');
104 it('should unregister error event handler', function () {
105 sinon
.spy(client
._sock
, 'off');
107 expect(client
._sock
.off
).to
.have
.been
.calledWith('error');
110 it('should unregister message event handler', function () {
111 sinon
.spy(client
._sock
, 'off');
113 expect(client
._sock
.off
).to
.have
.been
.calledWith('message');
116 it('should unregister open event handler', function () {
117 sinon
.spy(client
._sock
, 'off');
119 expect(client
._sock
.off
).to
.have
.been
.calledWith('open');
123 describe('#sendPassword', function () {
124 beforeEach(function () { this.clock
= sinon
.useFakeTimers(); });
125 afterEach(function () { this.clock
.restore(); });
127 it('should set the rfb password properly"', function () {
128 client
.sendPassword('pass');
129 expect(client
._rfb_password
).to
.equal('pass');
132 it('should call init_msg "soon"', function () {
133 client
._init_msg
= sinon
.spy();
134 client
.sendPassword('pass');
136 expect(client
._init_msg
).to
.have
.been
.calledOnce
;
140 describe('#sendCtrlAlDel', function () {
141 beforeEach(function () {
142 client
._sock
= new Websock();
143 client
._sock
.open('ws://', 'binary');
144 client
._sock
._websocket
._open();
145 sinon
.spy(client
._sock
, 'flush');
146 client
._rfb_connection_state
= 'connected';
147 client
._view_only
= false;
150 it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () {
151 var expected
= {_sQ
: new Uint8Array(48), _sQlen
: 0, flush: function () {}};
152 RFB
.messages
.keyEvent(expected
, 0xFFE3, 1);
153 RFB
.messages
.keyEvent(expected
, 0xFFE9, 1);
154 RFB
.messages
.keyEvent(expected
, 0xFFFF, 1);
155 RFB
.messages
.keyEvent(expected
, 0xFFFF, 0);
156 RFB
.messages
.keyEvent(expected
, 0xFFE9, 0);
157 RFB
.messages
.keyEvent(expected
, 0xFFE3, 0);
159 client
.sendCtrlAltDel();
160 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
163 it('should not send the keys if we are not in a normal state', function () {
164 client
._rfb_connection_state
= "broken";
165 client
.sendCtrlAltDel();
166 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
169 it('should not send the keys if we are set as view_only', function () {
170 client
._view_only
= true;
171 client
.sendCtrlAltDel();
172 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
176 describe('#sendKey', function () {
177 beforeEach(function () {
178 client
._sock
= new Websock();
179 client
._sock
.open('ws://', 'binary');
180 client
._sock
._websocket
._open();
181 sinon
.spy(client
._sock
, 'flush');
182 client
._rfb_connection_state
= 'connected';
183 client
._view_only
= false;
186 it('should send a single key with the given code and state (down = true)', function () {
187 var expected
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
188 RFB
.messages
.keyEvent(expected
, 123, 1);
189 client
.sendKey(123, true);
190 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
193 it('should send both a down and up event if the state is not specified', function () {
194 var expected
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function () {}};
195 RFB
.messages
.keyEvent(expected
, 123, 1);
196 RFB
.messages
.keyEvent(expected
, 123, 0);
198 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
201 it('should not send the key if we are not in a normal state', function () {
202 client
._rfb_connection_state
= "broken";
204 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
207 it('should not send the key if we are set as view_only', function () {
208 client
._view_only
= true;
210 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
214 describe('#clipboardPasteFrom', function () {
215 beforeEach(function () {
216 client
._sock
= new Websock();
217 client
._sock
.open('ws://', 'binary');
218 client
._sock
._websocket
._open();
219 sinon
.spy(client
._sock
, 'flush');
220 client
._rfb_connection_state
= 'connected';
221 client
._view_only
= false;
224 it('should send the given text in a paste event', function () {
225 var expected
= {_sQ
: new Uint8Array(11), _sQlen
: 0, flush: function () {}};
226 RFB
.messages
.clientCutText(expected
, 'abc');
227 client
.clipboardPasteFrom('abc');
228 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
231 it('should not send the text if we are not in a normal state', function () {
232 client
._rfb_connection_state
= "broken";
233 client
.clipboardPasteFrom('abc');
234 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
238 describe("#requestDesktopSize", function () {
239 beforeEach(function() {
240 client
._sock
= new Websock();
241 client
._sock
.open('ws://', 'binary');
242 client
._sock
._websocket
._open();
243 sinon
.spy(client
._sock
, 'flush');
244 client
._rfb_connection_state
= 'connected';
245 client
._view_only
= false;
246 client
._supportsSetDesktopSize
= true;
249 it('should send the request with the given width and height', function () {
250 var expected
= [251];
251 push8(expected
, 0); // padding
252 push16(expected
, 1); // width
253 push16(expected
, 2); // height
254 push8(expected
, 1); // number-of-screens
255 push8(expected
, 0); // padding before screen array
256 push32(expected
, 0); // id
257 push16(expected
, 0); // x-position
258 push16(expected
, 0); // y-position
259 push16(expected
, 1); // width
260 push16(expected
, 2); // height
261 push32(expected
, 0); // flags
263 client
.requestDesktopSize(1, 2);
264 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
267 it('should not send the request if the client has not recieved a ExtendedDesktopSize rectangle', function () {
268 client
._supportsSetDesktopSize
= false;
269 client
.requestDesktopSize(1,2);
270 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
273 it('should not send the request if we are not in a normal state', function () {
274 client
._rfb_connection_state
= "broken";
275 client
.requestDesktopSize(1,2);
276 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
280 describe("XVP operations", function () {
281 beforeEach(function () {
282 client
._sock
= new Websock();
283 client
._sock
.open('ws://', 'binary');
284 client
._sock
._websocket
._open();
285 sinon
.spy(client
._sock
, 'flush');
286 client
._rfb_connection_state
= 'connected';
287 client
._view_only
= false;
288 client
._rfb_xvp_ver
= 1;
291 it('should send the shutdown signal on #xvpShutdown', function () {
292 client
.xvpShutdown();
293 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x02]));
296 it('should send the reboot signal on #xvpReboot', function () {
298 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x03]));
301 it('should send the reset signal on #xvpReset', function () {
303 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x04]));
306 it('should support sending arbitrary XVP operations via #xvpOp', function () {
308 expect(client
._sock
).to
.have
.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x07]));
311 it('should not send XVP operations with higher versions than we support', function () {
312 expect(client
.xvpOp(2, 7)).to
.be
.false;
313 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
318 describe('Misc Internals', function () {
319 describe('#_updateConnectionState', function () {
321 beforeEach(function () {
322 this.clock
= sinon
.useFakeTimers();
326 afterEach(function () {
327 this.clock
.restore();
330 it('should clear the disconnect timer if the state is not "disconnecting"', function () {
331 var spy
= sinon
.spy();
332 client
._disconnTimer
= setTimeout(spy
, 50);
333 client
._updateConnectionState('connected');
335 expect(spy
).to
.not
.have
.been
.called
;
336 expect(client
._disconnTimer
).to
.be
.null;
339 it('should call the updateState callback', function () {
340 client
.set_onUpdateState(sinon
.spy());
341 client
._updateConnectionState('a specific state');
342 var spy
= client
.get_onUpdateState();
343 expect(spy
).to
.have
.been
.calledOnce
;
344 expect(spy
.args
[0][1]).to
.equal('a specific state');
347 it('should set the rfb_connection_state', function () {
348 client
._updateConnectionState('a specific state');
349 expect(client
._rfb_connection_state
).to
.equal('a specific state');
352 it('should not change the state when we are disconnected', function () {
353 client
._rfb_connection_state
= 'disconnected';
354 client
._updateConnectionState('a specific state');
355 expect(client
._rfb_connection_state
).to
.not
.equal('a specific state');
358 it('should ignore state changes to the same state', function () {
359 client
.set_onUpdateState(sinon
.spy());
360 client
._rfb_connection_state
= 'a specific state';
361 client
._updateConnectionState('a specific state');
362 var spy
= client
.get_onUpdateState();
363 expect(spy
).to
.not
.have
.been
.called
;
367 describe('#_fail', function () {
369 beforeEach(function () {
370 this.clock
= sinon
.useFakeTimers();
372 client
.connect('host', 8675);
375 afterEach(function () {
376 this.clock
.restore();
379 it('should close the WebSocket connection', function () {
380 sinon
.spy(client
._sock
, 'close');
382 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
385 it('should transition to disconnected', function () {
386 sinon
.spy(client
, '_updateConnectionState');
388 this.clock
.tick(2000);
389 expect(client
._updateConnectionState
).to
.have
.been
.called
;
390 expect(client
._rfb_connection_state
).to
.equal('disconnected');
393 it('should set disconnect_reason', function () {
394 client
._fail('a reason');
395 expect(client
._rfb_disconnect_reason
).to
.equal('a reason');
398 it('should result in disconnect callback with message when reason given', function () {
399 client
.set_onDisconnected(sinon
.spy());
400 client
._fail('a reason');
401 var spy
= client
.get_onDisconnected();
402 this.clock
.tick(2000);
403 expect(spy
).to
.have
.been
.calledOnce
;
404 expect(spy
.args
[0].length
).to
.equal(2);
405 expect(spy
.args
[0][1]).to
.equal('a reason');
410 describe('#_notification', function () {
412 beforeEach(function () { client
= make_rfb(); });
414 it('should call the notification callback', function () {
415 client
.set_onNotification(sinon
.spy());
416 client
._notification('notify!', 'warn');
417 var spy
= client
.get_onNotification();
418 expect(spy
).to
.have
.been
.calledOnce
;
419 expect(spy
.args
[0][1]).to
.equal('notify!');
420 expect(spy
.args
[0][2]).to
.equal('warn');
423 it('should not call the notification callback when level is invalid', function () {
424 client
.set_onNotification(sinon
.spy());
425 client
._notification('notify!', 'invalid');
426 var spy
= client
.get_onNotification();
427 expect(spy
).to
.not
.have
.been
.called
;
432 describe('Connection States', function () {
433 describe('connecting', function () {
435 beforeEach(function () { client
= make_rfb(); });
437 it('should reset the variable states', function () {
438 sinon
.spy(client
, '_init_vars');
439 client
._updateConnectionState('connecting');
440 expect(client
._init_vars
).to
.have
.been
.calledOnce
;
443 it('should actually connect to the websocket', function () {
444 sinon
.spy(client
._sock
, 'open');
445 client
._updateConnectionState('connecting');
446 expect(client
._sock
.open
).to
.have
.been
.calledOnce
;
449 it('should use wss:// to connect if encryption is enabled', function () {
450 sinon
.spy(client
._sock
, 'open');
451 client
.set_encrypt(true);
452 client
._updateConnectionState('connecting');
453 expect(client
._sock
.open
.args
[0][0]).to
.contain('wss://');
456 it('should use ws:// to connect if encryption is not enabled', function () {
457 sinon
.spy(client
._sock
, 'open');
458 client
.set_encrypt(true);
459 client
._updateConnectionState('connecting');
460 expect(client
._sock
.open
.args
[0][0]).to
.contain('wss://');
463 it('should use a uri with the host, port, and path specified to connect', function () {
464 sinon
.spy(client
._sock
, 'open');
465 client
.set_encrypt(false);
466 client
._rfb_host
= 'HOST';
467 client
._rfb_port
= 8675;
468 client
._rfb_path
= 'PATH';
469 client
._updateConnectionState('connecting');
470 expect(client
._sock
.open
).to
.have
.been
.calledWith('ws://HOST:8675/PATH');
474 describe('disconnecting', function () {
476 beforeEach(function () {
477 this.clock
= sinon
.useFakeTimers();
479 client
.connect('host', 8675);
482 afterEach(function () {
483 this.clock
.restore();
486 it('should force disconnect if we do not call Websock.onclose within the disconnection timeout', function () {
487 sinon
.spy(client
, '_updateConnectionState');
488 client
._sock
._websocket
.close = function () {}; // explicitly don't call onclose
489 client
._updateConnectionState('disconnecting');
490 this.clock
.tick(client
.get_disconnectTimeout() * 1000);
491 expect(client
._updateConnectionState
).to
.have
.been
.calledTwice
;
492 expect(client
._rfb_disconnect_reason
).to
.not
.equal("");
493 expect(client
._rfb_connection_state
).to
.equal("disconnected");
496 it('should not fail if Websock.onclose gets called within the disconnection timeout', function () {
497 client
._updateConnectionState('disconnecting');
498 this.clock
.tick(client
.get_disconnectTimeout() * 500);
499 client
._sock
._websocket
.close();
500 this.clock
.tick(client
.get_disconnectTimeout() * 500 + 1);
501 expect(client
._rfb_connection_state
).to
.equal('disconnected');
504 it('should close the WebSocket connection', function () {
505 sinon
.spy(client
._sock
, 'close');
506 client
._updateConnectionState('disconnecting');
507 expect(client
._sock
.close
).to
.have
.been
.calledOnce
;
511 describe('disconnected', function () {
513 beforeEach(function () { client
= make_rfb(); });
515 it('should call the disconnect callback if the state is "disconnected"', function () {
516 client
.set_onDisconnected(sinon
.spy());
517 client
._rfb_connection_state
= 'disconnecting';
518 client
._rfb_disconnect_reason
= "error";
519 client
._updateConnectionState('disconnected');
520 var spy
= client
.get_onDisconnected();
521 expect(spy
).to
.have
.been
.calledOnce
;
522 expect(spy
.args
[0][1]).to
.equal("error");
525 it('should not call the disconnect callback if the state is not "disconnected"', function () {
526 client
.set_onDisconnected(sinon
.spy());
527 client
._updateConnectionState('disconnecting');
528 var spy
= client
.get_onDisconnected();
529 expect(spy
).to
.not
.have
.been
.called
;
532 it('should call the disconnect callback without msg when no reason given', function () {
533 client
.set_onDisconnected(sinon
.spy());
534 client
._rfb_connection_state
= 'disconnecting';
535 client
._rfb_disconnect_reason
= "";
536 client
._updateConnectionState('disconnected');
537 var spy
= client
.get_onDisconnected();
538 expect(spy
).to
.have
.been
.calledOnce
;
539 expect(spy
.args
[0].length
).to
.equal(1);
542 it('should call the updateState callback before the disconnect callback', function () {
543 client
.set_onDisconnected(sinon
.spy());
544 client
.set_onUpdateState(sinon
.spy());
545 client
._rfb_connection_state
= 'other state';
546 client
._updateConnectionState('disconnected');
547 var updateStateSpy
= client
.get_onUpdateState();
548 var disconnectSpy
= client
.get_onDisconnected();
549 expect(updateStateSpy
.calledBefore(disconnectSpy
)).to
.be
.true;
553 // NB(directxman12): Connected does *nothing* in updateConnectionState
556 describe('Protocol Initialization States', function () {
557 describe('ProtocolVersion', function () {
558 beforeEach(function () {
559 this.clock
= sinon
.useFakeTimers();
562 afterEach(function () {
563 this.clock
.restore();
566 function send_ver (ver
, client
) {
567 var arr
= new Uint8Array(12);
568 for (var i
= 0; i
< ver
.length
; i
++) {
569 arr
[i
+4] = ver
.charCodeAt(i
);
571 arr
[0] = 'R'; arr
[1] = 'F'; arr
[2] = 'B'; arr
[3] = ' ';
573 client
._sock
._websocket
._receive_data(arr
);
576 describe('version parsing', function () {
578 beforeEach(function () {
580 client
.connect('host', 8675);
581 client
._sock
._websocket
._open();
584 it('should interpret version 000.000 as a repeater', function () {
585 client
._repeaterID
= '\x01\x02\x03\x04\x05';
586 send_ver('000.000', client
);
587 expect(client
._rfb_version
).to
.equal(0);
589 var sent_data
= client
._sock
._websocket
._get_sent_data();
590 expect(new Uint8Array(sent_data
.buffer
, 0, 5)).to
.array
.equal(new Uint8Array([1, 2, 3, 4, 5]));
593 it('should interpret version 003.003 as version 3.3', function () {
594 send_ver('003.003', client
);
595 expect(client
._rfb_version
).to
.equal(3.3);
598 it('should interpret version 003.006 as version 3.3', function () {
599 send_ver('003.006', client
);
600 expect(client
._rfb_version
).to
.equal(3.3);
603 it('should interpret version 003.889 as version 3.3', function () {
604 send_ver('003.889', client
);
605 expect(client
._rfb_version
).to
.equal(3.3);
608 it('should interpret version 003.007 as version 3.7', function () {
609 send_ver('003.007', client
);
610 expect(client
._rfb_version
).to
.equal(3.7);
613 it('should interpret version 003.008 as version 3.8', function () {
614 send_ver('003.008', client
);
615 expect(client
._rfb_version
).to
.equal(3.8);
618 it('should interpret version 004.000 as version 3.8', function () {
619 send_ver('004.000', client
);
620 expect(client
._rfb_version
).to
.equal(3.8);
623 it('should interpret version 004.001 as version 3.8', function () {
624 send_ver('004.001', client
);
625 expect(client
._rfb_version
).to
.equal(3.8);
628 it('should interpret version 005.000 as version 3.8', function () {
629 send_ver('005.000', client
);
630 expect(client
._rfb_version
).to
.equal(3.8);
633 it('should fail on an invalid version', function () {
634 sinon
.spy(client
, "_fail");
635 send_ver('002.000', client
);
636 expect(client
._fail
).to
.have
.been
.calledOnce
;
641 beforeEach(function () {
643 client
.connect('host', 8675);
644 client
._sock
._websocket
._open();
647 it('should handle two step repeater negotiation', function () {
648 client
._repeaterID
= '\x01\x02\x03\x04\x05';
650 send_ver('000.000', client
);
651 expect(client
._rfb_version
).to
.equal(0);
652 var sent_data
= client
._sock
._websocket
._get_sent_data();
653 expect(new Uint8Array(sent_data
.buffer
, 0, 5)).to
.array
.equal(new Uint8Array([1, 2, 3, 4, 5]));
654 expect(sent_data
).to
.have
.length(250);
656 send_ver('003.008', client
);
657 expect(client
._rfb_version
).to
.equal(3.8);
660 it('should send back the interpreted version', function () {
661 send_ver('004.000', client
);
663 var expected_str
= 'RFB 003.008\n';
665 for (var i
= 0; i
< expected_str
.length
; i
++) {
666 expected
[i
] = expected_str
.charCodeAt(i
);
669 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
672 it('should transition to the Security state on successful negotiation', function () {
673 send_ver('003.008', client
);
674 expect(client
._rfb_init_state
).to
.equal('Security');
678 describe('Security', function () {
681 beforeEach(function () {
683 client
.connect('host', 8675);
684 client
._sock
._websocket
._open();
685 client
._rfb_init_state
= 'Security';
688 it('should simply receive the auth scheme when for versions < 3.7', function () {
689 client
._rfb_version
= 3.6;
690 var auth_scheme_raw
= [1, 2, 3, 4];
691 var auth_scheme
= (auth_scheme_raw
[0] << 24) + (auth_scheme_raw
[1] << 16) +
692 (auth_scheme_raw
[2] << 8) + auth_scheme_raw
[3];
693 client
._sock
._websocket
._receive_data(auth_scheme_raw
);
694 expect(client
._rfb_auth_scheme
).to
.equal(auth_scheme
);
697 it('should choose for the most prefered scheme possible for versions >= 3.7', function () {
698 client
._rfb_version
= 3.7;
699 var auth_schemes
= [2, 1, 2];
700 client
._sock
._websocket
._receive_data(auth_schemes
);
701 expect(client
._rfb_auth_scheme
).to
.equal(2);
702 expect(client
._sock
).to
.have
.sent(new Uint8Array([2]));
705 it('should fail if there are no supported schemes for versions >= 3.7', function () {
706 sinon
.spy(client
, "_fail");
707 client
._rfb_version
= 3.7;
708 var auth_schemes
= [1, 32];
709 client
._sock
._websocket
._receive_data(auth_schemes
);
710 expect(client
._fail
).to
.have
.been
.calledOnce
;
713 it('should fail with the appropriate message if no types are sent for versions >= 3.7', function () {
714 client
._rfb_version
= 3.7;
715 var failure_data
= [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
716 sinon
.spy(client
, '_fail');
717 client
._sock
._websocket
._receive_data(failure_data
);
719 expect(client
._fail
).to
.have
.been
.calledOnce
;
720 expect(client
._fail
).to
.have
.been
.calledWith('Security failure: whoops');
723 it('should transition to the Authentication state and continue on successful negotiation', function () {
724 client
._rfb_version
= 3.7;
725 var auth_schemes
= [1, 1];
726 client
._negotiate_authentication
= sinon
.spy();
727 client
._sock
._websocket
._receive_data(auth_schemes
);
728 expect(client
._rfb_init_state
).to
.equal('Authentication');
729 expect(client
._negotiate_authentication
).to
.have
.been
.calledOnce
;
733 describe('Authentication', function () {
736 beforeEach(function () {
738 client
.connect('host', 8675);
739 client
._sock
._websocket
._open();
740 client
._rfb_init_state
= 'Security';
743 function send_security(type
, cl
) {
744 cl
._sock
._websocket
._receive_data(new Uint8Array([1, type
]));
747 it('should fail on auth scheme 0 (pre 3.7) with the given message', function () {
748 client
._rfb_version
= 3.6;
749 var err_msg
= "Whoopsies";
750 var data
= [0, 0, 0, 0];
751 var err_len
= err_msg
.length
;
752 push32(data
, err_len
);
753 for (var i
= 0; i
< err_len
; i
++) {
754 data
.push(err_msg
.charCodeAt(i
));
757 sinon
.spy(client
, '_fail');
758 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
759 expect(client
._fail
).to
.have
.been
.calledWith('Auth failure: Whoopsies');
762 it('should transition straight to SecurityResult on "no auth" (1) for versions >= 3.8', function () {
763 client
._rfb_version
= 3.8;
764 send_security(1, client
);
765 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
768 it('should transition straight to ServerInitialisation on "no auth" for versions < 3.8', function () {
769 client
._rfb_version
= 3.7;
770 send_security(1, client
);
771 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
774 it('should fail on an unknown auth scheme', function () {
775 sinon
.spy(client
, "_fail");
776 client
._rfb_version
= 3.8;
777 send_security(57, client
);
778 expect(client
._fail
).to
.have
.been
.calledOnce
;
781 describe('VNC Authentication (type 2) Handler', function () {
784 beforeEach(function () {
786 client
.connect('host', 8675);
787 client
._sock
._websocket
._open();
788 client
._rfb_init_state
= 'Security';
789 client
._rfb_version
= 3.8;
792 it('should call the passwordRequired callback if missing a password', function () {
793 client
.set_onPasswordRequired(sinon
.spy());
794 send_security(2, client
);
796 var spy
= client
.get_onPasswordRequired();
797 expect(client
._rfb_password
.length
).to
.equal(0);
798 expect(spy
).to
.have
.been
.calledOnce
;
801 it('should encrypt the password with DES and then send it back', function () {
802 client
._rfb_password
= 'passwd';
803 send_security(2, client
);
804 client
._sock
._websocket
._get_sent_data(); // skip the choice of auth reply
807 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
808 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
810 var des_pass
= RFB
.genDES('passwd', challenge
);
811 expect(client
._sock
).to
.have
.sent(new Uint8Array(des_pass
));
814 it('should transition to SecurityResult immediately after sending the password', function () {
815 client
._rfb_password
= 'passwd';
816 send_security(2, client
);
819 for (var i
= 0; i
< 16; i
++) { challenge
[i
] = i
; }
820 client
._sock
._websocket
._receive_data(new Uint8Array(challenge
));
822 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
826 describe('XVP Authentication (type 22) Handler', function () {
829 beforeEach(function () {
831 client
.connect('host', 8675);
832 client
._sock
._websocket
._open();
833 client
._rfb_init_state
= 'Security';
834 client
._rfb_version
= 3.8;
837 it('should fall through to standard VNC authentication upon completion', function () {
838 client
.set_xvp_password_sep('#');
839 client
._rfb_password
= 'user#target#password';
840 client
._negotiate_std_vnc_auth
= sinon
.spy();
841 send_security(22, client
);
842 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
845 it('should call the passwordRequired callback if the password is missing', function() {
846 client
.set_onPasswordRequired(sinon
.spy());
847 client
._rfb_password
= '';
848 send_security(22, client
);
850 var spy
= client
.get_onPasswordRequired();
851 expect(client
._rfb_password
.length
).to
.equal(0);
852 expect(spy
).to
.have
.been
.calledOnce
;
855 it('should call the passwordRequired callback if the password is improperly formatted', function() {
856 client
.set_onPasswordRequired(sinon
.spy());
857 client
._rfb_password
= 'user@target';
858 send_security(22, client
);
860 var spy
= client
.get_onPasswordRequired();
861 expect(spy
).to
.have
.been
.calledOnce
;
864 it('should split the password, send the first two parts, and pass on the last part', function () {
865 client
.set_xvp_password_sep('#');
866 client
._rfb_password
= 'user#target#password';
867 client
._negotiate_std_vnc_auth
= sinon
.spy();
869 send_security(22, client
);
871 expect(client
._rfb_password
).to
.equal('password');
873 var expected
= [22, 4, 6]; // auth selection, len user, len target
874 for (var i
= 0; i
< 10; i
++) { expected
[i
+3] = 'usertarget'.charCodeAt(i
); }
876 expect(client
._sock
).to
.have
.sent(new Uint8Array(expected
));
880 describe('TightVNC Authentication (type 16) Handler', function () {
883 beforeEach(function () {
885 client
.connect('host', 8675);
886 client
._sock
._websocket
._open();
887 client
._rfb_init_state
= 'Security';
888 client
._rfb_version
= 3.8;
889 send_security(16, client
);
890 client
._sock
._websocket
._get_sent_data(); // skip the security reply
893 function send_num_str_pairs(pairs
, client
) {
894 var pairs_len
= pairs
.length
;
896 push32(data
, pairs_len
);
898 for (var i
= 0; i
< pairs_len
; i
++) {
899 push32(data
, pairs
[i
][0]);
901 for (j
= 0; j
< 4; j
++) {
902 data
.push(pairs
[i
][1].charCodeAt(j
));
904 for (j
= 0; j
< 8; j
++) {
905 data
.push(pairs
[i
][2].charCodeAt(j
));
909 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
912 it('should skip tunnel negotiation if no tunnels are requested', function () {
913 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
914 expect(client
._rfb_tightvnc
).to
.be
.true;
917 it('should fail if no supported tunnels are listed', function () {
918 sinon
.spy(client
, "_fail");
919 send_num_str_pairs([[123, 'OTHR', 'SOMETHNG']], client
);
920 expect(client
._fail
).to
.have
.been
.calledOnce
;
923 it('should choose the notunnel tunnel type', function () {
924 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL'], [123, 'OTHR', 'SOMETHNG']], client
);
925 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 0]));
928 it('should continue to sub-auth negotiation after tunnel negotiation', function () {
929 send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL']], client
);
930 client
._sock
._websocket
._get_sent_data(); // skip the tunnel choice here
931 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
932 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
933 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
936 /*it('should attempt to use VNC auth over no auth when possible', function () {
937 client._rfb_tightvnc = true;
938 client._negotiate_std_vnc_auth = sinon.spy();
939 send_num_str_pairs([[1, 'STDV', 'NOAUTH__'], [2, 'STDV', 'VNCAUTH_']], client);
940 expect(client._sock).to.have.sent([0, 0, 0, 1]);
941 expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce;
942 expect(client._rfb_auth_scheme).to.equal(2);
943 });*/ // while this would make sense, the original code doesn't actually do this
945 it('should accept the "no auth" auth type and transition to SecurityResult', function () {
946 client
._rfb_tightvnc
= true;
947 send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client
);
948 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 1]));
949 expect(client
._rfb_init_state
).to
.equal('SecurityResult');
952 it('should accept VNC authentication and transition to that', function () {
953 client
._rfb_tightvnc
= true;
954 client
._negotiate_std_vnc_auth
= sinon
.spy();
955 send_num_str_pairs([[2, 'STDV', 'VNCAUTH__']], client
);
956 expect(client
._sock
).to
.have
.sent(new Uint8Array([0, 0, 0, 2]));
957 expect(client
._negotiate_std_vnc_auth
).to
.have
.been
.calledOnce
;
958 expect(client
._rfb_auth_scheme
).to
.equal(2);
961 it('should fail if there are no supported auth types', function () {
962 sinon
.spy(client
, "_fail");
963 client
._rfb_tightvnc
= true;
964 send_num_str_pairs([[23, 'stdv', 'badval__']], client
);
965 expect(client
._fail
).to
.have
.been
.calledOnce
;
970 describe('SecurityResult', function () {
973 beforeEach(function () {
975 client
.connect('host', 8675);
976 client
._sock
._websocket
._open();
977 client
._rfb_init_state
= 'SecurityResult';
980 it('should fall through to ServerInitialisation on a response code of 0', function () {
981 client
._updateConnectionState
= sinon
.spy();
982 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
983 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
986 it('should fail on an error code of 1 with the given message for versions >= 3.8', function () {
987 client
._rfb_version
= 3.8;
988 sinon
.spy(client
, '_fail');
989 var failure_data
= [0, 0, 0, 1, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
990 client
._sock
._websocket
._receive_data(new Uint8Array(failure_data
));
991 expect(client
._fail
).to
.have
.been
.calledWith('whoops');
994 it('should fail on an error code of 1 with a standard message for version < 3.8', function () {
995 sinon
.spy(client
, '_fail');
996 client
._rfb_version
= 3.7;
997 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 1]));
998 expect(client
._fail
).to
.have
.been
.calledWith('Authentication failure');
1002 describe('ClientInitialisation', function () {
1005 beforeEach(function () {
1006 client
= make_rfb();
1007 client
.connect('host', 8675);
1008 client
._sock
._websocket
._open();
1009 client
._rfb_init_state
= 'SecurityResult';
1012 it('should transition to the ServerInitialisation state', function () {
1013 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1014 expect(client
._rfb_init_state
).to
.equal('ServerInitialisation');
1017 it('should send 1 if we are in shared mode', function () {
1018 client
.set_shared(true);
1019 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1020 expect(client
._sock
).to
.have
.sent(new Uint8Array([1]));
1023 it('should send 0 if we are not in shared mode', function () {
1024 client
.set_shared(false);
1025 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 0]));
1026 expect(client
._sock
).to
.have
.sent(new Uint8Array([0]));
1030 describe('ServerInitialisation', function () {
1033 beforeEach(function () {
1034 client
= make_rfb();
1035 client
.connect('host', 8675);
1036 client
._sock
._websocket
._open();
1037 client
._rfb_init_state
= 'ServerInitialisation';
1040 function send_server_init(opts
, client
) {
1041 var full_opts
= { width
: 10, height
: 12, bpp
: 24, depth
: 24, big_endian
: 0,
1042 true_color
: 1, red_max
: 255, green_max
: 255, blue_max
: 255,
1043 red_shift
: 16, green_shift
: 8, blue_shift
: 0, name
: 'a name' };
1044 for (var opt
in opts
) {
1045 full_opts
[opt
] = opts
[opt
];
1049 push16(data
, full_opts
.width
);
1050 push16(data
, full_opts
.height
);
1052 data
.push(full_opts
.bpp
);
1053 data
.push(full_opts
.depth
);
1054 data
.push(full_opts
.big_endian
);
1055 data
.push(full_opts
.true_color
);
1057 push16(data
, full_opts
.red_max
);
1058 push16(data
, full_opts
.green_max
);
1059 push16(data
, full_opts
.blue_max
);
1060 push8(data
, full_opts
.red_shift
);
1061 push8(data
, full_opts
.green_shift
);
1062 push8(data
, full_opts
.blue_shift
);
1069 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1072 push32(name_data
, full_opts
.name
.length
);
1073 for (var i
= 0; i
< full_opts
.name
.length
; i
++) {
1074 name_data
.push(full_opts
.name
.charCodeAt(i
));
1076 client
._sock
._websocket
._receive_data(new Uint8Array(name_data
));
1079 it('should set the framebuffer width and height', function () {
1080 send_server_init({ width
: 32, height
: 84 }, client
);
1081 expect(client
._fb_width
).to
.equal(32);
1082 expect(client
._fb_height
).to
.equal(84);
1085 // NB(sross): we just warn, not fail, for endian-ness and shifts, so we don't test them
1087 it('should set the framebuffer name and call the callback', function () {
1088 client
.set_onDesktopName(sinon
.spy());
1089 send_server_init({ name
: 'some name' }, client
);
1091 var spy
= client
.get_onDesktopName();
1092 expect(client
._fb_name
).to
.equal('some name');
1093 expect(spy
).to
.have
.been
.calledOnce
;
1094 expect(spy
.args
[0][1]).to
.equal('some name');
1097 it('should handle the extended init message of the tight encoding', function () {
1098 // NB(sross): we don't actually do anything with it, so just test that we can
1099 // read it w/o throwing an error
1100 client
._rfb_tightvnc
= true;
1101 send_server_init({}, client
);
1103 var tight_data
= [];
1104 push16(tight_data
, 1);
1105 push16(tight_data
, 2);
1106 push16(tight_data
, 3);
1107 push16(tight_data
, 0);
1108 for (var i
= 0; i
< 16 + 32 + 48; i
++) {
1111 client
._sock
._websocket
._receive_data(tight_data
);
1113 expect(client
._rfb_connection_state
).to
.equal('connected');
1116 it('should set the true color mode on the display to the configuration variable', function () {
1117 client
.set_true_color(false);
1118 sinon
.spy(client
._display
, 'set_true_color');
1119 send_server_init({ true_color
: 1 }, client
);
1120 expect(client
._display
.set_true_color
).to
.have
.been
.calledOnce
;
1121 expect(client
._display
.set_true_color
).to
.have
.been
.calledWith(false);
1124 it('should call the resize callback and resize the display', function () {
1125 client
.set_onFBResize(sinon
.spy());
1126 sinon
.spy(client
._display
, 'resize');
1127 send_server_init({ width
: 27, height
: 32 }, client
);
1129 var spy
= client
.get_onFBResize();
1130 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1131 expect(client
._display
.resize
).to
.have
.been
.calledWith(27, 32);
1132 expect(spy
).to
.have
.been
.calledOnce
;
1133 expect(spy
.args
[0][1]).to
.equal(27);
1134 expect(spy
.args
[0][2]).to
.equal(32);
1137 it('should grab the mouse and keyboard', function () {
1138 sinon
.spy(client
._keyboard
, 'grab');
1139 sinon
.spy(client
._mouse
, 'grab');
1140 send_server_init({}, client
);
1141 expect(client
._keyboard
.grab
).to
.have
.been
.calledOnce
;
1142 expect(client
._mouse
.grab
).to
.have
.been
.calledOnce
;
1145 it('should set the BPP and depth to 4 and 3 respectively if in true color mode', function () {
1146 client
.set_true_color(true);
1147 send_server_init({}, client
);
1148 expect(client
._fb_Bpp
).to
.equal(4);
1149 expect(client
._fb_depth
).to
.equal(3);
1152 it('should set the BPP and depth to 1 and 1 respectively if not in true color mode', function () {
1153 client
.set_true_color(false);
1154 send_server_init({}, client
);
1155 expect(client
._fb_Bpp
).to
.equal(1);
1156 expect(client
._fb_depth
).to
.equal(1);
1159 // TODO(directxman12): test the various options in this configuration matrix
1160 it('should reply with the pixel format, client encodings, and initial update request', function () {
1161 client
.set_true_color(true);
1162 client
.set_local_cursor(false);
1163 // we skip the cursor encoding
1164 var expected
= {_sQ
: new Uint8Array(34 + 4 * (client
._encodings
.length
- 1)),
1166 flush: function () {}};
1167 RFB
.messages
.pixelFormat(expected
, 4, 3, true);
1168 RFB
.messages
.clientEncodings(expected
, client
._encodings
, false, true);
1169 RFB
.messages
.fbUpdateRequest(expected
, false, 0, 0, 27, 32);
1171 send_server_init({ width
: 27, height
: 32 }, client
);
1172 expect(client
._sock
).to
.have
.sent(expected
._sQ
);
1175 it('should transition to the "connected" state', function () {
1176 send_server_init({}, client
);
1177 expect(client
._rfb_connection_state
).to
.equal('connected');
1182 describe('Protocol Message Processing After Completing Initialization', function () {
1185 beforeEach(function () {
1186 client
= make_rfb();
1187 client
.connect('host', 8675);
1188 client
._sock
._websocket
._open();
1189 client
._rfb_connection_state
= 'connected';
1190 client
._fb_name
= 'some device';
1191 client
._fb_width
= 640;
1192 client
._fb_height
= 20;
1195 describe('Framebuffer Update Handling', function () {
1198 beforeEach(function () {
1199 client
= make_rfb();
1200 client
.connect('host', 8675);
1201 client
._sock
._websocket
._open();
1202 client
._rfb_connection_state
= 'connected';
1203 client
._fb_name
= 'some device';
1204 client
._fb_width
= 640;
1205 client
._fb_height
= 20;
1208 var target_data_arr
= [
1209 0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1210 0x00, 0xff, 0x00, 255, 0xff, 0x00, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1211 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255,
1212 0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255
1216 var target_data_check_arr
= [
1217 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1218 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
1219 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
1220 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
1222 var target_data_check
;
1224 before(function () {
1225 // NB(directxman12): PhantomJS 1.x doesn't implement Uint8ClampedArray
1226 target_data
= new Uint8Array(target_data_arr
);
1227 target_data_check
= new Uint8Array(target_data_check_arr
);
1230 function send_fbu_msg (rect_info
, rect_data
, client
, rect_cnt
) {
1233 if (!rect_cnt
|| rect_cnt
> -1) {
1235 data
.push(0); // msg type
1236 data
.push(0); // padding
1237 push16(data
, rect_cnt
|| rect_data
.length
);
1240 for (var i
= 0; i
< rect_data
.length
; i
++) {
1242 push16(data
, rect_info
[i
].x
);
1243 push16(data
, rect_info
[i
].y
);
1244 push16(data
, rect_info
[i
].width
);
1245 push16(data
, rect_info
[i
].height
);
1246 push32(data
, rect_info
[i
].encoding
);
1248 data
= data
.concat(rect_data
[i
]);
1251 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1254 it('should send an update request if there is sufficient data', function () {
1255 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1256 RFB
.messages
.fbUpdateRequest(expected_msg
, false, 0, 0, 240, 20);
1258 client
._framebufferUpdate = function () { return true; };
1259 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1261 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1264 it('should not send an update request if we need more data', function () {
1265 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1266 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1269 it('should resume receiving an update if we previously did not have enough data', function () {
1270 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1271 RFB
.messages
.fbUpdateRequest(expected_msg
, false, 0, 0, 240, 20);
1273 // just enough to set FBU.rects
1274 client
._sock
._websocket
._receive_data(new Uint8Array([0, 0, 0, 3]));
1275 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1277 client
._framebufferUpdate = function () { this._sock
.rQskip8(); return true; }; // we magically have enough data
1278 // 247 should *not* be used as the message type here
1279 client
._sock
._websocket
._receive_data(new Uint8Array([247]));
1280 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1283 it('should send a request for both clean and dirty areas', function () {
1284 var expected_msg
= {_sQ
: new Uint8Array(20), _sQlen
: 0, flush: function() {}};
1285 var expected_cdr
= { cleanBox
: { x
: 0, y
: 0, w
: 120, h
: 20 },
1286 dirtyBoxes
: [ { x
: 120, y
: 0, w
: 120, h
: 20 } ] };
1288 RFB
.messages
.fbUpdateRequest(expected_msg
, true, 0, 0, 120, 20);
1289 RFB
.messages
.fbUpdateRequest(expected_msg
, false, 120, 0, 120, 20);
1291 client
._framebufferUpdate = function () { return true; };
1292 client
._display
.getCleanDirtyReset = function () { return expected_cdr
; };
1293 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1295 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1298 it('should only request non-incremental rects in continuous updates mode', function () {
1299 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1300 var expected_cdr
= { cleanBox
: { x
: 0, y
: 0, w
: 120, h
: 20 },
1301 dirtyBoxes
: [ { x
: 120, y
: 0, w
: 120, h
: 20 } ] };
1303 RFB
.messages
.fbUpdateRequest(expected_msg
, false, 120, 0, 120, 20);
1305 client
._enabledContinuousUpdates
= true;
1306 client
._framebufferUpdate = function () { return true; };
1307 client
._display
.getCleanDirtyReset = function () { return expected_cdr
; };
1308 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1310 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1313 it('should not send a request in continuous updates mode when clean', function () {
1314 var expected_cdr
= { cleanBox
: { x
: 0, y
: 0, w
: 240, h
: 20 },
1317 client
._enabledContinuousUpdates
= true;
1318 client
._framebufferUpdate = function () { return true; };
1319 client
._display
.getCleanDirtyReset = function () { return expected_cdr
; };
1320 client
._sock
._websocket
._receive_data(new Uint8Array([0]));
1322 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1325 it('should parse out information from a header before any actual data comes in', function () {
1326 client
.set_onFBUReceive(sinon
.spy());
1327 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 0x02, encodingName
: 'RRE' };
1328 send_fbu_msg([rect_info
], [[]], client
);
1330 var spy
= client
.get_onFBUReceive();
1331 expect(spy
).to
.have
.been
.calledOnce
;
1332 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, rect_info
);
1335 it('should fire onFBUComplete when the update is complete', function () {
1336 client
.set_onFBUComplete(sinon
.spy());
1337 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: -224, encodingName
: 'last_rect' };
1338 send_fbu_msg([rect_info
], [[]], client
); // last_rect
1340 var spy
= client
.get_onFBUComplete();
1341 expect(spy
).to
.have
.been
.calledOnce
;
1342 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, rect_info
);
1345 it('should not fire onFBUComplete if we have not finished processing the update', function () {
1346 client
.set_onFBUComplete(sinon
.spy());
1347 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 0x00, encodingName
: 'RAW' };
1348 send_fbu_msg([rect_info
], [[]], client
);
1349 expect(client
.get_onFBUComplete()).to
.not
.have
.been
.called
;
1352 it('should call the appropriate encoding handler', function () {
1353 client
._encHandlers
[0x02] = sinon
.spy();
1354 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 0x02 };
1355 send_fbu_msg([rect_info
], [[]], client
);
1356 expect(client
._encHandlers
[0x02]).to
.have
.been
.calledOnce
;
1359 it('should fail on an unsupported encoding', function () {
1360 sinon
.spy(client
, "_fail");
1361 var rect_info
= { x
: 8, y
: 11, width
: 27, height
: 32, encoding
: 234 };
1362 send_fbu_msg([rect_info
], [[]], client
);
1363 expect(client
._fail
).to
.have
.been
.calledOnce
;
1366 it('should be able to pause and resume receiving rects if not enought data', function () {
1367 // seed some initial data to copy
1368 client
._fb_width
= 4;
1369 client
._fb_height
= 4;
1370 client
._display
.resize(4, 4);
1371 var initial_data
= client
._display
._drawCtx
.createImageData(4, 2);
1372 var initial_data_arr
= target_data_check_arr
.slice(0, 32);
1373 for (var i
= 0; i
< 32; i
++) { initial_data
.data
[i
] = initial_data_arr
[i
]; }
1374 client
._display
._drawCtx
.putImageData(initial_data
, 0, 0);
1376 var info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1377 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1378 // data says [{ old_x: 0, old_y: 0 }, { old_x: 0, old_y: 0 }]
1379 var rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1380 send_fbu_msg([info
[0]], [rects
[0]], client
, 2);
1381 send_fbu_msg([info
[1]], [rects
[1]], client
, -1);
1382 expect(client
._display
).to
.have
.displayed(target_data_check
);
1385 describe('Message Encoding Handlers', function () {
1388 beforeEach(function () {
1389 client
= make_rfb();
1390 client
.connect('host', 8675);
1391 client
._sock
._websocket
._open();
1392 client
._rfb_connection_state
= 'connected';
1393 client
._fb_name
= 'some device';
1394 // a really small frame
1395 client
._fb_width
= 4;
1396 client
._fb_height
= 4;
1397 client
._display
._fb_width
= 4;
1398 client
._display
._fb_height
= 4;
1399 client
._display
._viewportLoc
.w
= 4;
1400 client
._display
._viewportLoc
.h
= 4;
1404 it('should handle the RAW encoding', function () {
1405 var info
= [{ x
: 0, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1406 { x
: 2, y
: 0, width
: 2, height
: 2, encoding
: 0x00 },
1407 { x
: 0, y
: 2, width
: 4, height
: 1, encoding
: 0x00 },
1408 { x
: 0, y
: 3, width
: 4, height
: 1, encoding
: 0x00 }];
1411 [0x00, 0x00, 0xff, 0, 0x00, 0xff, 0x00, 0, 0x00, 0xff, 0x00, 0, 0x00, 0x00, 0xff, 0],
1412 [0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0],
1413 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0],
1414 [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0]];
1415 send_fbu_msg(info
, rects
, client
);
1416 expect(client
._display
).to
.have
.displayed(target_data
);
1419 it('should handle the COPYRECT encoding', function () {
1420 // seed some initial data to copy
1421 var initial_data
= client
._display
._drawCtx
.createImageData(4, 2);
1422 var initial_data_arr
= target_data_check_arr
.slice(0, 32);
1423 for (var i
= 0; i
< 32; i
++) { initial_data
.data
[i
] = initial_data_arr
[i
]; }
1424 client
._display
._drawCtx
.putImageData(initial_data
, 0, 0);
1426 var info
= [{ x
: 0, y
: 2, width
: 2, height
: 2, encoding
: 0x01},
1427 { x
: 2, y
: 2, width
: 2, height
: 2, encoding
: 0x01}];
1428 // data says [{ old_x: 0, old_y: 0 }, { old_x: 0, old_y: 0 }]
1429 var rects
= [[0, 2, 0, 0], [0, 0, 0, 0]];
1430 send_fbu_msg(info
, rects
, client
);
1431 expect(client
._display
).to
.have
.displayed(target_data_check
);
1434 // TODO(directxman12): for encodings with subrects, test resuming on partial send?
1435 // TODO(directxman12): test rre_chunk_sz (related to above about subrects)?
1437 it('should handle the RRE encoding', function () {
1438 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x02 }];
1440 push32(rect
, 2); // 2 subrects
1441 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1442 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1446 push16(rect
, 0); // x: 0
1447 push16(rect
, 0); // y: 0
1448 push16(rect
, 2); // width: 2
1449 push16(rect
, 2); // height: 2
1450 rect
.push(0xff); // becomes ff0000ff --> #0000FF color
1454 push16(rect
, 2); // x: 2
1455 push16(rect
, 2); // y: 2
1456 push16(rect
, 2); // width: 2
1457 push16(rect
, 2); // height: 2
1459 send_fbu_msg(info
, [rect
], client
);
1460 expect(client
._display
).to
.have
.displayed(target_data_check
);
1463 describe('the HEXTILE encoding handler', function () {
1465 beforeEach(function () {
1466 client
= make_rfb();
1467 client
.connect('host', 8675);
1468 client
._sock
._websocket
._open();
1469 client
._rfb_connection_state
= 'connected';
1470 client
._fb_name
= 'some device';
1471 // a really small frame
1472 client
._fb_width
= 4;
1473 client
._fb_height
= 4;
1474 client
._display
._fb_width
= 4;
1475 client
._display
._fb_height
= 4;
1476 client
._display
._viewportLoc
.w
= 4;
1477 client
._display
._viewportLoc
.h
= 4;
1481 it('should handle a tile with fg, bg specified, normal subrects', function () {
1482 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1484 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1485 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1486 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1490 rect
.push(2); // 2 subrects
1491 rect
.push(0); // x: 0, y: 0
1492 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1493 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1494 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1495 send_fbu_msg(info
, [rect
], client
);
1496 expect(client
._display
).to
.have
.displayed(target_data_check
);
1499 it('should handle a raw tile', function () {
1500 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1502 rect
.push(0x01); // raw
1503 for (var i
= 0; i
< target_data
.length
; i
+= 4) {
1504 rect
.push(target_data
[i
+ 2]);
1505 rect
.push(target_data
[i
+ 1]);
1506 rect
.push(target_data
[i
]);
1507 rect
.push(target_data
[i
+ 3]);
1509 send_fbu_msg(info
, [rect
], client
);
1510 expect(client
._display
).to
.have
.displayed(target_data
);
1513 it('should handle a tile with only bg specified (solid bg)', function () {
1514 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1517 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1518 send_fbu_msg(info
, [rect
], client
);
1521 for (var i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); }
1522 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1525 it('should handle a tile with only bg specified and an empty frame afterwards', function () {
1526 // set the width so we can have two tiles
1527 client
._fb_width
= 8;
1528 client
._display
._fb_width
= 8;
1529 client
._display
._viewportLoc
.w
= 8;
1531 var info
= [{ x
: 0, y
: 0, width
: 32, height
: 4, encoding
: 0x05 }];
1537 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1539 // send an empty frame
1542 send_fbu_msg(info
, [rect
], client
);
1546 for (i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 1: solid
1547 for (i
= 0; i
< 16; i
++) { push32(expected
, 0xff00ff); } // rect 2: same bkground color
1548 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1551 it('should handle a tile with bg and coloured subrects', function () {
1552 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1554 rect
.push(0x02 | 0x08 | 0x10); // bg spec, anysubrects, colouredsubrects
1555 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1556 rect
.push(2); // 2 subrects
1557 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1561 rect
.push(0); // x: 0, y: 0
1562 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1563 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1567 rect
.push(2 | (2 << 4)); // x: 2, y: 2
1568 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1569 send_fbu_msg(info
, [rect
], client
);
1570 expect(client
._display
).to
.have
.displayed(target_data_check
);
1573 it('should carry over fg and bg colors from the previous tile if not specified', function () {
1574 client
._fb_width
= 4;
1575 client
._fb_height
= 17;
1576 client
._display
.resize(4, 17);
1578 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 17, encoding
: 0x05}];
1580 rect
.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
1581 push32(rect
, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
1582 rect
.push(0xff); // becomes ff0000ff --> #0000FF fg color
1586 rect
.push(8); // 8 subrects
1588 for (i
= 0; i
< 4; i
++) {
1589 rect
.push((0 << 4) | (i
* 4)); // x: 0, y: i*4
1590 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1591 rect
.push((2 << 4) | (i
* 4 + 2)); // x: 2, y: i * 4 + 2
1592 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1594 rect
.push(0x08); // anysubrects
1595 rect
.push(1); // 1 subrect
1596 rect
.push(0); // x: 0, y: 0
1597 rect
.push(1 | (1 << 4)); // width: 2, height: 2
1598 send_fbu_msg(info
, [rect
], client
);
1601 for (i
= 0; i
< 4; i
++) { expected
= expected
.concat(target_data_check_arr
); }
1602 expected
= expected
.concat(target_data_check_arr
.slice(0, 16));
1603 expect(client
._display
).to
.have
.displayed(new Uint8Array(expected
));
1606 it('should fail on an invalid subencoding', function () {
1607 sinon
.spy(client
,"_fail");
1608 var info
= [{ x
: 0, y
: 0, width
: 4, height
: 4, encoding
: 0x05 }];
1609 var rects
= [[45]]; // an invalid subencoding
1610 send_fbu_msg(info
, rects
, client
);
1611 expect(client
._fail
).to
.have
.been
.calledOnce
;
1615 it
.skip('should handle the TIGHT encoding', function () {
1616 // TODO(directxman12): test this
1619 it
.skip('should handle the TIGHT_PNG encoding', function () {
1620 // TODO(directxman12): test this
1623 it('should handle the DesktopSize pseduo-encoding', function () {
1624 client
.set_onFBResize(sinon
.spy());
1625 sinon
.spy(client
._display
, 'resize');
1626 send_fbu_msg([{ x
: 0, y
: 0, width
: 20, height
: 50, encoding
: -223 }], [[]], client
);
1628 var spy
= client
.get_onFBResize();
1629 expect(spy
).to
.have
.been
.calledOnce
;
1630 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 20, 50);
1632 expect(client
._fb_width
).to
.equal(20);
1633 expect(client
._fb_height
).to
.equal(50);
1635 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1636 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1639 describe('the ExtendedDesktopSize pseudo-encoding handler', function () {
1642 beforeEach(function () {
1643 client
= make_rfb();
1644 client
.connect('host', 8675);
1645 client
._sock
._websocket
._open();
1646 client
._rfb_connection_state
= 'connected';
1647 client
._fb_name
= 'some device';
1648 client
._supportsSetDesktopSize
= false;
1649 // a really small frame
1650 client
._fb_width
= 4;
1651 client
._fb_height
= 4;
1652 client
._display
._fb_width
= 4;
1653 client
._display
._fb_height
= 4;
1654 client
._display
._viewportLoc
.w
= 4;
1655 client
._display
._viewportLoc
.h
= 4;
1657 sinon
.spy(client
._display
, 'resize');
1658 client
.set_onFBResize(sinon
.spy());
1661 function make_screen_data (nr_of_screens
) {
1663 push8(data
, nr_of_screens
); // number-of-screens
1664 push8(data
, 0); // padding
1665 push16(data
, 0); // padding
1666 for (var i
=0; i
<nr_of_screens
; i
+= 1) {
1667 push32(data
, 0); // id
1668 push16(data
, 0); // x-position
1669 push16(data
, 0); // y-position
1670 push16(data
, 20); // width
1671 push16(data
, 50); // height
1672 push32(data
, 0); // flags
1677 it('should handle a resize requested by this client', function () {
1678 var reason_for_change
= 1; // requested by this client
1679 var status_code
= 0; // No error
1681 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1682 width
: 20, height
: 50, encoding
: -308 }],
1683 make_screen_data(1), client
);
1685 expect(client
._supportsSetDesktopSize
).to
.be
.true;
1686 expect(client
._fb_width
).to
.equal(20);
1687 expect(client
._fb_height
).to
.equal(50);
1689 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1690 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1692 var spy
= client
.get_onFBResize();
1693 expect(spy
).to
.have
.been
.calledOnce
;
1694 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 20, 50);
1697 it('should handle a resize requested by another client', function () {
1698 var reason_for_change
= 2; // requested by another client
1699 var status_code
= 0; // No error
1701 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1702 width
: 20, height
: 50, encoding
: -308 }],
1703 make_screen_data(1), client
);
1705 expect(client
._supportsSetDesktopSize
).to
.be
.true;
1706 expect(client
._fb_width
).to
.equal(20);
1707 expect(client
._fb_height
).to
.equal(50);
1709 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1710 expect(client
._display
.resize
).to
.have
.been
.calledWith(20, 50);
1712 var spy
= client
.get_onFBResize();
1713 expect(spy
).to
.have
.been
.calledOnce
;
1714 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 20, 50);
1717 it('should be able to recieve requests which contain data for multiple screens', function () {
1718 var reason_for_change
= 2; // requested by another client
1719 var status_code
= 0; // No error
1721 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1722 width
: 60, height
: 50, encoding
: -308 }],
1723 make_screen_data(3), client
);
1725 expect(client
._supportsSetDesktopSize
).to
.be
.true;
1726 expect(client
._fb_width
).to
.equal(60);
1727 expect(client
._fb_height
).to
.equal(50);
1729 expect(client
._display
.resize
).to
.have
.been
.calledOnce
;
1730 expect(client
._display
.resize
).to
.have
.been
.calledWith(60, 50);
1732 var spy
= client
.get_onFBResize();
1733 expect(spy
).to
.have
.been
.calledOnce
;
1734 expect(spy
).to
.have
.been
.calledWith(sinon
.match
.any
, 60, 50);
1737 it('should not handle a failed request', function () {
1738 var reason_for_change
= 1; // requested by this client
1739 var status_code
= 1; // Resize is administratively prohibited
1741 send_fbu_msg([{ x
: reason_for_change
, y
: status_code
,
1742 width
: 20, height
: 50, encoding
: -308 }],
1743 make_screen_data(1), client
);
1745 expect(client
._fb_width
).to
.equal(4);
1746 expect(client
._fb_height
).to
.equal(4);
1748 expect(client
._display
.resize
).to
.not
.have
.been
.called
;
1750 var spy
= client
.get_onFBResize();
1751 expect(spy
).to
.not
.have
.been
.called
;
1755 it
.skip('should handle the Cursor pseudo-encoding', function () {
1756 // TODO(directxman12): test
1759 it('should handle the last_rect pseudo-encoding', function () {
1760 client
.set_onFBUReceive(sinon
.spy());
1761 send_fbu_msg([{ x
: 0, y
: 0, width
: 0, height
: 0, encoding
: -224}], [[]], client
, 100);
1762 expect(client
._FBU
.rects
).to
.equal(0);
1763 expect(client
.get_onFBUReceive()).to
.have
.been
.calledOnce
;
1768 it('should set the colour map on the display on SetColourMapEntries', function () {
1769 var expected_cm
= [];
1770 var data
= [1, 0, 0, 1, 0, 4];
1772 for (i
= 0; i
< 4; i
++) {
1773 expected_cm
[i
+ 1] = [i
* 10, i
* 10 + 1, i
* 10 + 2];
1774 push16(data
, expected_cm
[i
+ 1][2] << 8);
1775 push16(data
, expected_cm
[i
+ 1][1] << 8);
1776 push16(data
, expected_cm
[i
+ 1][0] << 8);
1779 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1780 expect(client
._display
.get_colourMap()).to
.deep
.equal(expected_cm
);
1783 describe('XVP Message Handling', function () {
1784 beforeEach(function () {
1785 client
= make_rfb();
1786 client
.connect('host', 8675);
1787 client
._sock
._websocket
._open();
1788 client
._rfb_connection_state
= 'connected';
1789 client
._fb_name
= 'some device';
1790 client
._fb_width
= 27;
1791 client
._fb_height
= 32;
1794 it('should send a notification on XVP_FAIL', function () {
1795 client
.set_onNotification(sinon
.spy());
1796 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 0]));
1797 var spy
= client
.get_onNotification();
1798 expect(spy
).to
.have
.been
.calledOnce
;
1799 expect(spy
.args
[0][1]).to
.equal('XVP Operation Failed');
1802 it('should set the XVP version and fire the callback with the version on XVP_INIT', function () {
1803 client
.set_onXvpInit(sinon
.spy());
1804 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 1]));
1805 expect(client
._rfb_xvp_ver
).to
.equal(10);
1806 expect(client
.get_onXvpInit()).to
.have
.been
.calledOnce
;
1807 expect(client
.get_onXvpInit()).to
.have
.been
.calledWith(10);
1810 it('should fail on unknown XVP message types', function () {
1811 sinon
.spy(client
, "_fail");
1812 client
._sock
._websocket
._receive_data(new Uint8Array([250, 0, 10, 237]));
1813 expect(client
._fail
).to
.have
.been
.calledOnce
;
1817 it('should fire the clipboard callback with the retrieved text on ServerCutText', function () {
1818 var expected_str
= 'cheese!';
1819 var data
= [3, 0, 0, 0];
1820 push32(data
, expected_str
.length
);
1821 for (var i
= 0; i
< expected_str
.length
; i
++) { data
.push(expected_str
.charCodeAt(i
)); }
1822 client
.set_onClipboard(sinon
.spy());
1824 client
._sock
._websocket
._receive_data(new Uint8Array(data
));
1825 var spy
= client
.get_onClipboard();
1826 expect(spy
).to
.have
.been
.calledOnce
;
1827 expect(spy
.args
[0][1]).to
.equal(expected_str
);
1830 it('should fire the bell callback on Bell', function () {
1831 client
.set_onBell(sinon
.spy());
1832 client
._sock
._websocket
._receive_data(new Uint8Array([2]));
1833 expect(client
.get_onBell()).to
.have
.been
.calledOnce
;
1836 it('should respond correctly to ServerFence', function () {
1837 var expected_msg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function() {}};
1838 var incoming_msg
= {_sQ
: new Uint8Array(16), _sQlen
: 0, flush: function() {}};
1840 var payload
= "foo\x00ab9";
1842 // ClientFence and ServerFence are identical in structure
1843 RFB
.messages
.clientFence(expected_msg
, (1<<0) | (1<<1), payload
);
1844 RFB
.messages
.clientFence(incoming_msg
, 0xffffffff, payload
);
1846 client
._sock
._websocket
._receive_data(incoming_msg
._sQ
);
1848 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1850 expected_msg
._sQlen
= 0;
1851 incoming_msg
._sQlen
= 0;
1853 RFB
.messages
.clientFence(expected_msg
, (1<<0), payload
);
1854 RFB
.messages
.clientFence(incoming_msg
, (1<<0) | (1<<31), payload
);
1856 client
._sock
._websocket
._receive_data(incoming_msg
._sQ
);
1858 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1861 it('should enable continuous updates on first EndOfContinousUpdates', function () {
1862 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1864 RFB
.messages
.enableContinuousUpdates(expected_msg
, true, 0, 0, 640, 20);
1866 expect(client
._enabledContinuousUpdates
).to
.be
.false;
1868 client
._sock
._websocket
._receive_data(new Uint8Array([150]));
1870 expect(client
._enabledContinuousUpdates
).to
.be
.true;
1871 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1874 it('should disable continuous updates on subsequent EndOfContinousUpdates', function () {
1875 client
._enabledContinuousUpdates
= true;
1876 client
._supportsContinuousUpdates
= true;
1878 client
._sock
._websocket
._receive_data(new Uint8Array([150]));
1880 expect(client
._enabledContinuousUpdates
).to
.be
.false;
1883 it('should update continuous updates on resize', function () {
1884 var expected_msg
= {_sQ
: new Uint8Array(10), _sQlen
: 0, flush: function() {}};
1885 RFB
.messages
.enableContinuousUpdates(expected_msg
, true, 0, 0, 90, 700);
1887 client
._FBU
.width
= 450;
1888 client
._FBU
.height
= 160;
1890 client
._encHandlers
.handle_FB_resize();
1892 expect(client
._sock
._websocket
._get_sent_data()).to
.have
.length(0);
1894 client
._enabledContinuousUpdates
= true;
1896 client
._FBU
.width
= 90;
1897 client
._FBU
.height
= 700;
1899 client
._encHandlers
.handle_FB_resize();
1901 expect(client
._sock
).to
.have
.sent(expected_msg
._sQ
);
1904 it('should fail on an unknown message type', function () {
1905 sinon
.spy(client
, "_fail");
1906 client
._sock
._websocket
._receive_data(new Uint8Array([87]));
1907 expect(client
._fail
).to
.have
.been
.calledOnce
;
1911 describe('Asynchronous Events', function () {
1912 describe('Mouse event handlers', function () {
1914 beforeEach(function () {
1915 client
= make_rfb();
1916 client
._sock
= new Websock();
1917 client
._sock
.open('ws://', 'binary');
1918 client
._sock
._websocket
._open();
1919 sinon
.spy(client
._sock
, 'flush');
1920 client
._rfb_connection_state
= 'connected';
1923 it('should not send button messages in view-only mode', function () {
1924 client
._view_only
= true;
1925 client
._mouse
._onMouseButton(0, 0, 1, 0x001);
1926 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1929 it('should not send movement messages in view-only mode', function () {
1930 client
._view_only
= true;
1931 client
._mouse
._onMouseMove(0, 0);
1932 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1935 it('should send a pointer event on mouse button presses', function () {
1936 client
._mouse
._onMouseButton(10, 12, 1, 0x001);
1937 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1938 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x001);
1939 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1942 it('should send a mask of 1 on mousedown', function () {
1943 client
._mouse
._onMouseButton(10, 12, 1, 0x001);
1944 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1945 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x001);
1946 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1949 it('should send a mask of 0 on mouseup', function () {
1950 client
._mouse_buttonMask
= 0x001;
1951 client
._mouse
._onMouseButton(10, 12, 0, 0x001);
1952 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1953 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
1954 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1957 it('should send a pointer event on mouse movement', function () {
1958 client
._mouse
._onMouseMove(10, 12);
1959 var pointer_msg
= {_sQ
: new Uint8Array(6), _sQlen
: 0, flush: function () {}};
1960 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x000);
1961 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1964 it('should set the button mask so that future mouse movements use it', function () {
1965 client
._mouse
._onMouseButton(10, 12, 1, 0x010);
1966 client
._mouse
._onMouseMove(13, 9);
1967 var pointer_msg
= {_sQ
: new Uint8Array(12), _sQlen
: 0, flush: function () {}};
1968 RFB
.messages
.pointerEvent(pointer_msg
, 10, 12, 0x010);
1969 RFB
.messages
.pointerEvent(pointer_msg
, 13, 9, 0x010);
1970 expect(client
._sock
).to
.have
.sent(pointer_msg
._sQ
);
1973 // NB(directxman12): we don't need to test not sending messages in
1974 // non-normal modes, since we haven't grabbed input
1975 // yet (grabbing input should be checked in the lifecycle tests).
1977 it('should not send movement messages when viewport dragging', function () {
1978 client
._viewportDragging
= true;
1979 client
._display
.viewportChangePos
= sinon
.spy();
1980 client
._mouse
._onMouseMove(13, 9);
1981 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1984 it('should not send button messages when initiating viewport dragging', function () {
1985 client
._viewportDrag
= true;
1986 client
._mouse
._onMouseButton(13, 9, 0x001);
1987 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
1990 it('should be initiate viewport dragging on a button down event, if enabled', function () {
1991 client
._viewportDrag
= true;
1992 client
._mouse
._onMouseButton(13, 9, 0x001);
1993 expect(client
._viewportDragging
).to
.be
.true;
1994 expect(client
._viewportDragPos
).to
.deep
.equal({ x
: 13, y
: 9 });
1997 it('should terminate viewport dragging on a button up event, if enabled', function () {
1998 client
._viewportDrag
= true;
1999 client
._viewportDragging
= true;
2000 client
._mouse
._onMouseButton(13, 9, 0x000);
2001 expect(client
._viewportDragging
).to
.be
.false;
2004 it('if enabled, viewportDragging should occur on mouse movement while a button is down', function () {
2005 client
._viewportDrag
= true;
2006 client
._viewportDragging
= true;
2007 client
._viewportHasMoved
= false;
2008 client
._viewportDragPos
= { x
: 23, y
: 9 };
2009 client
._display
.viewportChangePos
= sinon
.spy();
2011 client
._mouse
._onMouseMove(10, 4);
2013 expect(client
._viewportDragging
).to
.be
.true;
2014 expect(client
._viewportHasMoved
).to
.be
.true;
2015 expect(client
._viewportDragPos
).to
.deep
.equal({ x
: 10, y
: 4 });
2016 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledOnce
;
2017 expect(client
._display
.viewportChangePos
).to
.have
.been
.calledWith(13, 5);
2021 describe('Keyboard Event Handlers', function () {
2023 beforeEach(function () {
2024 client
= make_rfb();
2025 client
._sock
= new Websock();
2026 client
._sock
.open('ws://', 'binary');
2027 client
._sock
._websocket
._open();
2028 sinon
.spy(client
._sock
, 'flush');
2031 it('should send a key message on a key press', function () {
2033 keyevent
.type
= 'keydown';
2034 keyevent
.keysym
= {};
2035 keyevent
.keysym
.keysym
= 1234;
2036 client
._keyboard
._onKeyPress(keyevent
);
2037 var key_msg
= {_sQ
: new Uint8Array(8), _sQlen
: 0, flush: function () {}};
2038 RFB
.messages
.keyEvent(key_msg
, 1234, 1);
2039 expect(client
._sock
).to
.have
.sent(key_msg
._sQ
);
2042 it('should not send messages in view-only mode', function () {
2043 client
._view_only
= true;
2044 client
._keyboard
._onKeyPress(1234, 1);
2045 expect(client
._sock
.flush
).to
.not
.have
.been
.called
;
2049 describe('WebSocket event handlers', function () {
2051 beforeEach(function () {
2052 client
= make_rfb();
2053 this.clock
= sinon
.useFakeTimers();
2056 afterEach(function () { this.clock
.restore(); });
2059 it ('should do nothing if we receive an empty message and have nothing in the queue', function () {
2060 client
.connect('host', 8675);
2061 client
._rfb_connection_state
= 'connected';
2062 client
._normal_msg
= sinon
.spy();
2063 client
._sock
._websocket
._receive_data(new Uint8Array([]));
2064 expect(client
._normal_msg
).to
.not
.have
.been
.called
;
2067 it('should handle a message in the connected state as a normal message', function () {
2068 client
.connect('host', 8675);
2069 client
._rfb_connection_state
= 'connected';
2070 client
._normal_msg
= sinon
.spy();
2071 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
2072 expect(client
._normal_msg
).to
.have
.been
.calledOnce
;
2075 it('should handle a message in any non-disconnected/failed state like an init message', function () {
2076 client
.connect('host', 8675);
2077 client
._rfb_init_state
= 'ProtocolVersion';
2078 client
._init_msg
= sinon
.spy();
2079 client
._sock
._websocket
._receive_data(new Uint8Array([1, 2, 3]));
2080 expect(client
._init_msg
).to
.have
.been
.calledOnce
;
2083 it('should process all normal messages directly', function () {
2084 client
.connect('host', 8675);
2085 client
._sock
._websocket
._open();
2086 client
._rfb_connection_state
= 'connected';
2087 client
.set_onBell(sinon
.spy());
2088 client
._sock
._websocket
._receive_data(new Uint8Array([0x02, 0x02]));
2089 expect(client
.get_onBell()).to
.have
.been
.calledTwice
;
2093 it('should update the state to ProtocolVersion on open (if the state is "connecting")', function () {
2094 client
.connect('host', 8675);
2095 client
._sock
._websocket
._open();
2096 expect(client
._rfb_init_state
).to
.equal('ProtocolVersion');
2099 it('should fail if we are not currently ready to connect and we get an "open" event', function () {
2100 sinon
.spy(client
, "_fail");
2101 client
.connect('host', 8675);
2102 client
._rfb_connection_state
= 'some_other_state';
2103 client
._sock
._websocket
._open();
2104 expect(client
._fail
).to
.have
.been
.calledOnce
;
2108 it('should transition to "disconnected" from "disconnecting" on a close event', function () {
2109 client
.connect('host', 8675);
2110 client
._rfb_connection_state
= 'disconnecting';
2111 client
._sock
._websocket
.close();
2112 expect(client
._rfb_connection_state
).to
.equal('disconnected');
2115 it('should fail if we get a close event while connecting', function () {
2116 sinon
.spy(client
, "_fail");
2117 client
.connect('host', 8675);
2118 client
._rfb_connection_state
= 'connecting';
2119 client
._sock
._websocket
.close();
2120 expect(client
._fail
).to
.have
.been
.calledOnce
;
2123 it('should unregister close event handler', function () {
2124 sinon
.spy(client
._sock
, 'off');
2125 client
.connect('host', 8675);
2126 client
._rfb_connection_state
= 'disconnecting';
2127 client
._sock
._websocket
.close();
2128 expect(client
._sock
.off
).to
.have
.been
.calledWith('close');
2131 // error events do nothing