Bug 1962317: Initial framework for accessibility engine performance testing, plus some initial tests. r=morgan
Tests go in accessible/tests/browser/performance. They use the usual addAccessibleTask function to add a test. Inside the task, they can time operations using the timeThis() function, which takes a name and a function to time. As well as logging the time, this also logs all accessibility PerfStats captured during the function. For now, output is simply logged using info(). In future, we may wish to output this data elsewhere; e.g. so it can be picked up by performance monitoring tools. Having this single function should make extending this fairly easy without needing to rewrite tests. Two initial tests have been included: - browser_manyMutations.js: Test for bug 1948375. - browser_spellingErrors.js: Test for bug 1961832. Differential Revision: https://phabricator.services.mozilla.com/D246544
This commit is contained in:
@@ -44,6 +44,7 @@ BROWSER_CHROME_MANIFESTS += [
|
||||
"tests/browser/general/browser.toml",
|
||||
"tests/browser/hittest/browser.toml",
|
||||
"tests/browser/mac/browser.toml",
|
||||
"tests/browser/performance/browser.toml",
|
||||
"tests/browser/pivot/browser.toml",
|
||||
"tests/browser/role/browser.toml",
|
||||
"tests/browser/scroll/browser.toml",
|
||||
|
||||
12
accessible/tests/browser/performance/browser.toml
Normal file
12
accessible/tests/browser/performance/browser.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[DEFAULT]
|
||||
subsuite = "a11y"
|
||||
support-files = [
|
||||
"head.js",
|
||||
"!/accessible/tests/browser/shared-head.js",
|
||||
"!/accessible/tests/mochitest/*.js",
|
||||
]
|
||||
skip-if = ["tsan"] # Bug 1963506
|
||||
|
||||
["browser_manyMutations.js"]
|
||||
|
||||
["browser_spellingErrors.js"]
|
||||
@@ -0,0 +1,25 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
addAccessibleTask(
|
||||
`<div id="container" role="group"></div>`,
|
||||
async function testManyShowEvents(browser) {
|
||||
await timeThis("manyShowEvents", async () => {
|
||||
info("Adding many children");
|
||||
let lastShown = waitForEvent(EVENT_SHOW, "last");
|
||||
await invokeContentTask(browser, [], () => {
|
||||
const container = content.document.getElementById("container");
|
||||
for (let c = 0; c < 100000; ++c) {
|
||||
const child = content.document.createElement("p");
|
||||
child.textContent = c;
|
||||
container.append(child);
|
||||
}
|
||||
container.lastChild.id = "last";
|
||||
});
|
||||
info("Waiting for last show event");
|
||||
await lastShown;
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,32 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
addAccessibleTask(
|
||||
`<div id="editable" contenteditable></div>`,
|
||||
async function testRemoveManySpellingErrors(browser) {
|
||||
let changed = waitForEvent(EVENT_TEXT_ATTRIBUTE_CHANGED);
|
||||
await invokeContentTask(browser, [], () => {
|
||||
const editable = content.document.getElementById("editable");
|
||||
for (let i = 0; i < 500; ++i) {
|
||||
const p = content.document.createElement("p");
|
||||
p.textContent = "tset tset tset";
|
||||
editable.append(p);
|
||||
}
|
||||
editable.focus();
|
||||
});
|
||||
// Spell checking is async, so we need to wait for it to happen.
|
||||
info("Waiting for spell check");
|
||||
await changed;
|
||||
await timeThis("removeManySpellingErrors", async () => {
|
||||
info("Removing all content from editable");
|
||||
let reorder = waitForEvent(EVENT_REORDER, "editable");
|
||||
await invokeContentTask(browser, [], () => {
|
||||
content.document.getElementById("editable").innerHTML = "";
|
||||
});
|
||||
info("Waiting for reorder event");
|
||||
await reorder;
|
||||
});
|
||||
}
|
||||
);
|
||||
71
accessible/tests/browser/performance/head.js
Normal file
71
accessible/tests/browser/performance/head.js
Normal file
@@ -0,0 +1,71 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
/* exported timeThis */
|
||||
|
||||
// Load the shared-head file first.
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js",
|
||||
this
|
||||
);
|
||||
|
||||
// Load common.js and promisified-events.js from accessible/tests/mochitest/ for
|
||||
// all tests.
|
||||
loadScripts(
|
||||
{ name: "common.js", dir: MOCHITESTS_DIR },
|
||||
{ name: "promisified-events.js", dir: MOCHITESTS_DIR }
|
||||
);
|
||||
|
||||
// All the A11Y metrics in tools/performance/PerfStats.h.
|
||||
const ALL_A11Y_PERFSTATS =
|
||||
(1 << 29) |
|
||||
(1 << 30) |
|
||||
(1 << 31) |
|
||||
(1 << 32) |
|
||||
(1 << 33) |
|
||||
(1 << 34) |
|
||||
(1 << 35) |
|
||||
(1 << 36) |
|
||||
(1 << 37) |
|
||||
(1 << 38) |
|
||||
(1 << 39) |
|
||||
(1 << 40) |
|
||||
(1 << 41) |
|
||||
(1 << 42) |
|
||||
(1 << 43) |
|
||||
(1 << 44);
|
||||
|
||||
/**
|
||||
* Time a function and log how long it took. The given name is included in log
|
||||
* messages. All accessibility PerfStats metrics are also captured and logged.
|
||||
*/
|
||||
async function timeThis(name, func) {
|
||||
const logPrefix = `Timing: ${name}`;
|
||||
info(`${logPrefix}: begin`);
|
||||
const start = performance.now();
|
||||
ChromeUtils.setPerfStatsCollectionMask(ALL_A11Y_PERFSTATS);
|
||||
await func();
|
||||
const delta = performance.now() - start;
|
||||
info(`${logPrefix}: took ${delta} ms`);
|
||||
const stats = JSON.parse(await ChromeUtils.collectPerfStats());
|
||||
ChromeUtils.setPerfStatsCollectionMask(0);
|
||||
// Filter stuff out of stats that we don't care about.
|
||||
// Filter out the GPU process, since accessibility doesn't do anything there.
|
||||
stats.processes = stats.processes.filter(process => process.type != "gpu");
|
||||
for (const process of stats.processes) {
|
||||
// Because of weird JS -> WebIDL 64 bit number issues, we get metrics here
|
||||
// that aren't for accessibility. For example, 1 << 32 also gets us 1 << 0.
|
||||
// Filter those out. Also, filter out any metrics with a count of 0.
|
||||
process.perfstats.metrics = process.perfstats.metrics.filter(
|
||||
metric => metric.metric.startsWith("A11Y_") && metric.count > 0
|
||||
);
|
||||
}
|
||||
// Now that we've filtered metrics, remove any processes that have no metrics left.
|
||||
stats.processes = stats.processes.filter(
|
||||
process => !!process.perfstats.metrics.length
|
||||
);
|
||||
info(`${logPrefix}: PerfStats: ${JSON.stringify(stats, null, 2)}`);
|
||||
}
|
||||
Reference in New Issue
Block a user