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