##// END OF EJS Templates
Merge pull request #4259 from takluyver/win-test-exclusions...
Thomas Kluyver -
r12765:1bf5b7e4 merge
parent child Browse files
Show More
@@ -1,381 +1,381 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 - An @as_unittest decorator can be used to tag any normal parameter-less
19 - An @as_unittest decorator can be used to tag any normal parameter-less
20 function as a unittest TestCase. Then, both nose and normal unittest will
20 function as a unittest TestCase. Then, both nose and normal unittest will
21 recognize it as such. This will make it easier to migrate away from Nose if
21 recognize it as such. This will make it easier to migrate away from Nose if
22 we ever need/want to while maintaining very lightweight tests.
22 we ever need/want to while maintaining very lightweight tests.
23
23
24 NOTE: This file contains IPython-specific decorators. Using the machinery in
24 NOTE: This file contains IPython-specific decorators. Using the machinery in
25 IPython.external.decorators, we import either numpy.testing.decorators if numpy is
25 IPython.external.decorators, we import either numpy.testing.decorators if numpy is
26 available, OR use equivalent code in IPython.external._decorators, which
26 available, OR use equivalent code in IPython.external._decorators, which
27 we've copied verbatim from numpy.
27 we've copied verbatim from numpy.
28
28
29 Authors
29 Authors
30 -------
30 -------
31
31
32 - Fernando Perez <Fernando.Perez@berkeley.edu>
32 - Fernando Perez <Fernando.Perez@berkeley.edu>
33 """
33 """
34
34
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36 # Copyright (C) 2009-2011 The IPython Development Team
36 # Copyright (C) 2009-2011 The IPython Development Team
37 #
37 #
38 # Distributed under the terms of the BSD License. The full license is in
38 # Distributed under the terms of the BSD License. The full license is in
39 # the file COPYING, distributed as part of this software.
39 # the file COPYING, distributed as part of this software.
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41
41
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43 # Imports
43 # Imports
44 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45
45
46 # Stdlib imports
46 # Stdlib imports
47 import sys
47 import sys
48 import os
48 import os
49 import tempfile
49 import tempfile
50 import unittest
50 import unittest
51
51
52 # Third-party imports
52 # Third-party imports
53
53
54 # This is Michele Simionato's decorator module, kept verbatim.
54 # This is Michele Simionato's decorator module, kept verbatim.
55 from IPython.external.decorator import decorator
55 from IPython.external.decorator import decorator
56
56
57 # Expose the unittest-driven decorators
57 # Expose the unittest-driven decorators
58 from ipunittest import ipdoctest, ipdocstring
58 from ipunittest import ipdoctest, ipdocstring
59
59
60 # Grab the numpy-specific decorators which we keep in a file that we
60 # Grab the numpy-specific decorators which we keep in a file that we
61 # occasionally update from upstream: decorators.py is a copy of
61 # occasionally update from upstream: decorators.py is a copy of
62 # numpy.testing.decorators, we expose all of it here.
62 # numpy.testing.decorators, we expose all of it here.
63 from IPython.external.decorators import *
63 from IPython.external.decorators import *
64
64
65 # For onlyif_cmd_exists decorator
65 # For onlyif_cmd_exists decorator
66 from IPython.utils.process import is_cmd_found
66 from IPython.utils.process import is_cmd_found
67
67
68 #-----------------------------------------------------------------------------
68 #-----------------------------------------------------------------------------
69 # Classes and functions
69 # Classes and functions
70 #-----------------------------------------------------------------------------
70 #-----------------------------------------------------------------------------
71
71
72 # Simple example of the basic idea
72 # Simple example of the basic idea
73 def as_unittest(func):
73 def as_unittest(func):
74 """Decorator to make a simple function into a normal test via unittest."""
74 """Decorator to make a simple function into a normal test via unittest."""
75 class Tester(unittest.TestCase):
75 class Tester(unittest.TestCase):
76 def test(self):
76 def test(self):
77 func()
77 func()
78
78
79 Tester.__name__ = func.__name__
79 Tester.__name__ = func.__name__
80
80
81 return Tester
81 return Tester
82
82
83 # Utility functions
83 # Utility functions
84
84
85 def apply_wrapper(wrapper,func):
85 def apply_wrapper(wrapper,func):
86 """Apply a wrapper to a function for decoration.
86 """Apply a wrapper to a function for decoration.
87
87
88 This mixes Michele Simionato's decorator tool with nose's make_decorator,
88 This mixes Michele Simionato's decorator tool with nose's make_decorator,
89 to apply a wrapper in a decorator so that all nose attributes, as well as
89 to apply a wrapper in a decorator so that all nose attributes, as well as
90 function signature and other properties, survive the decoration cleanly.
90 function signature and other properties, survive the decoration cleanly.
91 This will ensure that wrapped functions can still be well introspected via
91 This will ensure that wrapped functions can still be well introspected via
92 IPython, for example.
92 IPython, for example.
93 """
93 """
94 import nose.tools
94 import nose.tools
95
95
96 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
96 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
97
97
98
98
99 def make_label_dec(label,ds=None):
99 def make_label_dec(label,ds=None):
100 """Factory function to create a decorator that applies one or more labels.
100 """Factory function to create a decorator that applies one or more labels.
101
101
102 Parameters
102 Parameters
103 ----------
103 ----------
104 label : string or sequence
104 label : string or sequence
105 One or more labels that will be applied by the decorator to the functions
105 One or more labels that will be applied by the decorator to the functions
106 it decorates. Labels are attributes of the decorated function with their
106 it decorates. Labels are attributes of the decorated function with their
107 value set to True.
107 value set to True.
108
108
109 ds : string
109 ds : string
110 An optional docstring for the resulting decorator. If not given, a
110 An optional docstring for the resulting decorator. If not given, a
111 default docstring is auto-generated.
111 default docstring is auto-generated.
112
112
113 Returns
113 Returns
114 -------
114 -------
115 A decorator.
115 A decorator.
116
116
117 Examples
117 Examples
118 --------
118 --------
119
119
120 A simple labeling decorator:
120 A simple labeling decorator:
121
121
122 >>> slow = make_label_dec('slow')
122 >>> slow = make_label_dec('slow')
123 >>> slow.__doc__
123 >>> slow.__doc__
124 "Labels a test as 'slow'."
124 "Labels a test as 'slow'."
125
125
126 And one that uses multiple labels and a custom docstring:
126 And one that uses multiple labels and a custom docstring:
127
127
128 >>> rare = make_label_dec(['slow','hard'],
128 >>> rare = make_label_dec(['slow','hard'],
129 ... "Mix labels 'slow' and 'hard' for rare tests.")
129 ... "Mix labels 'slow' and 'hard' for rare tests.")
130 >>> rare.__doc__
130 >>> rare.__doc__
131 "Mix labels 'slow' and 'hard' for rare tests."
131 "Mix labels 'slow' and 'hard' for rare tests."
132
132
133 Now, let's test using this one:
133 Now, let's test using this one:
134 >>> @rare
134 >>> @rare
135 ... def f(): pass
135 ... def f(): pass
136 ...
136 ...
137 >>>
137 >>>
138 >>> f.slow
138 >>> f.slow
139 True
139 True
140 >>> f.hard
140 >>> f.hard
141 True
141 True
142 """
142 """
143
143
144 if isinstance(label,basestring):
144 if isinstance(label,basestring):
145 labels = [label]
145 labels = [label]
146 else:
146 else:
147 labels = label
147 labels = label
148
148
149 # Validate that the given label(s) are OK for use in setattr() by doing a
149 # Validate that the given label(s) are OK for use in setattr() by doing a
150 # dry run on a dummy function.
150 # dry run on a dummy function.
151 tmp = lambda : None
151 tmp = lambda : None
152 for label in labels:
152 for label in labels:
153 setattr(tmp,label,True)
153 setattr(tmp,label,True)
154
154
155 # This is the actual decorator we'll return
155 # This is the actual decorator we'll return
156 def decor(f):
156 def decor(f):
157 for label in labels:
157 for label in labels:
158 setattr(f,label,True)
158 setattr(f,label,True)
159 return f
159 return f
160
160
161 # Apply the user's docstring, or autogenerate a basic one
161 # Apply the user's docstring, or autogenerate a basic one
162 if ds is None:
162 if ds is None:
163 ds = "Labels a test as %r." % label
163 ds = "Labels a test as %r." % label
164 decor.__doc__ = ds
164 decor.__doc__ = ds
165
165
166 return decor
166 return decor
167
167
168
168
169 # Inspired by numpy's skipif, but uses the full apply_wrapper utility to
169 # Inspired by numpy's skipif, but uses the full apply_wrapper utility to
170 # preserve function metadata better and allows the skip condition to be a
170 # preserve function metadata better and allows the skip condition to be a
171 # callable.
171 # callable.
172 def skipif(skip_condition, msg=None):
172 def skipif(skip_condition, msg=None):
173 ''' Make function raise SkipTest exception if skip_condition is true
173 ''' Make function raise SkipTest exception if skip_condition is true
174
174
175 Parameters
175 Parameters
176 ----------
176 ----------
177 skip_condition : bool or callable.
177 skip_condition : bool or callable.
178 Flag to determine whether to skip test. If the condition is a
178 Flag to determine whether to skip test. If the condition is a
179 callable, it is used at runtime to dynamically make the decision. This
179 callable, it is used at runtime to dynamically make the decision. This
180 is useful for tests that may require costly imports, to delay the cost
180 is useful for tests that may require costly imports, to delay the cost
181 until the test suite is actually executed.
181 until the test suite is actually executed.
182 msg : string
182 msg : string
183 Message to give on raising a SkipTest exception
183 Message to give on raising a SkipTest exception
184
184
185 Returns
185 Returns
186 -------
186 -------
187 decorator : function
187 decorator : function
188 Decorator, which, when applied to a function, causes SkipTest
188 Decorator, which, when applied to a function, causes SkipTest
189 to be raised when the skip_condition was True, and the function
189 to be raised when the skip_condition was True, and the function
190 to be called normally otherwise.
190 to be called normally otherwise.
191
191
192 Notes
192 Notes
193 -----
193 -----
194 You will see from the code that we had to further decorate the
194 You will see from the code that we had to further decorate the
195 decorator with the nose.tools.make_decorator function in order to
195 decorator with the nose.tools.make_decorator function in order to
196 transmit function name, and various other metadata.
196 transmit function name, and various other metadata.
197 '''
197 '''
198
198
199 def skip_decorator(f):
199 def skip_decorator(f):
200 # Local import to avoid a hard nose dependency and only incur the
200 # Local import to avoid a hard nose dependency and only incur the
201 # import time overhead at actual test-time.
201 # import time overhead at actual test-time.
202 import nose
202 import nose
203
203
204 # Allow for both boolean or callable skip conditions.
204 # Allow for both boolean or callable skip conditions.
205 if callable(skip_condition):
205 if callable(skip_condition):
206 skip_val = skip_condition
206 skip_val = skip_condition
207 else:
207 else:
208 skip_val = lambda : skip_condition
208 skip_val = lambda : skip_condition
209
209
210 def get_msg(func,msg=None):
210 def get_msg(func,msg=None):
211 """Skip message with information about function being skipped."""
211 """Skip message with information about function being skipped."""
212 if msg is None: out = 'Test skipped due to test condition.'
212 if msg is None: out = 'Test skipped due to test condition.'
213 else: out = msg
213 else: out = msg
214 return "Skipping test: %s. %s" % (func.__name__,out)
214 return "Skipping test: %s. %s" % (func.__name__,out)
215
215
216 # We need to define *two* skippers because Python doesn't allow both
216 # We need to define *two* skippers because Python doesn't allow both
217 # return with value and yield inside the same function.
217 # return with value and yield inside the same function.
218 def skipper_func(*args, **kwargs):
218 def skipper_func(*args, **kwargs):
219 """Skipper for normal test functions."""
219 """Skipper for normal test functions."""
220 if skip_val():
220 if skip_val():
221 raise nose.SkipTest(get_msg(f,msg))
221 raise nose.SkipTest(get_msg(f,msg))
222 else:
222 else:
223 return f(*args, **kwargs)
223 return f(*args, **kwargs)
224
224
225 def skipper_gen(*args, **kwargs):
225 def skipper_gen(*args, **kwargs):
226 """Skipper for test generators."""
226 """Skipper for test generators."""
227 if skip_val():
227 if skip_val():
228 raise nose.SkipTest(get_msg(f,msg))
228 raise nose.SkipTest(get_msg(f,msg))
229 else:
229 else:
230 for x in f(*args, **kwargs):
230 for x in f(*args, **kwargs):
231 yield x
231 yield x
232
232
233 # Choose the right skipper to use when building the actual generator.
233 # Choose the right skipper to use when building the actual generator.
234 if nose.util.isgenerator(f):
234 if nose.util.isgenerator(f):
235 skipper = skipper_gen
235 skipper = skipper_gen
236 else:
236 else:
237 skipper = skipper_func
237 skipper = skipper_func
238
238
239 return nose.tools.make_decorator(f)(skipper)
239 return nose.tools.make_decorator(f)(skipper)
240
240
241 return skip_decorator
241 return skip_decorator
242
242
243 # A version with the condition set to true, common case just to attach a message
243 # A version with the condition set to true, common case just to attach a message
244 # to a skip decorator
244 # to a skip decorator
245 def skip(msg=None):
245 def skip(msg=None):
246 """Decorator factory - mark a test function for skipping from test suite.
246 """Decorator factory - mark a test function for skipping from test suite.
247
247
248 Parameters
248 Parameters
249 ----------
249 ----------
250 msg : string
250 msg : string
251 Optional message to be added.
251 Optional message to be added.
252
252
253 Returns
253 Returns
254 -------
254 -------
255 decorator : function
255 decorator : function
256 Decorator, which, when applied to a function, causes SkipTest
256 Decorator, which, when applied to a function, causes SkipTest
257 to be raised, with the optional message added.
257 to be raised, with the optional message added.
258 """
258 """
259
259
260 return skipif(True,msg)
260 return skipif(True,msg)
261
261
262
262
263 def onlyif(condition, msg):
263 def onlyif(condition, msg):
264 """The reverse from skipif, see skipif for details."""
264 """The reverse from skipif, see skipif for details."""
265
265
266 if callable(condition):
266 if callable(condition):
267 skip_condition = lambda : not condition()
267 skip_condition = lambda : not condition()
268 else:
268 else:
269 skip_condition = lambda : not condition
269 skip_condition = lambda : not condition
270
270
271 return skipif(skip_condition, msg)
271 return skipif(skip_condition, msg)
272
272
273 #-----------------------------------------------------------------------------
273 #-----------------------------------------------------------------------------
274 # Utility functions for decorators
274 # Utility functions for decorators
275 def module_not_available(module):
275 def module_not_available(module):
276 """Can module be imported? Returns true if module does NOT import.
276 """Can module be imported? Returns true if module does NOT import.
277
277
278 This is used to make a decorator to skip tests that require module to be
278 This is used to make a decorator to skip tests that require module to be
279 available, but delay the 'import numpy' to test execution time.
279 available, but delay the 'import numpy' to test execution time.
280 """
280 """
281 try:
281 try:
282 mod = __import__(module)
282 mod = __import__(module)
283 mod_not_avail = False
283 mod_not_avail = False
284 except ImportError:
284 except ImportError:
285 mod_not_avail = True
285 mod_not_avail = True
286
286
287 return mod_not_avail
287 return mod_not_avail
288
288
289
289
290 def decorated_dummy(dec, name):
290 def decorated_dummy(dec, name):
291 """Return a dummy function decorated with dec, with the given name.
291 """Return a dummy function decorated with dec, with the given name.
292
292
293 Examples
293 Examples
294 --------
294 --------
295 import IPython.testing.decorators as dec
295 import IPython.testing.decorators as dec
296 setup = dec.decorated_dummy(dec.skip_if_no_x11, __name__)
296 setup = dec.decorated_dummy(dec.skip_if_no_x11, __name__)
297 """
297 """
298 dummy = lambda: None
298 dummy = lambda: None
299 dummy.__name__ = name
299 dummy.__name__ = name
300 return dec(dummy)
300 return dec(dummy)
301
301
302 #-----------------------------------------------------------------------------
302 #-----------------------------------------------------------------------------
303 # Decorators for public use
303 # Decorators for public use
304
304
305 # Decorators to skip certain tests on specific platforms.
305 # Decorators to skip certain tests on specific platforms.
306 skip_win32 = skipif(sys.platform == 'win32',
306 skip_win32 = skipif(sys.platform == 'win32',
307 "This test does not run under Windows")
307 "This test does not run under Windows")
308 skip_linux = skipif(sys.platform.startswith('linux'),
308 skip_linux = skipif(sys.platform.startswith('linux'),
309 "This test does not run under Linux")
309 "This test does not run under Linux")
310 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
310 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
311
311
312
312
313 # Decorators to skip tests if not on specific platforms.
313 # Decorators to skip tests if not on specific platforms.
314 skip_if_not_win32 = skipif(sys.platform != 'win32',
314 skip_if_not_win32 = skipif(sys.platform != 'win32',
315 "This test only runs under Windows")
315 "This test only runs under Windows")
316 skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
316 skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
317 "This test only runs under Linux")
317 "This test only runs under Linux")
318 skip_if_not_osx = skipif(sys.platform != 'darwin',
318 skip_if_not_osx = skipif(sys.platform != 'darwin',
319 "This test only runs under OSX")
319 "This test only runs under OSX")
320
320
321
321
322 _x11_skip_cond = (sys.platform not in ('darwin', 'win32') and
322 _x11_skip_cond = (sys.platform not in ('darwin', 'win32') and
323 os.environ.get('DISPLAY', '') == '')
323 os.environ.get('DISPLAY', '') == '')
324 _x11_skip_msg = "Skipped under *nix when X11/XOrg not available"
324 _x11_skip_msg = "Skipped under *nix when X11/XOrg not available"
325
325
326 skip_if_no_x11 = skipif(_x11_skip_cond, _x11_skip_msg)
326 skip_if_no_x11 = skipif(_x11_skip_cond, _x11_skip_msg)
327
327
328 # not a decorator itself, returns a dummy function to be used as setup
328 # not a decorator itself, returns a dummy function to be used as setup
329 def skip_file_no_x11(name):
329 def skip_file_no_x11(name):
330 return decorated_dummy(skip_if_no_x11, name) if _x11_skip_cond else None
330 return decorated_dummy(skip_if_no_x11, name) if _x11_skip_cond else None
331
331
332 # Other skip decorators
332 # Other skip decorators
333
333
334 # generic skip without module
334 # generic skip without module
335 skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod)
335 skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod)
336
336
337 skipif_not_numpy = skip_without('numpy')
337 skipif_not_numpy = skip_without('numpy')
338
338
339 skipif_not_matplotlib = skip_without('matplotlib')
339 skipif_not_matplotlib = skip_without('matplotlib')
340
340
341 skipif_not_sympy = skip_without('sympy')
341 skipif_not_sympy = skip_without('sympy')
342
342
343 skip_known_failure = knownfailureif(True,'This test is known to fail')
343 skip_known_failure = knownfailureif(True,'This test is known to fail')
344
344
345 known_failure_py3 = knownfailureif(sys.version_info[0] >= 3,
345 known_failure_py3 = knownfailureif(sys.version_info[0] >= 3,
346 'This test is known to fail on Python 3.')
346 'This test is known to fail on Python 3.')
347
347
348 # A null 'decorator', useful to make more readable code that needs to pick
348 # A null 'decorator', useful to make more readable code that needs to pick
349 # between different decorators based on OS or other conditions
349 # between different decorators based on OS or other conditions
350 null_deco = lambda f: f
350 null_deco = lambda f: f
351
351
352 # Some tests only run where we can use unicode paths. Note that we can't just
352 # Some tests only run where we can use unicode paths. Note that we can't just
353 # check os.path.supports_unicode_filenames, which is always False on Linux.
353 # check os.path.supports_unicode_filenames, which is always False on Linux.
354 try:
354 try:
355 f = tempfile.NamedTemporaryFile(prefix=u"tmp€")
355 f = tempfile.NamedTemporaryFile(prefix=u"tmp€")
356 except UnicodeEncodeError:
356 except UnicodeEncodeError:
357 unicode_paths = False
357 unicode_paths = False
358 else:
358 else:
359 unicode_paths = True
359 unicode_paths = True
360 f.close()
360 f.close()
361
361
362 onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable "
362 onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable "
363 "where we can use unicode in filenames."))
363 "where we can use unicode in filenames."))
364
364
365
365
366 def onlyif_cmds_exist(*commands):
366 def onlyif_cmds_exist(*commands):
367 """
367 """
368 Decorator to skip test when at least one of `commands` is not found.
368 Decorator to skip test when at least one of `commands` is not found.
369 """
369 """
370 for cmd in commands:
370 for cmd in commands:
371 try:
371 try:
372 if not is_cmd_found(cmd):
372 if not is_cmd_found(cmd):
373 return skip("This test runs only if command '{0}' "
373 return skip("This test runs only if command '{0}' "
374 "is installed".format(cmd))
374 "is installed".format(cmd))
375 except ImportError as e:
375 except ImportError as e:
376 # is_cmd_found uses pywin32 on windows, which might not be available
376 # is_cmd_found uses pywin32 on windows, which might not be available
377 if sys.platform == 'win32' and 'pywin32' in e.message:
377 if sys.platform == 'win32' and 'pywin32' in str(e):
378 return skip("This test runs only if pywin32 and command '{0}' "
378 return skip("This test runs only if pywin32 and command '{0}' "
379 "is installed".format(cmd))
379 "is installed".format(cmd))
380 raise e
380 raise e
381 return null_deco
381 return null_deco
@@ -1,436 +1,432 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 re
33 import re
34 import sys
34 import sys
35 import warnings
35 import warnings
36
36
37 # Now, proceed to import nose itself
37 # Now, proceed to import nose itself
38 import nose.plugins.builtin
38 import nose.plugins.builtin
39 from nose.plugins.xunit import Xunit
39 from nose.plugins.xunit import Xunit
40 from nose import SkipTest
40 from nose import SkipTest
41 from nose.core import TestProgram
41 from nose.core import TestProgram
42 from nose.plugins import Plugin
42 from nose.plugins import Plugin
43
43
44 # Our own imports
44 # Our own imports
45 from IPython.utils.importstring import import_item
45 from IPython.utils.importstring import import_item
46 from IPython.testing.plugin.ipdoctest import IPythonDoctest
46 from IPython.testing.plugin.ipdoctest import IPythonDoctest
47 from IPython.external.decorators import KnownFailure, knownfailureif
47 from IPython.external.decorators import KnownFailure, knownfailureif
48
48
49 pjoin = path.join
49 pjoin = path.join
50
50
51
51
52 #-----------------------------------------------------------------------------
52 #-----------------------------------------------------------------------------
53 # Globals
53 # Globals
54 #-----------------------------------------------------------------------------
54 #-----------------------------------------------------------------------------
55
55
56
56
57 #-----------------------------------------------------------------------------
57 #-----------------------------------------------------------------------------
58 # Warnings control
58 # Warnings control
59 #-----------------------------------------------------------------------------
59 #-----------------------------------------------------------------------------
60
60
61 # Twisted generates annoying warnings with Python 2.6, as will do other code
61 # Twisted generates annoying warnings with Python 2.6, as will do other code
62 # that imports 'sets' as of today
62 # that imports 'sets' as of today
63 warnings.filterwarnings('ignore', 'the sets module is deprecated',
63 warnings.filterwarnings('ignore', 'the sets module is deprecated',
64 DeprecationWarning )
64 DeprecationWarning )
65
65
66 # This one also comes from Twisted
66 # This one also comes from Twisted
67 warnings.filterwarnings('ignore', 'the sha module is deprecated',
67 warnings.filterwarnings('ignore', 'the sha module is deprecated',
68 DeprecationWarning)
68 DeprecationWarning)
69
69
70 # Wx on Fedora11 spits these out
70 # Wx on Fedora11 spits these out
71 warnings.filterwarnings('ignore', 'wxPython/wxWidgets release number mismatch',
71 warnings.filterwarnings('ignore', 'wxPython/wxWidgets release number mismatch',
72 UserWarning)
72 UserWarning)
73
73
74 # ------------------------------------------------------------------------------
74 # ------------------------------------------------------------------------------
75 # Monkeypatch Xunit to count known failures as skipped.
75 # Monkeypatch Xunit to count known failures as skipped.
76 # ------------------------------------------------------------------------------
76 # ------------------------------------------------------------------------------
77 def monkeypatch_xunit():
77 def monkeypatch_xunit():
78 try:
78 try:
79 knownfailureif(True)(lambda: None)()
79 knownfailureif(True)(lambda: None)()
80 except Exception as e:
80 except Exception as e:
81 KnownFailureTest = type(e)
81 KnownFailureTest = type(e)
82
82
83 def addError(self, test, err, capt=None):
83 def addError(self, test, err, capt=None):
84 if issubclass(err[0], KnownFailureTest):
84 if issubclass(err[0], KnownFailureTest):
85 err = (SkipTest,) + err[1:]
85 err = (SkipTest,) + err[1:]
86 return self.orig_addError(test, err, capt)
86 return self.orig_addError(test, err, capt)
87
87
88 Xunit.orig_addError = Xunit.addError
88 Xunit.orig_addError = Xunit.addError
89 Xunit.addError = addError
89 Xunit.addError = addError
90
90
91 #-----------------------------------------------------------------------------
91 #-----------------------------------------------------------------------------
92 # Check which dependencies are installed and greater than minimum version.
92 # Check which dependencies are installed and greater than minimum version.
93 #-----------------------------------------------------------------------------
93 #-----------------------------------------------------------------------------
94 def extract_version(mod):
94 def extract_version(mod):
95 return mod.__version__
95 return mod.__version__
96
96
97 def test_for(item, min_version=None, callback=extract_version):
97 def test_for(item, min_version=None, callback=extract_version):
98 """Test to see if item is importable, and optionally check against a minimum
98 """Test to see if item is importable, and optionally check against a minimum
99 version.
99 version.
100
100
101 If min_version is given, the default behavior is to check against the
101 If min_version is given, the default behavior is to check against the
102 `__version__` attribute of the item, but specifying `callback` allows you to
102 `__version__` attribute of the item, but specifying `callback` allows you to
103 extract the value you are interested in. e.g::
103 extract the value you are interested in. e.g::
104
104
105 In [1]: import sys
105 In [1]: import sys
106
106
107 In [2]: from IPython.testing.iptest import test_for
107 In [2]: from IPython.testing.iptest import test_for
108
108
109 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
109 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
110 Out[3]: True
110 Out[3]: True
111
111
112 """
112 """
113 try:
113 try:
114 check = import_item(item)
114 check = import_item(item)
115 except (ImportError, RuntimeError):
115 except (ImportError, RuntimeError):
116 # GTK reports Runtime error if it can't be initialized even if it's
116 # GTK reports Runtime error if it can't be initialized even if it's
117 # importable.
117 # importable.
118 return False
118 return False
119 else:
119 else:
120 if min_version:
120 if min_version:
121 if callback:
121 if callback:
122 # extra processing step to get version to compare
122 # extra processing step to get version to compare
123 check = callback(check)
123 check = callback(check)
124
124
125 return check >= min_version
125 return check >= min_version
126 else:
126 else:
127 return True
127 return True
128
128
129 # Global dict where we can store information on what we have and what we don't
129 # Global dict where we can store information on what we have and what we don't
130 # have available at test run time
130 # have available at test run time
131 have = {}
131 have = {}
132
132
133 have['curses'] = test_for('_curses')
133 have['curses'] = test_for('_curses')
134 have['matplotlib'] = test_for('matplotlib')
134 have['matplotlib'] = test_for('matplotlib')
135 have['numpy'] = test_for('numpy')
135 have['numpy'] = test_for('numpy')
136 have['pexpect'] = test_for('IPython.external.pexpect')
136 have['pexpect'] = test_for('IPython.external.pexpect')
137 have['pymongo'] = test_for('pymongo')
137 have['pymongo'] = test_for('pymongo')
138 have['pygments'] = test_for('pygments')
138 have['pygments'] = test_for('pygments')
139 have['qt'] = test_for('IPython.external.qt')
139 have['qt'] = test_for('IPython.external.qt')
140 have['rpy2'] = test_for('rpy2')
140 have['rpy2'] = test_for('rpy2')
141 have['sqlite3'] = test_for('sqlite3')
141 have['sqlite3'] = test_for('sqlite3')
142 have['cython'] = test_for('Cython')
142 have['cython'] = test_for('Cython')
143 have['oct2py'] = test_for('oct2py')
143 have['oct2py'] = test_for('oct2py')
144 have['tornado'] = test_for('tornado.version_info', (2,1,0), callback=None)
144 have['tornado'] = test_for('tornado.version_info', (2,1,0), callback=None)
145 have['jinja2'] = test_for('jinja2')
145 have['jinja2'] = test_for('jinja2')
146 have['wx'] = test_for('wx')
146 have['wx'] = test_for('wx')
147 have['wx.aui'] = test_for('wx.aui')
147 have['wx.aui'] = test_for('wx.aui')
148 have['azure'] = test_for('azure')
148 have['azure'] = test_for('azure')
149 have['sphinx'] = test_for('sphinx')
149 have['sphinx'] = test_for('sphinx')
150
150
151 min_zmq = (2,1,11)
151 min_zmq = (2,1,11)
152
152
153 have['zmq'] = test_for('zmq.pyzmq_version_info', min_zmq, callback=lambda x: x())
153 have['zmq'] = test_for('zmq.pyzmq_version_info', min_zmq, callback=lambda x: x())
154
154
155 #-----------------------------------------------------------------------------
155 #-----------------------------------------------------------------------------
156 # Test suite definitions
156 # Test suite definitions
157 #-----------------------------------------------------------------------------
157 #-----------------------------------------------------------------------------
158
158
159 test_group_names = ['parallel', 'kernel', 'kernel.inprocess', 'config', 'core',
159 test_group_names = ['parallel', 'kernel', 'kernel.inprocess', 'config', 'core',
160 'extensions', 'lib', 'terminal', 'testing', 'utils',
160 'extensions', 'lib', 'terminal', 'testing', 'utils',
161 'nbformat', 'qt', 'html', 'nbconvert'
161 'nbformat', 'qt', 'html', 'nbconvert'
162 ]
162 ]
163
163
164 class TestSection(object):
164 class TestSection(object):
165 def __init__(self, name, includes):
165 def __init__(self, name, includes):
166 self.name = name
166 self.name = name
167 self.includes = includes
167 self.includes = includes
168 self.excludes = []
168 self.excludes = []
169 self.dependencies = []
169 self.dependencies = []
170 self.enabled = True
170 self.enabled = True
171
171
172 def exclude(self, module):
172 def exclude(self, module):
173 if not module.startswith('IPython'):
173 if not module.startswith('IPython'):
174 module = self.includes[0] + "." + module
174 module = self.includes[0] + "." + module
175 self.excludes.append(module.replace('.', os.sep))
175 self.excludes.append(module.replace('.', os.sep))
176
176
177 def requires(self, *packages):
177 def requires(self, *packages):
178 self.dependencies.extend(packages)
178 self.dependencies.extend(packages)
179
179
180 @property
180 @property
181 def will_run(self):
181 def will_run(self):
182 return self.enabled and all(have[p] for p in self.dependencies)
182 return self.enabled and all(have[p] for p in self.dependencies)
183
183
184 # Name -> (include, exclude, dependencies_met)
184 # Name -> (include, exclude, dependencies_met)
185 test_sections = {n:TestSection(n, ['IPython.%s' % n]) for n in test_group_names}
185 test_sections = {n:TestSection(n, ['IPython.%s' % n]) for n in test_group_names}
186
186
187 # Exclusions and dependencies
187 # Exclusions and dependencies
188 # ---------------------------
188 # ---------------------------
189
189
190 # core:
190 # core:
191 sec = test_sections['core']
191 sec = test_sections['core']
192 if not have['sqlite3']:
192 if not have['sqlite3']:
193 sec.exclude('tests.test_history')
193 sec.exclude('tests.test_history')
194 sec.exclude('history')
194 sec.exclude('history')
195 if not have['matplotlib']:
195 if not have['matplotlib']:
196 sec.exclude('pylabtools'),
196 sec.exclude('pylabtools'),
197 sec.exclude('tests.test_pylabtools')
197 sec.exclude('tests.test_pylabtools')
198
198
199 # lib:
199 # lib:
200 sec = test_sections['lib']
200 sec = test_sections['lib']
201 if not have['wx']:
201 if not have['wx']:
202 sec.exclude('inputhookwx')
202 sec.exclude('inputhookwx')
203 if not have['pexpect']:
203 if not have['pexpect']:
204 sec.exclude('irunner')
204 sec.exclude('irunner')
205 sec.exclude('tests.test_irunner')
205 sec.exclude('tests.test_irunner')
206 if not have['zmq']:
206 if not have['zmq']:
207 sec.exclude('kernel')
207 sec.exclude('kernel')
208 # We do this unconditionally, so that the test suite doesn't import
208 # We do this unconditionally, so that the test suite doesn't import
209 # gtk, changing the default encoding and masking some unicode bugs.
209 # gtk, changing the default encoding and masking some unicode bugs.
210 sec.exclude('inputhookgtk')
210 sec.exclude('inputhookgtk')
211 # Testing inputhook will need a lot of thought, to figure out
211 # Testing inputhook will need a lot of thought, to figure out
212 # how to have tests that don't lock up with the gui event
212 # how to have tests that don't lock up with the gui event
213 # loops in the picture
213 # loops in the picture
214 sec.exclude('inputhook')
214 sec.exclude('inputhook')
215
215
216 # testing:
216 # testing:
217 sec = test_sections['lib']
217 sec = test_sections['lib']
218 # This guy is probably attic material
218 # This guy is probably attic material
219 sec.exclude('mkdoctests')
219 sec.exclude('mkdoctests')
220 # These have to be skipped on win32 because the use echo, rm, cd, etc.
220 # These have to be skipped on win32 because the use echo, rm, cd, etc.
221 # See ticket https://github.com/ipython/ipython/issues/87
221 # See ticket https://github.com/ipython/ipython/issues/87
222 if sys.platform == 'win32':
222 if sys.platform == 'win32':
223 sec.exclude('plugin.test_exampleip')
223 sec.exclude('plugin.test_exampleip')
224 sec.exclude('plugin.dtexample')
224 sec.exclude('plugin.dtexample')
225
225
226 # terminal:
226 # terminal:
227 if (not have['pexpect']) or (not have['zmq']):
227 if (not have['pexpect']) or (not have['zmq']):
228 test_sections['terminal'].exclude('console')
228 test_sections['terminal'].exclude('console')
229
229
230 # parallel
230 # parallel
231 sec = test_sections['parallel']
231 sec = test_sections['parallel']
232 sec.requires('zmq')
232 sec.requires('zmq')
233 if not have['pymongo']:
233 if not have['pymongo']:
234 sec.exclude('controller.mongodb')
234 sec.exclude('controller.mongodb')
235 sec.exclude('tests.test_mongodb')
235 sec.exclude('tests.test_mongodb')
236
236
237 # kernel:
237 # kernel:
238 sec = test_sections['kernel']
238 sec = test_sections['kernel']
239 sec.requires('zmq')
239 sec.requires('zmq')
240 # The in-process kernel tests are done in a separate section
240 # The in-process kernel tests are done in a separate section
241 sec.exclude('inprocess')
241 sec.exclude('inprocess')
242 # importing gtk sets the default encoding, which we want to avoid
242 # importing gtk sets the default encoding, which we want to avoid
243 sec.exclude('zmq.gui.gtkembed')
243 sec.exclude('zmq.gui.gtkembed')
244 if not have['matplotlib']:
244 if not have['matplotlib']:
245 sec.exclude('zmq.pylab')
245 sec.exclude('zmq.pylab')
246
246
247 # kernel.inprocess:
247 # kernel.inprocess:
248 test_sections['kernel.inprocess'].requires('zmq')
248 test_sections['kernel.inprocess'].requires('zmq')
249
249
250 # extensions:
250 # extensions:
251 sec = test_sections['extensions']
251 sec = test_sections['extensions']
252 if not have['cython']:
252 if not have['cython']:
253 sec.exclude('cythonmagic')
253 sec.exclude('cythonmagic')
254 sec.exclude('tests.test_cythonmagic')
254 sec.exclude('tests.test_cythonmagic')
255 if not have['oct2py']:
255 if not have['oct2py']:
256 sec.exclude('octavemagic')
256 sec.exclude('octavemagic')
257 sec.exclude('tests.test_octavemagic')
257 sec.exclude('tests.test_octavemagic')
258 if not have['rpy2'] or not have['numpy']:
258 if not have['rpy2'] or not have['numpy']:
259 sec.exclude('rmagic')
259 sec.exclude('rmagic')
260 sec.exclude('tests.test_rmagic')
260 sec.exclude('tests.test_rmagic')
261 # autoreload does some strange stuff, so move it to its own test section
261 # autoreload does some strange stuff, so move it to its own test section
262 sec.exclude('autoreload')
262 sec.exclude('autoreload')
263 sec.exclude('tests.test_autoreload')
263 sec.exclude('tests.test_autoreload')
264 test_sections['autoreload'] = TestSection('autoreload',
264 test_sections['autoreload'] = TestSection('autoreload',
265 ['IPython.extensions.autoreload', 'IPython.extensions.tests.test_autoreload'])
265 ['IPython.extensions.autoreload', 'IPython.extensions.tests.test_autoreload'])
266 test_group_names.append('autoreload')
266 test_group_names.append('autoreload')
267
267
268 # qt:
268 # qt:
269 test_sections['qt'].requires('zmq', 'qt', 'pygments')
269 test_sections['qt'].requires('zmq', 'qt', 'pygments')
270
270
271 # html:
271 # html:
272 sec = test_sections['html']
272 sec = test_sections['html']
273 sec.requires('zmq', 'tornado')
273 sec.requires('zmq', 'tornado')
274 # The notebook 'static' directory contains JS, css and other
274 # The notebook 'static' directory contains JS, css and other
275 # files for web serving. Occasionally projects may put a .py
275 # files for web serving. Occasionally projects may put a .py
276 # file in there (MathJax ships a conf.py), so we might as
276 # file in there (MathJax ships a conf.py), so we might as
277 # well play it safe and skip the whole thing.
277 # well play it safe and skip the whole thing.
278 sec.exclude('static')
278 sec.exclude('static')
279 sec.exclude('fabfile')
279 sec.exclude('fabfile')
280 if not have['jinja2']:
280 if not have['jinja2']:
281 sec.exclude('notebookapp')
281 sec.exclude('notebookapp')
282 if not have['azure']:
282 if not have['azure']:
283 sec.exclude('services.notebooks.azurenbmanager')
283 sec.exclude('services.notebooks.azurenbmanager')
284
284
285 # config:
285 # config:
286 # Config files aren't really importable stand-alone
286 # Config files aren't really importable stand-alone
287 test_sections['config'].exclude('profile')
287 test_sections['config'].exclude('profile')
288
288
289 # nbconvert:
289 # nbconvert:
290 sec = test_sections['nbconvert']
290 sec = test_sections['nbconvert']
291 sec.requires('pygments', 'jinja2', 'sphinx')
291 sec.requires('pygments', 'jinja2', 'sphinx')
292 # Exclude nbconvert directories containing config files used to test.
292 # Exclude nbconvert directories containing config files used to test.
293 # Executing the config files with iptest would cause an exception.
293 # Executing the config files with iptest would cause an exception.
294 sec.exclude('tests.files')
294 sec.exclude('tests.files')
295 sec.exclude('exporters.tests.files')
295 sec.exclude('exporters.tests.files')
296 if not have['tornado']:
296 if not have['tornado']:
297 sec.exclude('nbconvert.post_processors.serve')
297 sec.exclude('nbconvert.post_processors.serve')
298 sec.exclude('nbconvert.post_processors.tests.test_serve')
298 sec.exclude('nbconvert.post_processors.tests.test_serve')
299
299
300 #-----------------------------------------------------------------------------
300 #-----------------------------------------------------------------------------
301 # Functions and classes
301 # Functions and classes
302 #-----------------------------------------------------------------------------
302 #-----------------------------------------------------------------------------
303
303
304 def check_exclusions_exist():
304 def check_exclusions_exist():
305 from IPython.utils.path import get_ipython_package_dir
305 from IPython.utils.path import get_ipython_package_dir
306 from IPython.utils.warn import warn
306 from IPython.utils.warn import warn
307 parent = os.path.dirname(get_ipython_package_dir())
307 parent = os.path.dirname(get_ipython_package_dir())
308 for sec in test_sections:
308 for sec in test_sections:
309 for pattern in sec.exclusions:
309 for pattern in sec.exclusions:
310 fullpath = pjoin(parent, pattern)
310 fullpath = pjoin(parent, pattern)
311 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
311 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
312 warn("Excluding nonexistent file: %r" % pattern)
312 warn("Excluding nonexistent file: %r" % pattern)
313
313
314
314
315 class ExclusionPlugin(Plugin):
315 class ExclusionPlugin(Plugin):
316 """A nose plugin to effect our exclusions of files and directories.
316 """A nose plugin to effect our exclusions of files and directories.
317 """
317 """
318 name = 'exclusions'
318 name = 'exclusions'
319 score = 3000 # Should come before any other plugins
319 score = 3000 # Should come before any other plugins
320
320
321 def __init__(self, exclude_patterns=None):
321 def __init__(self, exclude_patterns=None):
322 """
322 """
323 Parameters
323 Parameters
324 ----------
324 ----------
325
325
326 exclude_patterns : sequence of strings, optional
326 exclude_patterns : sequence of strings, optional
327 These patterns are compiled as regular expressions, subsequently used
327 Filenames containing these patterns (as raw strings, not as regular
328 to exclude any filename which matches them from inclusion in the test
328 expressions) are excluded from the tests.
329 suite (using pattern.search(), NOT pattern.match() ).
330 """
329 """
331
330 self.exclude_patterns = exclude_patterns or []
332 if exclude_patterns is None:
333 exclude_patterns = []
334 self.exclude_patterns = [re.compile(p) for p in exclude_patterns]
335 super(ExclusionPlugin, self).__init__()
331 super(ExclusionPlugin, self).__init__()
336
332
337 def options(self, parser, env=os.environ):
333 def options(self, parser, env=os.environ):
338 Plugin.options(self, parser, env)
334 Plugin.options(self, parser, env)
339
335
340 def configure(self, options, config):
336 def configure(self, options, config):
341 Plugin.configure(self, options, config)
337 Plugin.configure(self, options, config)
342 # Override nose trying to disable plugin.
338 # Override nose trying to disable plugin.
343 self.enabled = True
339 self.enabled = True
344
340
345 def wantFile(self, filename):
341 def wantFile(self, filename):
346 """Return whether the given filename should be scanned for tests.
342 """Return whether the given filename should be scanned for tests.
347 """
343 """
348 if any(pat.search(filename) for pat in self.exclude_patterns):
344 if any(pat in filename for pat in self.exclude_patterns):
349 return False
345 return False
350 return None
346 return None
351
347
352 def wantDirectory(self, directory):
348 def wantDirectory(self, directory):
353 """Return whether the given directory should be scanned for tests.
349 """Return whether the given directory should be scanned for tests.
354 """
350 """
355 if any(pat.search(directory) for pat in self.exclude_patterns):
351 if any(pat in directory for pat in self.exclude_patterns):
356 return False
352 return False
357 return None
353 return None
358
354
359
355
360 def run_iptest():
356 def run_iptest():
361 """Run the IPython test suite using nose.
357 """Run the IPython test suite using nose.
362
358
363 This function is called when this script is **not** called with the form
359 This function is called when this script is **not** called with the form
364 `iptest all`. It simply calls nose with appropriate command line flags
360 `iptest all`. It simply calls nose with appropriate command line flags
365 and accepts all of the standard nose arguments.
361 and accepts all of the standard nose arguments.
366 """
362 """
367 # Apply our monkeypatch to Xunit
363 # Apply our monkeypatch to Xunit
368 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
364 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
369 monkeypatch_xunit()
365 monkeypatch_xunit()
370
366
371 warnings.filterwarnings('ignore',
367 warnings.filterwarnings('ignore',
372 'This will be removed soon. Use IPython.testing.util instead')
368 'This will be removed soon. Use IPython.testing.util instead')
373
369
374 arg1 = sys.argv[1]
370 arg1 = sys.argv[1]
375 if arg1 in test_sections:
371 if arg1 in test_sections:
376 section = test_sections[arg1]
372 section = test_sections[arg1]
377 sys.argv[1:2] = section.includes
373 sys.argv[1:2] = section.includes
378 elif arg1.startswith('IPython.') and arg1[8:] in test_sections:
374 elif arg1.startswith('IPython.') and arg1[8:] in test_sections:
379 section = test_sections[arg1[8:]]
375 section = test_sections[arg1[8:]]
380 sys.argv[1:2] = section.includes
376 sys.argv[1:2] = section.includes
381 else:
377 else:
382 section = TestSection(arg1, includes=[arg1])
378 section = TestSection(arg1, includes=[arg1])
383
379
384
380
385 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
381 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
386
382
387 '--with-ipdoctest',
383 '--with-ipdoctest',
388 '--ipdoctest-tests','--ipdoctest-extension=txt',
384 '--ipdoctest-tests','--ipdoctest-extension=txt',
389
385
390 # We add --exe because of setuptools' imbecility (it
386 # We add --exe because of setuptools' imbecility (it
391 # blindly does chmod +x on ALL files). Nose does the
387 # blindly does chmod +x on ALL files). Nose does the
392 # right thing and it tries to avoid executables,
388 # right thing and it tries to avoid executables,
393 # setuptools unfortunately forces our hand here. This
389 # setuptools unfortunately forces our hand here. This
394 # has been discussed on the distutils list and the
390 # has been discussed on the distutils list and the
395 # setuptools devs refuse to fix this problem!
391 # setuptools devs refuse to fix this problem!
396 '--exe',
392 '--exe',
397 ]
393 ]
398 if '-a' not in argv and '-A' not in argv:
394 if '-a' not in argv and '-A' not in argv:
399 argv = argv + ['-a', '!crash']
395 argv = argv + ['-a', '!crash']
400
396
401 if nose.__version__ >= '0.11':
397 if nose.__version__ >= '0.11':
402 # I don't fully understand why we need this one, but depending on what
398 # I don't fully understand why we need this one, but depending on what
403 # directory the test suite is run from, if we don't give it, 0 tests
399 # directory the test suite is run from, if we don't give it, 0 tests
404 # get run. Specifically, if the test suite is run from the source dir
400 # get run. Specifically, if the test suite is run from the source dir
405 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
401 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
406 # even if the same call done in this directory works fine). It appears
402 # even if the same call done in this directory works fine). It appears
407 # that if the requested package is in the current dir, nose bails early
403 # that if the requested package is in the current dir, nose bails early
408 # by default. Since it's otherwise harmless, leave it in by default
404 # by default. Since it's otherwise harmless, leave it in by default
409 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
405 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
410 argv.append('--traverse-namespace')
406 argv.append('--traverse-namespace')
411
407
412 # use our plugin for doctesting. It will remove the standard doctest plugin
408 # use our plugin for doctesting. It will remove the standard doctest plugin
413 # if it finds it enabled
409 # if it finds it enabled
414 plugins = [ExclusionPlugin(section.excludes), IPythonDoctest(), KnownFailure()]
410 plugins = [ExclusionPlugin(section.excludes), IPythonDoctest(), KnownFailure()]
415
411
416 # Use working directory set by parent process (see iptestcontroller)
412 # Use working directory set by parent process (see iptestcontroller)
417 if 'IPTEST_WORKING_DIR' in os.environ:
413 if 'IPTEST_WORKING_DIR' in os.environ:
418 os.chdir(os.environ['IPTEST_WORKING_DIR'])
414 os.chdir(os.environ['IPTEST_WORKING_DIR'])
419
415
420 # We need a global ipython running in this process, but the special
416 # We need a global ipython running in this process, but the special
421 # in-process group spawns its own IPython kernels, so for *that* group we
417 # in-process group spawns its own IPython kernels, so for *that* group we
422 # must avoid also opening the global one (otherwise there's a conflict of
418 # must avoid also opening the global one (otherwise there's a conflict of
423 # singletons). Ultimately the solution to this problem is to refactor our
419 # singletons). Ultimately the solution to this problem is to refactor our
424 # assumptions about what needs to be a singleton and what doesn't (app
420 # assumptions about what needs to be a singleton and what doesn't (app
425 # objects should, individual shells shouldn't). But for now, this
421 # objects should, individual shells shouldn't). But for now, this
426 # workaround allows the test suite for the inprocess module to complete.
422 # workaround allows the test suite for the inprocess module to complete.
427 if 'kernel.inprocess' not in section.name:
423 if 'kernel.inprocess' not in section.name:
428 from IPython.testing import globalipapp
424 from IPython.testing import globalipapp
429 globalipapp.start_ipython()
425 globalipapp.start_ipython()
430
426
431 # Now nose can run
427 # Now nose can run
432 TestProgram(argv=argv, addplugins=plugins)
428 TestProgram(argv=argv, addplugins=plugins)
433
429
434 if __name__ == '__main__':
430 if __name__ == '__main__':
435 run_iptest()
431 run_iptest()
436
432
General Comments 0
You need to be logged in to leave comments. Login now