[Scipy-svn] r3819 - in trunk/scipy: . testing testing/examples
scipy-svn@scip...
scipy-svn@scip...
Sat Jan 12 01:24:58 CST 2008
Author: matthew.brett@gmail.com
Date: 2008-01-12 01:24:52 -0600 (Sat, 12 Jan 2008)
New Revision: 3819
Added:
trunk/scipy/testing/
trunk/scipy/testing/__init__.py
trunk/scipy/testing/decorators.py
trunk/scipy/testing/examples/
trunk/scipy/testing/examples/README.txt
trunk/scipy/testing/examples/test_foo.py
trunk/scipy/testing/nosetester.py
trunk/scipy/testing/nulltester.py
trunk/scipy/testing/pkgtester.py
trunk/scipy/testing/setup.py
trunk/scipy/testing/utils.py
Log:
Added nose testing utilities
Added: trunk/scipy/testing/__init__.py
===================================================================
--- trunk/scipy/testing/__init__.py 2008-01-12 07:02:12 UTC (rev 3818)
+++ trunk/scipy/testing/__init__.py 2008-01-12 07:24:52 UTC (rev 3819)
@@ -0,0 +1,19 @@
+"""Common test support for all scipy test scripts.
+
+This single module should provide all the common functionality for scipy tests
+in a single location, so that test script can just import it and work right
+away.
+"""
+
+import unittest
+from unittest import TestCase
+
+try:
+ import nose
+except ImportError:
+ print 'Need nose testing framework installed for scipy tests'
+ raise
+
+import decorators as dec
+from numpy.testing.utils import *
+from utils import *
Added: trunk/scipy/testing/decorators.py
===================================================================
--- trunk/scipy/testing/decorators.py 2008-01-12 07:02:12 UTC (rev 3818)
+++ trunk/scipy/testing/decorators.py 2008-01-12 07:24:52 UTC (rev 3819)
@@ -0,0 +1,42 @@
+"""Decorators for labeling test objects."""
+
+def slow(t):
+ """Labels a test as 'slow'.
+
+ The exact definition of a slow test is obviously both subjective and
+ hardware-dependent, but in general any individual test that requires more
+ than a second or two should be labeled as slow (the whole suite consits of
+ thousands of tests, so even a second is significant)."""
+
+ t.slow = True
+ return t
+
+def bench(t):
+ ''' Labels a test as a benchmark.
+
+ Benchmark tests are often slow, and intended to test timings
+ between different algorithms rather than validity of the result. '''
+
+ t.bench = True
+ return t
+
+def willfail(t):
+ ''' Labels test as known failure
+
+ This label allows the tester to deselect the test in standard cases '''
+ t.willfail = True
+ return t
+
+def is_nosetest(tf):
+ ''' Signals to nose that this function is or is not a test
+
+ e.g
+ >>> @is_nosetest(False)
+ >>> def func_with_test_in_name(arg1, arg2): pass
+ ...
+ >>>
+ '''
+ def set_test(t):
+ t.__test__ = tf
+ return t
+ return set_test
Added: trunk/scipy/testing/examples/README.txt
===================================================================
--- trunk/scipy/testing/examples/README.txt 2008-01-12 07:02:12 UTC (rev 3818)
+++ trunk/scipy/testing/examples/README.txt 2008-01-12 07:24:52 UTC (rev 3819)
@@ -0,0 +1,66 @@
+============
+ Nose tests
+============
+
+SciPy will use, and require nose for all testing. It will be possible for
+users to use all of scipy without nose on their systems, but *not* to run the
+test suite.
+
+Labels
+======
+
+We are going to use labels for tests, instead of the old level system. For
+now, we'll use 'slow' as a label for slow-running tests, and will make more if
+needed in the future for speed considerations.
+
+Labels will be implemented as one decorator per test. All pre-defined labels
+are implemented in numpy.testing.decorators
+
+This would run all tests, since nose by default discovers everything::
+ nosetests -s test_foo.py
+
+this::
+ nosetests -s -A slow test_foo.py
+
+will only run tests labeled 'slow' (for which there's a decorator) and this::
+ nosetests -s -A "not slow" test_foo.py
+
+will *exclude* all slow tests from a run.
+
+The scipy.test() function call will expose the above with convenient syntax.
+
+Initially we only have the @slow decorator, later we may provide new ones as
+the need for them arises in actual use.
+
+
+Benchmark tests
+===============
+
+Routines that don't actually test correctness but instead do performance
+benchmarking will live in a benchmarks/ directory next to the tests/ directory
+of each module. There will be a scipy.benchmark() call that does benchmarking,
+similar to scipy.test() but separate from it.
+
+Scipy test
+
+For each package, there will be a function that takes level arguments,
+and performs tests per level
+
+import scipy.mypackage
+scipy.mypackage.test() # fast, unlabeled tests
+scipy.mypackage.test('full') # unlabeled, not slow, not villfail
+scipy.mypackage.test('slow') # just slow tests
+scipy.mypackage.test('bench') # just benchmarks
+scipy.mypackage.test(None) # all possible tests, including benchmarks
+scipy.mypackage.test(doctests=True) # fast tests, with doctests
+
+At the base level, scipy.test(*args) collects the test suite from each
+package, and runs it, with *args as above.
+
+scipy.mypackage.test()
+
+Runs all plausible tests in this package, and package test directory,
+and runs any subpackage tests such as
+scipy.mypackage.mysubpackage.test()
+
+
Property changes on: trunk/scipy/testing/examples/README.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/scipy/testing/examples/test_foo.py
===================================================================
--- trunk/scipy/testing/examples/test_foo.py 2008-01-12 07:02:12 UTC (rev 3818)
+++ trunk/scipy/testing/examples/test_foo.py 2008-01-12 07:24:52 UTC (rev 3819)
@@ -0,0 +1,106 @@
+"""Example SciPy test module, using nose features.
+
+For information on nose, see:
+
+ http://somethingaboutorange.com/mrl/projects/nose
+
+To run it in its simplest form::
+ nosetests test_foo.py
+
+
+Labels will be implemented as one decorator per test. All pre-defined labels
+are implemented in numpy.testing.decorators
+
+This would run all tests, since nose by default discovers everything::
+ nosetests -s test_foo.py
+
+this::
+ nosetests -s -A slow test_foo.py
+
+will only run tests labeled 'slow' (for which there's a decorator) and this::
+ nosetests -s -A "not slow" test_foo.py
+
+will *exclude* all slow tests from a run.
+"""
+
+# This single import statement should provide all the common functionality for
+# scipy tests in a single location.
+from scipy.testing import *
+
+def setup():
+ """Module-level setup"""
+ print 'doing setup'
+
+def teardown():
+ """Module-level teardown"""
+ print 'doing teardown'
+
+
+class ClassicTest(TestCase):
+ """A regular unittest, with the extra Numpy features."""
+ def test_1(self):
+ print 'First test'
+
+ def test_2(self):
+ print 'Second test'
+
+ # This is how to tag a test as slow
+ @dec.slow
+ def test_big(self,level=5):
+ print 'Big, slow test'
+
+
+def test_simplefunction():
+ """A simple test function."""
+ assert True
+
+def check_even(n, nn):
+ """A check function to be used in a test generator."""
+ assert n % 2 == 0 or nn % 2 == 0
+
+# Test generators are best left without docstring, so nose (in verbose mode)
+# shows the actual call they produce automatically, including arguments.
+def test_evens():
+ for i in range(0,4,2):
+ yield check_even, i, i*3
+
+def setup_func():
+ """A trivial setup function."""
+ print "In setup_func"
+
+def teardown_func():
+ """A trivial teardown function."""
+ print "In teardown_func"
+
+@nose.with_setup(setup_func, teardown_func)
+def test_with_extras():
+ """This test uses the setup/teardown functions."""
+ print " In test_with_extras"
+
+# Setup and teardown functions may be used with test generators. The setup and
+# teardown attributes must be attached to the generator function:
+@nose.with_setup(setup_func, teardown_func)
+def test_generator():
+ for i in range(6,10,2):
+ yield check_even, i, i*3
+
+@dec.slow
+def test_nasty():
+ "A difficult test that takes a long time..."
+ print '*** nasty slow test ***'
+
+
+def test_time():
+ "A simple test that times things"
+ x = 1
+ time = measure("x+1",times=100,label='test_time')
+ info('Time taken: %s' % time)
+
+def test_warn():
+ "A simple test that prints a warning."
+ warn('Bad things are happening...')
+
+def test_error():
+ "A simple test that prints an error message."
+ error('Really bad things are happening...')
+
Added: trunk/scipy/testing/nosetester.py
===================================================================
--- trunk/scipy/testing/nosetester.py 2008-01-12 07:02:12 UTC (rev 3818)
+++ trunk/scipy/testing/nosetester.py 2008-01-12 07:24:52 UTC (rev 3819)
@@ -0,0 +1,56 @@
+''' Nose tester object '''
+import os
+import sys
+
+import nose
+
+class NoseTester(object):
+ """ Scipy nose test runner.
+
+ Usage: NoseTester(<package>).test()
+
+ <package> is package path or module - None finds calling module path
+ """
+ def __init__(self, package=None):
+ if package is None:
+ f = sys._getframe(1)
+ package = f.f_locals.get('__file__', None)
+ assert package is not None
+ package = os.path.dirname(package)
+ else isinstance(package, type(os)):
+ package = os.path.dirname(package.__file__)
+ self.package_path = package
+
+ def test(self, labels='fast', verbose=1, doctests=False, extra_argv=None):
+ ''' Module testing function
+
+ labels - identifies tests to run. This can be a string to
+ pass to the nosetests executable with the '-A'
+ option, or one of several special values.
+ Special values are:
+ 'fast' - the default - which corresponds to
+ nosetests -A option of
+ 'not slow and not bench and not willfail'.
+ 'full' - fast (as above) and slow tests as in
+ nosetests -A option of 'not bench and not willfail'.
+ None or '' - run all tests and benchmarks
+
+ verbose - verbosity value 1-10
+ doctests - if True, run doctests in module
+ extra_argv - list with any extra args to pass to nosetest
+ '''
+ argv = ['scipy module test', self.package_path, '-s']
+ if labels:
+ if labels == 'fast':
+ labels = 'not slow and not bench and not willfail'
+ elif labels == 'full':
+ labels = 'not bench and not willfail'
+ argv += ['-A', labels]
+ argv += ['--verbosity', str(verbose)]
+ if doctests:
+ argv+=['--with-doctest']
+ if extra_argv:
+ argv+= extra_argv
+ nose.run(argv=argv)
+
+
Added: trunk/scipy/testing/nulltester.py
===================================================================
--- trunk/scipy/testing/nulltester.py 2008-01-12 07:02:12 UTC (rev 3818)
+++ trunk/scipy/testing/nulltester.py 2008-01-12 07:24:52 UTC (rev 3819)
@@ -0,0 +1,8 @@
+''' Null tester (when nose not importable '''
+
+class NullTester(object):
+ def __init__(self, *args, **kwargs):
+ pass
+ def test(self, labels=None, *args, **kwargs):
+ raise ImportError, 'Need nose testing on path for tests'
+
Added: trunk/scipy/testing/pkgtester.py
===================================================================
--- trunk/scipy/testing/pkgtester.py 2008-01-12 07:02:12 UTC (rev 3818)
+++ trunk/scipy/testing/pkgtester.py 2008-01-12 07:24:52 UTC (rev 3819)
@@ -0,0 +1,8 @@
+''' Define test function for scipy package '''
+try:
+ import nose
+except ImportError:
+ from scipy.testing.nulltester import NullTester as Tester
+else:
+ from scipy.testing.nosetester import NoseTester as Tester
+
Added: trunk/scipy/testing/setup.py
===================================================================
--- trunk/scipy/testing/setup.py 2008-01-12 07:02:12 UTC (rev 3818)
+++ trunk/scipy/testing/setup.py 2008-01-12 07:24:52 UTC (rev 3819)
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+
+def configuration(parent_package='',top_path=None):
+ from numpy.distutils.misc_util import Configuration
+ config = Configuration('testing', parent_package, top_path)
+ return config
+
+if __name__ == '__main__':
+ from numpy.distutils.core import setup
+ setup(**configuration(top_path='').todict())
Added: trunk/scipy/testing/utils.py
===================================================================
--- trunk/scipy/testing/utils.py 2008-01-12 07:02:12 UTC (rev 3818)
+++ trunk/scipy/testing/utils.py 2008-01-12 07:24:52 UTC (rev 3819)
@@ -0,0 +1,60 @@
+"""Simple testing utilities
+"""
+
+__all__ = ['set_local_path', 'restore_path', 'measure', 'info', 'warn', 'error']
+
+import os
+import sys
+
+from numpy.distutils.misc_util import yellow_text, red_text
+from numpy.testing.utils import jiffies
+
+def set_local_path():
+ """ Prepend local directory to sys.path.
+
+ The caller is responsible for removing this path by using
+
+ restore_path()
+ """
+ f = sys._getframe(1)
+ if f.f_locals['__name__']=='__main__':
+ testfile = sys.argv[0]
+ else:
+ testfile = f.f_locals['__file__']
+ local_path = os.path.normpath(os.path.dirname(os.path.abspath(testfile)))
+ sys.path.insert(0,local_path)
+ return
+
+def restore_path():
+ del sys.path[0]
+ return
+
+def measure(code_str,times=1,label=None):
+ """ Return elapsed time for executing code_str in the
+ namespace of the caller for given times.
+ """
+ frame = sys._getframe(1)
+ locs,globs = frame.f_locals,frame.f_globals
+
+ code = compile(code_str,
+ 'Test name: %s ' % label,
+ 'exec')
+ i = 0
+ elapsed = jiffies()
+ while i<times:
+ i += 1
+ exec code in globs,locs
+ elapsed = jiffies() - elapsed
+ return 0.01*elapsed
+
+def info(message):
+ print >> sys.stdout, message
+ sys.stdout.flush()
+
+def warn(message):
+ print >> sys.stderr,yellow_text('Warning: %s' % (message))
+ sys.stderr.flush()
+
+def error(message):
+ print >> sys.stderr,red_text('Error: %s' % (message))
+ sys.stderr.flush()
More information about the Scipy-svn
mailing list