Coverage for mlos_bench/mlos_bench/tests/tunables/tunable_slice_references_test.py: 97%
58 statements
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-06 00:35 +0000
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-06 00:35 +0000
1#
2# Copyright (c) Microsoft Corporation.
3# Licensed under the MIT License.
4#
5"""
6Unit tests for unique references to tunables when they're loaded multiple times.
7"""
9import json5 as json
10import pytest
12from mlos_bench.tunables.tunable_groups import TunableGroups
15def test_duplicate_merging_tunable_groups(tunable_groups_config: dict) -> None:
16 """
17 Check that the merging logic of tunable groups works as expected.
18 """
19 parent_tunables = TunableGroups(tunable_groups_config)
21 # Pretend we loaded this one from disk another time.
22 tunables_dup = TunableGroups(tunable_groups_config)
24 (tunable, covariant_group) = next(iter(parent_tunables))
25 (tunable_dup, covariant_group_dup) = next(iter(tunables_dup))
27 assert tunable == tunable_dup
28 assert covariant_group == covariant_group_dup
30 # Test merging prior to making any changes.
31 parent_tunable_copy = parent_tunables.copy()
32 parent_tunables = parent_tunables.merge(tunables_dup)
34 # Check that they're the same.
35 assert covariant_group == covariant_group_dup
36 assert parent_tunables == tunables_dup
37 assert parent_tunables == parent_tunable_copy
39 (tunable_retry, covariant_group_retry) = next(iter(parent_tunables))
40 assert tunable == tunable_retry
41 assert covariant_group == covariant_group_retry
43 # Update a value to indicate that they're separate copies.
44 if tunable.is_categorical:
45 tunable.category = [x for x in tunable.categories if x != tunable.category][0]
46 elif tunable.is_numerical:
47 tunable.numerical_value += 1
49 # Check that they're separate.
50 assert tunable != tunable_dup
51 assert covariant_group != covariant_group_dup
52 assert parent_tunables != tunables_dup
54 # Should be ok since we only changed the value.
55 parent_tunable_copy = parent_tunables.copy()
56 parent_tunables = parent_tunables.merge(tunables_dup)
58 # Make sure nothing changed in the parent.
59 assert tunable != tunable_dup
60 assert covariant_group != covariant_group_dup
61 assert parent_tunables != tunables_dup
62 assert parent_tunables == parent_tunable_copy
65def test_overlapping_group_merge_tunable_groups(tunable_groups_config: dict) -> None:
66 """
67 Check that the merging logic of tunable groups works as expected.
68 """
69 parent_tunables = TunableGroups(tunable_groups_config)
71 # This config should overlap with the parent config.
72 # (same group name, different param name, different values)
73 other_tunables_json = """
74 {
75 "boot": {
76 "cost": 300,
77 "params": {
78 "noidle": {
79 "description": "(different) idling method",
80 "type": "categorical",
81 "default": "nomwait",
82 "values": ["nohalt", "nomwait", "idle"]
83 }
84 }
85 }
86 }
87 """
89 other_tunables_config = json.loads(other_tunables_json)
90 other_tunables = TunableGroups(other_tunables_config)
92 with pytest.raises(ValueError):
93 parent_tunables.merge(other_tunables)
96def test_bad_extended_merge_tunable_group(tunable_groups_config: dict) -> None:
97 """
98 Check that the merging logic of tunable groups works as expected.
99 """
100 parent_tunables = TunableGroups(tunable_groups_config)
102 # This config should overlap with the parent config.
103 # (different group name, same param name)
104 other_tunables_json = """
105 {
106 "new-group": {
107 "cost": 300,
108 "params": {
109 "idle": {
110 "type": "categorical",
111 "description": "Idling method",
112 "default": "mwait",
113 "values": ["halt", "mwait", "noidle"]
114 }
115 }
116 }
117 }
118 """
120 other_tunables_config = json.loads(other_tunables_json)
121 other_tunables = TunableGroups(other_tunables_config)
123 with pytest.raises(ValueError):
124 parent_tunables.merge(other_tunables)
127def test_good_extended_merge_tunable_group(tunable_groups_config: dict) -> None:
128 """
129 Check that the merging logic of tunable groups works as expected.
130 """
131 parent_tunables = TunableGroups(tunable_groups_config)
133 # This config should overlap with the parent config.
134 # (different group name, same param name)
135 other_tunables_json = """
136 {
137 "new-group": {
138 "cost": 300,
139 "params": {
140 "new-param": {
141 "type": "int",
142 "default": 0,
143 "range": [0, 10]
144 }
145 }
146 }
147 }
148 """
150 other_tunables_config = json.loads(other_tunables_json)
151 other_tunables = TunableGroups(other_tunables_config)
153 assert "new-param" not in parent_tunables
154 assert "new-param" in other_tunables
156 parent_tunables = parent_tunables.merge(other_tunables)
158 assert "new-param" in parent_tunables
159 (tunable_param, covariant_group) = parent_tunables.get_tunable("new-param")
160 assert tunable_param.name == "new-param"
161 assert covariant_group.name == "new-group"