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