Merge remote-tracking branch 'origin/pr/329'
* origin/pr/329: tests: make PEP8 happier tests: fix already registered event impl
This commit is contained in:
commit
ddc13d6b62
@ -71,13 +71,11 @@ TEMPLATE = 'fedora-23'
|
|||||||
VMPREFIX = 'test-inst-'
|
VMPREFIX = 'test-inst-'
|
||||||
CLSVMPREFIX = 'test-cls-'
|
CLSVMPREFIX = 'test-cls-'
|
||||||
|
|
||||||
|
|
||||||
if 'DEFAULT_LVM_POOL' in os.environ.keys():
|
if 'DEFAULT_LVM_POOL' in os.environ.keys():
|
||||||
DEFAULT_LVM_POOL = os.environ['DEFAULT_LVM_POOL']
|
DEFAULT_LVM_POOL = os.environ['DEFAULT_LVM_POOL']
|
||||||
else:
|
else:
|
||||||
DEFAULT_LVM_POOL = 'qubes_dom0/pool00'
|
DEFAULT_LVM_POOL = 'qubes_dom0/pool00'
|
||||||
|
|
||||||
|
|
||||||
POOL_CONF = {'name': 'test-lvm',
|
POOL_CONF = {'name': 'test-lvm',
|
||||||
'driver': 'lvm_thin',
|
'driver': 'lvm_thin',
|
||||||
'volume_group': DEFAULT_LVM_POOL.split('/')[0],
|
'volume_group': DEFAULT_LVM_POOL.split('/')[0],
|
||||||
@ -92,6 +90,7 @@ in_git = False
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
import libvirt
|
import libvirt
|
||||||
|
|
||||||
libvirt.openReadOnly(qubes.config.defaults['libvirt_uri']).close()
|
libvirt.openReadOnly(qubes.config.defaults['libvirt_uri']).close()
|
||||||
in_dom0 = True
|
in_dom0 = True
|
||||||
except libvirt.libvirtError:
|
except libvirt.libvirtError:
|
||||||
@ -99,8 +98,7 @@ except libvirt.libvirtError:
|
|||||||
|
|
||||||
if in_dom0:
|
if in_dom0:
|
||||||
import libvirtaio
|
import libvirtaio
|
||||||
libvirt_event_impl = libvirtaio.virEventRegisterAsyncIOImpl()
|
|
||||||
else:
|
|
||||||
libvirt_event_impl = None
|
libvirt_event_impl = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -118,37 +116,40 @@ except OSError:
|
|||||||
|
|
||||||
ha_syslog = None
|
ha_syslog = None
|
||||||
|
|
||||||
|
|
||||||
def skipUnlessDom0(test_item):
|
def skipUnlessDom0(test_item):
|
||||||
'''Decorator that skips test outside dom0.
|
"""Decorator that skips test outside dom0.
|
||||||
|
|
||||||
Some tests (especially integration tests) have to be run in more or less
|
Some tests (especially integration tests) have to be run in more or less
|
||||||
working dom0. This is checked by connecting to libvirt.
|
working dom0. This is checked by connecting to libvirt.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
return unittest.skipUnless(in_dom0, 'outside dom0')(test_item)
|
return unittest.skipUnless(in_dom0, 'outside dom0')(test_item)
|
||||||
|
|
||||||
|
|
||||||
def skipUnlessGit(test_item):
|
def skipUnlessGit(test_item):
|
||||||
'''Decorator that skips test outside git repo.
|
"""Decorator that skips test outside git repo.
|
||||||
|
|
||||||
There are very few tests that an be run only in git. One example is
|
There are very few tests that an be run only in git. One example is
|
||||||
correctness of example code that won't get included in RPM.
|
correctness of example code that won't get included in RPM.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
return unittest.skipUnless(in_git, 'outside git tree')(test_item)
|
return unittest.skipUnless(in_git, 'outside git tree')(test_item)
|
||||||
|
|
||||||
|
|
||||||
def skipUnlessEnv(varname):
|
def skipUnlessEnv(varname):
|
||||||
'''Decorator generator for skipping tests without environment variable set.
|
"""Decorator generator for skipping tests without environment variable set.
|
||||||
|
|
||||||
Some tests require working X11 display, like those using GTK library, which
|
Some tests require working X11 display, like those using GTK library, which
|
||||||
segfaults without connection to X.
|
segfaults without connection to X.
|
||||||
Other require their own, custom variables.
|
Other require their own, custom variables.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
return unittest.skipUnless(os.getenv(varname), 'no {} set'.format(varname))
|
return unittest.skipUnless(os.getenv(varname), 'no {} set'.format(varname))
|
||||||
|
|
||||||
|
|
||||||
class TestEmitter(qubes.events.Emitter):
|
class TestEmitter(qubes.events.Emitter):
|
||||||
'''Dummy event emitter which records events fired on it.
|
"""Dummy event emitter which records events fired on it.
|
||||||
|
|
||||||
Events are counted in :py:attr:`fired_events` attribute, which is
|
Events are counted in :py:attr:`fired_events` attribute, which is
|
||||||
:py:class:`collections.Counter` instance. For each event, ``(event, args,
|
:py:class:`collections.Counter` instance. For each event, ``(event, args,
|
||||||
@ -162,7 +163,7 @@ class TestEmitter(qubes.events.Emitter):
|
|||||||
>>> emitter.fire_event('event', spam='eggs', foo='bar')
|
>>> emitter.fire_event('event', spam='eggs', foo='bar')
|
||||||
>>> emitter.fired_events
|
>>> emitter.fired_events
|
||||||
Counter({('event', (1, 2, 3), (('foo', 'bar'), ('spam', 'eggs'))): 1})
|
Counter({('event', (1, 2, 3), (('foo', 'bar'), ('spam', 'eggs'))): 1})
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(TestEmitter, self).__init__(*args, **kwargs)
|
super(TestEmitter, self).__init__(*args, **kwargs)
|
||||||
@ -202,6 +203,7 @@ def expectedFailureIfTemplate(templates):
|
|||||||
handle both 'whonix-ws' and 'whonix-gw'.
|
handle both 'whonix-ws' and 'whonix-gw'.
|
||||||
templates can be either a single string, or an iterable
|
templates can be either a single string, or an iterable
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorator(func):
|
def decorator(func):
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def wrapper(self, *args, **kwargs):
|
def wrapper(self, *args, **kwargs):
|
||||||
@ -219,7 +221,9 @@ def expectedFailureIfTemplate(templates):
|
|||||||
else:
|
else:
|
||||||
# Call directly:
|
# Call directly:
|
||||||
func(self, *args, **kwargs)
|
func(self, *args, **kwargs)
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
@ -243,10 +247,12 @@ def wait_on_fail(func):
|
|||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
reader = asyncio.StreamReader(loop=self.loop)
|
reader = asyncio.StreamReader(loop=self.loop)
|
||||||
transport, protocol = self.loop.run_until_complete(
|
transport, protocol = self.loop.run_until_complete(
|
||||||
self.loop.connect_read_pipe(lambda: asyncio.StreamReaderProtocol(reader),
|
self.loop.connect_read_pipe(
|
||||||
|
lambda: asyncio.StreamReaderProtocol(reader),
|
||||||
sys.stdin))
|
sys.stdin))
|
||||||
self.loop.run_until_complete(reader.readline())
|
self.loop.run_until_complete(reader.readline())
|
||||||
raise
|
raise
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
@ -265,11 +271,9 @@ class _AssertNotRaisesContext(object):
|
|||||||
|
|
||||||
self.failureException = test_case.failureException
|
self.failureException = test_case.failureException
|
||||||
|
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_value, tb):
|
def __exit__(self, exc_type, exc_value, tb):
|
||||||
if exc_type is None:
|
if exc_type is None:
|
||||||
return True
|
return True
|
||||||
@ -284,8 +288,9 @@ class _AssertNotRaisesContext(object):
|
|||||||
|
|
||||||
self.exception = exc_value # store for later retrieval
|
self.exception = exc_value # store for later retrieval
|
||||||
|
|
||||||
|
|
||||||
class _QrexecPolicyContext(object):
|
class _QrexecPolicyContext(object):
|
||||||
'''Context manager for SystemTestCase.qrexec_policy'''
|
"""Context manager for SystemTestCase.qrexec_policy"""
|
||||||
|
|
||||||
def __init__(self, service, source, destination, allow=True, action=None):
|
def __init__(self, service, source, destination, allow=True, action=None):
|
||||||
try:
|
try:
|
||||||
@ -345,8 +350,9 @@ class _QrexecPolicyContext(object):
|
|||||||
self.close()
|
self.close()
|
||||||
self._filename.unlink()
|
self._filename.unlink()
|
||||||
|
|
||||||
|
|
||||||
class substitute_entry_points(object):
|
class substitute_entry_points(object):
|
||||||
'''Monkey-patch pkg_resources to substitute one group in iter_entry_points
|
"""Monkey-patch pkg_resources to substitute one group in iter_entry_points
|
||||||
|
|
||||||
This is for testing plugins, like device classes.
|
This is for testing plugins, like device classes.
|
||||||
|
|
||||||
@ -358,7 +364,7 @@ class substitute_entry_points(object):
|
|||||||
|
|
||||||
This context manager is stackable. To substitute more than one entry point
|
This context manager is stackable. To substitute more than one entry point
|
||||||
group, just nest two contexts.
|
group, just nest two contexts.
|
||||||
''' # pylint: disable=invalid-name
|
""" # pylint: disable=invalid-name
|
||||||
|
|
||||||
def __init__(self, group, tempgroup):
|
def __init__(self, group, tempgroup):
|
||||||
self.group = group
|
self.group = group
|
||||||
@ -381,8 +387,8 @@ class substitute_entry_points(object):
|
|||||||
|
|
||||||
|
|
||||||
class QubesTestCase(unittest.TestCase):
|
class QubesTestCase(unittest.TestCase):
|
||||||
'''Base class for Qubes unit tests.
|
"""Base class for Qubes unit tests.
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(QubesTestCase, self).__init__(*args, **kwargs)
|
super(QubesTestCase, self).__init__(*args, **kwargs)
|
||||||
@ -396,6 +402,10 @@ class QubesTestCase(unittest.TestCase):
|
|||||||
|
|
||||||
self.loop = None
|
self.loop = None
|
||||||
|
|
||||||
|
global libvirt_event_impl
|
||||||
|
|
||||||
|
if in_dom0 and not libvirt_event_impl:
|
||||||
|
libvirt_event_impl = libvirtaio.virEventRegisterAsyncIOImpl()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '{}/{}/{}'.format(
|
return '{}/{}/{}'.format(
|
||||||
@ -403,7 +413,6 @@ class QubesTestCase(unittest.TestCase):
|
|||||||
self.__class__.__name__,
|
self.__class__.__name__,
|
||||||
self._testMethodName)
|
self._testMethodName)
|
||||||
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
self.addCleanup(self.cleanup_gc)
|
self.addCleanup(self.cleanup_gc)
|
||||||
@ -413,9 +422,9 @@ class QubesTestCase(unittest.TestCase):
|
|||||||
self.addCleanup(self.cleanup_traceback)
|
self.addCleanup(self.cleanup_traceback)
|
||||||
|
|
||||||
def cleanup_traceback(self):
|
def cleanup_traceback(self):
|
||||||
'''Remove local variables reference from tracebacks to allow garbage
|
"""Remove local variables reference from tracebacks to allow garbage
|
||||||
collector to clean all Qubes*() objects, otherwise file descriptors
|
collector to clean all Qubes*() objects, otherwise file descriptors
|
||||||
held by them will leak'''
|
held by them will leak"""
|
||||||
exc_infos = [e for test_case, e in self._outcome.errors
|
exc_infos = [e for test_case, e in self._outcome.errors
|
||||||
if test_case is self]
|
if test_case is self]
|
||||||
if self._outcome.expectedFailure:
|
if self._outcome.expectedFailure:
|
||||||
@ -442,7 +451,8 @@ class QubesTestCase(unittest.TestCase):
|
|||||||
import objgraph
|
import objgraph
|
||||||
objgraph.show_backrefs(leaked,
|
objgraph.show_backrefs(leaked,
|
||||||
max_depth=15, extra_info=extra_info,
|
max_depth=15, extra_info=extra_info,
|
||||||
filename='/tmp/objgraph-{}.png'.format(self.id()))
|
filename='/tmp/objgraph-{}.png'.format(
|
||||||
|
self.id()))
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -451,7 +461,7 @@ class QubesTestCase(unittest.TestCase):
|
|||||||
assert not leaked
|
assert not leaked
|
||||||
|
|
||||||
def cleanup_loop(self):
|
def cleanup_loop(self):
|
||||||
'''Check if the loop is empty'''
|
"""Check if the loop is empty"""
|
||||||
# XXX BEWARE this is touching undocumented, implementation-specific
|
# XXX BEWARE this is touching undocumented, implementation-specific
|
||||||
# attributes of the loop. This is most certainly unsupported and likely
|
# attributes of the loop. This is most certainly unsupported and likely
|
||||||
# will break when messing with: Python version, kernel family, loop
|
# will break when messing with: Python version, kernel family, loop
|
||||||
@ -521,15 +531,14 @@ class QubesTestCase(unittest.TestCase):
|
|||||||
with context:
|
with context:
|
||||||
callableObj(*args, **kwargs)
|
callableObj(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def assertXMLEqual(self, xml1, xml2, msg=''):
|
def assertXMLEqual(self, xml1, xml2, msg=''):
|
||||||
'''Check for equality of two XML objects.
|
"""Check for equality of two XML objects.
|
||||||
|
|
||||||
:param xml1: first element
|
:param xml1: first element
|
||||||
:param xml2: second element
|
:param xml2: second element
|
||||||
:type xml1: :py:class:`lxml.etree._Element`
|
:type xml1: :py:class:`lxml.etree._Element`
|
||||||
:type xml2: :py:class:`lxml.etree._Element`
|
:type xml2: :py:class:`lxml.etree._Element`
|
||||||
'''
|
"""
|
||||||
|
|
||||||
self.assertEqual(xml1.tag, xml2.tag)
|
self.assertEqual(xml1.tag, xml2.tag)
|
||||||
msg += '/' + str(xml1.tag)
|
msg += '/' + str(xml1.tag)
|
||||||
@ -557,7 +566,7 @@ class QubesTestCase(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def assertEventFired(self, subject, event, kwargs=None):
|
def assertEventFired(self, subject, event, kwargs=None):
|
||||||
'''Check whether event was fired on given emitter and fail if it did
|
"""Check whether event was fired on given emitter and fail if it did
|
||||||
not.
|
not.
|
||||||
|
|
||||||
:param subject: emitter which is being checked
|
:param subject: emitter which is being checked
|
||||||
@ -565,7 +574,7 @@ class QubesTestCase(unittest.TestCase):
|
|||||||
:param str event: event identifier
|
:param str event: event identifier
|
||||||
:param dict kwargs: when given, all items must appear in kwargs passed \
|
:param dict kwargs: when given, all items must appear in kwargs passed \
|
||||||
to an event
|
to an event
|
||||||
'''
|
"""
|
||||||
|
|
||||||
will_not_match = object()
|
will_not_match = object()
|
||||||
for ev, ev_kwargs in subject.fired_events:
|
for ev, ev_kwargs in subject.fired_events:
|
||||||
@ -582,16 +591,15 @@ class QubesTestCase(unittest.TestCase):
|
|||||||
self.fail('event {!r} {}did not fire on {!r}'.format(
|
self.fail('event {!r} {}did not fire on {!r}'.format(
|
||||||
event, ('' if kwargs is None else '{!r} '.format(kwargs)), subject))
|
event, ('' if kwargs is None else '{!r} '.format(kwargs)), subject))
|
||||||
|
|
||||||
|
|
||||||
def assertEventNotFired(self, subject, event, kwargs=None):
|
def assertEventNotFired(self, subject, event, kwargs=None):
|
||||||
'''Check whether event was fired on given emitter. Fail if it did.
|
"""Check whether event was fired on given emitter. Fail if it did.
|
||||||
|
|
||||||
:param subject: emitter which is being checked
|
:param subject: emitter which is being checked
|
||||||
:type emitter: :py:class:`TestEmitter`
|
:type emitter: :py:class:`TestEmitter`
|
||||||
:param str event: event identifier
|
:param str event: event identifier
|
||||||
:param list kwargs: when given, all items must appear in kwargs passed \
|
:param list kwargs: when given, all items must appear in kwargs passed \
|
||||||
to an event
|
to an event
|
||||||
'''
|
"""
|
||||||
|
|
||||||
will_not_match = object()
|
will_not_match = object()
|
||||||
for ev, ev_kwargs in subject.fired_events:
|
for ev, ev_kwargs in subject.fired_events:
|
||||||
@ -610,9 +618,8 @@ class QubesTestCase(unittest.TestCase):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
def assertXMLIsValid(self, xml, file=None, schema=None):
|
def assertXMLIsValid(self, xml, file=None, schema=None):
|
||||||
'''Check whether given XML fulfills Relax NG schema.
|
"""Check whether given XML fulfills Relax NG schema.
|
||||||
|
|
||||||
Schema can be given in a couple of ways:
|
Schema can be given in a couple of ways:
|
||||||
|
|
||||||
@ -625,7 +632,7 @@ class QubesTestCase(unittest.TestCase):
|
|||||||
:param lxml.etree._Element xml: XML element instance to check
|
:param lxml.etree._Element xml: XML element instance to check
|
||||||
:param str file: filename of Relax NG schema
|
:param str file: filename of Relax NG schema
|
||||||
:param str schema: optional explicit schema string
|
:param str schema: optional explicit schema string
|
||||||
''' # pylint: disable=redefined-builtin
|
""" # pylint: disable=redefined-builtin
|
||||||
|
|
||||||
if schema is not None and file is None:
|
if schema is not None and file is None:
|
||||||
relaxng = schema
|
relaxng = schema
|
||||||
@ -691,6 +698,7 @@ class SystemTestCase(QubesTestCase):
|
|||||||
Such (group of) test need to take care about
|
Such (group of) test need to take care about
|
||||||
:py:meth:`TestCase.tearDownClass` implementation itself.
|
:py:meth:`TestCase.tearDownClass` implementation itself.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# noinspection PyAttributeOutsideInit
|
# noinspection PyAttributeOutsideInit
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
if not in_dom0:
|
if not in_dom0:
|
||||||
@ -803,11 +811,10 @@ class SystemTestCase(QubesTestCase):
|
|||||||
|
|
||||||
self.app.default_netvm = str(default_netvm)
|
self.app.default_netvm = str(default_netvm)
|
||||||
|
|
||||||
|
|
||||||
def _find_pool(self, volume_group, thin_pool):
|
def _find_pool(self, volume_group, thin_pool):
|
||||||
''' Returns the pool matching the specified ``volume_group`` &
|
""" Returns the pool matching the specified ``volume_group`` &
|
||||||
``thin_pool``, or None.
|
``thin_pool``, or None.
|
||||||
'''
|
"""
|
||||||
pools = [p for p in self.app.pools
|
pools = [p for p in self.app.pools
|
||||||
if issubclass(p.__class__, qubes.storage.lvm.ThinPool)]
|
if issubclass(p.__class__, qubes.storage.lvm.ThinPool)]
|
||||||
for pool in pools:
|
for pool in pools:
|
||||||
@ -865,7 +872,6 @@ class SystemTestCase(QubesTestCase):
|
|||||||
|
|
||||||
self._remove_vm_disk(vmname)
|
self._remove_vm_disk(vmname)
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _remove_vm_libvirt(dom):
|
def _remove_vm_libvirt(dom):
|
||||||
try:
|
try:
|
||||||
@ -874,7 +880,6 @@ class SystemTestCase(QubesTestCase):
|
|||||||
pass
|
pass
|
||||||
dom.undefine()
|
dom.undefine()
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _remove_vm_disk(vmname):
|
def _remove_vm_disk(vmname):
|
||||||
for dirspec in (
|
for dirspec in (
|
||||||
@ -890,11 +895,11 @@ class SystemTestCase(QubesTestCase):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _remove_vm_disk_lvm(prefix=VMPREFIX):
|
def _remove_vm_disk_lvm(prefix=VMPREFIX):
|
||||||
''' Remove LVM volumes with given prefix
|
""" Remove LVM volumes with given prefix
|
||||||
|
|
||||||
This is "a bit" drastic, as it removes volumes regardless of volume
|
This is "a bit" drastic, as it removes volumes regardless of volume
|
||||||
group, thin pool etc. But we assume no important data on test system.
|
group, thin pool etc. But we assume no important data on test system.
|
||||||
'''
|
"""
|
||||||
try:
|
try:
|
||||||
volumes = subprocess.check_output(
|
volumes = subprocess.check_output(
|
||||||
['lvs', '--noheadings', '-o', 'vg_name,name',
|
['lvs', '--noheadings', '-o', 'vg_name,name',
|
||||||
@ -972,10 +977,10 @@ class SystemTestCase(QubesTestCase):
|
|||||||
vm.startup_lock.release()
|
vm.startup_lock.release()
|
||||||
|
|
||||||
def remove_test_vms(self, xmlpath=XMLPATH, prefix=VMPREFIX):
|
def remove_test_vms(self, xmlpath=XMLPATH, prefix=VMPREFIX):
|
||||||
'''Aggressively remove any domain that has name in testing namespace.
|
"""Aggressively remove any domain that has name in testing namespace.
|
||||||
|
|
||||||
:param prefix: name prefix of VMs to remove, can be a list of prefixes
|
:param prefix: name prefix of VMs to remove, can be a list of prefixes
|
||||||
'''
|
"""
|
||||||
|
|
||||||
if isinstance(prefix, str):
|
if isinstance(prefix, str):
|
||||||
prefixes = [prefix]
|
prefixes = [prefix]
|
||||||
@ -994,8 +999,10 @@ class SystemTestCase(QubesTestCase):
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
host_app = qubes.Qubes()
|
host_app = qubes.Qubes()
|
||||||
self.remove_vms([vm for vm in app.domains
|
self.remove_vms([vm for vm in app.domains
|
||||||
if any(vm.name.startswith(prefix) for prefix in prefixes) or
|
if any(
|
||||||
(isinstance(vm, qubes.vm.dispvm.DispVM) and vm.name
|
vm.name.startswith(prefix) for prefix in prefixes) or
|
||||||
|
(isinstance(vm,
|
||||||
|
qubes.vm.dispvm.DispVM) and vm.name
|
||||||
not in host_app.domains)])
|
not in host_app.domains)])
|
||||||
if not hasattr(self, 'host_app'):
|
if not hasattr(self, 'host_app'):
|
||||||
host_app.close()
|
host_app.close()
|
||||||
@ -1055,7 +1062,8 @@ class SystemTestCase(QubesTestCase):
|
|||||||
"""
|
"""
|
||||||
wait_count = 0
|
wait_count = 0
|
||||||
while subprocess.call(['xdotool', 'getwindowname', str(winid)],
|
while subprocess.call(['xdotool', 'getwindowname', str(winid)],
|
||||||
stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) == 0:
|
stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.STDOUT) == 0:
|
||||||
wait_count += 1
|
wait_count += 1
|
||||||
if wait_count > timeout * 10:
|
if wait_count > timeout * 10:
|
||||||
self.fail("Timeout while waiting for {}({}) window to "
|
self.fail("Timeout while waiting for {}({}) window to "
|
||||||
@ -1147,7 +1155,8 @@ class SystemTestCase(QubesTestCase):
|
|||||||
|
|
||||||
def shutdown_and_wait(self, vm, timeout=60):
|
def shutdown_and_wait(self, vm, timeout=60):
|
||||||
try:
|
try:
|
||||||
self.loop.run_until_complete(vm.shutdown(wait=True, timeout=timeout))
|
self.loop.run_until_complete(
|
||||||
|
vm.shutdown(wait=True, timeout=timeout))
|
||||||
except qubes.exc.QubesException:
|
except qubes.exc.QubesException:
|
||||||
name = vm.name
|
name = vm.name
|
||||||
del vm
|
del vm
|
||||||
@ -1225,9 +1234,9 @@ class SystemTestCase(QubesTestCase):
|
|||||||
subprocess.check_call(['sudo', 'losetup', '-d', loopdev])
|
subprocess.check_call(['sudo', 'losetup', '-d', loopdev])
|
||||||
|
|
||||||
def create_bootable_iso(self):
|
def create_bootable_iso(self):
|
||||||
'''Create simple bootable ISO image.
|
"""Create simple bootable ISO image.
|
||||||
Type 'poweroff' to it to terminate that VM.
|
Type 'poweroff' to it to terminate that VM.
|
||||||
'''
|
"""
|
||||||
isolinux_cfg = (
|
isolinux_cfg = (
|
||||||
'prompt 1\n'
|
'prompt 1\n'
|
||||||
'label poweroff\n'
|
'label poweroff\n'
|
||||||
@ -1279,8 +1288,10 @@ class SystemTestCase(QubesTestCase):
|
|||||||
|
|
||||||
|
|
||||||
_templates = None
|
_templates = None
|
||||||
|
|
||||||
|
|
||||||
def list_templates():
|
def list_templates():
|
||||||
'''Returns tuple of template names available in the system.'''
|
"""Returns tuple of template names available in the system."""
|
||||||
global _templates
|
global _templates
|
||||||
if _templates is None:
|
if _templates is None:
|
||||||
if 'QUBES_TEST_TEMPLATES' in os.environ:
|
if 'QUBES_TEST_TEMPLATES' in os.environ:
|
||||||
@ -1289,7 +1300,8 @@ def list_templates():
|
|||||||
try:
|
try:
|
||||||
app = qubes.Qubes()
|
app = qubes.Qubes()
|
||||||
_templates = tuple(vm.name for vm in app.domains
|
_templates = tuple(vm.name for vm in app.domains
|
||||||
if isinstance(vm, qubes.vm.templatevm.TemplateVM) and
|
if isinstance(vm,
|
||||||
|
qubes.vm.templatevm.TemplateVM) and
|
||||||
vm.features.get('os', None) != 'Windows')
|
vm.features.get('os', None) != 'Windows')
|
||||||
app.close()
|
app.close()
|
||||||
del app
|
del app
|
||||||
@ -1297,8 +1309,9 @@ def list_templates():
|
|||||||
_templates = ()
|
_templates = ()
|
||||||
return _templates
|
return _templates
|
||||||
|
|
||||||
|
|
||||||
def create_testcases_for_templates(name, *bases, module, **kwds):
|
def create_testcases_for_templates(name, *bases, module, **kwds):
|
||||||
'''Do-it-all helper for generating per-template tests via load_tests proto
|
"""Do-it-all helper for generating per-template tests via load_tests proto
|
||||||
|
|
||||||
This does several things:
|
This does several things:
|
||||||
- creates per-template classes
|
- creates per-template classes
|
||||||
@ -1324,7 +1337,7 @@ def create_testcases_for_templates(name, *bases, module, **kwds):
|
|||||||
*NOTE* adding ``module=sys.modules[__name__]`` is *mandatory*, and to allow
|
*NOTE* adding ``module=sys.modules[__name__]`` is *mandatory*, and to allow
|
||||||
enforcing this, it uses keyword-only argument syntax, which is only in
|
enforcing this, it uses keyword-only argument syntax, which is only in
|
||||||
Python 3.
|
Python 3.
|
||||||
'''
|
"""
|
||||||
# Do not attempt to grab the module from traceback, since we are actually
|
# Do not attempt to grab the module from traceback, since we are actually
|
||||||
# a generator and loadTestsFromNames may also be a generator, so it's not
|
# a generator and loadTestsFromNames may also be a generator, so it's not
|
||||||
# possible to correctly guess frame from stack. Explicit is better than
|
# possible to correctly guess frame from stack. Explicit is better than
|
||||||
@ -1340,8 +1353,9 @@ def create_testcases_for_templates(name, *bases, module, **kwds):
|
|||||||
setattr(module, clsname, cls)
|
setattr(module, clsname, cls)
|
||||||
yield '.'.join((module.__name__, clsname))
|
yield '.'.join((module.__name__, clsname))
|
||||||
|
|
||||||
|
|
||||||
def maybe_create_testcases_on_import(create_testcases_gen):
|
def maybe_create_testcases_on_import(create_testcases_gen):
|
||||||
'''If certain conditions are met, call *create_testcases_gen* to create
|
"""If certain conditions are met, call *create_testcases_gen* to create
|
||||||
testcases for templates tests. The purpose is to use it on integration
|
testcases for templates tests. The purpose is to use it on integration
|
||||||
tests module(s) import, so the test runner could discover tests without
|
tests module(s) import, so the test runner could discover tests without
|
||||||
using load tests protocol.
|
using load tests protocol.
|
||||||
@ -1350,17 +1364,18 @@ def maybe_create_testcases_on_import(create_testcases_gen):
|
|||||||
- QUBES_TEST_TEMPLATES present in the environment (it's possible to
|
- QUBES_TEST_TEMPLATES present in the environment (it's possible to
|
||||||
create test cases without opening qubes.xml)
|
create test cases without opening qubes.xml)
|
||||||
- QUBES_TEST_LOAD_ALL present in the environment
|
- QUBES_TEST_LOAD_ALL present in the environment
|
||||||
'''
|
"""
|
||||||
if 'QUBES_TEST_TEMPLATES' in os.environ or \
|
if 'QUBES_TEST_TEMPLATES' in os.environ or \
|
||||||
'QUBES_TEST_LOAD_ALL' in os.environ:
|
'QUBES_TEST_LOAD_ALL' in os.environ:
|
||||||
list(create_testcases_gen())
|
list(create_testcases_gen())
|
||||||
|
|
||||||
|
|
||||||
def extra_info(obj):
|
def extra_info(obj):
|
||||||
'''Return short info identifying object.
|
"""Return short info identifying object.
|
||||||
|
|
||||||
For example, if obj is a qube, return its name. This is for use with
|
For example, if obj is a qube, return its name. This is for use with
|
||||||
:py:mod:`objgraph` package.
|
:py:mod:`objgraph` package.
|
||||||
'''
|
"""
|
||||||
# Feel free to extend to other cases.
|
# Feel free to extend to other cases.
|
||||||
|
|
||||||
if isinstance(obj, qubes.vm.qubesvm.QubesVM):
|
if isinstance(obj, qubes.vm.qubesvm.QubesVM):
|
||||||
@ -1373,6 +1388,7 @@ def extra_info(obj):
|
|||||||
|
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
|
||||||
def load_tests(loader, tests, pattern): # pylint: disable=unused-argument
|
def load_tests(loader, tests, pattern): # pylint: disable=unused-argument
|
||||||
# discard any tests from this module, because it hosts base classes
|
# discard any tests from this module, because it hosts base classes
|
||||||
tests = unittest.TestSuite()
|
tests = unittest.TestSuite()
|
||||||
|
Loading…
Reference in New Issue
Block a user