diff --git a/bugbug/models/testselect.py b/bugbug/models/testselect.py index 150f16be8c..43831ae8f7 100644 --- a/bugbug/models/testselect.py +++ b/bugbug/models/testselect.py @@ -280,7 +280,7 @@ def load_failing_together(task: str) -> dict[str, tuple[float, float]]: def select_configs( - groups: Collection[str], + group_confidences: dict[str, float], min_redundancy_confidence: float, max_configurations: int = 3, ) -> dict[str, list[str]]: @@ -290,6 +290,12 @@ def select_configs( all_configs_by_group = pickle.loads(failing_together[b"$CONFIGS_BY_GROUP$"]) config_costs = {config: _get_cost(config) for config in all_configs} + all_groups = group_confidences.keys() + high_confidence_groups = { + group for group in all_groups if group_confidences.get(group, 0.0) >= 0.99 + } + groups = [group for group in all_groups if group not in high_confidence_groups] + solver = pywraplp.Solver( "select_configs", pywraplp.Solver.CBC_MIXED_INTEGER_PROGRAMMING ) @@ -305,6 +311,14 @@ def select_configs( ) } + # Configs used by high-confidence groups are already committed; fix their + # config_vars to 1 so the solver treats their fixed cost as already paid. + committed_configs = set() + for group in high_confidence_groups: + committed_configs |= set(all_configs_by_group.get(group, all_configs)) + for config in committed_configs: + solver.Add(config_vars[config] == 1) + equivalence_sets = _get_equivalence_sets(min_redundancy_confidence) for group in groups: @@ -374,7 +388,10 @@ def select_configs( ) ) - configs_by_group: dict[str, list[str]] = {} + configs_by_group: dict[str, list[str]] = { + group: list(all_configs_by_group.get(group, all_configs)) + for group in high_confidence_groups + } for group in groups: configs_by_group[group] = [] @@ -922,7 +939,14 @@ def eval_apply_transforms( if granularity == "label": selected = reduce_configs(selected, reduction) elif granularity == "group": - group_configs = select_configs(selected, reduction) + group_configs = select_configs( + { + name: confidence + for name, confidence in push["all_possibly_selected"].items() + if name in selected + }, + reduction, + ) if minimum is not None and len(selected) < minimum: remaining = [ diff --git a/http_service/bugbug_http/models.py b/http_service/bugbug_http/models.py index 0d756e658b..5fd17b9c4f 100644 --- a/http_service/bugbug_http/models.py +++ b/http_service/bugbug_http/models.py @@ -373,7 +373,7 @@ def _analyze_patch(revs: list[bytes], branch: str | None) -> dict: for group in test_scheduling.find_manifests_for_paths(REPO_DIR, modified_paths): groups[group] = 1.0 - config_groups = testselect.select_configs(groups.keys(), 0.9) + config_groups = testselect.select_configs(groups, 0.9) data = { "tasks": tasks, diff --git a/tests/test_testselect.py b/tests/test_testselect.py index 49757aee2c..9e768e67f4 100644 --- a/tests/test_testselect.py +++ b/tests/test_testselect.py @@ -718,37 +718,28 @@ def test_select_configs(failing_together_config_group: LMDBDict) -> None: test_scheduling.close_failing_together_db("config_group") result = testselect.select_configs( - { - "group1", - }, + {"group1": 0.0}, 1.0, ) assert len(result) == 1 assert set(result["group1"]) == {"linux2404-64-asan/debug", "linux2404-64/opt"} result = testselect.select_configs( - { - "group2", - }, + {"group2": 0.0}, 1.0, ) assert len(result) == 1 assert set(result["group2"]) == {"linux2404-64/debug", "linux2404-64/opt"} result = testselect.select_configs( - { - "group3", - }, + {"group3": 0.0}, 1.0, ) assert len(result) == 1 assert set(result["group3"]) == {"windows10/debug", "linux2404-64/opt"} result = testselect.select_configs( - { - "group1", - "group2", - }, + {"group1": 0.0, "group2": 0.0}, 1.0, ) assert len(result) == 2 @@ -759,10 +750,7 @@ def test_select_configs(failing_together_config_group: LMDBDict) -> None: } result = testselect.select_configs( - { - "group1", - "group3", - }, + {"group1": 0.0, "group3": 0.0}, 1.0, ) assert len(result) == 2 @@ -770,10 +758,7 @@ def test_select_configs(failing_together_config_group: LMDBDict) -> None: assert set(result["group3"]) == {"windows10/debug", "linux2404-64/opt"} result = testselect.select_configs( - { - "group2", - "group3", - }, + {"group2": 0.0, "group3": 0.0}, 1.0, ) assert len(result) == 2 @@ -781,11 +766,7 @@ def test_select_configs(failing_together_config_group: LMDBDict) -> None: assert set(result["group3"]) == {"linux2404-64/opt", "windows10/debug"} result = testselect.select_configs( - { - "group1", - "group2", - "group3", - }, + {"group1": 0.0, "group2": 0.0, "group3": 0.0}, 1.0, ) assert len(result) == 3 @@ -796,3 +777,19 @@ def test_select_configs(failing_together_config_group: LMDBDict) -> None: "linux2404-64-asan/debug", } assert set(result["group3"]) == {"linux2404-64/opt", "windows10/debug"} + + # A group selected with >= 0.99 confidence runs on all its configs. + all_configs = { + "linux2404-64-asan/debug", + "linux2404-64/debug", + "linux2404-64/opt", + "mac/debug", + "windows10/debug", + } + result = testselect.select_configs( + {"group1": 0.99, "group2": 0.0}, + 1.0, + ) + assert len(result) == 2 + assert set(result["group1"]) == all_configs + assert set(result["group2"]) == {"linux2404-64/opt", "linux2404-64/debug"}