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