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