##// END OF EJS Templates
Unify entry points for iptest
Thomas Kluyver -
Show More
@@ -130,20 +130,27 b' class PyTestController(TestController):'
130
130
131 @property
131 @property
132 def will_run(self):
132 def will_run(self):
133 return test_sections[self.section].will_run
133 try:
134 return test_sections[self.section].will_run
135 except KeyError:
136 return True
134
137
135 def add_xunit(self):
138 def add_xunit(self):
136 xunit_file = os.path.abspath(self.section + '.xunit.xml')
139 xunit_file = os.path.abspath(self.section + '.xunit.xml')
137 self.cmd.extend(['--with-xunit', '--xunit-file', xunit_file])
140 self.cmd.extend(['--with-xunit', '--xunit-file', xunit_file])
138
141
139 def add_coverage(self):
142 def add_coverage(self):
143 try:
144 sources = test_sections[self.section].includes
145 except KeyError:
146 sources = ['IPython']
147
140 coverage_rc = ("[run]\n"
148 coverage_rc = ("[run]\n"
141 "data_file = {data_file}\n"
149 "data_file = {data_file}\n"
142 "source =\n"
150 "source =\n"
143 " {source}\n"
151 " {source}\n"
144 ).format(data_file=os.path.abspath('.coverage.'+self.section),
152 ).format(data_file=os.path.abspath('.coverage.'+self.section),
145 source="\n ".join(test_sections[self.section].includes))
153 source="\n ".join(sources))
146
147 config_file = os.path.join(self.workingdir.name, '.coveragerc')
154 config_file = os.path.join(self.workingdir.name, '.coveragerc')
148 with open(config_file, 'w') as f:
155 with open(config_file, 'w') as f:
149 f.write(coverage_rc)
156 f.write(coverage_rc)
@@ -156,7 +163,7 b' class PyTestController(TestController):'
156 super(PyTestController, self).launch()
163 super(PyTestController, self).launch()
157
164
158
165
159 def prepare_py_test_controllers(inc_slow=False, xunit=False, coverage=False):
166 def prepare_py_test_controllers(inc_slow=False):
160 """Returns an ordered list of PyTestController instances to be run."""
167 """Returns an ordered list of PyTestController instances to be run."""
161 to_run, not_run = [], []
168 to_run, not_run = [], []
162 if not inc_slow:
169 if not inc_slow:
@@ -164,16 +171,20 b' def prepare_py_test_controllers(inc_slow=False, xunit=False, coverage=False):'
164
171
165 for name in test_group_names:
172 for name in test_group_names:
166 controller = PyTestController(name)
173 controller = PyTestController(name)
167 if xunit:
168 controller.add_xunit()
169 if coverage:
170 controller.add_coverage()
171 if controller.will_run:
174 if controller.will_run:
172 to_run.append(controller)
175 to_run.append(controller)
173 else:
176 else:
174 not_run.append(controller)
177 not_run.append(controller)
175 return to_run, not_run
178 return to_run, not_run
176
179
180 def configure_controllers(controllers, xunit=False, coverage=False):
181 """Apply options for a collection of TestController objects."""
182 for controller in controllers:
183 if xunit:
184 controller.add_xunit()
185 if coverage:
186 controller.add_coverage()
187
177 def do_run(controller):
188 def do_run(controller):
178 try:
189 try:
179 try:
190 try:
@@ -217,7 +228,7 b' def report():'
217
228
218 return ''.join(out)
229 return ''.join(out)
219
230
220 def run_iptestall(inc_slow=False, jobs=1, xunit_out=False, coverage_out=False):
231 def run_iptestall(options):
221 """Run the entire IPython test suite by calling nose and trial.
232 """Run the entire IPython test suite by calling nose and trial.
222
233
223 This function constructs :class:`IPTester` instances for all IPython
234 This function constructs :class:`IPTester` instances for all IPython
@@ -227,20 +238,39 b' def run_iptestall(inc_slow=False, jobs=1, xunit_out=False, coverage_out=False):'
227
238
228 Parameters
239 Parameters
229 ----------
240 ----------
230
241
231 inc_slow : bool, optional
242 All parameters are passed as attributes of the options object.
243
244 testgroups : list of str
245 Run only these sections of the test suite. If empty, run all the available
246 sections.
247
248 fast : int or None
249 Run the test suite in parallel, using n simultaneous processes. If None
250 is passed, one process is used per CPU core. Default 1 (i.e. sequential)
251
252 inc_slow : bool
232 Include slow tests, like IPython.parallel. By default, these tests aren't
253 Include slow tests, like IPython.parallel. By default, these tests aren't
233 run.
254 run.
234
255
235 fast : bool, option
256 xunit : bool
236 Run the test suite in parallel, if True, using as many threads as there
257 Produce Xunit XML output. This is written to multiple foo.xunit.xml files.
237 are processors
258
259 coverage : bool or str
260 Measure code coverage from tests. True will store the raw coverage data,
261 or pass 'html' or 'xml' to get reports.
238 """
262 """
239 if jobs != 1:
263 if options.fast != 1:
264 # If running in parallel, capture output so it doesn't get interleaved
240 TestController.buffer_output = True
265 TestController.buffer_output = True
241
266
242 to_run, not_run = prepare_py_test_controllers(inc_slow=inc_slow, xunit=xunit_out,
267 if options.testgroups:
243 coverage=coverage_out)
268 to_run = [PyTestController(name) for name in options.testgroups]
269 not_run = []
270 else:
271 to_run, not_run = prepare_py_test_controllers(inc_slow=options.all)
272
273 configure_controllers(to_run, xunit=options.xunit, coverage=options.coverage)
244
274
245 def justify(ltext, rtext, width=70, fill='-'):
275 def justify(ltext, rtext, width=70, fill='-'):
246 ltext += ' '
276 ltext += ' '
@@ -251,8 +281,9 b' def run_iptestall(inc_slow=False, jobs=1, xunit_out=False, coverage_out=False):'
251 failed = []
281 failed = []
252 t_start = time.time()
282 t_start = time.time()
253
283
254 print('*'*70)
284 print()
255 if jobs == 1:
285 if options.fast == 1:
286 # This actually means sequential, i.e. with 1 job
256 for controller in to_run:
287 for controller in to_run:
257 print('IPython test group:', controller.section)
288 print('IPython test group:', controller.section)
258 controller, res = do_run(controller)
289 controller, res = do_run(controller)
@@ -264,8 +295,9 b' def run_iptestall(inc_slow=False, jobs=1, xunit_out=False, coverage_out=False):'
264 print()
295 print()
265
296
266 else:
297 else:
298 # Run tests concurrently
267 try:
299 try:
268 pool = multiprocessing.pool.ThreadPool(jobs)
300 pool = multiprocessing.pool.ThreadPool(options.fast)
269 for (controller, res) in pool.imap_unordered(do_run, to_run):
301 for (controller, res) in pool.imap_unordered(do_run, to_run):
270 res_string = 'OK' if res == 0 else 'FAILED'
302 res_string = 'OK' if res == 0 else 'FAILED'
271 print(justify('IPython test group: ' + controller.section, res_string))
303 print(justify('IPython test group: ' + controller.section, res_string))
@@ -286,33 +318,33 b' def run_iptestall(inc_slow=False, jobs=1, xunit_out=False, coverage_out=False):'
286 nrunners = len(to_run)
318 nrunners = len(to_run)
287 nfail = len(failed)
319 nfail = len(failed)
288 # summarize results
320 # summarize results
289 print('*'*70)
321 print('_'*70)
290 print('Test suite completed for system with the following information:')
322 print('Test suite completed for system with the following information:')
291 print(report())
323 print(report())
292 print('Ran %s test groups in %.3fs' % (nrunners, t_tests))
324 print('Ran %s test groups in %.3fs' % (nrunners, t_tests))
293 print()
325 print()
294 print('Status:')
326 print('Status: ', end='')
295 if not failed:
327 if not failed:
296 print('OK')
328 print('OK')
297 else:
329 else:
298 # If anything went wrong, point out what command to rerun manually to
330 # If anything went wrong, point out what command to rerun manually to
299 # see the actual errors and individual summary
331 # see the actual errors and individual summary
300 print('ERROR - %s out of %s test groups failed.' % (nfail, nrunners))
332 failed_sections = [c.section for c in failed]
301 for controller in failed:
333 print('ERROR - {} out of {} test groups failed ({}).'.format(nfail,
302 print('-'*40)
334 nrunners, ', '.join(failed_sections)))
303 print('Runner failed:', controller.section)
335 print()
304 print('You may wish to rerun this one individually, with:')
336 print('You may wish to rerun these, with:')
305 print(' iptest', *controller.cmd[3:])
337 print(' iptest', *failed_sections)
306 print()
338 print()
307
339
308 if coverage_out:
340 if options.coverage:
309 from coverage import coverage
341 from coverage import coverage
310 cov = coverage(data_file='.coverage')
342 cov = coverage(data_file='.coverage')
311 cov.combine()
343 cov.combine()
312 cov.save()
344 cov.save()
313
345
314 # Coverage HTML report
346 # Coverage HTML report
315 if coverage_out == 'html':
347 if options.coverage == 'html':
316 html_dir = 'ipy_htmlcov'
348 html_dir = 'ipy_htmlcov'
317 shutil.rmtree(html_dir, ignore_errors=True)
349 shutil.rmtree(html_dir, ignore_errors=True)
318 print("Writing HTML coverage report to %s/ ... " % html_dir, end="")
350 print("Writing HTML coverage report to %s/ ... " % html_dir, end="")
@@ -340,7 +372,7 b' def run_iptestall(inc_slow=False, jobs=1, xunit_out=False, coverage_out=False):'
340 print('done.')
372 print('done.')
341
373
342 # Coverage XML report
374 # Coverage XML report
343 elif coverage_out == 'xml':
375 elif options.coverage == 'xml':
344 cov.xml_report(outfile='ipy_coverage.xml')
376 cov.xml_report(outfile='ipy_coverage.xml')
345
377
346 if failed:
378 if failed:
@@ -349,14 +381,10 b' def run_iptestall(inc_slow=False, jobs=1, xunit_out=False, coverage_out=False):'
349
381
350
382
351 def main():
383 def main():
352 if len(sys.argv) > 1 and \
353 ((sys.argv[1] in test_sections) or sys.argv[1].startswith('IPython')):
354 from .iptest import run_iptest
355 # This is in-process
356 run_iptest()
357 return
358
359 parser = argparse.ArgumentParser(description='Run IPython test suite')
384 parser = argparse.ArgumentParser(description='Run IPython test suite')
385 parser.add_argument('testgroups', nargs='*',
386 help='Run specified groups of tests. If omitted, run '
387 'all tests.')
360 parser.add_argument('--all', action='store_true',
388 parser.add_argument('--all', action='store_true',
361 help='Include slow tests not run by default.')
389 help='Include slow tests not run by default.')
362 parser.add_argument('-j', '--fast', nargs='?', const=None, default=1,
390 parser.add_argument('-j', '--fast', nargs='?', const=None, default=1,
@@ -370,13 +398,12 b' def main():'
370 options = parser.parse_args()
398 options = parser.parse_args()
371
399
372 try:
400 try:
373 jobs = int(options.fast)
401 options.fast = int(options.fast)
374 except TypeError:
402 except TypeError:
375 jobs = options.fast
403 pass
376
404
377 # This starts subprocesses
405 # This starts subprocesses
378 run_iptestall(inc_slow=options.all, jobs=jobs,
406 run_iptestall(options)
379 xunit_out=options.xunit, coverage_out=options.coverage)
380
407
381
408
382 if __name__ == '__main__':
409 if __name__ == '__main__':
General Comments 0
You need to be logged in to leave comments. Login now