blob: 04b1b1c89bd47f46ea6336c298bdcd5d6f26789c [file] [log] [blame]
# Copyright 2022 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import pathlib
import unittest
from unittest import mock
import hjson
from crossbench.env import (HostEnvironment, HostEnvironmentConfig,
ValidationError, ValidationMode)
from tests import test_helper
from tests.crossbench.base import CrossbenchFakeFsTestCase
class HostEnvironmentConfigTestCase(unittest.TestCase):
def test_combine_bool_value(self):
default = HostEnvironmentConfig()
self.assertIsNone(default.power_use_battery)
battery = HostEnvironmentConfig(power_use_battery=True)
self.assertTrue(battery.power_use_battery)
self.assertTrue(battery.merge(battery).power_use_battery)
self.assertTrue(default.merge(battery).power_use_battery)
self.assertTrue(battery.merge(default).power_use_battery)
power = HostEnvironmentConfig(power_use_battery=False)
self.assertFalse(power.power_use_battery)
self.assertFalse(power.merge(power).power_use_battery)
self.assertFalse(default.merge(power).power_use_battery)
self.assertFalse(power.merge(default).power_use_battery)
with self.assertRaises(ValueError):
power.merge(battery)
def test_combine_min_float_value(self):
default = HostEnvironmentConfig()
self.assertIsNone(default.cpu_min_relative_speed)
high = HostEnvironmentConfig(cpu_min_relative_speed=1)
self.assertEqual(high.cpu_min_relative_speed, 1)
self.assertEqual(high.merge(high).cpu_min_relative_speed, 1)
self.assertEqual(default.merge(high).cpu_min_relative_speed, 1)
self.assertEqual(high.merge(default).cpu_min_relative_speed, 1)
low = HostEnvironmentConfig(cpu_min_relative_speed=0.5)
self.assertEqual(low.cpu_min_relative_speed, 0.5)
self.assertEqual(low.merge(low).cpu_min_relative_speed, 0.5)
self.assertEqual(default.merge(low).cpu_min_relative_speed, 0.5)
self.assertEqual(low.merge(default).cpu_min_relative_speed, 0.5)
self.assertEqual(high.merge(low).cpu_min_relative_speed, 1)
def test_combine_max_float_value(self):
default = HostEnvironmentConfig()
self.assertIsNone(default.cpu_max_usage_percent)
high = HostEnvironmentConfig(cpu_max_usage_percent=100)
self.assertEqual(high.cpu_max_usage_percent, 100)
self.assertEqual(high.merge(high).cpu_max_usage_percent, 100)
self.assertEqual(default.merge(high).cpu_max_usage_percent, 100)
self.assertEqual(high.merge(default).cpu_max_usage_percent, 100)
low = HostEnvironmentConfig(cpu_max_usage_percent=0)
self.assertEqual(low.cpu_max_usage_percent, 0)
self.assertEqual(low.merge(low).cpu_max_usage_percent, 0)
self.assertEqual(default.merge(low).cpu_max_usage_percent, 0)
self.assertEqual(low.merge(default).cpu_max_usage_percent, 0)
self.assertEqual(high.merge(low).cpu_max_usage_percent, 0)
def test_parse_example_config_file(self):
example_config_file = pathlib.Path(
__file__).parent.parent / "config/doc/env.config.hjson"
if not example_config_file.exists():
raise unittest.SkipTest(f"Test file {example_config_file} does not exist")
with example_config_file.open(encoding="utf-8") as f:
data = hjson.load(f)
HostEnvironmentConfig(**data["env"])
class HostEnvironmentTestCase(CrossbenchFakeFsTestCase):
def setUp(self):
super().setUp()
self.mock_platform = mock.Mock()
self.mock_platform.processes.return_value = []
self.out_dir = pathlib.Path("results/current_benchmark_run_results")
self.fs.create_file(self.out_dir)
self.mock_runner = mock.Mock(
platform=self.mock_platform,
probes=[],
browsers=[],
out_dir=self.out_dir)
def test_instantiate(self):
env = HostEnvironment(self.mock_runner)
self.assertEqual(env.runner, self.mock_runner)
config = HostEnvironmentConfig()
env = HostEnvironment(self.mock_runner, config)
self.assertEqual(env.runner, self.mock_runner)
self.assertEqual(env.config, config)
def test_warn_mode_skip(self):
config = HostEnvironmentConfig()
env = HostEnvironment(self.mock_runner, config, ValidationMode.SKIP)
env.handle_warning("foo")
def test_warn_mode_fail(self):
config = HostEnvironmentConfig()
env = HostEnvironment(self.mock_runner, config, ValidationMode.THROW)
with self.assertRaises(ValidationError) as cm:
env.handle_warning("custom env check warning")
self.assertIn("custom env check warning", str(cm.exception))
def test_warn_mode_prompt(self):
config = HostEnvironmentConfig()
env = HostEnvironment(self.mock_runner, config, ValidationMode.PROMPT)
with mock.patch("builtins.input", return_value="Y") as cm:
env.handle_warning("custom env check warning")
cm.assert_called_once()
self.assertIn("custom env check warning", cm.call_args[0][0])
with mock.patch("builtins.input", return_value="n") as cm:
with self.assertRaises(ValidationError):
env.handle_warning("custom env check warning")
cm.assert_called_once()
self.assertIn("custom env check warning", cm.call_args[0][0])
def test_warn_mode_warn(self):
config = HostEnvironmentConfig()
env = HostEnvironment(self.mock_runner, config, ValidationMode.WARN)
with mock.patch("logging.warning") as cm:
env.handle_warning("custom env check warning")
cm.assert_called_once()
self.assertIn("custom env check warning", cm.call_args[0][0])
def test_validate_skip(self):
env = HostEnvironment(self.mock_runner, HostEnvironmentConfig(),
ValidationMode.SKIP)
env.validate()
def test_validate_warn(self):
env = HostEnvironment(self.mock_runner, HostEnvironmentConfig(),
ValidationMode.WARN)
with mock.patch("logging.warning") as cm:
env.validate()
cm.assert_not_called()
self.mock_platform.sh_stdout.assert_not_called()
self.mock_platform.sh.assert_not_called()
def test_validate_warn_no_probes(self):
env = HostEnvironment(self.mock_runner,
HostEnvironmentConfig(require_probes=True),
ValidationMode.WARN)
with mock.patch("logging.warning") as cm:
env.validate()
cm.assert_called_once()
self.mock_platform.sh_stdout.assert_not_called()
self.mock_platform.sh.assert_not_called()
def test_request_battery_power_on(self):
env = HostEnvironment(self.mock_runner,
HostEnvironmentConfig(power_use_battery=True),
ValidationMode.THROW)
self.mock_platform.is_battery_powered = True
env.validate()
self.mock_platform.is_battery_powered = False
with self.assertRaises(Exception) as cm:
env.validate()
self.assertIn("battery", str(cm.exception).lower())
def test_request_battery_power_off(self):
env = HostEnvironment(self.mock_runner,
HostEnvironmentConfig(power_use_battery=False),
ValidationMode.THROW)
self.mock_platform.is_battery_powered = True
with self.assertRaises(ValidationError) as cm:
env.validate()
self.assertIn("battery", str(cm.exception).lower())
self.mock_platform.is_battery_powered = False
env.validate()
def test_request_battery_power_off_conflicting_probe(self):
env = HostEnvironment(self.mock_runner,
HostEnvironmentConfig(power_use_battery=False),
ValidationMode.THROW)
self.mock_platform.is_battery_powered = False
mock_probe = mock.Mock()
mock_probe.configure_mock(BATTERY_ONLY=True, name="mock_probe")
self.mock_runner.probes = [mock_probe]
with self.assertRaises(ValidationError) as cm:
env.validate()
message = str(cm.exception).lower()
self.assertIn("mock_probe", message)
self.assertIn("battery", message)
mock_probe.BATTERY_ONLY = False
env.validate()
def test_request_is_headless_default(self):
env = HostEnvironment(
self.mock_runner,
HostEnvironmentConfig(browser_is_headless=HostEnvironmentConfig.IGNORE),
ValidationMode.THROW)
mock_browser = mock.Mock(platform=self.mock_platform)
self.mock_runner.browsers = [mock_browser]
mock_browser.viewport.is_headless = False
env.validate()
mock_browser.viewport.is_headless = True
env.validate()
def test_request_is_headless_true(self):
env = HostEnvironment(self.mock_runner,
HostEnvironmentConfig(browser_is_headless=True),
ValidationMode.THROW)
mock_browser = mock.Mock(platform=self.mock_platform)
self.mock_runner.browsers = [mock_browser]
self.mock_platform.has_display = True
mock_browser.viewport.is_headless = False
with self.assertRaises(ValidationError) as cm:
env.validate()
self.assertIn("is_headless", str(cm.exception))
self.mock_platform.has_display = False
with self.assertRaises(ValidationError) as cm:
env.validate()
self.mock_platform.has_display = True
mock_browser.viewport.is_headless = True
env.validate()
self.mock_platform.has_display = False
env.validate()
def test_request_is_headless_false(self):
env = HostEnvironment(self.mock_runner,
HostEnvironmentConfig(browser_is_headless=False),
ValidationMode.THROW)
mock_browser = mock.Mock(platform=self.mock_platform)
self.mock_runner.browsers = [mock_browser]
self.mock_platform.has_display = True
mock_browser.viewport.is_headless = False
env.validate()
self.mock_platform.has_display = False
with self.assertRaises(ValidationError) as cm:
env.validate()
self.mock_platform.has_display = True
mock_browser.viewport.is_headless = True
with self.assertRaises(ValidationError) as cm:
env.validate()
self.assertIn("is_headless", str(cm.exception))
def test_results_dir_single(self):
env = HostEnvironment(self.mock_runner)
with mock.patch("logging.warning") as cm:
env.validate()
cm.assert_not_called()
def test_results_dir_non_existent(self):
self.mock_runner.out_dir = pathlib.Path("does/not/exist")
env = HostEnvironment(self.mock_runner)
with mock.patch("logging.warning") as cm:
env.validate()
cm.assert_not_called()
def test_results_dir_many(self):
# Create fake test result dirs:
for i in range(30):
(self.out_dir.parent / str(i)).mkdir()
env = HostEnvironment(self.mock_runner)
with mock.patch("logging.warning") as cm:
env.validate()
cm.assert_called_once()
def test_results_dir_too_many(self):
# Create fake test result dirs:
for i in range(100):
(self.out_dir.parent / str(i)).mkdir()
env = HostEnvironment(self.mock_runner)
with mock.patch("logging.error") as cm:
env.validate()
cm.assert_called_once()
def test_check_installed_missing(self):
def which_none(_):
return None
self.mock_platform.which = which_none
env = HostEnvironment(self.mock_runner)
with self.assertRaises(ValidationError) as cm:
env.check_installed(["custom_binary"])
self.assertIn("custom_binary", str(cm.exception))
with self.assertRaises(ValidationError) as cm:
env.check_installed(["custom_binary_a", "custom_binary_b"])
self.assertIn("custom_binary_a", str(cm.exception))
self.assertIn("custom_binary_b", str(cm.exception))
def test_check_installed_partially_missing(self):
def which_custom(binary):
if binary == "custom_binary_b":
return "/bin/custom_binary_b"
return None
self.mock_platform.which = which_custom
env = HostEnvironment(self.mock_runner)
env.check_installed(["custom_binary_b"])
with self.assertRaises(ValidationError) as cm:
env.check_installed(["custom_binary_a", "custom_binary_b"])
self.assertIn("custom_binary_a", str(cm.exception))
self.assertNotIn("custom_binary_b", str(cm.exception))
class ValidationModeTestCase(unittest.TestCase):
def test_construct(self):
self.assertIs(ValidationMode("throw"), ValidationMode.THROW)
self.assertIs(ValidationMode("THROW"), ValidationMode.THROW)
self.assertIs(ValidationMode("prompt"), ValidationMode.PROMPT)
self.assertIs(ValidationMode("PROMPT"), ValidationMode.PROMPT)
self.assertIs(ValidationMode("warn"), ValidationMode.WARN)
self.assertIs(ValidationMode("WARN"), ValidationMode.WARN)
self.assertIs(ValidationMode("skip"), ValidationMode.SKIP)
self.assertIs(ValidationMode("SKIP"), ValidationMode.SKIP)
if __name__ == "__main__":
test_helper.run_pytest(__file__)