[Numpy-svn] r6342 - in trunk/numpy/testing: . tests

numpy-svn@scip... numpy-svn@scip...
Thu Feb 5 14:11:45 CST 2009


Author: alan.mcintyre
Date: 2009-02-05 14:11:40 -0600 (Thu, 05 Feb 2009)
New Revision: 6342

Added:
   trunk/numpy/testing/tests/test_decorators.py
Modified:
   trunk/numpy/testing/decorators.py
Log:
Issue #957:
- Fix problems with test decorators when used on test generators.  
- The skip/fail arguments for skipif and knownfailureif can now be 
  either a bool or a callable that returns a bool.
- Added tests for the test decorators. 


Modified: trunk/numpy/testing/decorators.py
===================================================================
--- trunk/numpy/testing/decorators.py	2009-02-05 04:31:51 UTC (rev 6341)
+++ trunk/numpy/testing/decorators.py	2009-02-05 20:11:40 UTC (rev 6342)
@@ -51,8 +51,11 @@
 
     Parameters
     ---------
-    skip_condition : bool
-        Flag to determine whether to skip test (True) or not (False)
+    skip_condition : bool or callable.
+        Flag to determine whether to skip test.  If the condition is a
+        callable, it is used at runtime to dynamically make the decision.  This
+        is useful for tests that may require costly imports, to delay the cost
+        until the test suite is actually executed.
     msg : string
         Message to give on raising a SkipTest exception
 
@@ -69,28 +72,66 @@
     decorator with the nose.tools.make_decorator function in order to
     transmit function name, and various other metadata.
     '''
-    if msg is None:
-        msg = 'Test skipped due to test condition'
+
     def skip_decorator(f):
         # Local import to avoid a hard nose dependency and only incur the
         # import time overhead at actual test-time.
         import nose
-        def skipper(*args, **kwargs):
-            if skip_condition:
-                raise nose.SkipTest, msg
+
+        # Allow for both boolean or callable skip conditions.
+        if callable(skip_condition):
+            skip_val = lambda : skip_condition()
+        else:
+            skip_val = lambda : skip_condition
+
+        def get_msg(func,msg=None):
+            """Skip message with information about function being skipped."""
+            if msg is None: 
+                out = 'Test skipped due to test condition'
+            else: 
+                out = '\n'+msg
+
+            return "Skipping test: %s%s" % (func.__name__,out)
+
+        # We need to define *two* skippers because Python doesn't allow both
+        # return with value and yield inside the same function.
+        def skipper_func(*args, **kwargs):
+            """Skipper for normal test functions."""
+            if skip_val():
+                raise nose.SkipTest(get_msg(f,msg))
             else:
                 return f(*args, **kwargs)
+
+        def skipper_gen(*args, **kwargs):
+            """Skipper for test generators."""
+            if skip_val():
+                raise nose.SkipTest(get_msg(f,msg))
+            else:
+                for x in f(*args, **kwargs):
+                    yield x
+
+        # Choose the right skipper to use when building the actual decorator.
+        if nose.util.isgenerator(f):
+            skipper = skipper_gen
+        else:
+            skipper = skipper_func
+            
         return nose.tools.make_decorator(f)(skipper)
+
     return skip_decorator
 
-def knownfailureif(skip_condition, msg=None):
-    ''' Make function raise KnownFailureTest exception if skip_condition is true
 
+def knownfailureif(fail_condition, msg=None):
+    ''' Make function raise KnownFailureTest exception if fail_condition is true
+
     Parameters
     ---------
-    skip_condition : bool
+    fail_condition : bool or callable.
         Flag to determine whether to mark test as known failure (True)
-        or not (False)
+        or not (False).  If the condition is a callable, it is used at
+        runtime to dynamically make the decision.  This is useful for 
+        tests that may require costly imports, to delay the cost
+        until the test suite is actually executed.
     msg : string
         Message to give on raising a KnownFailureTest exception
 
@@ -109,15 +150,23 @@
     '''
     if msg is None:
         msg = 'Test skipped due to known failure'
-    def skip_decorator(f):
+
+    # Allow for both boolean or callable known failure conditions.
+    if callable(fail_condition):
+        fail_val = lambda : fail_condition()
+    else:
+        fail_val = lambda : fail_condition
+
+    def knownfail_decorator(f):
         # Local import to avoid a hard nose dependency and only incur the
         # import time overhead at actual test-time.
         import nose
         from noseclasses import KnownFailureTest
