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