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