Can you explain how your wrapper class is different from functools.wraps? I'm sure I'm missing some detail...
On Thu, May 29, 2014 at 4:37 PM, Dylan Baker <[email protected]> wrote: > 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) > + > + > [email protected] > +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 > > _______________________________________________ > Piglit mailing list > [email protected] > http://lists.freedesktop.org/mailman/listinfo/piglit _______________________________________________ Piglit mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/piglit
