Files
sousa-gecko/testing/performance/hooks_recording.py
T
KSandAndrew Creskey 7ac86cb4b4 Bug 2033466 - Add a prototype navigation benchmark test. r=perftest-reviewers,mozperftest-reviewers,sparky
`nav-bench` is a new prototype benchmark which measures pageload and sub
navigation performance across 5 sites (Amazon, BBC, DuckDuckGo, Reddit,
Wikipedia) in a single combined browsertime session.

Score is calculated as the geomean of 60000 (ms) /SpeedIndex (ms) across
all sites and cycles (inspired by LoadLine)

Runs at tier 3 (for now) on:
- Linux (ubuntu 24.04)
- Windows 11
- Windows 11 (ref)
- macOS (aarch64)

Uses the recently added mitmproxy 12.2.1

`nav-serverplayback.py` is a separate script rather than an extension of
`alt-serverplayback.py` for a couple of reasons:

1. API incompatibility between mitmproxy 12.2.1 and 8.1.1/11.0.0 it did
   not make sense for a single script to satisfy all versions without
   significan branching logic

2. Extending `alt-serverplayback.py` risks regressiong every existing
   tp6 test which relies on it. Modifying it would require re-validating
   everything.

This "duplication" is temporary. Bug 2025545 tracks upgrading
everything to mitmproxy 12 (or whatever the latest is at the time) and
Bug 2025374 tracks upstreaming some changes to use mitmproxy built-in
flags entirely.

Co-authored-by: Andrew Creskey <acreskey@mozilla.com>

Differential Revision: https://phabricator.services.mozilla.com/D301754
2026-06-22 22:28:07 +00:00

222 lines
7.9 KiB
Python

