##// END OF EJS Templates
Merge pull request #12362 from Carreau/pytest-ref...
Matthias Bussonnier -
r25806:94b7db20 merge
parent child Browse files
Show More
@@ -1,181 +1,183 b''
1 # encoding: utf-8
2 1 """Tests for IPython.utils.path.py"""
3
4 2 # Copyright (c) IPython Development Team.
5 3 # Distributed under the terms of the Modified BSD License.
4
5 from contextlib import contextmanager
6 6 from unittest.mock import patch
7
7 8 import nose.tools as nt
9 import pytest
8 10
9 11 from IPython.lib import latextools
10 12 from IPython.testing.decorators import onlyif_cmds_exist, skipif_not_matplotlib
11 13 from IPython.utils.process import FindCmdError
12 14
13 15
14 def test_latex_to_png_dvipng_fails_when_no_cmd():
15 """
16 `latex_to_png_dvipng` should return None when there is no required command
17 """
18 for command in ['latex', 'dvipng']:
19 yield (check_latex_to_png_dvipng_fails_when_no_cmd, command)
20
21
22 def check_latex_to_png_dvipng_fails_when_no_cmd(command):
16 @pytest.mark.parametrize('command', ['latex', 'dvipng'])
17 def test_check_latex_to_png_dvipng_fails_when_no_cmd(command):
23 18 def mock_find_cmd(arg):
24 19 if arg == command:
25 20 raise FindCmdError
26 21
27 22 with patch.object(latextools, "find_cmd", mock_find_cmd):
28 nt.assert_equal(latextools.latex_to_png_dvipng("whatever", True),
29 None)
23 assert latextools.latex_to_png_dvipng("whatever", True) == None
30 24
31 25
32 26 @onlyif_cmds_exist('latex', 'dvipng')
33 27 def test_latex_to_png_dvipng_runs():
34 28 """
35 29 Test that latex_to_png_dvipng just runs without error.
36 30 """
37 31 def mock_kpsewhich(filename):
38 nt.assert_equal(filename, "breqn.sty")
32 assert filename == "breqn.sty"
39 33 return None
40 34
41 35 for (s, wrap) in [(u"$$x^2$$", False), (u"x^2", True)]:
42 36 yield (latextools.latex_to_png_dvipng, s, wrap)
43 37
44 38 with patch.object(latextools, "kpsewhich", mock_kpsewhich):
45 39 yield (latextools.latex_to_png_dvipng, s, wrap)
46 40
47 @skipif_not_matplotlib
48 def test_latex_to_png_mpl_runs():
49 """
50 Test that latex_to_png_mpl just runs without error.
51 """
41
42 @contextmanager
43 def no_op(*args, **kwargs):
44 yield
45
52 46 def mock_kpsewhich(filename):
53 nt.assert_equal(filename, "breqn.sty")
47 assert filename == "breqn.sty"
54 48 return None
55 49
56 for (s, wrap) in [("$x^2$", False), ("x^2", True)]:
57 yield (latextools.latex_to_png_mpl, s, wrap)
58
50 @contextmanager
51 def patch_latextool():
59 52 with patch.object(latextools, "kpsewhich", mock_kpsewhich):
60 yield (latextools.latex_to_png_mpl, s, wrap)
53 yield
54
55 @pytest.mark.parametrize('context', [no_op, patch_latextool])
56 @pytest.mark.parametrize('s_wrap', [("$x^2$", False), ("x^2", True)])
57 def test_latex_to_png_mpl_runs(s_wrap, context):
58 """
59 Test that latex_to_png_mpl just runs without error.
60 """
61 try:
62 import matplotbli
63 except ImportError:
64 pytest.skip("This needs matplotlib to be availlable")
65 return
66 s, wrap = s_wrap
67 with context():
68 latextools.latex_to_png_mpl(s, wrap)
61 69
62 70 @skipif_not_matplotlib
63 71 def test_latex_to_html():
64 72 img = latextools.latex_to_html("$x^2$")
65 nt.assert_in("data:image/png;base64,iVBOR", img)
73 assert "data:image/png;base64,iVBOR" in img
66 74
67 75
68 76 def test_genelatex_no_wrap():
69 77 """
70 78 Test genelatex with wrap=False.
71 79 """
72 80 def mock_kpsewhich(filename):
73 81 assert False, ("kpsewhich should not be called "
74 82 "(called with {0})".format(filename))
75 83
76 84 with patch.object(latextools, "kpsewhich", mock_kpsewhich):
77 nt.assert_equal(
78 '\n'.join(latextools.genelatex("body text", False)),
79 r'''\documentclass{article}
85 assert '\n'.join(latextools.genelatex("body text", False)) == r'''\documentclass{article}
80 86 \usepackage{amsmath}
81 87 \usepackage{amsthm}
82 88 \usepackage{amssymb}
83 89 \usepackage{bm}
84 90 \pagestyle{empty}
85 91 \begin{document}
86 92 body text
87 \end{document}''')
93 \end{document}'''
88 94
89 95
90 96 def test_genelatex_wrap_with_breqn():
91 97 """
92 98 Test genelatex with wrap=True for the case breqn.sty is installed.
93 99 """
94 100 def mock_kpsewhich(filename):
95 nt.assert_equal(filename, "breqn.sty")
101 assert filename == "breqn.sty"
96 102 return "path/to/breqn.sty"
97 103
98 104 with patch.object(latextools, "kpsewhich", mock_kpsewhich):
99 nt.assert_equal(
100 '\n'.join(latextools.genelatex("x^2", True)),
101 r'''\documentclass{article}
105 assert '\n'.join(latextools.genelatex("x^2", True)) == r'''\documentclass{article}
102 106 \usepackage{amsmath}
103 107 \usepackage{amsthm}
104 108 \usepackage{amssymb}
105 109 \usepackage{bm}
106 110 \usepackage{breqn}
107 111 \pagestyle{empty}
108 112 \begin{document}
109 113 \begin{dmath*}
110 114 x^2
111 115 \end{dmath*}
112 \end{document}''')
116 \end{document}'''
113 117
114 118
115 119 def test_genelatex_wrap_without_breqn():
116 120 """
117 121 Test genelatex with wrap=True for the case breqn.sty is not installed.
118 122 """
119 123 def mock_kpsewhich(filename):
120 nt.assert_equal(filename, "breqn.sty")
124 assert filename == "breqn.sty"
121 125 return None
122 126
123 127 with patch.object(latextools, "kpsewhich", mock_kpsewhich):
124 nt.assert_equal(
125 '\n'.join(latextools.genelatex("x^2", True)),
126 r'''\documentclass{article}
128 assert '\n'.join(latextools.genelatex("x^2", True)) == r'''\documentclass{article}
127 129 \usepackage{amsmath}
128 130 \usepackage{amsthm}
129 131 \usepackage{amssymb}
130 132 \usepackage{bm}
131 133 \pagestyle{empty}
132 134 \begin{document}
133 135 $$x^2$$
134 \end{document}''')
136 \end{document}'''
135 137
136 138
137 139 @skipif_not_matplotlib
138 140 @onlyif_cmds_exist('latex', 'dvipng')
139 141 def test_latex_to_png_color():
140 142 """
141 143 Test color settings for latex_to_png.
142 144 """
143 145 latex_string = "$x^2$"
144 146 default_value = latextools.latex_to_png(latex_string, wrap=False)
145 147 default_hexblack = latextools.latex_to_png(latex_string, wrap=False,
146 148 color='#000000')
147 149 dvipng_default = latextools.latex_to_png_dvipng(latex_string, False)
148 150 dvipng_black = latextools.latex_to_png_dvipng(latex_string, False, 'Black')
149 nt.assert_equal(dvipng_default, dvipng_black)
151 assert dvipng_default == dvipng_black
150 152 mpl_default = latextools.latex_to_png_mpl(latex_string, False)
151 153 mpl_black = latextools.latex_to_png_mpl(latex_string, False, 'Black')
152 nt.assert_equal(mpl_default, mpl_black)
153 nt.assert_in(default_value, [dvipng_black, mpl_black])
154 nt.assert_in(default_hexblack, [dvipng_black, mpl_black])
154 assert mpl_default == mpl_black
155 assert default_value in [dvipng_black, mpl_black]
156 assert default_hexblack in [dvipng_black, mpl_black]
155 157
156 158 # Test that dvips name colors can be used without error
157 159 dvipng_maroon = latextools.latex_to_png_dvipng(latex_string, False,
158 160 'Maroon')
159 161 # And that it doesn't return the black one
160 nt.assert_not_equal(dvipng_black, dvipng_maroon)
162 assert dvipng_black != dvipng_maroon
161 163
162 164 mpl_maroon = latextools.latex_to_png_mpl(latex_string, False, 'Maroon')
163 nt.assert_not_equal(mpl_black, mpl_maroon)
165 assert mpl_black != mpl_maroon
164 166 mpl_white = latextools.latex_to_png_mpl(latex_string, False, 'White')
165 167 mpl_hexwhite = latextools.latex_to_png_mpl(latex_string, False, '#FFFFFF')
166 nt.assert_equal(mpl_white, mpl_hexwhite)
168 assert mpl_white == mpl_hexwhite
167 169
168 170 mpl_white_scale = latextools.latex_to_png_mpl(latex_string, False,
169 171 'White', 1.2)
170 nt.assert_not_equal(mpl_white, mpl_white_scale)
172 assert mpl_white != mpl_white_scale
171 173
172 174
173 175 def test_latex_to_png_invalid_hex_colors():
174 176 """
175 177 Test that invalid hex colors provided to dvipng gives an exception.
176 178 """
177 179 latex_string = "$x^2$"
178 180 nt.assert_raises(ValueError, lambda: latextools.latex_to_png(latex_string,
179 181 backend='dvipng', color="#f00bar"))
180 182 nt.assert_raises(ValueError, lambda: latextools.latex_to_png(latex_string,
181 183 backend='dvipng', color="#f00"))
@@ -1,458 +1,457 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 IPython -- -vvs`. In this form
12 12 the script simply calls nose, but with special command line flags and
13 13 plugins loaded. Options after `--` are passed to nose.
14 14
15 15 """
16 16
17 17 # Copyright (c) IPython Development Team.
18 18 # Distributed under the terms of the Modified BSD License.
19 19
20 20
21 21 import glob
22 22 from io import BytesIO
23 23 import os
24 24 import os.path as path
25 25 import sys
26 26 from threading import Thread, Lock, Event
27 27 import warnings
28 28
29 29 import nose.plugins.builtin
30 30 from nose.plugins.xunit import Xunit
31 31 from nose import SkipTest
32 32 from nose.core import TestProgram
33 33 from nose.plugins import Plugin
34 34 from nose.util import safe_str
35 35
36 36 from IPython import version_info
37 37 from IPython.utils.py3compat import decode
38 38 from IPython.utils.importstring import import_item
39 39 from IPython.testing.plugin.ipdoctest import IPythonDoctest
40 40 from IPython.external.decorators import KnownFailure, knownfailureif
41 41
42 42 pjoin = path.join
43 43
44 44
45 45 # Enable printing all warnings raise by IPython's modules
46 46 warnings.filterwarnings('ignore', message='.*Matplotlib is building the font cache.*', category=UserWarning, module='.*')
47 47 warnings.filterwarnings('error', message='.*', category=ResourceWarning, module='.*')
48 48 warnings.filterwarnings('error', message=".*{'config': True}.*", category=DeprecationWarning, module='IPy.*')
49 49 warnings.filterwarnings('default', message='.*', category=Warning, module='IPy.*')
50 50
51 51 warnings.filterwarnings('error', message='.*apply_wrapper.*', category=DeprecationWarning, module='.*')
52 52 warnings.filterwarnings('error', message='.*make_label_dec', category=DeprecationWarning, module='.*')
53 53 warnings.filterwarnings('error', message='.*decorated_dummy.*', category=DeprecationWarning, module='.*')
54 54 warnings.filterwarnings('error', message='.*skip_file_no_x11.*', category=DeprecationWarning, module='.*')
55 55 warnings.filterwarnings('error', message='.*onlyif_any_cmd_exists.*', category=DeprecationWarning, module='.*')
56 56
57 57 warnings.filterwarnings('error', message='.*disable_gui.*', category=DeprecationWarning, module='.*')
58 58
59 59 warnings.filterwarnings('error', message='.*ExceptionColors global is deprecated.*', category=DeprecationWarning, module='.*')
60 60 warnings.filterwarnings('error', message='.*IPython.core.display.*', category=DeprecationWarning, module='.*')
61 61
62 62 # Jedi older versions
63 63 warnings.filterwarnings(
64 64 'error', message='.*elementwise != comparison failed and.*', category=FutureWarning, module='.*')
65 65
66 66 if version_info < (6,):
67 67 # nose.tools renames all things from `camelCase` to `snake_case` which raise an
68 68 # warning with the runner they also import from standard import library. (as of Dec 2015)
69 69 # Ignore, let's revisit that in a couple of years for IPython 6.
70 70 warnings.filterwarnings(
71 71 'ignore', message='.*Please use assertEqual instead', category=Warning, module='IPython.*')
72 72
73 73 if version_info < (8,):
74 74 warnings.filterwarnings('ignore', message='.*Completer.complete.*',
75 75 category=PendingDeprecationWarning, module='.*')
76 76 else:
77 77 warnings.warn(
78 78 'Completer.complete was pending deprecation and should be changed to Deprecated', FutureWarning)
79 79
80 80
81 81
82 82 # ------------------------------------------------------------------------------
83 83 # Monkeypatch Xunit to count known failures as skipped.
84 84 # ------------------------------------------------------------------------------
85 85 def monkeypatch_xunit():
86 86 try:
87 87 dec.knownfailureif(True)(lambda: None)()
88 88 except Exception as e:
89 89 KnownFailureTest = type(e)
90 90
91 91 def addError(self, test, err, capt=None):
92 92 if issubclass(err[0], KnownFailureTest):
93 93 err = (SkipTest,) + err[1:]
94 94 return self.orig_addError(test, err, capt)
95 95
96 96 Xunit.orig_addError = Xunit.addError
97 97 Xunit.addError = addError
98 98
99 99 #-----------------------------------------------------------------------------
100 100 # Check which dependencies are installed and greater than minimum version.
101 101 #-----------------------------------------------------------------------------
102 102 def extract_version(mod):
103 103 return mod.__version__
104 104
105 105 def test_for(item, min_version=None, callback=extract_version):
106 106 """Test to see if item is importable, and optionally check against a minimum
107 107 version.
108 108
109 109 If min_version is given, the default behavior is to check against the
110 110 `__version__` attribute of the item, but specifying `callback` allows you to
111 111 extract the value you are interested in. e.g::
112 112
113 113 In [1]: import sys
114 114
115 115 In [2]: from IPython.testing.iptest import test_for
116 116
117 117 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
118 118 Out[3]: True
119 119
120 120 """
121 121 try:
122 122 check = import_item(item)
123 123 except (ImportError, RuntimeError):
124 124 # GTK reports Runtime error if it can't be initialized even if it's
125 125 # importable.
126 126 return False
127 127 else:
128 128 if min_version:
129 129 if callback:
130 130 # extra processing step to get version to compare
131 131 check = callback(check)
132 132
133 133 return check >= min_version
134 134 else:
135 135 return True
136 136
137 137 # Global dict where we can store information on what we have and what we don't
138 138 # have available at test run time
139 139 have = {'matplotlib': test_for('matplotlib'),
140 140 'pygments': test_for('pygments'),
141 141 }
142 142
143 143 #-----------------------------------------------------------------------------
144 144 # Test suite definitions
145 145 #-----------------------------------------------------------------------------
146 146
147 147 test_group_names = ['core',
148 148 'extensions', 'lib', 'terminal', 'testing', 'utils',
149 149 ]
150 150
151 151 class TestSection(object):
152 152 def __init__(self, name, includes):
153 153 self.name = name
154 154 self.includes = includes
155 155 self.excludes = []
156 156 self.dependencies = []
157 157 self.enabled = True
158 158
159 159 def exclude(self, module):
160 160 if not module.startswith('IPython'):
161 161 module = self.includes[0] + "." + module
162 162 self.excludes.append(module.replace('.', os.sep))
163 163
164 164 def requires(self, *packages):
165 165 self.dependencies.extend(packages)
166 166
167 167 @property
168 168 def will_run(self):
169 169 return self.enabled and all(have[p] for p in self.dependencies)
170 170
171 171 # Name -> (include, exclude, dependencies_met)
172 172 test_sections = {n:TestSection(n, ['IPython.%s' % n]) for n in test_group_names}
173 173
174 174
175 175 # Exclusions and dependencies
176 176 # ---------------------------
177 177
178 178 # core:
179 179 sec = test_sections['core']
180 180 if not have['matplotlib']:
181 181 sec.exclude('pylabtools'),
182 182 sec.exclude('tests.test_pylabtools')
183 183
184 184 # lib:
185 185 sec = test_sections['lib']
186 sec.exclude('kernel')
186 sec.exclude('tests.test_latextools')
187 #sec.exclude('kernel')
187 188 if not have['pygments']:
188 189 sec.exclude('tests.test_lexers')
189 190 # We do this unconditionally, so that the test suite doesn't import
190 191 # gtk, changing the default encoding and masking some unicode bugs.
191 192 sec.exclude('inputhookgtk')
192 193 # We also do this unconditionally, because wx can interfere with Unix signals.
193 194 # There are currently no tests for it anyway.
194 195 sec.exclude('inputhookwx')
195 196 # Testing inputhook will need a lot of thought, to figure out
196 197 # how to have tests that don't lock up with the gui event
197 198 # loops in the picture
198 199 sec.exclude('inputhook')
199 200
200 201 # testing:
201 202 sec = test_sections['testing']
202 203 # These have to be skipped on win32 because they use echo, rm, cd, etc.
203 204 # See ticket https://github.com/ipython/ipython/issues/87
204 205 if sys.platform == 'win32':
205 206 sec.exclude('plugin.test_exampleip')
206 207 sec.exclude('plugin.dtexample')
207 208
208 209 # don't run jupyter_console tests found via shim
209 210 test_sections['terminal'].exclude('console')
210 211
211 212 # extensions:
212 213 sec = test_sections['extensions']
213 # This is deprecated in favour of rpy2
214 sec.exclude('rmagic')
215 214 # autoreload does some strange stuff, so move it to its own test section
216 215 sec.exclude('autoreload')
217 216 sec.exclude('tests.test_autoreload')
218 217 test_sections['autoreload'] = TestSection('autoreload',
219 218 ['IPython.extensions.autoreload', 'IPython.extensions.tests.test_autoreload'])
220 219 test_group_names.append('autoreload')
221 220
222 221
223 222 #-----------------------------------------------------------------------------
224 223 # Functions and classes
225 224 #-----------------------------------------------------------------------------
226 225
227 226 def check_exclusions_exist():
228 227 from IPython.paths import get_ipython_package_dir
229 228 from warnings import warn
230 229 parent = os.path.dirname(get_ipython_package_dir())
231 230 for sec in test_sections:
232 231 for pattern in sec.exclusions:
233 232 fullpath = pjoin(parent, pattern)
234 233 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
235 234 warn("Excluding nonexistent file: %r" % pattern)
236 235
237 236
238 237 class ExclusionPlugin(Plugin):
239 238 """A nose plugin to effect our exclusions of files and directories.
240 239 """
241 240 name = 'exclusions'
242 241 score = 3000 # Should come before any other plugins
243 242
244 243 def __init__(self, exclude_patterns=None):
245 244 """
246 245 Parameters
247 246 ----------
248 247
249 248 exclude_patterns : sequence of strings, optional
250 249 Filenames containing these patterns (as raw strings, not as regular
251 250 expressions) are excluded from the tests.
252 251 """
253 252 self.exclude_patterns = exclude_patterns or []
254 253 super(ExclusionPlugin, self).__init__()
255 254
256 255 def options(self, parser, env=os.environ):
257 256 Plugin.options(self, parser, env)
258 257
259 258 def configure(self, options, config):
260 259 Plugin.configure(self, options, config)
261 260 # Override nose trying to disable plugin.
262 261 self.enabled = True
263 262
264 263 def wantFile(self, filename):
265 264 """Return whether the given filename should be scanned for tests.
266 265 """
267 266 if any(pat in filename for pat in self.exclude_patterns):
268 267 return False
269 268 return None
270 269
271 270 def wantDirectory(self, directory):
272 271 """Return whether the given directory should be scanned for tests.
273 272 """
274 273 if any(pat in directory for pat in self.exclude_patterns):
275 274 return False
276 275 return None
277 276
278 277
279 278 class StreamCapturer(Thread):
280 279 daemon = True # Don't hang if main thread crashes
281 280 started = False
282 281 def __init__(self, echo=False):
283 282 super(StreamCapturer, self).__init__()
284 283 self.echo = echo
285 284 self.streams = []
286 285 self.buffer = BytesIO()
287 286 self.readfd, self.writefd = os.pipe()
288 287 self.buffer_lock = Lock()
289 288 self.stop = Event()
290 289
291 290 def run(self):
292 291 self.started = True
293 292
294 293 while not self.stop.is_set():
295 294 chunk = os.read(self.readfd, 1024)
296 295
297 296 with self.buffer_lock:
298 297 self.buffer.write(chunk)
299 298 if self.echo:
300 299 sys.stdout.write(decode(chunk))
301 300
302 301 os.close(self.readfd)
303 302 os.close(self.writefd)
304 303
305 304 def reset_buffer(self):
306 305 with self.buffer_lock:
307 306 self.buffer.truncate(0)
308 307 self.buffer.seek(0)
309 308
310 309 def get_buffer(self):
311 310 with self.buffer_lock:
312 311 return self.buffer.getvalue()
313 312
314 313 def ensure_started(self):
315 314 if not self.started:
316 315 self.start()
317 316
318 317 def halt(self):
319 318 """Safely stop the thread."""
320 319 if not self.started:
321 320 return
322 321
323 322 self.stop.set()
324 323 os.write(self.writefd, b'\0') # Ensure we're not locked in a read()
325 324 self.join()
326 325
327 326 class SubprocessStreamCapturePlugin(Plugin):
328 327 name='subprocstreams'
329 328 def __init__(self):
330 329 Plugin.__init__(self)
331 330 self.stream_capturer = StreamCapturer()
332 331 self.destination = os.environ.get('IPTEST_SUBPROC_STREAMS', 'capture')
333 332 # This is ugly, but distant parts of the test machinery need to be able
334 333 # to redirect streams, so we make the object globally accessible.
335 334 nose.iptest_stdstreams_fileno = self.get_write_fileno
336 335
337 336 def get_write_fileno(self):
338 337 if self.destination == 'capture':
339 338 self.stream_capturer.ensure_started()
340 339 return self.stream_capturer.writefd
341 340 elif self.destination == 'discard':
342 341 return os.open(os.devnull, os.O_WRONLY)
343 342 else:
344 343 return sys.__stdout__.fileno()
345 344
346 345 def configure(self, options, config):
347 346 Plugin.configure(self, options, config)
348 347 # Override nose trying to disable plugin.
349 348 if self.destination == 'capture':
350 349 self.enabled = True
351 350
352 351 def startTest(self, test):
353 352 # Reset log capture
354 353 self.stream_capturer.reset_buffer()
355 354
356 355 def formatFailure(self, test, err):
357 356 # Show output
358 357 ec, ev, tb = err
359 358 captured = self.stream_capturer.get_buffer().decode('utf-8', 'replace')
360 359 if captured.strip():
361 360 ev = safe_str(ev)
362 361 out = [ev, '>> begin captured subprocess output <<',
363 362 captured,
364 363 '>> end captured subprocess output <<']
365 364 return ec, '\n'.join(out), tb
366 365
367 366 return err
368 367
369 368 formatError = formatFailure
370 369
371 370 def finalize(self, result):
372 371 self.stream_capturer.halt()
373 372
374 373
375 374 def run_iptest():
376 375 """Run the IPython test suite using nose.
377 376
378 377 This function is called when this script is **not** called with the form
379 378 `iptest all`. It simply calls nose with appropriate command line flags
380 379 and accepts all of the standard nose arguments.
381 380 """
382 381 # Apply our monkeypatch to Xunit
383 382 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
384 383 monkeypatch_xunit()
385 384
386 385 arg1 = sys.argv[1]
387 386 if arg1.startswith('IPython/'):
388 387 if arg1.endswith('.py'):
389 388 arg1 = arg1[:-3]
390 389 sys.argv[1] = arg1.replace('/', '.')
391 390
392 391 arg1 = sys.argv[1]
393 392 if arg1 in test_sections:
394 393 section = test_sections[arg1]
395 394 sys.argv[1:2] = section.includes
396 395 elif arg1.startswith('IPython.') and arg1[8:] in test_sections:
397 396 section = test_sections[arg1[8:]]
398 397 sys.argv[1:2] = section.includes
399 398 else:
400 399 section = TestSection(arg1, includes=[arg1])
401 400
402 401
403 402 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
404 403 # We add --exe because of setuptools' imbecility (it
405 404 # blindly does chmod +x on ALL files). Nose does the
406 405 # right thing and it tries to avoid executables,
407 406 # setuptools unfortunately forces our hand here. This
408 407 # has been discussed on the distutils list and the
409 408 # setuptools devs refuse to fix this problem!
410 409 '--exe',
411 410 ]
412 411 if '-a' not in argv and '-A' not in argv:
413 412 argv = argv + ['-a', '!crash']
414 413
415 414 if nose.__version__ >= '0.11':
416 415 # I don't fully understand why we need this one, but depending on what
417 416 # directory the test suite is run from, if we don't give it, 0 tests
418 417 # get run. Specifically, if the test suite is run from the source dir
419 418 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
420 419 # even if the same call done in this directory works fine). It appears
421 420 # that if the requested package is in the current dir, nose bails early
422 421 # by default. Since it's otherwise harmless, leave it in by default
423 422 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
424 423 argv.append('--traverse-namespace')
425 424
426 425 plugins = [ ExclusionPlugin(section.excludes), KnownFailure(),
427 426 SubprocessStreamCapturePlugin() ]
428 427
429 428 # we still have some vestigial doctests in core
430 429 if (section.name.startswith(('core', 'IPython.core', 'IPython.utils'))):
431 430 plugins.append(IPythonDoctest())
432 431 argv.extend([
433 432 '--with-ipdoctest',
434 433 '--ipdoctest-tests',
435 434 '--ipdoctest-extension=txt',
436 435 ])
437 436
438 437
439 438 # Use working directory set by parent process (see iptestcontroller)
440 439 if 'IPTEST_WORKING_DIR' in os.environ:
441 440 os.chdir(os.environ['IPTEST_WORKING_DIR'])
442 441
443 442 # We need a global ipython running in this process, but the special
444 443 # in-process group spawns its own IPython kernels, so for *that* group we
445 444 # must avoid also opening the global one (otherwise there's a conflict of
446 445 # singletons). Ultimately the solution to this problem is to refactor our
447 446 # assumptions about what needs to be a singleton and what doesn't (app
448 447 # objects should, individual shells shouldn't). But for now, this
449 448 # workaround allows the test suite for the inprocess module to complete.
450 449 if 'kernel.inprocess' not in section.name:
451 450 from IPython.testing import globalipapp
452 451 globalipapp.start_ipython()
453 452
454 453 # Now nose can run
455 454 TestProgram(argv=argv, addplugins=plugins)
456 455
457 456 if __name__ == '__main__':
458 457 run_iptest()
General Comments 0
You need to be logged in to leave comments. Login now