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