##// END OF EJS Templates
add nbconvert serve exclusions without tornado
Min RK -
Show More
@@ -1,433 +1,436 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']:
297 sec.exclude('nbconvert.post_processors.serve')
298 sec.exclude('nbconvert.post_processors.tests.test_serve')
296
299
297 #-----------------------------------------------------------------------------
300 #-----------------------------------------------------------------------------
298 # Functions and classes
301 # Functions and classes
299 #-----------------------------------------------------------------------------
302 #-----------------------------------------------------------------------------
300
303
301 def check_exclusions_exist():
304 def check_exclusions_exist():
302 from IPython.utils.path import get_ipython_package_dir
305 from IPython.utils.path import get_ipython_package_dir
303 from IPython.utils.warn import warn
306 from IPython.utils.warn import warn
304 parent = os.path.dirname(get_ipython_package_dir())
307 parent = os.path.dirname(get_ipython_package_dir())
305 for sec in test_sections:
308 for sec in test_sections:
306 for pattern in sec.exclusions:
309 for pattern in sec.exclusions:
307 fullpath = pjoin(parent, pattern)
310 fullpath = pjoin(parent, pattern)
308 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
311 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
309 warn("Excluding nonexistent file: %r" % pattern)
312 warn("Excluding nonexistent file: %r" % pattern)
310
313
311
314
312 class ExclusionPlugin(Plugin):
315 class ExclusionPlugin(Plugin):
313 """A nose plugin to effect our exclusions of files and directories.
316 """A nose plugin to effect our exclusions of files and directories.
314 """
317 """
315 name = 'exclusions'
318 name = 'exclusions'
316 score = 3000 # Should come before any other plugins
319 score = 3000 # Should come before any other plugins
317
320
318 def __init__(self, exclude_patterns=None):
321 def __init__(self, exclude_patterns=None):
319 """
322 """
320 Parameters
323 Parameters
321 ----------
324 ----------
322
325
323 exclude_patterns : sequence of strings, optional
326 exclude_patterns : sequence of strings, optional
324 These patterns are compiled as regular expressions, subsequently used
327 These patterns are compiled as regular expressions, subsequently used
325 to exclude any filename which matches them from inclusion in the test
328 to exclude any filename which matches them from inclusion in the test
326 suite (using pattern.search(), NOT pattern.match() ).
329 suite (using pattern.search(), NOT pattern.match() ).
327 """
330 """
328
331
329 if exclude_patterns is None:
332 if exclude_patterns is None:
330 exclude_patterns = []
333 exclude_patterns = []
331 self.exclude_patterns = [re.compile(p) for p in exclude_patterns]
334 self.exclude_patterns = [re.compile(p) for p in exclude_patterns]
332 super(ExclusionPlugin, self).__init__()
335 super(ExclusionPlugin, self).__init__()
333
336
334 def options(self, parser, env=os.environ):
337 def options(self, parser, env=os.environ):
335 Plugin.options(self, parser, env)
338 Plugin.options(self, parser, env)
336
339
337 def configure(self, options, config):
340 def configure(self, options, config):
338 Plugin.configure(self, options, config)
341 Plugin.configure(self, options, config)
339 # Override nose trying to disable plugin.
342 # Override nose trying to disable plugin.
340 self.enabled = True
343 self.enabled = True
341
344
342 def wantFile(self, filename):
345 def wantFile(self, filename):
343 """Return whether the given filename should be scanned for tests.
346 """Return whether the given filename should be scanned for tests.
344 """
347 """
345 if any(pat.search(filename) for pat in self.exclude_patterns):
348 if any(pat.search(filename) for pat in self.exclude_patterns):
346 return False
349 return False
347 return None
350 return None
348
351
349 def wantDirectory(self, directory):
352 def wantDirectory(self, directory):
350 """Return whether the given directory should be scanned for tests.
353 """Return whether the given directory should be scanned for tests.
351 """
354 """
352 if any(pat.search(directory) for pat in self.exclude_patterns):
355 if any(pat.search(directory) for pat in self.exclude_patterns):
353 return False
356 return False
354 return None
357 return None
355
358
356
359
357 def run_iptest():
360 def run_iptest():
358 """Run the IPython test suite using nose.
361 """Run the IPython test suite using nose.
359
362
360 This function is called when this script is **not** called with the form
363 This function is called when this script is **not** called with the form
361 `iptest all`. It simply calls nose with appropriate command line flags
364 `iptest all`. It simply calls nose with appropriate command line flags
362 and accepts all of the standard nose arguments.
365 and accepts all of the standard nose arguments.
363 """
366 """
364 # Apply our monkeypatch to Xunit
367 # Apply our monkeypatch to Xunit
365 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
368 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
366 monkeypatch_xunit()
369 monkeypatch_xunit()
367
370
368 warnings.filterwarnings('ignore',
371 warnings.filterwarnings('ignore',
369 'This will be removed soon. Use IPython.testing.util instead')
372 'This will be removed soon. Use IPython.testing.util instead')
370
373
371 arg1 = sys.argv[1]
374 arg1 = sys.argv[1]
372 if arg1 in test_sections:
375 if arg1 in test_sections:
373 section = test_sections[arg1]
376 section = test_sections[arg1]
374 sys.argv[1:2] = section.includes
377 sys.argv[1:2] = section.includes
375 elif arg1.startswith('IPython.') and arg1[8:] in test_sections:
378 elif arg1.startswith('IPython.') and arg1[8:] in test_sections:
376 section = test_sections[arg1[8:]]
379 section = test_sections[arg1[8:]]
377 sys.argv[1:2] = section.includes
380 sys.argv[1:2] = section.includes
378 else:
381 else:
379 section = TestSection(arg1, includes=[arg1])
382 section = TestSection(arg1, includes=[arg1])
380
383
381
384
382 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
385 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
383
386
384 '--with-ipdoctest',
387 '--with-ipdoctest',
385 '--ipdoctest-tests','--ipdoctest-extension=txt',
388 '--ipdoctest-tests','--ipdoctest-extension=txt',
386
389
387 # We add --exe because of setuptools' imbecility (it
390 # We add --exe because of setuptools' imbecility (it
388 # blindly does chmod +x on ALL files). Nose does the
391 # blindly does chmod +x on ALL files). Nose does the
389 # right thing and it tries to avoid executables,
392 # right thing and it tries to avoid executables,
390 # setuptools unfortunately forces our hand here. This
393 # setuptools unfortunately forces our hand here. This
391 # has been discussed on the distutils list and the
394 # has been discussed on the distutils list and the
392 # setuptools devs refuse to fix this problem!
395 # setuptools devs refuse to fix this problem!
393 '--exe',
396 '--exe',
394 ]
397 ]
395 if '-a' not in argv and '-A' not in argv:
398 if '-a' not in argv and '-A' not in argv:
396 argv = argv + ['-a', '!crash']
399 argv = argv + ['-a', '!crash']
397
400
398 if nose.__version__ >= '0.11':
401 if nose.__version__ >= '0.11':
399 # I don't fully understand why we need this one, but depending on what
402 # I don't fully understand why we need this one, but depending on what
400 # directory the test suite is run from, if we don't give it, 0 tests
403 # directory the test suite is run from, if we don't give it, 0 tests
401 # get run. Specifically, if the test suite is run from the source dir
404 # get run. Specifically, if the test suite is run from the source dir
402 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
405 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
403 # even if the same call done in this directory works fine). It appears
406 # even if the same call done in this directory works fine). It appears
404 # that if the requested package is in the current dir, nose bails early
407 # that if the requested package is in the current dir, nose bails early
405 # by default. Since it's otherwise harmless, leave it in by default
408 # by default. Since it's otherwise harmless, leave it in by default
406 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
409 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
407 argv.append('--traverse-namespace')
410 argv.append('--traverse-namespace')
408
411
409 # use our plugin for doctesting. It will remove the standard doctest plugin
412 # use our plugin for doctesting. It will remove the standard doctest plugin
410 # if it finds it enabled
413 # if it finds it enabled
411 plugins = [ExclusionPlugin(section.excludes), IPythonDoctest(), KnownFailure()]
414 plugins = [ExclusionPlugin(section.excludes), IPythonDoctest(), KnownFailure()]
412
415
413 # Use working directory set by parent process (see iptestcontroller)
416 # Use working directory set by parent process (see iptestcontroller)
414 if 'IPTEST_WORKING_DIR' in os.environ:
417 if 'IPTEST_WORKING_DIR' in os.environ:
415 os.chdir(os.environ['IPTEST_WORKING_DIR'])
418 os.chdir(os.environ['IPTEST_WORKING_DIR'])
416
419
417 # We need a global ipython running in this process, but the special
420 # We need a global ipython running in this process, but the special
418 # in-process group spawns its own IPython kernels, so for *that* group we
421 # in-process group spawns its own IPython kernels, so for *that* group we
419 # must avoid also opening the global one (otherwise there's a conflict of
422 # must avoid also opening the global one (otherwise there's a conflict of
420 # singletons). Ultimately the solution to this problem is to refactor our
423 # singletons). Ultimately the solution to this problem is to refactor our
421 # assumptions about what needs to be a singleton and what doesn't (app
424 # assumptions about what needs to be a singleton and what doesn't (app
422 # objects should, individual shells shouldn't). But for now, this
425 # objects should, individual shells shouldn't). But for now, this
423 # workaround allows the test suite for the inprocess module to complete.
426 # workaround allows the test suite for the inprocess module to complete.
424 if 'kernel.inprocess' not in section.name:
427 if 'kernel.inprocess' not in section.name:
425 from IPython.testing import globalipapp
428 from IPython.testing import globalipapp
426 globalipapp.start_ipython()
429 globalipapp.start_ipython()
427
430
428 # Now nose can run
431 # Now nose can run
429 TestProgram(argv=argv, addplugins=plugins)
432 TestProgram(argv=argv, addplugins=plugins)
430
433
431 if __name__ == '__main__':
434 if __name__ == '__main__':
432 run_iptest()
435 run_iptest()
433
436
General Comments 0
You need to be logged in to leave comments. Login now