From dfe7688158a4463b689527d273e8b7f400d8f923 Mon Sep 17 00:00:00 2001 From: Wojtek Porczyk Date: Fri, 30 Mar 2018 03:04:15 +0200 Subject: [PATCH 1/2] qubes/tests: use loadTestsFromNames for nose2 compat --- qubes/tests/__init__.py | 44 ++++++++++++++++++++++++++++++ qubes/tests/extra.py | 18 +++++------- qubes/tests/integ/backup.py | 11 +++----- qubes/tests/integ/basic.py | 19 ++++++------- qubes/tests/integ/dispvm.py | 11 +++----- qubes/tests/integ/dom0_update.py | 11 +++----- qubes/tests/integ/network.py | 28 ++++++++----------- qubes/tests/integ/pvgrub.py | 11 +++----- qubes/tests/integ/salt.py | 10 +++---- qubes/tests/integ/vm_qrexec_gui.py | 11 +++----- 10 files changed, 95 insertions(+), 79 deletions(-) diff --git a/qubes/tests/__init__.py b/qubes/tests/__init__.py index 367e269e..4c492827 100644 --- a/qubes/tests/__init__.py +++ b/qubes/tests/__init__.py @@ -1127,6 +1127,50 @@ def list_templates(): _templates = () return _templates +def create_testcases_for_templates(name, *bases, globals, **kwds): + '''Do-it-all helper for generating per-template tests via load_tests proto + + This does several things: + - creates per-template classes + - adds them to module's :py:func:`globals` + - returns an iterable suitable for passing to loader.loadTestsFromNames + + TestCase classes created by this function have implicit `.template` + attribute, which contains name of the respective template. They are also + named with given prefix, underscore and template name. If template name + contains characters not valid as part of Python identifier, they are + impossible to get via standard ``.`` operator, though :py:func:`getattr` is + still usable. + + >>> class MyTestsMixIn: + ... def test_000_my_test(self): + ... assert self.template.startswith('debian') + >>> def load_tests(loader, tests, pattern): + ... tests.addTests(loader.loadTestsFromNames( + ... qubes.tests.create_testcases_for_templates( + ... 'TC_00_MyTests', MyTestsMixIn, qubes.tests.SystemTestCase, + ... globals=globals()))) + + *NOTE* adding ``globals=globals()`` is *mandatory*, and to allow enforcing + this, it uses keyword-only argument syntax, which is Python 3 only. + ''' + + # NOTE globals is passed from calling function, and has slightly different + # semantics (no ``()``). + # + # Do not attempt to grab from traceback, since we are actually a generator + # and loadTestsFromNames may also be a generator, so it's not possible to + # correctly guess frame from stack. Explicit is better than implicit! + + module = globals['__name__'] + for template in list_templates(): + clsname = name + '_' + template + cls = type(clsname, bases, {'template': template, **kwds}) + cls.__module__ = module + # XXX I wonder what other __dunder__ attrs did I miss + globals[clsname] = cls + yield '.'.join((module, clsname)) + def extra_info(obj): '''Return short info identifying object. diff --git a/qubes/tests/extra.py b/qubes/tests/extra.py index 215200e7..4ee6534e 100644 --- a/qubes/tests/extra.py +++ b/qubes/tests/extra.py @@ -18,11 +18,12 @@ # License along with this library; if not, see . # -import sys - import asyncio import subprocess +import sys + import pkg_resources + import qubes.tests import qubes.vm.appvm @@ -206,15 +207,10 @@ def load_tests(loader, tests, pattern): 'qubes.tests.extra.for_template'): try: for test_case in entry.load()(): - for template in qubes.tests.list_templates(): - tests.addTests(loader.loadTestsFromTestCase( - type( - '{}_{}_{}'.format( - entry.name, test_case.__name__, template), - (test_case,), - {'template': template} - ) - )) + test.addTests(loader.loadTestsFromNames( + qubes.tests.create_testcases_for_templates( + test_case.__name__, test_case, + globals=sys.modules[test_case.__module__].__dict__))) except Exception as err: # pylint: disable=broad-except def runTest(self): raise err diff --git a/qubes/tests/integ/backup.py b/qubes/tests/integ/backup.py index cf1576d7..2954e945 100644 --- a/qubes/tests/integ/backup.py +++ b/qubes/tests/integ/backup.py @@ -651,11 +651,8 @@ class TC_10_BackupVMMixin(BackupTestsMixin): def load_tests(loader, tests, pattern): - for template in qubes.tests.list_templates(): - tests.addTests(loader.loadTestsFromTestCase( - type( - 'TC_10_BackupVM_' + template, - (TC_10_BackupVMMixin, qubes.tests.SystemTestCase), - {'template': template}))) - + tests.addTests(loader.loadTestsFromNames( + qubes.tests.create_testcases_for_templates('TC_10_BackupVM', + TC_10_BackupVMMixin, qubes.tests.SystemTestCase, + globals=globals()))) return tests diff --git a/qubes/tests/integ/basic.py b/qubes/tests/integ/basic.py index 9bd06987..5b0eb089 100644 --- a/qubes/tests/integ/basic.py +++ b/qubes/tests/integ/basic.py @@ -793,17 +793,14 @@ class TC_06_AppVMMixin(object): def load_tests(loader, tests, pattern): - for template in qubes.tests.list_templates(): - tests.addTests(loader.loadTestsFromTestCase( - type( - 'TC_05_StandaloneVM_' + template, - (TC_05_StandaloneVMMixin, qubes.tests.SystemTestCase), - {'template': template}))) - tests.addTests(loader.loadTestsFromTestCase( - type( - 'TC_06_AppVM_' + template, - (TC_06_AppVMMixin, qubes.tests.SystemTestCase), - {'template': template}))) + tests.addTests(loader.loadTestsFromNames( + qubes.tests.create_testcases_for_templates('TC_05_StandaloneVM', + TC_05_StandaloneVMMixin, qubes.tests.SystemTestCase, + globals=globals()))) + tests.addTests(loader.loadTestsFromNames( + qubes.tests.create_testcases_for_templates('TC_06_AppVM', + TC_06_AppVMMixin, qubes.tests.SystemTestCase, + globals=globals()))) return tests diff --git a/qubes/tests/integ/dispvm.py b/qubes/tests/integ/dispvm.py index 10aa1e66..7b1abd71 100644 --- a/qubes/tests/integ/dispvm.py +++ b/qubes/tests/integ/dispvm.py @@ -282,11 +282,8 @@ class TC_20_DispVMMixin(object): self.assertEqual(test_txt_content, b"Test test 2\ntest1\n") def load_tests(loader, tests, pattern): - for template in qubes.tests.list_templates(): - tests.addTests(loader.loadTestsFromTestCase( - type( - 'TC_20_DispVM_' + template, - (TC_20_DispVMMixin, qubes.tests.SystemTestCase), - {'template': template}))) - + tests.addTests(loader.loadTestsFromNames( + qubes.tests.create_testcases_for_templates('TC_20_DispVM', + TC_20_DispVMMixin, qubes.tests.SystemTestCase, + globals=globals()))) return tests diff --git a/qubes/tests/integ/dom0_update.py b/qubes/tests/integ/dom0_update.py index 91aef182..12b8b6e2 100644 --- a/qubes/tests/integ/dom0_update.py +++ b/qubes/tests/integ/dom0_update.py @@ -386,11 +386,8 @@ Test package def load_tests(loader, tests, pattern): - for template in qubes.tests.list_templates(): - tests.addTests(loader.loadTestsFromTestCase( - type( - 'TC_00_Dom0Upgrade_' + template, - (TC_00_Dom0UpgradeMixin, qubes.tests.SystemTestCase), - {'template': template}))) - + tests.addTests(loader.loadTestsFromNames( + qubes.tests.create_testcases_for_templates('TC_00_Dom0Upgrade', + TC_00_Dom0UpgradeMixin, qubes.tests.SystemTestCase + globals=globals()))) return tests diff --git a/qubes/tests/integ/network.py b/qubes/tests/integ/network.py index cfca09bf..cafd7226 100644 --- a/qubes/tests/integ/network.py +++ b/qubes/tests/integ/network.py @@ -1323,20 +1323,16 @@ SHA256: '{}: {}\n{}'.format(self.update_cmd, stdout, stderr)) def load_tests(loader, tests, pattern): - for template in qubes.tests.list_templates(): - tests.addTests(loader.loadTestsFromTestCase( - type( - 'VmNetworking_' + template, - (VmNetworkingMixin, qubes.tests.SystemTestCase), - {'template': template}))) - tests.addTests(loader.loadTestsFromTestCase( - type( - 'VmIPv6Networking_' + template, - (VmIPv6NetworkingMixin, qubes.tests.SystemTestCase), - {'template': template}))) - tests.addTests(loader.loadTestsFromTestCase( - type( - 'VmUpdates_' + template, - (VmUpdatesMixin, qubes.tests.SystemTestCase), - {'template': template}))) + tests.addTests(loader.loadTestsFromNames( + qubes.tests.create_testcases_for_templates('VmNetworking', + VmNetworkingMixin, qubes.tests.SystemTestCase + globals=globals()))) + tests.addTests(loader.loadTestsFromNames( + qubes.tests.create_testcases_for_templates('VmIPv6Networking', + VmIPv6NetworkingMixin, qubes.tests.SystemTestCase + globals=globals()))) + tests.addTests(loader.loadTestsFromNames( + qubes.tests.create_testcases_for_templates('VmUpdates', + VmUpdates, qubes.tests.SystemTestCase + globals=globals()))) return tests diff --git a/qubes/tests/integ/pvgrub.py b/qubes/tests/integ/pvgrub.py index 92a12f29..3498651b 100644 --- a/qubes/tests/integ/pvgrub.py +++ b/qubes/tests/integ/pvgrub.py @@ -136,11 +136,8 @@ class TC_40_PVGrub(object): def load_tests(loader, tests, pattern): - for template in qubes.tests.list_templates(): - tests.addTests(loader.loadTestsFromTestCase( - type( - 'TC_40_PVGrub_' + template, - (TC_40_PVGrub, qubes.tests.SystemTestCase), - {'template': template}))) - + tests.addTests(loader.loadTestsFromNames( + qubes.tests.create_testcases_for_templates('TC_40_PVGrub', + TC_40_PVGrub, qubes.tests.SystemTestCase + globals=globals()))) return tests diff --git a/qubes/tests/integ/salt.py b/qubes/tests/integ/salt.py index 57497c6c..09f35be6 100644 --- a/qubes/tests/integ/salt.py +++ b/qubes/tests/integ/salt.py @@ -392,10 +392,8 @@ class SaltVMTestMixin(SaltTestMixin): def load_tests(loader, tests, pattern): - for template in qubes.tests.list_templates(): - tests.addTests(loader.loadTestsFromTestCase( - type( - 'TC_10_VMSalt_' + template, - (SaltVMTestMixin, qubes.tests.SystemTestCase), - {'template': template}))) + tests.addTests(loader.loadTestsFromNames( + qubes.tests.create_testcases_for_templates('TC_10_VMSalt', + SaltVMTestMixin, qubes.tests.SystemTestCase + globals=globals()))) return tests diff --git a/qubes/tests/integ/vm_qrexec_gui.py b/qubes/tests/integ/vm_qrexec_gui.py index e818c8cd..a2a7f144 100644 --- a/qubes/tests/integ/vm_qrexec_gui.py +++ b/qubes/tests/integ/vm_qrexec_gui.py @@ -1012,11 +1012,8 @@ class TC_10_Generic(qubes.tests.SystemTestCase): def load_tests(loader, tests, pattern): - for template in qubes.tests.list_templates(): - tests.addTests(loader.loadTestsFromTestCase( - type( - 'TC_00_AppVM_' + template, - (TC_00_AppVMMixin, qubes.tests.SystemTestCase), - {'template': template}))) - + tests.addTests(loader.loadTestsFromNames( + qubes.tests.create_testcases_for_templates('TC_00_AppVM', + TC_00_AppVMMixin, qubes.tests.SystemTestCase + globals=globals()))) return tests From 843bbdb2c5a9a7c64e9e313c6582e68e5033a4f5 Mon Sep 17 00:00:00 2001 From: Wojtek Porczyk Date: Tue, 24 Apr 2018 18:30:55 +0200 Subject: [PATCH 2/2] qubes/tests: change globals= to module= and fix syntax errors --- qubes/tests/__init__.py | 27 ++++++++++++--------------- qubes/tests/integ/dom0_update.py | 8 ++++---- qubes/tests/integ/network.py | 15 ++++++++------- qubes/tests/integ/pvgrub.py | 7 +++++-- qubes/tests/integ/salt.py | 14 +++++++------- qubes/tests/integ/vm_qrexec_gui.py | 5 +++-- 6 files changed, 39 insertions(+), 37 deletions(-) diff --git a/qubes/tests/__init__.py b/qubes/tests/__init__.py index 4c492827..08c58bb6 100644 --- a/qubes/tests/__init__.py +++ b/qubes/tests/__init__.py @@ -1127,7 +1127,7 @@ def list_templates(): _templates = () return _templates -def create_testcases_for_templates(name, *bases, globals, **kwds): +def create_testcases_for_templates(name, *bases, module, **kwds): '''Do-it-all helper for generating per-template tests via load_tests proto This does several things: @@ -1149,27 +1149,24 @@ def create_testcases_for_templates(name, *bases, globals, **kwds): ... tests.addTests(loader.loadTestsFromNames( ... qubes.tests.create_testcases_for_templates( ... 'TC_00_MyTests', MyTestsMixIn, qubes.tests.SystemTestCase, - ... globals=globals()))) + ... module=sys.modules[__name__]))) - *NOTE* adding ``globals=globals()`` is *mandatory*, and to allow enforcing - this, it uses keyword-only argument syntax, which is Python 3 only. + *NOTE* adding ``module=sys.modules[__name__]`` is *mandatory*, and to allow + enforcing this, it uses keyword-only argument syntax, which is only in + Python 3. ''' + # 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 + # possible to correctly guess frame from stack. Explicit is better than + # implicit! - # NOTE globals is passed from calling function, and has slightly different - # semantics (no ``()``). - # - # Do not attempt to grab from traceback, since we are actually a generator - # and loadTestsFromNames may also be a generator, so it's not possible to - # correctly guess frame from stack. Explicit is better than implicit! - - module = globals['__name__'] for template in list_templates(): clsname = name + '_' + template cls = type(clsname, bases, {'template': template, **kwds}) - cls.__module__ = module + cls.__module__ = module.__name__ # XXX I wonder what other __dunder__ attrs did I miss - globals[clsname] = cls - yield '.'.join((module, clsname)) + setattr(module, clsname, cls) + yield '.'.join((module.__name__, clsname)) def extra_info(obj): '''Return short info identifying object. diff --git a/qubes/tests/integ/dom0_update.py b/qubes/tests/integ/dom0_update.py index 12b8b6e2..a289396f 100644 --- a/qubes/tests/integ/dom0_update.py +++ b/qubes/tests/integ/dom0_update.py @@ -18,14 +18,14 @@ # USA. # +import asyncio import os import shutil import subprocess +import sys import tempfile import unittest -import asyncio - import qubes import qubes.tests @@ -388,6 +388,6 @@ Test package def load_tests(loader, tests, pattern): tests.addTests(loader.loadTestsFromNames( qubes.tests.create_testcases_for_templates('TC_00_Dom0Upgrade', - TC_00_Dom0UpgradeMixin, qubes.tests.SystemTestCase - globals=globals()))) + TC_00_Dom0UpgradeMixin, qubes.tests.SystemTestCase, + module=sys.modules[__name__]))) return tests diff --git a/qubes/tests/integ/network.py b/qubes/tests/integ/network.py index cafd7226..b9382e9f 100644 --- a/qubes/tests/integ/network.py +++ b/qubes/tests/integ/network.py @@ -25,8 +25,9 @@ import asyncio import multiprocessing import os import subprocess -import unittest +import sys import time +import unittest import qubes.tests import qubes.firewall @@ -1325,14 +1326,14 @@ SHA256: def load_tests(loader, tests, pattern): tests.addTests(loader.loadTestsFromNames( qubes.tests.create_testcases_for_templates('VmNetworking', - VmNetworkingMixin, qubes.tests.SystemTestCase - globals=globals()))) + VmNetworkingMixin, qubes.tests.SystemTestCase, + module=sys.modules[__name__]))) tests.addTests(loader.loadTestsFromNames( qubes.tests.create_testcases_for_templates('VmIPv6Networking', - VmIPv6NetworkingMixin, qubes.tests.SystemTestCase - globals=globals()))) + VmIPv6NetworkingMixin, qubes.tests.SystemTestCase, + module=sys.modules[__name__]))) tests.addTests(loader.loadTestsFromNames( qubes.tests.create_testcases_for_templates('VmUpdates', - VmUpdates, qubes.tests.SystemTestCase - globals=globals()))) + VmUpdates, qubes.tests.SystemTestCase, + module=sys.modules[__name__]))) return tests diff --git a/qubes/tests/integ/pvgrub.py b/qubes/tests/integ/pvgrub.py index 3498651b..fa78f03b 100644 --- a/qubes/tests/integ/pvgrub.py +++ b/qubes/tests/integ/pvgrub.py @@ -23,8 +23,11 @@ import os import subprocess +import sys import unittest + import qubes.tests + @unittest.skipUnless(os.path.exists('/var/lib/qubes/vm-kernels/pvgrub2'), 'grub-xen package not installed') class TC_40_PVGrub(object): @@ -138,6 +141,6 @@ class TC_40_PVGrub(object): def load_tests(loader, tests, pattern): tests.addTests(loader.loadTestsFromNames( qubes.tests.create_testcases_for_templates('TC_40_PVGrub', - TC_40_PVGrub, qubes.tests.SystemTestCase - globals=globals()))) + TC_40_PVGrub, qubes.tests.SystemTestCase, + module=sys.modules[__name__]))) return tests diff --git a/qubes/tests/integ/salt.py b/qubes/tests/integ/salt.py index 09f35be6..f598c7e7 100644 --- a/qubes/tests/integ/salt.py +++ b/qubes/tests/integ/salt.py @@ -17,13 +17,13 @@ # # You should have received a copy of the GNU General Public License along # with this program; if not, see . -import os -import subprocess -import json - -import shutil import asyncio +import json +import os +import shutil +import subprocess +import sys import qubes.tests @@ -394,6 +394,6 @@ class SaltVMTestMixin(SaltTestMixin): def load_tests(loader, tests, pattern): tests.addTests(loader.loadTestsFromNames( qubes.tests.create_testcases_for_templates('TC_10_VMSalt', - SaltVMTestMixin, qubes.tests.SystemTestCase - globals=globals()))) + SaltVMTestMixin, qubes.tests.SystemTestCase, + module=sys.modules[__name__]))) return tests diff --git a/qubes/tests/integ/vm_qrexec_gui.py b/qubes/tests/integ/vm_qrexec_gui.py index a2a7f144..0ed3ee7f 100644 --- a/qubes/tests/integ/vm_qrexec_gui.py +++ b/qubes/tests/integ/vm_qrexec_gui.py @@ -23,6 +23,7 @@ import asyncio import multiprocessing import os import subprocess +import sys import unittest from distutils import spawn @@ -1014,6 +1015,6 @@ class TC_10_Generic(qubes.tests.SystemTestCase): def load_tests(loader, tests, pattern): tests.addTests(loader.loadTestsFromNames( qubes.tests.create_testcases_for_templates('TC_00_AppVM', - TC_00_AppVMMixin, qubes.tests.SystemTestCase - globals=globals()))) + TC_00_AppVMMixin, qubes.tests.SystemTestCase, + module=sys.modules[__name__]))) return tests