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