blob: 59fa4fa21906369d0479ed65b041201585584577 [file] [log] [blame]
#! vpython3
import argparse
import enum
from pathlib import Path
import shlex
import subprocess
import sys
import textwrap
import urllib.request
import urllib.error
CANDIDATES = ("chr", "chrome", "chromium")
def find_chrome_checkout(start: Path):
for parent in start.absolute().parents:
for candidate_name in CANDIDATES:
candidate = parent / candidate_name / "src/tools/perf/cb"
print(candidate)
if candidate.exists():
return candidate
return None
SCRIPT_PATH = Path(__file__).absolute()
DEFAULT_CROSSBENCH_BIN = find_chrome_checkout(SCRIPT_PATH.parents[1])
class TestMode(enum.StrEnum):
FAST = "fast"
DEFAULT = "default"
COMPLETE = "complete"
def main():
parser = argparse.ArgumentParser(
description="Test server interaction with optional crossbench execution.")
parser.add_argument("url", nargs='?', help="URL for the test server")
parser.add_argument("--url", dest="url", help="URL for the test server")
parser.add_argument(
"crossbench_bin",
nargs='?',
type=Path,
help=f"Path to the crossbench binary",
default=DEFAULT_CROSSBENCH_BIN)
parser.add_argument(
"--crossbench",
"--cb",
dest="crossbench_bin",
type=Path,
help=f"Path to the crossbench binary , default={DEFAULT_CROSSBENCH_BIN}")
test_mode_group = parser.add_argument_group(
"Test Mode",
textwrap.dedent(""""
There are 3 test modes,
where --default provides the best test to duration ratio.
In all modes workloads are configured for functional testing rather
than accurate performance numbers. Concretely we lower the total
iteration count and avoid slow sanity checks.
"""))
test_mode_exclusive_group = test_mode_group.add_mutually_exclusive_group()
test_mode_exclusive_group.add_argument(
"--fast",
help=("Select a fast subset of stories "
"for all workloads for quick testing."),
dest="test_mode",
action="store_const",
const=TestMode.FAST,
default=TestMode.DEFAULT)
test_mode_exclusive_group.add_argument(
"--default",
help=("Select a fast subset of stories for non-stable workloads, "
"and all stories for the stable versions."),
dest="test_mode",
action="store_const",
const=TestMode.DEFAULT,
)
test_mode_exclusive_group.add_argument(
"--complete",
"--slow",
help=("Select all stories for all workload versions "
"for exhaustive testing."),
dest="test_mode",
action="store_const",
const=TestMode.COMPLETE)
args = parser.parse_args()
def fail(message):
print(message)
print()
parser.print_usage()
sys.exit(1)
if not args.crossbench_bin:
fail("Could not find crossbench. "
"Please explicitly provide a path to go/crossbench.")
if not args.crossbench_bin.exists():
fail(f"{args.crossbench_bin} does not exist.")
if not args.url:
fail("Missing stage/deployment URL")
if not is_url_reachable(args.url):
fail(f"URL {repr(args.url)} is not reachable.")
run_tests(args.url, args.crossbench_bin, args.test_mode)
def is_url_reachable(url):
try:
with urllib.request.urlopen(url) as response:
return True
except (urllib.error.URLError, ValueError):
return False
def split_version_string(value: str):
return tuple(map(int, value.split(".")))
RED = "\033[31m"
YELLOW = "\033[33m"
RESET = "\033[0m"
def sh(*cmds):
print(YELLOW, shlex.join(map(str, cmds)), RESET)
process = subprocess.Popen(
cmds,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
stdout, stderr = process.communicate()
if process.returncode != 0:
print(f"{RED}FAILURE:")
print("STDOUT:")
print(stdout.decode("utf-8").strip())
print("STDERR:")
print(stderr.decode("utf-8").strip())
print(RESET)
sys.exit(process.returncode)
def run_tests(url: str, crossbench_bin: Path, test_mode: TestMode):
test_speedometer(url, crossbench_bin, test_mode)
test_jetstream(url, crossbench_bin, test_mode)
# TODO(cbruni): enable motionmark once crossbench stories selection is fixed on 1.3
# test_motionmark(url, crossbench_bin, test_mode)
# TODO(cbruni): add DOM ops workload
# TODO(cbruni): add memory workload test
SPEEDOMETER_VERSIONS = (
("2.1", "v2.0"),
("2.1", "v2.1"),
("2.1", "v2.1-custom"),
("3.0", "v3.0"),
("3.0", "v3.0-custom"),
("3.1", "v3.1"),
("3.1", "v3.1-custom"),
("main", "main"),
("main", "main-custom"),
)
SPEEDOMETER_STABLE_PATH = "v3.0"
def test_speedometer(url, crossbench_bin, test_mode: TestMode):
print("SPEEDOMETER:")
for version, path in SPEEDOMETER_VERSIONS:
extra_args = []
if test_mode == TestMode.FAST:
extra_args = ["--stories=.*React.*"]
elif test_mode == TestMode.DEFAULT and path == SPEEDOMETER_STABLE_PATH:
# Run the complete stable version.
pass
sh(crossbench_bin, f"speedometer_{version}",
f"--url={url}/speedometer/{path}", "--fast", "--iterations=1",
*extra_args)
JETSTREAM_VERSIONS = (
# v1.1 is not supported by crossbench.
# ("1.1", "v1.1"),
("2.1", "v2.1"),
("2.1", "v2.1-custom"),
("2.2", "v2.2"),
("2.2", "v2.2-custom"),
# TODO(cbruni): enable main once available in crossbench.
#("main", "main"),
)
JETSTREAM_STABLE_PATH = "v2.2"
def test_jetstream(url, crossbench_bin, test_mode: TestMode):
print("JETSTREAM:")
for version, path in JETSTREAM_VERSIONS:
extra_args = []
if test_mode == TestMode.FAST:
extra_args = ["--stories=.*richards.*"]
elif test_mode == TestMode.DEFAULT:
# TODO(cbruni): create fast preflight testing for the stable version.
# Run the complete stable version.
if path != JETSTREAM_STABLE_PATH:
extra_args = ["--stories=.*richards.*"]
sh(crossbench_bin, f"jetstream_{version}", f"--url={url}/jetstream/{path}",
"--fast", *extra_args)
MOTIONMARK_VERSIONS_FAST = (("1.3.1", "v1.3.1"),)
MOTIONMARK_VERSIONS_DEFAULT = (
("1.3", "v1.3"),
# TODO(cbruni): enable main once available in crossbench
# ("main", "main"),
) + MOTIONMARK_VERSIONS_FAST
MOTIONMARK_VERSIONS_COMPLETE = (
("1.0", "v1.0"),
("1.1", "v1.1"),
("1.2", "v1.2"),
) + MOTIONMARK_VERSIONS_DEFAULT
MOTIONMARK_STABLE_VERSION = "v1.3.1"
def test_motionmark(url, crossbench_bin, test_mode: TestMode):
print("MOTIONMARK:")
# Motionmark is too slow, let's only use a minimal version subset
# https://crbug.com/400746865 hopefully will address this.
versions = MOTIONMARK_VERSIONS_DEFAULT
if test_mode is TestMode.FAST:
versions = MOTIONMARK_VERSIONS_FAST
elif test_mode is TestMode.COMPLETE:
versions = MOTIONMARK_VERSIONS_COMPLETE
versions = sorted(versions, key=lambda pair: split_version_string(pair[0]))
extra_args = []
# TODO(cbruni): create fast preflight testing for the stable version
if test_mode in (TestMode.FAST, TestMode.DEFAULT):
extra_args = ["--stories=.*Canvas Arc.*"]
for version, path in versions:
sh(crossbench_bin, f"motionmark_{version}",
f"--url={url}/motionmark/{path}", "--fast", *extra_args)
if __name__ == "__main__":
main()