##// END OF EJS Templates
Fix config part of the test suite.
Fernando Perez -
Show More
@@ -1,393 +1,393 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) or trial recursively. This
8 calling this script (with different arguments) or trial 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 For now, this script requires that both nose and twisted are installed. This
15 For now, this script requires that both nose and twisted are installed. This
16 will change in the future.
16 will change in the future.
17 """
17 """
18
18
19 from __future__ import absolute_import
19 from __future__ import absolute_import
20
20
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Module imports
22 # Module imports
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24
25 import os
25 import os
26 import os.path as path
26 import os.path as path
27 import signal
27 import signal
28 import sys
28 import sys
29 import subprocess
29 import subprocess
30 import tempfile
30 import tempfile
31 import time
31 import time
32 import warnings
32 import warnings
33
33
34 import nose.plugins.builtin
34 import nose.plugins.builtin
35 from nose.core import TestProgram
35 from nose.core import TestProgram
36
36
37 from IPython.utils import genutils
37 from IPython.utils import genutils
38 from IPython.utils.platutils import find_cmd, FindCmdError
38 from IPython.utils.platutils import find_cmd, FindCmdError
39 from . import globalipapp
39 from . import globalipapp
40 from .plugin.ipdoctest import IPythonDoctest
40 from .plugin.ipdoctest import IPythonDoctest
41
41
42 pjoin = path.join
42 pjoin = path.join
43
43
44 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45 # Warnings control
45 # Warnings control
46 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
47 # Twisted generates annoying warnings with Python 2.6, as will do other code
47 # Twisted generates annoying warnings with Python 2.6, as will do other code
48 # that imports 'sets' as of today
48 # that imports 'sets' as of today
49 warnings.filterwarnings('ignore', 'the sets module is deprecated',
49 warnings.filterwarnings('ignore', 'the sets module is deprecated',
50 DeprecationWarning )
50 DeprecationWarning )
51
51
52 #-----------------------------------------------------------------------------
52 #-----------------------------------------------------------------------------
53 # Logic for skipping doctests
53 # Logic for skipping doctests
54 #-----------------------------------------------------------------------------
54 #-----------------------------------------------------------------------------
55
55
56 def test_for(mod):
56 def test_for(mod):
57 """Test to see if mod is importable."""
57 """Test to see if mod is importable."""
58 try:
58 try:
59 __import__(mod)
59 __import__(mod)
60 except ImportError:
60 except ImportError:
61 return False
61 return False
62 else:
62 else:
63 return True
63 return True
64
64
65 have_curses = test_for('_curses')
65 have_curses = test_for('_curses')
66 have_wx = test_for('wx')
66 have_wx = test_for('wx')
67 have_wx_aui = test_for('wx.aui')
67 have_wx_aui = test_for('wx.aui')
68 have_zi = test_for('zope.interface')
68 have_zi = test_for('zope.interface')
69 have_twisted = test_for('twisted')
69 have_twisted = test_for('twisted')
70 have_foolscap = test_for('foolscap')
70 have_foolscap = test_for('foolscap')
71 have_objc = test_for('objc')
71 have_objc = test_for('objc')
72 have_pexpect = test_for('pexpect')
72 have_pexpect = test_for('pexpect')
73 have_gtk = test_for('gtk')
73 have_gtk = test_for('gtk')
74 have_gobject = test_for('gobject')
74 have_gobject = test_for('gobject')
75
75
76
76
77 def make_exclude():
77 def make_exclude():
78
78
79 # For the IPythonDoctest plugin, we need to exclude certain patterns that
79 # For the IPythonDoctest plugin, we need to exclude certain patterns that
80 # cause testing problems. We should strive to minimize the number of
80 # cause testing problems. We should strive to minimize the number of
81 # skipped modules, since this means untested code. As the testing
81 # skipped modules, since this means untested code. As the testing
82 # machinery solidifies, this list should eventually become empty.
82 # machinery solidifies, this list should eventually become empty.
83
83 # These modules and packages will NOT get scanned by nose at all for tests
84 # Note that these exclusions only mean that the docstrings are not analyzed
85 # for examples to be run as tests, if there are other test functions in
86 # those modules, they do get run.
87 exclusions = [pjoin('IPython', 'external'),
84 exclusions = [pjoin('IPython', 'external'),
88 pjoin('IPython', 'frontend', 'process', 'winprocess.py'),
85 pjoin('IPython', 'frontend', 'process', 'winprocess.py'),
89 pjoin('IPython_doctest_plugin'),
86 pjoin('IPython_doctest_plugin'),
90 pjoin('IPython', 'quarantine'),
87 pjoin('IPython', 'quarantine'),
91 pjoin('IPython', 'deathrow'),
88 pjoin('IPython', 'deathrow'),
92 pjoin('IPython', 'testing', 'attic'),
89 pjoin('IPython', 'testing', 'attic'),
93 pjoin('IPython', 'testing', 'tools'),
90 pjoin('IPython', 'testing', 'tools'),
94 pjoin('IPython', 'testing', 'mkdoctests'),
91 pjoin('IPython', 'testing', 'mkdoctests'),
95 pjoin('IPython', 'lib', 'inputhook')
92 pjoin('IPython', 'lib', 'inputhook'),
96 ]
93 # Config files aren't really importable stand-alone
94 pjoin('IPython', 'config', 'default'),
95 pjoin('IPython', 'config', 'profile'),
96 ]
97
97
98 if not have_wx:
98 if not have_wx:
99 exclusions.append(pjoin('IPython', 'gui'))
99 exclusions.append(pjoin('IPython', 'gui'))
100 exclusions.append(pjoin('IPython', 'frontend', 'wx'))
100 exclusions.append(pjoin('IPython', 'frontend', 'wx'))
101 exclusions.append(pjoin('IPython', 'lib', 'inputhookwx'))
101 exclusions.append(pjoin('IPython', 'lib', 'inputhookwx'))
102
102
103 if not have_gtk or not have_gobject:
103 if not have_gtk or not have_gobject:
104 exclusions.append(pjoin('IPython', 'lib', 'inputhookgtk'))
104 exclusions.append(pjoin('IPython', 'lib', 'inputhookgtk'))
105
105
106 if not have_wx_aui:
106 if not have_wx_aui:
107 exclusions.append(pjoin('IPython', 'gui', 'wx', 'wxIPython'))
107 exclusions.append(pjoin('IPython', 'gui', 'wx', 'wxIPython'))
108
108
109 if not have_objc:
109 if not have_objc:
110 exclusions.append(pjoin('IPython', 'frontend', 'cocoa'))
110 exclusions.append(pjoin('IPython', 'frontend', 'cocoa'))
111
111
112 if not sys.platform == 'win32':
112 if not sys.platform == 'win32':
113 exclusions.append(pjoin('IPython', 'utils', 'platutils_win32'))
113 exclusions.append(pjoin('IPython', 'utils', 'platutils_win32'))
114
114
115 # These have to be skipped on win32 because the use echo, rm, cd, etc.
115 # These have to be skipped on win32 because the use echo, rm, cd, etc.
116 # See ticket https://bugs.launchpad.net/bugs/366982
116 # See ticket https://bugs.launchpad.net/bugs/366982
117 if sys.platform == 'win32':
117 if sys.platform == 'win32':
118 exclusions.append(pjoin('IPython', 'testing', 'plugin', 'test_exampleip'))
118 exclusions.append(pjoin('IPython', 'testing', 'plugin', 'test_exampleip'))
119 exclusions.append(pjoin('IPython', 'testing', 'plugin', 'dtexample'))
119 exclusions.append(pjoin('IPython', 'testing', 'plugin', 'dtexample'))
120
120
121 if not os.name == 'posix':
121 if not os.name == 'posix':
122 exclusions.append(pjoin('IPython', 'utils', 'platutils_posix'))
122 exclusions.append(pjoin('IPython', 'utils', 'platutils_posix'))
123
123
124 if not have_pexpect:
124 if not have_pexpect:
125 exclusions.append(pjoin('IPython', 'scripts', 'irunner'))
125 exclusions.append(pjoin('IPython', 'scripts', 'irunner'))
126
126
127 # This is scary. We still have things in frontend and testing that
127 # This is scary. We still have things in frontend and testing that
128 # are being tested by nose that use twisted. We need to rethink
128 # are being tested by nose that use twisted. We need to rethink
129 # how we are isolating dependencies in testing.
129 # how we are isolating dependencies in testing.
130 if not (have_twisted and have_zi and have_foolscap):
130 if not (have_twisted and have_zi and have_foolscap):
131 exclusions.append(pjoin('IPython', 'frontend', 'asyncfrontendbase'))
131 exclusions.append(pjoin('IPython', 'frontend', 'asyncfrontendbase'))
132 exclusions.append(pjoin('IPython', 'frontend', 'prefilterfrontend'))
132 exclusions.append(pjoin('IPython', 'frontend', 'prefilterfrontend'))
133 exclusions.append(pjoin('IPython', 'frontend', 'frontendbase'))
133 exclusions.append(pjoin('IPython', 'frontend', 'frontendbase'))
134 exclusions.append(pjoin('IPython', 'frontend', 'linefrontendbase'))
134 exclusions.append(pjoin('IPython', 'frontend', 'linefrontendbase'))
135 exclusions.append(pjoin('IPython', 'frontend', 'tests',
135 exclusions.append(pjoin('IPython', 'frontend', 'tests',
136 'test_linefrontend'))
136 'test_linefrontend'))
137 exclusions.append(pjoin('IPython', 'frontend', 'tests',
137 exclusions.append(pjoin('IPython', 'frontend', 'tests',
138 'test_frontendbase'))
138 'test_frontendbase'))
139 exclusions.append(pjoin('IPython', 'frontend', 'tests',
139 exclusions.append(pjoin('IPython', 'frontend', 'tests',
140 'test_prefilterfrontend'))
140 'test_prefilterfrontend'))
141 exclusions.append(pjoin('IPython', 'frontend', 'tests',
141 exclusions.append(pjoin('IPython', 'frontend', 'tests',
142 'test_asyncfrontendbase')),
142 'test_asyncfrontendbase')),
143 exclusions.append(pjoin('IPython', 'testing', 'parametric'))
143 exclusions.append(pjoin('IPython', 'testing', 'parametric'))
144 exclusions.append(pjoin('IPython', 'testing', 'util'))
144 exclusions.append(pjoin('IPython', 'testing', 'util'))
145
145
146 # This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
146 # This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
147 if sys.platform == 'win32':
147 if sys.platform == 'win32':
148 exclusions = [s.replace('\\','\\\\') for s in exclusions]
148 exclusions = [s.replace('\\','\\\\') for s in exclusions]
149
149
150 return exclusions
150 return exclusions
151
151
152
152
153 #-----------------------------------------------------------------------------
153 #-----------------------------------------------------------------------------
154 # Functions and classes
154 # Functions and classes
155 #-----------------------------------------------------------------------------
155 #-----------------------------------------------------------------------------
156
156
157 class IPTester(object):
157 class IPTester(object):
158 """Call that calls iptest or trial in a subprocess.
158 """Call that calls iptest or trial in a subprocess.
159 """
159 """
160 #: string, name of test runner that will be called
160 #: string, name of test runner that will be called
161 runner = None
161 runner = None
162 #: list, parameters for test runner
162 #: list, parameters for test runner
163 params = None
163 params = None
164 #: list, arguments of system call to be made to call test runner
164 #: list, arguments of system call to be made to call test runner
165 call_args = None
165 call_args = None
166 #: list, process ids of subprocesses we start (for cleanup)
166 #: list, process ids of subprocesses we start (for cleanup)
167 pids = None
167 pids = None
168
168
169 def __init__(self,runner='iptest',params=None):
169 def __init__(self,runner='iptest',params=None):
170 """Create new test runner."""
170 """Create new test runner."""
171 if runner == 'iptest':
171 if runner == 'iptest':
172 # Find our own 'iptest' script OS-level entry point
172 # Find our own 'iptest' script OS-level entry point
173 try:
173 try:
174 iptest_path = os.path.abspath(find_cmd('iptest'))
174 iptest_path = os.path.abspath(find_cmd('iptest'))
175 except FindCmdError:
175 except FindCmdError:
176 # Script not installed (may be the case for testing situations
176 # Script not installed (may be the case for testing situations
177 # that are running from a source tree only), pull from internal
177 # that are running from a source tree only), pull from internal
178 # path:
178 # path:
179 iptest_path = pjoin(genutils.get_ipython_package_dir(),
179 iptest_path = pjoin(genutils.get_ipython_package_dir(),
180 'scripts','iptest')
180 'scripts','iptest')
181 self.runner = ['python', iptest_path, '-v']
181 self.runner = ['python', iptest_path, '-v']
182 else:
182 else:
183 self.runner = ['python', os.path.abspath(find_cmd('trial'))]
183 self.runner = ['python', os.path.abspath(find_cmd('trial'))]
184 if params is None:
184 if params is None:
185 params = []
185 params = []
186 if isinstance(params,str):
186 if isinstance(params,str):
187 params = [params]
187 params = [params]
188 self.params = params
188 self.params = params
189
189
190 # Assemble call
190 # Assemble call
191 self.call_args = self.runner+self.params
191 self.call_args = self.runner+self.params
192
192
193 # Store pids of anything we start to clean up on deletion, if possible
193 # Store pids of anything we start to clean up on deletion, if possible
194 # (on posix only, since win32 has no os.kill)
194 # (on posix only, since win32 has no os.kill)
195 self.pids = []
195 self.pids = []
196
196
197 if sys.platform == 'win32':
197 if sys.platform == 'win32':
198 def _run_cmd(self):
198 def _run_cmd(self):
199 # On Windows, use os.system instead of subprocess.call, because I
199 # On Windows, use os.system instead of subprocess.call, because I
200 # was having problems with subprocess and I just don't know enough
200 # was having problems with subprocess and I just don't know enough
201 # about win32 to debug this reliably. Os.system may be the 'old
201 # about win32 to debug this reliably. Os.system may be the 'old
202 # fashioned' way to do it, but it works just fine. If someone
202 # fashioned' way to do it, but it works just fine. If someone
203 # later can clean this up that's fine, as long as the tests run
203 # later can clean this up that's fine, as long as the tests run
204 # reliably in win32.
204 # reliably in win32.
205 return os.system(' '.join(self.call_args))
205 return os.system(' '.join(self.call_args))
206 else:
206 else:
207 def _run_cmd(self):
207 def _run_cmd(self):
208 subp = subprocess.Popen(self.call_args)
208 subp = subprocess.Popen(self.call_args)
209 self.pids.append(subp.pid)
209 self.pids.append(subp.pid)
210 # If this fails, the pid will be left in self.pids and cleaned up
210 # If this fails, the pid will be left in self.pids and cleaned up
211 # later, but if the wait call succeeds, then we can clear the
211 # later, but if the wait call succeeds, then we can clear the
212 # stored pid.
212 # stored pid.
213 retcode = subp.wait()
213 retcode = subp.wait()
214 self.pids.pop()
214 self.pids.pop()
215 return retcode
215 return retcode
216
216
217 def run(self):
217 def run(self):
218 """Run the stored commands"""
218 """Run the stored commands"""
219 try:
219 try:
220 return self._run_cmd()
220 return self._run_cmd()
221 except:
221 except:
222 import traceback
222 import traceback
223 traceback.print_exc()
223 traceback.print_exc()
224 return 1 # signal failure
224 return 1 # signal failure
225
225
226 def __del__(self):
226 def __del__(self):
227 """Cleanup on exit by killing any leftover processes."""
227 """Cleanup on exit by killing any leftover processes."""
228
228
229 if not hasattr(os, 'kill'):
229 if not hasattr(os, 'kill'):
230 return
230 return
231
231
232 for pid in self.pids:
232 for pid in self.pids:
233 try:
233 try:
234 print 'Cleaning stale PID:', pid
234 print 'Cleaning stale PID:', pid
235 os.kill(pid, signal.SIGKILL)
235 os.kill(pid, signal.SIGKILL)
236 except OSError:
236 except OSError:
237 # This is just a best effort, if we fail or the process was
237 # This is just a best effort, if we fail or the process was
238 # really gone, ignore it.
238 # really gone, ignore it.
239 pass
239 pass
240
240
241
241
242 def make_runners():
242 def make_runners():
243 """Define the top-level packages that need to be tested.
243 """Define the top-level packages that need to be tested.
244 """
244 """
245
245
246 nose_packages = ['config', 'core', 'extensions', 'frontend', 'lib',
246 nose_packages = ['config', 'core', 'extensions', 'frontend', 'lib',
247 'scripts', 'testing', 'utils']
247 'scripts', 'testing', 'utils']
248 trial_packages = ['kernel']
248 trial_packages = ['kernel']
249
249
250 if have_wx:
250 if have_wx:
251 nose_packages.append('gui')
251 nose_packages.append('gui')
252
252
253 #nose_packages = ['core'] # dbg
253 #nose_packages = ['core'] # dbg
254 #trial_packages = [] # dbg
254 #trial_packages = [] # dbg
255
255
256 nose_packages = ['IPython.%s' % m for m in nose_packages ]
256 nose_packages = ['IPython.%s' % m for m in nose_packages ]
257 trial_packages = ['IPython.%s' % m for m in trial_packages ]
257 trial_packages = ['IPython.%s' % m for m in trial_packages ]
258
258
259 # Make runners, most with nose
259 # Make runners, most with nose
260 nose_testers = [IPTester(params=v) for v in nose_packages]
260 nose_testers = [IPTester(params=v) for v in nose_packages]
261 runners = dict(zip(nose_packages, nose_testers))
261 runners = dict(zip(nose_packages, nose_testers))
262 # And add twisted ones if conditions are met
262 # And add twisted ones if conditions are met
263 if have_zi and have_twisted and have_foolscap:
263 if have_zi and have_twisted and have_foolscap:
264 trial_testers = [IPTester('trial',params=v) for v in trial_packages]
264 trial_testers = [IPTester('trial',params=v) for v in trial_packages]
265 runners.update(dict(zip(trial_packages,trial_testers)))
265 runners.update(dict(zip(trial_packages,trial_testers)))
266
266
267 return runners
267 return runners
268
268
269
269
270 def run_iptest():
270 def run_iptest():
271 """Run the IPython test suite using nose.
271 """Run the IPython test suite using nose.
272
272
273 This function is called when this script is **not** called with the form
273 This function is called when this script is **not** called with the form
274 `iptest all`. It simply calls nose with appropriate command line flags
274 `iptest all`. It simply calls nose with appropriate command line flags
275 and accepts all of the standard nose arguments.
275 and accepts all of the standard nose arguments.
276 """
276 """
277
277
278 warnings.filterwarnings('ignore',
278 warnings.filterwarnings('ignore',
279 'This will be removed soon. Use IPython.testing.util instead')
279 'This will be removed soon. Use IPython.testing.util instead')
280
280
281 argv = sys.argv + [ '--detailed-errors',
281 argv = sys.argv + [ '--detailed-errors',
282 # Loading ipdoctest causes problems with Twisted, but
282 # Loading ipdoctest causes problems with Twisted, but
283 # our test suite runner now separates things and runs
283 # our test suite runner now separates things and runs
284 # all Twisted tests with trial.
284 # all Twisted tests with trial.
285 '--with-ipdoctest',
285 '--with-ipdoctest',
286 '--ipdoctest-tests','--ipdoctest-extension=txt',
286 '--ipdoctest-tests','--ipdoctest-extension=txt',
287
287
288 #'-x','-s', # dbg
288 #'-x','-s', # dbg
289
289
290 # We add --exe because of setuptools' imbecility (it
290 # We add --exe because of setuptools' imbecility (it
291 # blindly does chmod +x on ALL files). Nose does the
291 # blindly does chmod +x on ALL files). Nose does the
292 # right thing and it tries to avoid executables,
292 # right thing and it tries to avoid executables,
293 # setuptools unfortunately forces our hand here. This
293 # setuptools unfortunately forces our hand here. This
294 # has been discussed on the distutils list and the
294 # has been discussed on the distutils list and the
295 # setuptools devs refuse to fix this problem!
295 # setuptools devs refuse to fix this problem!
296 '--exe',
296 '--exe',
297 ]
297 ]
298
298
299 # Detect if any tests were required by explicitly calling an IPython
299 # Detect if any tests were required by explicitly calling an IPython
300 # submodule or giving a specific path
300 # submodule or giving a specific path
301 has_tests = False
301 has_tests = False
302 for arg in sys.argv:
302 for arg in sys.argv:
303 if 'IPython' in arg or arg.endswith('.py') or \
303 if 'IPython' in arg or arg.endswith('.py') or \
304 (':' in arg and '.py' in arg):
304 (':' in arg and '.py' in arg):
305 has_tests = True
305 has_tests = True
306 break
306 break
307
307
308 # If nothing was specifically requested, test full IPython
308 # If nothing was specifically requested, test full IPython
309 if not has_tests:
309 if not has_tests:
310 argv.append('IPython')
310 argv.append('IPython')
311
311
312 ## # Construct list of plugins, omitting the existing doctest plugin, which
312 ## # Construct list of plugins, omitting the existing doctest plugin, which
313 ## # ours replaces (and extends).
313 ## # ours replaces (and extends).
314 plugins = [IPythonDoctest(make_exclude())]
314 plugins = [IPythonDoctest(make_exclude())]
315 for p in nose.plugins.builtin.plugins:
315 for p in nose.plugins.builtin.plugins:
316 plug = p()
316 plug = p()
317 if plug.name == 'doctest':
317 if plug.name == 'doctest':
318 continue
318 continue
319 plugins.append(plug)
319 plugins.append(plug)
320
320
321 # We need a global ipython running in this process
321 # We need a global ipython running in this process
322 globalipapp.start_ipython()
322 globalipapp.start_ipython()
323 # Now nose can run
323 # Now nose can run
324 TestProgram(argv=argv,plugins=plugins)
324 TestProgram(argv=argv,plugins=plugins)
325
325
326
326
327 def run_iptestall():
327 def run_iptestall():
328 """Run the entire IPython test suite by calling nose and trial.
328 """Run the entire IPython test suite by calling nose and trial.
329
329
330 This function constructs :class:`IPTester` instances for all IPython
330 This function constructs :class:`IPTester` instances for all IPython
331 modules and package and then runs each of them. This causes the modules
331 modules and package and then runs each of them. This causes the modules
332 and packages of IPython to be tested each in their own subprocess using
332 and packages of IPython to be tested each in their own subprocess using
333 nose or twisted.trial appropriately.
333 nose or twisted.trial appropriately.
334 """
334 """
335
335
336 runners = make_runners()
336 runners = make_runners()
337
337
338 # Run the test runners in a temporary dir so we can nuke it when finished
338 # Run the test runners in a temporary dir so we can nuke it when finished
339 # to clean up any junk files left over by accident. This also makes it
339 # to clean up any junk files left over by accident. This also makes it
340 # robust against being run in non-writeable directories by mistake, as the
340 # robust against being run in non-writeable directories by mistake, as the
341 # temp dir will always be user-writeable.
341 # temp dir will always be user-writeable.
342 curdir = os.getcwd()
342 curdir = os.getcwd()
343 testdir = tempfile.gettempdir()
343 testdir = tempfile.gettempdir()
344 os.chdir(testdir)
344 os.chdir(testdir)
345
345
346 # Run all test runners, tracking execution time
346 # Run all test runners, tracking execution time
347 failed = {}
347 failed = {}
348 t_start = time.time()
348 t_start = time.time()
349 try:
349 try:
350 for name,runner in runners.iteritems():
350 for name,runner in runners.iteritems():
351 print '*'*77
351 print '*'*77
352 print 'IPython test group:',name
352 print 'IPython test group:',name
353 res = runner.run()
353 res = runner.run()
354 if res:
354 if res:
355 failed[name] = res
355 failed[name] = res
356 finally:
356 finally:
357 os.chdir(curdir)
357 os.chdir(curdir)
358 t_end = time.time()
358 t_end = time.time()
359 t_tests = t_end - t_start
359 t_tests = t_end - t_start
360 nrunners = len(runners)
360 nrunners = len(runners)
361 nfail = len(failed)
361 nfail = len(failed)
362 # summarize results
362 # summarize results
363 print
363 print
364 print '*'*77
364 print '*'*77
365 print 'Ran %s test groups in %.3fs' % (nrunners, t_tests)
365 print 'Ran %s test groups in %.3fs' % (nrunners, t_tests)
366 print
366 print
367 if not failed:
367 if not failed:
368 print 'OK'
368 print 'OK'
369 else:
369 else:
370 # If anything went wrong, point out what command to rerun manually to
370 # If anything went wrong, point out what command to rerun manually to
371 # see the actual errors and individual summary
371 # see the actual errors and individual summary
372 print 'ERROR - %s out of %s test groups failed.' % (nfail, nrunners)
372 print 'ERROR - %s out of %s test groups failed.' % (nfail, nrunners)
373 for name in failed:
373 for name in failed:
374 failed_runner = runners[name]
374 failed_runner = runners[name]
375 print '-'*40
375 print '-'*40
376 print 'Runner failed:',name
376 print 'Runner failed:',name
377 print 'You may wish to rerun this one individually, with:'
377 print 'You may wish to rerun this one individually, with:'
378 print ' '.join(failed_runner.call_args)
378 print ' '.join(failed_runner.call_args)
379 print
379 print
380
380
381
381
382 def main():
382 def main():
383 if len(sys.argv) == 1:
383 if len(sys.argv) == 1:
384 run_iptestall()
384 run_iptestall()
385 else:
385 else:
386 if sys.argv[1] == 'all':
386 if sys.argv[1] == 'all':
387 run_iptestall()
387 run_iptestall()
388 else:
388 else:
389 run_iptest()
389 run_iptest()
390
390
391
391
392 if __name__ == '__main__':
392 if __name__ == '__main__':
393 main()
393 main()
General Comments 0
You need to be logged in to leave comments. Login now