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