https://bugs.webkit.org/show_bug.cgi?id=73503
[WebKit-https.git] / LayoutTests / fast / canvas / webgl / script-tests / arraybuffer-transfer-of-control.js
1 window.jsTestIsAsync = true;
2
3 description('Test transfer of control semantics for ArrayBuffers.');
4 window.testsComplete = 0;
5
6 var arraySize = 40;
7 var arrayOffset = 8;
8 var arrayEffectiveSize = arraySize - arrayOffset;
9
10 var basicBufferTypes =
11 [
12     ["Int32", Int32Array, 4],
13     ["Uint32", Uint32Array, 4],
14     ["Int8", Int8Array, 1],
15     ["Uint8", Uint8Array, 1],
16     ["Int16", Int16Array, 2],
17     ["Uint16", Uint16Array, 2],
18     ["Float32", Float32Array, 4],
19     ["Float64", Float64Array, 8]
20 ];
21
22 var allBufferTypes =
23 [
24     ["Int32", Int32Array, 4],
25     ["Uint32", Uint32Array, 4],
26     ["Int8", Int8Array, 1],
27     ["Uint8", Uint8Array, 1],
28     ["Int16", Int16Array, 2],
29     ["Uint16", Uint16Array, 2],
30     ["Float32", Float32Array, 4],
31     ["Float64", Float64Array, 8],
32     ["DataView", DataView, 1]
33 ];
34
35 function isTypedArray(view)
36 {
37     for (var i = 0; i < basicBufferTypes.length; ++i) {
38         var bufferType = basicBufferTypes[i];
39         if (view instanceof bufferType[1]) {
40             return true;
41         }
42     }
43     return false;
44 }
45
46 function isDataView(view)
47 {
48     return (view instanceof DataView);
49 }
50
51 function isArrayBuffer(buffer)
52 {
53     return (buffer instanceof ArrayBuffer);
54 }
55
56 function assertBufferClosed(testName, buffer)
57 {
58     if (buffer === null) {
59         return true;
60     }
61     if (!isArrayBuffer(buffer)) {
62         testFailed(testName + ": not an array buffer (" + buffer + ")");
63         return false;
64     }
65     if (buffer.byteLength !== 0 || !(buffer.byteLength === 0)) {
66         testFailed(testName + ": ArrayBuffer byteLength !== 0");
67         return false;
68     }
69     return true;
70 }
71
72 function assertViewClosed(testName, view)
73 {
74     if (isTypedArray(view) || isDataView(view)) {
75         if (view.buffer !== null && !assertBufferClosed(testName, view.buffer))
76             return false;
77         if (view.byteOffset !== 0 || !(view.byteOffset === 0)) {
78             testFailed(testName + ": view byteOffset !== 0");
79             return false;
80         }
81         if (view.byteLength !== 0 || !(view.byteLength === 0)) {
82             testFailed(testName + ": view byteLength !== 0");
83             return false;
84         }
85         if (!isDataView(view)) {
86             if (view.length !== 0 || !(view.length === 0)) {
87                 testFailed(testName + ": TypedArray length !== 0");
88                 return false;
89             }
90             try {
91                 var v = view[0];
92                 if (v !== undefined) {
93                     testFailed(testName + ": index get on a closed view did not return undefined.");
94                     return false;
95                 }
96             } catch(xn) {
97                 testFailed(testName + ": index get on a closed view threw an exception: " + xn);
98                 return false;
99             }
100             try {
101                 view[0] = 42;
102                 var v = view[0];
103                 if (v !== undefined) {
104                     testFailed(testName + ": index set then get on a closed view did not return undefined.");
105                     return false;
106                 }
107             } catch(xn) {
108                 testFailed(testName + ": index set then get on a closed view threw an exception: " + xn);
109                 return false;
110             }
111             try {
112                 view.get(0);
113                 testFailed(testName + ": get on a closed view succeeded");
114                 return false;
115             } catch (xn) { }
116             try {
117                 view.set(0, 1);
118                 testFailed(testName + ": set on a closed view succeeded");
119                 return false;
120             } catch (xn) { }
121         } else {
122             try {
123                 view.getInt8(0);
124                 testFailed(testName + ": get on a closed view succeeded");
125                 return false;
126             } catch (xn) { }
127             try {
128                 view.setInt8(0, 1);
129                 testFailed(testName + ": set on a closed view succeeded");
130                 return false;
131             } catch (xn) { }
132         }
133     } else {
134         testFailed(testName + " not a view (" + view + ")");
135         return false;
136     }
137     return true;
138 }
139
140 function createBuffer(length)
141 {
142     var buffer = new ArrayBuffer(length);
143     var view = new Uint8Array(buffer);
144     for (var i = 0; i < length; ++i) {
145         view[i] = i + 1;
146     }
147     return buffer;
148 }
149
150 function checkBuffer(testName, buffer, length)
151 {
152     if (!isArrayBuffer(buffer)) {
153         testFailed(testName + ": buffer is not an ArrayBuffer");
154         return false;
155     }
156     if (buffer.byteLength !== length) {
157         testFailed(testName + ": buffer is the wrong length");
158         return false;
159     }
160     var view = new Uint8Array(buffer);
161     for (var i = 0; i < length; ++i) {
162         if (view[i] !== i + 1) {
163             testFailed(testName + ": buffer contains the wrong data");
164             return false;
165         }
166     }
167     return true;
168 }
169
170 function createView(viewType, bytesPerElement)
171 {
172     if (viewType === DataView) {
173         var view = new Uint8Array(arraySize);
174         for (var i = arrayOffset; i < arraySize; ++i) {
175             view[i] = i - arrayOffset + 1;
176         }
177         return new DataView(view.buffer, arrayOffset, arrayEffectiveSize);
178     } else {
179         var view = new viewType(new ArrayBuffer(arraySize), arrayOffset, arrayEffectiveSize / bytesPerElement);
180         for (var i = 0; i < arrayEffectiveSize / bytesPerElement; ++i) {
181             view[i] = i + 1;
182         }
183         return view;
184     }
185 }
186
187 function createEveryView(buffer)
188 {
189     return allBufferTypes.map(function (bufferType) {
190         return new bufferType[1](buffer, arrayOffset, arrayEffectiveSize / bufferType[2]);
191     });
192 }
193
194 function checkView(testName, typedArrayType, view)
195 {
196     if (!(view instanceof typedArrayType)) {
197         testFailed(testName + ": " + view + " not an instance of " + typedArrayType);
198         return false;
199     }
200     if (view.buffer.byteLength !== arraySize ||
201         (!(view instanceof DataView) && view.length !== arrayEffectiveSize / view.BYTES_PER_ELEMENT)) {
202         testFailed(testName + ": view has the wrong length (" + view.length + ")");
203         return false;
204     }
205     if (view.byteOffset !== arrayOffset) {
206         testFailed(testName + ": view has wrong byte offset");
207     }
208     var max = arrayEffectiveSize;
209     if (!(view instanceof DataView)) {
210         max = max / view.BYTES_PER_ELEMENT;
211     }
212     for (var i = 0; i < max; ++i) {
213         if (view instanceof DataView) {
214             if (view.getInt8(i) !== i + 1) {
215                 testFailed(testName + ": view contains the wrong data");
216                 return false;
217             }
218         } else {
219             if (view[i] !== i + 1) {
220                 testFailed(testName + ": view contains the wrong data");
221                 return false;
222             }
223         }
224     }
225     return true;
226 }
227
228 function checkEmptyArray(testName, array)
229 {
230     if (array === null || array === undefined) {
231         testFailed(testName + ": port list is null or undefined");
232         return false;
233     }
234     if (array.length !== 0) {
235         testFailed(testName + ": port list is not zero-length");
236         return false;
237     }
238     return true;
239 }
240
241 function wrapSend(testName, message, xfer)
242 {
243     try {
244         window.webkitPostMessage(message, xfer, '*');
245     } catch (e) {
246         testFailed(testName + ": could not webkitPostMessage: " + e);
247         doneTest();
248         return false;
249     }
250     return true;
251 }
252
253 function wrapFailSend(testName, message, xfer)
254 {
255     try {
256         window.webkitPostMessage(message, xfer, '*');
257     } catch (e) {
258         return true;
259     }
260     testFailed(testName + ": expected webkitPostMessage to fail but it didn't.");
261     return false;
262 }
263
264 var testList = [{
265     name: "sanity check",
266     send: function (name) { wrapSend(name, [], []); },
267     test: function (name, e) { return true; }
268 }, {
269     name: "raw ArrayBuffer",
270     send: function (name) {
271         var buffer = createBuffer(3);
272         wrapSend(name, buffer, [buffer]);
273         assertBufferClosed(name, buffer);
274         wrapFailSend(name, buffer, [buffer]);
275         wrapFailSend(name, buffer, []);
276     },
277     test: function (name, e) { return checkBuffer(name, e.data, 3) && checkEmptyArray(name, e.ports); }
278 }, {
279     name: "sending buffers is sane even if cloning doesn't special-case",
280     send: function(name) {
281         var view = createView(Int32Array, 4);
282         var buffer = view.buffer;
283         wrapSend(name, [view, buffer], [buffer]);
284         assertBufferClosed(name, buffer);
285         assertViewClosed(name, view);
286     },
287     test: function (name, e) {
288         if (e.data[0].buffer !== e.data[1]) {
289             testFailed("View and buffer were not linked.");
290             return false;
291         }
292         return true;
293     }
294 }, {
295     name: "send every view",
296     send: function(name) {
297         var buffer = createBuffer(arraySize);
298         var views = createEveryView(buffer);
299         wrapSend(name, views, [buffer]);
300         assertBufferClosed(name, buffer);
301         wrapFailSend(name, views, [buffer]);
302         wrapFailSend(name, views, []);
303     },
304     test: function (name, e) {
305         if (e.data.length !== allBufferTypes.length) {
306             testFailed(name + ": not every view was sent.");
307         }
308         for (var v = 0; v < e.data.length; ++v) {
309             var view = e.data[v];
310             if (view.buffer !== e.data[0].buffer) {
311                 testFailed(name + ": not every view pointed to the correct buffer.");
312                 return false;
313             }
314         }
315         return true;
316     }
317 }, {
318     name: "transfer list multiple",
319     send: function(name) {
320         var buffer = createBuffer(arraySize);
321         wrapSend(name, { buffer : buffer }, [buffer,buffer]);
322         assertBufferClosed(name, buffer);
323         wrapFailSend(name, [buffer], [buffer]);
324         wrapFailSend(name, [], [buffer]);
325         var buffer2 = createBuffer(arraySize);
326         wrapFailSend(name, [], [buffer2, buffer]);
327         checkBuffer(name, buffer2, arraySize);
328         wrapFailSend(name, [], [buffer, buffer2]);
329         checkBuffer(name, buffer2, arraySize);
330         wrapFailSend(name, [buffer2], [buffer2, buffer]);
331         checkBuffer(name, buffer2, arraySize);
332     },
333     test: function (name, e) {
334         return checkBuffer(name, e.data.buffer, arraySize);
335     }
336 }, {
337     name: "transfer neuters unmentioned",
338     send: function (name) {
339         var buffer = createBuffer(arraySize);
340         wrapSend(name, [], [buffer]);
341         assertBufferClosed(name, buffer);
342     },
343     test : function (name, e) {
344         return e.data.length == 0;
345     }
346 }];
347
348 testList = testList.concat(allBufferTypes.map(function(bufferType) { return {
349     name: "raw " + bufferType[0],
350     send: function (name) {
351         var view = createView(bufferType[1], bufferType[2]);
352         wrapSend(name, view, [view.buffer]);
353         assertViewClosed(name, view);
354         assertBufferClosed(name, view.buffer);
355         wrapFailSend(name, view, [view.buffer]);
356         wrapFailSend(name, view, []);
357     },
358     test: function (name, e) {
359         return checkView(name, bufferType[1], e.data) && checkEmptyArray(name, e.ports);
360     }
361 }}));
362
363 function viewAndBuffer(viewFirst, bufferType) {
364     return {
365         name: (viewFirst ? "send view, buffer for " : "send buffer, view for ") + bufferType[0],
366         send: function (name) {
367             var view = createView(bufferType[1], bufferType[2]);
368             var buffer = view.buffer;
369             wrapSend(name, viewFirst ? [view, buffer] : [buffer, view], [buffer]);
370             assertViewClosed(name, view);
371             assertBufferClosed(name, buffer);
372             wrapFailSend(name, view, [buffer]);
373             wrapFailSend(name, view, []);
374             wrapFailSend(name, buffer, [buffer]);
375             wrapFailSend(name, buffer, []);
376             wrapFailSend(name, [view, buffer], [buffer]);
377             wrapFailSend(name, [buffer, view], [buffer]);
378             wrapFailSend(name, [view, buffer], []);
379             wrapFailSend(name, [buffer, view], []);
380         },
381         test: function (name, e) {
382             var view = e.data[viewFirst ? 0 : 1];
383             var buffer = e.data[viewFirst ? 1 : 0];
384             if (buffer !== view.buffer) {
385                 testFailed(name + " buffer not shared");
386                 return false;
387             }
388             return checkView(name, bufferType[1], view) && checkEmptyArray(name, e.ports);
389         }
390     }
391 };
392
393 function squashUnrelatedViews(bufferType) {
394     return {
395         name: "squash unrelated views for " + bufferType[0],
396         send: function(name) {
397             var view = createView(bufferType[1], bufferType[2]);
398             var views = createEveryView(view.buffer);
399             var buffer = view.buffer;
400             wrapSend(name, view, [view.buffer]);
401             assertViewClosed(name, view);
402             assertBufferClosed(name, view.buffer);
403             for (var v = 0; v < views.length; ++v) {
404                 assertViewClosed(name + "(view " + v + ")", views[v]);
405             }
406             wrapFailSend(name, views, [buffer]);
407         },
408         test: function (name, e) { return checkView(name, bufferType[1], e.data) && checkEmptyArray(name, e.ports); }
409     }
410 }
411
412 testList = testList.concat(allBufferTypes.map(function(bufferType) { return viewAndBuffer(true, bufferType); }));
413 testList = testList.concat(allBufferTypes.map(function(bufferType) { return viewAndBuffer(false, bufferType); }));
414 testList = testList.concat(allBufferTypes.map(function(bufferType) { return squashUnrelatedViews(bufferType); }));
415
416 function doneTest() {
417     if (++window.testsComplete == testList.length) {
418         finishJSTest();
419     }
420     else {
421         var t = testList[window.testsComplete];
422         try {
423             t.send(t.name);
424         } catch(e) {
425             testFailed(t.name + ": on send: " + e);
426             doneTest();
427         }
428     }
429 }
430
431 function windowHandleMessage(event) {
432     var currentTest = testList[window.testsComplete];
433     if (currentTest.alreadyHit) {
434         testFailed(currentTest.name + ": windowHandleMessage hit more than once.");
435         return false;
436     }
437     currentTest.alreadyHit = true;
438     try {
439         if (currentTest.test(currentTest.name, event)) {
440             testPassed(currentTest.name);
441         }
442     } catch(e) {
443         testFailed(currentTest.name + ": on recieve: " + e + ". event.data = " + event.data);
444     }
445     doneTest();
446 }
447
448 window.addEventListener('message', windowHandleMessage);
449 window.testsComplete = -1;
450 doneTest();
451
452 successfullyParsed = true;
453