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