##// END OF EJS Templates
Update decorators and test scripts.
Fernando Perez -
Show More
@@ -1,160 +1,244 b''
1 """Decorators for labeling test objects.
1 """Decorators for labeling test objects.
2
2
3 Decorators that merely return a modified version of the original
3 Decorators that merely return a modified version of the original function
4 function object are straightforward. Decorators that return a new
4 object are straightforward. Decorators that return a new function object need
5 function object need to use
5 to use nose.tools.make_decorator(original_function)(decorator) in returning the
6 nose.tools.make_decorator(original_function)(decorator) in returning
6 decorator, in order to preserve metadata such as function name, setup and
7 the decorator, in order to preserve metadata such as function name,
7 teardown functions and so on - see nose.tools for more information.
8 setup and teardown functions and so on - see nose.tools for more
9 information.
10
8
11 This module provides a set of useful decorators meant to be ready to use in
9 This module provides a set of useful decorators meant to be ready to use in
12 your own tests. See the bottom of the file for the ready-made ones, and if you
10 your own tests. See the bottom of the file for the ready-made ones, and if you
13 find yourself writing a new one that may be of generic use, add it here.
11 find yourself writing a new one that may be of generic use, add it here.
14
12
15 NOTE: This file contains IPython-specific decorators and imports the
13 NOTE: This file contains IPython-specific decorators and imports the
16 numpy.testing.decorators file, which we've copied verbatim. Any of our own
14 numpy.testing.decorators file, which we've copied verbatim. Any of our own
17 code will be added at the bottom if we end up extending this.
15 code will be added at the bottom if we end up extending this.
18 """
16 """
19
17
20 # Stdlib imports
18 # Stdlib imports
21 import inspect
19 import inspect
22 import sys
20 import sys
23
21
24 # Third-party imports
22 # Third-party imports
25
23
26 # This is Michele Simionato's decorator module, also kept verbatim.
24 # This is Michele Simionato's decorator module, also kept verbatim.
27 from decorator_msim import decorator, update_wrapper
25 from decorator_msim import decorator, update_wrapper
28
26
29 # Grab the numpy-specific decorators which we keep in a file that we
27 # Grab the numpy-specific decorators which we keep in a file that we
30 # occasionally update from upstream: decorators_numpy.py is an IDENTICAL copy
28 # occasionally update from upstream: decorators_numpy.py is an IDENTICAL copy
31 # of numpy.testing.decorators.
29 # of numpy.testing.decorators.
32 from decorators_numpy import *
30 from decorators_numpy import *
33
31
34 ##############################################################################
32 ##############################################################################
35 # Local code begins
33 # Local code begins
36
34
37 # Utility functions
35 # Utility functions
38
36
39 def apply_wrapper(wrapper,func):
37 def apply_wrapper(wrapper,func):
40 """Apply a wrapper to a function for decoration.
38 """Apply a wrapper to a function for decoration.
41
39
42 This mixes Michele Simionato's decorator tool with nose's make_decorator,
40 This mixes Michele Simionato's decorator tool with nose's make_decorator,
43 to apply a wrapper in a decorator so that all nose attributes, as well as
41 to apply a wrapper in a decorator so that all nose attributes, as well as
44 function signature and other properties, survive the decoration cleanly.
42 function signature and other properties, survive the decoration cleanly.
45 This will ensure that wrapped functions can still be well introspected via
43 This will ensure that wrapped functions can still be well introspected via
46 IPython, for example.
44 IPython, for example.
47 """
45 """
48 import nose.tools
46 import nose.tools
49
47
50 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
48 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
51
49
52
50
53 def make_label_dec(label,ds=None):
51 def make_label_dec(label,ds=None):
54 """Factory function to create a decorator that applies one or more labels.
52 """Factory function to create a decorator that applies one or more labels.
55
53
56 :Parameters:
54 :Parameters:
57 label : string or sequence
55 label : string or sequence
58 One or more labels that will be applied by the decorator to the functions
56 One or more labels that will be applied by the decorator to the functions
59 it decorates. Labels are attributes of the decorated function with their
57 it decorates. Labels are attributes of the decorated function with their
60 value set to True.
58 value set to True.
61
59
62 :Keywords:
60 :Keywords:
63 ds : string
61 ds : string
64 An optional docstring for the resulting decorator. If not given, a
62 An optional docstring for the resulting decorator. If not given, a
65 default docstring is auto-generated.
63 default docstring is auto-generated.
66
64
67 :Returns:
65 :Returns:
68 A decorator.
66 A decorator.
69
67
70 :Examples:
68 :Examples:
71
69
72 A simple labeling decorator:
70 A simple labeling decorator:
73 >>> slow = make_label_dec('slow')
71 >>> slow = make_label_dec('slow')
74 >>> print slow.__doc__
72 >>> print slow.__doc__
75 Labels a test as 'slow'.
73 Labels a test as 'slow'.
76
74
77 And one that uses multiple labels and a custom docstring:
75 And one that uses multiple labels and a custom docstring:
78 >>> rare = make_label_dec(['slow','hard'],
76 >>> rare = make_label_dec(['slow','hard'],
79 ... "Mix labels 'slow' and 'hard' for rare tests.")
77 ... "Mix labels 'slow' and 'hard' for rare tests.")
80 >>> print rare.__doc__
78 >>> print rare.__doc__
81 Mix labels 'slow' and 'hard' for rare tests.
79 Mix labels 'slow' and 'hard' for rare tests.
82
80
83 Now, let's test using this one:
81 Now, let's test using this one:
84 >>> @rare
82 >>> @rare
85 ... def f(): pass
83 ... def f(): pass
86 ...
84 ...
87 >>>
85 >>>
88 >>> f.slow
86 >>> f.slow
89 True
87 True
90 >>> f.hard
88 >>> f.hard
91 True
89 True
92 """
90 """
93
91
94 if isinstance(label,basestring):
92 if isinstance(label,basestring):
95 labels = [label]
93 labels = [label]
96 else:
94 else:
97 labels = label
95 labels = label
98
96
99 # Validate that the given label(s) are OK for use in setattr() by doing a
97 # Validate that the given label(s) are OK for use in setattr() by doing a
100 # dry run on a dummy function.
98 # dry run on a dummy function.
101 tmp = lambda : None
99 tmp = lambda : None
102 for label in labels:
100 for label in labels:
103 setattr(tmp,label,True)
101 setattr(tmp,label,True)
104
102
105 # This is the actual decorator we'll return
103 # This is the actual decorator we'll return
106 def decor(f):
104 def decor(f):
107 for label in labels:
105 for label in labels:
108 setattr(f,label,True)
106 setattr(f,label,True)
109 return f
107 return f
110
108
111 # Apply the user's docstring, or autogenerate a basic one
109 # Apply the user's docstring, or autogenerate a basic one
112 if ds is None:
110 if ds is None:
113 ds = "Labels a test as %r." % label
111 ds = "Labels a test as %r." % label
114 decor.__doc__ = ds
112 decor.__doc__ = ds
115
113
116 return decor
114 return decor
117
115
118 #-----------------------------------------------------------------------------
119 # Decorators for public use
120
121 skip_doctest = make_label_dec('skip_doctest',
122 """Decorator - mark a function or method for skipping its doctest.
123
116
124 This decorator allows you to mark a function whose docstring you wish to
117 # Inspired by numpy's skipif, but uses the full apply_wrapper utility to
125 omit from testing, while preserving the docstring for introspection, help,
118 # preserve function metadata better and allows the skip condition to be a
126 etc.""")
119 # callable.
127
120 def skipif(skip_condition, msg=None):
128 def skip(msg=''):
121 ''' Make function raise SkipTest exception if skip_condition is true
129 """Decorator - mark a test function for skipping from test suite.
122
130
123 Parameters
131 This function *is* already a decorator, it is not a factory like
124 ---------
132 make_label_dec or some of those in decorators_numpy.
125 skip_condition : bool or callable.
126 Flag to determine whether to skip test. If the condition is a
127 callable, it is used at runtime to dynamically make the decision. This
128 is useful for tests that may require costly imports, to delay the cost
129 until the test suite is actually executed.
130 msg : string
131 Message to give on raising a SkipTest exception
132
133 Returns
134 -------
135 decorator : function
136 Decorator, which, when applied to a function, causes SkipTest
137 to be raised when the skip_condition was True, and the function
138 to be called normally otherwise.
139
140 Notes
141 -----
142 You will see from the code that we had to further decorate the
143 decorator with the nose.tools.make_decorator function in order to
144 transmit function name, and various other metadata.
145 '''
146
147 def skip_decorator(f):
148 # Local import to avoid a hard nose dependency and only incur the
149 # import time overhead at actual test-time.
150 import nose
151
152 # Allow for both boolean or callable skip conditions.
153 if callable(skip_condition):
154 skip_val = lambda : skip_condition()
155 else:
156 skip_val = lambda : skip_condition
157
158 def get_msg(func,msg=None):
159 """Skip message with information about function being skipped."""
160 if msg is None: out = 'Test skipped due to test condition.'
161 else: out = msg
162 return "Skipping test: %s. %s" % (func.__name__,out)
163
164 # We need to define *two* skippers because Python doesn't allow both
165 # return with value and yield inside the same function.
166 def skipper_func(*args, **kwargs):
167 """Skipper for normal test functions."""
168 if skip_val():
169 raise nose.SkipTest(get_msg(f,msg))
170 else:
171 return f(*args, **kwargs)
172
173 def skipper_gen(*args, **kwargs):
174 """Skipper for test generators."""
175 if skip_val():
176 raise nose.SkipTest(get_msg(f,msg))
177 else:
178 for x in f(*args, **kwargs):
179 yield x
180
181 # Choose the right skipper to use when building the actual generator.
182 if nose.util.isgenerator(f):
183 skipper = skipper_gen
184 else:
185 skipper = skipper_func
186
187 return nose.tools.make_decorator(f)(skipper)
188
189 return skip_decorator
190
191 # A version with the condition set to true, common case just to attacha message
192 # to a skip decorator
193 def skip(msg=None):
194 """Decorator factory - mark a test function for skipping from test suite.
133
195
134 :Parameters:
196 :Parameters:
135
136 func : function
137 Test function to be skipped
138
139 msg : string
197 msg : string
140 Optional message to be added.
198 Optional message to be added.
199
200 :Returns:
201 decorator : function
202 Decorator, which, when applied to a function, causes SkipTest
203 to be raised, with the optional message added.
141 """
204 """
142
205
143 import nose
206 return skipif(True,msg)
144
207
145 def inner(func):
146
208
147 def wrapper(*a,**k):
209 #-----------------------------------------------------------------------------
148 if msg: out = '\n'+msg
210 # Utility functions for decorators
149 else: out = ''
211 def numpy_not_available():
150 raise nose.SkipTest("Skipping test for function: %s%s" %
212 """Can numpy be imported? Returns true if numpy does NOT import.
151 (func.__name__,out))
152
213
153 return apply_wrapper(wrapper,func)
214 This is used to make a decorator to skip tests that require numpy to be
215 available, but delay the 'import numpy' to test execution time.
216 """
217 try:
218 import numpy
219 np_not_avail = False
220 except ImportError:
221 np_not_avail = True
222
223 return np_not_avail
224
225 #-----------------------------------------------------------------------------
226 # Decorators for public use
227
228 skip_doctest = make_label_dec('skip_doctest',
229 """Decorator - mark a function or method for skipping its doctest.
154
230
155 return inner
231 This decorator allows you to mark a function whose docstring you wish to
232 omit from testing, while preserving the docstring for introspection, help,
233 etc.""")
156
234
157 # Decorators to skip certain tests on specific platforms.
235 # Decorators to skip certain tests on specific platforms.
158 skip_win32 = skipif(sys.platform=='win32',"This test does not run under Windows")
236 skip_win32 = skipif(sys.platform=='win32',
237 "This test does not run under Windows")
159 skip_linux = skipif(sys.platform=='linux2',"This test does not run under Linux")
238 skip_linux = skipif(sys.platform=='linux2',"This test does not run under Linux")
160 skip_osx = skipif(sys.platform=='darwin',"This test does not run under OSX")
239 skip_osx = skipif(sys.platform=='darwin',"This test does not run under OS X")
240
241
242 skipif_not_numpy = skipif(numpy_not_available,"This test requires numpy")
243
244 skipknownfailure = skip('This test is known to fail')
@@ -1,53 +1,55 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """IPython Test Suite Runner.
2 """IPython Test Suite Runner.
3 """
3 """
4
4
5 import sys
5 import sys
6 import warnings
6 import warnings
7
7
8 from nose.core import TestProgram
8 from nose.core import TestProgram
9 import nose.plugins.builtin
9 import nose.plugins.builtin
10
10
11 from IPython.testing.plugin.ipdoctest import IPythonDoctest
11 from IPython.testing.plugin.ipdoctest import IPythonDoctest
12
12
13 def main():
13 def main():
14 """Run the IPython test suite.
14 """Run the IPython test suite.
15 """
15 """
16
16
17 warnings.filterwarnings('ignore',
17 warnings.filterwarnings('ignore',
18 'This will be removed soon. Use IPython.testing.util instead')
18 'This will be removed soon. Use IPython.testing.util instead')
19
19
20 argv = sys.argv + [ '--with-ipdoctest',
20 argv = sys.argv + [ '--with-ipdoctest',
21 '--doctest-tests','--doctest-extension=txt',
21 '--doctest-tests','--doctest-extension=txt',
22 '--detailed-errors',
22 '--detailed-errors',
23
23
24 # We add --exe because of setuptools' imbecility (it
24 # We add --exe because of setuptools' imbecility (it
25 # blindly does chmod +x on ALL files). Nose does the
25 # blindly does chmod +x on ALL files). Nose does the
26 # right thing and it tries to avoid executables,
26 # right thing and it tries to avoid executables,
27 # setuptools unfortunately forces our hand here. This
27 # setuptools unfortunately forces our hand here. This
28 # has been discussed on the distutils list and the
28 # has been discussed on the distutils list and the
29 # setuptools devs refuse to fix this problem!
29 # setuptools devs refuse to fix this problem!
30 '--exe',
30 '--exe',
31 ]
31 ]
32
32
33 has_ip = False
33 # Detect if any tests were required by explicitly calling an IPython
34 # submodule or giving a specific path
35 has_tests = False
34 for arg in sys.argv:
36 for arg in sys.argv:
35 if 'IPython' in arg:
37 if 'IPython' in arg or arg.endswith('.py') or \
36 has_ip = True
38 (':' in arg and '.py' in arg):
39 has_tests = True
37 break
40 break
38
41 # If nothing was specifically requested, test full IPython
39 if not has_ip:
42 if not has_tests:
40 argv.append('IPython')
43 argv.append('IPython')
41
44
42 # construct list of plugins, omitting the existing doctest plugin
45 # construct list of plugins, omitting the existing doctest plugin
43 plugins = [IPythonDoctest()]
46 plugins = [IPythonDoctest()]
44 for p in nose.plugins.builtin.plugins:
47 for p in nose.plugins.builtin.plugins:
45 plug = p()
48 plug = p()
46 if plug.name == 'doctest':
49 if plug.name == 'doctest':
47 continue
50 continue
48
51
49 #print '*** adding plugin:',plug.name # dbg
52 #print '*** adding plugin:',plug.name # dbg
50 plugins.append(plug)
53 plugins.append(plug)
51
54
52
53 TestProgram(argv=argv,plugins=plugins)
55 TestProgram(argv=argv,plugins=plugins)
@@ -1,92 +1,115 b''
1 """ Tests for various magic functions
1 """Tests for various magic functions.
2
2
3 Needs to be run by nose (to make ipython session available)
3 Needs to be run by nose (to make ipython session available).
4 """
4 """
5
5
6 # Standard library imports
7 import os
8 import sys
9
10 # Third-party imports
11 import nose.tools as nt
12
13 # From our own code
6 from IPython.testing import decorators as dec
14 from IPython.testing import decorators as dec
7
15
16 #-----------------------------------------------------------------------------
17 # Test functions begin
18
8 def test_rehashx():
19 def test_rehashx():
9 # clear up everything
20 # clear up everything
10 _ip.IP.alias_table.clear()
21 _ip.IP.alias_table.clear()
11 del _ip.db['syscmdlist']
22 del _ip.db['syscmdlist']
12
23
13 _ip.magic('rehashx')
24 _ip.magic('rehashx')
14 # Practically ALL ipython development systems will have more than 10 aliases
25 # Practically ALL ipython development systems will have more than 10 aliases
15
26
16 assert len(_ip.IP.alias_table) > 10
27 assert len(_ip.IP.alias_table) > 10
17 for key, val in _ip.IP.alias_table.items():
28 for key, val in _ip.IP.alias_table.items():
18 # we must strip dots from alias names
29 # we must strip dots from alias names
19 assert '.' not in key
30 assert '.' not in key
20
31
21 # rehashx must fill up syscmdlist
32 # rehashx must fill up syscmdlist
22 scoms = _ip.db['syscmdlist']
33 scoms = _ip.db['syscmdlist']
23 assert len(scoms) > 10
34 assert len(scoms) > 10
24
35
25
36
26 def doctest_run_ns():
37 def doctest_run_ns():
27 """Classes declared %run scripts must be instantiable afterwards.
38 """Classes declared %run scripts must be instantiable afterwards.
28
39
29 In [3]: run tclass.py
40 In [3]: run tclass.py
30
41
31 In [4]: f()
42 In [4]: f()
32 """
43 """
33 pass # doctest only
44 pass # doctest only
34
45
35
46
36 def doctest_hist_f():
47 def doctest_hist_f():
37 """Test %hist -f with temporary filename.
48 """Test %hist -f with temporary filename.
38
49
39 In [9]: import tempfile
50 In [9]: import tempfile
40
51
41 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
52 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
42
53
43 In [11]: %history -n -f $tfile 3
54 In [11]: %history -n -f $tfile 3
44 """
55 """
45
56
46
57
47 def doctest_hist_r():
58 def doctest_hist_r():
48 """Test %hist -r
59 """Test %hist -r
49
60
50 XXX - This test is not recording the output correctly. Not sure why...
61 XXX - This test is not recording the output correctly. Not sure why...
51
62
52 In [6]: x=1
63 In [6]: x=1
53
64
54 In [7]: hist -n -r 2
65 In [7]: hist -n -r 2
55 x=1 # random
66 x=1 # random
56 hist -n -r 2 # random
67 hist -n -r 2 # random
57 """
68 """
58
69
59
70
60 def test_shist():
71 def test_shist():
61 # Simple tests of ShadowHist class
72 # Simple tests of ShadowHist class - test generator.
62 import os, shutil, tempfile
73 import os, shutil, tempfile
63 import nose.tools as nt
64
74
65 from IPython.Extensions import pickleshare
75 from IPython.Extensions import pickleshare
66 from IPython.history import ShadowHist
76 from IPython.history import ShadowHist
67
77
68
69 tfile = tempfile.mktemp('','tmp-ipython-')
78 tfile = tempfile.mktemp('','tmp-ipython-')
70
79
71 db = pickleshare.PickleShareDB(tfile)
80 db = pickleshare.PickleShareDB(tfile)
72 s = ShadowHist(db)
81 s = ShadowHist(db)
73 s.add('hello')
82 s.add('hello')
74 s.add('world')
83 s.add('world')
75 s.add('hello')
84 s.add('hello')
76 s.add('hello')
85 s.add('hello')
77 s.add('karhu')
86 s.add('karhu')
78
87
79 yield nt.assert_equals,s.all(),[(1, 'hello'), (2, 'world'), (3, 'karhu')]
88 yield nt.assert_equals,s.all(),[(1, 'hello'), (2, 'world'), (3, 'karhu')]
80
89
81 yield nt.assert_equal,s.get(2),'world'
90 yield nt.assert_equal,s.get(2),'world'
82
91
83 shutil.rmtree(tfile)
92 shutil.rmtree(tfile)
84
93
94 @dec.skipif_not_numpy
95 def test_numpy_clear_array_undec():
96 _ip.ex('import numpy as np')
97 _ip.ex('a = np.empty(2)')
98
99 yield nt.assert_true,'a' in _ip.user_ns
100 _ip.magic('clear array')
101 yield nt.assert_false,'a' in _ip.user_ns
102
85
103
86 @dec.skip_numpy_not_avail
104 @dec.skip()
87 def doctest_clear_array():
105 def test_fail_dec(*a,**k):
88 """Check that array clearing works.
106 yield nt.assert_true, False
107
108 @dec.skip('This one shouldn not run')
109 def test_fail_dec2(*a,**k):
110 yield nt.assert_true, False
111
112 @dec.skipknownfailure
113 def test_fail_dec3(*a,**k):
114 yield nt.assert_true, False
89
115
90 >>> 1/0
91 """
92 pass # doctest only
General Comments 0
You need to be logged in to leave comments. Login now