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