##// END OF EJS Templates
parallel is slowest of all, moving it to the front...
Paul Ivanov -
Show More
@@ -1,664 +1,664 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) 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 """
16 16
17 17 #-----------------------------------------------------------------------------
18 18 # Copyright (C) 2009-2011 The IPython Development Team
19 19 #
20 20 # Distributed under the terms of the BSD License. The full license is in
21 21 # the file COPYING, distributed as part of this software.
22 22 #-----------------------------------------------------------------------------
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Imports
26 26 #-----------------------------------------------------------------------------
27 27 from __future__ import print_function
28 28
29 29 # Stdlib
30 30 import glob
31 31 import os
32 32 import os.path as path
33 33 import signal
34 34 import sys
35 35 import subprocess
36 36 import tempfile
37 37 import time
38 38 import warnings
39 39 import multiprocessing.pool
40 40
41 41 # Now, proceed to import nose itself
42 42 import nose.plugins.builtin
43 43 from nose.plugins.xunit import Xunit
44 44 from nose import SkipTest
45 45 from nose.core import TestProgram
46 46
47 47 # Our own imports
48 48 from IPython.utils import py3compat
49 49 from IPython.utils.importstring import import_item
50 50 from IPython.utils.path import get_ipython_module_path, get_ipython_package_dir
51 51 from IPython.utils.process import pycmd2argv
52 52 from IPython.utils.sysinfo import sys_info
53 53 from IPython.utils.tempdir import TemporaryDirectory
54 54 from IPython.utils.warn import warn
55 55
56 56 from IPython.testing import globalipapp
57 57 from IPython.testing.plugin.ipdoctest import IPythonDoctest
58 58 from IPython.external.decorators import KnownFailure, knownfailureif
59 59
60 60 pjoin = path.join
61 61
62 62
63 63 #-----------------------------------------------------------------------------
64 64 # Globals
65 65 #-----------------------------------------------------------------------------
66 66
67 67
68 68 #-----------------------------------------------------------------------------
69 69 # Warnings control
70 70 #-----------------------------------------------------------------------------
71 71
72 72 # Twisted generates annoying warnings with Python 2.6, as will do other code
73 73 # that imports 'sets' as of today
74 74 warnings.filterwarnings('ignore', 'the sets module is deprecated',
75 75 DeprecationWarning )
76 76
77 77 # This one also comes from Twisted
78 78 warnings.filterwarnings('ignore', 'the sha module is deprecated',
79 79 DeprecationWarning)
80 80
81 81 # Wx on Fedora11 spits these out
82 82 warnings.filterwarnings('ignore', 'wxPython/wxWidgets release number mismatch',
83 83 UserWarning)
84 84
85 85 # ------------------------------------------------------------------------------
86 86 # Monkeypatch Xunit to count known failures as skipped.
87 87 # ------------------------------------------------------------------------------
88 88 def monkeypatch_xunit():
89 89 try:
90 90 knownfailureif(True)(lambda: None)()
91 91 except Exception as e:
92 92 KnownFailureTest = type(e)
93 93
94 94 def addError(self, test, err, capt=None):
95 95 if issubclass(err[0], KnownFailureTest):
96 96 err = (SkipTest,) + err[1:]
97 97 return self.orig_addError(test, err, capt)
98 98
99 99 Xunit.orig_addError = Xunit.addError
100 100 Xunit.addError = addError
101 101
102 102 #-----------------------------------------------------------------------------
103 103 # Logic for skipping doctests
104 104 #-----------------------------------------------------------------------------
105 105 def extract_version(mod):
106 106 return mod.__version__
107 107
108 108 def test_for(item, min_version=None, callback=extract_version):
109 109 """Test to see if item is importable, and optionally check against a minimum
110 110 version.
111 111
112 112 If min_version is given, the default behavior is to check against the
113 113 `__version__` attribute of the item, but specifying `callback` allows you to
114 114 extract the value you are interested in. e.g::
115 115
116 116 In [1]: import sys
117 117
118 118 In [2]: from IPython.testing.iptest import test_for
119 119
120 120 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
121 121 Out[3]: True
122 122
123 123 """
124 124 try:
125 125 check = import_item(item)
126 126 except (ImportError, RuntimeError):
127 127 # GTK reports Runtime error if it can't be initialized even if it's
128 128 # importable.
129 129 return False
130 130 else:
131 131 if min_version:
132 132 if callback:
133 133 # extra processing step to get version to compare
134 134 check = callback(check)
135 135
136 136 return check >= min_version
137 137 else:
138 138 return True
139 139
140 140 # Global dict where we can store information on what we have and what we don't
141 141 # have available at test run time
142 142 have = {}
143 143
144 144 have['curses'] = test_for('_curses')
145 145 have['matplotlib'] = test_for('matplotlib')
146 146 have['numpy'] = test_for('numpy')
147 147 have['pexpect'] = test_for('IPython.external.pexpect')
148 148 have['pymongo'] = test_for('pymongo')
149 149 have['pygments'] = test_for('pygments')
150 150 have['qt'] = test_for('IPython.external.qt')
151 151 have['rpy2'] = test_for('rpy2')
152 152 have['sqlite3'] = test_for('sqlite3')
153 153 have['cython'] = test_for('Cython')
154 154 have['oct2py'] = test_for('oct2py')
155 155 have['tornado'] = test_for('tornado.version_info', (2,1,0), callback=None)
156 156 have['jinja2'] = test_for('jinja2')
157 157 have['wx'] = test_for('wx')
158 158 have['wx.aui'] = test_for('wx.aui')
159 159 have['azure'] = test_for('azure')
160 160 have['sphinx'] = test_for('sphinx')
161 161
162 162 min_zmq = (2,1,11)
163 163
164 164 have['zmq'] = test_for('zmq.pyzmq_version_info', min_zmq, callback=lambda x: x())
165 165
166 166 #-----------------------------------------------------------------------------
167 167 # Functions and classes
168 168 #-----------------------------------------------------------------------------
169 169
170 170 def report():
171 171 """Return a string with a summary report of test-related variables."""
172 172
173 173 out = [ sys_info(), '\n']
174 174
175 175 avail = []
176 176 not_avail = []
177 177
178 178 for k, is_avail in have.items():
179 179 if is_avail:
180 180 avail.append(k)
181 181 else:
182 182 not_avail.append(k)
183 183
184 184 if avail:
185 185 out.append('\nTools and libraries available at test time:\n')
186 186 avail.sort()
187 187 out.append(' ' + ' '.join(avail)+'\n')
188 188
189 189 if not_avail:
190 190 out.append('\nTools and libraries NOT available at test time:\n')
191 191 not_avail.sort()
192 192 out.append(' ' + ' '.join(not_avail)+'\n')
193 193
194 194 return ''.join(out)
195 195
196 196
197 197 def make_exclude():
198 198 """Make patterns of modules and packages to exclude from testing.
199 199
200 200 For the IPythonDoctest plugin, we need to exclude certain patterns that
201 201 cause testing problems. We should strive to minimize the number of
202 202 skipped modules, since this means untested code.
203 203
204 204 These modules and packages will NOT get scanned by nose at all for tests.
205 205 """
206 206 # Simple utility to make IPython paths more readably, we need a lot of
207 207 # these below
208 208 ipjoin = lambda *paths: pjoin('IPython', *paths)
209 209
210 210 exclusions = [ipjoin('external'),
211 211 ipjoin('quarantine'),
212 212 ipjoin('deathrow'),
213 213 # This guy is probably attic material
214 214 ipjoin('testing', 'mkdoctests'),
215 215 # Testing inputhook will need a lot of thought, to figure out
216 216 # how to have tests that don't lock up with the gui event
217 217 # loops in the picture
218 218 ipjoin('lib', 'inputhook'),
219 219 # Config files aren't really importable stand-alone
220 220 ipjoin('config', 'profile'),
221 221 # The notebook 'static' directory contains JS, css and other
222 222 # files for web serving. Occasionally projects may put a .py
223 223 # file in there (MathJax ships a conf.py), so we might as
224 224 # well play it safe and skip the whole thing.
225 225 ipjoin('html', 'static'),
226 226 ipjoin('html', 'fabfile'),
227 227 ]
228 228 if not have['sqlite3']:
229 229 exclusions.append(ipjoin('core', 'tests', 'test_history'))
230 230 exclusions.append(ipjoin('core', 'history'))
231 231 if not have['wx']:
232 232 exclusions.append(ipjoin('lib', 'inputhookwx'))
233 233
234 234 if 'IPython.kernel.inprocess' not in sys.argv:
235 235 exclusions.append(ipjoin('kernel', 'inprocess'))
236 236
237 237 # FIXME: temporarily disable autoreload tests, as they can produce
238 238 # spurious failures in subsequent tests (cythonmagic).
239 239 exclusions.append(ipjoin('extensions', 'autoreload'))
240 240 exclusions.append(ipjoin('extensions', 'tests', 'test_autoreload'))
241 241
242 242 # We do this unconditionally, so that the test suite doesn't import
243 243 # gtk, changing the default encoding and masking some unicode bugs.
244 244 exclusions.append(ipjoin('lib', 'inputhookgtk'))
245 245 exclusions.append(ipjoin('kernel', 'zmq', 'gui', 'gtkembed'))
246 246
247 247 #Also done unconditionally, exclude nbconvert directories containing
248 248 #config files used to test. Executing the config files with iptest would
249 249 #cause an exception.
250 250 exclusions.append(ipjoin('nbconvert', 'tests', 'files'))
251 251 exclusions.append(ipjoin('nbconvert', 'exporters', 'tests', 'files'))
252 252
253 253 # These have to be skipped on win32 because the use echo, rm, cd, etc.
254 254 # See ticket https://github.com/ipython/ipython/issues/87
255 255 if sys.platform == 'win32':
256 256 exclusions.append(ipjoin('testing', 'plugin', 'test_exampleip'))
257 257 exclusions.append(ipjoin('testing', 'plugin', 'dtexample'))
258 258
259 259 if not have['pexpect']:
260 260 exclusions.extend([ipjoin('lib', 'irunner'),
261 261 ipjoin('lib', 'tests', 'test_irunner'),
262 262 ipjoin('terminal', 'console'),
263 263 ])
264 264
265 265 if not have['zmq']:
266 266 exclusions.append(ipjoin('lib', 'kernel'))
267 267 exclusions.append(ipjoin('kernel'))
268 268 exclusions.append(ipjoin('qt'))
269 269 exclusions.append(ipjoin('html'))
270 270 exclusions.append(ipjoin('consoleapp.py'))
271 271 exclusions.append(ipjoin('terminal', 'console'))
272 272 exclusions.append(ipjoin('parallel'))
273 273 elif not have['qt'] or not have['pygments']:
274 274 exclusions.append(ipjoin('qt'))
275 275
276 276 if not have['pymongo']:
277 277 exclusions.append(ipjoin('parallel', 'controller', 'mongodb'))
278 278 exclusions.append(ipjoin('parallel', 'tests', 'test_mongodb'))
279 279
280 280 if not have['matplotlib']:
281 281 exclusions.extend([ipjoin('core', 'pylabtools'),
282 282 ipjoin('core', 'tests', 'test_pylabtools'),
283 283 ipjoin('kernel', 'zmq', 'pylab'),
284 284 ])
285 285
286 286 if not have['cython']:
287 287 exclusions.extend([ipjoin('extensions', 'cythonmagic')])
288 288 exclusions.extend([ipjoin('extensions', 'tests', 'test_cythonmagic')])
289 289
290 290 if not have['oct2py']:
291 291 exclusions.extend([ipjoin('extensions', 'octavemagic')])
292 292 exclusions.extend([ipjoin('extensions', 'tests', 'test_octavemagic')])
293 293
294 294 if not have['tornado']:
295 295 exclusions.append(ipjoin('html'))
296 296
297 297 if not have['jinja2']:
298 298 exclusions.append(ipjoin('html', 'notebookapp'))
299 299
300 300 if not have['rpy2'] or not have['numpy']:
301 301 exclusions.append(ipjoin('extensions', 'rmagic'))
302 302 exclusions.append(ipjoin('extensions', 'tests', 'test_rmagic'))
303 303
304 304 if not have['azure']:
305 305 exclusions.append(ipjoin('html', 'services', 'notebooks', 'azurenbmanager'))
306 306
307 307 if not all((have['pygments'], have['jinja2'], have['sphinx'])):
308 308 exclusions.append(ipjoin('nbconvert'))
309 309
310 310 # This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
311 311 if sys.platform == 'win32':
312 312 exclusions = [s.replace('\\','\\\\') for s in exclusions]
313 313
314 314 # check for any exclusions that don't seem to exist:
315 315 parent, _ = os.path.split(get_ipython_package_dir())
316 316 for exclusion in exclusions:
317 317 if exclusion.endswith(('deathrow', 'quarantine')):
318 318 # ignore deathrow/quarantine, which exist in dev, but not install
319 319 continue
320 320 fullpath = pjoin(parent, exclusion)
321 321 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
322 322 warn("Excluding nonexistent file: %r" % exclusion)
323 323
324 324 return exclusions
325 325
326 326
327 327 class IPTester(object):
328 328 """Call that calls iptest or trial in a subprocess.
329 329 """
330 330 #: string, name of test runner that will be called
331 331 runner = None
332 332 #: list, parameters for test runner
333 333 params = None
334 334 #: list, arguments of system call to be made to call test runner
335 335 call_args = None
336 336 #: list, subprocesses we start (for cleanup)
337 337 processes = None
338 338 #: str, coverage xml output file
339 339 coverage_xml = None
340 340 buffer_output = False
341 341
342 342 def __init__(self, runner='iptest', params=None):
343 343 """Create new test runner."""
344 344 p = os.path
345 345 if runner == 'iptest':
346 346 iptest_app = os.path.abspath(get_ipython_module_path('IPython.testing.iptest'))
347 347 self.runner = pycmd2argv(iptest_app) + sys.argv[1:]
348 348 else:
349 349 raise Exception('Not a valid test runner: %s' % repr(runner))
350 350 if params is None:
351 351 params = []
352 352 if isinstance(params, str):
353 353 params = [params]
354 354 self.params = params
355 355
356 356 # Assemble call
357 357 self.call_args = self.runner+self.params
358 358
359 359 # Find the section we're testing (IPython.foo)
360 360 for sect in self.params:
361 361 if sect.startswith('IPython') or sect in special_test_suites: break
362 362 else:
363 363 raise ValueError("Section not found", self.params)
364 364
365 365 if '--with-xunit' in self.call_args:
366 366
367 367 self.call_args.append('--xunit-file')
368 368 # FIXME: when Windows uses subprocess.call, these extra quotes are unnecessary:
369 369 xunit_file = path.abspath(sect+'.xunit.xml')
370 370 if sys.platform == 'win32':
371 371 xunit_file = '"%s"' % xunit_file
372 372 self.call_args.append(xunit_file)
373 373
374 374 if '--with-xml-coverage' in self.call_args:
375 375 self.coverage_xml = path.abspath(sect+".coverage.xml")
376 376 self.call_args.remove('--with-xml-coverage')
377 377 self.call_args = ["coverage", "run", "--source="+sect] + self.call_args[1:]
378 378
379 379 # Store anything we start to clean up on deletion
380 380 self.processes = []
381 381
382 382 def _run_cmd(self):
383 383 with TemporaryDirectory() as IPYTHONDIR:
384 384 env = os.environ.copy()
385 385 env['IPYTHONDIR'] = IPYTHONDIR
386 386 # print >> sys.stderr, '*** CMD:', ' '.join(self.call_args) # dbg
387 387 output = subprocess.PIPE if self.buffer_output else None
388 388 subp = subprocess.Popen(self.call_args, stdout=output,
389 389 stderr=output, env=env)
390 390 self.processes.append(subp)
391 391 # If this fails, the process will be left in self.processes and
392 392 # cleaned up later, but if the wait call succeeds, then we can
393 393 # clear the stored process.
394 394 retcode = subp.wait()
395 395 self.processes.pop()
396 396 self.stdout = subp.stdout
397 397 self.stderr = subp.stderr
398 398 return retcode
399 399
400 400 def run(self):
401 401 """Run the stored commands"""
402 402 try:
403 403 retcode = self._run_cmd()
404 404 #print(self.stdout.read())
405 405 #print("std err")
406 406 #print(self.stderr.read())
407 407 except KeyboardInterrupt:
408 408 return -signal.SIGINT
409 409 except:
410 410 import traceback
411 411 traceback.print_exc()
412 412 return 1 # signal failure
413 413
414 414 if self.coverage_xml:
415 415 subprocess.call(["coverage", "xml", "-o", self.coverage_xml])
416 416 return retcode
417 417
418 418 def __del__(self):
419 419 """Cleanup on exit by killing any leftover processes."""
420 420 for subp in self.processes:
421 421 if subp.poll() is not None:
422 422 continue # process is already dead
423 423
424 424 try:
425 425 print('Cleaning up stale PID: %d' % subp.pid)
426 426 subp.kill()
427 427 except: # (OSError, WindowsError) ?
428 428 # This is just a best effort, if we fail or the process was
429 429 # really gone, ignore it.
430 430 pass
431 431 else:
432 432 for i in range(10):
433 433 if subp.poll() is None:
434 434 time.sleep(0.1)
435 435 else:
436 436 break
437 437
438 438 if subp.poll() is None:
439 439 # The process did not die...
440 440 print('... failed. Manual cleanup may be required.')
441 441
442 442
443 443 special_test_suites = {
444 444 'autoreload': ['IPython.extensions.autoreload', 'IPython.extensions.tests.test_autoreload'],
445 445 }
446 446
447 447 def make_runners(inc_slow=False):
448 448 """Define the top-level packages that need to be tested.
449 449 """
450 450
451 451 # Packages to be tested via nose, that only depend on the stdlib
452 452 nose_pkg_names = ['config', 'core', 'extensions', 'lib', 'terminal',
453 453 'testing', 'utils', 'nbformat']
454 454
455 455 if have['qt']:
456 456 nose_pkg_names.append('qt')
457 457
458 458 if have['tornado']:
459 459 nose_pkg_names.append('html')
460 460
461 461 if have['zmq']:
462 462 nose_pkg_names.insert(0, 'kernel')
463 463 nose_pkg_names.insert(1, 'kernel.inprocess')
464 464 if inc_slow:
465 nose_pkg_names.append('parallel')
465 nose_pkg_names.insert(0, 'parallel')
466 466
467 467 if all((have['pygments'], have['jinja2'], have['sphinx'])):
468 468 nose_pkg_names.append('nbconvert')
469 469
470 470 # For debugging this code, only load quick stuff
471 471 #nose_pkg_names = ['core', 'extensions'] # dbg
472 472
473 473 # Make fully qualified package names prepending 'IPython.' to our name lists
474 474 nose_packages = ['IPython.%s' % m for m in nose_pkg_names ]
475 475
476 476 # Make runners
477 477 runners = [ (v, IPTester('iptest', params=v)) for v in nose_packages ]
478 478
479 479 for name in special_test_suites:
480 480 runners.append((name, IPTester('iptest', params=name)))
481 481
482 482 return runners
483 483
484 484
485 485 def run_iptest():
486 486 """Run the IPython test suite using nose.
487 487
488 488 This function is called when this script is **not** called with the form
489 489 `iptest all`. It simply calls nose with appropriate command line flags
490 490 and accepts all of the standard nose arguments.
491 491 """
492 492 # Apply our monkeypatch to Xunit
493 493 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
494 494 monkeypatch_xunit()
495 495
496 496 warnings.filterwarnings('ignore',
497 497 'This will be removed soon. Use IPython.testing.util instead')
498 498
499 499 if sys.argv[1] in special_test_suites:
500 500 sys.argv[1:2] = special_test_suites[sys.argv[1]]
501 501 special_suite = True
502 502 else:
503 503 special_suite = False
504 504
505 505 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
506 506
507 507 '--with-ipdoctest',
508 508 '--ipdoctest-tests','--ipdoctest-extension=txt',
509 509
510 510 # We add --exe because of setuptools' imbecility (it
511 511 # blindly does chmod +x on ALL files). Nose does the
512 512 # right thing and it tries to avoid executables,
513 513 # setuptools unfortunately forces our hand here. This
514 514 # has been discussed on the distutils list and the
515 515 # setuptools devs refuse to fix this problem!
516 516 '--exe',
517 517 ]
518 518 if '-a' not in argv and '-A' not in argv:
519 519 argv = argv + ['-a', '!crash']
520 520
521 521 if nose.__version__ >= '0.11':
522 522 # I don't fully understand why we need this one, but depending on what
523 523 # directory the test suite is run from, if we don't give it, 0 tests
524 524 # get run. Specifically, if the test suite is run from the source dir
525 525 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
526 526 # even if the same call done in this directory works fine). It appears
527 527 # that if the requested package is in the current dir, nose bails early
528 528 # by default. Since it's otherwise harmless, leave it in by default
529 529 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
530 530 argv.append('--traverse-namespace')
531 531
532 532 # use our plugin for doctesting. It will remove the standard doctest plugin
533 533 # if it finds it enabled
534 534 ipdt = IPythonDoctest() if special_suite else IPythonDoctest(make_exclude())
535 535 plugins = [ipdt, KnownFailure()]
536 536
537 537 # We need a global ipython running in this process, but the special
538 538 # in-process group spawns its own IPython kernels, so for *that* group we
539 539 # must avoid also opening the global one (otherwise there's a conflict of
540 540 # singletons). Ultimately the solution to this problem is to refactor our
541 541 # assumptions about what needs to be a singleton and what doesn't (app
542 542 # objects should, individual shells shouldn't). But for now, this
543 543 # workaround allows the test suite for the inprocess module to complete.
544 544 if not 'IPython.kernel.inprocess' in sys.argv:
545 545 globalipapp.start_ipython()
546 546
547 547 # Now nose can run
548 548 TestProgram(argv=argv, addplugins=plugins)
549 549
550 550 def do_run(x):
551 551 print('IPython test group:',x[0])
552 552 ret = x[1].run()
553 553 return ret
554 554
555 555 def run_iptestall(inc_slow=False, fast=False):
556 556 """Run the entire IPython test suite by calling nose and trial.
557 557
558 558 This function constructs :class:`IPTester` instances for all IPython
559 559 modules and package and then runs each of them. This causes the modules
560 560 and packages of IPython to be tested each in their own subprocess using
561 561 nose.
562 562
563 563 Parameters
564 564 ----------
565 565
566 566 inc_slow : bool, optional
567 567 Include slow tests, like IPython.parallel. By default, these tests aren't
568 568 run.
569 569
570 570 fast : bool, option
571 571 Run the test suite in parallel, if True, using as many threads as there
572 572 are processors
573 573 """
574 574 if fast:
575 575 p = multiprocessing.pool.ThreadPool()
576 576 else:
577 577 p = multiprocessing.pool.ThreadPool(1)
578 578
579 579 runners = make_runners(inc_slow=inc_slow)
580 580
581 581 # Run the test runners in a temporary dir so we can nuke it when finished
582 582 # to clean up any junk files left over by accident. This also makes it
583 583 # robust against being run in non-writeable directories by mistake, as the
584 584 # temp dir will always be user-writeable.
585 585 curdir = os.getcwdu()
586 586 testdir = tempfile.gettempdir()
587 587 os.chdir(testdir)
588 588
589 589 # Run all test runners, tracking execution time
590 590 failed = []
591 591 t_start = time.time()
592 592
593 593 #runners = runners[::-1]
594 594
595 595 print([r[0] for r in runners])
596 596
597 597 try:
598 598
599 599 print(len(runners))
600 600 all_res = p.map(do_run, runners)
601 601 print('*'*70)
602 602 for ((name, runner), res) in zip(runners, all_res):
603 603 print(' '*70)
604 604 tgroup = 'IPython test group: ' + name
605 605 res_string = 'OK' if res == 0 else 'FAILED'
606 606 res_string = res_string.rjust(70 - len(tgroup), '.')
607 607 print(tgroup + res_string)
608 608 if res:
609 609 failed.append( (name, runner) )
610 610 if res == -signal.SIGINT:
611 611 print("Interrupted")
612 612 break
613 613 finally:
614 614 os.chdir(curdir)
615 615 t_end = time.time()
616 616 t_tests = t_end - t_start
617 617 nrunners = len(runners)
618 618 nfail = len(failed)
619 619 # summarize results
620 620 print()
621 621 print('*'*70)
622 622 print('Test suite completed for system with the following information:')
623 623 print(report())
624 624 print('Ran %s test groups in %.3fs' % (nrunners, t_tests))
625 625 print()
626 626 print('Status:')
627 627 if not failed:
628 628 print('OK')
629 629 else:
630 630 # If anything went wrong, point out what command to rerun manually to
631 631 # see the actual errors and individual summary
632 632 print('ERROR - %s out of %s test groups failed.' % (nfail, nrunners))
633 633 for name, failed_runner in failed:
634 634 print('-'*40)
635 635 print('Runner failed:',name)
636 636 print('You may wish to rerun this one individually, with:')
637 637 failed_call_args = [py3compat.cast_unicode(x) for x in failed_runner.call_args]
638 638 print(u' '.join(failed_call_args))
639 639 print()
640 640 # Ensure that our exit code indicates failure
641 641 sys.exit(1)
642 642
643 643
644 644 def main():
645 645 for arg in sys.argv[1:]:
646 646 if arg.startswith('IPython') or arg in special_test_suites:
647 647 # This is in-process
648 648 run_iptest()
649 649 else:
650 650 inc_slow = "--all" in sys.argv
651 651 if inc_slow:
652 652 sys.argv.remove("--all")
653 653
654 654 fast = "--fast" in sys.argv
655 655 if fast:
656 656 sys.argv.remove("--fast")
657 657 IPTester.buffer_output = True
658 658
659 659 # This starts subprocesses
660 660 run_iptestall(inc_slow=inc_slow, fast=fast)
661 661
662 662
663 663 if __name__ == '__main__':
664 664 main()
General Comments 0
You need to be logged in to leave comments. Login now