[Piglit] [PATCH 05/20] unittests: Make skip less invasive
Dylan Baker
dylan at pnwbakers.com
Wed Jun 1 23:50:12 UTC 2016
I prefer that skip is just a decorator so that the test itself doesn't
need to contain code to skip.
---
tox.ini | 1 +
unittests/base_tests.py | 31 +++-----
unittests/compressed_backend_tests.py | 14 +---
unittests/core_tests.py | 6 +-
unittests/json_tests.py | 12 +--
unittests/junit_backends_tests.py | 2 +-
unittests/monitoring_tests.py | 8 +-
unittests/profile_tests.py | 4 +-
unittests/utils/nose.py | 135 ++++++++++++++++++++--------------
9 files changed, 110 insertions(+), 103 deletions(-)
diff --git a/tox.ini b/tox.ini
index 901432e..d6a845f 100644
--- a/tox.ini
+++ b/tox.ini
@@ -9,6 +9,7 @@ setenv =
deps =
nose
coverage
+ wrapt
six==1.5.2
accel: simplejson
accel-nix: lxml
diff --git a/unittests/base_tests.py b/unittests/base_tests.py
index 1933d71..2058703 100644
--- a/unittests/base_tests.py
+++ b/unittests/base_tests.py
@@ -31,6 +31,10 @@ try:
from unittest import mock
except ImportError:
import mock
+try:
+ import subprocess32 as subprocess
+except ImportError:
+ import subprocess
import six
import nose.tools as nt
@@ -87,6 +91,8 @@ def test_run_return_early():
@attr('slow')
+ at utils.nose.Skip.module('psutil', available=True)
+ at utils.nose.Skip.backport(3.3, 'subprocess32')
@nt.timed(6)
def test_timeout_kill_children():
"""test.base.Test: kill children if terminate fails
@@ -97,13 +103,6 @@ def test_timeout_kill_children():
This test could leave processes running if it fails.
"""
- utils.nose.module_check('psutil')
- if six.PY2:
- utils.nose.module_check('subprocess32')
- import subprocess32 as subprocess # pylint: disable=import-error
- elif six.PY3:
- import subprocess
-
class PopenProxy(object):
"""An object that proxies Popen, and saves the Popen instance as an
attribute.
@@ -173,6 +172,8 @@ def test_timeout_kill_children():
@attr('slow')
+ at utils.nose.Skip.backport(3.3, 'subprocess32')
+ at utils.nose.Skip.binary('sleep')
@nt.timed(6)
def test_timeout():
"""test.base.Test: Stops running test after timeout expires
@@ -181,37 +182,29 @@ def test_timeout():
if the test runs 5 seconds it's run too long
"""
- if six.PY2:
- utils.nose.module_check('subprocess32')
- utils.nose.binary_check('sleep', 1)
-
test = TimeoutTest(['sleep', '60'])
test.timeout = 1
test.run()
@attr('slow')
+ at utils.nose.Skip.backport(3.3, 'subprocess32')
+ at utils.nose.Skip.binary('sleep')
@nt.timed(6)
def test_timeout_timeout():
"""test.base.Test: Sets status to 'timeout' when timeout exceeded"""
- if six.PY2:
- utils.nose.module_check('subprocess32')
- utils.nose.binary_check('sleep', 1)
-
test = TimeoutTest(['sleep', '60'])
test.timeout = 1
test.run()
nt.eq_(test.result.result, 'timeout')
+ at utils.nose.Skip.backport(3.3, 'subprocess32')
+ at utils.nose.Skip.binary('true')
@nt.timed(2)
def test_timeout_pass():
"""test.base.Test: Doesn't change status when timeout not exceeded
"""
- if six.PY2:
- utils.nose.module_check('subprocess32')
- utils.nose.binary_check('true')
-
test = TimeoutTest(['true'])
test.timeout = 1
test.result.result = 'pass'
diff --git a/unittests/compressed_backend_tests.py b/unittests/compressed_backend_tests.py
index c3be896..18786c1 100644
--- a/unittests/compressed_backend_tests.py
+++ b/unittests/compressed_backend_tests.py
@@ -32,8 +32,6 @@ import os
import functools
import nose.tools as nt
-from nose.plugins.skip import SkipTest
-import six
from . import utils
from framework import results
@@ -233,20 +231,12 @@ def test_update_piglit_conf():
nt.eq_(compression.get_mode(), 'foobar')
+ at utils.nose.Skip.py3
+ at utils.nose.Skip.module('backports.lzma', available=False)
@utils.nose.set_env(PIGLIT_COMPRESSION='xz')
@utils.nose.test_in_tempdir
def test_xz_shell_override():
"""framework.backends.compression: the xz shell utility path can overwrite"""
- if six.PY3:
- raise SkipTest('Test is irrelvent on python 3')
-
- try:
- import backports.lzma # pylint: disable=unused-variable
- except ImportError:
- pass
- else:
- raise SkipTest('Test requires shell path, not backports.lzma path.')
-
with open('foo.json.xz', 'w') as f:
f.write('foo')
diff --git a/unittests/core_tests.py b/unittests/core_tests.py
index 4d83e73..8a160e7 100644
--- a/unittests/core_tests.py
+++ b/unittests/core_tests.py
@@ -33,9 +33,9 @@ import textwrap
# There is a very high potential that one of these will raise an ImportError
# pylint: disable=import-error
try:
- from unittest import mock
-except ImportError:
import mock
+except ImportError:
+ from unittest import mock
# pylint: enable=import-error
import nose.tools as nt
@@ -332,7 +332,7 @@ def test_check_dir_handler():
handler=mock.Mock(side_effect=utils.nose.SentinalException))
- at utils.nose.skip(not six.PY3, 'Test is only relevant on python 3.x')
+ at utils.nose.Skip.py2
def test_check_dir_stat_FileNotFoundError():
"""core.check_dir: FileNotFoundError is raised and failifexsits is False continue"""
with mock.patch('framework.core.os.stat',
diff --git a/unittests/json_tests.py b/unittests/json_tests.py
index 0d17f19..7c7451e 100644
--- a/unittests/json_tests.py
+++ b/unittests/json_tests.py
@@ -97,22 +97,22 @@ class TestJsonOutput(utils.nose.StaticDirectory):
"""JSON: tests is a root key."""
nt.assert_in('tests', self.json)
+ @utils.nose.Skip.platform('linux')
+ @utils.nose.Skip.binary('lspci')
def test_root_lspci(self):
"""JSON: lspci is a root key."""
- utils.nose.platform_check('linux')
- utils.nose.binary_check('lspci')
nt.assert_in('lspci', self.json)
+ @utils.nose.Skip.platform('linux')
+ @utils.nose.Skip.binary('uname')
def test_root_uname(self):
"""JSON: uname is a root key."""
- utils.nose.platform_check('linux')
- utils.nose.binary_check('uname')
nt.assert_in('uname', self.json)
+ @utils.nose.Skip.platform('linux')
+ @utils.nose.Skip.binary('glxinfo')
def test_root_glxinfo(self):
"""JSON: glxinfo is a root key."""
- utils.nose.platform_check('linux')
- utils.nose.binary_check('glxinfo')
nt.assert_in('glxinfo', self.json)
def test_root_time_elapsed(self):
diff --git a/unittests/junit_backends_tests.py b/unittests/junit_backends_tests.py
index f034a55..aaad5bf 100644
--- a/unittests/junit_backends_tests.py
+++ b/unittests/junit_backends_tests.py
@@ -113,9 +113,9 @@ class TestJUnitSingleTest(TestJunitNoTests):
"""backends.junit.JUnitBackend.write_test(): (once) produces well formed xml"""
super(TestJUnitSingleTest, self).test_xml_well_formed()
+ @utils.nose.Skip.module('lxml', available=True)
def test_xml_valid(self):
"""backends.junit.JUnitBackend.write_test(): (once) produces valid xml"""
- utils.nose.module_check('lxml')
schema = etree.XMLSchema(file=JUNIT_SCHEMA)
with open(self.test_file, 'r') as f:
nt.ok_(schema.validate(etree.parse(f)), msg='xml is not valid')
diff --git a/unittests/monitoring_tests.py b/unittests/monitoring_tests.py
index 817ee55..158fcb5 100644
--- a/unittests/monitoring_tests.py
+++ b/unittests/monitoring_tests.py
@@ -151,11 +151,9 @@ class TestMonitoring(object):
nt.assert_equal(self.monitoring.abort_needed, False)
+ @utils.nose.Skip.platform('linux')
def test_Monitoring_dmesg_error(self):
"""monitoring.Monitoring: error found on the dmesg."""
-
- utils.nose.platform_check('linux')
-
mock_out = mock.Mock(return_value=b'[1.0]This\n[2.0]is\n[3.0]dmesg')
with mock.patch('framework.dmesg.subprocess.check_output', mock_out):
self.monitoring.add_rule('no_error_file',
@@ -170,11 +168,9 @@ class TestMonitoring(object):
nt.assert_equal(self.monitoring.abort_needed, True)
+ @utils.nose.Skip.platform('linux')
def test_Monitoring_dmesg_no_error(self):
"""monitoring.Monitoring: no error found on the dmesg."""
-
- utils.nose.platform_check('linux')
-
mock_out = mock.Mock(return_value=b'[1.0]This\n[2.0]is\n[3.0]dmesg')
with mock.patch('framework.dmesg.subprocess.check_output', mock_out):
self.monitoring.add_rule('no_error_file',
diff --git a/unittests/profile_tests.py b/unittests/profile_tests.py
index 1a2d450..d190feb 100644
--- a/unittests/profile_tests.py
+++ b/unittests/profile_tests.py
@@ -77,17 +77,17 @@ def test_testprofile_default_dmesg():
nt.ok_(isinstance(profile_.dmesg, dmesg.DummyDmesg))
+ at utils.nose.Skip.platform('linux')
def test_testprofile_set_dmesg_true():
"""profile.TestProfile: Dmesg returns an appropriate dmesg is set to True"""
- utils.nose.platform_check('linux')
profile_ = profile.TestProfile()
profile_.dmesg = True
nt.ok_(isinstance(profile_.dmesg, dmesg.LinuxDmesg))
+ at utils.nose.Skip.platform('linux')
def test_testprofile_set_dmesg_false():
"""profile.TestProfile: Dmesg returns a DummyDmesg if set to False"""
- utils.nose.platform_check('linux')
profile_ = profile.TestProfile()
profile_.dmesg = True
profile_.dmesg = False
diff --git a/unittests/utils/nose.py b/unittests/utils/nose.py
index 573b573..739089c 100644
--- a/unittests/utils/nose.py
+++ b/unittests/utils/nose.py
@@ -42,6 +42,7 @@ from contextlib import contextmanager
from nose.plugins.skip import SkipTest
import six
+import wrapt
try:
from six.moves import getcwd
except ImportError:
@@ -244,40 +245,91 @@ def generator(func):
return test_wrapper
-def binary_check(bin_, errno_=None):
- """Check for the existence of a binary or raise SkipTest.
+ at wrapt.decorator
+def passthrough(wrapped, _, args, kwargs):
+ return wrapped(*args, **kwargs)
- If an errno_ is provided then a skip test will be raised unless the error
- number provided is raised, or no error is raised.
+
+class Skip(object):
+ """Class providing conditional skipping support.
+
+ Provides a number of class methods as alternate constructors for special
+ skip conditions, these are merely convenience methods.
"""
- with open(os.devnull, 'w') as null:
- try:
- subprocess.check_call([bin_], stdout=null, stderr=null)
- except OSError as e:
- if e.errno == errno.ENOENT:
- raise SkipTest('Binary {} not available'.format(bin_))
- except subprocess.CalledProcessError as e:
- if errno_ is not None and e.returncode == errno_:
- pass
- else:
- raise SkipTest(
- 'Binary provided bad returncode of {} (wanted {})'.format(
- e.returncode, errno_))
-
-
-def module_check(module):
- """Check that an external module is available or skip."""
- try:
- importlib.import_module(module)
- except ImportError:
- raise SkipTest('Required module {} not available.'.format(module))
+ def __init__(self, condition, description):
+ self.__skip = condition
+ self.__description = description
+
+ @classmethod
+ def py3(cls, _=None):
+ """Skip if the interpreter python 3."""
+ return cls(six.PY3, 'Test is not relevant on python 3.x')
+
+ @classmethod
+ def py2(cls, _=None):
+ """Skip if the interpreter is python 2."""
+ return cls(six.PY2, 'Test is not relevant on python 2.x')
+
+ @classmethod
+ def module(cls, name, available=False):
+ """Skip if the provided external module is (not) available."""
+ assert isinstance(available, bool), 'avilable must be bool'
+ def check():
+ try:
+ importlib.import_module(name)
+ except ImportError:
+ return False
+ return True
+
+ return cls(check() is not available,
+ 'Test requires that module {} is {}available'.format(
+ name, '' if available else 'not '))
+
+ @classmethod
+ def backport(cls, version, name):
+ """Skip if the interpreter needs a backport that isn't available.
+
+ Arguments:
+ version -- The minimum version that doesn't require the backport
+ name -- the name of the required module
+
+ """
+ assert isinstance(version, float), 'version must be float'
+ if float('.'.join(str(v) for v in sys.version_info[:2])) < version:
+ return cls.module(name, True)
+ return passthrough
+ @classmethod
+ def binary(cls, name):
+ """Skip if the requested binary isn't available."""
+ def check():
+ with open(os.devnull, 'w') as null:
+ try:
+ # Pass the bogus arg in case the program tries to read
+ # stdin, like xz
+ subprocess.check_call([name, 'totallymadeupdoesntexistarg'],
+ stdout=null, stderr=null)
+ except OSError as e:
+ if e.errno == errno.ENOENT:
+ return False
+ except subprocess.CalledProcessError as e:
+ pass
+ return True
+
+ return cls(
+ check(), 'Test requires that binary {} is available'.format(name))
+
+ @classmethod
+ def platform(cls, name, is_=False):
+ return cls(sys.platform.startswith(name) is is_,
+ 'Platform {} is not supported'.format(sys.platform))
-def platform_check(plat):
- """If the platform is not in the list specified skip the test."""
- if not sys.platform.startswith(plat):
- raise SkipTest('Platform {} is not supported'.format(sys.platform))
+ @wrapt.decorator
+ def __call__(self, wrapped, instance, args, kwargs):
+ if self.__skip:
+ raise SkipTest(self.__description)
+ return wrapped(*args, **kwargs)
def test_in_tempdir(func):
@@ -387,28 +439,3 @@ def set_env(**envargs):
return _inner
return _decorator
-
-
-def skip(condition, description):
- """Skip a test if the condition is met.
-
- Arguments:
- condition -- If this is truthy then the test will be skippped
- description -- the message that SkipTest will display if the test is
- skipped
-
- """
-
- def _wrapper(func):
- """The function that acutally does the wrapping."""
-
- @functools.wraps(func)
- def _inner(*args, **kwargs):
- """The function that is actually called."""
- if condition:
- raise SkipTest(description)
- return func(*args, **kwargs)
-
- return _inner
-
- return _wrapper
--
2.8.3
More information about the Piglit
mailing list