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