blob: 423160aadee7c863be9b152d3849df5b762d7983 [file] [log] [blame]
# Copyright 2022 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import argparse
import io
import json
import pathlib
import unittest
import unittest.mock as mock
import pyfakefs.fake_filesystem_unittest
import crossbench as cb
from crossbench.cli import BrowserConfig, CrossBenchCLI, FlagGroupConfig
from . import mockbenchmark
class SysExitException(Exception):
def __init__(self):
super().__init__("sys.exit")
class TestCLI(mockbenchmark.BaseCrossbenchTestCase):
def run_cli(self, *args, raises=None):
with mock.patch("sys.stdout", new_callable=io.StringIO) as mock_stdout:
cli = mockbenchmark.MockCLI()
if raises:
with self.assertRaises(raises):
cli.run(args)
else:
cli.run(args)
return mock_stdout.getvalue()
def test_invalid(self):
with mock.patch("sys.exit", side_effect=SysExitException) as exit_mock:
self.run_cli(
"unknown subcommand", "--invalid flag", raises=SysExitException)
def test_describe(self):
stdout = self.run_cli("describe", "--json")
data = json.loads(stdout)
self.assertIn("benchmarks", data)
self.assertIn("probes", data)
self.assertIsInstance(data["benchmarks"], dict)
self.assertIsInstance(data["probes"], dict)
def test_help(self):
with mock.patch("sys.exit", side_effect=SysExitException) as exit_mock:
stdout = self.run_cli("--help", raises=SysExitException)
self.assertTrue(exit_mock.called)
exit_mock.assert_called_with(0)
self.assertGreater(len(stdout), 0)
def test_help_subcommand(self):
for benchmark in CrossBenchCLI.BENCHMARKS:
with mock.patch("sys.exit", side_effect=SysExitException()) as exit_mock:
stdout = self.run_cli(benchmark.NAME, "--help", raises=SysExitException)
self.assertTrue(exit_mock.called)
exit_mock.assert_called_with(0)
self.assertGreater(len(stdout), 0)
def test_invalid_probe(self):
with mock.patch("sys.exit", side_effect=SysExitException()) as exit_mock:
self.run_cli(
"loading", "--probe=invalid_probe_name", raises=SysExitException)
def test_basic_probe_setting(self):
with mock.patch.object(
cb.cli.CrossBenchCLI, "_get_browsers", return_value=self.browsers):
url = "http://test.com"
self.run_cli("loading", "--probe=v8.log", f"--urls={url}",
"--skip-checklist")
for browser in self.browsers:
self.assertListEqual([url], browser.url_list)
self.assertIn("--log", browser.js_flags)
def test_invalid_empty_probe_config_file(self):
config_file = pathlib.Path("/config.hjson")
config_file.touch()
with mock.patch.object(
cb.cli.CrossBenchCLI, "_get_browsers", return_value=self.browsers):
url = "http://test.com"
with self.assertRaises(ValueError):
self.run_cli("loading", f"--probe-config={config_file}",
f"--urls={url}", "--skip-checklist")
for browser in self.browsers:
self.assertListEqual([], browser.url_list)
self.assertNotIn("--log", browser.js_flags)
def test_empty_probe_config_file(self):
config_file = pathlib.Path("/config.hjson")
config_data = {"probes": {}}
with config_file.open("w") as f:
json.dump(config_data, f)
with mock.patch.object(
cb.cli.CrossBenchCLI, "_get_browsers", return_value=self.browsers):
url = "http://test.com"
self.run_cli("loading", f"--probe-config={config_file}", f"--urls={url}",
"--skip-checklist")
for browser in self.browsers:
self.assertListEqual([url], browser.url_list)
self.assertNotIn("--log", browser.js_flags)
def test_invalid_probe_config_file(self):
config_file = pathlib.Path("/config.hjson")
config_data = {"probes": {"invalid probe name": {}}}
with config_file.open("w") as f:
json.dump(config_data, f)
with mock.patch.object(
cb.cli.CrossBenchCLI, "_get_browsers", return_value=self.browsers):
url = "http://test.com"
with self.assertRaises(ValueError):
self.run_cli("loading", f"--probe-config={config_file}",
f"--urls={url}", "--skip-checklist")
for browser in self.browsers:
self.assertListEqual([], browser.url_list)
self.assertEqual(len(browser.js_flags), 0)
def test_probe_config_file(self):
config_file = pathlib.Path("/config.hjson")
js_flags = ["--log-foo", "--log-bar"]
config_data = {"probes": {"v8.log": {"js_flags": js_flags}}}
with config_file.open("w", encoding="utf-8") as f:
json.dump(config_data, f)
with mock.patch.object(
cb.cli.CrossBenchCLI, "_get_browsers", return_value=self.browsers):
url = "http://test.com"
self.run_cli("loading", f"--probe-config={config_file}", f"--urls={url}",
"--skip-checklist")
for browser in self.browsers:
self.assertListEqual([url], browser.url_list)
for flag in js_flags:
self.assertIn(flag, browser.js_flags)
class TestBrowserConfig(pyfakefs.fake_filesystem_unittest.TestCase):
EXAMPLE_CONFIG_PATH = pathlib.Path(
__file__).parent.parent / "browser.config.example.hjson"
def setUp(self):
# TODO: Move to separate common helper class
self.setUpPyfakefs(modules_to_reload=[cb, mockbenchmark])
mockbenchmark.MockChromeStable.setup_fs(self.fs)
mockbenchmark.MockChromeDev.setup_fs(self.fs)
mockbenchmark.MockChromeCanary.setup_fs(self.fs)
if cb.helper.platform.is_macos:
mockbenchmark.MockSafari.setup_fs(self.fs)
self.BROWSER_LOOKUP = {
"stable": (mockbenchmark.MockChromeStable,
mockbenchmark.MockChromeStable.BIN_PATH),
"dev":
(mockbenchmark.MockChromeDev, mockbenchmark.MockChromeDev.BIN_PATH),
"chrome-stable": (mockbenchmark.MockChromeStable,
mockbenchmark.MockChromeStable.BIN_PATH),
"chrome-dev":
(mockbenchmark.MockChromeDev, mockbenchmark.MockChromeDev.BIN_PATH),
}
def test_load_browser_config_template(self):
if not self.EXAMPLE_CONFIG_PATH.exists():
return
self.fs.add_real_file(self.EXAMPLE_CONFIG_PATH)
with self.EXAMPLE_CONFIG_PATH.open() as f:
config = BrowserConfig.load(
f, browser_lookup_override=self.BROWSER_LOOKUP)
self.assertIn("default", config.flag_groups)
self.assertGreaterEqual(len(config.flag_groups), 1)
self.assertGreaterEqual(len(config.variants), 1)
def test_flag_combination_invalid(self):
with self.assertRaises(Exception):
BrowserConfig(
{
"flags": {
"group1": {
"invalidFlag": [None, "", "v1"],
},
},
"browsers": {
"stable": {
"path": "stable",
"flags": ["group1", "group2"]
}
}
},
browser_lookup_override=self.BROWSER_LOOKUP)
def test_flag_combination_duplicate(self):
with self.assertRaises(AssertionError):
BrowserConfig(
{
"flags": {
"group1": {
"--duplicate-flag": [None, "", "v1"],
},
"group2": {
"--duplicate-flag": [None, "", "v1"],
}
},
"browsers": {
"stable": {
"path": "stable",
"flags": ["group1", "group2"]
}
}
},
browser_lookup_override=self.BROWSER_LOOKUP)
def test_unknown_path(self):
with self.assertRaises(Exception):
BrowserConfig({"browsers": {"stable": {"path": "path/does/not/exist",}}})
with self.assertRaises(Exception):
BrowserConfig({"browsers": {"stable": {"path": "chrome-unknown",}}})
def test_flag_combination(self):
config = BrowserConfig(
{
"flags": {
"group1": {
"--foo": [None, "", "v1"],
"--bar": [None, "", "v1"],
}
},
"browsers": {
"stable": {
"path": "stable",
"flags": ["group1"]
}
}
},
browser_lookup_override=self.BROWSER_LOOKUP)
self.assertEqual(len(config.variants), 3 * 3)
def test_no_flags(self):
config = BrowserConfig(
{"browsers": {
"stable": {
"path": "stable",
},
"dev": {
"path": "dev",
}
}},
browser_lookup_override=self.BROWSER_LOOKUP)
self.assertEqual(len(config.variants), 2)
def test_inline_flags(self):
with mock.patch.object(
cb.browsers.Chrome, "_extract_version", return_value="101.22.333.44"):
config = BrowserConfig(
{"browsers": {
"stable": {
"path": "stable",
"flags": ["--foo=bar"]
}
}})
self.assertEqual(len(config.variants), 1)
def test_inline_load_safari(self):
if not cb.helper.platform.is_macos:
return
with mock.patch.object(
cb.browsers.Safari, "_extract_version", return_value="16.0"):
config = BrowserConfig({"browsers": {"safari": {"path": "safari",}}})
self.assertEqual(len(config.variants), 1)
def test_flag_combination_with_fixed(self):
config = BrowserConfig(
{
"flags": {
"group1": {
"--foo": [None, "", "v1"],
"--bar": [None, "", "v1"],
"--always_1": "true",
"--always_2": "true",
"--always_3": "true",
}
},
"browsers": {
"stable": {
"path": "stable",
"flags": ["group1"]
}
}
},
browser_lookup_override=self.BROWSER_LOOKUP)
self.assertEqual(len(config.variants), 3 * 3)
def test_flag_group_combination(self):
config = BrowserConfig(
{
"flags": {
"group1": {
"--foo": [None, "", "v1"],
},
"group2": {
"--bar": [None, "", "v1"],
},
"group3": {
"--other": ["v1", "v2"],
}
},
"browsers": {
"stable": {
"path": "stable",
"flags": ["group1", "group2", "group3"]
}
}
},
browser_lookup_override=self.BROWSER_LOOKUP)
self.assertEqual(len(config.variants), 3 * 3 * 2)
class TestFlagGroupConfig(unittest.TestCase):
def parse(self, config_dict):
config = FlagGroupConfig("test", config_dict)
variants = list(config.get_variant_items())
return variants
def test_empty(self):
config = FlagGroupConfig("empty_name", dict())
self.assertEqual(config.name, "empty_name")
variants = list(config.get_variant_items())
self.assertEqual(len(variants), 0)
def test_single_flag(self):
variants = self.parse({"--foo": set()})
self.assertListEqual(variants, [
(),
])
variants = self.parse({"--foo": []})
self.assertListEqual(variants, [
(),
])
variants = self.parse({"--foo": (None,)})
self.assertListEqual(variants, [
(None,),
])
variants = self.parse({"--foo": ("",)})
self.assertEqual(len(variants), 1)
self.assertTupleEqual(
variants[0],
(("--foo", None),),
)
variants = self.parse({"--foo": (
"",
None,
)})
self.assertEqual(len(variants), 1)
self.assertTupleEqual(variants[0], (("--foo", None), None))
variants = self.parse({"--foo": (
"v1",
"v2",
"",
None,
)})
self.assertEqual(len(variants), 1)
self.assertTupleEqual(variants[0], (("--foo", "v1"), ("--foo", "v2"),
("--foo", None), None))
def test_two_flags(self):
variants = self.parse({"--foo": [], "--bar": []})
self.assertListEqual(variants, [(), ()])
variants = self.parse({"--foo": "a", "--bar": "b"})
self.assertEqual(len(variants), 2)
self.assertTupleEqual(variants[0], (("--foo", "a"),))
self.assertTupleEqual(variants[1], (("--bar", "b"),))
variants = self.parse({"--foo": ["a1", "a2"], "--bar": "b"})
self.assertEqual(len(variants), 2)
self.assertTupleEqual(variants[0], (
("--foo", "a1"),
("--foo", "a2"),
))
self.assertTupleEqual(variants[1], (("--bar", "b"),))
variants = self.parse({"--foo": ["a1", "a2"], "--bar": ["b1", "b2"]})
self.assertEqual(len(variants), 2)
self.assertTupleEqual(variants[0], (
("--foo", "a1"),
("--foo", "a2"),
))
self.assertTupleEqual(variants[1], (
("--bar", "b1"),
("--bar", "b2"),
))