Skip to content

Commit 7dfca9d

Browse files
committed
Bug 1644778 - add a mozperftest to measure browser.xhtml DOM size, r=tarek,dhouse,sparky
Differential Revision: https://phabricator.services.mozilla.com/D79152
1 parent bddb48e commit 7dfca9d

File tree

14 files changed

+549
-233
lines changed

14 files changed

+549
-233
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/* Any copyright is dedicated to the Public Domain.
2+
* https://creativecommons.org/publicdomain/zero/1.0/ */
3+
/* eslint-env node */
4+
"use strict";
5+
6+
/* global module */
7+
async function test(context, commands) {
8+
await context.selenium.driver.setContext("chrome");
9+
let elementData = await context.selenium.driver.executeAsyncScript(
10+
function() {
11+
let callback = arguments[arguments.length - 1];
12+
(async function() {
13+
let lightDOM = document.querySelectorAll("*");
14+
let elementsWithoutIDs = {};
15+
let idElements = [];
16+
let lightDOMDetails = { idElements, elementsWithoutIDs };
17+
lightDOM.forEach(n => {
18+
if (n.id) {
19+
idElements.push(n.id);
20+
} else {
21+
if (!elementsWithoutIDs.hasOwnProperty(n.localName)) {
22+
elementsWithoutIDs[n.localName] = 0;
23+
}
24+
elementsWithoutIDs[n.localName]++;
25+
}
26+
});
27+
let lightDOMCount = lightDOM.length;
28+
29+
// Recursively explore shadow DOM:
30+
function getShadowElements(root) {
31+
let allElems = Array.from(root.querySelectorAll("*"));
32+
let shadowRoots = allElems.map(n => n.openOrClosedShadowRoot);
33+
for (let innerRoot of shadowRoots) {
34+
if (innerRoot) {
35+
allElems.push(getShadowElements(innerRoot));
36+
}
37+
}
38+
return allElems;
39+
}
40+
let totalDOMCount = Array.from(lightDOM, node => {
41+
if (node.openOrClosedShadowRoot) {
42+
return [node].concat(
43+
getShadowElements(node.openOrClosedShadowRoot)
44+
);
45+
}
46+
return node;
47+
}).flat().length;
48+
let panelMenuCount = document.querySelectorAll(
49+
"panel,menupopup,popup,popupnotification"
50+
).length;
51+
return {
52+
panelMenuCount,
53+
lightDOMCount,
54+
totalDOMCount,
55+
lightDOMDetails,
56+
};
57+
})().then(callback);
58+
}
59+
);
60+
let { lightDOMDetails } = elementData;
61+
delete elementData.lightDOMDetails;
62+
lightDOMDetails.idElements.sort();
63+
for (let id of lightDOMDetails.idElements) {
64+
console.log(id);
65+
}
66+
console.log("Elements without ids:");
67+
for (let [localName, count] of Object.entries(
68+
lightDOMDetails.elementsWithoutIDs
69+
)) {
70+
console.log(count.toString().padStart(4) + " " + localName);
71+
}
72+
console.log(elementData);
73+
await context.selenium.driver.setContext("content");
74+
await commands.measure.start("data:text/html,BrowserDOM");
75+
commands.measure.addObject(elementData);
76+
}
77+
78+
module.exports = {
79+
test,
80+
owner: "Browser Front-end team",
81+
name: "Dom-size",
82+
description: "Measures the size of the DOM",
83+
supportedBrowsers: ["Desktop"],
84+
supportedPlatforms: ["Windows", "Linux", "macOS"],
85+
};

build/gen_test_packages_manifest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
'gtest',
2525
'updater-dep',
2626
'jsreftest',
27+
'perftests',
2728
]
2829

2930
PACKAGE_SPECIFIED_HARNESSES = [
@@ -39,6 +40,7 @@
3940
'updater-dep',
4041
'jittest',
4142
'jsreftest',
43+
'perftests',
4244
]
4345

4446
# These packages are not present for every build configuration.

build/sparse-profiles/perftest

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@
33
[include]
44
path:tools/lint/eslint/
55
path:testing/performance
6+
glob:**/perftest_*.js
7+
8+

