#!/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