Skip to content

Commit 544aeda

Browse files
committed
Add tests for the VM utilities
1 parent b0c6ec3 commit 544aeda

File tree

1 file changed

+275
-0
lines changed

1 file changed

+275
-0
lines changed
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
from unittest.mock import Mock, PropertyMock, patch
2+
3+
import pytest
4+
5+
from firewheel.cli.vm_utils import VMMixTable, ScheduleProgress
6+
7+
8+
@patch.object(VMMixTable, "mm_api", new=Mock())
9+
class TestVMMixTable:
10+
"""Tests for the ``VMMixTable`` object."""
11+
12+
sample_mm_vms = {
13+
"vm0": {"state": "RUNNING", "image": "img_A"},
14+
"vm1": {"state": "RUNNING", "image": "img_A"},
15+
"vm2": {"state": "RUNNING", "image": "img_B"},
16+
"vm3": {"state": "RUNNING", "image": "img_C"},
17+
"vm4": {"state": "ERROR", "image": "img_C"},
18+
}
19+
sample_vmr_states = {
20+
"vm0": "configured",
21+
"vm1": "configured",
22+
"vm2": "configuring",
23+
"vm3": "uninitialized",
24+
"vm4": "error",
25+
}
26+
sample_mm_states = {
27+
"RUNNING": {
28+
"configured": ["img_A", "img_A"],
29+
"configuring": ["img_B"],
30+
"uninitialized": ["img_C"],
31+
},
32+
"ERROR": {"error": ["img_C"]},
33+
}
34+
35+
@patch.object(VMMixTable, "get_mm_states", new=Mock(return_value=sample_mm_states))
36+
def test_initialization(self):
37+
assert VMMixTable().row_count == 4
38+
39+
@patch.object(VMMixTable, "get_mm_states", new=Mock(return_value={}))
40+
def test_initialization_empty(self):
41+
# The table is a single row of N/A values if the minimega API finds no states
42+
assert VMMixTable().row_count == 1
43+
44+
def test_get_mm_vms(self):
45+
assert VMMixTable.get_mm_vms() == VMMixTable.mm_api.mm_vms()
46+
47+
@patch.object(
48+
VMMixTable, "check_is_active_experiment", new=Mock(return_value=True)
49+
)
50+
@patch.object(
51+
VMMixTable, "get_mm_vms", new=Mock(return_value=sample_mm_vms)
52+
)
53+
@patch(
54+
"firewheel.cli.vm_utils.vmr_api.get_vm_states",
55+
new=Mock(return_value=sample_vmr_states),
56+
)
57+
def test_get_mm_states(self):
58+
assert VMMixTable.get_mm_states() == self.sample_mm_states
59+
60+
@patch.object(
61+
VMMixTable, "check_is_active_experiment", new=Mock(return_value=False)
62+
)
63+
def test_get_mm_states_inactive_experiment(self):
64+
assert VMMixTable.get_mm_states() == {}
65+
66+
@pytest.mark.parametrize(
67+
["launch_time", "mm_vms", "active"],
68+
[
69+
[100, {}, True],
70+
[None, sample_mm_vms, True],
71+
[None, {}, False],
72+
],
73+
)
74+
@patch("firewheel.cli.vm_utils.vmr_api.get_experiment_launch_time")
75+
@patch.object(VMMixTable, "get_mm_vms")
76+
def test_check_is_active_experiment(
77+
self, mock_get_mm_vms, mock_get_launch_time, launch_time, mm_vms, active
78+
):
79+
mock_get_launch_time.return_value = launch_time
80+
mock_get_mm_vms.return_value = mm_vms
81+
assert VMMixTable.check_is_active_experiment() == active
82+
83+
84+
class TestScheduleProgress:
85+
"""Tests for the ``ScheduleProgress`` object."""
86+
87+
sample_schedule_db_list = [
88+
{"server_name": "vmA", "text": Mock("schedule A")},
89+
{"server_name": "vmB", "text": Mock("schedule B")},
90+
{"server_name": "vmC", "text": Mock("schedule C")},
91+
]
92+
sample_schedules = {
93+
"vmA": [
94+
Mock(name="entry-1a", start_time=-10),
95+
Mock(name="entry-2a", start_time=-5),
96+
Mock(name="entry-3a", start_time=10),
97+
],
98+
"vmB": [
99+
Mock(name="entry-1b", start_time=-10),
100+
Mock(name="entry-2b", start_time=-5),
101+
Mock(name="entry-3b", start_time=-1),
102+
],
103+
"vmC": [
104+
Mock(name="entry-1b", start_time=-100),
105+
Mock(name="entry-2b", start_time=1),
106+
Mock(name="entry-3b", start_time=2),
107+
Mock(name="entry-4b", start_time=3),
108+
],
109+
}
110+
111+
def test_initialization(self):
112+
schedule_progress = ScheduleProgress()
113+
assert len(schedule_progress.columns) == 4
114+
115+
@pytest.mark.parametrize(
116+
["vm_times", "has_begun"],
117+
[
118+
({"vmA": "0", "vmB": "1", "vmC": "2"}, True),
119+
({"vmA": "0", "vmB": "1", "vmC": ""}, True),
120+
({"vmA": "", "vmB": "", "vmC": ""}, False),
121+
],
122+
)
123+
@patch("firewheel.cli.vm_utils.vmr_api.get_vm_times")
124+
def test_experiment_has_begun(self, mock_get_vm_times, vm_times, has_begun):
125+
mock_get_vm_times.return_value = vm_times
126+
assert ScheduleProgress().experiment_has_begun == has_begun
127+
128+
@pytest.mark.parametrize(
129+
["experiment_start_time", "in_negative_time"],
130+
[(0, False), (1, False), (None, True)],
131+
)
132+
@patch("firewheel.cli.vm_utils.vmr_api.get_experiment_start_time")
133+
def test_experiment_in_negative_time(
134+
self, mock_get_experiment_start_time, experiment_start_time, in_negative_time
135+
):
136+
mock_get_experiment_start_time.return_value = experiment_start_time
137+
assert ScheduleProgress().experiment_in_negative_time == in_negative_time
138+
139+
@pytest.mark.parametrize(
140+
["has_begun", "in_negative_time", "visible"],
141+
[
142+
(False, False, False),
143+
(False, True, False),
144+
(True, False, False),
145+
(True, True, True),
146+
],
147+
)
148+
@patch.object(ScheduleProgress, "experiment_has_begun", new_callable=PropertyMock)
149+
@patch.object(
150+
ScheduleProgress, "experiment_in_negative_time", new_callable=PropertyMock
151+
)
152+
def test_visible(
153+
self,
154+
mock_has_begun_property,
155+
mock_in_negative_time_property,
156+
has_begun,
157+
in_negative_time,
158+
visible,
159+
):
160+
mock_has_begun_property.return_value = has_begun
161+
mock_in_negative_time_property.return_value = in_negative_time
162+
assert ScheduleProgress().visible == visible
163+
164+
@patch(
165+
"firewheel.cli.vm_utils.ScheduleDb.list_all",
166+
new=Mock(return_value=sample_schedule_db_list),
167+
)
168+
@patch("firewheel.cli.vm_utils.pickle.loads")
169+
def test_schedules(self, mock_pickle_loads):
170+
assert ScheduleProgress().schedules == {
171+
"vmA": mock_pickle_loads.return_value,
172+
"vmB": mock_pickle_loads.return_value,
173+
"vmC": mock_pickle_loads.return_value,
174+
}
175+
176+
@pytest.mark.parametrize(
177+
["has_begun", "event_count"],
178+
[
179+
(False, None),
180+
(True, 6),
181+
(True, 6),
182+
],
183+
)
184+
@patch.object(ScheduleProgress, "experiment_has_begun", new_callable=PropertyMock)
185+
@patch.object(
186+
ScheduleProgress,
187+
"schedules",
188+
new_callable=PropertyMock,
189+
return_value=sample_schedules,
190+
)
191+
def test_total_negative_event_count(
192+
self, mock_schedules, mock_has_begun_property, has_begun, event_count
193+
):
194+
mock_has_begun_property.return_value = has_begun
195+
assert ScheduleProgress().total_negative_event_count == event_count
196+
197+
@pytest.mark.parametrize(
198+
["vm_times", "event_count"],
199+
[
200+
({"vmA": "-50", "vmB": "-50", "vmC": "-50"}, 0 + 0 + 1),
201+
({"vmA": "-1", "vmB": "-1", "vmC": "0"}, 2 + 2 + 1),
202+
({"vmA": "0", "vmB": "1", "vmC": "2"}, 2 + 3 + 1),
203+
({"vmA": "0", "vmB": "1", "vmC": ""}, 2 + 3 + 0),
204+
({"vmA": "", "vmB": "", "vmC": ""}, None), # experiment has not yet begun
205+
],
206+
)
207+
@patch("firewheel.cli.vm_utils.vmr_api.get_vm_times")
208+
@patch.object(
209+
ScheduleProgress,
210+
"schedules",
211+
new_callable=PropertyMock,
212+
return_value=sample_schedules,
213+
)
214+
def test_completed_negative_event_count(
215+
self, mock_schedules, mock_get_vm_times, vm_times, event_count
216+
):
217+
mock_get_vm_times.return_value = vm_times
218+
assert ScheduleProgress().completed_negative_event_count == event_count
219+
220+
@pytest.mark.parametrize(
221+
["total_events", "completed_events", "remaining_events"],
222+
[
223+
(1, 1, 0),
224+
(1, 0, 1),
225+
(10, 7, 3),
226+
(100, 70, 30),
227+
(100, 30, 70),
228+
],
229+
)
230+
@patch.object(
231+
ScheduleProgress, "completed_negative_event_count", new_callable=PropertyMock
232+
)
233+
@patch.object(
234+
ScheduleProgress, "total_negative_event_count", new_callable=PropertyMock
235+
)
236+
def test_remaining_negative_event_count(
237+
self,
238+
mock_total_count_property,
239+
mock_completed_count_property,
240+
total_events,
241+
completed_events,
242+
remaining_events,
243+
):
244+
mock_total_count_property.return_value = total_events
245+
mock_completed_count_property.return_value = completed_events
246+
assert ScheduleProgress().remaining_negative_event_count == remaining_events
247+
248+
@patch("firewheel.cli.vm_utils.Progress.add_task")
249+
def test_add_negative_time_task(self, mock_add_task_method):
250+
task_id = ScheduleProgress().add_negative_time_task()
251+
assert task_id == mock_add_task_method.return_value
252+
mock_add_task_method.assert_called_once()
253+
254+
@pytest.mark.parametrize(
255+
"kwargs",
256+
[{"visible": False}, {"visible": True, "total": 100, "completed": 10}],
257+
)
258+
@patch.object(
259+
ScheduleProgress, "completed_negative_event_count", new_callable=PropertyMock
260+
)
261+
@patch.object(
262+
ScheduleProgress, "total_negative_event_count", new_callable=PropertyMock
263+
)
264+
@patch("firewheel.cli.vm_utils.Progress.update")
265+
def test_update_negative_time_task(
266+
self,
267+
mock_update_task_method,
268+
mock_total_count_property,
269+
mock_completed_count_property,
270+
kwargs,
271+
):
272+
schedule_progress = ScheduleProgress()
273+
with patch.object(schedule_progress, "_negative_time_task_id") as mock_task_id:
274+
schedule_progress.update_negative_time_task(**kwargs)
275+
mock_update_task_method.assert_called_once_with(mock_task_id, **kwargs)

0 commit comments

Comments
 (0)