##// END OF EJS Templates
Remove unused imports in IPython.testing
Thomas Kluyver -
Show More
@@ -1,360 +1,359 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Decorators for labeling test objects.
2 """Decorators for labeling test objects.
3
3
4 Decorators that merely return a modified version of the original function
4 Decorators that merely return a modified version of the original function
5 object are straightforward. Decorators that return a new function object need
5 object are straightforward. Decorators that return a new function object need
6 to use nose.tools.make_decorator(original_function)(decorator) in returning the
6 to use nose.tools.make_decorator(original_function)(decorator) in returning the
7 decorator, in order to preserve metadata such as function name, setup and
7 decorator, in order to preserve metadata such as function name, setup and
8 teardown functions and so on - see nose.tools for more information.
8 teardown functions and so on - see nose.tools for more information.
9
9
10 This module provides a set of useful decorators meant to be ready to use in
10 This module provides a set of useful decorators meant to be ready to use in
11 your own tests. See the bottom of the file for the ready-made ones, and if you
11 your own tests. See the bottom of the file for the ready-made ones, and if you
12 find yourself writing a new one that may be of generic use, add it here.
12 find yourself writing a new one that may be of generic use, add it here.
13
13
14 Included decorators:
14 Included decorators:
15
15
16
16
17 Lightweight testing that remains unittest-compatible.
17 Lightweight testing that remains unittest-compatible.
18
18
19 - @parametric, for parametric test support that is vastly easier to use than
19 - @parametric, for parametric test support that is vastly easier to use than
20 nose's for debugging. With ours, if a test fails, the stack under inspection
20 nose's for debugging. With ours, if a test fails, the stack under inspection
21 is that of the test and not that of the test framework.
21 is that of the test and not that of the test framework.
22
22
23 - An @as_unittest decorator can be used to tag any normal parameter-less
23 - An @as_unittest decorator can be used to tag any normal parameter-less
24 function as a unittest TestCase. Then, both nose and normal unittest will
24 function as a unittest TestCase. Then, both nose and normal unittest will
25 recognize it as such. This will make it easier to migrate away from Nose if
25 recognize it as such. This will make it easier to migrate away from Nose if
26 we ever need/want to while maintaining very lightweight tests.
26 we ever need/want to while maintaining very lightweight tests.
27
27
28 NOTE: This file contains IPython-specific decorators. Using the machinery in
28 NOTE: This file contains IPython-specific decorators. Using the machinery in
29 IPython.external.decorators, we import either numpy.testing.decorators if numpy is
29 IPython.external.decorators, we import either numpy.testing.decorators if numpy is
30 available, OR use equivalent code in IPython.external._decorators, which
30 available, OR use equivalent code in IPython.external._decorators, which
31 we've copied verbatim from numpy.
31 we've copied verbatim from numpy.
32
32
33 Authors
33 Authors
34 -------
34 -------
35
35
36 - Fernando Perez <Fernando.Perez@berkeley.edu>
36 - Fernando Perez <Fernando.Perez@berkeley.edu>
37 """
37 """
38
38
39 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
40 # Copyright (C) 2009-2011 The IPython Development Team
40 # Copyright (C) 2009-2011 The IPython Development Team
41 #
41 #
42 # Distributed under the terms of the BSD License. The full license is in
42 # Distributed under the terms of the BSD License. The full license is in
43 # the file COPYING, distributed as part of this software.
43 # the file COPYING, distributed as part of this software.
44 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45
45
46 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
47 # Imports
47 # Imports
48 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
49
49
50 # Stdlib imports
50 # Stdlib imports
51 import inspect
52 import sys
51 import sys
53 import tempfile
52 import tempfile
54 import unittest
53 import unittest
55
54
56 # Third-party imports
55 # Third-party imports
57
56
58 # This is Michele Simionato's decorator module, kept verbatim.
57 # This is Michele Simionato's decorator module, kept verbatim.
59 from IPython.external.decorator import decorator
58 from IPython.external.decorator import decorator
60
59
61 # We already have python3-compliant code for parametric tests
60 # We already have python3-compliant code for parametric tests
62 if sys.version[0]=='2':
61 if sys.version[0]=='2':
63 from _paramtestpy2 import parametric, ParametricTestCase
62 from _paramtestpy2 import parametric
64 else:
63 else:
65 from _paramtestpy3 import parametric, ParametricTestCase
64 from _paramtestpy3 import parametric
66
65
67 # Expose the unittest-driven decorators
66 # Expose the unittest-driven decorators
68 from ipunittest import ipdoctest, ipdocstring
67 from ipunittest import ipdoctest, ipdocstring
69
68
70 # Grab the numpy-specific decorators which we keep in a file that we
69 # Grab the numpy-specific decorators which we keep in a file that we
71 # occasionally update from upstream: decorators.py is a copy of
70 # occasionally update from upstream: decorators.py is a copy of
72 # numpy.testing.decorators, we expose all of it here.
71 # numpy.testing.decorators, we expose all of it here.
73 from IPython.external.decorators import *
72 from IPython.external.decorators import *
74
73
75 # For onlyif_cmd_exists decorator
74 # For onlyif_cmd_exists decorator
76 from IPython.utils.process import is_cmd_found
75 from IPython.utils.process import is_cmd_found
77
76
78 #-----------------------------------------------------------------------------
77 #-----------------------------------------------------------------------------
79 # Classes and functions
78 # Classes and functions
80 #-----------------------------------------------------------------------------
79 #-----------------------------------------------------------------------------
81
80
82 # Simple example of the basic idea
81 # Simple example of the basic idea
83 def as_unittest(func):
82 def as_unittest(func):
84 """Decorator to make a simple function into a normal test via unittest."""
83 """Decorator to make a simple function into a normal test via unittest."""
85 class Tester(unittest.TestCase):
84 class Tester(unittest.TestCase):
86 def test(self):
85 def test(self):
87 func()
86 func()
88
87
89 Tester.__name__ = func.__name__
88 Tester.__name__ = func.__name__
90
89
91 return Tester
90 return Tester
92
91
93 # Utility functions
92 # Utility functions
94
93
95 def apply_wrapper(wrapper,func):
94 def apply_wrapper(wrapper,func):
96 """Apply a wrapper to a function for decoration.
95 """Apply a wrapper to a function for decoration.
97
96
98 This mixes Michele Simionato's decorator tool with nose's make_decorator,
97 This mixes Michele Simionato's decorator tool with nose's make_decorator,
99 to apply a wrapper in a decorator so that all nose attributes, as well as
98 to apply a wrapper in a decorator so that all nose attributes, as well as
100 function signature and other properties, survive the decoration cleanly.
99 function signature and other properties, survive the decoration cleanly.
101 This will ensure that wrapped functions can still be well introspected via
100 This will ensure that wrapped functions can still be well introspected via
102 IPython, for example.
101 IPython, for example.
103 """
102 """
104 import nose.tools
103 import nose.tools
105
104
106 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
105 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
107
106
108
107
109 def make_label_dec(label,ds=None):
108 def make_label_dec(label,ds=None):
110 """Factory function to create a decorator that applies one or more labels.
109 """Factory function to create a decorator that applies one or more labels.
111
110
112 Parameters
111 Parameters
113 ----------
112 ----------
114 label : string or sequence
113 label : string or sequence
115 One or more labels that will be applied by the decorator to the functions
114 One or more labels that will be applied by the decorator to the functions
116 it decorates. Labels are attributes of the decorated function with their
115 it decorates. Labels are attributes of the decorated function with their
117 value set to True.
116 value set to True.
118
117
119 ds : string
118 ds : string
120 An optional docstring for the resulting decorator. If not given, a
119 An optional docstring for the resulting decorator. If not given, a
121 default docstring is auto-generated.
120 default docstring is auto-generated.
122
121
123 Returns
122 Returns
124 -------
123 -------
125 A decorator.
124 A decorator.
126
125
127 Examples
126 Examples
128 --------
127 --------
129
128
130 A simple labeling decorator:
129 A simple labeling decorator:
131
130
132 >>> slow = make_label_dec('slow')
131 >>> slow = make_label_dec('slow')
133 >>> slow.__doc__
132 >>> slow.__doc__
134 "Labels a test as 'slow'."
133 "Labels a test as 'slow'."
135
134
136 And one that uses multiple labels and a custom docstring:
135 And one that uses multiple labels and a custom docstring:
137
136
138 >>> rare = make_label_dec(['slow','hard'],
137 >>> rare = make_label_dec(['slow','hard'],
139 ... "Mix labels 'slow' and 'hard' for rare tests.")
138 ... "Mix labels 'slow' and 'hard' for rare tests.")
140 >>> rare.__doc__
139 >>> rare.__doc__
141 "Mix labels 'slow' and 'hard' for rare tests."
140 "Mix labels 'slow' and 'hard' for rare tests."
142
141
143 Now, let's test using this one:
142 Now, let's test using this one:
144 >>> @rare
143 >>> @rare
145 ... def f(): pass
144 ... def f(): pass
146 ...
145 ...
147 >>>
146 >>>
148 >>> f.slow
147 >>> f.slow
149 True
148 True
150 >>> f.hard
149 >>> f.hard
151 True
150 True
152 """
151 """
153
152
154 if isinstance(label,basestring):
153 if isinstance(label,basestring):
155 labels = [label]
154 labels = [label]
156 else:
155 else:
157 labels = label
156 labels = label
158
157
159 # Validate that the given label(s) are OK for use in setattr() by doing a
158 # Validate that the given label(s) are OK for use in setattr() by doing a
160 # dry run on a dummy function.
159 # dry run on a dummy function.
161 tmp = lambda : None
160 tmp = lambda : None
162 for label in labels:
161 for label in labels:
163 setattr(tmp,label,True)
162 setattr(tmp,label,True)
164
163
165 # This is the actual decorator we'll return
164 # This is the actual decorator we'll return
166 def decor(f):
165 def decor(f):
167 for label in labels:
166 for label in labels:
168 setattr(f,label,True)
167 setattr(f,label,True)
169 return f
168 return f
170
169
171 # Apply the user's docstring, or autogenerate a basic one
170 # Apply the user's docstring, or autogenerate a basic one
172 if ds is None:
171 if ds is None:
173 ds = "Labels a test as %r." % label
172 ds = "Labels a test as %r." % label
174 decor.__doc__ = ds
173 decor.__doc__ = ds
175
174
176 return decor
175 return decor
177
176
178
177
179 # Inspired by numpy's skipif, but uses the full apply_wrapper utility to
178 # Inspired by numpy's skipif, but uses the full apply_wrapper utility to
180 # preserve function metadata better and allows the skip condition to be a
179 # preserve function metadata better and allows the skip condition to be a
181 # callable.
180 # callable.
182 def skipif(skip_condition, msg=None):
181 def skipif(skip_condition, msg=None):
183 ''' Make function raise SkipTest exception if skip_condition is true
182 ''' Make function raise SkipTest exception if skip_condition is true
184
183
185 Parameters
184 Parameters
186 ----------
185 ----------
187 skip_condition : bool or callable.
186 skip_condition : bool or callable.
188 Flag to determine whether to skip test. If the condition is a
187 Flag to determine whether to skip test. If the condition is a
189 callable, it is used at runtime to dynamically make the decision. This
188 callable, it is used at runtime to dynamically make the decision. This
190 is useful for tests that may require costly imports, to delay the cost
189 is useful for tests that may require costly imports, to delay the cost
191 until the test suite is actually executed.
190 until the test suite is actually executed.
192 msg : string
191 msg : string
193 Message to give on raising a SkipTest exception
192 Message to give on raising a SkipTest exception
194
193
195 Returns
194 Returns
196 -------
195 -------
197 decorator : function
196 decorator : function
198 Decorator, which, when applied to a function, causes SkipTest
197 Decorator, which, when applied to a function, causes SkipTest
199 to be raised when the skip_condition was True, and the function
198 to be raised when the skip_condition was True, and the function
200 to be called normally otherwise.
199 to be called normally otherwise.
201
200
202 Notes
201 Notes
203 -----
202 -----
204 You will see from the code that we had to further decorate the
203 You will see from the code that we had to further decorate the
205 decorator with the nose.tools.make_decorator function in order to
204 decorator with the nose.tools.make_decorator function in order to
206 transmit function name, and various other metadata.
205 transmit function name, and various other metadata.
207 '''
206 '''
208
207
209 def skip_decorator(f):
208 def skip_decorator(f):
210 # Local import to avoid a hard nose dependency and only incur the
209 # Local import to avoid a hard nose dependency and only incur the
211 # import time overhead at actual test-time.
210 # import time overhead at actual test-time.
212 import nose
211 import nose
213
212
214 # Allow for both boolean or callable skip conditions.
213 # Allow for both boolean or callable skip conditions.
215 if callable(skip_condition):
214 if callable(skip_condition):
216 skip_val = skip_condition
215 skip_val = skip_condition
217 else:
216 else:
218 skip_val = lambda : skip_condition
217 skip_val = lambda : skip_condition
219
218
220 def get_msg(func,msg=None):
219 def get_msg(func,msg=None):
221 """Skip message with information about function being skipped."""
220 """Skip message with information about function being skipped."""
222 if msg is None: out = 'Test skipped due to test condition.'
221 if msg is None: out = 'Test skipped due to test condition.'
223 else: out = msg
222 else: out = msg
224 return "Skipping test: %s. %s" % (func.__name__,out)
223 return "Skipping test: %s. %s" % (func.__name__,out)
225
224
226 # We need to define *two* skippers because Python doesn't allow both
225 # We need to define *two* skippers because Python doesn't allow both
227 # return with value and yield inside the same function.
226 # return with value and yield inside the same function.
228 def skipper_func(*args, **kwargs):
227 def skipper_func(*args, **kwargs):
229 """Skipper for normal test functions."""
228 """Skipper for normal test functions."""
230 if skip_val():
229 if skip_val():
231 raise nose.SkipTest(get_msg(f,msg))
230 raise nose.SkipTest(get_msg(f,msg))
232 else:
231 else:
233 return f(*args, **kwargs)
232 return f(*args, **kwargs)
234
233
235 def skipper_gen(*args, **kwargs):
234 def skipper_gen(*args, **kwargs):
236 """Skipper for test generators."""
235 """Skipper for test generators."""
237 if skip_val():
236 if skip_val():
238 raise nose.SkipTest(get_msg(f,msg))
237 raise nose.SkipTest(get_msg(f,msg))
239 else:
238 else:
240 for x in f(*args, **kwargs):
239 for x in f(*args, **kwargs):
241 yield x
240 yield x
242
241
243 # Choose the right skipper to use when building the actual generator.
242 # Choose the right skipper to use when building the actual generator.
244 if nose.util.isgenerator(f):
243 if nose.util.isgenerator(f):
245 skipper = skipper_gen
244 skipper = skipper_gen
246 else:
245 else:
247 skipper = skipper_func
246 skipper = skipper_func
248
247
249 return nose.tools.make_decorator(f)(skipper)
248 return nose.tools.make_decorator(f)(skipper)
250
249
251 return skip_decorator
250 return skip_decorator
252
251
253 # A version with the condition set to true, common case just to attach a message
252 # A version with the condition set to true, common case just to attach a message
254 # to a skip decorator
253 # to a skip decorator
255 def skip(msg=None):
254 def skip(msg=None):
256 """Decorator factory - mark a test function for skipping from test suite.
255 """Decorator factory - mark a test function for skipping from test suite.
257
256
258 Parameters
257 Parameters
259 ----------
258 ----------
260 msg : string
259 msg : string
261 Optional message to be added.
260 Optional message to be added.
262
261
263 Returns
262 Returns
264 -------
263 -------
265 decorator : function
264 decorator : function
266 Decorator, which, when applied to a function, causes SkipTest
265 Decorator, which, when applied to a function, causes SkipTest
267 to be raised, with the optional message added.
266 to be raised, with the optional message added.
268 """
267 """
269
268
270 return skipif(True,msg)
269 return skipif(True,msg)
271
270
272
271
273 def onlyif(condition, msg):
272 def onlyif(condition, msg):
274 """The reverse from skipif, see skipif for details."""
273 """The reverse from skipif, see skipif for details."""
275
274
276 if callable(condition):
275 if callable(condition):
277 skip_condition = lambda : not condition()
276 skip_condition = lambda : not condition()
278 else:
277 else:
279 skip_condition = lambda : not condition
278 skip_condition = lambda : not condition
280
279
281 return skipif(skip_condition, msg)
280 return skipif(skip_condition, msg)
282
281
283 #-----------------------------------------------------------------------------
282 #-----------------------------------------------------------------------------
284 # Utility functions for decorators
283 # Utility functions for decorators
285 def module_not_available(module):
284 def module_not_available(module):
286 """Can module be imported? Returns true if module does NOT import.
285 """Can module be imported? Returns true if module does NOT import.
287
286
288 This is used to make a decorator to skip tests that require module to be
287 This is used to make a decorator to skip tests that require module to be
289 available, but delay the 'import numpy' to test execution time.
288 available, but delay the 'import numpy' to test execution time.
290 """
289 """
291 try:
290 try:
292 mod = __import__(module)
291 mod = __import__(module)
293 mod_not_avail = False
292 mod_not_avail = False
294 except ImportError:
293 except ImportError:
295 mod_not_avail = True
294 mod_not_avail = True
296
295
297 return mod_not_avail
296 return mod_not_avail
298
297
299 #-----------------------------------------------------------------------------
298 #-----------------------------------------------------------------------------
300 # Decorators for public use
299 # Decorators for public use
301
300
302 # Decorators to skip certain tests on specific platforms.
301 # Decorators to skip certain tests on specific platforms.
303 skip_win32 = skipif(sys.platform == 'win32',
302 skip_win32 = skipif(sys.platform == 'win32',
304 "This test does not run under Windows")
303 "This test does not run under Windows")
305 skip_linux = skipif(sys.platform.startswith('linux'),
304 skip_linux = skipif(sys.platform.startswith('linux'),
306 "This test does not run under Linux")
305 "This test does not run under Linux")
307 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
306 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
308
307
309
308
310 # Decorators to skip tests if not on specific platforms.
309 # Decorators to skip tests if not on specific platforms.
311 skip_if_not_win32 = skipif(sys.platform != 'win32',
310 skip_if_not_win32 = skipif(sys.platform != 'win32',
312 "This test only runs under Windows")
311 "This test only runs under Windows")
313 skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
312 skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
314 "This test only runs under Linux")
313 "This test only runs under Linux")
315 skip_if_not_osx = skipif(sys.platform != 'darwin',
314 skip_if_not_osx = skipif(sys.platform != 'darwin',
316 "This test only runs under OSX")
315 "This test only runs under OSX")
317
316
318 # Other skip decorators
317 # Other skip decorators
319
318
320 # generic skip without module
319 # generic skip without module
321 skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod)
320 skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod)
322
321
323 skipif_not_numpy = skip_without('numpy')
322 skipif_not_numpy = skip_without('numpy')
324
323
325 skipif_not_matplotlib = skip_without('matplotlib')
324 skipif_not_matplotlib = skip_without('matplotlib')
326
325
327 skipif_not_sympy = skip_without('sympy')
326 skipif_not_sympy = skip_without('sympy')
328
327
329 skip_known_failure = knownfailureif(True,'This test is known to fail')
328 skip_known_failure = knownfailureif(True,'This test is known to fail')
330
329
331 known_failure_py3 = knownfailureif(sys.version_info[0] >= 3,
330 known_failure_py3 = knownfailureif(sys.version_info[0] >= 3,
332 'This test is known to fail on Python 3.')
331 'This test is known to fail on Python 3.')
333
332
334 # A null 'decorator', useful to make more readable code that needs to pick
333 # A null 'decorator', useful to make more readable code that needs to pick
335 # between different decorators based on OS or other conditions
334 # between different decorators based on OS or other conditions
336 null_deco = lambda f: f
335 null_deco = lambda f: f
337
336
338 # Some tests only run where we can use unicode paths. Note that we can't just
337 # Some tests only run where we can use unicode paths. Note that we can't just
339 # check os.path.supports_unicode_filenames, which is always False on Linux.
338 # check os.path.supports_unicode_filenames, which is always False on Linux.
340 try:
339 try:
341 f = tempfile.NamedTemporaryFile(prefix=u"tmp€")
340 f = tempfile.NamedTemporaryFile(prefix=u"tmp€")
342 except UnicodeEncodeError:
341 except UnicodeEncodeError:
343 unicode_paths = False
342 unicode_paths = False
344 else:
343 else:
345 unicode_paths = True
344 unicode_paths = True
346 f.close()
345 f.close()
347
346
348 onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable "
347 onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable "
349 "where we can use unicode in filenames."))
348 "where we can use unicode in filenames."))
350
349
351
350
352 def onlyif_cmds_exist(*commands):
351 def onlyif_cmds_exist(*commands):
353 """
352 """
354 Decorator to skip test when at least one of `commands` is not found.
353 Decorator to skip test when at least one of `commands` is not found.
355 """
354 """
356 for cmd in commands:
355 for cmd in commands:
357 if not is_cmd_found(cmd):
356 if not is_cmd_found(cmd):
358 return skip("This test runs only if command '{0}' "
357 return skip("This test runs only if command '{0}' "
359 "is installed".format(cmd))
358 "is installed".format(cmd))
360 return null_deco
359 return null_deco
@@ -1,632 +1,632 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """IPython Test Suite Runner.
2 """IPython Test Suite Runner.
3
3
4 This module provides a main entry point to a user script to test IPython
4 This module provides a main entry point to a user script to test IPython
5 itself from the command line. There are two ways of running this script:
5 itself from the command line. There are two ways of running this script:
6
6
7 1. With the syntax `iptest all`. This runs our entire test suite by
7 1. With the syntax `iptest all`. This runs our entire test suite by
8 calling this script (with different arguments) recursively. This
8 calling this script (with different arguments) recursively. This
9 causes modules and package to be tested in different processes, using nose
9 causes modules and package to be tested in different processes, using nose
10 or trial where appropriate.
10 or trial where appropriate.
11 2. With the regular nose syntax, like `iptest -vvs IPython`. In this form
11 2. With the regular nose syntax, like `iptest -vvs IPython`. In this form
12 the script simply calls nose, but with special command line flags and
12 the script simply calls nose, but with special command line flags and
13 plugins loaded.
13 plugins loaded.
14
14
15 """
15 """
16
16
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18 # Copyright (C) 2009-2011 The IPython Development Team
18 # Copyright (C) 2009-2011 The IPython Development Team
19 #
19 #
20 # Distributed under the terms of the BSD License. The full license is in
20 # Distributed under the terms of the BSD License. The full license is in
21 # the file COPYING, distributed as part of this software.
21 # the file COPYING, distributed as part of this software.
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Imports
25 # Imports
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 from __future__ import print_function
27 from __future__ import print_function
28
28
29 # Stdlib
29 # Stdlib
30 import glob
30 import glob
31 import os
31 import os
32 import os.path as path
32 import os.path as path
33 import signal
33 import signal
34 import sys
34 import sys
35 import subprocess
35 import subprocess
36 import tempfile
36 import tempfile
37 import time
37 import time
38 import warnings
38 import warnings
39
39
40 # Note: monkeypatch!
40 # Note: monkeypatch!
41 # We need to monkeypatch a small problem in nose itself first, before importing
41 # We need to monkeypatch a small problem in nose itself first, before importing
42 # it for actual use. This should get into nose upstream, but its release cycle
42 # it for actual use. This should get into nose upstream, but its release cycle
43 # is slow and we need it for our parametric tests to work correctly.
43 # is slow and we need it for our parametric tests to work correctly.
44 from IPython.testing import nosepatch
44 from IPython.testing import nosepatch
45
45
46 # Monkeypatch extra assert methods into nose.tools if they're not already there.
46 # Monkeypatch extra assert methods into nose.tools if they're not already there.
47 # This can be dropped once we no longer test on Python 2.6
47 # This can be dropped once we no longer test on Python 2.6
48 from IPython.testing import nose_assert_methods
48 from IPython.testing import nose_assert_methods
49
49
50 # Now, proceed to import nose itself
50 # Now, proceed to import nose itself
51 import nose.plugins.builtin
51 import nose.plugins.builtin
52 from nose.plugins.xunit import Xunit
52 from nose.plugins.xunit import Xunit
53 from nose import SkipTest
53 from nose import SkipTest
54 from nose.core import TestProgram
54 from nose.core import TestProgram
55
55
56 # Our own imports
56 # Our own imports
57 from IPython.utils import py3compat
57 from IPython.utils import py3compat
58 from IPython.utils.importstring import import_item
58 from IPython.utils.importstring import import_item
59 from IPython.utils.path import get_ipython_module_path, get_ipython_package_dir
59 from IPython.utils.path import get_ipython_module_path, get_ipython_package_dir
60 from IPython.utils.process import find_cmd, pycmd2argv
60 from IPython.utils.process import pycmd2argv
61 from IPython.utils.sysinfo import sys_info
61 from IPython.utils.sysinfo import sys_info
62 from IPython.utils.tempdir import TemporaryDirectory
62 from IPython.utils.tempdir import TemporaryDirectory
63 from IPython.utils.warn import warn
63 from IPython.utils.warn import warn
64
64
65 from IPython.testing import globalipapp
65 from IPython.testing import globalipapp
66 from IPython.testing.plugin.ipdoctest import IPythonDoctest
66 from IPython.testing.plugin.ipdoctest import IPythonDoctest
67 from IPython.external.decorators import KnownFailure, knownfailureif
67 from IPython.external.decorators import KnownFailure, knownfailureif
68
68
69 pjoin = path.join
69 pjoin = path.join
70
70
71
71
72 #-----------------------------------------------------------------------------
72 #-----------------------------------------------------------------------------
73 # Globals
73 # Globals
74 #-----------------------------------------------------------------------------
74 #-----------------------------------------------------------------------------
75
75
76
76
77 #-----------------------------------------------------------------------------
77 #-----------------------------------------------------------------------------
78 # Warnings control
78 # Warnings control
79 #-----------------------------------------------------------------------------
79 #-----------------------------------------------------------------------------
80
80
81 # Twisted generates annoying warnings with Python 2.6, as will do other code
81 # Twisted generates annoying warnings with Python 2.6, as will do other code
82 # that imports 'sets' as of today
82 # that imports 'sets' as of today
83 warnings.filterwarnings('ignore', 'the sets module is deprecated',
83 warnings.filterwarnings('ignore', 'the sets module is deprecated',
84 DeprecationWarning )
84 DeprecationWarning )
85
85
86 # This one also comes from Twisted
86 # This one also comes from Twisted
87 warnings.filterwarnings('ignore', 'the sha module is deprecated',
87 warnings.filterwarnings('ignore', 'the sha module is deprecated',
88 DeprecationWarning)
88 DeprecationWarning)
89
89
90 # Wx on Fedora11 spits these out
90 # Wx on Fedora11 spits these out
91 warnings.filterwarnings('ignore', 'wxPython/wxWidgets release number mismatch',
91 warnings.filterwarnings('ignore', 'wxPython/wxWidgets release number mismatch',
92 UserWarning)
92 UserWarning)
93
93
94 # ------------------------------------------------------------------------------
94 # ------------------------------------------------------------------------------
95 # Monkeypatch Xunit to count known failures as skipped.
95 # Monkeypatch Xunit to count known failures as skipped.
96 # ------------------------------------------------------------------------------
96 # ------------------------------------------------------------------------------
97 def monkeypatch_xunit():
97 def monkeypatch_xunit():
98 try:
98 try:
99 knownfailureif(True)(lambda: None)()
99 knownfailureif(True)(lambda: None)()
100 except Exception as e:
100 except Exception as e:
101 KnownFailureTest = type(e)
101 KnownFailureTest = type(e)
102
102
103 def addError(self, test, err, capt=None):
103 def addError(self, test, err, capt=None):
104 if issubclass(err[0], KnownFailureTest):
104 if issubclass(err[0], KnownFailureTest):
105 err = (SkipTest,) + err[1:]
105 err = (SkipTest,) + err[1:]
106 return self.orig_addError(test, err, capt)
106 return self.orig_addError(test, err, capt)
107
107
108 Xunit.orig_addError = Xunit.addError
108 Xunit.orig_addError = Xunit.addError
109 Xunit.addError = addError
109 Xunit.addError = addError
110
110
111 #-----------------------------------------------------------------------------
111 #-----------------------------------------------------------------------------
112 # Logic for skipping doctests
112 # Logic for skipping doctests
113 #-----------------------------------------------------------------------------
113 #-----------------------------------------------------------------------------
114 def extract_version(mod):
114 def extract_version(mod):
115 return mod.__version__
115 return mod.__version__
116
116
117 def test_for(item, min_version=None, callback=extract_version):
117 def test_for(item, min_version=None, callback=extract_version):
118 """Test to see if item is importable, and optionally check against a minimum
118 """Test to see if item is importable, and optionally check against a minimum
119 version.
119 version.
120
120
121 If min_version is given, the default behavior is to check against the
121 If min_version is given, the default behavior is to check against the
122 `__version__` attribute of the item, but specifying `callback` allows you to
122 `__version__` attribute of the item, but specifying `callback` allows you to
123 extract the value you are interested in. e.g::
123 extract the value you are interested in. e.g::
124
124
125 In [1]: import sys
125 In [1]: import sys
126
126
127 In [2]: from IPython.testing.iptest import test_for
127 In [2]: from IPython.testing.iptest import test_for
128
128
129 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
129 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
130 Out[3]: True
130 Out[3]: True
131
131
132 """
132 """
133 try:
133 try:
134 check = import_item(item)
134 check = import_item(item)
135 except (ImportError, RuntimeError):
135 except (ImportError, RuntimeError):
136 # GTK reports Runtime error if it can't be initialized even if it's
136 # GTK reports Runtime error if it can't be initialized even if it's
137 # importable.
137 # importable.
138 return False
138 return False
139 else:
139 else:
140 if min_version:
140 if min_version:
141 if callback:
141 if callback:
142 # extra processing step to get version to compare
142 # extra processing step to get version to compare
143 check = callback(check)
143 check = callback(check)
144
144
145 return check >= min_version
145 return check >= min_version
146 else:
146 else:
147 return True
147 return True
148
148
149 # Global dict where we can store information on what we have and what we don't
149 # Global dict where we can store information on what we have and what we don't
150 # have available at test run time
150 # have available at test run time
151 have = {}
151 have = {}
152
152
153 have['curses'] = test_for('_curses')
153 have['curses'] = test_for('_curses')
154 have['matplotlib'] = test_for('matplotlib')
154 have['matplotlib'] = test_for('matplotlib')
155 have['numpy'] = test_for('numpy')
155 have['numpy'] = test_for('numpy')
156 have['pexpect'] = test_for('IPython.external.pexpect')
156 have['pexpect'] = test_for('IPython.external.pexpect')
157 have['pymongo'] = test_for('pymongo')
157 have['pymongo'] = test_for('pymongo')
158 have['pygments'] = test_for('pygments')
158 have['pygments'] = test_for('pygments')
159 have['qt'] = test_for('IPython.external.qt')
159 have['qt'] = test_for('IPython.external.qt')
160 have['rpy2'] = test_for('rpy2')
160 have['rpy2'] = test_for('rpy2')
161 have['sqlite3'] = test_for('sqlite3')
161 have['sqlite3'] = test_for('sqlite3')
162 have['cython'] = test_for('Cython')
162 have['cython'] = test_for('Cython')
163 have['oct2py'] = test_for('oct2py')
163 have['oct2py'] = test_for('oct2py')
164 have['tornado'] = test_for('tornado.version_info', (2,1,0), callback=None)
164 have['tornado'] = test_for('tornado.version_info', (2,1,0), callback=None)
165 have['jinja2'] = test_for('jinja2')
165 have['jinja2'] = test_for('jinja2')
166 have['wx'] = test_for('wx')
166 have['wx'] = test_for('wx')
167 have['wx.aui'] = test_for('wx.aui')
167 have['wx.aui'] = test_for('wx.aui')
168 have['azure'] = test_for('azure')
168 have['azure'] = test_for('azure')
169 have['sphinx'] = test_for('sphinx')
169 have['sphinx'] = test_for('sphinx')
170 have['markdown'] = test_for('markdown')
170 have['markdown'] = test_for('markdown')
171
171
172 min_zmq = (2,1,11)
172 min_zmq = (2,1,11)
173
173
174 have['zmq'] = test_for('zmq.pyzmq_version_info', min_zmq, callback=lambda x: x())
174 have['zmq'] = test_for('zmq.pyzmq_version_info', min_zmq, callback=lambda x: x())
175
175
176 #-----------------------------------------------------------------------------
176 #-----------------------------------------------------------------------------
177 # Functions and classes
177 # Functions and classes
178 #-----------------------------------------------------------------------------
178 #-----------------------------------------------------------------------------
179
179
180 def report():
180 def report():
181 """Return a string with a summary report of test-related variables."""
181 """Return a string with a summary report of test-related variables."""
182
182
183 out = [ sys_info(), '\n']
183 out = [ sys_info(), '\n']
184
184
185 avail = []
185 avail = []
186 not_avail = []
186 not_avail = []
187
187
188 for k, is_avail in have.items():
188 for k, is_avail in have.items():
189 if is_avail:
189 if is_avail:
190 avail.append(k)
190 avail.append(k)
191 else:
191 else:
192 not_avail.append(k)
192 not_avail.append(k)
193
193
194 if avail:
194 if avail:
195 out.append('\nTools and libraries available at test time:\n')
195 out.append('\nTools and libraries available at test time:\n')
196 avail.sort()
196 avail.sort()
197 out.append(' ' + ' '.join(avail)+'\n')
197 out.append(' ' + ' '.join(avail)+'\n')
198
198
199 if not_avail:
199 if not_avail:
200 out.append('\nTools and libraries NOT available at test time:\n')
200 out.append('\nTools and libraries NOT available at test time:\n')
201 not_avail.sort()
201 not_avail.sort()
202 out.append(' ' + ' '.join(not_avail)+'\n')
202 out.append(' ' + ' '.join(not_avail)+'\n')
203
203
204 return ''.join(out)
204 return ''.join(out)
205
205
206
206
207 def make_exclude():
207 def make_exclude():
208 """Make patterns of modules and packages to exclude from testing.
208 """Make patterns of modules and packages to exclude from testing.
209
209
210 For the IPythonDoctest plugin, we need to exclude certain patterns that
210 For the IPythonDoctest plugin, we need to exclude certain patterns that
211 cause testing problems. We should strive to minimize the number of
211 cause testing problems. We should strive to minimize the number of
212 skipped modules, since this means untested code.
212 skipped modules, since this means untested code.
213
213
214 These modules and packages will NOT get scanned by nose at all for tests.
214 These modules and packages will NOT get scanned by nose at all for tests.
215 """
215 """
216 # Simple utility to make IPython paths more readably, we need a lot of
216 # Simple utility to make IPython paths more readably, we need a lot of
217 # these below
217 # these below
218 ipjoin = lambda *paths: pjoin('IPython', *paths)
218 ipjoin = lambda *paths: pjoin('IPython', *paths)
219
219
220 exclusions = [ipjoin('external'),
220 exclusions = [ipjoin('external'),
221 ipjoin('quarantine'),
221 ipjoin('quarantine'),
222 ipjoin('deathrow'),
222 ipjoin('deathrow'),
223 # This guy is probably attic material
223 # This guy is probably attic material
224 ipjoin('testing', 'mkdoctests'),
224 ipjoin('testing', 'mkdoctests'),
225 # Testing inputhook will need a lot of thought, to figure out
225 # Testing inputhook will need a lot of thought, to figure out
226 # how to have tests that don't lock up with the gui event
226 # how to have tests that don't lock up with the gui event
227 # loops in the picture
227 # loops in the picture
228 ipjoin('lib', 'inputhook'),
228 ipjoin('lib', 'inputhook'),
229 # Config files aren't really importable stand-alone
229 # Config files aren't really importable stand-alone
230 ipjoin('config', 'profile'),
230 ipjoin('config', 'profile'),
231 # The notebook 'static' directory contains JS, css and other
231 # The notebook 'static' directory contains JS, css and other
232 # files for web serving. Occasionally projects may put a .py
232 # files for web serving. Occasionally projects may put a .py
233 # file in there (MathJax ships a conf.py), so we might as
233 # file in there (MathJax ships a conf.py), so we might as
234 # well play it safe and skip the whole thing.
234 # well play it safe and skip the whole thing.
235 ipjoin('html', 'static'),
235 ipjoin('html', 'static'),
236 ipjoin('html', 'fabfile'),
236 ipjoin('html', 'fabfile'),
237 ]
237 ]
238 if not have['sqlite3']:
238 if not have['sqlite3']:
239 exclusions.append(ipjoin('core', 'tests', 'test_history'))
239 exclusions.append(ipjoin('core', 'tests', 'test_history'))
240 exclusions.append(ipjoin('core', 'history'))
240 exclusions.append(ipjoin('core', 'history'))
241 if not have['wx']:
241 if not have['wx']:
242 exclusions.append(ipjoin('lib', 'inputhookwx'))
242 exclusions.append(ipjoin('lib', 'inputhookwx'))
243
243
244 if 'IPython.kernel.inprocess' not in sys.argv:
244 if 'IPython.kernel.inprocess' not in sys.argv:
245 exclusions.append(ipjoin('kernel', 'inprocess'))
245 exclusions.append(ipjoin('kernel', 'inprocess'))
246
246
247 # FIXME: temporarily disable autoreload tests, as they can produce
247 # FIXME: temporarily disable autoreload tests, as they can produce
248 # spurious failures in subsequent tests (cythonmagic).
248 # spurious failures in subsequent tests (cythonmagic).
249 exclusions.append(ipjoin('extensions', 'autoreload'))
249 exclusions.append(ipjoin('extensions', 'autoreload'))
250 exclusions.append(ipjoin('extensions', 'tests', 'test_autoreload'))
250 exclusions.append(ipjoin('extensions', 'tests', 'test_autoreload'))
251
251
252 # We do this unconditionally, so that the test suite doesn't import
252 # We do this unconditionally, so that the test suite doesn't import
253 # gtk, changing the default encoding and masking some unicode bugs.
253 # gtk, changing the default encoding and masking some unicode bugs.
254 exclusions.append(ipjoin('lib', 'inputhookgtk'))
254 exclusions.append(ipjoin('lib', 'inputhookgtk'))
255 exclusions.append(ipjoin('kernel', 'zmq', 'gui', 'gtkembed'))
255 exclusions.append(ipjoin('kernel', 'zmq', 'gui', 'gtkembed'))
256
256
257 # These have to be skipped on win32 because the use echo, rm, cd, etc.
257 # These have to be skipped on win32 because the use echo, rm, cd, etc.
258 # See ticket https://github.com/ipython/ipython/issues/87
258 # See ticket https://github.com/ipython/ipython/issues/87
259 if sys.platform == 'win32':
259 if sys.platform == 'win32':
260 exclusions.append(ipjoin('testing', 'plugin', 'test_exampleip'))
260 exclusions.append(ipjoin('testing', 'plugin', 'test_exampleip'))
261 exclusions.append(ipjoin('testing', 'plugin', 'dtexample'))
261 exclusions.append(ipjoin('testing', 'plugin', 'dtexample'))
262
262
263 if not have['pexpect']:
263 if not have['pexpect']:
264 exclusions.extend([ipjoin('lib', 'irunner'),
264 exclusions.extend([ipjoin('lib', 'irunner'),
265 ipjoin('lib', 'tests', 'test_irunner'),
265 ipjoin('lib', 'tests', 'test_irunner'),
266 ipjoin('terminal', 'console'),
266 ipjoin('terminal', 'console'),
267 ])
267 ])
268
268
269 if not have['zmq']:
269 if not have['zmq']:
270 exclusions.append(ipjoin('kernel'))
270 exclusions.append(ipjoin('kernel'))
271 exclusions.append(ipjoin('qt'))
271 exclusions.append(ipjoin('qt'))
272 exclusions.append(ipjoin('html'))
272 exclusions.append(ipjoin('html'))
273 exclusions.append(ipjoin('consoleapp.py'))
273 exclusions.append(ipjoin('consoleapp.py'))
274 exclusions.append(ipjoin('terminal', 'console'))
274 exclusions.append(ipjoin('terminal', 'console'))
275 exclusions.append(ipjoin('parallel'))
275 exclusions.append(ipjoin('parallel'))
276 elif not have['qt'] or not have['pygments']:
276 elif not have['qt'] or not have['pygments']:
277 exclusions.append(ipjoin('qt'))
277 exclusions.append(ipjoin('qt'))
278
278
279 if not have['pymongo']:
279 if not have['pymongo']:
280 exclusions.append(ipjoin('parallel', 'controller', 'mongodb'))
280 exclusions.append(ipjoin('parallel', 'controller', 'mongodb'))
281 exclusions.append(ipjoin('parallel', 'tests', 'test_mongodb'))
281 exclusions.append(ipjoin('parallel', 'tests', 'test_mongodb'))
282
282
283 if not have['matplotlib']:
283 if not have['matplotlib']:
284 exclusions.extend([ipjoin('core', 'pylabtools'),
284 exclusions.extend([ipjoin('core', 'pylabtools'),
285 ipjoin('core', 'tests', 'test_pylabtools'),
285 ipjoin('core', 'tests', 'test_pylabtools'),
286 ipjoin('kernel', 'zmq', 'pylab'),
286 ipjoin('kernel', 'zmq', 'pylab'),
287 ])
287 ])
288
288
289 if not have['cython']:
289 if not have['cython']:
290 exclusions.extend([ipjoin('extensions', 'cythonmagic')])
290 exclusions.extend([ipjoin('extensions', 'cythonmagic')])
291 exclusions.extend([ipjoin('extensions', 'tests', 'test_cythonmagic')])
291 exclusions.extend([ipjoin('extensions', 'tests', 'test_cythonmagic')])
292
292
293 if not have['oct2py']:
293 if not have['oct2py']:
294 exclusions.extend([ipjoin('extensions', 'octavemagic')])
294 exclusions.extend([ipjoin('extensions', 'octavemagic')])
295 exclusions.extend([ipjoin('extensions', 'tests', 'test_octavemagic')])
295 exclusions.extend([ipjoin('extensions', 'tests', 'test_octavemagic')])
296
296
297 if not have['tornado']:
297 if not have['tornado']:
298 exclusions.append(ipjoin('html'))
298 exclusions.append(ipjoin('html'))
299
299
300 if not have['jinja2']:
300 if not have['jinja2']:
301 exclusions.append(ipjoin('html', 'notebookapp'))
301 exclusions.append(ipjoin('html', 'notebookapp'))
302
302
303 if not have['rpy2'] or not have['numpy']:
303 if not have['rpy2'] or not have['numpy']:
304 exclusions.append(ipjoin('extensions', 'rmagic'))
304 exclusions.append(ipjoin('extensions', 'rmagic'))
305 exclusions.append(ipjoin('extensions', 'tests', 'test_rmagic'))
305 exclusions.append(ipjoin('extensions', 'tests', 'test_rmagic'))
306
306
307 if not have['azure']:
307 if not have['azure']:
308 exclusions.append(ipjoin('html', 'services', 'notebooks', 'azurenbmanager'))
308 exclusions.append(ipjoin('html', 'services', 'notebooks', 'azurenbmanager'))
309
309
310 if not all((have['pygments'], have['jinja2'], have['markdown'], have['sphinx'])):
310 if not all((have['pygments'], have['jinja2'], have['markdown'], have['sphinx'])):
311 exclusions.append(ipjoin('nbconvert'))
311 exclusions.append(ipjoin('nbconvert'))
312
312
313 # This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
313 # This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
314 if sys.platform == 'win32':
314 if sys.platform == 'win32':
315 exclusions = [s.replace('\\','\\\\') for s in exclusions]
315 exclusions = [s.replace('\\','\\\\') for s in exclusions]
316
316
317 # check for any exclusions that don't seem to exist:
317 # check for any exclusions that don't seem to exist:
318 parent, _ = os.path.split(get_ipython_package_dir())
318 parent, _ = os.path.split(get_ipython_package_dir())
319 for exclusion in exclusions:
319 for exclusion in exclusions:
320 if exclusion.endswith(('deathrow', 'quarantine')):
320 if exclusion.endswith(('deathrow', 'quarantine')):
321 # ignore deathrow/quarantine, which exist in dev, but not install
321 # ignore deathrow/quarantine, which exist in dev, but not install
322 continue
322 continue
323 fullpath = pjoin(parent, exclusion)
323 fullpath = pjoin(parent, exclusion)
324 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
324 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
325 warn("Excluding nonexistent file: %r" % exclusion)
325 warn("Excluding nonexistent file: %r" % exclusion)
326
326
327 return exclusions
327 return exclusions
328
328
329
329
330 class IPTester(object):
330 class IPTester(object):
331 """Call that calls iptest or trial in a subprocess.
331 """Call that calls iptest or trial in a subprocess.
332 """
332 """
333 #: string, name of test runner that will be called
333 #: string, name of test runner that will be called
334 runner = None
334 runner = None
335 #: list, parameters for test runner
335 #: list, parameters for test runner
336 params = None
336 params = None
337 #: list, arguments of system call to be made to call test runner
337 #: list, arguments of system call to be made to call test runner
338 call_args = None
338 call_args = None
339 #: list, subprocesses we start (for cleanup)
339 #: list, subprocesses we start (for cleanup)
340 processes = None
340 processes = None
341 #: str, coverage xml output file
341 #: str, coverage xml output file
342 coverage_xml = None
342 coverage_xml = None
343
343
344 def __init__(self, runner='iptest', params=None):
344 def __init__(self, runner='iptest', params=None):
345 """Create new test runner."""
345 """Create new test runner."""
346 p = os.path
346 p = os.path
347 if runner == 'iptest':
347 if runner == 'iptest':
348 iptest_app = os.path.abspath(get_ipython_module_path('IPython.testing.iptest'))
348 iptest_app = os.path.abspath(get_ipython_module_path('IPython.testing.iptest'))
349 self.runner = pycmd2argv(iptest_app) + sys.argv[1:]
349 self.runner = pycmd2argv(iptest_app) + sys.argv[1:]
350 else:
350 else:
351 raise Exception('Not a valid test runner: %s' % repr(runner))
351 raise Exception('Not a valid test runner: %s' % repr(runner))
352 if params is None:
352 if params is None:
353 params = []
353 params = []
354 if isinstance(params, str):
354 if isinstance(params, str):
355 params = [params]
355 params = [params]
356 self.params = params
356 self.params = params
357
357
358 # Assemble call
358 # Assemble call
359 self.call_args = self.runner+self.params
359 self.call_args = self.runner+self.params
360
360
361 # Find the section we're testing (IPython.foo)
361 # Find the section we're testing (IPython.foo)
362 for sect in self.params:
362 for sect in self.params:
363 if sect.startswith('IPython') or sect in special_test_suites: break
363 if sect.startswith('IPython') or sect in special_test_suites: break
364 else:
364 else:
365 raise ValueError("Section not found", self.params)
365 raise ValueError("Section not found", self.params)
366
366
367 if '--with-xunit' in self.call_args:
367 if '--with-xunit' in self.call_args:
368
368
369 self.call_args.append('--xunit-file')
369 self.call_args.append('--xunit-file')
370 # FIXME: when Windows uses subprocess.call, these extra quotes are unnecessary:
370 # FIXME: when Windows uses subprocess.call, these extra quotes are unnecessary:
371 xunit_file = path.abspath(sect+'.xunit.xml')
371 xunit_file = path.abspath(sect+'.xunit.xml')
372 if sys.platform == 'win32':
372 if sys.platform == 'win32':
373 xunit_file = '"%s"' % xunit_file
373 xunit_file = '"%s"' % xunit_file
374 self.call_args.append(xunit_file)
374 self.call_args.append(xunit_file)
375
375
376 if '--with-xml-coverage' in self.call_args:
376 if '--with-xml-coverage' in self.call_args:
377 self.coverage_xml = path.abspath(sect+".coverage.xml")
377 self.coverage_xml = path.abspath(sect+".coverage.xml")
378 self.call_args.remove('--with-xml-coverage')
378 self.call_args.remove('--with-xml-coverage')
379 self.call_args = ["coverage", "run", "--source="+sect] + self.call_args[1:]
379 self.call_args = ["coverage", "run", "--source="+sect] + self.call_args[1:]
380
380
381 # Store anything we start to clean up on deletion
381 # Store anything we start to clean up on deletion
382 self.processes = []
382 self.processes = []
383
383
384 def _run_cmd(self):
384 def _run_cmd(self):
385 with TemporaryDirectory() as IPYTHONDIR:
385 with TemporaryDirectory() as IPYTHONDIR:
386 env = os.environ.copy()
386 env = os.environ.copy()
387 env['IPYTHONDIR'] = IPYTHONDIR
387 env['IPYTHONDIR'] = IPYTHONDIR
388 # print >> sys.stderr, '*** CMD:', ' '.join(self.call_args) # dbg
388 # print >> sys.stderr, '*** CMD:', ' '.join(self.call_args) # dbg
389 subp = subprocess.Popen(self.call_args, env=env)
389 subp = subprocess.Popen(self.call_args, env=env)
390 self.processes.append(subp)
390 self.processes.append(subp)
391 # If this fails, the process will be left in self.processes and
391 # If this fails, the process will be left in self.processes and
392 # cleaned up later, but if the wait call succeeds, then we can
392 # cleaned up later, but if the wait call succeeds, then we can
393 # clear the stored process.
393 # clear the stored process.
394 retcode = subp.wait()
394 retcode = subp.wait()
395 self.processes.pop()
395 self.processes.pop()
396 return retcode
396 return retcode
397
397
398 def run(self):
398 def run(self):
399 """Run the stored commands"""
399 """Run the stored commands"""
400 try:
400 try:
401 retcode = self._run_cmd()
401 retcode = self._run_cmd()
402 except KeyboardInterrupt:
402 except KeyboardInterrupt:
403 return -signal.SIGINT
403 return -signal.SIGINT
404 except:
404 except:
405 import traceback
405 import traceback
406 traceback.print_exc()
406 traceback.print_exc()
407 return 1 # signal failure
407 return 1 # signal failure
408
408
409 if self.coverage_xml:
409 if self.coverage_xml:
410 subprocess.call(["coverage", "xml", "-o", self.coverage_xml])
410 subprocess.call(["coverage", "xml", "-o", self.coverage_xml])
411 return retcode
411 return retcode
412
412
413 def __del__(self):
413 def __del__(self):
414 """Cleanup on exit by killing any leftover processes."""
414 """Cleanup on exit by killing any leftover processes."""
415 for subp in self.processes:
415 for subp in self.processes:
416 if subp.poll() is not None:
416 if subp.poll() is not None:
417 continue # process is already dead
417 continue # process is already dead
418
418
419 try:
419 try:
420 print('Cleaning up stale PID: %d' % subp.pid)
420 print('Cleaning up stale PID: %d' % subp.pid)
421 subp.kill()
421 subp.kill()
422 except: # (OSError, WindowsError) ?
422 except: # (OSError, WindowsError) ?
423 # This is just a best effort, if we fail or the process was
423 # This is just a best effort, if we fail or the process was
424 # really gone, ignore it.
424 # really gone, ignore it.
425 pass
425 pass
426 else:
426 else:
427 for i in range(10):
427 for i in range(10):
428 if subp.poll() is None:
428 if subp.poll() is None:
429 time.sleep(0.1)
429 time.sleep(0.1)
430 else:
430 else:
431 break
431 break
432
432
433 if subp.poll() is None:
433 if subp.poll() is None:
434 # The process did not die...
434 # The process did not die...
435 print('... failed. Manual cleanup may be required.')
435 print('... failed. Manual cleanup may be required.')
436
436
437
437
438 special_test_suites = {
438 special_test_suites = {
439 'autoreload': ['IPython.extensions.autoreload', 'IPython.extensions.tests.test_autoreload'],
439 'autoreload': ['IPython.extensions.autoreload', 'IPython.extensions.tests.test_autoreload'],
440 }
440 }
441
441
442 def make_runners(inc_slow=False):
442 def make_runners(inc_slow=False):
443 """Define the top-level packages that need to be tested.
443 """Define the top-level packages that need to be tested.
444 """
444 """
445
445
446 # Packages to be tested via nose, that only depend on the stdlib
446 # Packages to be tested via nose, that only depend on the stdlib
447 nose_pkg_names = ['config', 'core', 'extensions', 'lib', 'terminal',
447 nose_pkg_names = ['config', 'core', 'extensions', 'lib', 'terminal',
448 'testing', 'utils', 'nbformat']
448 'testing', 'utils', 'nbformat']
449
449
450 if have['qt']:
450 if have['qt']:
451 nose_pkg_names.append('qt')
451 nose_pkg_names.append('qt')
452
452
453 if have['tornado']:
453 if have['tornado']:
454 nose_pkg_names.append('html')
454 nose_pkg_names.append('html')
455
455
456 if have['zmq']:
456 if have['zmq']:
457 nose_pkg_names.append('kernel')
457 nose_pkg_names.append('kernel')
458 nose_pkg_names.append('kernel.inprocess')
458 nose_pkg_names.append('kernel.inprocess')
459 if inc_slow:
459 if inc_slow:
460 nose_pkg_names.append('parallel')
460 nose_pkg_names.append('parallel')
461
461
462 if all((have['pygments'], have['jinja2'], have['markdown'], have['sphinx'])):
462 if all((have['pygments'], have['jinja2'], have['markdown'], have['sphinx'])):
463 nose_pkg_names.append('nbconvert')
463 nose_pkg_names.append('nbconvert')
464
464
465 # For debugging this code, only load quick stuff
465 # For debugging this code, only load quick stuff
466 #nose_pkg_names = ['core', 'extensions'] # dbg
466 #nose_pkg_names = ['core', 'extensions'] # dbg
467
467
468 # Make fully qualified package names prepending 'IPython.' to our name lists
468 # Make fully qualified package names prepending 'IPython.' to our name lists
469 nose_packages = ['IPython.%s' % m for m in nose_pkg_names ]
469 nose_packages = ['IPython.%s' % m for m in nose_pkg_names ]
470
470
471 # Make runners
471 # Make runners
472 runners = [ (v, IPTester('iptest', params=v)) for v in nose_packages ]
472 runners = [ (v, IPTester('iptest', params=v)) for v in nose_packages ]
473
473
474 for name in special_test_suites:
474 for name in special_test_suites:
475 runners.append((name, IPTester('iptest', params=name)))
475 runners.append((name, IPTester('iptest', params=name)))
476
476
477 return runners
477 return runners
478
478
479
479
480 def run_iptest():
480 def run_iptest():
481 """Run the IPython test suite using nose.
481 """Run the IPython test suite using nose.
482
482
483 This function is called when this script is **not** called with the form
483 This function is called when this script is **not** called with the form
484 `iptest all`. It simply calls nose with appropriate command line flags
484 `iptest all`. It simply calls nose with appropriate command line flags
485 and accepts all of the standard nose arguments.
485 and accepts all of the standard nose arguments.
486 """
486 """
487 # Apply our monkeypatch to Xunit
487 # Apply our monkeypatch to Xunit
488 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
488 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
489 monkeypatch_xunit()
489 monkeypatch_xunit()
490
490
491 warnings.filterwarnings('ignore',
491 warnings.filterwarnings('ignore',
492 'This will be removed soon. Use IPython.testing.util instead')
492 'This will be removed soon. Use IPython.testing.util instead')
493
493
494 if sys.argv[1] in special_test_suites:
494 if sys.argv[1] in special_test_suites:
495 sys.argv[1:2] = special_test_suites[sys.argv[1]]
495 sys.argv[1:2] = special_test_suites[sys.argv[1]]
496 special_suite = True
496 special_suite = True
497 else:
497 else:
498 special_suite = False
498 special_suite = False
499
499
500 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
500 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
501
501
502 '--with-ipdoctest',
502 '--with-ipdoctest',
503 '--ipdoctest-tests','--ipdoctest-extension=txt',
503 '--ipdoctest-tests','--ipdoctest-extension=txt',
504
504
505 # We add --exe because of setuptools' imbecility (it
505 # We add --exe because of setuptools' imbecility (it
506 # blindly does chmod +x on ALL files). Nose does the
506 # blindly does chmod +x on ALL files). Nose does the
507 # right thing and it tries to avoid executables,
507 # right thing and it tries to avoid executables,
508 # setuptools unfortunately forces our hand here. This
508 # setuptools unfortunately forces our hand here. This
509 # has been discussed on the distutils list and the
509 # has been discussed on the distutils list and the
510 # setuptools devs refuse to fix this problem!
510 # setuptools devs refuse to fix this problem!
511 '--exe',
511 '--exe',
512 ]
512 ]
513 if '-a' not in argv and '-A' not in argv:
513 if '-a' not in argv and '-A' not in argv:
514 argv = argv + ['-a', '!crash']
514 argv = argv + ['-a', '!crash']
515
515
516 if nose.__version__ >= '0.11':
516 if nose.__version__ >= '0.11':
517 # I don't fully understand why we need this one, but depending on what
517 # I don't fully understand why we need this one, but depending on what
518 # directory the test suite is run from, if we don't give it, 0 tests
518 # directory the test suite is run from, if we don't give it, 0 tests
519 # get run. Specifically, if the test suite is run from the source dir
519 # get run. Specifically, if the test suite is run from the source dir
520 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
520 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
521 # even if the same call done in this directory works fine). It appears
521 # even if the same call done in this directory works fine). It appears
522 # that if the requested package is in the current dir, nose bails early
522 # that if the requested package is in the current dir, nose bails early
523 # by default. Since it's otherwise harmless, leave it in by default
523 # by default. Since it's otherwise harmless, leave it in by default
524 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
524 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
525 argv.append('--traverse-namespace')
525 argv.append('--traverse-namespace')
526
526
527 # use our plugin for doctesting. It will remove the standard doctest plugin
527 # use our plugin for doctesting. It will remove the standard doctest plugin
528 # if it finds it enabled
528 # if it finds it enabled
529 ipdt = IPythonDoctest() if special_suite else IPythonDoctest(make_exclude())
529 ipdt = IPythonDoctest() if special_suite else IPythonDoctest(make_exclude())
530 plugins = [ipdt, KnownFailure()]
530 plugins = [ipdt, KnownFailure()]
531
531
532 # We need a global ipython running in this process, but the special
532 # We need a global ipython running in this process, but the special
533 # in-process group spawns its own IPython kernels, so for *that* group we
533 # in-process group spawns its own IPython kernels, so for *that* group we
534 # must avoid also opening the global one (otherwise there's a conflict of
534 # must avoid also opening the global one (otherwise there's a conflict of
535 # singletons). Ultimately the solution to this problem is to refactor our
535 # singletons). Ultimately the solution to this problem is to refactor our
536 # assumptions about what needs to be a singleton and what doesn't (app
536 # assumptions about what needs to be a singleton and what doesn't (app
537 # objects should, individual shells shouldn't). But for now, this
537 # objects should, individual shells shouldn't). But for now, this
538 # workaround allows the test suite for the inprocess module to complete.
538 # workaround allows the test suite for the inprocess module to complete.
539 if not 'IPython.kernel.inprocess' in sys.argv:
539 if not 'IPython.kernel.inprocess' in sys.argv:
540 globalipapp.start_ipython()
540 globalipapp.start_ipython()
541
541
542 # Now nose can run
542 # Now nose can run
543 TestProgram(argv=argv, addplugins=plugins)
543 TestProgram(argv=argv, addplugins=plugins)
544
544
545
545
546 def run_iptestall(inc_slow=False):
546 def run_iptestall(inc_slow=False):
547 """Run the entire IPython test suite by calling nose and trial.
547 """Run the entire IPython test suite by calling nose and trial.
548
548
549 This function constructs :class:`IPTester` instances for all IPython
549 This function constructs :class:`IPTester` instances for all IPython
550 modules and package and then runs each of them. This causes the modules
550 modules and package and then runs each of them. This causes the modules
551 and packages of IPython to be tested each in their own subprocess using
551 and packages of IPython to be tested each in their own subprocess using
552 nose.
552 nose.
553
553
554 Parameters
554 Parameters
555 ----------
555 ----------
556
556
557 inc_slow : bool, optional
557 inc_slow : bool, optional
558 Include slow tests, like IPython.parallel. By default, these tests aren't
558 Include slow tests, like IPython.parallel. By default, these tests aren't
559 run.
559 run.
560 """
560 """
561
561
562 runners = make_runners(inc_slow=inc_slow)
562 runners = make_runners(inc_slow=inc_slow)
563
563
564 # Run the test runners in a temporary dir so we can nuke it when finished
564 # Run the test runners in a temporary dir so we can nuke it when finished
565 # to clean up any junk files left over by accident. This also makes it
565 # to clean up any junk files left over by accident. This also makes it
566 # robust against being run in non-writeable directories by mistake, as the
566 # robust against being run in non-writeable directories by mistake, as the
567 # temp dir will always be user-writeable.
567 # temp dir will always be user-writeable.
568 curdir = os.getcwdu()
568 curdir = os.getcwdu()
569 testdir = tempfile.gettempdir()
569 testdir = tempfile.gettempdir()
570 os.chdir(testdir)
570 os.chdir(testdir)
571
571
572 # Run all test runners, tracking execution time
572 # Run all test runners, tracking execution time
573 failed = []
573 failed = []
574 t_start = time.time()
574 t_start = time.time()
575 try:
575 try:
576 for (name, runner) in runners:
576 for (name, runner) in runners:
577 print('*'*70)
577 print('*'*70)
578 print('IPython test group:',name)
578 print('IPython test group:',name)
579 res = runner.run()
579 res = runner.run()
580 if res:
580 if res:
581 failed.append( (name, runner) )
581 failed.append( (name, runner) )
582 if res == -signal.SIGINT:
582 if res == -signal.SIGINT:
583 print("Interrupted")
583 print("Interrupted")
584 break
584 break
585 finally:
585 finally:
586 os.chdir(curdir)
586 os.chdir(curdir)
587 t_end = time.time()
587 t_end = time.time()
588 t_tests = t_end - t_start
588 t_tests = t_end - t_start
589 nrunners = len(runners)
589 nrunners = len(runners)
590 nfail = len(failed)
590 nfail = len(failed)
591 # summarize results
591 # summarize results
592 print()
592 print()
593 print('*'*70)
593 print('*'*70)
594 print('Test suite completed for system with the following information:')
594 print('Test suite completed for system with the following information:')
595 print(report())
595 print(report())
596 print('Ran %s test groups in %.3fs' % (nrunners, t_tests))
596 print('Ran %s test groups in %.3fs' % (nrunners, t_tests))
597 print()
597 print()
598 print('Status:')
598 print('Status:')
599 if not failed:
599 if not failed:
600 print('OK')
600 print('OK')
601 else:
601 else:
602 # If anything went wrong, point out what command to rerun manually to
602 # If anything went wrong, point out what command to rerun manually to
603 # see the actual errors and individual summary
603 # see the actual errors and individual summary
604 print('ERROR - %s out of %s test groups failed.' % (nfail, nrunners))
604 print('ERROR - %s out of %s test groups failed.' % (nfail, nrunners))
605 for name, failed_runner in failed:
605 for name, failed_runner in failed:
606 print('-'*40)
606 print('-'*40)
607 print('Runner failed:',name)
607 print('Runner failed:',name)
608 print('You may wish to rerun this one individually, with:')
608 print('You may wish to rerun this one individually, with:')
609 failed_call_args = [py3compat.cast_unicode(x) for x in failed_runner.call_args]
609 failed_call_args = [py3compat.cast_unicode(x) for x in failed_runner.call_args]
610 print(u' '.join(failed_call_args))
610 print(u' '.join(failed_call_args))
611 print()
611 print()
612 # Ensure that our exit code indicates failure
612 # Ensure that our exit code indicates failure
613 sys.exit(1)
613 sys.exit(1)
614
614
615
615
616 def main():
616 def main():
617 for arg in sys.argv[1:]:
617 for arg in sys.argv[1:]:
618 if arg.startswith('IPython') or arg in special_test_suites:
618 if arg.startswith('IPython') or arg in special_test_suites:
619 # This is in-process
619 # This is in-process
620 run_iptest()
620 run_iptest()
621 else:
621 else:
622 if "--all" in sys.argv:
622 if "--all" in sys.argv:
623 sys.argv.remove("--all")
623 sys.argv.remove("--all")
624 inc_slow = True
624 inc_slow = True
625 else:
625 else:
626 inc_slow = False
626 inc_slow = False
627 # This starts subprocesses
627 # This starts subprocesses
628 run_iptestall(inc_slow=inc_slow)
628 run_iptestall(inc_slow=inc_slow)
629
629
630
630
631 if __name__ == '__main__':
631 if __name__ == '__main__':
632 main()
632 main()
@@ -1,188 +1,187 b''
1 """Tests for the decorators we've created for IPython.
1 """Tests for the decorators we've created for IPython.
2 """
2 """
3
3
4 # Module imports
4 # Module imports
5 # Std lib
5 # Std lib
6 import inspect
6 import inspect
7 import sys
7 import sys
8 import unittest
9
8
10 # Third party
9 # Third party
11 import nose.tools as nt
10 import nose.tools as nt
12
11
13 # Our own
12 # Our own
14 from IPython.testing import decorators as dec
13 from IPython.testing import decorators as dec
15 from IPython.testing.skipdoctest import skip_doctest
14 from IPython.testing.skipdoctest import skip_doctest
16 from IPython.testing.ipunittest import ParametricTestCase
15 from IPython.testing.ipunittest import ParametricTestCase
17
16
18 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
19 # Utilities
18 # Utilities
20
19
21 # Note: copied from OInspect, kept here so the testing stuff doesn't create
20 # Note: copied from OInspect, kept here so the testing stuff doesn't create
22 # circular dependencies and is easier to reuse.
21 # circular dependencies and is easier to reuse.
23 def getargspec(obj):
22 def getargspec(obj):
24 """Get the names and default values of a function's arguments.
23 """Get the names and default values of a function's arguments.
25
24
26 A tuple of four things is returned: (args, varargs, varkw, defaults).
25 A tuple of four things is returned: (args, varargs, varkw, defaults).
27 'args' is a list of the argument names (it may contain nested lists).
26 'args' is a list of the argument names (it may contain nested lists).
28 'varargs' and 'varkw' are the names of the * and ** arguments or None.
27 'varargs' and 'varkw' are the names of the * and ** arguments or None.
29 'defaults' is an n-tuple of the default values of the last n arguments.
28 'defaults' is an n-tuple of the default values of the last n arguments.
30
29
31 Modified version of inspect.getargspec from the Python Standard
30 Modified version of inspect.getargspec from the Python Standard
32 Library."""
31 Library."""
33
32
34 if inspect.isfunction(obj):
33 if inspect.isfunction(obj):
35 func_obj = obj
34 func_obj = obj
36 elif inspect.ismethod(obj):
35 elif inspect.ismethod(obj):
37 func_obj = obj.im_func
36 func_obj = obj.im_func
38 else:
37 else:
39 raise TypeError('arg is not a Python function')
38 raise TypeError('arg is not a Python function')
40 args, varargs, varkw = inspect.getargs(func_obj.func_code)
39 args, varargs, varkw = inspect.getargs(func_obj.func_code)
41 return args, varargs, varkw, func_obj.func_defaults
40 return args, varargs, varkw, func_obj.func_defaults
42
41
43 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
44 # Testing functions
43 # Testing functions
45
44
46 @dec.as_unittest
45 @dec.as_unittest
47 def trivial():
46 def trivial():
48 """A trivial test"""
47 """A trivial test"""
49 pass
48 pass
50
49
51 # Some examples of parametric tests.
50 # Some examples of parametric tests.
52
51
53 def is_smaller(i,j):
52 def is_smaller(i,j):
54 assert i<j,"%s !< %s" % (i,j)
53 assert i<j,"%s !< %s" % (i,j)
55
54
56 class Tester(ParametricTestCase):
55 class Tester(ParametricTestCase):
57
56
58 def test_parametric(self):
57 def test_parametric(self):
59 yield is_smaller(3, 4)
58 yield is_smaller(3, 4)
60 x, y = 1, 2
59 x, y = 1, 2
61 yield is_smaller(x, y)
60 yield is_smaller(x, y)
62
61
63 @dec.parametric
62 @dec.parametric
64 def test_par_standalone():
63 def test_par_standalone():
65 yield is_smaller(3, 4)
64 yield is_smaller(3, 4)
66 x, y = 1, 2
65 x, y = 1, 2
67 yield is_smaller(x, y)
66 yield is_smaller(x, y)
68
67
69
68
70 @dec.skip
69 @dec.skip
71 def test_deliberately_broken():
70 def test_deliberately_broken():
72 """A deliberately broken test - we want to skip this one."""
71 """A deliberately broken test - we want to skip this one."""
73 1/0
72 1/0
74
73
75 @dec.skip('Testing the skip decorator')
74 @dec.skip('Testing the skip decorator')
76 def test_deliberately_broken2():
75 def test_deliberately_broken2():
77 """Another deliberately broken test - we want to skip this one."""
76 """Another deliberately broken test - we want to skip this one."""
78 1/0
77 1/0
79
78
80
79
81 # Verify that we can correctly skip the doctest for a function at will, but
80 # Verify that we can correctly skip the doctest for a function at will, but
82 # that the docstring itself is NOT destroyed by the decorator.
81 # that the docstring itself is NOT destroyed by the decorator.
83 @skip_doctest
82 @skip_doctest
84 def doctest_bad(x,y=1,**k):
83 def doctest_bad(x,y=1,**k):
85 """A function whose doctest we need to skip.
84 """A function whose doctest we need to skip.
86
85
87 >>> 1+1
86 >>> 1+1
88 3
87 3
89 """
88 """
90 print 'x:',x
89 print 'x:',x
91 print 'y:',y
90 print 'y:',y
92 print 'k:',k
91 print 'k:',k
93
92
94
93
95 def call_doctest_bad():
94 def call_doctest_bad():
96 """Check that we can still call the decorated functions.
95 """Check that we can still call the decorated functions.
97
96
98 >>> doctest_bad(3,y=4)
97 >>> doctest_bad(3,y=4)
99 x: 3
98 x: 3
100 y: 4
99 y: 4
101 k: {}
100 k: {}
102 """
101 """
103 pass
102 pass
104
103
105
104
106 def test_skip_dt_decorator():
105 def test_skip_dt_decorator():
107 """Doctest-skipping decorator should preserve the docstring.
106 """Doctest-skipping decorator should preserve the docstring.
108 """
107 """
109 # Careful: 'check' must be a *verbatim* copy of the doctest_bad docstring!
108 # Careful: 'check' must be a *verbatim* copy of the doctest_bad docstring!
110 check = """A function whose doctest we need to skip.
109 check = """A function whose doctest we need to skip.
111
110
112 >>> 1+1
111 >>> 1+1
113 3
112 3
114 """
113 """
115 # Fetch the docstring from doctest_bad after decoration.
114 # Fetch the docstring from doctest_bad after decoration.
116 val = doctest_bad.__doc__
115 val = doctest_bad.__doc__
117
116
118 nt.assert_equal(check,val,"doctest_bad docstrings don't match")
117 nt.assert_equal(check,val,"doctest_bad docstrings don't match")
119
118
120
119
121 # Doctest skipping should work for class methods too
120 # Doctest skipping should work for class methods too
122 class FooClass(object):
121 class FooClass(object):
123 """FooClass
122 """FooClass
124
123
125 Example:
124 Example:
126
125
127 >>> 1+1
126 >>> 1+1
128 2
127 2
129 """
128 """
130
129
131 @skip_doctest
130 @skip_doctest
132 def __init__(self,x):
131 def __init__(self,x):
133 """Make a FooClass.
132 """Make a FooClass.
134
133
135 Example:
134 Example:
136
135
137 >>> f = FooClass(3)
136 >>> f = FooClass(3)
138 junk
137 junk
139 """
138 """
140 print 'Making a FooClass.'
139 print 'Making a FooClass.'
141 self.x = x
140 self.x = x
142
141
143 @skip_doctest
142 @skip_doctest
144 def bar(self,y):
143 def bar(self,y):
145 """Example:
144 """Example:
146
145
147 >>> ff = FooClass(3)
146 >>> ff = FooClass(3)
148 >>> ff.bar(0)
147 >>> ff.bar(0)
149 boom!
148 boom!
150 >>> 1/0
149 >>> 1/0
151 bam!
150 bam!
152 """
151 """
153 return 1/y
152 return 1/y
154
153
155 def baz(self,y):
154 def baz(self,y):
156 """Example:
155 """Example:
157
156
158 >>> ff2 = FooClass(3)
157 >>> ff2 = FooClass(3)
159 Making a FooClass.
158 Making a FooClass.
160 >>> ff2.baz(3)
159 >>> ff2.baz(3)
161 True
160 True
162 """
161 """
163 return self.x==y
162 return self.x==y
164
163
165
164
166 def test_skip_dt_decorator2():
165 def test_skip_dt_decorator2():
167 """Doctest-skipping decorator should preserve function signature.
166 """Doctest-skipping decorator should preserve function signature.
168 """
167 """
169 # Hardcoded correct answer
168 # Hardcoded correct answer
170 dtargs = (['x', 'y'], None, 'k', (1,))
169 dtargs = (['x', 'y'], None, 'k', (1,))
171 # Introspect out the value
170 # Introspect out the value
172 dtargsr = getargspec(doctest_bad)
171 dtargsr = getargspec(doctest_bad)
173 assert dtargsr==dtargs, \
172 assert dtargsr==dtargs, \
174 "Incorrectly reconstructed args for doctest_bad: %s" % (dtargsr,)
173 "Incorrectly reconstructed args for doctest_bad: %s" % (dtargsr,)
175
174
176
175
177 @dec.skip_linux
176 @dec.skip_linux
178 def test_linux():
177 def test_linux():
179 nt.assert_false(sys.platform.startswith('linux'),"This test can't run under linux")
178 nt.assert_false(sys.platform.startswith('linux'),"This test can't run under linux")
180
179
181 @dec.skip_win32
180 @dec.skip_win32
182 def test_win32():
181 def test_win32():
183 nt.assert_not_equal(sys.platform,'win32',"This test can't run under windows")
182 nt.assert_not_equal(sys.platform,'win32',"This test can't run under windows")
184
183
185 @dec.skip_osx
184 @dec.skip_osx
186 def test_osx():
185 def test_osx():
187 nt.assert_not_equal(sys.platform,'darwin',"This test can't run under osx")
186 nt.assert_not_equal(sys.platform,'darwin',"This test can't run under osx")
188
187
@@ -1,136 +1,135 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Tests for testing.tools
3 Tests for testing.tools
4 """
4 """
5
5
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2011 The IPython Development Team
7 # Copyright (C) 2008-2011 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from __future__ import with_statement
16 from __future__ import with_statement
17
17
18 import os
18 import os
19 import sys
20 import unittest
19 import unittest
21
20
22 import nose.tools as nt
21 import nose.tools as nt
23
22
24 from IPython.testing import decorators as dec
23 from IPython.testing import decorators as dec
25 from IPython.testing import tools as tt
24 from IPython.testing import tools as tt
26
25
27 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
28 # Tests
27 # Tests
29 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
30
29
31 @dec.skip_win32
30 @dec.skip_win32
32 def test_full_path_posix():
31 def test_full_path_posix():
33 spath = '/foo/bar.py'
32 spath = '/foo/bar.py'
34 result = tt.full_path(spath,['a.txt','b.txt'])
33 result = tt.full_path(spath,['a.txt','b.txt'])
35 nt.assert_equal(result, ['/foo/a.txt', '/foo/b.txt'])
34 nt.assert_equal(result, ['/foo/a.txt', '/foo/b.txt'])
36 spath = '/foo'
35 spath = '/foo'
37 result = tt.full_path(spath,['a.txt','b.txt'])
36 result = tt.full_path(spath,['a.txt','b.txt'])
38 nt.assert_equal(result, ['/a.txt', '/b.txt'])
37 nt.assert_equal(result, ['/a.txt', '/b.txt'])
39 result = tt.full_path(spath,'a.txt')
38 result = tt.full_path(spath,'a.txt')
40 nt.assert_equal(result, ['/a.txt'])
39 nt.assert_equal(result, ['/a.txt'])
41
40
42
41
43 @dec.skip_if_not_win32
42 @dec.skip_if_not_win32
44 def test_full_path_win32():
43 def test_full_path_win32():
45 spath = 'c:\\foo\\bar.py'
44 spath = 'c:\\foo\\bar.py'
46 result = tt.full_path(spath,['a.txt','b.txt'])
45 result = tt.full_path(spath,['a.txt','b.txt'])
47 nt.assert_equal(result, ['c:\\foo\\a.txt', 'c:\\foo\\b.txt'])
46 nt.assert_equal(result, ['c:\\foo\\a.txt', 'c:\\foo\\b.txt'])
48 spath = 'c:\\foo'
47 spath = 'c:\\foo'
49 result = tt.full_path(spath,['a.txt','b.txt'])
48 result = tt.full_path(spath,['a.txt','b.txt'])
50 nt.assert_equal(result, ['c:\\a.txt', 'c:\\b.txt'])
49 nt.assert_equal(result, ['c:\\a.txt', 'c:\\b.txt'])
51 result = tt.full_path(spath,'a.txt')
50 result = tt.full_path(spath,'a.txt')
52 nt.assert_equal(result, ['c:\\a.txt'])
51 nt.assert_equal(result, ['c:\\a.txt'])
53
52
54
53
55 @dec.parametric
54 @dec.parametric
56 def test_parser():
55 def test_parser():
57 err = ("FAILED (errors=1)", 1, 0)
56 err = ("FAILED (errors=1)", 1, 0)
58 fail = ("FAILED (failures=1)", 0, 1)
57 fail = ("FAILED (failures=1)", 0, 1)
59 both = ("FAILED (errors=1, failures=1)", 1, 1)
58 both = ("FAILED (errors=1, failures=1)", 1, 1)
60 for txt, nerr, nfail in [err, fail, both]:
59 for txt, nerr, nfail in [err, fail, both]:
61 nerr1, nfail1 = tt.parse_test_output(txt)
60 nerr1, nfail1 = tt.parse_test_output(txt)
62 yield nt.assert_equal(nerr, nerr1)
61 yield nt.assert_equal(nerr, nerr1)
63 yield nt.assert_equal(nfail, nfail1)
62 yield nt.assert_equal(nfail, nfail1)
64
63
65
64
66 @dec.parametric
65 @dec.parametric
67 def test_temp_pyfile():
66 def test_temp_pyfile():
68 src = 'pass\n'
67 src = 'pass\n'
69 fname, fh = tt.temp_pyfile(src)
68 fname, fh = tt.temp_pyfile(src)
70 yield nt.assert_true(os.path.isfile(fname))
69 yield nt.assert_true(os.path.isfile(fname))
71 fh.close()
70 fh.close()
72 with open(fname) as fh2:
71 with open(fname) as fh2:
73 src2 = fh2.read()
72 src2 = fh2.read()
74 yield nt.assert_equal(src2, src)
73 yield nt.assert_equal(src2, src)
75
74
76 class TestAssertPrints(unittest.TestCase):
75 class TestAssertPrints(unittest.TestCase):
77 def test_passing(self):
76 def test_passing(self):
78 with tt.AssertPrints("abc"):
77 with tt.AssertPrints("abc"):
79 print "abcd"
78 print "abcd"
80 print "def"
79 print "def"
81 print b"ghi"
80 print b"ghi"
82
81
83 def test_failing(self):
82 def test_failing(self):
84 def func():
83 def func():
85 with tt.AssertPrints("abc"):
84 with tt.AssertPrints("abc"):
86 print "acd"
85 print "acd"
87 print "def"
86 print "def"
88 print b"ghi"
87 print b"ghi"
89
88
90 self.assertRaises(AssertionError, func)
89 self.assertRaises(AssertionError, func)
91
90
92
91
93 class Test_ipexec_validate(unittest.TestCase, tt.TempFileMixin):
92 class Test_ipexec_validate(unittest.TestCase, tt.TempFileMixin):
94 def test_main_path(self):
93 def test_main_path(self):
95 """Test with only stdout results.
94 """Test with only stdout results.
96 """
95 """
97 self.mktmp("print('A')\n"
96 self.mktmp("print('A')\n"
98 "print('B')\n"
97 "print('B')\n"
99 )
98 )
100 out = "A\nB"
99 out = "A\nB"
101 tt.ipexec_validate(self.fname, out)
100 tt.ipexec_validate(self.fname, out)
102
101
103 def test_main_path2(self):
102 def test_main_path2(self):
104 """Test with only stdout results, expecting windows line endings.
103 """Test with only stdout results, expecting windows line endings.
105 """
104 """
106 self.mktmp("print('A')\n"
105 self.mktmp("print('A')\n"
107 "print('B')\n"
106 "print('B')\n"
108 )
107 )
109 out = "A\r\nB"
108 out = "A\r\nB"
110 tt.ipexec_validate(self.fname, out)
109 tt.ipexec_validate(self.fname, out)
111
110
112 def test_exception_path(self):
111 def test_exception_path(self):
113 """Test exception path in exception_validate.
112 """Test exception path in exception_validate.
114 """
113 """
115 self.mktmp("from __future__ import print_function\n"
114 self.mktmp("from __future__ import print_function\n"
116 "import sys\n"
115 "import sys\n"
117 "print('A')\n"
116 "print('A')\n"
118 "print('B')\n"
117 "print('B')\n"
119 "print('C', file=sys.stderr)\n"
118 "print('C', file=sys.stderr)\n"
120 "print('D', file=sys.stderr)\n"
119 "print('D', file=sys.stderr)\n"
121 )
120 )
122 out = "A\nB"
121 out = "A\nB"
123 tt.ipexec_validate(self.fname, expected_out=out, expected_err="C\nD")
122 tt.ipexec_validate(self.fname, expected_out=out, expected_err="C\nD")
124
123
125 def test_exception_path(self):
124 def test_exception_path2(self):
126 """Test exception path in exception_validate, expecting windows line endings.
125 """Test exception path in exception_validate, expecting windows line endings.
127 """
126 """
128 self.mktmp("from __future__ import print_function\n"
127 self.mktmp("from __future__ import print_function\n"
129 "import sys\n"
128 "import sys\n"
130 "print('A')\n"
129 "print('A')\n"
131 "print('B')\n"
130 "print('B')\n"
132 "print('C', file=sys.stderr)\n"
131 "print('C', file=sys.stderr)\n"
133 "print('D', file=sys.stderr)\n"
132 "print('D', file=sys.stderr)\n"
134 )
133 )
135 out = "A\r\nB"
134 out = "A\r\nB"
136 tt.ipexec_validate(self.fname, expected_out=out, expected_err="C\r\nD")
135 tt.ipexec_validate(self.fname, expected_out=out, expected_err="C\r\nD")
General Comments 0
You need to be logged in to leave comments. Login now