##// END OF EJS Templates
Fail on bad use of traitlets.
Matthias Bussonnier -
Show More
@@ -1,435 +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 # Copyright (c) IPython Development Team.
17 # Copyright (c) IPython Development Team.
18 # Distributed under the terms of the Modified BSD License.
18 # Distributed under the terms of the Modified BSD License.
19
19
20 from __future__ import print_function
20 from __future__ import print_function
21
21
22 import glob
22 import glob
23 from io import BytesIO
23 from io import BytesIO
24 import os
24 import os
25 import os.path as path
25 import os.path as path
26 import sys
26 import sys
27 from threading import Thread, Lock, Event
27 from threading import Thread, Lock, Event
28 import warnings
28 import warnings
29
29
30 import nose.plugins.builtin
30 import nose.plugins.builtin
31 from nose.plugins.xunit import Xunit
31 from nose.plugins.xunit import Xunit
32 from nose import SkipTest
32 from nose import SkipTest
33 from nose.core import TestProgram
33 from nose.core import TestProgram
34 from nose.plugins import Plugin
34 from nose.plugins import Plugin
35 from nose.util import safe_str
35 from nose.util import safe_str
36
36
37 from IPython import version_info
37 from IPython import version_info
38 from IPython.utils.py3compat import bytes_to_str
38 from IPython.utils.py3compat import bytes_to_str
39 from IPython.utils.importstring import import_item
39 from IPython.utils.importstring import import_item
40 from IPython.testing.plugin.ipdoctest import IPythonDoctest
40 from IPython.testing.plugin.ipdoctest import IPythonDoctest
41 from IPython.external.decorators import KnownFailure, knownfailureif
41 from IPython.external.decorators import KnownFailure, knownfailureif
42
42
43 pjoin = path.join
43 pjoin = path.join
44
44
45
45
46 # Enable printing all warnings raise by IPython's modules
46 # Enable printing all warnings raise by IPython's modules
47 warnings.filterwarnings('ignore', message='.*Matplotlib is building the font cache.*', category=UserWarning, module='.*')
47 warnings.filterwarnings('ignore', message='.*Matplotlib is building the font cache.*', category=UserWarning, module='.*')
48 if sys.version_info > (3,0):
48 if sys.version_info > (3,0):
49 warnings.filterwarnings('error', message='.*', category=ResourceWarning, module='.*')
49 warnings.filterwarnings('error', message='.*', category=ResourceWarning, module='.*')
50 warnings.filterwarnings('error', message=".*{'config': True}.*", category=DeprecationWarning, module='IPy.*')
50 warnings.filterwarnings('default', message='.*', category=Warning, module='IPy.*')
51 warnings.filterwarnings('default', message='.*', category=Warning, module='IPy.*')
51
52
52 if version_info < (6,):
53 if version_info < (6,):
53 # nose.tools renames all things from `camelCase` to `snake_case` which raise an
54 # nose.tools renames all things from `camelCase` to `snake_case` which raise an
54 # warning with the runner they also import from standard import library. (as of Dec 2015)
55 # warning with the runner they also import from standard import library. (as of Dec 2015)
55 # Ignore, let's revisit that in a couple of years for IPython 6.
56 # Ignore, let's revisit that in a couple of years for IPython 6.
56 warnings.filterwarnings('ignore', message='.*Please use assertEqual instead', category=Warning, module='IPython.*')
57 warnings.filterwarnings('ignore', message='.*Please use assertEqual instead', category=Warning, module='IPython.*')
57
58
58
59
59 # ------------------------------------------------------------------------------
60 # ------------------------------------------------------------------------------
60 # Monkeypatch Xunit to count known failures as skipped.
61 # Monkeypatch Xunit to count known failures as skipped.
61 # ------------------------------------------------------------------------------
62 # ------------------------------------------------------------------------------
62 def monkeypatch_xunit():
63 def monkeypatch_xunit():
63 try:
64 try:
64 knownfailureif(True)(lambda: None)()
65 knownfailureif(True)(lambda: None)()
65 except Exception as e:
66 except Exception as e:
66 KnownFailureTest = type(e)
67 KnownFailureTest = type(e)
67
68
68 def addError(self, test, err, capt=None):
69 def addError(self, test, err, capt=None):
69 if issubclass(err[0], KnownFailureTest):
70 if issubclass(err[0], KnownFailureTest):
70 err = (SkipTest,) + err[1:]
71 err = (SkipTest,) + err[1:]
71 return self.orig_addError(test, err, capt)
72 return self.orig_addError(test, err, capt)
72
73
73 Xunit.orig_addError = Xunit.addError
74 Xunit.orig_addError = Xunit.addError
74 Xunit.addError = addError
75 Xunit.addError = addError
75
76
76 #-----------------------------------------------------------------------------
77 #-----------------------------------------------------------------------------
77 # Check which dependencies are installed and greater than minimum version.
78 # Check which dependencies are installed and greater than minimum version.
78 #-----------------------------------------------------------------------------
79 #-----------------------------------------------------------------------------
79 def extract_version(mod):
80 def extract_version(mod):
80 return mod.__version__
81 return mod.__version__
81
82
82 def test_for(item, min_version=None, callback=extract_version):
83 def test_for(item, min_version=None, callback=extract_version):
83 """Test to see if item is importable, and optionally check against a minimum
84 """Test to see if item is importable, and optionally check against a minimum
84 version.
85 version.
85
86
86 If min_version is given, the default behavior is to check against the
87 If min_version is given, the default behavior is to check against the
87 `__version__` attribute of the item, but specifying `callback` allows you to
88 `__version__` attribute of the item, but specifying `callback` allows you to
88 extract the value you are interested in. e.g::
89 extract the value you are interested in. e.g::
89
90
90 In [1]: import sys
91 In [1]: import sys
91
92
92 In [2]: from IPython.testing.iptest import test_for
93 In [2]: from IPython.testing.iptest import test_for
93
94
94 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
95 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
95 Out[3]: True
96 Out[3]: True
96
97
97 """
98 """
98 try:
99 try:
99 check = import_item(item)
100 check = import_item(item)
100 except (ImportError, RuntimeError):
101 except (ImportError, RuntimeError):
101 # GTK reports Runtime error if it can't be initialized even if it's
102 # GTK reports Runtime error if it can't be initialized even if it's
102 # importable.
103 # importable.
103 return False
104 return False
104 else:
105 else:
105 if min_version:
106 if min_version:
106 if callback:
107 if callback:
107 # extra processing step to get version to compare
108 # extra processing step to get version to compare
108 check = callback(check)
109 check = callback(check)
109
110
110 return check >= min_version
111 return check >= min_version
111 else:
112 else:
112 return True
113 return True
113
114
114 # Global dict where we can store information on what we have and what we don't
115 # Global dict where we can store information on what we have and what we don't
115 # have available at test run time
116 # have available at test run time
116 have = {'matplotlib': test_for('matplotlib'),
117 have = {'matplotlib': test_for('matplotlib'),
117 'pygments': test_for('pygments'),
118 'pygments': test_for('pygments'),
118 'sqlite3': test_for('sqlite3')}
119 'sqlite3': test_for('sqlite3')}
119
120
120 #-----------------------------------------------------------------------------
121 #-----------------------------------------------------------------------------
121 # Test suite definitions
122 # Test suite definitions
122 #-----------------------------------------------------------------------------
123 #-----------------------------------------------------------------------------
123
124
124 test_group_names = ['core',
125 test_group_names = ['core',
125 'extensions', 'lib', 'terminal', 'testing', 'utils',
126 'extensions', 'lib', 'terminal', 'testing', 'utils',
126 ]
127 ]
127
128
128 class TestSection(object):
129 class TestSection(object):
129 def __init__(self, name, includes):
130 def __init__(self, name, includes):
130 self.name = name
131 self.name = name
131 self.includes = includes
132 self.includes = includes
132 self.excludes = []
133 self.excludes = []
133 self.dependencies = []
134 self.dependencies = []
134 self.enabled = True
135 self.enabled = True
135
136
136 def exclude(self, module):
137 def exclude(self, module):
137 if not module.startswith('IPython'):
138 if not module.startswith('IPython'):
138 module = self.includes[0] + "." + module
139 module = self.includes[0] + "." + module
139 self.excludes.append(module.replace('.', os.sep))
140 self.excludes.append(module.replace('.', os.sep))
140
141
141 def requires(self, *packages):
142 def requires(self, *packages):
142 self.dependencies.extend(packages)
143 self.dependencies.extend(packages)
143
144
144 @property
145 @property
145 def will_run(self):
146 def will_run(self):
146 return self.enabled and all(have[p] for p in self.dependencies)
147 return self.enabled and all(have[p] for p in self.dependencies)
147
148
148 # Name -> (include, exclude, dependencies_met)
149 # Name -> (include, exclude, dependencies_met)
149 test_sections = {n:TestSection(n, ['IPython.%s' % n]) for n in test_group_names}
150 test_sections = {n:TestSection(n, ['IPython.%s' % n]) for n in test_group_names}
150
151
151
152
152 # Exclusions and dependencies
153 # Exclusions and dependencies
153 # ---------------------------
154 # ---------------------------
154
155
155 # core:
156 # core:
156 sec = test_sections['core']
157 sec = test_sections['core']
157 if not have['sqlite3']:
158 if not have['sqlite3']:
158 sec.exclude('tests.test_history')
159 sec.exclude('tests.test_history')
159 sec.exclude('history')
160 sec.exclude('history')
160 if not have['matplotlib']:
161 if not have['matplotlib']:
161 sec.exclude('pylabtools'),
162 sec.exclude('pylabtools'),
162 sec.exclude('tests.test_pylabtools')
163 sec.exclude('tests.test_pylabtools')
163
164
164 # lib:
165 # lib:
165 sec = test_sections['lib']
166 sec = test_sections['lib']
166 sec.exclude('kernel')
167 sec.exclude('kernel')
167 if not have['pygments']:
168 if not have['pygments']:
168 sec.exclude('tests.test_lexers')
169 sec.exclude('tests.test_lexers')
169 # We do this unconditionally, so that the test suite doesn't import
170 # We do this unconditionally, so that the test suite doesn't import
170 # gtk, changing the default encoding and masking some unicode bugs.
171 # gtk, changing the default encoding and masking some unicode bugs.
171 sec.exclude('inputhookgtk')
172 sec.exclude('inputhookgtk')
172 # We also do this unconditionally, because wx can interfere with Unix signals.
173 # We also do this unconditionally, because wx can interfere with Unix signals.
173 # There are currently no tests for it anyway.
174 # There are currently no tests for it anyway.
174 sec.exclude('inputhookwx')
175 sec.exclude('inputhookwx')
175 # Testing inputhook will need a lot of thought, to figure out
176 # Testing inputhook will need a lot of thought, to figure out
176 # how to have tests that don't lock up with the gui event
177 # how to have tests that don't lock up with the gui event
177 # loops in the picture
178 # loops in the picture
178 sec.exclude('inputhook')
179 sec.exclude('inputhook')
179
180
180 # testing:
181 # testing:
181 sec = test_sections['testing']
182 sec = test_sections['testing']
182 # These have to be skipped on win32 because they use echo, rm, cd, etc.
183 # These have to be skipped on win32 because they use echo, rm, cd, etc.
183 # See ticket https://github.com/ipython/ipython/issues/87
184 # See ticket https://github.com/ipython/ipython/issues/87
184 if sys.platform == 'win32':
185 if sys.platform == 'win32':
185 sec.exclude('plugin.test_exampleip')
186 sec.exclude('plugin.test_exampleip')
186 sec.exclude('plugin.dtexample')
187 sec.exclude('plugin.dtexample')
187
188
188 # don't run jupyter_console tests found via shim
189 # don't run jupyter_console tests found via shim
189 test_sections['terminal'].exclude('console')
190 test_sections['terminal'].exclude('console')
190
191
191 # extensions:
192 # extensions:
192 sec = test_sections['extensions']
193 sec = test_sections['extensions']
193 # This is deprecated in favour of rpy2
194 # This is deprecated in favour of rpy2
194 sec.exclude('rmagic')
195 sec.exclude('rmagic')
195 # autoreload does some strange stuff, so move it to its own test section
196 # autoreload does some strange stuff, so move it to its own test section
196 sec.exclude('autoreload')
197 sec.exclude('autoreload')
197 sec.exclude('tests.test_autoreload')
198 sec.exclude('tests.test_autoreload')
198 test_sections['autoreload'] = TestSection('autoreload',
199 test_sections['autoreload'] = TestSection('autoreload',
199 ['IPython.extensions.autoreload', 'IPython.extensions.tests.test_autoreload'])
200 ['IPython.extensions.autoreload', 'IPython.extensions.tests.test_autoreload'])
200 test_group_names.append('autoreload')
201 test_group_names.append('autoreload')
201
202
202
203
203 #-----------------------------------------------------------------------------
204 #-----------------------------------------------------------------------------
204 # Functions and classes
205 # Functions and classes
205 #-----------------------------------------------------------------------------
206 #-----------------------------------------------------------------------------
206
207
207 def check_exclusions_exist():
208 def check_exclusions_exist():
208 from IPython.paths import get_ipython_package_dir
209 from IPython.paths import get_ipython_package_dir
209 from warnings import warn
210 from warnings import warn
210 parent = os.path.dirname(get_ipython_package_dir())
211 parent = os.path.dirname(get_ipython_package_dir())
211 for sec in test_sections:
212 for sec in test_sections:
212 for pattern in sec.exclusions:
213 for pattern in sec.exclusions:
213 fullpath = pjoin(parent, pattern)
214 fullpath = pjoin(parent, pattern)
214 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
215 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
215 warn("Excluding nonexistent file: %r" % pattern)
216 warn("Excluding nonexistent file: %r" % pattern)
216
217
217
218
218 class ExclusionPlugin(Plugin):
219 class ExclusionPlugin(Plugin):
219 """A nose plugin to effect our exclusions of files and directories.
220 """A nose plugin to effect our exclusions of files and directories.
220 """
221 """
221 name = 'exclusions'
222 name = 'exclusions'
222 score = 3000 # Should come before any other plugins
223 score = 3000 # Should come before any other plugins
223
224
224 def __init__(self, exclude_patterns=None):
225 def __init__(self, exclude_patterns=None):
225 """
226 """
226 Parameters
227 Parameters
227 ----------
228 ----------
228
229
229 exclude_patterns : sequence of strings, optional
230 exclude_patterns : sequence of strings, optional
230 Filenames containing these patterns (as raw strings, not as regular
231 Filenames containing these patterns (as raw strings, not as regular
231 expressions) are excluded from the tests.
232 expressions) are excluded from the tests.
232 """
233 """
233 self.exclude_patterns = exclude_patterns or []
234 self.exclude_patterns = exclude_patterns or []
234 super(ExclusionPlugin, self).__init__()
235 super(ExclusionPlugin, self).__init__()
235
236
236 def options(self, parser, env=os.environ):
237 def options(self, parser, env=os.environ):
237 Plugin.options(self, parser, env)
238 Plugin.options(self, parser, env)
238
239
239 def configure(self, options, config):
240 def configure(self, options, config):
240 Plugin.configure(self, options, config)
241 Plugin.configure(self, options, config)
241 # Override nose trying to disable plugin.
242 # Override nose trying to disable plugin.
242 self.enabled = True
243 self.enabled = True
243
244
244 def wantFile(self, filename):
245 def wantFile(self, filename):
245 """Return whether the given filename should be scanned for tests.
246 """Return whether the given filename should be scanned for tests.
246 """
247 """
247 if any(pat in filename for pat in self.exclude_patterns):
248 if any(pat in filename for pat in self.exclude_patterns):
248 return False
249 return False
249 return None
250 return None
250
251
251 def wantDirectory(self, directory):
252 def wantDirectory(self, directory):
252 """Return whether the given directory should be scanned for tests.
253 """Return whether the given directory should be scanned for tests.
253 """
254 """
254 if any(pat in directory for pat in self.exclude_patterns):
255 if any(pat in directory for pat in self.exclude_patterns):
255 return False
256 return False
256 return None
257 return None
257
258
258
259
259 class StreamCapturer(Thread):
260 class StreamCapturer(Thread):
260 daemon = True # Don't hang if main thread crashes
261 daemon = True # Don't hang if main thread crashes
261 started = False
262 started = False
262 def __init__(self, echo=False):
263 def __init__(self, echo=False):
263 super(StreamCapturer, self).__init__()
264 super(StreamCapturer, self).__init__()
264 self.echo = echo
265 self.echo = echo
265 self.streams = []
266 self.streams = []
266 self.buffer = BytesIO()
267 self.buffer = BytesIO()
267 self.readfd, self.writefd = os.pipe()
268 self.readfd, self.writefd = os.pipe()
268 self.buffer_lock = Lock()
269 self.buffer_lock = Lock()
269 self.stop = Event()
270 self.stop = Event()
270
271
271 def run(self):
272 def run(self):
272 self.started = True
273 self.started = True
273
274
274 while not self.stop.is_set():
275 while not self.stop.is_set():
275 chunk = os.read(self.readfd, 1024)
276 chunk = os.read(self.readfd, 1024)
276
277
277 with self.buffer_lock:
278 with self.buffer_lock:
278 self.buffer.write(chunk)
279 self.buffer.write(chunk)
279 if self.echo:
280 if self.echo:
280 sys.stdout.write(bytes_to_str(chunk))
281 sys.stdout.write(bytes_to_str(chunk))
281
282
282 os.close(self.readfd)
283 os.close(self.readfd)
283 os.close(self.writefd)
284 os.close(self.writefd)
284
285
285 def reset_buffer(self):
286 def reset_buffer(self):
286 with self.buffer_lock:
287 with self.buffer_lock:
287 self.buffer.truncate(0)
288 self.buffer.truncate(0)
288 self.buffer.seek(0)
289 self.buffer.seek(0)
289
290
290 def get_buffer(self):
291 def get_buffer(self):
291 with self.buffer_lock:
292 with self.buffer_lock:
292 return self.buffer.getvalue()
293 return self.buffer.getvalue()
293
294
294 def ensure_started(self):
295 def ensure_started(self):
295 if not self.started:
296 if not self.started:
296 self.start()
297 self.start()
297
298
298 def halt(self):
299 def halt(self):
299 """Safely stop the thread."""
300 """Safely stop the thread."""
300 if not self.started:
301 if not self.started:
301 return
302 return
302
303
303 self.stop.set()
304 self.stop.set()
304 os.write(self.writefd, b'\0') # Ensure we're not locked in a read()
305 os.write(self.writefd, b'\0') # Ensure we're not locked in a read()
305 self.join()
306 self.join()
306
307
307 class SubprocessStreamCapturePlugin(Plugin):
308 class SubprocessStreamCapturePlugin(Plugin):
308 name='subprocstreams'
309 name='subprocstreams'
309 def __init__(self):
310 def __init__(self):
310 Plugin.__init__(self)
311 Plugin.__init__(self)
311 self.stream_capturer = StreamCapturer()
312 self.stream_capturer = StreamCapturer()
312 self.destination = os.environ.get('IPTEST_SUBPROC_STREAMS', 'capture')
313 self.destination = os.environ.get('IPTEST_SUBPROC_STREAMS', 'capture')
313 # This is ugly, but distant parts of the test machinery need to be able
314 # This is ugly, but distant parts of the test machinery need to be able
314 # to redirect streams, so we make the object globally accessible.
315 # to redirect streams, so we make the object globally accessible.
315 nose.iptest_stdstreams_fileno = self.get_write_fileno
316 nose.iptest_stdstreams_fileno = self.get_write_fileno
316
317
317 def get_write_fileno(self):
318 def get_write_fileno(self):
318 if self.destination == 'capture':
319 if self.destination == 'capture':
319 self.stream_capturer.ensure_started()
320 self.stream_capturer.ensure_started()
320 return self.stream_capturer.writefd
321 return self.stream_capturer.writefd
321 elif self.destination == 'discard':
322 elif self.destination == 'discard':
322 return os.open(os.devnull, os.O_WRONLY)
323 return os.open(os.devnull, os.O_WRONLY)
323 else:
324 else:
324 return sys.__stdout__.fileno()
325 return sys.__stdout__.fileno()
325
326
326 def configure(self, options, config):
327 def configure(self, options, config):
327 Plugin.configure(self, options, config)
328 Plugin.configure(self, options, config)
328 # Override nose trying to disable plugin.
329 # Override nose trying to disable plugin.
329 if self.destination == 'capture':
330 if self.destination == 'capture':
330 self.enabled = True
331 self.enabled = True
331
332
332 def startTest(self, test):
333 def startTest(self, test):
333 # Reset log capture
334 # Reset log capture
334 self.stream_capturer.reset_buffer()
335 self.stream_capturer.reset_buffer()
335
336
336 def formatFailure(self, test, err):
337 def formatFailure(self, test, err):
337 # Show output
338 # Show output
338 ec, ev, tb = err
339 ec, ev, tb = err
339 captured = self.stream_capturer.get_buffer().decode('utf-8', 'replace')
340 captured = self.stream_capturer.get_buffer().decode('utf-8', 'replace')
340 if captured.strip():
341 if captured.strip():
341 ev = safe_str(ev)
342 ev = safe_str(ev)
342 out = [ev, '>> begin captured subprocess output <<',
343 out = [ev, '>> begin captured subprocess output <<',
343 captured,
344 captured,
344 '>> end captured subprocess output <<']
345 '>> end captured subprocess output <<']
345 return ec, '\n'.join(out), tb
346 return ec, '\n'.join(out), tb
346
347
347 return err
348 return err
348
349
349 formatError = formatFailure
350 formatError = formatFailure
350
351
351 def finalize(self, result):
352 def finalize(self, result):
352 self.stream_capturer.halt()
353 self.stream_capturer.halt()
353
354
354
355
355 def run_iptest():
356 def run_iptest():
356 """Run the IPython test suite using nose.
357 """Run the IPython test suite using nose.
357
358
358 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
359 `iptest all`. It simply calls nose with appropriate command line flags
360 `iptest all`. It simply calls nose with appropriate command line flags
360 and accepts all of the standard nose arguments.
361 and accepts all of the standard nose arguments.
361 """
362 """
362 # Apply our monkeypatch to Xunit
363 # Apply our monkeypatch to Xunit
363 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'):
364 monkeypatch_xunit()
365 monkeypatch_xunit()
365
366
366 warnings.filterwarnings('ignore',
367 warnings.filterwarnings('ignore',
367 'This will be removed soon. Use IPython.testing.util instead')
368 'This will be removed soon. Use IPython.testing.util instead')
368
369
369 arg1 = sys.argv[1]
370 arg1 = sys.argv[1]
370 if arg1 in test_sections:
371 if arg1 in test_sections:
371 section = test_sections[arg1]
372 section = test_sections[arg1]
372 sys.argv[1:2] = section.includes
373 sys.argv[1:2] = section.includes
373 elif arg1.startswith('IPython.') and arg1[8:] in test_sections:
374 elif arg1.startswith('IPython.') and arg1[8:] in test_sections:
374 section = test_sections[arg1[8:]]
375 section = test_sections[arg1[8:]]
375 sys.argv[1:2] = section.includes
376 sys.argv[1:2] = section.includes
376 else:
377 else:
377 section = TestSection(arg1, includes=[arg1])
378 section = TestSection(arg1, includes=[arg1])
378
379
379
380
380 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
381 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
381 # We add --exe because of setuptools' imbecility (it
382 # We add --exe because of setuptools' imbecility (it
382 # blindly does chmod +x on ALL files). Nose does the
383 # blindly does chmod +x on ALL files). Nose does the
383 # right thing and it tries to avoid executables,
384 # right thing and it tries to avoid executables,
384 # setuptools unfortunately forces our hand here. This
385 # setuptools unfortunately forces our hand here. This
385 # has been discussed on the distutils list and the
386 # has been discussed on the distutils list and the
386 # setuptools devs refuse to fix this problem!
387 # setuptools devs refuse to fix this problem!
387 '--exe',
388 '--exe',
388 ]
389 ]
389 if '-a' not in argv and '-A' not in argv:
390 if '-a' not in argv and '-A' not in argv:
390 argv = argv + ['-a', '!crash']
391 argv = argv + ['-a', '!crash']
391
392
392 if nose.__version__ >= '0.11':
393 if nose.__version__ >= '0.11':
393 # I don't fully understand why we need this one, but depending on what
394 # I don't fully understand why we need this one, but depending on what
394 # directory the test suite is run from, if we don't give it, 0 tests
395 # directory the test suite is run from, if we don't give it, 0 tests
395 # get run. Specifically, if the test suite is run from the source dir
396 # get run. Specifically, if the test suite is run from the source dir
396 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
397 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
397 # even if the same call done in this directory works fine). It appears
398 # even if the same call done in this directory works fine). It appears
398 # that if the requested package is in the current dir, nose bails early
399 # that if the requested package is in the current dir, nose bails early
399 # by default. Since it's otherwise harmless, leave it in by default
400 # by default. Since it's otherwise harmless, leave it in by default
400 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
401 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
401 argv.append('--traverse-namespace')
402 argv.append('--traverse-namespace')
402
403
403 plugins = [ ExclusionPlugin(section.excludes), KnownFailure(),
404 plugins = [ ExclusionPlugin(section.excludes), KnownFailure(),
404 SubprocessStreamCapturePlugin() ]
405 SubprocessStreamCapturePlugin() ]
405
406
406 # we still have some vestigial doctests in core
407 # we still have some vestigial doctests in core
407 if (section.name.startswith(('core', 'IPython.core'))):
408 if (section.name.startswith(('core', 'IPython.core'))):
408 plugins.append(IPythonDoctest())
409 plugins.append(IPythonDoctest())
409 argv.extend([
410 argv.extend([
410 '--with-ipdoctest',
411 '--with-ipdoctest',
411 '--ipdoctest-tests',
412 '--ipdoctest-tests',
412 '--ipdoctest-extension=txt',
413 '--ipdoctest-extension=txt',
413 ])
414 ])
414
415
415
416
416 # Use working directory set by parent process (see iptestcontroller)
417 # Use working directory set by parent process (see iptestcontroller)
417 if 'IPTEST_WORKING_DIR' in os.environ:
418 if 'IPTEST_WORKING_DIR' in os.environ:
418 os.chdir(os.environ['IPTEST_WORKING_DIR'])
419 os.chdir(os.environ['IPTEST_WORKING_DIR'])
419
420
420 # We need a global ipython running in this process, but the special
421 # 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
422 # 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
423 # 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
424 # 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
425 # assumptions about what needs to be a singleton and what doesn't (app
425 # objects should, individual shells shouldn't). But for now, this
426 # objects should, individual shells shouldn't). But for now, this
426 # workaround allows the test suite for the inprocess module to complete.
427 # workaround allows the test suite for the inprocess module to complete.
427 if 'kernel.inprocess' not in section.name:
428 if 'kernel.inprocess' not in section.name:
428 from IPython.testing import globalipapp
429 from IPython.testing import globalipapp
429 globalipapp.start_ipython()
430 globalipapp.start_ipython()
430
431
431 # Now nose can run
432 # Now nose can run
432 TestProgram(argv=argv, addplugins=plugins)
433 TestProgram(argv=argv, addplugins=plugins)
433
434
434 if __name__ == '__main__':
435 if __name__ == '__main__':
435 run_iptest()
436 run_iptest()
General Comments 0
You need to be logged in to leave comments. Login now