-        def skipper(*args, **kwargs):
-            if skip_condition:
+        def knownfailer(*args, **kwargs):
+            if fail_val():
                 raise KnownFailureTest, msg
             else:
                 return f(*args, **kwargs)
-        return nose.tools.make_decorator(f)(skipper)
-    return skip_decorator
+        return nose.tools.make_decorator(f)(knownfailer)
+
+    return knownfail_decorator

Added: trunk/numpy/testing/tests/test_decorators.py
===================================================================
--- trunk/numpy/testing/tests/test_decorators.py	2009-02-05 04:31:51 UTC (rev 6341)
+++ trunk/numpy/testing/tests/test_decorators.py	2009-02-05 20:11:40 UTC (rev 6342)
@@ -0,0 +1,156 @@
+import numpy as np
+from numpy.testing import *
+from numpy.testing.noseclasses import KnownFailureTest
+import nose
+
+def test_slow():
+    @dec.slow
+    def slow_func(x,y,z):
+        pass
+
+    assert(slow_func.slow)
+
+def test_setastest():
+    @dec.setastest()
+    def f_default(a):
+        pass
+
+    @dec.setastest(True)
+    def f_istest(a):
+        pass
+
+    @dec.setastest(False)
+    def f_isnottest(a):
+        pass
+
+    assert(f_default.__test__)
+    assert(f_istest.__test__)
+    assert(not f_isnottest.__test__)
+
+class DidntSkipException(Exception): 
+    pass
+
+def test_skip_functions_hardcoded():
+    @dec.skipif(True)
+    def f1(x):
+        raise DidntSkipException
+
+    try:
+        f1('a')
+    except DidntSkipException:
+        raise Exception('Failed to skip')
+    except nose.SkipTest:
+        pass
+
+    @dec.skipif(False)
+    def f2(x):
+        raise DidntSkipException
+
+    try:
+        f2('a')
+    except DidntSkipException:
+        pass
+    except nose.SkipTest:
+        raise Exception('Skipped when not expected to')
+
+
+def test_skip_functions_callable():
+    def skip_tester():
+        return skip_flag == 'skip me!'
+
+    @dec.skipif(skip_tester)
+    def f1(x):
+        raise DidntSkipException
+
+    try:
+        skip_flag = 'skip me!'
+        f1('a')
+    except DidntSkipException:
+        raise Exception('Failed to skip')
+    except nose.SkipTest:
+        pass
+
+    @dec.skipif(skip_tester)
+    def f2(x):
+        raise DidntSkipException
+
+    try:
+        skip_flag = 'five is right out!'
+        f2('a')
+    except DidntSkipException:
+        pass
+    except nose.SkipTest:
+        raise Exception('Skipped when not expected to')
+
+
+def test_skip_generators_hardcoded():
+    @dec.knownfailureif(True, "This test is known to fail")
+    def g1(x):
+        for i in xrange(x):
+            yield i
+
+    try:
+        for j in g1(10):
+            pass
+    except KnownFailureTest:
+        pass
+    else:
+        raise Exception('Failed to mark as known failure')
+
+
+    @dec.knownfailureif(False, "This test is NOT known to fail")
+    def g2(x):
+        for i in xrange(x):
+            yield i
+        raise DidntSkipException('FAIL')
+
+    try:
+        for j in g2(10):
+            pass
+    except KnownFailureTest:
+        raise Exception('Marked incorretly as known failure')
+    except DidntSkipException:
+        pass
+
+
+def test_skip_generators_callable():
+    def skip_tester():
+        return skip_flag == 'skip me!'
+
+    @dec.knownfailureif(skip_tester, "This test is known to fail")
+    def g1(x):
+        for i in xrange(x):
+            yield i
+
+    try:
+        skip_flag = 'skip me!'
+        for j in g1(10):
+            pass
+    except KnownFailureTest:
+        pass
+    else:
+        raise Exception('Failed to mark as known failure')
+
+
+    @dec.knownfailureif(skip_tester, "This test is NOT known to fail")
+    def g2(x):
+        for i in xrange(x):
+            yield i
+        raise DidntSkipException('FAIL')
+
+    try:
+        skip_flag = 'do not skip'
+        for j in g2(10):
+            pass
+    except KnownFailureTest:
+        raise Exception('Marked incorretly as known failure')
+    except DidntSkipException:
+        pass
+
+
+if __name__ == '__main__':
+    run_module_suite()
+
+
+
+



More information about the Numpy-svn mailing list