]> git.proxmox.com Git - mirror_novnc.git/blame - tests/test.display.js
Remove keysym names from keysymdef.js
[mirror_novnc.git] / tests / test.display.js
CommitLineData
1e13775b
SR
1/* jshint expr: true */
2var expect = chai.expect;
3
dfae3209
SR
4import Base64 from '../core/base64.js';
5import Display from '../core/display.js';
6import { _forceCursorURIs, browserSupportsCursorURIs } from '../core/util/browsers.js';
7
8import './assertions.js';
9import 'sinon';
10import sinonChai from '../node_modules/sinon-chai/lib/sinon-chai.js'
11chai.use(sinonChai);
12
1e13775b
SR
13describe('Display/Canvas Helper', function () {
14 var checked_data = [
15 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
16 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
17 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
18 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
19 ];
20 checked_data = new Uint8Array(checked_data);
21
22 var basic_data = [0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0xff, 0xff, 0xff, 255];
23 basic_data = new Uint8Array(basic_data);
24
25 function make_image_canvas (input_data) {
26 var canvas = document.createElement('canvas');
27 canvas.width = 4;
28 canvas.height = 4;
29 var ctx = canvas.getContext('2d');
30 var data = ctx.createImageData(4, 4);
31 for (var i = 0; i < checked_data.length; i++) { data.data[i] = input_data[i]; }
32 ctx.putImageData(data, 0, 0);
33 return canvas;
34 }
35
74e39051
PO
36 function make_image_png (input_data) {
37 var canvas = make_image_canvas(input_data);
38 var url = canvas.toDataURL();
39 var data = url.split(",")[1];
40 return Base64.decode(data);
41 }
42
53762c31 43 describe('checking for cursor uri support', function () {
53762c31 44 it('should disable cursor URIs if there is no support', function () {
dfae3209 45 _forceCursorURIs(false);
53762c31
SR
46 var display = new Display({ target: document.createElement('canvas'), prefer_js: true, viewport: false });
47 expect(display._cursor_uri).to.be.false;
48 });
49
50 it('should enable cursor URIs if there is support', function () {
dfae3209 51 _forceCursorURIs(true);
53762c31
SR
52 var display = new Display({ target: document.createElement('canvas'), prefer_js: true, viewport: false });
53 expect(display._cursor_uri).to.be.true;
54 });
55
56 it('respect the cursor_uri option if there is support', function () {
dfae3209 57 _forceCursorURIs(false);
53762c31
SR
58 var display = new Display({ target: document.createElement('canvas'), prefer_js: true, viewport: false, cursor_uri: false });
59 expect(display._cursor_uri).to.be.false;
60 });
53762c31
SR
61 });
62
1e13775b
SR
63 describe('viewport handling', function () {
64 var display;
65 beforeEach(function () {
66 display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true });
67 display.resize(5, 5);
636be753 68 display.viewportChangeSize(3, 3);
69 display.viewportChangePos(1, 1);
1e13775b
SR
70 });
71
72 it('should take viewport location into consideration when drawing images', function () {
e549ae07 73 display.resize(4, 4);
636be753 74 display.viewportChangeSize(2, 2);
1e13775b 75 display.drawImage(make_image_canvas(basic_data), 1, 1);
2ba767a7 76 display.flip();
1e13775b
SR
77
78 var expected = new Uint8Array(16);
79 var i;
80 for (i = 0; i < 8; i++) { expected[i] = basic_data[i]; }
81 for (i = 8; i < 16; i++) { expected[i] = 0; }
82 expect(display).to.have.displayed(expected);
83 });
84
3f781f2a 85 it('should resize the target canvas when resizing the viewport', function() {
2ba767a7 86 display.viewportChangeSize(2, 2);
3f781f2a
PO
87 expect(display._target.width).to.equal(2);
88 expect(display._target.height).to.equal(2);
1e13775b
SR
89 });
90
adf345fd
PO
91 it('should move the viewport if necessary', function() {
92 display.viewportChangeSize(5, 5);
93 expect(display.absX(0)).to.equal(0);
94 expect(display.absY(0)).to.equal(0);
95 expect(display._target.width).to.equal(5);
96 expect(display._target.height).to.equal(5);
97 });
98
99 it('should limit the viewport to the framebuffer size', function() {
100 display.viewportChangeSize(6, 6);
101 expect(display._target.width).to.equal(5);
102 expect(display._target.height).to.equal(5);
103 });
104
2ba767a7
PO
105 it('should redraw when moving the viewport', function () {
106 display.flip = sinon.spy();
107 display.viewportChangePos(-1, 1);
108 expect(display.flip).to.have.been.calledOnce;
1e13775b
SR
109 });
110
2ba767a7
PO
111 it('should redraw when resizing the viewport', function () {
112 display.flip = sinon.spy();
113 display.viewportChangeSize(2, 2);
114 expect(display.flip).to.have.been.calledOnce;
1e13775b 115 });
fdedbafb 116
3f781f2a 117 it('should report clipping when framebuffer > viewport', function () {
fdedbafb 118 var clipping = display.clippingDisplay();
119 expect(clipping).to.be.true;
120 });
121
3f781f2a
PO
122 it('should report not clipping when framebuffer = viewport', function () {
123 display.viewportChangeSize(5, 5);
fdedbafb 124 var clipping = display.clippingDisplay();
125 expect(clipping).to.be.false;
126 });
adf345fd
PO
127
128 it('should show the entire framebuffer when disabling the viewport', function() {
129 display.set_viewport(false);
130 expect(display.absX(0)).to.equal(0);
131 expect(display.absY(0)).to.equal(0);
132 expect(display._target.width).to.equal(5);
133 expect(display._target.height).to.equal(5);
134 });
135
136 it('should ignore viewport changes when the viewport is disabled', function() {
137 display.set_viewport(false);
138 display.viewportChangeSize(2, 2);
139 display.viewportChangePos(1, 1);
140 expect(display.absX(0)).to.equal(0);
141 expect(display.absY(0)).to.equal(0);
142 expect(display._target.width).to.equal(5);
143 expect(display._target.height).to.equal(5);
144 });
145
146 it('should show the entire framebuffer just after enabling the viewport', function() {
147 display.set_viewport(false);
148 display.set_viewport(true);
149 expect(display.absX(0)).to.equal(0);
150 expect(display.absY(0)).to.equal(0);
151 expect(display._target.width).to.equal(5);
152 expect(display._target.height).to.equal(5);
153 });
fdedbafb 154 });
155
1e13775b
SR
156 describe('resizing', function () {
157 var display;
158 beforeEach(function () {
adf345fd
PO
159 display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: false });
160 display.resize(4, 4);
1e13775b
SR
161 });
162
163 it('should change the size of the logical canvas', function () {
164 display.resize(5, 7);
165 expect(display._fb_width).to.equal(5);
166 expect(display._fb_height).to.equal(7);
167 });
168
2ba767a7 169 it('should keep the framebuffer data', function () {
adf345fd 170 display.fillRect(0, 0, 4, 4, [0, 0, 0xff]);
2ba767a7
PO
171 display.resize(2, 2);
172 display.flip();
173 var expected = [];
174 for (var i = 0; i < 4 * 2*2; i += 4) {
175 expected[i] = 0xff;
176 expected[i+1] = expected[i+2] = 0;
177 expected[i+3] = 0xff;
178 }
179 expect(display).to.have.displayed(new Uint8Array(expected));
180 });
adf345fd
PO
181
182 describe('viewport', function () {
183 beforeEach(function () {
184 display.set_viewport(true);
185 display.viewportChangeSize(3, 3);
186 display.viewportChangePos(1, 1);
187 });
188
189 it('should keep the viewport position and size if possible', function () {
190 display.resize(6, 6);
191 expect(display.absX(0)).to.equal(1);
192 expect(display.absY(0)).to.equal(1);
193 expect(display._target.width).to.equal(3);
194 expect(display._target.height).to.equal(3);
195 });
196
197 it('should move the viewport if necessary', function () {
198 display.resize(3, 3);
199 expect(display.absX(0)).to.equal(0);
200 expect(display.absY(0)).to.equal(0);
201 expect(display._target.width).to.equal(3);
202 expect(display._target.height).to.equal(3);
203 });
204
205 it('should shrink the viewport if necessary', function () {
206 display.resize(2, 2);
207 expect(display.absX(0)).to.equal(0);
208 expect(display.absY(0)).to.equal(0);
209 expect(display._target.width).to.equal(2);
210 expect(display._target.height).to.equal(2);
211 });
212 });
1e13775b
SR
213 });
214
72747869
SR
215 describe('rescaling', function () {
216 var display;
217 var canvas;
218
219 beforeEach(function () {
220 display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true });
adf345fd
PO
221 display.resize(4, 4);
222 display.viewportChangeSize(3, 3);
223 display.viewportChangePos(1, 1);
72747869
SR
224 canvas = display.get_target();
225 document.body.appendChild(canvas);
226 });
227
228 afterEach(function () {
229 document.body.removeChild(canvas);
230 });
231
232 it('should not change the bitmap size of the canvas', function () {
adf345fd
PO
233 display.set_scale(2.0);
234 expect(canvas.width).to.equal(3);
72747869
SR
235 expect(canvas.height).to.equal(3);
236 });
237
238 it('should change the effective rendered size of the canvas', function () {
adf345fd
PO
239 display.set_scale(2.0);
240 expect(canvas.clientWidth).to.equal(6);
241 expect(canvas.clientHeight).to.equal(6);
242 });
243
244 it('should not change when resizing', function () {
245 display.set_scale(2.0);
246 display.resize(5, 5);
247 expect(display.get_scale()).to.equal(2.0);
248 expect(canvas.width).to.equal(3);
249 expect(canvas.height).to.equal(3);
250 expect(canvas.clientWidth).to.equal(6);
251 expect(canvas.clientHeight).to.equal(6);
72747869
SR
252 });
253 });
254
255 describe('autoscaling', function () {
256 var display;
257 var canvas;
258
259 beforeEach(function () {
260 display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true });
261 display.resize(4, 3);
262 canvas = display.get_target();
263 document.body.appendChild(canvas);
264 });
265
266 afterEach(function () {
267 document.body.removeChild(canvas);
268 });
269
270 it('should preserve aspect ratio while autoscaling', function () {
271 display.autoscale(16, 9);
272 expect(canvas.clientWidth / canvas.clientHeight).to.equal(4 / 3);
273 });
274
275 it('should use width to determine scale when the current aspect ratio is wider than the target', function () {
280676c7
SM
276 display.autoscale(9, 16);
277 expect(display.absX(9)).to.equal(4);
278 expect(display.absY(18)).to.equal(8);
72747869
SR
279 expect(canvas.clientWidth).to.equal(9);
280 expect(canvas.clientHeight).to.equal(7); // round 9 / (4 / 3)
281 });
282
283 it('should use height to determine scale when the current aspect ratio is taller than the target', function () {
280676c7
SM
284 display.autoscale(16, 9);
285 expect(display.absX(9)).to.equal(3);
286 expect(display.absY(18)).to.equal(6);
72747869
SR
287 expect(canvas.clientWidth).to.equal(12); // 16 * (4 / 3)
288 expect(canvas.clientHeight).to.equal(9);
289
290 });
291
292 it('should not change the bitmap size of the canvas', function () {
293 display.autoscale(16, 9);
294 expect(canvas.width).to.equal(4);
295 expect(canvas.height).to.equal(3);
296 });
297
298 it('should not upscale when downscaleOnly is true', function () {
280676c7
SM
299 display.autoscale(2, 2, true);
300 expect(display.absX(9)).to.equal(18);
301 expect(display.absY(18)).to.equal(36);
72747869
SR
302 expect(canvas.clientWidth).to.equal(2);
303 expect(canvas.clientHeight).to.equal(2);
304
280676c7
SM
305 display.autoscale(16, 9, true);
306 expect(display.absX(9)).to.equal(9);
307 expect(display.absY(18)).to.equal(18);
72747869
SR
308 expect(canvas.clientWidth).to.equal(4);
309 expect(canvas.clientHeight).to.equal(3);
310 });
311 });
312
1e13775b
SR
313 describe('drawing', function () {
314
315 // TODO(directxman12): improve the tests for each of the drawing functions to cover more than just the
316 // basic cases
317 function drawing_tests (pref_js) {
318 var display;
319 beforeEach(function () {
320 display = new Display({ target: document.createElement('canvas'), prefer_js: pref_js });
321 display.resize(4, 4);
322 });
323
324 it('should clear the screen on #clear without a logo set', function () {
325 display.fillRect(0, 0, 4, 4, [0x00, 0x00, 0xff]);
326 display._logo = null;
327 display.clear();
328 display.resize(4, 4);
329 var empty = [];
330 for (var i = 0; i < 4 * display._fb_width * display._fb_height; i++) { empty[i] = 0; }
331 expect(display).to.have.displayed(new Uint8Array(empty));
332 });
333
334 it('should draw the logo on #clear with a logo set', function (done) {
74e39051
PO
335 display._logo = { width: 4, height: 4, type: "image/png", data: make_image_png(checked_data) };
336 display.clear();
337 display.set_onFlush(function () {
1e13775b 338 expect(display).to.have.displayed(checked_data);
74e39051
PO
339 expect(display._fb_width).to.equal(4);
340 expect(display._fb_height).to.equal(4);
1e13775b 341 done();
74e39051
PO
342 });
343 display.flush();
1e13775b
SR
344 });
345
2ba767a7
PO
346 it('should not draw directly on the target canvas', function () {
347 display.fillRect(0, 0, 4, 4, [0, 0, 0xff]);
348 display.flip();
349 display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
350 var expected = [];
351 for (var i = 0; i < 4 * display._fb_width * display._fb_height; i += 4) {
352 expected[i] = 0xff;
353 expected[i+1] = expected[i+2] = 0;
354 expected[i+3] = 0xff;
355 }
356 expect(display).to.have.displayed(new Uint8Array(expected));
357 });
358
1e13775b
SR
359 it('should support filling a rectangle with particular color via #fillRect', function () {
360 display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
361 display.fillRect(0, 0, 2, 2, [0xff, 0, 0]);
362 display.fillRect(2, 2, 2, 2, [0xff, 0, 0]);
2ba767a7 363 display.flip();
1e13775b
SR
364 expect(display).to.have.displayed(checked_data);
365 });
366
367 it('should support copying an portion of the canvas via #copyImage', function () {
368 display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
369 display.fillRect(0, 0, 2, 2, [0xff, 0, 0x00]);
370 display.copyImage(0, 0, 2, 2, 2, 2);
2ba767a7 371 display.flip();
1e13775b
SR
372 expect(display).to.have.displayed(checked_data);
373 });
374
74e39051
PO
375 it('should support drawing images via #imageRect', function (done) {
376 display.imageRect(0, 0, "image/png", make_image_png(checked_data));
377 display.flip();
378 display.set_onFlush(function () {
379 expect(display).to.have.displayed(checked_data);
380 done();
381 });
382 display.flush();
383 });
384
1e13775b
SR
385 it('should support drawing tile data with a background color and sub tiles', function () {
386 display.startTile(0, 0, 4, 4, [0, 0xff, 0]);
387 display.subTile(0, 0, 2, 2, [0xff, 0, 0]);
388 display.subTile(2, 2, 2, 2, [0xff, 0, 0]);
389 display.finishTile();
2ba767a7 390 display.flip();
1e13775b
SR
391 expect(display).to.have.displayed(checked_data);
392 });
393
394 it('should support drawing BGRX blit images with true color via #blitImage', function () {
395 var data = [];
396 for (var i = 0; i < 16; i++) {
397 data[i * 4] = checked_data[i * 4 + 2];
398 data[i * 4 + 1] = checked_data[i * 4 + 1];
399 data[i * 4 + 2] = checked_data[i * 4];
400 data[i * 4 + 3] = checked_data[i * 4 + 3];
401 }
402 display.blitImage(0, 0, 4, 4, data, 0);
2ba767a7 403 display.flip();
1e13775b
SR
404 expect(display).to.have.displayed(checked_data);
405 });
406
407 it('should support drawing RGB blit images with true color via #blitRgbImage', function () {
408 var data = [];
409 for (var i = 0; i < 16; i++) {
410 data[i * 3] = checked_data[i * 4];
411 data[i * 3 + 1] = checked_data[i * 4 + 1];
412 data[i * 3 + 2] = checked_data[i * 4 + 2];
413 }
414 display.blitRgbImage(0, 0, 4, 4, data, 0);
2ba767a7 415 display.flip();
1e13775b
SR
416 expect(display).to.have.displayed(checked_data);
417 });
418
1e13775b
SR
419 it('should support drawing solid colors with color maps', function () {
420 display._true_color = false;
421 display.set_colourMap({ 0: [0xff, 0, 0], 1: [0, 0xff, 0] });
a369a80c
SR
422 display.fillRect(0, 0, 4, 4, 1);
423 display.fillRect(0, 0, 2, 2, 0);
424 display.fillRect(2, 2, 2, 2, 0);
2ba767a7 425 display.flip();
1e13775b
SR
426 expect(display).to.have.displayed(checked_data);
427 });
428
429 it('should support drawing blit images with color maps', function () {
430 display._true_color = false;
431 display.set_colourMap({ 1: [0xff, 0, 0], 0: [0, 0xff, 0] });
432 var data = [1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1].map(function (elem) { return [elem]; });
433 display.blitImage(0, 0, 4, 4, data, 0);
2ba767a7 434 display.flip();
1e13775b
SR
435 expect(display).to.have.displayed(checked_data);
436 });
437
438 it('should support drawing an image object via #drawImage', function () {
439 var img = make_image_canvas(checked_data);
440 display.drawImage(img, 0, 0);
2ba767a7 441 display.flip();
1e13775b
SR
442 expect(display).to.have.displayed(checked_data);
443 });
444 }
445
446 describe('(prefering native methods)', function () { drawing_tests.call(this, false); });
447 describe('(prefering JavaScript)', function () { drawing_tests.call(this, true); });
448 });
449
450 describe('the render queue processor', function () {
451 var display;
452 beforeEach(function () {
453 display = new Display({ target: document.createElement('canvas'), prefer_js: false });
454 display.resize(4, 4);
455 sinon.spy(display, '_scan_renderQ');
1e13775b
SR
456 });
457
458 afterEach(function () {
e4fef7be 459 window.requestAnimationFrame = this.old_requestAnimationFrame;
1e13775b
SR
460 });
461
462 it('should try to process an item when it is pushed on, if nothing else is on the queue', function () {
1578fa68 463 display._renderQ_push({ type: 'noop' }); // does nothing
1e13775b
SR
464 expect(display._scan_renderQ).to.have.been.calledOnce;
465 });
466
467 it('should not try to process an item when it is pushed on if we are waiting for other items', function () {
468 display._renderQ.length = 2;
1578fa68 469 display._renderQ_push({ type: 'noop' });
1e13775b
SR
470 expect(display._scan_renderQ).to.not.have.been.called;
471 });
472
473 it('should wait until an image is loaded to attempt to draw it and the rest of the queue', function () {
bb6965f2 474 var img = { complete: false, addEventListener: sinon.spy() }
1e13775b
SR
475 display._renderQ = [{ type: 'img', x: 3, y: 4, img: img },
476 { type: 'fill', x: 1, y: 2, width: 3, height: 4, color: 5 }];
477 display.drawImage = sinon.spy();
478 display.fillRect = sinon.spy();
479
480 display._scan_renderQ();
481 expect(display.drawImage).to.not.have.been.called;
482 expect(display.fillRect).to.not.have.been.called;
bb6965f2 483 expect(img.addEventListener).to.have.been.calledOnce;
1e13775b
SR
484
485 display._renderQ[0].img.complete = true;
bb6965f2 486 display._scan_renderQ();
1e13775b
SR
487 expect(display.drawImage).to.have.been.calledOnce;
488 expect(display.fillRect).to.have.been.calledOnce;
bb6965f2 489 expect(img.addEventListener).to.have.been.calledOnce;
1e13775b
SR
490 });
491
3181a032
PO
492 it('should call callback when queue is flushed', function () {
493 display.set_onFlush(sinon.spy());
494 display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
495 expect(display.get_onFlush()).to.not.have.been.called;
496 display.flush();
497 expect(display.get_onFlush()).to.have.been.calledOnce;
498 });
499
1e13775b
SR
500 it('should draw a blit image on type "blit"', function () {
501 display.blitImage = sinon.spy();
1578fa68 502 display._renderQ_push({ type: 'blit', x: 3, y: 4, width: 5, height: 6, data: [7, 8, 9] });
1e13775b
SR
503 expect(display.blitImage).to.have.been.calledOnce;
504 expect(display.blitImage).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9], 0);
505 });
506
507 it('should draw a blit RGB image on type "blitRgb"', function () {
508 display.blitRgbImage = sinon.spy();
1578fa68 509 display._renderQ_push({ type: 'blitRgb', x: 3, y: 4, width: 5, height: 6, data: [7, 8, 9] });
1e13775b
SR
510 expect(display.blitRgbImage).to.have.been.calledOnce;
511 expect(display.blitRgbImage).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9], 0);
512 });
513
514 it('should copy a region on type "copy"', function () {
515 display.copyImage = sinon.spy();
1578fa68 516 display._renderQ_push({ type: 'copy', x: 3, y: 4, width: 5, height: 6, old_x: 7, old_y: 8 });
1e13775b
SR
517 expect(display.copyImage).to.have.been.calledOnce;
518 expect(display.copyImage).to.have.been.calledWith(7, 8, 3, 4, 5, 6);
519 });
520
521 it('should fill a rect with a given color on type "fill"', function () {
522 display.fillRect = sinon.spy();
1578fa68 523 display._renderQ_push({ type: 'fill', x: 3, y: 4, width: 5, height: 6, color: [7, 8, 9]});
1e13775b
SR
524 expect(display.fillRect).to.have.been.calledOnce;
525 expect(display.fillRect).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9]);
526 });
527
528 it('should draw an image from an image object on type "img" (if complete)', function () {
529 display.drawImage = sinon.spy();
1578fa68 530 display._renderQ_push({ type: 'img', x: 3, y: 4, img: { complete: true } });
1e13775b
SR
531 expect(display.drawImage).to.have.been.calledOnce;
532 expect(display.drawImage).to.have.been.calledWith({ complete: true }, 3, 4);
533 });
534 });
535});