]> git.proxmox.com Git - mirror_novnc.git/blob - tests/test.display.js
Cleanup: Display Helper
[mirror_novnc.git] / tests / test.display.js
1 // requires local modules: util, base64, display
2 /* jshint expr: true */
3 var expect = chai.expect;
4
5 chai.use(function (_chai, utils) {
6 _chai.Assertion.addMethod('displayed', function (target_data) {
7 var obj = this._obj;
8 var data_cl = obj._drawCtx.getImageData(0, 0, obj._viewportLoc.w, obj._viewportLoc.h).data;
9 // NB(directxman12): PhantomJS 1.x doesn't implement Uint8ClampedArray, so work around that
10 var data = new Uint8Array(data_cl);
11 this.assert(utils.eql(data, target_data),
12 "expected #{this} to have displayed the image #{exp}, but instead it displayed #{act}",
13 "expected #{this} not to have displayed the image #{act}",
14 target_data,
15 data);
16 });
17 });
18
19 describe('Display/Canvas Helper', function () {
20 var checked_data = [
21 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
22 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
23 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
24 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
25 ];
26 checked_data = new Uint8Array(checked_data);
27
28 var basic_data = [0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0xff, 0xff, 0xff, 255];
29 basic_data = new Uint8Array(basic_data);
30
31 function make_image_canvas (input_data) {
32 var canvas = document.createElement('canvas');
33 canvas.width = 4;
34 canvas.height = 4;
35 var ctx = canvas.getContext('2d');
36 var data = ctx.createImageData(4, 4);
37 for (var i = 0; i < checked_data.length; i++) { data.data[i] = input_data[i]; }
38 ctx.putImageData(data, 0, 0);
39 return canvas;
40 }
41
42 describe('viewport handling', function () {
43 var display;
44 beforeEach(function () {
45 display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true });
46 display.resize(5, 5);
47 display.viewportChange(1, 1, 3, 3);
48 display.getCleanDirtyReset();
49 });
50
51 it('should take viewport location into consideration when drawing images', function () {
52 display.resize(4, 4);
53 display.viewportChange(0, 0, 2, 2);
54 display.drawImage(make_image_canvas(basic_data), 1, 1);
55
56 var expected = new Uint8Array(16);
57 var i;
58 for (i = 0; i < 8; i++) { expected[i] = basic_data[i]; }
59 for (i = 8; i < 16; i++) { expected[i] = 0; }
60 expect(display).to.have.displayed(expected);
61 });
62
63 it('should redraw the left side when shifted left', function () {
64 display.viewportChange(-1, 0, 3, 3);
65 var cdr = display.getCleanDirtyReset();
66 expect(cdr.cleanBox).to.deep.equal({ x: 1, y: 1, w: 2, h: 3 });
67 expect(cdr.dirtyBoxes).to.have.length(1);
68 expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 0, y: 1, w: 2, h: 3 });
69 });
70
71 it('should redraw the right side when shifted right', function () {
72 display.viewportChange(1, 0, 3, 3);
73 var cdr = display.getCleanDirtyReset();
74 expect(cdr.cleanBox).to.deep.equal({ x: 2, y: 1, w: 2, h: 3 });
75 expect(cdr.dirtyBoxes).to.have.length(1);
76 expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 4, y: 1, w: 1, h: 3 });
77 });
78
79 it('should redraw the top part when shifted up', function () {
80 display.viewportChange(0, -1, 3, 3);
81 var cdr = display.getCleanDirtyReset();
82 expect(cdr.cleanBox).to.deep.equal({ x: 1, y: 1, w: 3, h: 2 });
83 expect(cdr.dirtyBoxes).to.have.length(1);
84 expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 1, y: 0, w: 3, h: 1 });
85 });
86
87 it('should redraw the bottom part when shifted down', function () {
88 display.viewportChange(0, 1, 3, 3);
89 var cdr = display.getCleanDirtyReset();
90 expect(cdr.cleanBox).to.deep.equal({ x: 1, y: 2, w: 3, h: 2 });
91 expect(cdr.dirtyBoxes).to.have.length(1);
92 expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 1, y: 4, w: 3, h: 1 });
93 });
94
95 it('should reset the entire viewport to being clean after calculating the clean/dirty boxes', function () {
96 display.viewportChange(0, 1, 3, 3);
97 var cdr1 = display.getCleanDirtyReset();
98 var cdr2 = display.getCleanDirtyReset();
99 expect(cdr1).to.not.deep.equal(cdr2);
100 expect(cdr2.cleanBox).to.deep.equal({ x: 1, y: 2, w: 3, h: 3 });
101 expect(cdr2.dirtyBoxes).to.be.empty;
102 });
103
104 it('should simply mark the whole display area as dirty if not using viewports', function () {
105 display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: false });
106 display.resize(5, 5);
107 var cdr = display.getCleanDirtyReset();
108 expect(cdr.cleanBox).to.deep.equal({ x: 0, y: 0, w: 0, h: 0 });
109 expect(cdr.dirtyBoxes).to.have.length(1);
110 expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 0, y: 0, w: 5, h: 5 });
111 });
112 });
113
114 describe('resizing', function () {
115 var display;
116 beforeEach(function () {
117 display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true });
118 display.resize(4, 3);
119 });
120
121 it('should change the size of the logical canvas', function () {
122 display.resize(5, 7);
123 expect(display._fb_width).to.equal(5);
124 expect(display._fb_height).to.equal(7);
125 });
126
127 it('should update the viewport dimensions', function () {
128 sinon.spy(display, 'viewportChange');
129 display.resize(2, 2);
130 expect(display.viewportChange).to.have.been.calledOnce;
131 });
132 });
133
134 describe('drawing', function () {
135
136 // TODO(directxman12): improve the tests for each of the drawing functions to cover more than just the
137 // basic cases
138 function drawing_tests (pref_js) {
139 var display;
140 beforeEach(function () {
141 display = new Display({ target: document.createElement('canvas'), prefer_js: pref_js });
142 display.resize(4, 4);
143 });
144
145 it('should clear the screen on #clear without a logo set', function () {
146 display.fillRect(0, 0, 4, 4, [0x00, 0x00, 0xff]);
147 display._logo = null;
148 display.clear();
149 display.resize(4, 4);
150 var empty = [];
151 for (var i = 0; i < 4 * display._fb_width * display._fb_height; i++) { empty[i] = 0; }
152 expect(display).to.have.displayed(new Uint8Array(empty));
153 });
154
155 it('should draw the logo on #clear with a logo set', function (done) {
156 display._logo = { width: 4, height: 4, data: make_image_canvas(checked_data).toDataURL() };
157 display._drawCtx._act_drawImg = display._drawCtx.drawImage;
158 display._drawCtx.drawImage = function (img, x, y) {
159 this._act_drawImg(img, x, y);
160 expect(display).to.have.displayed(checked_data);
161 done();
162 };
163 display.clear();
164 expect(display._fb_width).to.equal(4);
165 expect(display._fb_height).to.equal(4);
166 });
167
168 it('should support filling a rectangle with particular color via #fillRect', function () {
169 display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
170 display.fillRect(0, 0, 2, 2, [0xff, 0, 0]);
171 display.fillRect(2, 2, 2, 2, [0xff, 0, 0]);
172 expect(display).to.have.displayed(checked_data);
173 });
174
175 it('should support copying an portion of the canvas via #copyImage', function () {
176 display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
177 display.fillRect(0, 0, 2, 2, [0xff, 0, 0x00]);
178 display.copyImage(0, 0, 2, 2, 2, 2);
179 expect(display).to.have.displayed(checked_data);
180 });
181
182 it('should support drawing tile data with a background color and sub tiles', function () {
183 display.startTile(0, 0, 4, 4, [0, 0xff, 0]);
184 display.subTile(0, 0, 2, 2, [0xff, 0, 0]);
185 display.subTile(2, 2, 2, 2, [0xff, 0, 0]);
186 display.finishTile();
187 expect(display).to.have.displayed(checked_data);
188 });
189
190 it('should support drawing BGRX blit images with true color via #blitImage', function () {
191 var data = [];
192 for (var i = 0; i < 16; i++) {
193 data[i * 4] = checked_data[i * 4 + 2];
194 data[i * 4 + 1] = checked_data[i * 4 + 1];
195 data[i * 4 + 2] = checked_data[i * 4];
196 data[i * 4 + 3] = checked_data[i * 4 + 3];
197 }
198 display.blitImage(0, 0, 4, 4, data, 0);
199 expect(display).to.have.displayed(checked_data);
200 });
201
202 it('should support drawing RGB blit images with true color via #blitRgbImage', function () {
203 var data = [];
204 for (var i = 0; i < 16; i++) {
205 data[i * 3] = checked_data[i * 4];
206 data[i * 3 + 1] = checked_data[i * 4 + 1];
207 data[i * 3 + 2] = checked_data[i * 4 + 2];
208 }
209 display.blitRgbImage(0, 0, 4, 4, data, 0);
210 expect(display).to.have.displayed(checked_data);
211 });
212
213 it('should support drawing blit images from a data URL via #blitStringImage', function (done) {
214 var img_url = make_image_canvas(checked_data).toDataURL();
215 display._drawCtx._act_drawImg = display._drawCtx.drawImage;
216 display._drawCtx.drawImage = function (img, x, y) {
217 this._act_drawImg(img, x, y);
218 expect(display).to.have.displayed(checked_data);
219 done();
220 };
221 display.blitStringImage(img_url, 0, 0);
222 });
223
224 it('should support drawing solid colors with color maps', function () {
225 display._true_color = false;
226 display.set_colourMap({ 0: [0xff, 0, 0], 1: [0, 0xff, 0] });
227 display.fillRect(0, 0, 4, 4, [1]);
228 display.fillRect(0, 0, 2, 2, [0]);
229 display.fillRect(2, 2, 2, 2, [0]);
230 expect(display).to.have.displayed(checked_data);
231 });
232
233 it('should support drawing blit images with color maps', function () {
234 display._true_color = false;
235 display.set_colourMap({ 1: [0xff, 0, 0], 0: [0, 0xff, 0] });
236 var data = [1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1].map(function (elem) { return [elem]; });
237 display.blitImage(0, 0, 4, 4, data, 0);
238 expect(display).to.have.displayed(checked_data);
239 });
240
241 it('should support drawing an image object via #drawImage', function () {
242 var img = make_image_canvas(checked_data);
243 display.drawImage(img, 0, 0);
244 expect(display).to.have.displayed(checked_data);
245 });
246 }
247
248 describe('(prefering native methods)', function () { drawing_tests.call(this, false); });
249 describe('(prefering JavaScript)', function () { drawing_tests.call(this, true); });
250 });
251
252 describe('the render queue processor', function () {
253 var display;
254 beforeEach(function () {
255 display = new Display({ target: document.createElement('canvas'), prefer_js: false });
256 display.resize(4, 4);
257 sinon.spy(display, '_scan_renderQ');
258 this.old_requestAnimFrame = window.requestAnimFrame;
259 window.requestAnimFrame = function (cb) {
260 this.next_frame_cb = cb;
261 }.bind(this);
262 this.next_frame = function () { this.next_frame_cb(); };
263 });
264
265 afterEach(function () {
266 window.requestAnimFrame = this.old_requestAnimFrame;
267 });
268
269 it('should try to process an item when it is pushed on, if nothing else is on the queue', function () {
270 display.renderQ_push({ type: 'noop' }); // does nothing
271 expect(display._scan_renderQ).to.have.been.calledOnce;
272 });
273
274 it('should not try to process an item when it is pushed on if we are waiting for other items', function () {
275 display._renderQ.length = 2;
276 display.renderQ_push({ type: 'noop' });
277 expect(display._scan_renderQ).to.not.have.been.called;
278 });
279
280 it('should wait until an image is loaded to attempt to draw it and the rest of the queue', function () {
281 var img = { complete: false };
282 display._renderQ = [{ type: 'img', x: 3, y: 4, img: img },
283 { type: 'fill', x: 1, y: 2, width: 3, height: 4, color: 5 }];
284 display.drawImage = sinon.spy();
285 display.fillRect = sinon.spy();
286
287 display._scan_renderQ();
288 expect(display.drawImage).to.not.have.been.called;
289 expect(display.fillRect).to.not.have.been.called;
290
291 display._renderQ[0].img.complete = true;
292 this.next_frame();
293 expect(display.drawImage).to.have.been.calledOnce;
294 expect(display.fillRect).to.have.been.calledOnce;
295 });
296
297 it('should draw a blit image on type "blit"', function () {
298 display.blitImage = sinon.spy();
299 display.renderQ_push({ type: 'blit', x: 3, y: 4, width: 5, height: 6, data: [7, 8, 9] });
300 expect(display.blitImage).to.have.been.calledOnce;
301 expect(display.blitImage).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9], 0);
302 });
303
304 it('should draw a blit RGB image on type "blitRgb"', function () {
305 display.blitRgbImage = sinon.spy();
306 display.renderQ_push({ type: 'blitRgb', x: 3, y: 4, width: 5, height: 6, data: [7, 8, 9] });
307 expect(display.blitRgbImage).to.have.been.calledOnce;
308 expect(display.blitRgbImage).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9], 0);
309 });
310
311 it('should copy a region on type "copy"', function () {
312 display.copyImage = sinon.spy();
313 display.renderQ_push({ type: 'copy', x: 3, y: 4, width: 5, height: 6, old_x: 7, old_y: 8 });
314 expect(display.copyImage).to.have.been.calledOnce;
315 expect(display.copyImage).to.have.been.calledWith(7, 8, 3, 4, 5, 6);
316 });
317
318 it('should fill a rect with a given color on type "fill"', function () {
319 display.fillRect = sinon.spy();
320 display.renderQ_push({ type: 'fill', x: 3, y: 4, width: 5, height: 6, color: [7, 8, 9]});
321 expect(display.fillRect).to.have.been.calledOnce;
322 expect(display.fillRect).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9]);
323 });
324
325 it('should draw an image from an image object on type "img" (if complete)', function () {
326 display.drawImage = sinon.spy();
327 display.renderQ_push({ type: 'img', x: 3, y: 4, img: { complete: true } });
328 expect(display.drawImage).to.have.been.calledOnce;
329 expect(display.drawImage).to.have.been.calledWith({ complete: true }, 3, 4);
330 });
331 });
332 });