summaryrefslogtreecommitdiff
path: root/numpy/testing
diff options
context:
space:
mode:
authorAlan McIntyre <alan.mcintyre@local>2009-02-05 20:11:40 +0000
committerAlan McIntyre <alan.mcintyre@local>2009-02-05 20:11:40 +0000
commitaeb090d5f7081a166357fa850950da89feb25e97 (patch)
tree394185643a227b2b23fdbeddb575d5720bfa4ed5 /numpy/testing
parent911e94dd0a64adae9fb2057fb0210e512a9b7d4a (diff)
downloadnumpy-aeb090d5f7081a166357fa850950da89feb25e97.tar.gz
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.
Diffstat (limited to 'numpy/testing')
-rw-r--r--numpy/testing/decorators.py81
-rw-r--r--numpy/testing/tests/test_decorators.py156
2 files changed, 221 insertions, 16 deletions
diff --git a/numpy/testing/decorators.py b/numpy/testing/decorators.py
index 418c1d9fa..70301f250 100644
--- a/numpy/testing/decorators.py
+++ b/numpy/testing/decorators.py
@@ -51,8 +51,11 @@ def skipif(skip_condition, msg=None):
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 @@ def skipif(skip_condition, msg=None):
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 @@ def knownfailureif(skip_condition, msg=None):
'''
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
diff --git a/numpy/testing/tests/test_decorators.py b/numpy/testing/tests/test_decorators.py
new file mode 100644
index 000000000..504971e61
--- /dev/null
+++ b/numpy/testing/tests/test_decorators.py
@@ -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()
+
+
+
+