[Piglit] [PATCH] python tests: Adds an object proxy to make generator names correct
Dylan Baker
baker.dylan.c at gmail.com
Thu May 29 13:37:49 PDT 2014
This adds a class that wraps python functions to work around a bug in
nose. The bug has to do with the way nose test generators and python
instances interact, but the short is that all generated tests that raise
an error will get the description of the last test run, rather than
their own. This works around that bug by creating a class instance per
generated test, so they get the right description.
---
The problem with this solution is it requires adding more boilerplate to
each generator, a better solution would be to use a decorator, of course,
there is a problem with the way nose handles generators which makes
decorating generators difficult, and I don't have the time to dig into it.
framework/tests/core_tests.py | 5 ++---
framework/tests/dmesg_tests.py | 12 ++++++----
framework/tests/log_tests.py | 3 ++-
framework/tests/status_tests.py | 3 ++-
framework/tests/summary_tests.py | 7 +++---
framework/tests/test_lists.py | 5 +++--
framework/tests/utils.py | 47 ++++++++++++++++++++++++++++++++++++++++
7 files changed, 68 insertions(+), 14 deletions(-)
diff --git a/framework/tests/core_tests.py b/framework/tests/core_tests.py
index 44462ce..de424ad 100644
--- a/framework/tests/core_tests.py
+++ b/framework/tests/core_tests.py
@@ -47,10 +47,9 @@ def test_generate_initialize():
even work?
"""
- yieldable = check_initialize
- for target in [core.Environment, core.TestrunResult, core.TestResult,
- core.PiglitJSONEncoder]:
+ for target in [core.Environment, core.PiglitJSONEncoder,]:
+ yieldable = utils.GeneratedTestWrapper(check_initialize)
yieldable.description = "Test that {} initializes".format(
target.__name__)
yield yieldable, target
diff --git a/framework/tests/dmesg_tests.py b/framework/tests/dmesg_tests.py
index ccc3144..13ea705 100644
--- a/framework/tests/dmesg_tests.py
+++ b/framework/tests/dmesg_tests.py
@@ -32,6 +32,7 @@ from framework.exectest import PiglitTest
from framework.gleantest import GleanTest
from framework.shader_test import ShaderTest
from framework.glsl_parser_test import GLSLParserTest
+import framework.tests.utils as utils
def _get_dmesg():
@@ -180,10 +181,11 @@ def test_update_result_replace():
new_result = dmesg.update_result(create_test_result(res))
# Create a yieldable and set the description for useful per-test names
- yieldable = check_update_result
+ yieldable = utils.GeneratedTestWrapper(check_update_result)
yieldable.description = "Test update_result: {0}".format(res)
yield yieldable, new_result['result'], res
+ yieldable = utils.GeneratedTestWrapper(check_update_result)
yieldable.description = "Test update_result subtest: {0}".format(res)
yield yieldable, new_result['subtest']['test'], res
@@ -193,7 +195,7 @@ def test_update_result_replace():
_write_dev_kmesg()
new_result = dmesg.update_result(create_test_result(res))
- yieldable = check_equal_result
+ yieldable = utils.GeneratedTestWrapper(check_equal_result)
yieldable.description = ("Test update_result with non-matching regex: "
"{0}".format(res))
yield yieldable, new_result['result'], res
@@ -204,11 +206,12 @@ def test_update_result_replace():
_write_dev_kmesg()
new_result = dmesg.update_result(create_test_result(res))
- yieldable = check_update_result
+ yieldable = utils.GeneratedTestWrapper(check_update_result)
yieldable.description = ("Test update_result with matching regex: "
"{0} ".format(res))
yield yieldable, new_result['result'], res
+
def check_equal_result(result, status):
""" Tests that the result and status are equal
@@ -220,6 +223,7 @@ def check_equal_result(result, status):
nt.assert_equal(result, status, msg="status should not have changed "
"from {} to {}".format(status, result))
+
def check_update_result(result, status):
""" Tests that update result replaces results correctly
@@ -272,9 +276,9 @@ def test_testclasses_dmesg():
(GLSLParserTest, 'tests/glslparsertest/shaders/main1.vert',
'GLSLParserTest')]
- yieldable = check_classes_dmesg
for tclass, tfile, desc in lists:
+ yieldable = utils.GeneratedTestWrapper(check_classes_dmesg)
yieldable.description = "Test dmesg in {}".format(desc)
yield yieldable, tclass, tfile
diff --git a/framework/tests/log_tests.py b/framework/tests/log_tests.py
index e53b95b..10ad86e 100644
--- a/framework/tests/log_tests.py
+++ b/framework/tests/log_tests.py
@@ -25,6 +25,7 @@ import itertools
from types import * # This is a special * safe module
import nose.tools as nt
from framework.log import Log
+import framework.tests.utils as utils
valid_statuses = ('pass', 'fail', 'crash', 'warn', 'dmesg-warn',
'dmesg-fail', 'skip', 'dry-run')
@@ -73,9 +74,9 @@ def check_post_log_increment_summary(stat):
def test_post_log_increment_summary():
""" Generator that creates tests for self.__summary """
- yieldable = check_post_log_increment_summary
for stat in valid_statuses:
+ yieldable = utils.GeneratedTestWrapper(check_post_log_increment_summary)
yieldable.description = ("Test that Log.post_log increments "
"self._summary[{}]".format(stat))
yield yieldable, stat
diff --git a/framework/tests/status_tests.py b/framework/tests/status_tests.py
index 599225f..c8f2f4a 100644
--- a/framework/tests/status_tests.py
+++ b/framework/tests/status_tests.py
@@ -28,6 +28,7 @@ etc
import itertools
import nose.tools as nt
import framework.status as status
+import framework.tests.utils as utils
# Statuses from worst to last. NotRun is intentionally not in this list and
# tested separately because of upcoming features for it
@@ -73,9 +74,9 @@ def check_lookup(stat):
def test_gen_lookup():
""" Generator that attempts to do a lookup on all statuses """
- yieldable = check_lookup
for stat in STATUSES + ['skip', 'notrun']:
+ yieldable = utils.GeneratedTestWrapper(check_lookup)
yieldable.description = "Lookup: {}".format(stat)
yield yieldable, stat
diff --git a/framework/tests/summary_tests.py b/framework/tests/summary_tests.py
index 8961e1a..438b481 100644
--- a/framework/tests/summary_tests.py
+++ b/framework/tests/summary_tests.py
@@ -56,10 +56,11 @@ def test_summary_add_to_set():
('timeout', 'pass', 'fixes'),
('pass', 'timeout', 'regressions'),
('pass', 'timeout', 'problems')]:
- check_sets.description = "{0} -> {1} should be added to {2}".format(
- ostat, nstat, set_)
+ yieldable = utils.GeneratedTestWrapper(check_sets)
+ yieldable.description = "{0} -> {1} should be added to {2}".format(
+ ostat, nstat, set_)
- yield check_sets, old, ostat, new, nstat, set_
+ yield yieldable, old, ostat, new, nstat, set_
def check_sets(old, ostat, new, nstat, set_):
diff --git a/framework/tests/test_lists.py b/framework/tests/test_lists.py
index fe5ec13..97b5bc3 100644
--- a/framework/tests/test_lists.py
+++ b/framework/tests/test_lists.py
@@ -29,19 +29,20 @@ es3conform, etc)
import importlib
import os.path as path
from nose.plugins.skip import SkipTest
+import framework.tests.utils as utils
def gen_test_import():
""" Generates a bunch of tests to import the various test modules """
- yieldable = check_import
-
# Test the various OpenGL modules
for module in ['all', 'quick', 'gpu', 'sanity', 'r500', 'r300']:
+ yieldable = utils.GeneratedTestWrapper(check_import)
yieldable.description = "Test import of tests.{}".format(module)
yield yieldable, "tests." + module
# Test the various OpenCL modules
for module in ['cl', 'all_cl', 'quick_cl']:
+ yieldable = utils.GeneratedTestWrapper(check_import)
yieldable.description = "Test import of tests.{}".format(module)
yield yieldable, "tests." + module
diff --git a/framework/tests/utils.py b/framework/tests/utils.py
index f337b1e..2b47052 100644
--- a/framework/tests/utils.py
+++ b/framework/tests/utils.py
@@ -33,6 +33,7 @@ try:
import simplejson as json
except ImportError:
import json
+import nose.tools as nt
__all__ = [
@@ -106,3 +107,49 @@ def tempdir():
tdir = tempfile.mkdtemp()
yield tdir
shutil.rmtree(tdir)
+
+
+ at nt.nottest
+class GeneratedTestWrapper(object):
+ """ This class is used to work around a bug in nose
+
+ There is a bug in nose that causes generated tests to all be the same
+ instance, which means they all have the same description. This class works
+ around that by wrapping the test, and then being itself an initialized
+ object, and acting as an object proxy.
+
+ It generally passes through the attribues of the wrapped function, and
+ provides a __call__ method that returns the value of calling the underlying
+ function.
+
+ This class can also be used to wrap a class, but that class needs to
+ provide a __call__ method, and use that to return results.
+
+ Argument:
+ wrapped -- A function or function-like-class
+
+ """
+ def __init__(self, wrapped):
+ self._wrapped = wrapped
+
+ # set the __name__ attribute to be the __name__ of the wrapped function
+ try:
+ self.__name__ = wrapped.__name__
+ except AttributeError:
+ pass
+
+ @property
+ def __class__(self):
+ return self._wrapped.__class__
+
+ def __getattr__(self, name):
+ return getattr(self._wrapped, name)
+
+ def __call__(self, *args, **kwargs):
+ """ calls the wrapped function
+
+ Arguments:
+ *args -- arguments to be passed to the wrapped function
+ **kwargs -- keyword arguments to be passed to the wrapped function
+ """
+ return self._wrapped(*args, **kwargs)
--
2.0.0.rc4
More information about the Piglit
mailing list