[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