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