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