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