blob: 1fcfb137e4a80897882fb4f2588f676b3fcacc65 [file]
# Copyright 2023 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from __future__ import annotations
import contextlib
import io
import json
from typing import TYPE_CHECKING
from unittest import mock
import pytest
import crossbench.browsers.all as all_browsers
from crossbench import plt
from crossbench.cli.cli import CrossBenchCLI
from tests import test_helper
from tests.end2end.conftest import mock_patch_chrome_stable
if TYPE_CHECKING:
import pathlib
from tests.test_helper import TestEnv
class SysExitException(Exception):
def __init__(self):
super().__init__("sys.exit")
def _run_cli(*args: str,
extra_flags: tuple[str, ...] = (),
test_env: TestEnv | None = None,
auto_headless: bool = False) -> tuple[CrossBenchCLI, io.StringIO]:
if test_env is not None:
args += (f"--out-dir={test_env.results_dir}", *test_env.cq_flags)
if auto_headless and not plt.PLATFORM.has_display and ("--headless"
not in args):
args += ("--headless",)
args = tuple(arg for arg in args if not arg.startswith("--viewport="))
args += extra_flags
cli = CrossBenchCLI()
with contextlib.redirect_stdout(io.StringIO()) as stdout, mock.patch(
"sys.exit", side_effect=SysExitException):
cli.run(args)
return cli, stdout
def _get_browser_dirs(results_dir: pathlib.Path) -> list[pathlib.Path]:
assert results_dir.is_dir()
browser_dirs = []
for path in results_dir.iterdir():
if not path.is_dir():
continue
if path.is_symlink():
continue
if path.name in ("runs", "sessions"):
continue
browser_dirs.append(path)
return browser_dirs
def _get_v8_log_files(results_dir: pathlib.Path) -> list[pathlib.Path]:
return list(results_dir.glob("**/*-v8.log"))
@pytest.mark.xdist_group("end2end-benchmark")
def test_speedometer_2_0(test_env: TestEnv, browser_path) -> None:
# - Speedometer 2.0
# - Speedometer --iterations flag
# - Tracing probe with inline args
# - --browser-config
with pytest.raises(SysExitException):
_run_cli("speedometer_2.0", "--help")
_run_cli("describe", "benchmark", "speedometer_2.0")
browser_config = test_env.root_dir / "config/doc/browser.config.hjson"
assert browser_config.is_file()
results_dir = test_env.results_dir
assert not results_dir.exists()
with mock_patch_chrome_stable(browser_path):
_run_cli(
"sp_2.0",
f"--browser-config={browser_config}",
"--iterations=2",
"--stories=jQuery-TodoMVC",
"--env-validation=skip",
"--probe=tracing:{preset:'minimal'}",
test_env=test_env,
auto_headless=True)
@pytest.mark.xdist_group("end2end-benchmark")
def test_speedometer_2_1(test_env: TestEnv, test_chrome_name) -> None:
# - Speedometer 2.1
# - Story filtering with regexp
# - V8 probes
# - minimal splashscreen
# - inline probe arguments
with pytest.raises(SysExitException):
_run_cli("speedometer_2.1", "--help")
_run_cli("describe", "benchmark", "speedometer_2.1")
_run_cli(
"sp2.1",
f"--browser={test_chrome_name}",
"--splashscreen=minimal",
"--iterations=2",
"--env-validation=skip",
"--stories=.*Vanilla.*",
# V8 --prof doesn't always work on linux, skip it.
"--probe=v8.log:"
"{log_all:false, js_flags:['--log-maps'], prof:false, profview:false}",
"--probe=v8.turbolizer",
"--debug",
test_env=test_env,
auto_headless=True)
browser_dirs = _get_browser_dirs(test_env.results_dir)
assert len(browser_dirs) == 1
v8_log_files = _get_v8_log_files(test_env.results_dir)
assert len(v8_log_files) > 1
@pytest.mark.skip_mock
@pytest.mark.flaky(retries=3, delay=5)
def test_speedometer_2_1_custom_chrome_download(test_env: TestEnv) -> None:
# - Custom chrome version downloads
# - headless
# Flaky due to downloading live custom builds.
# TODO: speed up --browser=chrome-M111 and add it.
_run_cli(
"sp2.1",
"--browser=chrome-M113-any",
"--browser=chrome-111.0.5563.110",
"--browser=chrome-latest-dev",
"--headless",
"--iterations=1",
"--env-validation=skip",
"--stories=.*Vanilla.*",
"--debug",
test_env=test_env)
browser_dirs = _get_browser_dirs(test_env.results_dir)
assert len(browser_dirs) == 3
v8_log_files = _get_v8_log_files(test_env.results_dir)
assert not v8_log_files
@pytest.mark.xdist_group("end2end-benchmark")
def test_speedometer_2_1_chrome_safari(test_env: TestEnv, driver_path,
test_chrome_name) -> None:
# - Speedometer 3
# - Merging stories over multiple iterations and browsers
# - Testing safari
# - --verbose flag
# - no splashscreen
# This fails on the CQ bot, so make sure we skip it there:
if driver_path:
pytest.skip("Skipping test on CQ.")
if not plt.PLATFORM.is_macos:
return
platform = plt.PLATFORM
if not platform.is_macos and (not platform.exists(
all_browsers.Safari.default_path(platform))):
pytest.skip("Test requires Safari, skipping on non macOS devices.")
_run_cli(
"sp2.1",
f"--browser={test_chrome_name}",
"--browser=safari",
"--splashscreen=none",
"--iterations=1",
"--repeat=2",
"--env-validation=skip",
"--verbose",
"--stories=.*React.*",
test_env=test_env,
auto_headless=True)
browser_dirs = _get_browser_dirs(test_env.results_dir)
assert len(browser_dirs) == 2
v8_log_files = _get_v8_log_files(test_env.results_dir)
assert not v8_log_files
@pytest.mark.xdist_group("end2end-benchmark")
def test_jetstream_2_0(test_env: TestEnv, test_chrome_name) -> None:
# - jetstream 2.0
# - merge / run separate stories
# - custom multiple --js-flags
# - custom viewport
# - quiet flag
with pytest.raises(SysExitException):
_run_cli("jetstream_2.0", "--help")
_run_cli("describe", "--json", "benchmark", "jetstream_2.0")
_run_cli(
"jetstream_2.0",
f"--browser={test_chrome_name}",
"--separate",
"--repeat=2",
"--env-validation=skip",
"--viewport=maximised",
"--stories=.*date-format.*",
"--quiet",
"--js-flags=--log,--log-opt,--log-deopt",
extra_flags=(
"--",
"--no-sandbox",
),
test_env=test_env,
auto_headless=True)
browser_dirs = _get_browser_dirs(test_env.results_dir)
assert len(browser_dirs) == 1
@pytest.mark.xdist_group("end2end-benchmark")
def test_jetstream_2_1(test_env: TestEnv, test_chrome_name) -> None:
# - jetstream 2.1
# - custom --time-unit
# - explicit single story
# - custom splashscreen
# - custom viewport
# - --probe-config
with pytest.raises(SysExitException):
_run_cli("jetstream_2.1", "--help")
_run_cli("describe", "benchmark", "jetstream_2.1")
probe_config = test_env.root_dir / "config/doc/probe.config.hjson"
assert probe_config.is_file()
_run_cli(
"jetstream_2.1",
f"--browser={test_chrome_name}",
"--env-validation=skip",
"--splashscreen=http://google.com",
"--viewport=900x800",
"--stories=Box2D",
"--time-unit=0.9",
f"--probe-config={probe_config}",
"--throw",
test_env=test_env,
auto_headless=True)
browser_dirs = _get_browser_dirs(test_env.results_dir)
assert len(browser_dirs) == 1
v8_log_files = _get_v8_log_files(test_env.results_dir)
assert len(v8_log_files) > 1
@pytest.mark.xdist_group("end2end-benchmark")
def test_jetstream_2_2(test_env: TestEnv, test_chrome_name) -> None:
# - jetstream 2.2
# - custom --time-unit
# - explicit single story
# - custom splashscreen
# - custom viewport
# - --probe-config
with pytest.raises(SysExitException):
_run_cli("jetstream_2.2", "--help")
_run_cli("describe", "benchmark", "jetstream_2.2")
probe_config = test_env.root_dir / "config/doc/probe.config.hjson"
assert probe_config.is_file()
_run_cli(
"jetstream_2.2",
f"--browser={test_chrome_name}",
"--env-validation=skip",
"--splashscreen=http://google.com",
"--viewport=900x800",
"--stories=Box2D",
"--time-unit=0.9",
f"--probe-config={probe_config}",
"--throw",
test_env=test_env,
auto_headless=True)
browser_dirs = _get_browser_dirs(test_env.results_dir)
assert len(browser_dirs) == 1
v8_log_files = _get_v8_log_files(test_env.results_dir)
assert len(v8_log_files) > 1
@pytest.mark.xdist_group("end2end-benchmark")
def test_loading(test_env: TestEnv, test_chrome_name) -> None:
# - loading using named pages with timeouts
# - custom cooldown time
# - custom viewport
# - performance.mark probe
with pytest.raises(SysExitException):
_run_cli("loading", "--help")
_run_cli("describe", "benchmark", "loading")
_run_cli(
"loading",
f"--browser={test_chrome_name}",
"--env-validation=skip",
"--viewport=headless",
"--stories=wikipedia",
"--cool-down-time=2.5",
"--probe=performance.entries",
test_env=test_env,
auto_headless=True)
browser_dirs = _get_browser_dirs(test_env.results_dir)
assert len(browser_dirs) == 1
def test_loading_page_config(test_env: TestEnv, test_chrome_name) -> None:
# - loading with config file
page_config = test_env.root_dir / "config/doc/page.config.hjson"
assert page_config.is_file()
_run_cli(
"loading",
f"--browser={test_chrome_name}",
"--env-validation=skip",
f"--page-config={page_config}",
"--probe=performance.entries",
"--no-splash",
"--cool-down-time=0",
"--throw",
test_env=test_env,
auto_headless=True)
@pytest.mark.xdist_group("end2end-benchmark")
def test_loading_playback_urls(test_env: TestEnv, test_chrome_name) -> None:
# - loading using url
# - combined pages and --playback controller
_run_cli(
"loading",
f"--browser={test_chrome_name}",
"--env-validation=skip",
"--verbose-driver",
"--playback=5.3s",
"--viewport=fullscreen",
"--stories=http://google.com,0.5,http://bing.com,0.4",
"--probe=performance.entries",
test_env=test_env,
auto_headless=True)
@pytest.mark.xdist_group("end2end-benchmark")
@pytest.mark.skipif(
plt.PLATFORM.is_win, reason="Fails on Windows; crbug.com/463323491")
def test_loading_playback(test_env: TestEnv, test_chrome_name) -> None:
# - loading using named pages with timeouts
# - separate pages and --playback controller
# - viewport-size via chrome flag
args = [
"loading",
f"--browser={test_chrome_name}",
"--env-validation=skip",
"--playback=5.3s",
"--separate",
"--stories=twitter,2,facebook,0.4",
"--probe=performance.entries",
"--debug",
]
if not plt.PLATFORM.is_linux:
args.extend([
"--",
"--window-size=900,500",
"--window-position=150,150",
])
_run_cli(*args, test_env=test_env, auto_headless=True)
@pytest.mark.skipif(
not plt.PLATFORM.has_display, reason="Firefox cannot run headless")
@pytest.mark.xdist_group("end2end-benchmark")
def test_loading_playback_firefox(test_env: TestEnv, test_chrome_name) -> None:
# - loading using named pages with timeouts
# - --playback controller
# - Firefox
platform = plt.PLATFORM
try:
if not platform.exists(all_browsers.Firefox.default_path(platform)):
pytest.skip("Test requires Firefox.")
except Exception: # noqa: BLE001
pytest.skip("Test requires Firefox.")
_run_cli(
"loading",
f"--browser={test_chrome_name}",
"--browser=ff",
"--env-validation=skip",
"--playback=2x",
"--stories=twitter,1,facebook,0.4",
"--probe=performance.entries",
test_env=test_env,
auto_headless=True)
browser_dirs = _get_browser_dirs(test_env.results_dir)
assert len(browser_dirs) == 2
@pytest.mark.xdist_group("end2end-benchmark")
@pytest.mark.skipif(
plt.PLATFORM.is_win,
reason="stdout forwarding is not always supported on windows")
def test_chrome_stdout_logging(test_env: TestEnv) -> None:
# - loading inline hjson
# - executing custom JS
# - validating chrome browser stdout using generated content to make sure it
# is not just in the driver log as part of the script source.
out_dir = test_env.results_dir
assert not list(out_dir.glob("**/*.stdout.log"))
page_config = {
"pages": {
"STDOUT TEST": [{
"action": "get",
"url": "https://www.google.com"
}, {
"action": "js",
"script": "%DebugPrint('TestOutput'.repeat(3))"
}]
}
}
_run_cli(
"loading",
"--browser=chrome",
"--env-validation=skip",
"--js-flags=--allow-natives-syntax",
"--fast",
f"--page-config={json.dumps(page_config)}",
test_env=test_env,
auto_headless=True)
stdout_files = list(out_dir.glob("**/*.stdout.log"))
assert len(stdout_files) == 1
stdout_file = stdout_files[0]
test_output = "TestOutput" * 3
assert test_output in stdout_file.read_text()
@pytest.mark.xdist_group("end2end-benchmark")
def test_devtools_frontend_all(test_env: TestEnv, test_chrome_name,
test_chrome_version) -> None:
if test_chrome_version < 144:
pytest.skip("Skipping test for Chrome versions below 144; "
"CDP command may not be supported")
pytest.skip("Skip until b/487909749 is addressed")
_run_cli(
"devtools_frontend",
f"--browser={test_chrome_name}",
test_env=test_env,
auto_headless=True)
browser_dirs = _get_browser_dirs(test_env.results_dir)
assert len(browser_dirs) == 1
@pytest.mark.xdist_group("end2end-benchmark")
def test_devtools_frontend_selection(test_env: TestEnv, test_chrome_name,
test_chrome_version) -> None:
if test_chrome_version < 144:
pytest.skip("Skipping test for Chrome versions below 144; "
"CDP command may not be supported")
pytest.skip("Skip until b/487909749 is addressed")
_run_cli(
"devtools_frontend",
f"--browser={test_chrome_name}",
"--sites=blank,speedometertests",
"--panels=elements,resources",
test_env=test_env,
auto_headless=True)
browser_dirs = _get_browser_dirs(test_env.results_dir)
assert len(browser_dirs) == 1
if __name__ == "__main__":
test_helper.run_pytest(__file__)