core-admin/qubes/tests/__init__.py
2015-06-29 17:39:25 +02:00

135 lines
4.3 KiB
Python

#!/usr/bin/python -O
import collections
import unittest
import qubes.config
import qubes.events
#: :py:obj:`True` if running in dom0, :py:obj:`False` otherwise
in_dom0 = False
try:
import libvirt
libvirt.openReadOnly(qubes.config.defaults['libvirt_uri']).close()
in_dom0 = True
del libvirt
except libvirt.libvirtError:
pass
def skipUnlessDom0(test_item):
'''Decorator that skips test outside dom0.
Some tests (especially integration tests) have to be run in more or less
working dom0. This is checked by connecting to libvirt.
'''
return unittest.skipUnless(in_dom0, 'outside dom0')(test_item)
class TestEmitter(qubes.events.Emitter):
'''Dummy event emitter which records events fired on it.
Events are counted in :py:attr:`fired_events` attribute, which is
:py:class:`collections.Counter` instance. For each event, ``(event, args,
kwargs)`` object is counted. *event* is event name (a string), *args* is
tuple with positional arguments and *kwargs* is sorted tuple of items from
keyword arguments.
>>> emitter = TestEmitter()
>>> emitter.fired_events
Counter()
>>> emitter.fire_event('event', 1, 2, 3, spam='eggs', foo='bar')
>>> emitter.fired_events
Counter({('event', (1, 2, 3), (('foo', 'bar'), ('spam', 'eggs'))): 1})
'''
def __init__(self, *args, **kwargs):
super(TestEmitter, self).__init__(*args, **kwargs)
#: :py:class:`collections.Counter` instance
self.fired_events = collections.Counter()
def fire_event(self, event, *args, **kwargs):
super(TestEmitter, self).fire_event(event, *args, **kwargs)
self.fired_events[(event, args, tuple(sorted(kwargs.items())))] += 1
def fire_event_pre(self, event, *args, **kwargs):
super(TestEmitter, self).fire_event_pre(event, *args, **kwargs)
self.fired_events[(event, args, tuple(sorted(kwargs.items())))] += 1
class QubesTestCase(unittest.TestCase):
'''Base class for Qubes unit tests.
'''
def __str__(self):
return '{}/{}/{}'.format(
'.'.join(self.__class__.__module__.split('.')[2:]),
self.__class__.__name__,
self._testMethodName)
def assertXMLEqual(self, xml1, xml2):
'''Check for equality of two XML objects.
:param xml1: first element
:param xml2: second element
:type xml1: :py:class:`lxml.etree._Element`
:type xml2: :py:class:`lxml.etree._Element`
'''
self.assertEqual(xml1.tag, xml2.tag)
self.assertEqual(xml1.text, xml2.text)
self.assertItemsEqual(xml1.keys(), xml2.keys())
for key in xml1.keys():
self.assertEqual(xml1.get(key), xml2.get(key))
def assertEventFired(self, emitter, event, args=[], kwargs=[]):
'''Check whether event was fired on given emitter and fail if it did
not.
:param emitter: emitter which is being checked
:type emitter: :py:class:`TestEmitter`
:param str event: event identifier
:param list args: when given, all items must appear in args passed to event
:param list kwargs: when given, all items must appear in kwargs passed to event
'''
for ev, ev_args, ev_kwargs in emitter.fired_events:
if ev != event:
continue
if any(i not in ev_args for i in args):
continue
if any(i not in ev_kwargs for i in kwargs):
continue
return
self.fail('event {!r} did not fire on {!r}'.format(event, emitter))
def assertEventNotFired(self, emitter, event, args=[], kwargs=[]):
'''Check whether event was fired on given emitter. Fail if it did.
:param emitter: emitter which is being checked
:type emitter: :py:class:`TestEmitter`
:param str event: event identifier
:param list args: when given, all items must appear in args passed to event
:param list kwargs: when given, all items must appear in kwargs passed to event
'''
for ev, ev_args, ev_kwargs in emitter.fired_events:
if ev != event:
continue
if any(i not in ev_args for i in args):
continue
if any(i not in ev_kwargs for i in kwargs):
continue
self.fail('event {!r} did fire on {!r}'.format(event, emitter))
return