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