diff options
Diffstat (limited to 'dom/promise/tests/test_webassembly_compile.html')
-rw-r--r-- | dom/promise/tests/test_webassembly_compile.html | 446 |
1 files changed, 446 insertions, 0 deletions
diff --git a/dom/promise/tests/test_webassembly_compile.html b/dom/promise/tests/test_webassembly_compile.html new file mode 100644 index 0000000000..351f0f4ae4 --- /dev/null +++ b/dom/promise/tests/test_webassembly_compile.html @@ -0,0 +1,446 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> +<head> + <title>WebAssembly.compile Test</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<script> +const testingFunctions = SpecialPowers.Cu.getJSTestingFunctions(); +const wasmIsSupported = SpecialPowers.unwrap(testingFunctions.wasmIsSupported); +const wasmHasTier2CompilationCompleted = SpecialPowers.unwrap(testingFunctions.wasmHasTier2CompilationCompleted); +const wasmLoadedFromCache = SpecialPowers.unwrap(testingFunctions.wasmLoadedFromCache); +const isCachingEnabled = SpecialPowers.getBoolPref("javascript.options.wasm_caching"); + +// The test_webassembly_compile_sample.wasm is a medium-sized module with 100 +// functions that call each other recursively, returning a computed sum. +// Any other non-trivial module could be generated and used. +var sampleCode; +const sampleURL = "test_webassembly_compile_sample.wasm"; +const sampleFileSize = 16053; +const sampleURLWithRandomQuery = () => sampleURL + "?id=" + String(Math.ceil(Math.random()*100000)); +const sampleExportName = "run"; +const sampleResult = 1275; + +function checkSampleModule(m) { + ok(m instanceof WebAssembly.Module, "got a module"); + var i = new WebAssembly.Instance(m); + ok(i instanceof WebAssembly.Instance, "got an instance"); + ok(i.exports[sampleExportName]() === sampleResult, "got result"); +} + +function checkSampleInstance(i) { + ok(i instanceof WebAssembly.Instance, "got a module"); + ok(i.exports[sampleExportName]() === sampleResult, "got result"); +} + +function fetchSampleModuleCode() { + fetch(sampleURL) + .then(response => response.arrayBuffer()) + .then(buffer => { sampleCode = buffer; runTest(); }) + .catch(err => ok(false, String(err))); +} + +function propertiesExist() { + if (!wasmIsSupported()) { + ok(!this.WebAssembly, "If the device doesn't support, there will be no WebAssembly object"); + SimpleTest.finish(); + return; + } + + ok(WebAssembly, "WebAssembly object should exist"); + ok(WebAssembly.compile, "WebAssembly.compile function should exist"); + runTest(); +} + +function compileFail() { + WebAssembly.compile().then( + () => { ok(false, "should have failed"); runTest(); } + ).catch( + err => { ok(err instanceof TypeError, "empty compile failed"); runTest(); } + ); +} + +function compileSuccess() { + WebAssembly.compile(sampleCode).then( + m => { checkSampleModule(m); runTest(); } + ).catch( + err => { ok(false, String(err)); runTest(); } + ); +} + +function compileManySuccess() { + const N = 100; + + var arr = []; + for (var i = 0; i < N; i++) + arr.push(WebAssembly.compile(sampleCode)); + + SpecialPowers.gc(); + + Promise.all(arr).then(ms => { + ok(ms.length === N, "got the right number"); + for (var j = 0; j < N; j++) + checkSampleModule(ms[j]); + runTest(); + }).catch( + err => { ok(false, String(err)); runTest(); } + ); +} + +function terminateCompileInWorker() { + var w = new Worker(`data:text/plain, + var sampleCode; + function spawnWork() { + const N = 100; + var arr = []; + for (var i = 0; i < N; i++) + arr.push(WebAssembly.compile(sampleCode)); + Promise.all(arr).then(spawnWork); + } + onmessage = e => { + sampleCode = e.data; + spawnWork(); + postMessage("ok"); + } + `); + w.postMessage(sampleCode); + w.onmessage = e => { + ok(e.data === "ok", "worker finished first step"); + w.terminate(); + runTest(); + }; +} + +function instantiateFail() { + WebAssembly.instantiate().then( + () => { ok(false, "should have failed"); runTest(); } + ).catch( + err => { ok(err instanceof TypeError, "empty compile failed"); runTest(); } + ); +} + +function instantiateSuccess() { + WebAssembly.instantiate(sampleCode).then( + r => { checkSampleModule(r.module); checkSampleInstance(r.instance); runTest(); } + ).catch( + err => { ok(false, String(err)); runTest(); } + ); +} + +function chainSuccess() { + WebAssembly.compile(sampleCode).then( + m => WebAssembly.instantiate(m) + ).then( + i => { checkSampleInstance(i); runTest(); } + ).catch( + err => { ok(false, String(err)); runTest(); } + ); +} + +function compileStreamingNonResponse() { + WebAssembly.compileStreaming({}) + .then(() => { ok(false); }) + .catch(err => { ok(err instanceof TypeError, "rejected {}"); runTest(); }); +} + +function compileStreamingNoMime() { + WebAssembly.compileStreaming(new Response(new ArrayBuffer())) + .then(() => { ok(false); }) + .catch(err => { ok(err instanceof TypeError, "rejected no MIME type"); runTest(); }); +} + +function compileStreamingBadMime() { + var badMimes = [ + "", + "application/js", + "application/js;application/wasm", + "application/wasm;application/js", + "application/wasm;", + "application/wasm1", + ]; + var promises = []; + for (let mimeType of badMimes) { + var init = { headers: { "Content-Type": mimeType } }; + promises.push( + WebAssembly.compileStreaming(new Response(sampleCode, init)) + .then(() => Promise.reject(), err => { + is(err.message, + `WebAssembly: Response has unsupported MIME type '${mimeType}' expected 'application/wasm'`, + "correct MIME type error message"); + return Promise.resolve(); + }) + ); + } + Promise.all(promises) + .then(() => { ok(true, "all bad MIME types rejected"); runTest(); }); +} + +function compileStreamingGoodMime() { + var badMimes = [ + "application/wasm", + " application/wasm ", + "application/wasm ", + ]; + var promises = []; + for (let mimeType of badMimes) { + var init = { headers: { "Content-Type": mimeType } }; + promises.push( + WebAssembly.compileStreaming(new Response(sampleCode, init)) + ); + } + Promise.all(promises) + .then(() => { ok(true, "all good MIME types accepted"); runTest(); }); +} + +function compileStreamingDoubleUseFail() { + fetch(sampleURL) + .then(response => { + WebAssembly.compileStreaming(response) + .then(m => { + checkSampleModule(m); + return WebAssembly.compileStreaming(response); + }) + .then( + () => ok(false, "should have failed on second use"), + err => { ok(true, "failed on second use"); runTest(); } + ); + }); +} + +function compileStreamingNullBody() { + var init = { headers: { "Content-Type": "application/wasm" } }; + WebAssembly.compileStreaming(new Response(undefined, init)) + .then(() => { ok(false); }) + .catch(err => { ok(err instanceof WebAssembly.CompileError, "null body"); runTest(); }); +} + +function compileStreamingFetch() { + WebAssembly.compileStreaming(fetch(sampleURL)) + .then(m => { checkSampleModule(m); runTest(); }) + .catch(err => { ok(false, String(err)); }); +} + +function compileCachedBasic() { + const url = sampleURLWithRandomQuery(); + WebAssembly.compileStreaming(fetch(url)) + .then(module => { + checkSampleModule(module); + ok(!wasmLoadedFromCache(module), "not cached yet"); + while(!wasmHasTier2CompilationCompleted(module)); + return WebAssembly.compileStreaming(fetch(url)); + }) + .then(module => { + checkSampleModule(module); + ok(wasmLoadedFromCache(module), "loaded from cache"); + }) + .then(() => runTest()) + .catch(err => { ok(false, String(err)) }); +} + +function compileCachedCompressed() { + const url = sampleURLWithRandomQuery(); + + // It is a rough estimate that compilation code is about + // 2-4 times of the wasm file size. After it compression + // it will be less (about 60% ?) + const EstimatedCompilationArtifactSize = 2 * sampleFileSize; + const EstimatedCompressedArtifactSize = 0.6 * EstimatedCompilationArtifactSize; + + // Set limit on cache entry so it will fail if it is not + // compressed. + const cleanup = () => { + SpecialPowers.clearUserPref("browser.cache.disk.max_entry_size") + }; + Promise.resolve(SpecialPowers.setIntPref("browser.cache.disk.max_entry_size", + Math.round(EstimatedCompressedArtifactSize / 1024) /* kb */)) + .then(() => WebAssembly.compileStreaming(fetch(url))) + .then(module => { + checkSampleModule(module); + ok(!wasmLoadedFromCache(module), "not cached yet"); + while(!wasmHasTier2CompilationCompleted(module)); + return WebAssembly.compileStreaming(fetch(url)); + }) + .then(module => { + checkSampleModule(module); + ok(wasmLoadedFromCache(module), "loaded from cache"); + }) + .then(() => { cleanup(); runTest() }) + .catch(err => { cleanup(); ok(false, String(err)) }); +} + +function compileCachedTooLargeForCache() { + const url = sampleURLWithRandomQuery(); + // Set unreasonable limit, caching will fail. + // Bug 1719508 can change name of pref, this and + // compileCachedCompressed tests will become invalid. + const cleanup = () => { + SpecialPowers.clearUserPref("browser.cache.disk.max_entry_size") + }; + Promise.resolve(SpecialPowers.setIntPref("browser.cache.disk.max_entry_size", 1 /* kb */)) + .then(() => WebAssembly.compileStreaming(fetch(url))) + .then(module => { + console.log(module) + checkSampleModule(module); + ok(!wasmLoadedFromCache(module), "not cached yet"); + while(!wasmHasTier2CompilationCompleted(module)); + return WebAssembly.compileStreaming(fetch(url)); + }) + .then(module => { + checkSampleModule(module); + ok(!wasmLoadedFromCache(module), "not cached (size limit)"); + }) + .then(() => { cleanup(); runTest() }) + .catch(err => { cleanup(); ok(false, String(err)) }); +} + +const Original = "original"; +const Clone = "clone"; + +function compileCachedBothClonesHitCache(which) { + const url = sampleURLWithRandomQuery(); + WebAssembly.compileStreaming(fetch(url)) + .then(module => { + checkSampleModule(module); + ok(!wasmLoadedFromCache(module), "not cached yet"); + while(!wasmHasTier2CompilationCompleted(module)); + return fetch(url); + }) + .then(original => { + let clone = original.clone(); + if (which === Clone) [clone, original] = [original, clone]; + return Promise.all([ + WebAssembly.compileStreaming(original), + WebAssembly.compileStreaming(clone) + ]); + }) + .then(([m1, m2]) => { + checkSampleModule(m1); + ok(wasmLoadedFromCache(m1), "clone loaded from cache"); + checkSampleModule(m2); + ok(wasmLoadedFromCache(m2), "original loaded from cache"); + }) + .then(() => runTest()) + .catch(err => { ok(false, String(err)) }); +} + +function compileCachedCacheThroughClone(which) { + const url = sampleURLWithRandomQuery(); + fetch(url) + .then(original => { + ok(true, "fun time"); + let clone = original.clone(); + if (which === Clone) [clone, original] = [original, clone]; + return Promise.all([ + WebAssembly.compileStreaming(original), + clone.arrayBuffer() + ]); + }) + .then(([module, buffer]) => { + ok(!wasmLoadedFromCache(module), "not cached yet"); + ok(buffer instanceof ArrayBuffer); + while(!wasmHasTier2CompilationCompleted(module)); + return WebAssembly.compileStreaming(fetch(url)); + }) + .then(m => { + ok(wasmLoadedFromCache(m), "cache hit of " + which); + }) + .then(() => runTest()) + .catch(err => { ok(false, String(err)) }); +} + +function instantiateStreamingFetch() { + WebAssembly.instantiateStreaming(fetch(sampleURL)) + .then(({module, instance}) => { checkSampleModule(module); checkSampleInstance(instance); runTest(); }) + .catch(err => { ok(false, String(err)); }); +} + +function compileManyStreamingFetch() { + const N = 20; + + var arr = []; + for (var i = 0; i < N; i++) + arr.push(WebAssembly.compileStreaming(fetch(sampleURL))); + + SpecialPowers.gc(); + + Promise.all(arr).then(ms => { + ok(ms.length === N, "got the right number"); + for (var j = 0; j < N; j++) + checkSampleModule(ms[j]); + runTest(); + }).catch( + err => { ok(false, String(err)); runTest(); } + ); +} + +function runWorkerTests() { + var w = new Worker("test_webassembly_compile_worker.js"); + w.postMessage(sampleCode); + w.onmessage = e => { + ok(e.data === "ok", "worker test: " + e.data); + runTest(); + }; +} + +function terminateCompileStreamingInWorker() { + var w = new Worker("test_webassembly_compile_worker_terminate.js"); + w.onmessage = e => { + ok(e.data === "ok", "worker streaming terminate test: " + e.data); + w.terminate(); + runTest(); + }; +} + +var tests = [ propertiesExist, + compileFail, + compileSuccess, + compileManySuccess, + terminateCompileInWorker, + instantiateFail, + instantiateSuccess, + chainSuccess, + compileStreamingNonResponse, + compileStreamingNoMime, + compileStreamingBadMime, + compileStreamingGoodMime, + compileStreamingDoubleUseFail, + compileStreamingNullBody, + compileStreamingFetch, + ...(isCachingEnabled ? [ + compileCachedBasic, + compileCachedCompressed, + compileCachedTooLargeForCache, + compileCachedBothClonesHitCache.bind(Original), + compileCachedBothClonesHitCache.bind(Clone), + compileCachedCacheThroughClone.bind(Original), + compileCachedCacheThroughClone.bind(Clone), + ]: []), + instantiateStreamingFetch, + compileManyStreamingFetch, + runWorkerTests, + terminateCompileStreamingInWorker, + ]; + +// This initialization must always run +tests.unshift(fetchSampleModuleCode); + +function runTest() { + if (!tests.length) { + SimpleTest.finish(); + return; + } + + var test = tests.shift(); + test(); +} + +SimpleTest.waitForExplicitFinish(); +runTest(); +</script> +</body> +</html> |