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