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