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