Update rive-cpp to 2.0 version
[platform/core/uifw/rive-tizen.git] / submodule / skia / modules / canvaskit / tests / bazel / util.js
1 // The size of the golden images (DMs)
2 const CANVAS_WIDTH = 600;
3 const CANVAS_HEIGHT = 600;
4
5 const _commonGM = (it, pause, name, callback, assetsToFetchOrPromisesToWaitOn) => {
6     const fetchPromises = [];
7     for (const assetOrPromise of assetsToFetchOrPromisesToWaitOn) {
8         // https://stackoverflow.com/a/9436948
9         if (typeof assetOrPromise === 'string' || assetOrPromise instanceof String) {
10             const newPromise = fetchWithRetries(assetOrPromise)
11                 .then((response) => response.arrayBuffer())
12                 .catch((err) => {
13                     console.error(err);
14                     throw err;
15                 });
16             fetchPromises.push(newPromise);
17         } else if (typeof assetOrPromise.then === 'function') {
18             fetchPromises.push(assetOrPromise);
19         } else {
20             throw 'Neither a string nor a promise ' + assetOrPromise;
21         }
22     }
23     it('draws gm '+name, (done) => {
24         const surface = CanvasKit.MakeCanvasSurface('test');
25         expect(surface).toBeTruthy('Could not make surface');
26         if (!surface) {
27             done();
28             return;
29         }
30         // if fetchPromises is empty, the returned promise will
31         // resolve right away and just call the callback.
32         Promise.all(fetchPromises).then((values) => {
33             try {
34                 // If callback returns a promise, the chained .then
35                 // will wait for it.
36                 return callback(surface.getCanvas(), values, surface);
37             } catch (e) {
38                 console.log(`gm ${name} failed with error`, e);
39                 expect(e).toBeFalsy();
40                 debugger;
41                 done();
42             }
43         }).then(() => {
44             surface.flush();
45             if (pause) {
46                 reportSurface(surface, name, null);
47                 console.error('pausing due to pause_gm being invoked');
48             } else {
49                 reportSurface(surface, name, done);
50             }
51         }).catch((e) => {
52             console.log(`could not load assets for gm ${name}`, e);
53             debugger;
54             done();
55         });
56     })
57 };
58
59 const fetchWithRetries = (url) => {
60     const MAX_ATTEMPTS = 3;
61     const DELAY_AFTER_FAILURE = 1000;
62
63     return new Promise((resolve, reject) => {
64         let attempts = 0;
65         const attemptFetch = () => {
66             attempts++;
67             fetch(url).then((resp) => resolve(resp))
68                 .catch((err) => {
69                     if (attempts < MAX_ATTEMPTS) {
70                         console.warn(`got error in fetching ${url}, retrying`, err);
71                         retryAfterDelay();
72                     } else {
73                         console.error(`got error in fetching ${url} even after ${attempts} attempts`, err);
74                         reject(err);
75                     }
76                 });
77         };
78         const retryAfterDelay = () => {
79             setTimeout(() => {
80                 attemptFetch();
81             }, DELAY_AFTER_FAILURE);
82         }
83         attemptFetch();
84     });
85
86 }
87
88 /**
89  * Takes a name, a callback, and any number of assets or promises. It executes the
90  * callback (presumably, the test) and reports the resulting surface to Gold.
91  * @param name {string}
92  * @param callback {Function}, has two params, the first is a CanvasKit.Canvas
93  *    and the second is an array of results from the passed in assets or promises.
94  *    If a given assetOrPromise was a string, the result will be an ArrayBuffer.
95  * @param assetsToFetchOrPromisesToWaitOn {string|Promise}. If a string, it will
96  *    be treated as a url to fetch and return an ArrayBuffer with the contents as
97  *    a result in the callback. Otherwise, the promise will be waited on and its
98  *    result will be whatever the promise resolves to.
99  */
100 const gm = (name, callback, ...assetsToFetchOrPromisesToWaitOn) => {
101     _commonGM(it, false, name, callback, assetsToFetchOrPromisesToWaitOn);
102 };
103
104 /**
105  *  fgm is like gm, except only tests declared with fgm, force_gm, or fit will be
106  *  executed. This mimics the behavior of Jasmine.js.
107  */
108 const fgm = (name, callback, ...assetsToFetchOrPromisesToWaitOn) => {
109     _commonGM(fit, false, name, callback, assetsToFetchOrPromisesToWaitOn);
110 };
111
112 /**
113  *  force_gm is like gm, except only tests declared with fgm, force_gm, or fit will be
114  *  executed. This mimics the behavior of Jasmine.js.
115  */
116 const force_gm = (name, callback, ...assetsToFetchOrPromisesToWaitOn) => {
117     fgm(name, callback, assetsToFetchOrPromisesToWaitOn);
118 };
119
120 /**
121  *  skip_gm does nothing. It is a convenient way to skip a test temporarily.
122  */
123 const skip_gm = (name, callback, ...assetsToFetchOrPromisesToWaitOn) => {
124     console.log(`Skipping gm ${name}`);
125     // do nothing, skip the test for now
126 };
127
128 /**
129  *  pause_gm is like fgm, except the test will not finish right away and clear,
130  *  making it ideal for a human to manually inspect the results.
131  */
132 const pause_gm = (name, callback, ...assetsToFetchOrPromisesToWaitOn) => {
133     _commonGM(fit, true, name, callback, assetsToFetchOrPromisesToWaitOn);
134 };
135
136 const _commonMultipleCanvasGM = (it, pause, name, callback) => {
137     it(`draws gm ${name} on both CanvasKit and using Canvas2D`, (done) => {
138         const skcanvas = CanvasKit.MakeCanvas(CANVAS_WIDTH, CANVAS_HEIGHT);
139         skcanvas._config = 'software_canvas';
140         const realCanvas = document.getElementById('test');
141         realCanvas._config = 'html_canvas';
142         realCanvas.width = CANVAS_WIDTH;
143         realCanvas.height = CANVAS_HEIGHT;
144
145         if (pause) {
146             console.log('debugging canvaskit version');
147             callback(realCanvas);
148             callback(skcanvas);
149             const png = skcanvas.toDataURL();
150             const img = document.createElement('img');
151             document.body.appendChild(img);
152             img.src = png;
153             debugger;
154             return;
155         }
156
157         const promises = [];
158
159         for (const canvas of [skcanvas, realCanvas]) {
160             callback(canvas);
161             // canvas has .toDataURL (even though skcanvas is not a real Canvas)
162             // so this will work.
163             promises.push(reportCanvas(canvas, name, canvas._config));
164         }
165         Promise.all(promises).then(() => {
166             skcanvas.dispose();
167             done();
168         }).catch(reportError(done));
169     });
170 };
171
172 /**
173  * Takes a name and a callback. It executes the callback (presumably, the test)
174  * for both a CanvasKit.Canvas and a native Canvas2D. The result of both will be
175  * uploaded to Gold.
176  * @param name {string}
177  * @param callback {Function}, has one param, either a CanvasKit.Canvas or a native
178  *    Canvas2D object.
179  */
180 const multipleCanvasGM = (name, callback) => {
181     _commonMultipleCanvasGM(it, false, name, callback);
182 };
183
184 /**
185  *  fmultipleCanvasGM is like multipleCanvasGM, except only tests declared with
186  *  fmultipleCanvasGM, force_multipleCanvasGM, or fit will be executed. This
187  *  mimics the behavior of Jasmine.js.
188  */
189 const fmultipleCanvasGM = (name, callback) => {
190     _commonMultipleCanvasGM(fit, false, name, callback);
191 };
192
193 /**
194  *  force_multipleCanvasGM is like multipleCanvasGM, except only tests declared
195  *  with fmultipleCanvasGM, force_multipleCanvasGM, or fit will be executed. This
196  *  mimics the behavior of Jasmine.js.
197  */
198 const force_multipleCanvasGM = (name, callback) => {
199     fmultipleCanvasGM(name, callback);
200 };
201
202 /**
203  *  pause_multipleCanvasGM is like fmultipleCanvasGM, except the test will not
204  *  finish right away and clear, making it ideal for a human to manually inspect the results.
205  */
206 const pause_multipleCanvasGM = (name, callback) => {
207     _commonMultipleCanvasGM(fit, true, name, callback);
208 };
209
210 /**
211  *  skip_multipleCanvasGM does nothing. It is a convenient way to skip a test temporarily.
212  */
213 const skip_multipleCanvasGM = (name, callback) => {
214     console.log(`Skipping multiple canvas gm ${name}`);
215 };
216
217
218 function reportSurface(surface, testname, done) {
219     // Sometimes, the webgl canvas is blank, but the surface has the pixel
220     // data. So, we copy it out and draw it to a normal canvas to take a picture.
221     // To be consistent across CPU and GPU, we just do it for all configurations
222     // (even though the CPU canvas shows up after flush just fine).
223     let pixels = surface.getCanvas().readPixels(0, 0, {
224         width: CANVAS_WIDTH,
225         height: CANVAS_HEIGHT,
226         colorType: CanvasKit.ColorType.RGBA_8888,
227         alphaType: CanvasKit.AlphaType.Unpremul,
228         colorSpace: CanvasKit.ColorSpace.SRGB,
229     });
230     if (!pixels) {
231         throw 'Could not get pixels for test '+testname;
232     }
233     pixels = new Uint8ClampedArray(pixels.buffer);
234     const imageData = new ImageData(pixels, CANVAS_WIDTH, CANVAS_HEIGHT);
235
236     const reportingCanvas = document.getElementById('report');
237     if (!reportingCanvas) {
238         throw 'Reporting canvas not found';
239     }
240     reportingCanvas.getContext('2d').putImageData(imageData, 0, 0);
241     if (!done) {
242         return;
243     }
244     reportCanvas(reportingCanvas, testname).then(() => {
245         surface.delete();
246         done();
247     }).catch(reportError(done));
248 }
249
250
251 function starPath(CanvasKit, X=128, Y=128, R=116) {
252     const p = new CanvasKit.Path();
253     p.moveTo(X + R, Y);
254     for (let i = 1; i < 8; i++) {
255       let a = 2.6927937 * i;
256       p.lineTo(X + R * Math.cos(a), Y + R * Math.sin(a));
257     }
258     p.close();
259     return p;
260 }