# 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/.
import json
import os
import platform
from pathlib import Path
from mozperftest.test.browsertime import add_option, add_options
# Uncomment the manual_login line if you need to do a manual login.
# The extra browsertime arguments get overwritten below so they
# need to be set here. The value is the time you need to do a login.
options = [
("pageCompleteWaitTime", "10000"),
# ("browsertime.manual_login", 100000),
]
next_site = None
current_site = None
failed_sites = []
RECORDING_LIST = Path(Path(__file__).parent, "pageload_sites.json")
# Uncomment this to record tp7 desktop sites in CI or locally.
# This is still a WIP (Bug 1831310) and meant to be used by the
# perftest team.
# RECORDING_LIST = Path(Path(__file__).parent, "tp7_desktop_sites.json")
# Allow overriding the recording list via env var, so we don't need to edit
# this file when switching between site lists.
if os.environ.get("PERFTEST_RECORDING_LIST"):
RECORDING_LIST = Path(os.environ["PERFTEST_RECORDING_LIST"])
SCM_1_LOGIN_SITES = ("facebook", "netflix")
def before_iterations(kw):
global next_site, RECORDING_LIST # noqa: PLW0603
print("Setting up next site to record.")
# Nav sites all use a -nav suffix. Auto-switch to nav_sites.json so the
# caller doesn't need to set PERFTEST_RECORDING_LIST manually.
page = kw.get("proxy_perftest_page") or ""
if page.endswith("-nav") and not os.environ.get("PERFTEST_RECORDING_LIST"):
RECORDING_LIST = Path(Path(__file__).parent, "nav_sites.json")
with RECORDING_LIST.open() as f:
site_list = json.load(f)
# currently can't record websites that require user interactions(Logins)
if kw.get("android"):
site_list = site_list["mobile"]
else:
site_list = site_list["desktop"]
def __should_record(test):
# If a test page selection was provided, only select those
# tests and exclude all the others
specified_tests = kw["proxy_perftest_page"]
if specified_tests is not None:
if test.get("name") == specified_tests:
if test.get("login"):
print(f"WARNING: You selected a login test: {test.get('name')}")
return True
else:
return False
# Only perform login recordings in automation or when
# RAPTOR_LOGINS is defined
record = False
if not test.get("login") or test.get("login-test"):
record = True
if not (
"MOZ_AUTOMATION" in os.environ or "RAPTOR_LOGINS" in os.environ
) and test.get("login-test"):
record = False
print(
f"Skipping login test `{test.get('name')}` "
f"because login info cannot be obtained."
)
# When pushing to Try, only attempt login recording using the
# taskcluster secrets that are associated with SCM level 1 as defined
# in `SCM_LVL_1_SITES`.
if test.get("login"):
if "MOZ_AUTOMATION" in os.environ.keys():
if (
os.environ.get("MOZ_SCM_LEVEL") == 1
and test.get("name") not in SCM_1_LOGIN_SITES
):
print(
f"Skipping login test `{test.get('name')}` "
f"Because SCM = `{os.environ.get('MOZ_SCM_LEVEL')}`"
f"and there is no secret available at this level"
)
return False
return True
elif "RAPTOR_LOGINS" in os.environ:
# Leave it to the user to have properly set up a local json file with
# the login websites of interest
return True
return record
sites = [test_site for test_site in site_list if __should_record(test_site)]
if not sites:
raise Exception("No tests were selected for recording!")
def next_site():
yield from sites
next_site = next_site()
# Set the number of test-iterations to the number of builds
kw["test_iterations"] = len(sites)
return kw
def before_runs(env):
print("Running before_runs")
add_options(env, options)
if next_site:
test_site = next(next_site)
global current_site # noqa: PLW0603
current_site = test_site.get("name")
print(f"Next site: {test_site}")
if env.get_arg("android"):
platform_name = "android"
app_name = env.get_arg("android-app-name").split(".")[-1]
else:
platform_name = platform.system().lower()
app_name = "firefox"
name = [
"mitm12",
platform_name,
"gve" if app_name == "geckoview_example" else app_name,
test_site["name"],
]
recording_file = f"{'-'.join(name)}.zip"
# Remove any leftover zip from a previous failed/interrupted run so
# mozproxy doesn't refuse to start with "Recording file already exists".
output_dir = env.get_arg("output")
if output_dir:
full_path = os.path.join(output_dir, recording_file)
if os.path.exists(full_path):
os.remove(full_path)
print(f"Removed existing recording: {full_path}")
env.set_arg("proxy-mode", "record")
env.set_arg(
"proxy-file",
recording_file,
)
add_options(env, options, overwrite=True)
add_option(env, "browsertime.url", test_site.get("test_url"))
add_option(env, "browsertime.screenshot", "true")
add_option(env, "browsertime.testName", test_site.get("name"))
add_option(env, "browsertime.testType", test_site.get("type", "pageload"))
add_option(
env, "browsertime.login", "true" if test_site.get("login") else "false"
)
prefs = test_site.get("preferences", {})
for pref, val in prefs.items():
add_option(env, "firefox.preference", f"{pref}:{val}")
# Add prefs that will attempt to remove cookie banners
add_option(
env, "firefox.preference", "cookiebanners.bannerClicking.enabled:true"
)
add_option(env, "firefox.preference", "cookiebanners.service.mode:2")
second_url = test_site.get("secondary_url", None)
if second_url:
add_option(env, "browsertime.secondary_url", second_url)
inject_deterministic = test_site.get("inject_deterministic", True)
env.set_arg("proxy-deterministic", inject_deterministic)
dismiss_cookie_prompt = test_site.get("dismiss_cookie_prompt", [])
if dismiss_cookie_prompt:
parsed_cmds = [
":::".join([str(i) for i in item])
for item in dismiss_cookie_prompt
if item
]
add_option(
env, "browsertime.dismiss_cookie_prompt", ";;;".join(parsed_cmds)
)
cmds = test_site.get("test_cmds", [])
if cmds:
parsed_cmds = [":::".join([str(i) for i in item]) for item in cmds if item]
add_option(env, "browsertime.commands", ";;;".join(parsed_cmds))
print(f"Recording {test_site.get('test_url')} to file: {recording_file}")
def on_exception(env, layer, exc):
print(f"Recording failed for site '{current_site}': {exc}")
failed_sites.append(current_site)
return True
def after_runs(env):
if not failed_sites:
return
print(f"Sites failed so far: {', '.join(failed_sites)}")
output_dir = env.get_arg("output")
if output_dir:
failure_log = os.path.join(output_dir, "recording_failures.txt")
with open(failure_log, "w") as f:
f.write("\n".join(failed_sites) + "\n")
print(f"Failure list written to {failure_log}")