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