Skip to content

Commit c08d6eb

Browse files
author
anon
committed
add utests
1 parent a37814a commit c08d6eb

1 file changed

Lines changed: 182 additions & 0 deletions

File tree

qubesadmin/tests/tools/qvm_shutdown.py

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,3 +222,185 @@ def test_015_wait_all_kill_timeout(self):
222222
qubesadmin.tools.qvm_shutdown.main(
223223
['--wait', '--all', '--timeout=1'], app=self.app)
224224
self.assertAllCalled()
225+
226+
def test_005_force(self):
227+
'''test --force sends force flag to shutdown call'''
228+
self.app.expected_calls[
229+
('dom0', 'admin.vm.List', None, None)] = \
230+
b'0\x00some-vm class=AppVM state=Running\n'
231+
self.app.expected_calls[
232+
('some-vm', 'admin.vm.Shutdown', 'force', None)] = b'0\x00'
233+
qubesadmin.tools.qvm_shutdown.main(
234+
['--force', 'some-vm'], app=self.app)
235+
self.assertAllCalled()
236+
237+
def test_006_dry_run(self):
238+
'''test --dry-run skips shutdown calls'''
239+
self.app.expected_calls[
240+
('dom0', 'admin.vm.List', None, None)] = \
241+
b'0\x00some-vm class=AppVM state=Running\n'
242+
qubesadmin.tools.qvm_shutdown.main(
243+
['--dry-run', 'some-vm'], app=self.app)
244+
self.assertAllCalled()
245+
246+
@unittest.skipUnless(qubesadmin.tools.qvm_shutdown.have_events,
247+
'Events not present')
248+
def test_011_wait_retry(self):
249+
'''test --wait retries VMs whose shutdown request failed'''
250+
loop = asyncio.new_event_loop()
251+
asyncio.set_event_loop(loop)
252+
253+
mock_events = unittest.mock.AsyncMock()
254+
patch = unittest.mock.patch(
255+
'qubesadmin.events.EventsDispatcher._get_events_reader',
256+
mock_events)
257+
patch.start()
258+
self.addCleanup(patch.stop)
259+
mock_events.side_effect = qubesadmin.tests.tools.MockEventsReader([
260+
# round 1: wait for some-vm
261+
b'1\0\0connection-established\0\0',
262+
b'1\0some-vm\0domain-shutdown\0\0',
263+
# round 2: wait for other-vm
264+
b'1\0\0connection-established\0\0',
265+
b'1\0other-vm\0domain-shutdown\0\0',
266+
])
267+
268+
self.app.expected_calls[
269+
('dom0', 'admin.vm.List', None, None)] = \
270+
b'0\x00' \
271+
b'some-vm class=AppVM state=Running\n' \
272+
b'other-vm class=AppVM state=Running\n'
273+
self.app.expected_calls[
274+
('some-vm', 'admin.vm.Shutdown', None, None)] = \
275+
b'0\x00'
276+
# other-vm fails first attempt, succeeds on retry
277+
self.app.expected_calls[
278+
('other-vm', 'admin.vm.Shutdown', None, None)] = [
279+
b'2\x00QubesException\x00\x00Shutdown refused\x00',
280+
b'0\x00',
281+
]
282+
self.app.expected_calls[
283+
('some-vm', 'admin.vm.CurrentState', None, None)] = [
284+
b'0\x00power_state=Running',
285+
b'0\x00power_state=Halted',
286+
]
287+
self.app.expected_calls[
288+
('other-vm', 'admin.vm.CurrentState', None, None)] = [
289+
b'0\x00power_state=Running',
290+
b'0\x00power_state=Halted',
291+
]
292+
qubesadmin.tools.qvm_shutdown.main(
293+
['--wait', 'some-vm', 'other-vm'], app=self.app)
294+
self.assertAllCalled()
295+
296+
@unittest.skipUnless(qubesadmin.tools.qvm_shutdown.have_events,
297+
'Events not present')
298+
def test_013_wait_all_shutdown_fail(self):
299+
'''test --wait exits with error when all shutdown requests fail'''
300+
self.app.expected_calls[
301+
('dom0', 'admin.vm.List', None, None)] = \
302+
b'0\x00some-vm class=AppVM state=Running\n'
303+
self.app.expected_calls[
304+
('some-vm', 'admin.vm.Shutdown', None, None)] = \
305+
b'2\x00QubesException\x00\x00Shutdown refused\x00'
306+
self.app.expected_calls[
307+
('some-vm', 'admin.vm.CurrentState', None, None)] = \
308+
b'0\x00power_state=Running'
309+
with self.assertRaises(SystemExit):
310+
qubesadmin.tools.qvm_shutdown.main(
311+
['--wait', 'some-vm'], app=self.app)
312+
self.assertAllCalled()
313+
314+
@unittest.skipUnless(qubesadmin.tools.qvm_shutdown.have_events,
315+
'Events not present')
316+
def test_016_wait_kill_exception(self):
317+
'''test --wait timeout where kill raises QubesException'''
318+
loop = asyncio.new_event_loop()
319+
asyncio.set_event_loop(loop)
320+
321+
mock_events = unittest.mock.AsyncMock()
322+
patch = unittest.mock.patch(
323+
'qubesadmin.events.EventsDispatcher._get_events_reader',
324+
mock_events)
325+
patch.start()
326+
self.addCleanup(patch.stop)
327+
mock_events.side_effect = qubesadmin.tests.tools.MockEventsReader([
328+
b'1\0\0connection-established\0\0',
329+
])
330+
331+
self.app.expected_calls[
332+
('dom0', 'admin.vm.List', None, None)] = \
333+
b'0\x00some-vm class=AppVM state=Running\n'
334+
self.app.expected_calls[
335+
('some-vm', 'admin.vm.Shutdown', None, None)] = \
336+
b'0\x00'
337+
self.app.expected_calls[
338+
('some-vm', 'admin.vm.Kill', None, None)] = \
339+
b'2\x00QubesException\x00\x00Kill failed\x00'
340+
self.app.expected_calls[
341+
('some-vm', 'admin.vm.CurrentState', None, None)] = [
342+
b'0\x00power_state=Running',
343+
b'0\x00power_state=Running',
344+
]
345+
with self.assertRaises(SystemExit):
346+
qubesadmin.tools.qvm_shutdown.main(
347+
['--wait', '--timeout=1', 'some-vm'], app=self.app)
348+
self.assertAllCalled()
349+
350+
@unittest.skipUnless(qubesadmin.tools.qvm_shutdown.have_events,
351+
'Events not present')
352+
def test_017_wait_dispvm_na(self):
353+
'''test --wait treats DispVM with NA power state as shut down'''
354+
loop = asyncio.new_event_loop()
355+
asyncio.set_event_loop(loop)
356+
357+
mock_events = unittest.mock.AsyncMock()
358+
patch = unittest.mock.patch(
359+
'qubesadmin.events.EventsDispatcher._get_events_reader',
360+
mock_events)
361+
patch.start()
362+
self.addCleanup(patch.stop)
363+
mock_events.side_effect = qubesadmin.tests.tools.MockEventsReader([
364+
b'1\0\0connection-established\0\0',
365+
b'1\0disp123\0domain-shutdown\0\0',
366+
])
367+
368+
self.app.expected_calls[
369+
('dom0', 'admin.vm.List', None, None)] = \
370+
b'0\x00disp123 class=DispVM state=Running\n'
371+
self.app.expected_calls[
372+
('disp123', 'admin.vm.Shutdown', None, None)] = \
373+
b'0\x00'
374+
self.app.expected_calls[
375+
('disp123', 'admin.vm.CurrentState', None, None)] = [
376+
b'0\x00power_state=Running',
377+
# failed_domains: first get_power_state() != 'Halted',
378+
# then klass == 'DispVM' triggers second get_power_state()
379+
b'0\x00power_state=NA',
380+
b'0\x00power_state=NA',
381+
]
382+
qubesadmin.tools.qvm_shutdown.main(
383+
['--wait', 'disp123'], app=self.app)
384+
self.assertAllCalled()
385+
386+
def test_018_wait_polling_fallback(self):
387+
'''test --wait uses polling when events are unavailable'''
388+
loop = asyncio.new_event_loop()
389+
asyncio.set_event_loop(loop)
390+
391+
self.app.expected_calls[
392+
('dom0', 'admin.vm.List', None, None)] = \
393+
b'0\x00some-vm class=AppVM state=Running\n'
394+
self.app.expected_calls[
395+
('some-vm', 'admin.vm.Shutdown', None, None)] = \
396+
b'0\x00'
397+
self.app.expected_calls[
398+
('some-vm', 'admin.vm.CurrentState', None, None)] = [
399+
b'0\x00power_state=Halted',
400+
b'0\x00power_state=Halted',
401+
]
402+
with unittest.mock.patch.object(
403+
qubesadmin.tools.qvm_shutdown, 'have_events', False):
404+
qubesadmin.tools.qvm_shutdown.main(
405+
['--wait', 'some-vm'], app=self.app)
406+
self.assertAllCalled()

0 commit comments

Comments
 (0)