Coverage for mlos_bench/mlos_bench/tests/tunables/tunables_assign_test.py: 100%
86 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 assigning values to the individual parameters within tunable groups.
7"""
9import json5 as json
10import pytest
12from mlos_bench.tunables.tunable import Tunable
13from mlos_bench.tunables.tunable_groups import TunableGroups
16def test_tunables_assign_unknown_param(tunable_groups: TunableGroups) -> None:
17 """
18 Make sure that bulk assignment fails for parameters
19 that don't exist in the TunableGroups object.
20 """
21 with pytest.raises(KeyError):
22 tunable_groups.assign({
23 "vmSize": "Standard_B2ms",
24 "idle": "mwait",
25 "UnknownParam_1": 1,
26 "UnknownParam_2": "invalid-value"
27 })
30def test_tunables_assign_categorical(tunable_categorical: Tunable) -> None:
31 """
32 Regular assignment for categorical tunable.
33 """
34 # Must be one of: {"Standard_B2s", "Standard_B2ms", "Standard_B4ms"}
35 tunable_categorical.value = "Standard_B4ms"
36 assert not tunable_categorical.is_special
39def test_tunables_assign_invalid_categorical(tunable_groups: TunableGroups) -> None:
40 """
41 Check parameter validation for categorical tunables.
42 """
43 with pytest.raises(ValueError):
44 tunable_groups.assign({"vmSize": "InvalidSize"})
47def test_tunables_assign_invalid_range(tunable_groups: TunableGroups) -> None:
48 """
49 Check parameter out-of-range validation for numerical tunables.
50 """
51 with pytest.raises(ValueError):
52 tunable_groups.assign({"kernel_sched_migration_cost_ns": -2})
55def test_tunables_assign_coerce_str(tunable_groups: TunableGroups) -> None:
56 """
57 Check the conversion from strings when assigning to an integer parameter.
58 """
59 tunable_groups.assign({"kernel_sched_migration_cost_ns": "10000"})
62def test_tunables_assign_coerce_str_range_check(tunable_groups: TunableGroups) -> None:
63 """
64 Check the range when assigning to an integer tunable.
65 """
66 with pytest.raises(ValueError):
67 tunable_groups.assign({"kernel_sched_migration_cost_ns": "5500000"})
70def test_tunables_assign_coerce_str_invalid(tunable_groups: TunableGroups) -> None:
71 """
72 Make sure we fail when assigning an invalid string to an integer tunable.
73 """
74 with pytest.raises(ValueError):
75 tunable_groups.assign({"kernel_sched_migration_cost_ns": "1.1"})
78def test_tunable_assign_str_to_numerical(tunable_int: Tunable) -> None:
79 """
80 Check str to int coercion.
81 """
82 with pytest.raises(ValueError):
83 tunable_int.numerical_value = "foo" # type: ignore[assignment]
86def test_tunable_assign_int_to_numerical_value(tunable_int: Tunable) -> None:
87 """
88 Check numerical value assignment.
89 """
90 tunable_int.numerical_value = 10.0
91 assert tunable_int.numerical_value == 10
92 assert not tunable_int.is_special
95def test_tunable_assign_float_to_numerical_value(tunable_float: Tunable) -> None:
96 """
97 Check numerical value assignment.
98 """
99 tunable_float.numerical_value = 0.1
100 assert tunable_float.numerical_value == 0.1
101 assert not tunable_float.is_special
104def test_tunable_assign_str_to_int(tunable_int: Tunable) -> None:
105 """
106 Check str to int coercion.
107 """
108 tunable_int.value = "10"
109 assert tunable_int.value == 10 # type: ignore[comparison-overlap]
110 assert not tunable_int.is_special
113def test_tunable_assign_str_to_float(tunable_float: Tunable) -> None:
114 """
115 Check str to float coercion.
116 """
117 tunable_float.value = "0.5"
118 assert tunable_float.value == 0.5 # type: ignore[comparison-overlap]
119 assert not tunable_float.is_special
122def test_tunable_assign_float_to_int(tunable_int: Tunable) -> None:
123 """
124 Check float to int coercion.
125 """
126 tunable_int.value = 10.0
127 assert tunable_int.value == 10
128 assert not tunable_int.is_special
131def test_tunable_assign_float_to_int_fail(tunable_int: Tunable) -> None:
132 """
133 Check the invalid float to int coercion.
134 """
135 with pytest.raises(ValueError):
136 tunable_int.value = 10.1
139def test_tunable_assign_null_to_categorical() -> None:
140 """
141 Checks that we can use null/None in categorical tunables.
142 """
143 json_config = """
144 {
145 "name": "categorical_test",
146 "type": "categorical",
147 "values": ["foo", null],
148 "default": "foo"
149 }
150 """
151 config = json.loads(json_config)
152 categorical_tunable = Tunable(name='categorical_test', config=config)
153 assert categorical_tunable
154 assert categorical_tunable.category == "foo"
155 categorical_tunable.value = None
156 assert categorical_tunable.value is None
157 assert categorical_tunable.value != 'None'
158 assert categorical_tunable.category is None
161def test_tunable_assign_null_to_int(tunable_int: Tunable) -> None:
162 """
163 Checks that we can't use null/None in integer tunables.
164 """
165 with pytest.raises((TypeError, AssertionError)):
166 tunable_int.value = None
167 with pytest.raises((TypeError, AssertionError)):
168 tunable_int.numerical_value = None # type: ignore[assignment]
171def test_tunable_assign_null_to_float(tunable_float: Tunable) -> None:
172 """
173 Checks that we can't use null/None in float tunables.
174 """
175 with pytest.raises((TypeError, AssertionError)):
176 tunable_float.value = None
177 with pytest.raises((TypeError, AssertionError)):
178 tunable_float.numerical_value = None # type: ignore[assignment]
181def test_tunable_assign_special(tunable_int: Tunable) -> None:
182 """
183 Check the assignment of a special value outside of the range (but declared `special`).
184 """
185 tunable_int.numerical_value = -1
186 assert tunable_int.numerical_value == -1
187 assert tunable_int.is_special
190def test_tunable_assign_special_fail(tunable_int: Tunable) -> None:
191 """
192 Assign a value that is neither special nor in range and fail.
193 """
194 with pytest.raises(ValueError):
195 tunable_int.numerical_value = -2
198def test_tunable_assign_special_with_coercion(tunable_int: Tunable) -> None:
199 """
200 Check the assignment of a special value outside of the range (but declared `special`).
201 Check coercion from float to int.
202 """
203 tunable_int.numerical_value = -1.0
204 assert tunable_int.numerical_value == -1
205 assert tunable_int.is_special
208def test_tunable_assign_special_with_coercion_str(tunable_int: Tunable) -> None:
209 """
210 Check the assignment of a special value outside of the range (but declared `special`).
211 Check coercion from string to int.
212 """
213 tunable_int.value = "-1"
214 assert tunable_int.numerical_value == -1
215 assert tunable_int.is_special