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