python/mozbuild/mozbuild/action/test_archive.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
'jsreftest/**',
117117
'jit-test/**',
118118
'jittest/**', # To make the ignore checker happy
119+
'perftests/**',
119120
],
120121
},
121122
{
@@ -465,6 +466,60 @@
465466
'dest': 'talos/talos/tests/webkit/PerformanceTests/',
466467
},
467468
],
469+
'perftests': [
470+
{
471+
'source': buildconfig.topsrcdir,
472+
'pattern': 'testing/mozbase/**',
473+
},
474+
{
475+
'source': buildconfig.topsrcdir,
476+
'pattern': 'third_party/python/**',
477+
},
478+
{
479+
'source': buildconfig.topsrcdir,
480+
'pattern': 'tools/lint/eslint/**',
481+
},
482+
{
483+
'source': buildconfig.topsrcdir,
484+
'pattern': '**/perftest_*.js'
485+
},
486+
{
487+
'source': buildconfig.topsrcdir,
488+
'pattern': '**/hooks_*py'
489+
},
490+
{
491+
'source': buildconfig.topsrcdir,
492+
'pattern': 'build/autoconf/**'
493+
},
494+
{
495+
'source': buildconfig.topsrcdir,
496+
'pattern': 'build/moz.configure/**'
497+
},
498+
{
499+
'source': buildconfig.topsrcdir,
500+
'pattern': 'python/**'
501+
},
502+
{
503+
'source': buildconfig.topsrcdir,
504+
'pattern': 'build/mach_bootstrap.py'
505+
},
506+
{
507+
'source': buildconfig.topsrcdir,
508+
'pattern': 'build/virtualenv_packages.txt'
509+
},
510+
{
511+
'source': buildconfig.topsrcdir,
512+
'pattern': 'mach/**'
513+
},
514+
{
515+
'source': buildconfig.topsrcdir,
516+
'pattern': 'testing/web-platform/tests/tools/third_party/certifi/**'
517+
},
518+
{
519+
'source': buildconfig.topsrcdir,
520+
'pattern': 'testing/mozharness/**'
521+
}
522+
],
468523
'condprof': [
469524
{
470525
'source': buildconfig.topsrcdir,

python/mozperftest/mozperftest/runner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
"testing/mozbase/mozprofile",
4848
"testing/mozbase/mozproxy",
4949
"third_party/python/attrs/src",
50+
"third_party/python/distro",
5051
"third_party/python/dlmanager",
5152
"third_party/python/esprima",
5253
"third_party/python/importlib_metadata",

python/mozperftest/mozperftest/test/browsertime/runner.py

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121

2222
BROWSERTIME_SRC_ROOT = Path(__file__).parent
23-
PILLOW_VERSION = "6.0.0"
23+
PILLOW_VERSION = "6.2.1"
2424
PYSSIM_VERSION = "0.4"
2525

2626

@@ -60,6 +60,7 @@ class BrowsertimeRunner(NodeRunner):
6060
arguments = {
6161
"cycles": {"type": int, "default": 1, "help": "Number of full cycles"},
6262
"iterations": {"type": int, "default": 1, "help": "Number of iterations"},
63+
"node": {"type": str, "default": None, "help": "Path to Node.js"},
6364
"geckodriver": {"type": str, "default": None, "help": "Path to geckodriver"},
6465
"binary": {
6566
"type": str,
@@ -125,6 +126,10 @@ def browsertime_js(self):
125126
def setup(self):
126127
"""Install browsertime and visualmetrics.py prerequisites and the Node.js package.
127128
"""
129+
node = self.get_arg("node")
130+
if node is not None:
131+
os.environ["NODEJS"] = node
132+
128133
super(BrowsertimeRunner, self).setup()
129134
install_url = self.get_arg("install-url")
130135

@@ -138,7 +143,7 @@ def setup(self):
138143

139144
# installing Python deps on the fly
140145
for dep in ("Pillow==%s" % PILLOW_VERSION, "pyssim==%s" % PYSSIM_VERSION):
141-
install_package(self.virtualenv_manager, dep)
146+
install_package(self.virtualenv_manager, dep, ignore_failure=True)
142147

143148
# check if the browsertime package has been deployed correctly
144149
# for this we just check for the browsertime directory presence
@@ -236,21 +241,25 @@ def extra_default_args(self, args=[]):
236241
extra_args.append("--skipHar")
237242

238243
if not matches(args, "--android"):
239-
# If --firefox.binaryPath is not specified, default to the objdir binary
240-
# Note: --firefox.release is not a real browsertime option, but it will
241-
# silently ignore it instead and default to a release installation.
242-
if (
243-
not matches(
244-
args,
245-
"--firefox.binaryPath",
246-
"--firefox.release",
247-
"--firefox.nightly",
248-
"--firefox.beta",
249-
"--firefox.developer",
250-
)
251-
and extract_browser_name(args) != "chrome"
252-
):
253-
extra_args.extend(("--firefox.binaryPath", self.get_binary_path()))
244+
binary = self.get_arg("binary")
245+
if binary is not None:
246+
extra_args.extend(("--firefox.binaryPath", binary))
247+
else:
248+
# If --firefox.binaryPath is not specified, default to the objdir binary
249+
# Note: --firefox.release is not a real browsertime option, but it will
250+
# silently ignore it instead and default to a release installation.
251+
if (
252+
not matches(
253+
args,
254+
"--firefox.binaryPath",
255+
"--firefox.release",
256+
"--firefox.nightly",
257+
"--firefox.beta",
258+
"--firefox.developer",
259+
)
260+
and extract_browser_name(args) != "chrome"
261+
):
262+
extra_args.extend(("--firefox.binaryPath", self.get_binary_path()))
254263

255264
geckodriver = self.get_arg("geckodriver")
256265
if geckodriver is not None:

python/mozperftest/mozperftest/tests/test_utils.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,27 @@ def check_if_exists(self, **kw):
7676
def test_install_package():
7777
vem = mock.Mock()
7878
vem.bin_path = "someplace"
79-
install_package(vem, "foo")
79+
assert install_package(vem, "foo")
8080
vem._run_pip.assert_called()
8181

8282

83+
@mock.patch("pip._internal.req.constructors.install_req_from_line", new=_req)
84+
def test_install_package_failures():
85+
vem = mock.Mock()
86+
vem.bin_path = "someplace"
87+
88+
def run_pip(*args):
89+
raise Exception()
90+
91+
vem._run_pip = run_pip
92+
93+
with pytest.raises(Exception):
94+
install_package(vem, "foo")
95+
96+
# we can also absorb the error, and just return False
97+
assert not install_package(vem, "foo", ignore_failure=True)
98+
99+
83100
@mock.patch("mozperftest.utils.requests.get", requests_content())
84101
def test_build_test_list():
85102
tests = [EXAMPLE_TESTS_DIR, "https://some/location/perftest_one.js"]

python/mozperftest/mozperftest/utils.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,15 @@ def error(self, msg, name="mozperftest", **kwargs):
9595
self._logger(logging.ERROR, name, kwargs, msg)
9696

9797

98-
def install_package(virtualenv_manager, package):
98+
def install_package(virtualenv_manager, package, ignore_failure=False):
99+
"""Installs a package using the virtualenv manager.
100+
101+
Makes sure the package is really installed when the user already has it
102+
in their local installation.
103+
104+
Returns True on success, or re-raise the error. If ignore_failure
105+
is set to True, ignore the error and return False
106+
"""
99107
from pip._internal.req.constructors import install_req_from_line
100108

101109
req = install_req_from_line(package)
@@ -108,9 +116,15 @@ def install_package(virtualenv_manager, package):
108116
site_packages = os.path.abspath(req.satisfied_by.location)
109117
if site_packages.startswith(venv_site_lib):
110118
# already installed in this venv, we can skip
111-
return
119+
return True
112120
with silence():
113-
virtualenv_manager._run_pip(["install", package])
121+
try:
122+
virtualenv_manager._run_pip(["install", package])
123+
return True
124+
except Exception:
125+
if not ignore_failure:
126+
raise
127+
return False
114128

115129

116130
def build_test_list(tests, randomized=False):

0 commit comments

Comments
 (0)