diff options
Diffstat (limited to 'dom/serviceworkers/test/performance')
11 files changed, 471 insertions, 0 deletions
diff --git a/dom/serviceworkers/test/performance/intercepted.txt b/dom/serviceworkers/test/performance/intercepted.txt new file mode 100644 index 0000000000..87c7a8efe7 --- /dev/null +++ b/dom/serviceworkers/test/performance/intercepted.txt @@ -0,0 +1 @@ +intercepted diff --git a/dom/serviceworkers/test/performance/perftest.toml b/dom/serviceworkers/test/performance/perftest.toml new file mode 100644 index 0000000000..6a7e5928be --- /dev/null +++ b/dom/serviceworkers/test/performance/perftest.toml @@ -0,0 +1,14 @@ +[DEFAULT] +support-files = [ + "intercepted.txt", + "perfutils.js", + "sw_cacher.js", + "sw_empty.js", + "sw_intercept_target.js", + "target.txt", + "time_fetch.html", +] + +["test_caching.html"] +["test_fetch.html"] +["test_registration.html"] diff --git a/dom/serviceworkers/test/performance/perfutils.js b/dom/serviceworkers/test/performance/perfutils.js new file mode 100644 index 0000000000..d7edbe2fe7 --- /dev/null +++ b/dom/serviceworkers/test/performance/perfutils.js @@ -0,0 +1,46 @@ +"use strict"; + +/** + * Given a map from test names to arrays of results, report perfherder metrics + * and log full results. + */ +function reportMetrics(journal) { + let metrics = {}; + let text = "\nResults (ms)\n"; + + const names = Object.keys(journal); + const prefixLen = 1 + Math.max(...names.map(str => str.length)); + + for (const name in journal) { + const med = median(journal[name]); + text += (name + ":").padEnd(prefixLen, " ") + stringify(journal[name]); + text += " median " + med + "\n"; + metrics[name] = med; + } + + dump(text); + info("perfMetrics", JSON.stringify(metrics)); +} + +function median(arr) { + arr = [...arr].sort((a, b) => a - b); + const mid = Math.floor(arr.length / 2); + + if (arr.length % 2) { + return arr[mid]; + } + + return (arr[mid - 1] + arr[mid]) / 2; +} + +function stringify(arr) { + function pad(num) { + let s = num.toString().padStart(5, " "); + if (s[0] != " ") { + s = " " + s; + } + return s; + } + + return arr.reduce((acc, elem) => acc + pad(elem), ""); +} diff --git a/dom/serviceworkers/test/performance/sw_cacher.js b/dom/serviceworkers/test/performance/sw_cacher.js new file mode 100644 index 0000000000..5a441ef785 --- /dev/null +++ b/dom/serviceworkers/test/performance/sw_cacher.js @@ -0,0 +1,18 @@ +"use strict"; + +oninstall = function (event) { + event.waitUntil( + caches.open("perftest").then(function (cache) { + return cache.put("cached.txt", new Response("cached.txt")); + }) + ); +}; + +onfetch = function (event) { + if (event.request.url.endsWith("/cached.txt")) { + var p = caches.match("cached.txt", { cacheName: "perftest" }); + event.respondWith(p); + } else if (event.request.url.endsWith("/uncached.txt")) { + event.respondWith(new Response("uncached.txt")); + } +}; diff --git a/dom/serviceworkers/test/performance/sw_empty.js b/dom/serviceworkers/test/performance/sw_empty.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/dom/serviceworkers/test/performance/sw_empty.js diff --git a/dom/serviceworkers/test/performance/sw_intercept_target.js b/dom/serviceworkers/test/performance/sw_intercept_target.js new file mode 100644 index 0000000000..47b3853978 --- /dev/null +++ b/dom/serviceworkers/test/performance/sw_intercept_target.js @@ -0,0 +1,7 @@ +"use strict"; + +onfetch = function (event) { + if (event.request.url.indexOf("target.txt") != -1) { + event.respondWith(fetch("intercepted.txt")); + } +}; diff --git a/dom/serviceworkers/test/performance/target.txt b/dom/serviceworkers/test/performance/target.txt new file mode 100644 index 0000000000..eb5a316cbd --- /dev/null +++ b/dom/serviceworkers/test/performance/target.txt @@ -0,0 +1 @@ +target diff --git a/dom/serviceworkers/test/performance/test_caching.html b/dom/serviceworkers/test/performance/test_caching.html new file mode 100644 index 0000000000..cd6d4cf493 --- /dev/null +++ b/dom/serviceworkers/test/performance/test_caching.html @@ -0,0 +1,89 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Service worker performance test: caching</title> +</head> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script src="../utils.js"></script> +<script src="perfutils.js"></script> +<script> + + "use strict"; + + const NO_CACHE = "No cache"; + const CACHED = "Cached"; + const NO_CACHE_AGAIN = "No cache again"; + + var journal = {}; + journal[NO_CACHE] = []; + journal[CACHED] = []; + journal[NO_CACHE_AGAIN] = []; + + const ITERATIONS = 10; + + var perfMetadata = { + owner: "DOM LWS", + name: "Service Worker Caching", + description: "Test service worker caching.", + options: { + default: { + perfherder: true, + perfherder_metrics: [ + // Here, we can't use the constants defined above because perfherder + // grabs data from the parse tree. + { name: "No cache", unit: "ms", shouldAlert: true }, + { name: "Cached", unit: "ms", shouldAlert: true }, + { name: "No cache again", unit: "ms", shouldAlert: true }, + ], + verbose: true, + manifest: "perftest.toml", + manifest_flavor: "plain", + }, + }, + }; + + add_task(async () => { + await SpecialPowers.pushPrefEnv({ + set: [["dom.serviceWorkers.testing.enabled", true]] + }); + }); + + function create_iframe(url) { + return new Promise(function(res) { + let iframe = document.createElement("iframe"); + iframe.src = url; + iframe.onload = function() { res(iframe) } + document.body.appendChild(iframe); + }); + } + + async function time_fetch(journal, iframe, filename) { + for (let i = 0; i < ITERATIONS; i++) { + let result = await iframe.contentWindow.time_fetch(filename); + is(result.status, 200); + is(result.data, filename); + journal.push(result.elapsed_ms); + } + } + + add_task(async () => { + let reg = await navigator.serviceWorker.register("sw_cacher.js"); + await waitForState(reg.installing, "activated"); + + let iframe = await create_iframe("time_fetch.html"); + + await time_fetch(journal[NO_CACHE], iframe, "uncached.txt"); + await time_fetch(journal[CACHED], iframe, "cached.txt"); + await time_fetch(journal[NO_CACHE_AGAIN], iframe, "uncached.txt"); + + await reg.unregister(); + }); + + add_task(() => { + reportMetrics(journal); + }); + +</script> +<body> +</body> +</html> diff --git a/dom/serviceworkers/test/performance/test_fetch.html b/dom/serviceworkers/test/performance/test_fetch.html new file mode 100644 index 0000000000..29dd65b595 --- /dev/null +++ b/dom/serviceworkers/test/performance/test_fetch.html @@ -0,0 +1,168 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Service worker performance test: fetch</title> +</head> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script src="../utils.js"></script> +<script src="perfutils.js"></script> +<script> + + "use strict"; + + const COLD_FETCH = "Cold fetch"; + const UNDISTURBED_FETCH = "Undisturbed fetch"; + const INTERCEPTED_FETCH = "Intercepted fetch"; + const LIBERATED_FETCH = "Liberated fetch"; + const UNDISTURBED_XHR = "Undisturbed XHR"; + const INTERCEPTED_XHR = "Intercepted XHR"; + const LIBERATED_XHR = "Liberated XHR"; + + var journal = {}; + journal[COLD_FETCH] = []; + journal[UNDISTURBED_FETCH] = []; + journal[INTERCEPTED_FETCH] = []; + journal[LIBERATED_FETCH] = []; + journal[UNDISTURBED_XHR] = []; + journal[INTERCEPTED_XHR] = []; + journal[LIBERATED_XHR] = []; + + const ITERATIONS = 10; + + var perfMetadata = { + owner: "DOM LWS", + name: "Service Worker Fetch", + description: "Test cold and warm fetches.", + options: { + default: { + perfherder: true, + perfherder_metrics: [ + // Here, we can't use the constants defined above because perfherder + // grabs data from the parse tree. + { name: "Cold fetch", unit: "ms", shouldAlert: true }, + { name: "Undisturbed fetch", unit: "ms", shouldAlert: true }, + { name: "Intercepted fetch", unit: "ms", shouldAlert: true }, + { name: "Liberated fetch", unit: "ms", shouldAlert: true }, + { name: "Undisturbed XHR", unit: "ms", shouldAlert: true }, + { name: "Intercepted XHR", unit: "ms", shouldAlert: true }, + { name: "Liberated XHR", unit: "ms", shouldAlert: true }, + ], + verbose: true, + manifest: "perftest.toml", + manifest_flavor: "plain", + }, + }, + }; + + function create_iframe(url) { + return new Promise(function(res) { + let iframe = document.createElement("iframe"); + iframe.src = url; + iframe.onload = function() { res(iframe) } + document.body.appendChild(iframe); + }); + } + + add_task(async () => { + await SpecialPowers.pushPrefEnv({ + set: [["dom.serviceWorkers.testing.enabled", true]] + }); + }); + + /** + * Time fetch from a fresh service worker. + */ + add_task(async () => { + for (let i = 0; i < ITERATIONS; i++) { + let reg = await navigator.serviceWorker.register("sw_intercept_target.js"); + await waitForState(reg.installing, "activated"); + + let iframe = await create_iframe("time_fetch.html"); + + let result = await iframe.contentWindow.time_fetch("target.txt"); + is(result.status, 200); + is(result.data, "intercepted\n"); + journal[COLD_FETCH].push(result.elapsed_ms); + + ok(document.body.removeChild(iframe), "Failed to remove child iframe"); + + await reg.unregister(); + } + }); + + /** + * Time unintercepted fetch, intercepted fetch, then unintercepted + * fetch again. + */ + add_task(async () => { + let reg = await navigator.serviceWorker.register("sw_intercept_target.js"); + await waitForState(reg.installing, "activated"); + + async function measure(journal, sw_enabled) { + await SpecialPowers.pushPrefEnv({ + set: [["dom.serviceWorkers.enabled", sw_enabled]] + }); + + let iframe = await create_iframe("time_fetch.html"); + + for (let i = 0; i < ITERATIONS; i++) { + let result = await iframe.contentWindow.time_fetch("target.txt"); + is(result.status, 200); + is(result.data, sw_enabled ? "intercepted\n" : "target\n"); + journal.push(result.elapsed_ms); + } + + ok(document.body.removeChild(iframe), "Failed to remove child iframe"); + + await SpecialPowers.popPrefEnv(); + } + + await measure(journal[UNDISTURBED_FETCH], false); + await measure(journal[INTERCEPTED_FETCH], true); + await measure(journal[LIBERATED_FETCH], false); + + await reg.unregister(); + }); + + /** + * Time unintercepted XHR, intercepted XHR, then unintercepted + * XHR again. + */ + add_task(async () => { + let reg = await navigator.serviceWorker.register("sw_intercept_target.js"); + await waitForState(reg.installing, "activated"); + + async function measure(journal, sw_enabled) { + await SpecialPowers.pushPrefEnv({ + set: [["dom.serviceWorkers.enabled", sw_enabled]] + }); + + let iframe = await create_iframe("time_fetch.html"); + + for (let i = 0; i < ITERATIONS; i++) { + let result = await iframe.contentWindow.time_xhr("target.txt"); + is(result.status, 200); + is(result.data, sw_enabled ? "intercepted\n" : "target\n"); + journal.push(result.elapsed_ms); + } + + ok(document.body.removeChild(iframe), "Failed to remove child iframe"); + + await SpecialPowers.popPrefEnv(); + } + + await measure(journal[UNDISTURBED_XHR], false); + await measure(journal[INTERCEPTED_XHR], true); + await measure(journal[LIBERATED_XHR], false); + + await reg.unregister(); + }); + + add_task(() => { + reportMetrics(journal); + }); + +</script> +<body> +</body> +</html> diff --git a/dom/serviceworkers/test/performance/test_registration.html b/dom/serviceworkers/test/performance/test_registration.html new file mode 100644 index 0000000000..d5abbf6775 --- /dev/null +++ b/dom/serviceworkers/test/performance/test_registration.html @@ -0,0 +1,89 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Service worker performance test: registration</title> +</head> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script src="../utils.js"></script> +<script src="perfutils.js"></script> +<script> + + "use strict"; + + const REGISTRATION = "Registration"; + const ACTIVATION = "Activation"; + const UNREGISTRATION = "Unregistration"; + + var journal = []; + journal[REGISTRATION] = []; + journal[ACTIVATION] = []; + journal[UNREGISTRATION] = []; + + const ITERATIONS = 10; + + var perfMetadata = { + owner: "DOM LWS", + name: "Service Worker Registration", + description: "Test registration, activation, and unregistration.", + options: { + default: { + perfherder: true, + perfherder_metrics: [ + // Here, we can't use the constants defined above because perfherder + // grabs data from the parse tree. + { name: "Registration", unit: "ms", shouldAlert: true }, + { name: "Activation", unit: "ms", shouldAlert: true }, + { name: "Unregistration", unit: "ms", shouldAlert: true }, + ], + verbose: true, + manifest: "perftest.toml", + manifest_flavor: "plain", + }, + }, + }; + + function create_iframe(url) { + return new Promise(function(res) { + let iframe = document.createElement("iframe"); + iframe.src = url; + iframe.onload = function() { res(iframe) } + document.body.appendChild(iframe); + }); + } + + add_task(async () => { + await SpecialPowers.pushPrefEnv({ + set: [["dom.serviceWorkers.testing.enabled", true]] + }); + + async function measure() { + let begin_ts = performance.now(); + let reg = await navigator.serviceWorker.register("sw_empty.js"); + let reg_ts = performance.now(); + await waitForState(reg.installing, "activated"); + let act_ts = performance.now(); + await reg.unregister(); + let unreg_ts = performance.now(); + + journal[REGISTRATION].push(reg_ts - begin_ts); + journal[ACTIVATION].push(act_ts - reg_ts); + journal[UNREGISTRATION].push(unreg_ts - act_ts); + } + + for (let i = 0; i < ITERATIONS; i++) { + await measure(); + } + + await SpecialPowers.popPrefEnv(); + + ok(true); + }); + + add_task(() => { + reportMetrics(journal); + }); + +</script> +<body> +</body> +</html> diff --git a/dom/serviceworkers/test/performance/time_fetch.html b/dom/serviceworkers/test/performance/time_fetch.html new file mode 100644 index 0000000000..a771d4889f --- /dev/null +++ b/dom/serviceworkers/test/performance/time_fetch.html @@ -0,0 +1,38 @@ +<!DOCTYPE HTML> +<html> +<head> +<script> + + "use strict"; + + async function time_fetch(url) { + let start = performance.now(); + let res = await fetch(url); + let elapsed = performance.now() - start; + + return { + elapsed_ms : elapsed, + status : res.status, + data : await res.text() + }; + } + + async function time_xhr(url) { + let xhr = new XMLHttpRequest(); + xhr.open("GET", url, false); + let start = performance.now(); + xhr.send(); + let elapsed = performance.now() - start; + + return { + elapsed_ms : elapsed, + status : xhr.status, + data : xhr.responseText + } + } + +</script> +</head> +<body> +</body> +</html> |