##// END OF EJS Templates
Fixing bugs with the testing system.
Administrator -
Show More
@@ -1,265 +1,282 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """IPython Test Suite Runner.
2 """IPython Test Suite Runner.
3
3
4 This module provides a main entry point to a user script to test IPython
4 This module provides a main entry point to a user script to test IPython
5 itself from the command line. There are two ways of running this script:
5 itself from the command line. There are two ways of running this script:
6
6
7 1. With the syntax `iptest all`. This runs our entire test suite by
7 1. With the syntax `iptest all`. This runs our entire test suite by
8 calling this script (with different arguments) or trial recursively. This
8 calling this script (with different arguments) or trial recursively. This
9 causes modules and package to be tested in different processes, using nose
9 causes modules and package to be tested in different processes, using nose
10 or trial where appropriate.
10 or trial where appropriate.
11 2. With the regular nose syntax, like `iptest -vvs IPython`. In this form
11 2. With the regular nose syntax, like `iptest -vvs IPython`. In this form
12 the script simply calls nose, but with special command line flags and
12 the script simply calls nose, but with special command line flags and
13 plugins loaded.
13 plugins loaded.
14
14
15 For now, this script requires that both nose and twisted are installed. This
15 For now, this script requires that both nose and twisted are installed. This
16 will change in the future.
16 will change in the future.
17 """
17 """
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Module imports
20 # Module imports
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 import os
23 import os
24 import os.path as path
24 import os.path as path
25 import sys
25 import sys
26 import subprocess
26 import subprocess
27 import time
27 import time
28 import warnings
28 import warnings
29
29
30 import nose.plugins.builtin
30 import nose.plugins.builtin
31 from nose.core import TestProgram
31 from nose.core import TestProgram
32
32
33 from IPython.platutils import find_cmd
33 from IPython.platutils import find_cmd
34 from IPython.testing.plugin.ipdoctest import IPythonDoctest
34 from IPython.testing.plugin.ipdoctest import IPythonDoctest
35
35
36 pjoin = path.join
36 pjoin = path.join
37
37
38 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
39 # Globals and constants
39 # Logic for skipping doctests
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41
41
42 def test_for(mod):
43 """Test to see if mod is importable."""
44 try:
45 __import__(mod)
46 except ImportError:
47 return False
48 else:
49 return True
50
51 have_curses = test_for('_curses')
52 have_wx = test_for('wx')
53 have_zi = test_for('zope.interface')
54 have_twisted = test_for('twisted')
55 have_foolscap = test_for('foolscap')
56 have_objc = test_for('objc')
57 have_pexpect = test_for('pexpect')
58
42 # For the IPythonDoctest plugin, we need to exclude certain patterns that cause
59 # For the IPythonDoctest plugin, we need to exclude certain patterns that cause
43 # testing problems. We should strive to minimize the number of skipped
60 # testing problems. We should strive to minimize the number of skipped
44 # modules, since this means untested code. As the testing machinery
61 # modules, since this means untested code. As the testing machinery
45 # solidifies, this list should eventually become empty.
62 # solidifies, this list should eventually become empty.
46 EXCLUDE = [pjoin('IPython', 'external'),
63 EXCLUDE = [pjoin('IPython', 'external'),
47 # This skip is duplicated below XXX
48 pjoin('IPython', 'platutils_win32'),
49 pjoin('IPython', 'frontend', 'cocoa'),
50 pjoin('IPython', 'frontend', 'process', 'winprocess.py'),
64 pjoin('IPython', 'frontend', 'process', 'winprocess.py'),
51 pjoin('IPython_doctest_plugin'),
65 pjoin('IPython_doctest_plugin'),
52 pjoin('IPython', 'Gnuplot'),
66 pjoin('IPython', 'Gnuplot'),
53 pjoin('IPython', 'Extensions', 'ipy_'),
67 pjoin('IPython', 'Extensions', 'ipy_'),
54 pjoin('IPython', 'Extensions', 'clearcmd'),
68 pjoin('IPython', 'Extensions', 'clearcmd'),
55 pjoin('IPython', 'Extensions', 'PhysicalQInteractive'),
69 pjoin('IPython', 'Extensions', 'PhysicalQInteractive'),
56 pjoin('IPython', 'Extensions', 'scitedirector'),
70 pjoin('IPython', 'Extensions', 'scitedirector'),
57 pjoin('IPython', 'Extensions', 'numeric_formats'),
71 pjoin('IPython', 'Extensions', 'numeric_formats'),
58 pjoin('IPython', 'testing', 'attic'),
72 pjoin('IPython', 'testing', 'attic'),
73 pjoin('IPython', 'testing', 'tutils')
59 ]
74 ]
60
75
61 try:
76 if not have_wx:
62 import wx
63 except ImportError:
64 EXCLUDE.append(pjoin('IPython', 'Extensions', 'igrid'))
77 EXCLUDE.append(pjoin('IPython', 'Extensions', 'igrid'))
78 EXCLUDE.append(pjoin('IPython', 'gui'))
79 EXCLUDE.append(pjoin('IPython', 'frontend', 'wx'))
65
80
66 try:
81 if not have_objc:
67 import _curses
82 EXCLUDE.append(pjoin('IPython', 'frontend', 'cocoa'))
68 except ImportError:
83
84 if not have_curses:
69 EXCLUDE.append(pjoin('IPython', 'Extensions', 'ibrowse'))
85 EXCLUDE.append(pjoin('IPython', 'Extensions', 'ibrowse'))
70
86
87 if not sys.platform == 'win32':
88 EXCLUDE.append(pjoin('IPython', 'platutils_win32'))
89
90 if not os.name == 'posix':
91 EXCLUDE.append(pjoin('IPython', 'platutils_posix'))
92
93 if not have_pexpect:
94 EXCLUDE.append(pjoin('IPython', 'irunner'))
71
95
72 # This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
96 # This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
73 if sys.platform == 'win32':
97 if sys.platform == 'win32':
74 EXCLUDE = [s.replace('\\','\\\\') for s in EXCLUDE]
98 EXCLUDE = [s.replace('\\','\\\\') for s in EXCLUDE]
75
99
76
100
77 #-----------------------------------------------------------------------------
101 #-----------------------------------------------------------------------------
78 # Functions and classes
102 # Functions and classes
79 #-----------------------------------------------------------------------------
103 #-----------------------------------------------------------------------------
80
104
81 def run_iptest():
105 def run_iptest():
82 """Run the IPython test suite using nose.
106 """Run the IPython test suite using nose.
83
107
84 This function is called when this script is **not** called with the form
108 This function is called when this script is **not** called with the form
85 `iptest all`. It simply calls nose with appropriate command line flags
109 `iptest all`. It simply calls nose with appropriate command line flags
86 and accepts all of the standard nose arguments.
110 and accepts all of the standard nose arguments.
87 """
111 """
88
112
89 warnings.filterwarnings('ignore',
113 warnings.filterwarnings('ignore',
90 'This will be removed soon. Use IPython.testing.util instead')
114 'This will be removed soon. Use IPython.testing.util instead')
91
115
92 argv = sys.argv + [
116 argv = sys.argv + [
93 # Loading ipdoctest causes problems with Twisted.
117 # Loading ipdoctest causes problems with Twisted.
94 # I am removing this as a temporary fix to get the
118 # I am removing this as a temporary fix to get the
95 # test suite back into working shape. Our nose
119 # test suite back into working shape. Our nose
96 # plugin needs to be gone through with a fine
120 # plugin needs to be gone through with a fine
97 # toothed comb to find what is causing the problem.
121 # toothed comb to find what is causing the problem.
98 '--with-ipdoctest',
122 '--with-ipdoctest',
99 '--ipdoctest-tests','--ipdoctest-extension=txt',
123 '--ipdoctest-tests','--ipdoctest-extension=txt',
100 '--detailed-errors',
124 '--detailed-errors',
101
125
102 # We add --exe because of setuptools' imbecility (it
126 # We add --exe because of setuptools' imbecility (it
103 # blindly does chmod +x on ALL files). Nose does the
127 # blindly does chmod +x on ALL files). Nose does the
104 # right thing and it tries to avoid executables,
128 # right thing and it tries to avoid executables,
105 # setuptools unfortunately forces our hand here. This
129 # setuptools unfortunately forces our hand here. This
106 # has been discussed on the distutils list and the
130 # has been discussed on the distutils list and the
107 # setuptools devs refuse to fix this problem!
131 # setuptools devs refuse to fix this problem!
108 '--exe',
132 '--exe',
109 ]
133 ]
110
134
111 # Detect if any tests were required by explicitly calling an IPython
135 # Detect if any tests were required by explicitly calling an IPython
112 # submodule or giving a specific path
136 # submodule or giving a specific path
113 has_tests = False
137 has_tests = False
114 for arg in sys.argv:
138 for arg in sys.argv:
115 if 'IPython' in arg or arg.endswith('.py') or \
139 if 'IPython' in arg or arg.endswith('.py') or \
116 (':' in arg and '.py' in arg):
140 (':' in arg and '.py' in arg):
117 has_tests = True
141 has_tests = True
118 break
142 break
119
143
120 # If nothing was specifically requested, test full IPython
144 # If nothing was specifically requested, test full IPython
121 if not has_tests:
145 if not has_tests:
122 argv.append('IPython')
146 argv.append('IPython')
123
147
124 # Construct list of plugins, omitting the existing doctest plugin, which
148 # Construct list of plugins, omitting the existing doctest plugin, which
125 # ours replaces (and extends).
149 # ours replaces (and extends).
126 plugins = [IPythonDoctest(EXCLUDE)]
150 plugins = [IPythonDoctest(EXCLUDE)]
127 for p in nose.plugins.builtin.plugins:
151 for p in nose.plugins.builtin.plugins:
128 plug = p()
152 plug = p()
129 if plug.name == 'doctest':
153 if plug.name == 'doctest':
130 continue
154 continue
131
155
132 #print '*** adding plugin:',plug.name # dbg
156 #print '*** adding plugin:',plug.name # dbg
133 plugins.append(plug)
157 plugins.append(plug)
134
158
135 TestProgram(argv=argv,plugins=plugins)
159 TestProgram(argv=argv,plugins=plugins)
136
160
137
161
138 class IPTester(object):
162 class IPTester(object):
139 """Call that calls iptest or trial in a subprocess.
163 """Call that calls iptest or trial in a subprocess.
140 """
164 """
141 def __init__(self,runner='iptest',params=None):
165 def __init__(self,runner='iptest',params=None):
142 """ """
166 """ """
143 if runner == 'iptest':
167 if runner == 'iptest':
144 self.runner = ['iptest','-v']
168 self.runner = ['iptest','-v']
145 else:
169 else:
146 self.runner = [find_cmd('trial')]
170 self.runner = [find_cmd('trial')]
147 if params is None:
171 if params is None:
148 params = []
172 params = []
149 if isinstance(params,str):
173 if isinstance(params,str):
150 params = [params]
174 params = [params]
151 self.params = params
175 self.params = params
152
176
153 # Assemble call
177 # Assemble call
154 self.call_args = self.runner+self.params
178 self.call_args = self.runner+self.params
155
179
156 def run(self):
180 def run(self):
157 """Run the stored commands"""
181 """Run the stored commands"""
158 return subprocess.call(self.call_args)
182 return subprocess.call(self.call_args)
159
183
160
184
161 def make_runners():
185 def make_runners():
162 """Define the modules and packages that need to be tested.
186 """Define the modules and packages that need to be tested.
163 """
187 """
164
188
165 # This omits additional top-level modules that should not be doctested.
189 # This omits additional top-level modules that should not be doctested.
166 # XXX: Shell.py is also ommited because of a bug in the skip_doctest
190 # XXX: Shell.py is also ommited because of a bug in the skip_doctest
167 # decorator. See ticket https://bugs.launchpad.net/bugs/366209
191 # decorator. See ticket https://bugs.launchpad.net/bugs/366209
168 top_mod = \
192 top_mod = \
169 ['background_jobs.py', 'ColorANSI.py', 'completer.py', 'ConfigLoader.py',
193 ['background_jobs.py', 'ColorANSI.py', 'completer.py', 'ConfigLoader.py',
170 'CrashHandler.py', 'Debugger.py', 'deep_reload.py', 'demo.py',
194 'CrashHandler.py', 'Debugger.py', 'deep_reload.py', 'demo.py',
171 'DPyGetOpt.py', 'dtutils.py', 'excolors.py', 'FakeModule.py',
195 'DPyGetOpt.py', 'dtutils.py', 'excolors.py', 'FakeModule.py',
172 'generics.py', 'genutils.py', 'history.py', 'hooks.py', 'ipapi.py',
196 'generics.py', 'genutils.py', 'history.py', 'hooks.py', 'ipapi.py',
173 'iplib.py', 'ipmaker.py', 'ipstruct.py', 'irunner.py', 'Itpl.py',
197 'iplib.py', 'ipmaker.py', 'ipstruct.py', 'Itpl.py',
174 'Logger.py', 'macro.py', 'Magic.py', 'OInspect.py',
198 'Logger.py', 'macro.py', 'Magic.py', 'OInspect.py',
175 'OutputTrap.py', 'platutils.py', 'prefilter.py', 'Prompts.py',
199 'OutputTrap.py', 'platutils.py', 'prefilter.py', 'Prompts.py',
176 'PyColorize.py', 'Release.py', 'rlineimpl.py', 'shadowns.py',
200 'PyColorize.py', 'Release.py', 'rlineimpl.py', 'shadowns.py',
177 'shellglobals.py', 'strdispatch.py', 'twshell.py',
201 'shellglobals.py', 'strdispatch.py', 'twshell.py',
178 'ultraTB.py', 'upgrade_dir.py', 'usage.py', 'wildcard.py',
202 'ultraTB.py', 'upgrade_dir.py', 'usage.py', 'wildcard.py',
179 # See note above for why this is skipped
203 # See note above for why this is skipped
180 # 'Shell.py',
204 # 'Shell.py',
181 'winconsole.py']
205 'winconsole.py']
182
206
183 if os.name == 'posix':
207 if have_pexpect:
184 top_mod.append('platutils_posix.py')
208 top_mod.append('irunner.py')
185 elif sys.platform == 'win32':
186 top_mod.append('platutils_win32.py')
187 else:
188 top_mod.append('platutils_dummy.py')
189
209
190 # These are tested by nose, so skip IPython.kernel
210 # These are tested by nose, so skip IPython.kernel
191 top_pack = ['config','Extensions','frontend','gui',
211 top_pack = ['config','Extensions','frontend',
192 'testing','tests','tools','UserConfig']
212 'testing','tests','tools','UserConfig']
193
213
214 if have_wx:
215 top_pack.append('gui')
216
194 modules = ['IPython.%s' % m[:-3] for m in top_mod ]
217 modules = ['IPython.%s' % m[:-3] for m in top_mod ]
195 packages = ['IPython.%s' % m for m in top_pack ]
218 packages = ['IPython.%s' % m for m in top_pack ]
196
219
197 # Make runners
220 # Make runners
198 runners = dict(zip(top_pack, [IPTester(params=v) for v in packages]))
221 runners = dict(zip(top_pack, [IPTester(params=v) for v in packages]))
199
222
200 # Test IPython.kernel using trial if twisted is installed
223 # Test IPython.kernel using trial if twisted is installed
201 try:
224 if have_zi and have_twisted and have_foolscap:
202 import zope.interface
203 import twisted
204 import foolscap
205 except ImportError:
206 pass
207 else:
208 runners['trial'] = IPTester('trial',['IPython'])
225 runners['trial'] = IPTester('trial',['IPython'])
209
226
210 runners['modules'] = IPTester(params=modules)
227 runners['modules'] = IPTester(params=modules)
211
228
212 return runners
229 return runners
213
230
214
231
215 def run_iptestall():
232 def run_iptestall():
216 """Run the entire IPython test suite by calling nose and trial.
233 """Run the entire IPython test suite by calling nose and trial.
217
234
218 This function constructs :class:`IPTester` instances for all IPython
235 This function constructs :class:`IPTester` instances for all IPython
219 modules and package and then runs each of them. This causes the modules
236 modules and package and then runs each of them. This causes the modules
220 and packages of IPython to be tested each in their own subprocess using
237 and packages of IPython to be tested each in their own subprocess using
221 nose or twisted.trial appropriately.
238 nose or twisted.trial appropriately.
222 """
239 """
223 runners = make_runners()
240 runners = make_runners()
224 # Run all test runners, tracking execution time
241 # Run all test runners, tracking execution time
225 failed = {}
242 failed = {}
226 t_start = time.time()
243 t_start = time.time()
227 for name,runner in runners.iteritems():
244 for name,runner in runners.iteritems():
228 print '*'*77
245 print '*'*77
229 print 'IPython test set:',name
246 print 'IPython test set:',name
230 res = runner.run()
247 res = runner.run()
231 if res:
248 if res:
232 failed[name] = res
249 failed[name] = res
233 t_end = time.time()
250 t_end = time.time()
234 t_tests = t_end - t_start
251 t_tests = t_end - t_start
235 nrunners = len(runners)
252 nrunners = len(runners)
236 nfail = len(failed)
253 nfail = len(failed)
237 # summarize results
254 # summarize results
238 print
255 print
239 print '*'*77
256 print '*'*77
240 print 'Ran %s test sets in %.3fs' % (nrunners, t_tests)
257 print 'Ran %s test sets in %.3fs' % (nrunners, t_tests)
241 print
258 print
242 if not failed:
259 if not failed:
243 print 'OK'
260 print 'OK'
244 else:
261 else:
245 # If anything went wrong, point out what command to rerun manually to
262 # If anything went wrong, point out what command to rerun manually to
246 # see the actual errors and individual summary
263 # see the actual errors and individual summary
247 print 'ERROR - %s out of %s test sets failed.' % (nfail, nrunners)
264 print 'ERROR - %s out of %s test sets failed.' % (nfail, nrunners)
248 for name in failed:
265 for name in failed:
249 failed_runner = runners[name]
266 failed_runner = runners[name]
250 print '-'*40
267 print '-'*40
251 print 'Runner failed:',name
268 print 'Runner failed:',name
252 print 'You may wish to rerun this one individually, with:'
269 print 'You may wish to rerun this one individually, with:'
253 print ' '.join(failed_runner.call_args)
270 print ' '.join(failed_runner.call_args)
254 print
271 print
255
272
256
273
257 def main():
274 def main():
258 if sys.argv[1] == 'all':
275 if sys.argv[1] == 'all':
259 run_iptestall()
276 run_iptestall()
260 else:
277 else:
261 run_iptest()
278 run_iptest()
262
279
263
280
264 if __name__ == '__main__':
281 if __name__ == '__main__':
265 main() No newline at end of file
282 main()
1 NO CONTENT: modified file
NO CONTENT: modified file
General Comments 0
You need to be logged in to leave comments. Login now