blob: d9e8fc1cb1c6e5b60c538c168e3d92dac47557da [file] [log] [blame]
# Copyright 2025 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 json
from typing import Final
from unittest import mock
from crossbench.cli.config.flags import FlagsConfig
from crossbench.pinpoint.config import PinpointTryJobConfig, VariantConfig
from crossbench.pinpoint.list_builds import Build
from tests import test_helper
from tests.crossbench.pinpoint.requests_mixin import MockRequestsMixin
_TEST_PATCH: Final[
str] = "https://chromium-review.googlesource.com/c/crossbench/+/12345"
_TEST_PATCH2: Final[
str] = "https://chromium-review.googlesource.com/c/crossbench/+/111/2"
class VariantConfigTest(MockRequestsMixin):
_get_auth_session_patch_target = "crossbench.pinpoint.auth.get_auth_session"
def setUp(self):
super().setUp()
self.mock_fetch_builds = self.enterContext(
mock.patch("crossbench.pinpoint.config.fetch_builds"))
self.mock_fetch_builds.return_value = [
Build(commit="aaaabbbb", date="2025-11-01 00:00:00"),
]
def test_parse_variant(self):
variant = VariantConfig.parse(
json.dumps({
"commit": "abcd1234",
"patch": _TEST_PATCH,
"flags": "--test-flag --js-flags=--base-js-flag",
}))
self.assertEqual(variant.commit, "abcd1234")
self.assertEqual(variant.patch, _TEST_PATCH)
self.assertDictEqual(variant.flags_as_dict(), {
"--test-flag": None,
"--js-flags": "--base-js-flag"
})
def test_parse_variant_default(self):
variant = VariantConfig.parse("{}")
self.assertEqual(variant.commit, "HEAD")
self.assertIsNone(variant.patch)
self.assertIsNone(variant.flags_as_str())
self.assertDictEqual(variant.flags_as_dict(), {})
def test_parse_commit(self):
self.assertEqual(VariantConfig.parse_commit("HEAD"), "HEAD")
self.assertEqual(VariantConfig.parse_commit("-HEAD"), "HEAD")
self.assertEqual(VariantConfig.parse_commit(""), "HEAD")
self.assertEqual(VariantConfig.parse_commit("recent"), "recent")
self.assertEqual(VariantConfig.parse_commit("abcdef00"), "abcdef00")
self.assertEqual(VariantConfig.parse_commit("1234ABCD"), "1234abcd")
with self.assertRaises(ValueError):
VariantConfig.parse_commit("invalid")
with self.assertRaises(ValueError):
VariantConfig.parse_commit("1234567")
with self.assertRaises(ValueError):
VariantConfig.parse_commit("1" * 41)
def test_parse_patch(self):
self.assertEqual(VariantConfig.parse_patch(_TEST_PATCH), _TEST_PATCH)
with self.assertRaises(ValueError):
VariantConfig.parse_patch("invalid")
def test_override_commit(self):
config = VariantConfig()
config.override_commit("abcdef00", bot="test_bot")
self.assertEqual(config.commit, "abcdef00")
config.override_commit(None, bot="test_bot")
self.assertEqual(config.commit, "abcdef00")
config.override_commit("recent", bot="test_bot")
self.assertEqual(config.commit, "aaaabbbb")
self.mock_fetch_builds.assert_called_once_with("test_bot")
def test_override_patch(self):
config = VariantConfig()
config.override_patch(_TEST_PATCH)
self.assertEqual(config.patch, _TEST_PATCH)
config.override_patch(None)
self.assertEqual(config.patch, _TEST_PATCH)
def test_override_empty_flags(self):
config = VariantConfig()
config.override_flags(
js_flags="--js-flag1,--js-flag2",
enable_features="enablefeature1,enablefeature2",
disable_features="disablefeature1,disablefeature2")
self.assertDictEqual(
config.flags_as_dict(), {
"--js-flags": "--js-flag1,--js-flag2",
"--enable-features": "enablefeature1,enablefeature2",
"--disable-features": "disablefeature1,disablefeature2",
})
def test_override_existing_flags(self):
config = VariantConfig.parse(
"{flags: '--standalone --js-flags=--js-flag0 "
"--enable-features=enablefeature0 --disable-features=disablefeature0'}")
config.override_flags(
js_flags="--js-flag1,--js-flag2",
enable_features="enablefeature1,enablefeature2",
disable_features="disablefeature1,disablefeature2")
self.assertDictEqual(
config.flags_as_dict(), {
"--standalone": None,
"--js-flags": "--js-flag1,--js-flag2",
"--enable-features": "enablefeature1,enablefeature2",
"--disable-features": "disablefeature1,disablefeature2",
})
def test_override_with_none_flags(self):
config = VariantConfig.parse(
"{flags: '--standalone --js-flags=--js-flag0 "
"--enable-features=enablefeature0 --disable-features=disablefeature0'}")
config.override_flags()
self.assertDictEqual(
config.flags_as_dict(), {
"--standalone": None,
"--js-flags": "--js-flag0",
"--enable-features": "enablefeature0",
"--disable-features": "disablefeature0",
})
class PinpointTryJobConfigTest(MockRequestsMixin):
_get_auth_session_patch_target = "crossbench.pinpoint.auth.get_auth_session"
def setUp(self):
super().setUp()
self.mock_fetch_benchmarks = self.enterContext(
mock.patch("crossbench.pinpoint.config.fetch_benchmarks"))
self.mock_fetch_benchmarks.return_value = ["test_benchmark"]
self.mock_fetch_bots = self.enterContext(
mock.patch("crossbench.pinpoint.config.fetch_bots"))
self.mock_fetch_bots.return_value = ["test_bot"]
self.mock_fetch_stories = self.enterContext(
mock.patch("crossbench.pinpoint.config.fetch_stories"))
self.mock_fetch_stories.return_value = ["test_story"]
self.mock_fetch_builds = self.enterContext(
mock.patch("crossbench.pinpoint.config.fetch_builds"))
self.mock_fetch_builds.return_value = [
Build(commit="aaaabbbb", date="2025-11-02 00:00:00"),
]
self.mock_show_warnings = self.enterContext(
mock.patch("crossbench.pinpoint.config.show_warnings"))
self.response_dict = {
"comparison_mode": "try",
"job_id": "1234567890",
"arguments": {
"comparison_mode": "try",
"target": "performance_test_suite",
"base_git_hash": "HEAD",
"end_git_hash": "HEAD",
"initial_attempt_count": "20",
"configuration": "win-11-perf",
"benchmark": "speedometer3",
"story": "Speedometer3",
"story_tags": "",
"chart": "",
"extra_test_args": "",
"commit": "on,on",
"base_patch": "",
"experiment_patch": "",
"base_extra_args": "",
"experiment_extra_args": "",
"project": "",
"bug_id": "",
"batch_id": ""
},
"name": "Try job on win-11-perf/speedometer3",
}
def test_parse_minimal_config(self):
config = PinpointTryJobConfig.parse_and_override(
config=json.dumps({
"benchmark": "test_benchmark",
"bot": "test_bot",
"story": "test_story",
}))
self.assertEqual(
config,
PinpointTryJobConfig(
benchmark="test_benchmark",
bot="test_bot",
story="test_story",
base=VariantConfig(),
experiment=VariantConfig(),
))
def test_parse_all_fields(self):
config = PinpointTryJobConfig.parse_and_override(
config=json.dumps({
"benchmark": "test_benchmark",
"bot": "test_bot",
"story": "test_story",
"story_tags": "tag1,tag2",
"repeat": 42,
"bug": 67890,
"base": {
"commit": "abcdef00",
"patch": _TEST_PATCH,
"flags": "--js-flags=--base-js-flag",
},
"experiment": {
"commit": "aaaabbbb",
"patch": _TEST_PATCH2,
"flags": "--js-flags=--exp-js-flag",
},
}))
self.assertEqual(
config,
PinpointTryJobConfig(
benchmark="test_benchmark",
bot="test_bot",
story="test_story",
story_tags="tag1,tag2",
repeat=42,
bug=67890,
base=VariantConfig(
commit="abcdef00",
patch=_TEST_PATCH,
flags=FlagsConfig.parse("--js-flags=--base-js-flag"),
),
experiment=VariantConfig(
commit="aaaabbbb",
patch=_TEST_PATCH2,
flags=FlagsConfig.parse("--js-flags=--exp-js-flag"),
)))
def test_override_all_fields(self):
config = PinpointTryJobConfig.parse_and_override(
benchmark="test_benchmark",
bot="test_bot",
story="test_story",
story_tags="tag1,tag2",
repeat=42,
bug=67890,
base_commit="abcdef00",
exp_commit="12345678",
base_patch=_TEST_PATCH,
exp_patch=_TEST_PATCH2,
base_js_flags="--base-js-flag",
exp_js_flags="--exp-js-flag",
base_enable_features="enable1,enable2",
exp_enable_features="enable3,enable4",
base_disable_features="disable1,disable2",
exp_disable_features="disable3,disable4",
)
self.assertEqual(
config,
PinpointTryJobConfig(
benchmark="test_benchmark",
bot="test_bot",
story="test_story",
story_tags="tag1,tag2",
repeat=42,
bug=67890,
base=VariantConfig(
commit="abcdef00",
patch=_TEST_PATCH,
flags=FlagsConfig.parse("--js-flags=--base-js-flag "
"--enable-features=enable1,enable2 "
"--disable-features=disable1,disable2"),
),
experiment=VariantConfig(
commit="12345678",
patch=_TEST_PATCH2,
flags=FlagsConfig.parse("--js-flags=--exp-js-flag "
"--enable-features=enable3,enable4 "
"--disable-features=disable3,disable4"),
)))
def test_parse_and_override_missing_benchmark(self):
with self.assertRaises(ValueError):
PinpointTryJobConfig.parse_and_override(config="{bot: 'test_bot'}")
with self.assertRaises(ValueError):
PinpointTryJobConfig.parse_and_override(bot="test_bot")
def test_parse_and_override_missing_bot(self):
with self.assertRaises(ValueError):
PinpointTryJobConfig.parse_and_override(
config="{benchmark: 'test_benchmark'}")
with self.assertRaises(ValueError):
PinpointTryJobConfig.parse_and_override(benchmark="test_benchmark")
def test_parse_and_override_missing_story_and_tags(self):
self.mock_fetch_stories.return_value = []
with self.assertRaises(ValueError):
PinpointTryJobConfig.parse_and_override(
benchmark="test_benchmark", bot="test_bot")
def test_to_request_dict(self):
config = PinpointTryJobConfig.parse_and_override(
benchmark="test_benchmark",
bot="test_bot",
story="test_story",
story_tags="tag1,tag2",
repeat=42,
bug=12345,
base_commit="abcdef00",
exp_commit="12345678",
base_patch=_TEST_PATCH,
exp_patch=_TEST_PATCH2,
base_js_flags="--base-js-flag",
exp_js_flags="--exp-js-flag",
base_enable_features="enable1,enable2",
exp_enable_features="enable3,enable4",
base_disable_features="disable1,disable2",
exp_disable_features="disable3,disable4",
)
self.assertDictEqual(
config.to_request_dict(), {
"comparison_mode": "try",
"benchmark": "test_benchmark",
"configuration": "test_bot",
"story": "test_story",
"story_tags": "tag1,tag2",
"initial_attempt_count": 42,
"bug_id": 12345,
"base_git_hash": "abcdef00",
"end_git_hash": "12345678",
"base_patch": _TEST_PATCH,
"experiment_patch": _TEST_PATCH2,
"base_extra_args":
'--extra-browser-args="--js-flags=--base-js-flag '
'--enable-features=enable1,enable2 '
'--disable-features=disable1,disable2"',
"experiment_extra_args":
'--extra-browser-args="--js-flags=--exp-js-flag '
'--enable-features=enable3,enable4 '
'--disable-features=disable3,disable4"',
})
def test_to_request_dict_no_flags(self):
config = PinpointTryJobConfig.parse_and_override(
benchmark="test_benchmark",
bot="test_bot",
story="test_story",
)
self.assertDictEqual(
config.to_request_dict(), {
"comparison_mode": "try",
"benchmark": "test_benchmark",
"configuration": "test_bot",
"story": "test_story",
"story_tags": None,
"initial_attempt_count": 100,
"bug_id": None,
"base_git_hash": "HEAD",
"end_git_hash": "HEAD",
"base_patch": None,
"experiment_patch": None,
"base_extra_args": None,
"experiment_extra_args": None,
})
def test_parse_and_override_recent_commit(self):
config = PinpointTryJobConfig.parse_and_override(
benchmark="test_benchmark",
bot="test_bot",
story="test_story",
base_commit="recent",
exp_commit="recent")
self.assertEqual(config.base.commit, "aaaabbbb")
self.assertEqual(config.experiment.commit, "aaaabbbb")
self.mock_fetch_builds.assert_called_with("test_bot")
def test_parse_and_override_empty_commit_to_head(self):
config = PinpointTryJobConfig.parse_and_override(
benchmark="test_benchmark",
bot="test_bot",
story="test_story",
base_commit="",
exp_commit="")
self.assertEqual(config.base.commit, "HEAD")
self.assertEqual(config.experiment.commit, "HEAD")
def test_parse_and_override_story_auto_fetch_signle_story(self):
config = PinpointTryJobConfig.parse_and_override(
benchmark="test_benchmark", bot="test_bot")
self.assertEqual(config.story, "test_story")
self.mock_fetch_stories.assert_called_once_with("test_benchmark")
def test_parse_and_override_story_auto_fetch_multiple_stories(self):
self.mock_fetch_stories.return_value = ["story1", "story2"]
with self.assertRaises(ValueError):
PinpointTryJobConfig.parse_and_override(
benchmark="test_benchmark", bot="test_bot")
self.mock_fetch_stories.assert_called_once_with("test_benchmark")
def test_parse_and_override_story_auto_fetch_no_story(self):
self.mock_fetch_stories.return_value = []
with self.assertRaises(ValueError):
PinpointTryJobConfig.parse_and_override(
benchmark="test_benchmark", bot="test_bot")
self.mock_fetch_stories.assert_called_once_with("test_benchmark")
def test_parse_and_override_unknown_benchmark_show_warning(self):
self.mock_fetch_benchmarks.return_value = ["other_benchmark"]
PinpointTryJobConfig.parse_and_override(
benchmark="test_benchmark", bot="test_bot", story="test_story")
self.mock_show_warnings.assert_called_once_with(
["Unknown benchmark: test_benchmark"])
def test_parse_and_override_unknown_bot_show_warning(self):
self.mock_fetch_bots.return_value = ["other_bot"]
PinpointTryJobConfig.parse_and_override(
benchmark="test_benchmark", bot="test_bot", story="test_story")
self.mock_show_warnings.assert_called_once_with(["Unknown bot: test_bot"])
def test_parse_and_override_unknown_story_show_warning(self):
self.mock_fetch_stories.return_value = ["other_story"]
PinpointTryJobConfig.parse_and_override(
benchmark="test_benchmark", bot="test_bot", story="test_story")
self.mock_show_warnings.assert_called_once_with(
["Unknown story: test_story"])
def test_parser_raw_config_minimal(self):
config = PinpointTryJobConfig.from_response_dict(self.response_dict)
expectation = PinpointTryJobConfig(
benchmark="speedometer3",
bot="win-11-perf",
story="Speedometer3",
story_tags=None,
repeat=20,
bug=None,
base=VariantConfig(),
experiment=VariantConfig(),
)
self.assertEqual(config, expectation)
self.assertEqual(config.parse_and_override(config.to_dict()), expectation)
def test_parser_raw_config(self):
arguments = self.response_dict["arguments"]
arguments["base_git_hash"] = "abcdef00"
arguments["end_git_hash"] = "1234abcd"
arguments["story_tags"] = "tag1,tag2"
arguments["bug_id"] = 12345
arguments["base_patch"] = _TEST_PATCH
arguments["experiment_patch"] = _TEST_PATCH2
arguments[
"base_extra_args"] = '--extra-browser-args="--js-flags=--base-js-flag"'
arguments["experiment_extra_args"] = (
'--extra-browser-args="--js-flags=--exp-js-flag"')
config = PinpointTryJobConfig.from_response_dict(self.response_dict)
expectation = PinpointTryJobConfig(
benchmark="speedometer3",
bot="win-11-perf",
story="Speedometer3",
story_tags="tag1,tag2",
repeat=20,
bug=12345,
base=VariantConfig(
commit="abcdef00",
patch=_TEST_PATCH,
flags=FlagsConfig.parse("--js-flags=--base-js-flag"),
),
experiment=VariantConfig(
commit="1234abcd",
patch=_TEST_PATCH2,
flags=FlagsConfig.parse("--js-flags=--exp-js-flag"),
),
)
self.assertEqual(config, expectation)
self.assertEqual(config.parse_and_override(config.to_dict()), expectation)
def test_parser_raise_if_not_try_job(self):
self.response_dict["comparison_mode"] = "bisect"
with self.assertRaises(ValueError):
PinpointTryJobConfig.from_response_dict(self.response_dict)
if __name__ == "__main__":
test_helper.run_pytest(__file__)