##// END OF EJS Templates
allow more processing in test_for...
MinRK -
Show More
@@ -1,449 +1,473 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 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18 # Copyright (C) 2009 The IPython Development Team
18 # Copyright (C) 2009 The IPython Development Team
19 #
19 #
20 # Distributed under the terms of the BSD License. The full license is in
20 # Distributed under the terms of the BSD License. The full license is in
21 # the file COPYING, distributed as part of this software.
21 # the file COPYING, distributed as part of this software.
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Imports
25 # Imports
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27
27
28 # Stdlib
28 # Stdlib
29 import os
29 import os
30 import os.path as path
30 import os.path as path
31 import signal
31 import signal
32 import sys
32 import sys
33 import subprocess
33 import subprocess
34 import tempfile
34 import tempfile
35 import time
35 import time
36 import warnings
36 import warnings
37
37
38 # Note: monkeypatch!
38 # Note: monkeypatch!
39 # We need to monkeypatch a small problem in nose itself first, before importing
39 # We need to monkeypatch a small problem in nose itself first, before importing
40 # it for actual use. This should get into nose upstream, but its release cycle
40 # it for actual use. This should get into nose upstream, but its release cycle
41 # is slow and we need it for our parametric tests to work correctly.
41 # is slow and we need it for our parametric tests to work correctly.
42 from IPython.testing import nosepatch
42 from IPython.testing import nosepatch
43 # Now, proceed to import nose itself
43 # Now, proceed to import nose itself
44 import nose.plugins.builtin
44 import nose.plugins.builtin
45 from nose.core import TestProgram
45 from nose.core import TestProgram
46
46
47 # Our own imports
47 # Our own imports
48 from IPython.utils.importstring import import_item
48 from IPython.utils.path import get_ipython_module_path
49 from IPython.utils.path import get_ipython_module_path
49 from IPython.utils.process import find_cmd, pycmd2argv
50 from IPython.utils.process import find_cmd, pycmd2argv
50 from IPython.utils.sysinfo import sys_info
51 from IPython.utils.sysinfo import sys_info
51
52
52 from IPython.testing import globalipapp
53 from IPython.testing import globalipapp
53 from IPython.testing.plugin.ipdoctest import IPythonDoctest
54 from IPython.testing.plugin.ipdoctest import IPythonDoctest
54 from IPython.external.decorators import KnownFailure
55 from IPython.external.decorators import KnownFailure
55
56
56 pjoin = path.join
57 pjoin = path.join
57
58
58
59
59 #-----------------------------------------------------------------------------
60 #-----------------------------------------------------------------------------
60 # Globals
61 # Globals
61 #-----------------------------------------------------------------------------
62 #-----------------------------------------------------------------------------
62
63
63
64
64 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
65 # Warnings control
66 # Warnings control
66 #-----------------------------------------------------------------------------
67 #-----------------------------------------------------------------------------
67
68
68 # Twisted generates annoying warnings with Python 2.6, as will do other code
69 # Twisted generates annoying warnings with Python 2.6, as will do other code
69 # that imports 'sets' as of today
70 # that imports 'sets' as of today
70 warnings.filterwarnings('ignore', 'the sets module is deprecated',
71 warnings.filterwarnings('ignore', 'the sets module is deprecated',
71 DeprecationWarning )
72 DeprecationWarning )
72
73
73 # This one also comes from Twisted
74 # This one also comes from Twisted
74 warnings.filterwarnings('ignore', 'the sha module is deprecated',
75 warnings.filterwarnings('ignore', 'the sha module is deprecated',
75 DeprecationWarning)
76 DeprecationWarning)
76
77
77 # Wx on Fedora11 spits these out
78 # Wx on Fedora11 spits these out
78 warnings.filterwarnings('ignore', 'wxPython/wxWidgets release number mismatch',
79 warnings.filterwarnings('ignore', 'wxPython/wxWidgets release number mismatch',
79 UserWarning)
80 UserWarning)
80
81
81 #-----------------------------------------------------------------------------
82 #-----------------------------------------------------------------------------
82 # Logic for skipping doctests
83 # Logic for skipping doctests
83 #-----------------------------------------------------------------------------
84 #-----------------------------------------------------------------------------
85 def extract_version(mod):
86 return mod.__version__
84
87
85 def test_for(mod, min_version=None):
88 def test_for(item, min_version=None, callback=extract_version):
86 """Test to see if mod is importable."""
89 """Test to see if item is importable, and optionally check against a minimum
90 version.
91
92 If min_version is given, the default behavior is to check against the
93 `__version__` attribute of the item, but specifying `callback` allows you to
94 extract the value you are interested in. e.g::
95
96 In [1]: import sys
97
98 In [2]: from IPython.testing.iptest import test_for
99
100 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
101 Out[3]: True
102
103 """
87 try:
104 try:
88 __import__(mod)
105 check = import_item(item)
89 except (ImportError, RuntimeError):
106 except (ImportError, RuntimeError):
90 # 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
91 # importable.
108 # importable.
92 return False
109 return False
93 else:
110 else:
94 if min_version:
111 if min_version:
95 return sys.modules[mod].__version__ >= min_version
112 if callback:
113 # extra processing step to get version to compare
114 check = callback(check)
115
116 return check >= min_version
96 else:
117 else:
97 return True
118 return True
98
119
99 # 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
100 # have available at test run time
121 # have available at test run time
101 have = {}
122 have = {}
102
123
103 have['curses'] = test_for('_curses')
124 have['curses'] = test_for('_curses')
104 have['matplotlib'] = test_for('matplotlib')
125 have['matplotlib'] = test_for('matplotlib')
105 have['pexpect'] = test_for('pexpect')
126 have['pexpect'] = test_for('IPython.external.pexpect')
106 have['pymongo'] = test_for('pymongo')
127 have['pymongo'] = test_for('pymongo')
107 have['wx'] = test_for('wx')
128 have['wx'] = test_for('wx')
108 have['wx.aui'] = test_for('wx.aui')
129 have['wx.aui'] = test_for('wx.aui')
109 if os.name == 'nt':
110 have['zmq'] = test_for('zmq', '2.1.7')
111 else:
112 have['zmq'] = test_for('zmq', '2.1.4')
113 have['qt'] = test_for('IPython.external.qt')
130 have['qt'] = test_for('IPython.external.qt')
114
131
115 try:
132 have['tornado'] = test_for('tornado.version_info', (2,1,0), callback=None)
116 import tornado
133
117 if tornado.version_info < (2,1,0):
134 if os.name == 'nt':
118 raise ImportError
135 min_zmq = (2,1,7)
119 except ImportError:
120 have['tornado'] = False
121 else:
136 else:
122 have['tornado'] = True
137 min_zmq = (2,1,4)
138
139 def version_tuple(mod):
140 "turn '2.1.9' into (2,1,9), and '2.1dev' into (2,1,999)"
141 # turn 'dev' into 999, because Python3 rejects str-int comparisons
142 vs = mod.__version__.replace('dev', '.999')
143 tup = tuple([int(v) for v in vs.split('.') ])
144 return tup
145
146 have['zmq'] = test_for('zmq', min_zmq, version_tuple)
123
147
124 #-----------------------------------------------------------------------------
148 #-----------------------------------------------------------------------------
125 # Functions and classes
149 # Functions and classes
126 #-----------------------------------------------------------------------------
150 #-----------------------------------------------------------------------------
127
151
128 def report():
152 def report():
129 """Return a string with a summary report of test-related variables."""
153 """Return a string with a summary report of test-related variables."""
130
154
131 out = [ sys_info(), '\n']
155 out = [ sys_info(), '\n']
132
156
133 avail = []
157 avail = []
134 not_avail = []
158 not_avail = []
135
159
136 for k, is_avail in have.items():
160 for k, is_avail in have.items():
137 if is_avail:
161 if is_avail:
138 avail.append(k)
162 avail.append(k)
139 else:
163 else:
140 not_avail.append(k)
164 not_avail.append(k)
141
165
142 if avail:
166 if avail:
143 out.append('\nTools and libraries available at test time:\n')
167 out.append('\nTools and libraries available at test time:\n')
144 avail.sort()
168 avail.sort()
145 out.append(' ' + ' '.join(avail)+'\n')
169 out.append(' ' + ' '.join(avail)+'\n')
146
170
147 if not_avail:
171 if not_avail:
148 out.append('\nTools and libraries NOT available at test time:\n')
172 out.append('\nTools and libraries NOT available at test time:\n')
149 not_avail.sort()
173 not_avail.sort()
150 out.append(' ' + ' '.join(not_avail)+'\n')
174 out.append(' ' + ' '.join(not_avail)+'\n')
151
175
152 return ''.join(out)
176 return ''.join(out)
153
177
154
178
155 def make_exclude():
179 def make_exclude():
156 """Make patterns of modules and packages to exclude from testing.
180 """Make patterns of modules and packages to exclude from testing.
157
181
158 For the IPythonDoctest plugin, we need to exclude certain patterns that
182 For the IPythonDoctest plugin, we need to exclude certain patterns that
159 cause testing problems. We should strive to minimize the number of
183 cause testing problems. We should strive to minimize the number of
160 skipped modules, since this means untested code.
184 skipped modules, since this means untested code.
161
185
162 These modules and packages will NOT get scanned by nose at all for tests.
186 These modules and packages will NOT get scanned by nose at all for tests.
163 """
187 """
164 # Simple utility to make IPython paths more readably, we need a lot of
188 # Simple utility to make IPython paths more readably, we need a lot of
165 # these below
189 # these below
166 ipjoin = lambda *paths: pjoin('IPython', *paths)
190 ipjoin = lambda *paths: pjoin('IPython', *paths)
167
191
168 exclusions = [ipjoin('external'),
192 exclusions = [ipjoin('external'),
169 pjoin('IPython_doctest_plugin'),
193 pjoin('IPython_doctest_plugin'),
170 ipjoin('quarantine'),
194 ipjoin('quarantine'),
171 ipjoin('deathrow'),
195 ipjoin('deathrow'),
172 ipjoin('testing', 'attic'),
196 ipjoin('testing', 'attic'),
173 # This guy is probably attic material
197 # This guy is probably attic material
174 ipjoin('testing', 'mkdoctests'),
198 ipjoin('testing', 'mkdoctests'),
175 # Testing inputhook will need a lot of thought, to figure out
199 # Testing inputhook will need a lot of thought, to figure out
176 # how to have tests that don't lock up with the gui event
200 # how to have tests that don't lock up with the gui event
177 # loops in the picture
201 # loops in the picture
178 ipjoin('lib', 'inputhook'),
202 ipjoin('lib', 'inputhook'),
179 # Config files aren't really importable stand-alone
203 # Config files aren't really importable stand-alone
180 ipjoin('config', 'default'),
204 ipjoin('config', 'default'),
181 ipjoin('config', 'profile'),
205 ipjoin('config', 'profile'),
182 ]
206 ]
183
207
184 if not have['wx']:
208 if not have['wx']:
185 exclusions.append(ipjoin('lib', 'inputhookwx'))
209 exclusions.append(ipjoin('lib', 'inputhookwx'))
186
210
187 # We do this unconditionally, so that the test suite doesn't import
211 # We do this unconditionally, so that the test suite doesn't import
188 # gtk, changing the default encoding and masking some unicode bugs.
212 # gtk, changing the default encoding and masking some unicode bugs.
189 exclusions.append(ipjoin('lib', 'inputhookgtk'))
213 exclusions.append(ipjoin('lib', 'inputhookgtk'))
190
214
191 # These have to be skipped on win32 because the use echo, rm, cd, etc.
215 # These have to be skipped on win32 because the use echo, rm, cd, etc.
192 # See ticket https://github.com/ipython/ipython/issues/87
216 # See ticket https://github.com/ipython/ipython/issues/87
193 if sys.platform == 'win32':
217 if sys.platform == 'win32':
194 exclusions.append(ipjoin('testing', 'plugin', 'test_exampleip'))
218 exclusions.append(ipjoin('testing', 'plugin', 'test_exampleip'))
195 exclusions.append(ipjoin('testing', 'plugin', 'dtexample'))
219 exclusions.append(ipjoin('testing', 'plugin', 'dtexample'))
196
220
197 if not have['pexpect']:
221 if not have['pexpect']:
198 exclusions.extend([ipjoin('scripts', 'irunner'),
222 exclusions.extend([ipjoin('scripts', 'irunner'),
199 ipjoin('lib', 'irunner'),
223 ipjoin('lib', 'irunner'),
200 ipjoin('lib', 'tests', 'test_irunner')])
224 ipjoin('lib', 'tests', 'test_irunner')])
201
225
202 if not have['zmq']:
226 if not have['zmq']:
203 exclusions.append(ipjoin('zmq'))
227 exclusions.append(ipjoin('zmq'))
204 exclusions.append(ipjoin('frontend', 'qt'))
228 exclusions.append(ipjoin('frontend', 'qt'))
205 exclusions.append(ipjoin('parallel'))
229 exclusions.append(ipjoin('parallel'))
206 elif not have['qt']:
230 elif not have['qt']:
207 exclusions.append(ipjoin('frontend', 'qt'))
231 exclusions.append(ipjoin('frontend', 'qt'))
208
232
209 if not have['pymongo']:
233 if not have['pymongo']:
210 exclusions.append(ipjoin('parallel', 'controller', 'mongodb'))
234 exclusions.append(ipjoin('parallel', 'controller', 'mongodb'))
211 exclusions.append(ipjoin('parallel', 'tests', 'test_mongodb'))
235 exclusions.append(ipjoin('parallel', 'tests', 'test_mongodb'))
212
236
213 if not have['matplotlib']:
237 if not have['matplotlib']:
214 exclusions.extend([ipjoin('lib', 'pylabtools'),
238 exclusions.extend([ipjoin('lib', 'pylabtools'),
215 ipjoin('lib', 'tests', 'test_pylabtools')])
239 ipjoin('lib', 'tests', 'test_pylabtools')])
216
240
217 if not have['tornado']:
241 if not have['tornado']:
218 exclusions.append(ipjoin('frontend', 'html'))
242 exclusions.append(ipjoin('frontend', 'html'))
219
243
220 # This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
244 # This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
221 if sys.platform == 'win32':
245 if sys.platform == 'win32':
222 exclusions = [s.replace('\\','\\\\') for s in exclusions]
246 exclusions = [s.replace('\\','\\\\') for s in exclusions]
223
247
224 return exclusions
248 return exclusions
225
249
226
250
227 class IPTester(object):
251 class IPTester(object):
228 """Call that calls iptest or trial in a subprocess.
252 """Call that calls iptest or trial in a subprocess.
229 """
253 """
230 #: string, name of test runner that will be called
254 #: string, name of test runner that will be called
231 runner = None
255 runner = None
232 #: list, parameters for test runner
256 #: list, parameters for test runner
233 params = None
257 params = None
234 #: list, arguments of system call to be made to call test runner
258 #: list, arguments of system call to be made to call test runner
235 call_args = None
259 call_args = None
236 #: list, process ids of subprocesses we start (for cleanup)
260 #: list, process ids of subprocesses we start (for cleanup)
237 pids = None
261 pids = None
238
262
239 def __init__(self, runner='iptest', params=None):
263 def __init__(self, runner='iptest', params=None):
240 """Create new test runner."""
264 """Create new test runner."""
241 p = os.path
265 p = os.path
242 if runner == 'iptest':
266 if runner == 'iptest':
243 iptest_app = get_ipython_module_path('IPython.testing.iptest')
267 iptest_app = get_ipython_module_path('IPython.testing.iptest')
244 self.runner = pycmd2argv(iptest_app) + sys.argv[1:]
268 self.runner = pycmd2argv(iptest_app) + sys.argv[1:]
245 else:
269 else:
246 raise Exception('Not a valid test runner: %s' % repr(runner))
270 raise Exception('Not a valid test runner: %s' % repr(runner))
247 if params is None:
271 if params is None:
248 params = []
272 params = []
249 if isinstance(params, str):
273 if isinstance(params, str):
250 params = [params]
274 params = [params]
251 self.params = params
275 self.params = params
252
276
253 # Assemble call
277 # Assemble call
254 self.call_args = self.runner+self.params
278 self.call_args = self.runner+self.params
255
279
256 # Store pids of anything we start to clean up on deletion, if possible
280 # Store pids of anything we start to clean up on deletion, if possible
257 # (on posix only, since win32 has no os.kill)
281 # (on posix only, since win32 has no os.kill)
258 self.pids = []
282 self.pids = []
259
283
260 if sys.platform == 'win32':
284 if sys.platform == 'win32':
261 def _run_cmd(self):
285 def _run_cmd(self):
262 # On Windows, use os.system instead of subprocess.call, because I
286 # On Windows, use os.system instead of subprocess.call, because I
263 # was having problems with subprocess and I just don't know enough
287 # was having problems with subprocess and I just don't know enough
264 # about win32 to debug this reliably. Os.system may be the 'old
288 # about win32 to debug this reliably. Os.system may be the 'old
265 # fashioned' way to do it, but it works just fine. If someone
289 # fashioned' way to do it, but it works just fine. If someone
266 # later can clean this up that's fine, as long as the tests run
290 # later can clean this up that's fine, as long as the tests run
267 # reliably in win32.
291 # reliably in win32.
268 # What types of problems are you having. They may be related to
292 # What types of problems are you having. They may be related to
269 # running Python in unboffered mode. BG.
293 # running Python in unboffered mode. BG.
270 return os.system(' '.join(self.call_args))
294 return os.system(' '.join(self.call_args))
271 else:
295 else:
272 def _run_cmd(self):
296 def _run_cmd(self):
273 # print >> sys.stderr, '*** CMD:', ' '.join(self.call_args) # dbg
297 # print >> sys.stderr, '*** CMD:', ' '.join(self.call_args) # dbg
274 subp = subprocess.Popen(self.call_args)
298 subp = subprocess.Popen(self.call_args)
275 self.pids.append(subp.pid)
299 self.pids.append(subp.pid)
276 # If this fails, the pid will be left in self.pids and cleaned up
300 # If this fails, the pid will be left in self.pids and cleaned up
277 # later, but if the wait call succeeds, then we can clear the
301 # later, but if the wait call succeeds, then we can clear the
278 # stored pid.
302 # stored pid.
279 retcode = subp.wait()
303 retcode = subp.wait()
280 self.pids.pop()
304 self.pids.pop()
281 return retcode
305 return retcode
282
306
283 def run(self):
307 def run(self):
284 """Run the stored commands"""
308 """Run the stored commands"""
285 try:
309 try:
286 return self._run_cmd()
310 return self._run_cmd()
287 except:
311 except:
288 import traceback
312 import traceback
289 traceback.print_exc()
313 traceback.print_exc()
290 return 1 # signal failure
314 return 1 # signal failure
291
315
292 def __del__(self):
316 def __del__(self):
293 """Cleanup on exit by killing any leftover processes."""
317 """Cleanup on exit by killing any leftover processes."""
294
318
295 if not hasattr(os, 'kill'):
319 if not hasattr(os, 'kill'):
296 return
320 return
297
321
298 for pid in self.pids:
322 for pid in self.pids:
299 try:
323 try:
300 print 'Cleaning stale PID:', pid
324 print 'Cleaning stale PID:', pid
301 os.kill(pid, signal.SIGKILL)
325 os.kill(pid, signal.SIGKILL)
302 except OSError:
326 except OSError:
303 # This is just a best effort, if we fail or the process was
327 # This is just a best effort, if we fail or the process was
304 # really gone, ignore it.
328 # really gone, ignore it.
305 pass
329 pass
306
330
307
331
308 def make_runners():
332 def make_runners():
309 """Define the top-level packages that need to be tested.
333 """Define the top-level packages that need to be tested.
310 """
334 """
311
335
312 # Packages to be tested via nose, that only depend on the stdlib
336 # Packages to be tested via nose, that only depend on the stdlib
313 nose_pkg_names = ['config', 'core', 'extensions', 'frontend', 'lib',
337 nose_pkg_names = ['config', 'core', 'extensions', 'frontend', 'lib',
314 'scripts', 'testing', 'utils', 'nbformat' ]
338 'scripts', 'testing', 'utils', 'nbformat' ]
315
339
316 if have['zmq']:
340 if have['zmq']:
317 nose_pkg_names.append('parallel')
341 nose_pkg_names.append('parallel')
318
342
319 # For debugging this code, only load quick stuff
343 # For debugging this code, only load quick stuff
320 #nose_pkg_names = ['core', 'extensions'] # dbg
344 #nose_pkg_names = ['core', 'extensions'] # dbg
321
345
322 # Make fully qualified package names prepending 'IPython.' to our name lists
346 # Make fully qualified package names prepending 'IPython.' to our name lists
323 nose_packages = ['IPython.%s' % m for m in nose_pkg_names ]
347 nose_packages = ['IPython.%s' % m for m in nose_pkg_names ]
324
348
325 # Make runners
349 # Make runners
326 runners = [ (v, IPTester('iptest', params=v)) for v in nose_packages ]
350 runners = [ (v, IPTester('iptest', params=v)) for v in nose_packages ]
327
351
328 return runners
352 return runners
329
353
330
354
331 def run_iptest():
355 def run_iptest():
332 """Run the IPython test suite using nose.
356 """Run the IPython test suite using nose.
333
357
334 This function is called when this script is **not** called with the form
358 This function is called when this script is **not** called with the form
335 `iptest all`. It simply calls nose with appropriate command line flags
359 `iptest all`. It simply calls nose with appropriate command line flags
336 and accepts all of the standard nose arguments.
360 and accepts all of the standard nose arguments.
337 """
361 """
338
362
339 warnings.filterwarnings('ignore',
363 warnings.filterwarnings('ignore',
340 'This will be removed soon. Use IPython.testing.util instead')
364 'This will be removed soon. Use IPython.testing.util instead')
341
365
342 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
366 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
343
367
344 # Loading ipdoctest causes problems with Twisted, but
368 # Loading ipdoctest causes problems with Twisted, but
345 # our test suite runner now separates things and runs
369 # our test suite runner now separates things and runs
346 # all Twisted tests with trial.
370 # all Twisted tests with trial.
347 '--with-ipdoctest',
371 '--with-ipdoctest',
348 '--ipdoctest-tests','--ipdoctest-extension=txt',
372 '--ipdoctest-tests','--ipdoctest-extension=txt',
349
373
350 # We add --exe because of setuptools' imbecility (it
374 # We add --exe because of setuptools' imbecility (it
351 # blindly does chmod +x on ALL files). Nose does the
375 # blindly does chmod +x on ALL files). Nose does the
352 # right thing and it tries to avoid executables,
376 # right thing and it tries to avoid executables,
353 # setuptools unfortunately forces our hand here. This
377 # setuptools unfortunately forces our hand here. This
354 # has been discussed on the distutils list and the
378 # has been discussed on the distutils list and the
355 # setuptools devs refuse to fix this problem!
379 # setuptools devs refuse to fix this problem!
356 '--exe',
380 '--exe',
357 ]
381 ]
358
382
359 if nose.__version__ >= '0.11':
383 if nose.__version__ >= '0.11':
360 # I don't fully understand why we need this one, but depending on what
384 # I don't fully understand why we need this one, but depending on what
361 # directory the test suite is run from, if we don't give it, 0 tests
385 # directory the test suite is run from, if we don't give it, 0 tests
362 # get run. Specifically, if the test suite is run from the source dir
386 # get run. Specifically, if the test suite is run from the source dir
363 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
387 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
364 # even if the same call done in this directory works fine). It appears
388 # even if the same call done in this directory works fine). It appears
365 # that if the requested package is in the current dir, nose bails early
389 # that if the requested package is in the current dir, nose bails early
366 # by default. Since it's otherwise harmless, leave it in by default
390 # by default. Since it's otherwise harmless, leave it in by default
367 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
391 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
368 argv.append('--traverse-namespace')
392 argv.append('--traverse-namespace')
369
393
370 # use our plugin for doctesting. It will remove the standard doctest plugin
394 # use our plugin for doctesting. It will remove the standard doctest plugin
371 # if it finds it enabled
395 # if it finds it enabled
372 plugins = [IPythonDoctest(make_exclude()), KnownFailure()]
396 plugins = [IPythonDoctest(make_exclude()), KnownFailure()]
373 # We need a global ipython running in this process
397 # We need a global ipython running in this process
374 globalipapp.start_ipython()
398 globalipapp.start_ipython()
375 # Now nose can run
399 # Now nose can run
376 TestProgram(argv=argv, addplugins=plugins)
400 TestProgram(argv=argv, addplugins=plugins)
377
401
378
402
379 def run_iptestall():
403 def run_iptestall():
380 """Run the entire IPython test suite by calling nose and trial.
404 """Run the entire IPython test suite by calling nose and trial.
381
405
382 This function constructs :class:`IPTester` instances for all IPython
406 This function constructs :class:`IPTester` instances for all IPython
383 modules and package and then runs each of them. This causes the modules
407 modules and package and then runs each of them. This causes the modules
384 and packages of IPython to be tested each in their own subprocess using
408 and packages of IPython to be tested each in their own subprocess using
385 nose or twisted.trial appropriately.
409 nose or twisted.trial appropriately.
386 """
410 """
387
411
388 runners = make_runners()
412 runners = make_runners()
389
413
390 # Run the test runners in a temporary dir so we can nuke it when finished
414 # Run the test runners in a temporary dir so we can nuke it when finished
391 # to clean up any junk files left over by accident. This also makes it
415 # to clean up any junk files left over by accident. This also makes it
392 # robust against being run in non-writeable directories by mistake, as the
416 # robust against being run in non-writeable directories by mistake, as the
393 # temp dir will always be user-writeable.
417 # temp dir will always be user-writeable.
394 curdir = os.getcwdu()
418 curdir = os.getcwdu()
395 testdir = tempfile.gettempdir()
419 testdir = tempfile.gettempdir()
396 os.chdir(testdir)
420 os.chdir(testdir)
397
421
398 # Run all test runners, tracking execution time
422 # Run all test runners, tracking execution time
399 failed = []
423 failed = []
400 t_start = time.time()
424 t_start = time.time()
401 try:
425 try:
402 for (name, runner) in runners:
426 for (name, runner) in runners:
403 print '*'*70
427 print '*'*70
404 print 'IPython test group:',name
428 print 'IPython test group:',name
405 res = runner.run()
429 res = runner.run()
406 if res:
430 if res:
407 failed.append( (name, runner) )
431 failed.append( (name, runner) )
408 finally:
432 finally:
409 os.chdir(curdir)
433 os.chdir(curdir)
410 t_end = time.time()
434 t_end = time.time()
411 t_tests = t_end - t_start
435 t_tests = t_end - t_start
412 nrunners = len(runners)
436 nrunners = len(runners)
413 nfail = len(failed)
437 nfail = len(failed)
414 # summarize results
438 # summarize results
415 print
439 print
416 print '*'*70
440 print '*'*70
417 print 'Test suite completed for system with the following information:'
441 print 'Test suite completed for system with the following information:'
418 print report()
442 print report()
419 print 'Ran %s test groups in %.3fs' % (nrunners, t_tests)
443 print 'Ran %s test groups in %.3fs' % (nrunners, t_tests)
420 print
444 print
421 print 'Status:'
445 print 'Status:'
422 if not failed:
446 if not failed:
423 print 'OK'
447 print 'OK'
424 else:
448 else:
425 # If anything went wrong, point out what command to rerun manually to
449 # If anything went wrong, point out what command to rerun manually to
426 # see the actual errors and individual summary
450 # see the actual errors and individual summary
427 print 'ERROR - %s out of %s test groups failed.' % (nfail, nrunners)
451 print 'ERROR - %s out of %s test groups failed.' % (nfail, nrunners)
428 for name, failed_runner in failed:
452 for name, failed_runner in failed:
429 print '-'*40
453 print '-'*40
430 print 'Runner failed:',name
454 print 'Runner failed:',name
431 print 'You may wish to rerun this one individually, with:'
455 print 'You may wish to rerun this one individually, with:'
432 print ' '.join(failed_runner.call_args)
456 print ' '.join(failed_runner.call_args)
433 print
457 print
434 # Ensure that our exit code indicates failure
458 # Ensure that our exit code indicates failure
435 sys.exit(1)
459 sys.exit(1)
436
460
437
461
438 def main():
462 def main():
439 for arg in sys.argv[1:]:
463 for arg in sys.argv[1:]:
440 if arg.startswith('IPython'):
464 if arg.startswith('IPython'):
441 # This is in-process
465 # This is in-process
442 run_iptest()
466 run_iptest()
443 else:
467 else:
444 # This starts subprocesses
468 # This starts subprocesses
445 run_iptestall()
469 run_iptestall()
446
470
447
471
448 if __name__ == '__main__':
472 if __name__ == '__main__':
449 main()
473 main()
General Comments 0
You need to be logged in to leave comments. Login now