forked from mirrors/gecko-dev
Bug 1299271 - Service worker performance test r=asuth,sparky
These are a first-cut of service worker performance tests, just running as vanilla mochitests for now. Bug 1832059 tracks the integration work necessary. Differential Revision: https://phabricator.services.mozilla.com/D177427
This commit is contained in:
parent
e07d89b90c
commit
b831405fb6
14 changed files with 494 additions and 1 deletions
|
|
@ -114,6 +114,7 @@ FINAL_LIBRARY = "xul"
|
|||
MOCHITEST_MANIFESTS += [
|
||||
"test/mochitest-dFPI.ini",
|
||||
"test/mochitest.toml",
|
||||
"test/performance/perftest.toml",
|
||||
]
|
||||
|
||||
MOCHITEST_CHROME_MANIFESTS += [
|
||||
|
|
|
|||
1
dom/serviceworkers/test/performance/intercepted.txt
Normal file
1
dom/serviceworkers/test/performance/intercepted.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
intercepted
|
||||
14
dom/serviceworkers/test/performance/perftest.toml
Normal file
14
dom/serviceworkers/test/performance/perftest.toml
Normal file
|
|
@ -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"]
|
||||
46
dom/serviceworkers/test/performance/perfutils.js
Normal file
46
dom/serviceworkers/test/performance/perfutils.js
Normal file
|
|
@ -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), "");
|
||||
}
|
||||
18
dom/serviceworkers/test/performance/sw_cacher.js
Normal file
18
dom/serviceworkers/test/performance/sw_cacher.js
Normal file
|
|
@ -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"));
|
||||
}
|
||||
};
|
||||
0
dom/serviceworkers/test/performance/sw_empty.js
Normal file
0
dom/serviceworkers/test/performance/sw_empty.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
"use strict";
|
||||
|
||||
onfetch = function (event) {
|
||||
if (event.request.url.indexOf("target.txt") != -1) {
|
||||
event.respondWith(fetch("intercepted.txt"));
|
||||
}
|
||||
};
|
||||
1
dom/serviceworkers/test/performance/target.txt
Normal file
1
dom/serviceworkers/test/performance/target.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
target
|
||||
89
dom/serviceworkers/test/performance/test_caching.html
Normal file
89
dom/serviceworkers/test/performance/test_caching.html
Normal file
|
|
@ -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>
|
||||
168
dom/serviceworkers/test/performance/test_fetch.html
Normal file
168
dom/serviceworkers/test/performance/test_fetch.html
Normal file
|
|
@ -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>
|
||||
89
dom/serviceworkers/test/performance/test_registration.html
Normal file
89
dom/serviceworkers/test/performance/test_registration.html
Normal file
|
|
@ -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>
|
||||
38
dom/serviceworkers/test/performance/time_fetch.html
Normal file
38
dom/serviceworkers/test/performance/time_fetch.html
Normal file
|
|
@ -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>
|
||||
|
|
@ -151,6 +151,7 @@ class Mochitest(Layer):
|
|||
# Bug 1858155 - Attempting to only use one test_path triggers a failure
|
||||
# during test execution
|
||||
args.test_paths = [str(test.name), str(test.name)]
|
||||
args.keep_open = False
|
||||
args.runByManifest = True
|
||||
args.manifestFile = manifest
|
||||
args.topobjdir = self.topobjdir
|
||||
|
|
@ -183,7 +184,8 @@ class Mochitest(Layer):
|
|||
status, log_processor = FunctionalTestRunner.test(
|
||||
self.mach_cmd,
|
||||
[str(test)],
|
||||
self._parse_extra_args(self.get_arg("extra-args")),
|
||||
self._parse_extra_args(self.get_arg("extra-args"))
|
||||
+ ["--keep-open=False"],
|
||||
)
|
||||
|
||||
if status is not None and status != 0:
|
||||
|
|
|
|||
|
|
@ -118,6 +118,25 @@ domcount:
|
|||
--browsertime-geckodriver ${MOZ_FETCHES_DIR}/geckodriver
|
||||
--output $MOZ_FETCHES_DIR/../artifacts
|
||||
|
||||
service-worker:
|
||||
description: Run service worker tests
|
||||
treeherder:
|
||||
symbol: perftest(linux-sw)
|
||||
tier: 2
|
||||
attributes:
|
||||
batch: false
|
||||
cron: false
|
||||
run-on-projects: [autoland, mozilla-central]
|
||||
run:
|
||||
command: >-
|
||||
mkdir -p $MOZ_FETCHES_DIR/../artifacts &&
|
||||
cd $MOZ_FETCHES_DIR &&
|
||||
python3 python/mozperftest/mozperftest/runner.py
|
||||
dom/serviceworkers/test/performance/test_registration.html
|
||||
--mochitest-binary ${MOZ_FETCHES_DIR}/firefox/firefox-bin
|
||||
--flavor mochitest
|
||||
--output $MOZ_FETCHES_DIR/../artifacts
|
||||
|
||||
http3:
|
||||
description: Run HTTP/3 test
|
||||
treeherder:
|
||||
|
|
|
|||
Loading…
Reference in a new issue