]>
git.proxmox.com Git - mirror_novnc.git/blob - tests/test.display.js
1 const expect
= chai
.expect
;
3 import Base64
from '../core/base64.js';
4 import Display
from '../core/display.js';
6 describe('Display/Canvas Helper', function () {
7 const checked_data
= new Uint8Array([
8 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
9 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
10 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
11 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
14 const basic_data
= new Uint8Array([0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0xff, 0xff, 0xff, 255]);
16 function make_image_canvas(input_data
) {
17 const canvas
= document
.createElement('canvas');
20 const ctx
= canvas
.getContext('2d');
21 const data
= ctx
.createImageData(4, 4);
22 for (let i
= 0; i
< checked_data
.length
; i
++) { data
.data
[i
] = input_data
[i
]; }
23 ctx
.putImageData(data
, 0, 0);
27 function make_image_png(input_data
) {
28 const canvas
= make_image_canvas(input_data
);
29 const url
= canvas
.toDataURL();
30 const data
= url
.split(",")[1];
31 return Base64
.decode(data
);
34 describe('viewport handling', function () {
36 beforeEach(function () {
37 display
= new Display(document
.createElement('canvas'));
38 display
.clipViewport
= true;
40 display
.viewportChangeSize(3, 3);
41 display
.viewportChangePos(1, 1);
44 it('should take viewport location into consideration when drawing images', function () {
46 display
.viewportChangeSize(2, 2);
47 display
.drawImage(make_image_canvas(basic_data
), 1, 1);
50 const expected
= new Uint8Array(16);
51 for (let i
= 0; i
< 8; i
++) { expected
[i
] = basic_data
[i
]; }
52 for (let i
= 8; i
< 16; i
++) { expected
[i
] = 0; }
53 expect(display
).to
.have
.displayed(expected
);
56 it('should resize the target canvas when resizing the viewport', function () {
57 display
.viewportChangeSize(2, 2);
58 expect(display
._target
.width
).to
.equal(2);
59 expect(display
._target
.height
).to
.equal(2);
62 it('should move the viewport if necessary', function () {
63 display
.viewportChangeSize(5, 5);
64 expect(display
.absX(0)).to
.equal(0);
65 expect(display
.absY(0)).to
.equal(0);
66 expect(display
._target
.width
).to
.equal(5);
67 expect(display
._target
.height
).to
.equal(5);
70 it('should limit the viewport to the framebuffer size', function () {
71 display
.viewportChangeSize(6, 6);
72 expect(display
._target
.width
).to
.equal(5);
73 expect(display
._target
.height
).to
.equal(5);
76 it('should redraw when moving the viewport', function () {
77 display
.flip
= sinon
.spy();
78 display
.viewportChangePos(-1, 1);
79 expect(display
.flip
).to
.have
.been
.calledOnce
;
82 it('should redraw when resizing the viewport', function () {
83 display
.flip
= sinon
.spy();
84 display
.viewportChangeSize(2, 2);
85 expect(display
.flip
).to
.have
.been
.calledOnce
;
88 it('should show the entire framebuffer when disabling the viewport', function () {
89 display
.clipViewport
= false;
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);
96 it('should ignore viewport changes when the viewport is disabled', function () {
97 display
.clipViewport
= false;
98 display
.viewportChangeSize(2, 2);
99 display
.viewportChangePos(1, 1);
100 expect(display
.absX(0)).to
.equal(0);
101 expect(display
.absY(0)).to
.equal(0);
102 expect(display
._target
.width
).to
.equal(5);
103 expect(display
._target
.height
).to
.equal(5);
106 it('should show the entire framebuffer just after enabling the viewport', function () {
107 display
.clipViewport
= false;
108 display
.clipViewport
= true;
109 expect(display
.absX(0)).to
.equal(0);
110 expect(display
.absY(0)).to
.equal(0);
111 expect(display
._target
.width
).to
.equal(5);
112 expect(display
._target
.height
).to
.equal(5);
116 describe('resizing', function () {
118 beforeEach(function () {
119 display
= new Display(document
.createElement('canvas'));
120 display
.clipViewport
= false;
121 display
.resize(4, 4);
124 it('should change the size of the logical canvas', function () {
125 display
.resize(5, 7);
126 expect(display
._fb_width
).to
.equal(5);
127 expect(display
._fb_height
).to
.equal(7);
130 it('should keep the framebuffer data', function () {
131 display
.fillRect(0, 0, 4, 4, [0, 0, 0xff]);
132 display
.resize(2, 2);
135 for (let i
= 0; i
< 4 * 2*2; i
+= 4) {
137 expected
[i
+1] = expected
[i
+2] = 0;
138 expected
[i
+3] = 0xff;
140 expect(display
).to
.have
.displayed(new Uint8Array(expected
));
143 describe('viewport', function () {
144 beforeEach(function () {
145 display
.clipViewport
= true;
146 display
.viewportChangeSize(3, 3);
147 display
.viewportChangePos(1, 1);
150 it('should keep the viewport position and size if possible', function () {
151 display
.resize(6, 6);
152 expect(display
.absX(0)).to
.equal(1);
153 expect(display
.absY(0)).to
.equal(1);
154 expect(display
._target
.width
).to
.equal(3);
155 expect(display
._target
.height
).to
.equal(3);
158 it('should move the viewport if necessary', function () {
159 display
.resize(3, 3);
160 expect(display
.absX(0)).to
.equal(0);
161 expect(display
.absY(0)).to
.equal(0);
162 expect(display
._target
.width
).to
.equal(3);
163 expect(display
._target
.height
).to
.equal(3);
166 it('should shrink the viewport if necessary', function () {
167 display
.resize(2, 2);
168 expect(display
.absX(0)).to
.equal(0);
169 expect(display
.absY(0)).to
.equal(0);
170 expect(display
._target
.width
).to
.equal(2);
171 expect(display
._target
.height
).to
.equal(2);
176 describe('rescaling', function () {
180 beforeEach(function () {
181 canvas
= document
.createElement('canvas');
182 display
= new Display(canvas
);
183 display
.clipViewport
= true;
184 display
.resize(4, 4);
185 display
.viewportChangeSize(3, 3);
186 display
.viewportChangePos(1, 1);
187 document
.body
.appendChild(canvas
);
190 afterEach(function () {
191 document
.body
.removeChild(canvas
);
194 it('should not change the bitmap size of the canvas', function () {
196 expect(canvas
.width
).to
.equal(3);
197 expect(canvas
.height
).to
.equal(3);
200 it('should change the effective rendered size of the canvas', function () {
202 expect(canvas
.clientWidth
).to
.equal(6);
203 expect(canvas
.clientHeight
).to
.equal(6);
206 it('should not change when resizing', function () {
208 display
.resize(5, 5);
209 expect(display
.scale
).to
.equal(2.0);
210 expect(canvas
.width
).to
.equal(3);
211 expect(canvas
.height
).to
.equal(3);
212 expect(canvas
.clientWidth
).to
.equal(6);
213 expect(canvas
.clientHeight
).to
.equal(6);
217 describe('autoscaling', function () {
221 beforeEach(function () {
222 canvas
= document
.createElement('canvas');
223 display
= new Display(canvas
);
224 display
.clipViewport
= true;
225 display
.resize(4, 3);
226 display
.viewportChangeSize(4, 3);
227 document
.body
.appendChild(canvas
);
230 afterEach(function () {
231 document
.body
.removeChild(canvas
);
234 it('should preserve aspect ratio while autoscaling', function () {
235 display
.autoscale(16, 9);
236 expect(canvas
.clientWidth
/ canvas
.clientHeight
).to
.equal(4 / 3);
239 it('should use width to determine scale when the current aspect ratio is wider than the target', function () {
240 display
.autoscale(9, 16);
241 expect(display
.absX(9)).to
.equal(4);
242 expect(display
.absY(18)).to
.equal(8);
243 expect(canvas
.clientWidth
).to
.equal(9);
244 expect(canvas
.clientHeight
).to
.equal(7); // round 9 / (4 / 3)
247 it('should use height to determine scale when the current aspect ratio is taller than the target', function () {
248 display
.autoscale(16, 9);
249 expect(display
.absX(9)).to
.equal(3);
250 expect(display
.absY(18)).to
.equal(6);
251 expect(canvas
.clientWidth
).to
.equal(12); // 16 * (4 / 3)
252 expect(canvas
.clientHeight
).to
.equal(9);
256 it('should not change the bitmap size of the canvas', function () {
257 display
.autoscale(16, 9);
258 expect(canvas
.width
).to
.equal(4);
259 expect(canvas
.height
).to
.equal(3);
263 describe('drawing', function () {
265 // TODO(directxman12): improve the tests for each of the drawing functions to cover more than just the
268 beforeEach(function () {
269 display
= new Display(document
.createElement('canvas'));
270 display
.resize(4, 4);
273 it('should not draw directly on the target canvas', function () {
274 display
.fillRect(0, 0, 4, 4, [0, 0, 0xff]);
276 display
.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
278 for (let i
= 0; i
< 4 * display
._fb_width
* display
._fb_height
; i
+= 4) {
280 expected
[i
+1] = expected
[i
+2] = 0;
281 expected
[i
+3] = 0xff;
283 expect(display
).to
.have
.displayed(new Uint8Array(expected
));
286 it('should support filling a rectangle with particular color via #fillRect', function () {
287 display
.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
288 display
.fillRect(0, 0, 2, 2, [0xff, 0, 0]);
289 display
.fillRect(2, 2, 2, 2, [0xff, 0, 0]);
291 expect(display
).to
.have
.displayed(checked_data
);
294 it('should support copying an portion of the canvas via #copyImage', function () {
295 display
.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
296 display
.fillRect(0, 0, 2, 2, [0xff, 0, 0x00]);
297 display
.copyImage(0, 0, 2, 2, 2, 2);
299 expect(display
).to
.have
.displayed(checked_data
);
302 it('should support drawing images via #imageRect', function (done
) {
303 display
.imageRect(0, 0, 4, 4, "image/png", make_image_png(checked_data
));
305 display
.onflush
= () => {
306 expect(display
).to
.have
.displayed(checked_data
);
312 it('should support drawing tile data with a background color and sub tiles', function () {
313 display
.startTile(0, 0, 4, 4, [0, 0xff, 0]);
314 display
.subTile(0, 0, 2, 2, [0xff, 0, 0]);
315 display
.subTile(2, 2, 2, 2, [0xff, 0, 0]);
316 display
.finishTile();
318 expect(display
).to
.have
.displayed(checked_data
);
321 // We have a special cache for 16x16 tiles that we need to test
322 it('should support drawing a 16x16 tile', function () {
323 const large_checked_data
= new Uint8Array(16*16*4);
324 display
.resize(16, 16);
326 for (let y
= 0;y
< 16;y
++) {
327 for (let x
= 0;x
< 16;x
++) {
329 if ((x
< 4) && (y
< 4)) {
330 // NB: of course IE11 doesn't support #slice on ArrayBufferViews...
331 pixel
= Array
.prototype.slice
.call(checked_data
, (y
*4+x
)*4, (y
*4+x
+1)*4);
333 pixel
= [0, 0xff, 0, 255];
335 large_checked_data
.set(pixel
, (y
*16+x
)*4);
339 display
.startTile(0, 0, 16, 16, [0, 0xff, 0]);
340 display
.subTile(0, 0, 2, 2, [0xff, 0, 0]);
341 display
.subTile(2, 2, 2, 2, [0xff, 0, 0]);
342 display
.finishTile();
344 expect(display
).to
.have
.displayed(large_checked_data
);
347 it('should support drawing BGRX blit images with true color via #blitImage', function () {
349 for (let i
= 0; i
< 16; i
++) {
350 data
[i
* 4] = checked_data
[i
* 4 + 2];
351 data
[i
* 4 + 1] = checked_data
[i
* 4 + 1];
352 data
[i
* 4 + 2] = checked_data
[i
* 4];
353 data
[i
* 4 + 3] = checked_data
[i
* 4 + 3];
355 display
.blitImage(0, 0, 4, 4, data
, 0);
357 expect(display
).to
.have
.displayed(checked_data
);
360 it('should support drawing RGB blit images with true color via #blitRgbImage', function () {
362 for (let i
= 0; i
< 16; i
++) {
363 data
[i
* 3] = checked_data
[i
* 4];
364 data
[i
* 3 + 1] = checked_data
[i
* 4 + 1];
365 data
[i
* 3 + 2] = checked_data
[i
* 4 + 2];
367 display
.blitRgbImage(0, 0, 4, 4, data
, 0);
369 expect(display
).to
.have
.displayed(checked_data
);
372 it('should support drawing an image object via #drawImage', function () {
373 const img
= make_image_canvas(checked_data
);
374 display
.drawImage(img
, 0, 0);
376 expect(display
).to
.have
.displayed(checked_data
);
380 describe('the render queue processor', function () {
382 beforeEach(function () {
383 display
= new Display(document
.createElement('canvas'));
384 display
.resize(4, 4);
385 sinon
.spy(display
, '_scan_renderQ');
388 it('should try to process an item when it is pushed on, if nothing else is on the queue', function () {
389 display
._renderQ_push({ type
: 'noop' }); // does nothing
390 expect(display
._scan_renderQ
).to
.have
.been
.calledOnce
;
393 it('should not try to process an item when it is pushed on if we are waiting for other items', function () {
394 display
._renderQ
.length
= 2;
395 display
._renderQ_push({ type
: 'noop' });
396 expect(display
._scan_renderQ
).to
.not
.have
.been
.called
;
399 it('should wait until an image is loaded to attempt to draw it and the rest of the queue', function () {
400 const img
= { complete
: false, width
: 4, height
: 4, addEventListener
: sinon
.spy() };
401 display
._renderQ
= [{ type
: 'img', x
: 3, y
: 4, width
: 4, height
: 4, img
: img
},
402 { type
: 'fill', x
: 1, y
: 2, width
: 3, height
: 4, color
: 5 }];
403 display
.drawImage
= sinon
.spy();
404 display
.fillRect
= sinon
.spy();
406 display
._scan_renderQ();
407 expect(display
.drawImage
).to
.not
.have
.been
.called
;
408 expect(display
.fillRect
).to
.not
.have
.been
.called
;
409 expect(img
.addEventListener
).to
.have
.been
.calledOnce
;
411 display
._renderQ
[0].img
.complete
= true;
412 display
._scan_renderQ();
413 expect(display
.drawImage
).to
.have
.been
.calledOnce
;
414 expect(display
.fillRect
).to
.have
.been
.calledOnce
;
415 expect(img
.addEventListener
).to
.have
.been
.calledOnce
;
418 it('should wait if an image is incorrectly loaded', function () {
419 const img
= { complete
: true, width
: 0, height
: 0, addEventListener
: sinon
.spy() };
420 display
._renderQ
= [{ type
: 'img', x
: 3, y
: 4, width
: 4, height
: 4, img
: img
},
421 { type
: 'fill', x
: 1, y
: 2, width
: 3, height
: 4, color
: 5 }];
422 display
.drawImage
= sinon
.spy();
423 display
.fillRect
= sinon
.spy();
425 display
._scan_renderQ();
426 expect(display
.drawImage
).to
.not
.have
.been
.called
;
427 expect(display
.fillRect
).to
.not
.have
.been
.called
;
428 expect(img
.addEventListener
).to
.have
.been
.calledOnce
;
430 display
._renderQ
[0].img
.complete
= true;
431 display
._renderQ
[0].img
.width
= 4;
432 display
._renderQ
[0].img
.height
= 4;
433 display
._scan_renderQ();
434 expect(display
.drawImage
).to
.have
.been
.calledOnce
;
435 expect(display
.fillRect
).to
.have
.been
.calledOnce
;
436 expect(img
.addEventListener
).to
.have
.been
.calledOnce
;
439 it('should call callback when queue is flushed', function () {
440 display
.onflush
= sinon
.spy();
441 display
.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
442 expect(display
.onflush
).to
.not
.have
.been
.called
;
444 expect(display
.onflush
).to
.have
.been
.calledOnce
;
447 it('should draw a blit image on type "blit"', function () {
448 display
.blitImage
= sinon
.spy();
449 display
._renderQ_push({ type
: 'blit', x
: 3, y
: 4, width
: 5, height
: 6, data
: [7, 8, 9] });
450 expect(display
.blitImage
).to
.have
.been
.calledOnce
;
451 expect(display
.blitImage
).to
.have
.been
.calledWith(3, 4, 5, 6, [7, 8, 9], 0);
454 it('should draw a blit RGB image on type "blitRgb"', function () {
455 display
.blitRgbImage
= sinon
.spy();
456 display
._renderQ_push({ type
: 'blitRgb', x
: 3, y
: 4, width
: 5, height
: 6, data
: [7, 8, 9] });
457 expect(display
.blitRgbImage
).to
.have
.been
.calledOnce
;
458 expect(display
.blitRgbImage
).to
.have
.been
.calledWith(3, 4, 5, 6, [7, 8, 9], 0);
461 it('should copy a region on type "copy"', function () {
462 display
.copyImage
= sinon
.spy();
463 display
._renderQ_push({ type
: 'copy', x
: 3, y
: 4, width
: 5, height
: 6, old_x
: 7, old_y
: 8 });
464 expect(display
.copyImage
).to
.have
.been
.calledOnce
;
465 expect(display
.copyImage
).to
.have
.been
.calledWith(7, 8, 3, 4, 5, 6);
468 it('should fill a rect with a given color on type "fill"', function () {
469 display
.fillRect
= sinon
.spy();
470 display
._renderQ_push({ type
: 'fill', x
: 3, y
: 4, width
: 5, height
: 6, color
: [7, 8, 9]});
471 expect(display
.fillRect
).to
.have
.been
.calledOnce
;
472 expect(display
.fillRect
).to
.have
.been
.calledWith(3, 4, 5, 6, [7, 8, 9]);
475 it('should draw an image from an image object on type "img" (if complete)', function () {
476 display
.drawImage
= sinon
.spy();
477 display
._renderQ_push({ type
: 'img', x
: 3, y
: 4, img
: { complete
: true } });
478 expect(display
.drawImage
).to
.have
.been
.calledOnce
;
479 expect(display
.drawImage
).to
.have
.been
.calledWith({ complete
: true }, 3, 4);