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