##// END OF EJS Templates
Merge pull request #10370 from ipython/auto-backport-of-pr-10156...
Matthias Bussonnier -
r23463:1ba8673e merge
parent child Browse files
Show More
@@ -1,483 +1,483 b''
1 """Generic testing tools.
1 """Generic testing tools.
2
2
3 Authors
3 Authors
4 -------
4 -------
5 - Fernando Perez <Fernando.Perez@berkeley.edu>
5 - Fernando Perez <Fernando.Perez@berkeley.edu>
6 """
6 """
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 # Copyright (c) IPython Development Team.
10 # Copyright (c) IPython Development Team.
11 # Distributed under the terms of the Modified BSD License.
11 # Distributed under the terms of the Modified BSD License.
12
12
13 import os
13 import os
14 import re
14 import re
15 import sys
15 import sys
16 import tempfile
16 import tempfile
17
17
18 from contextlib import contextmanager
18 from contextlib import contextmanager
19 from io import StringIO
19 from io import StringIO
20 from subprocess import Popen, PIPE
20 from subprocess import Popen, PIPE
21 try:
21 try:
22 from unittest.mock import patch
22 from unittest.mock import patch
23 except ImportError:
23 except ImportError:
24 # Python 2 compatibility
24 # Python 2 compatibility
25 from mock import patch
25 from mock import patch
26
26
27 try:
27 try:
28 # These tools are used by parts of the runtime, so we make the nose
28 # These tools are used by parts of the runtime, so we make the nose
29 # dependency optional at this point. Nose is a hard dependency to run the
29 # dependency optional at this point. Nose is a hard dependency to run the
30 # test suite, but NOT to use ipython itself.
30 # test suite, but NOT to use ipython itself.
31 import nose.tools as nt
31 import nose.tools as nt
32 has_nose = True
32 has_nose = True
33 except ImportError:
33 except ImportError:
34 has_nose = False
34 has_nose = False
35
35
36 from traitlets.config.loader import Config
36 from traitlets.config.loader import Config
37 from IPython.utils.process import get_output_error_code
37 from IPython.utils.process import get_output_error_code
38 from IPython.utils.text import list_strings
38 from IPython.utils.text import list_strings
39 from IPython.utils.io import temp_pyfile, Tee
39 from IPython.utils.io import temp_pyfile, Tee
40 from IPython.utils import py3compat
40 from IPython.utils import py3compat
41 from IPython.utils.encoding import DEFAULT_ENCODING
41 from IPython.utils.encoding import DEFAULT_ENCODING
42
42
43 from . import decorators as dec
43 from . import decorators as dec
44 from . import skipdoctest
44 from . import skipdoctest
45
45
46
46
47 # The docstring for full_path doctests differently on win32 (different path
47 # The docstring for full_path doctests differently on win32 (different path
48 # separator) so just skip the doctest there. The example remains informative.
48 # separator) so just skip the doctest there. The example remains informative.
49 doctest_deco = skipdoctest.skip_doctest if sys.platform == 'win32' else dec.null_deco
49 doctest_deco = skipdoctest.skip_doctest if sys.platform == 'win32' else dec.null_deco
50
50
51 @doctest_deco
51 @doctest_deco
52 def full_path(startPath,files):
52 def full_path(startPath,files):
53 """Make full paths for all the listed files, based on startPath.
53 """Make full paths for all the listed files, based on startPath.
54
54
55 Only the base part of startPath is kept, since this routine is typically
55 Only the base part of startPath is kept, since this routine is typically
56 used with a script's ``__file__`` variable as startPath. The base of startPath
56 used with a script's ``__file__`` variable as startPath. The base of startPath
57 is then prepended to all the listed files, forming the output list.
57 is then prepended to all the listed files, forming the output list.
58
58
59 Parameters
59 Parameters
60 ----------
60 ----------
61 startPath : string
61 startPath : string
62 Initial path to use as the base for the results. This path is split
62 Initial path to use as the base for the results. This path is split
63 using os.path.split() and only its first component is kept.
63 using os.path.split() and only its first component is kept.
64
64
65 files : string or list
65 files : string or list
66 One or more files.
66 One or more files.
67
67
68 Examples
68 Examples
69 --------
69 --------
70
70
71 >>> full_path('/foo/bar.py',['a.txt','b.txt'])
71 >>> full_path('/foo/bar.py',['a.txt','b.txt'])
72 ['/foo/a.txt', '/foo/b.txt']
72 ['/foo/a.txt', '/foo/b.txt']
73
73
74 >>> full_path('/foo',['a.txt','b.txt'])
74 >>> full_path('/foo',['a.txt','b.txt'])
75 ['/a.txt', '/b.txt']
75 ['/a.txt', '/b.txt']
76
76
77 If a single file is given, the output is still a list::
77 If a single file is given, the output is still a list::
78
78
79 >>> full_path('/foo','a.txt')
79 >>> full_path('/foo','a.txt')
80 ['/a.txt']
80 ['/a.txt']
81 """
81 """
82
82
83 files = list_strings(files)
83 files = list_strings(files)
84 base = os.path.split(startPath)[0]
84 base = os.path.split(startPath)[0]
85 return [ os.path.join(base,f) for f in files ]
85 return [ os.path.join(base,f) for f in files ]
86
86
87
87
88 def parse_test_output(txt):
88 def parse_test_output(txt):
89 """Parse the output of a test run and return errors, failures.
89 """Parse the output of a test run and return errors, failures.
90
90
91 Parameters
91 Parameters
92 ----------
92 ----------
93 txt : str
93 txt : str
94 Text output of a test run, assumed to contain a line of one of the
94 Text output of a test run, assumed to contain a line of one of the
95 following forms::
95 following forms::
96
96
97 'FAILED (errors=1)'
97 'FAILED (errors=1)'
98 'FAILED (failures=1)'
98 'FAILED (failures=1)'
99 'FAILED (errors=1, failures=1)'
99 'FAILED (errors=1, failures=1)'
100
100
101 Returns
101 Returns
102 -------
102 -------
103 nerr, nfail
103 nerr, nfail
104 number of errors and failures.
104 number of errors and failures.
105 """
105 """
106
106
107 err_m = re.search(r'^FAILED \(errors=(\d+)\)', txt, re.MULTILINE)
107 err_m = re.search(r'^FAILED \(errors=(\d+)\)', txt, re.MULTILINE)
108 if err_m:
108 if err_m:
109 nerr = int(err_m.group(1))
109 nerr = int(err_m.group(1))
110 nfail = 0
110 nfail = 0
111 return nerr, nfail
111 return nerr, nfail
112
112
113 fail_m = re.search(r'^FAILED \(failures=(\d+)\)', txt, re.MULTILINE)
113 fail_m = re.search(r'^FAILED \(failures=(\d+)\)', txt, re.MULTILINE)
114 if fail_m:
114 if fail_m:
115 nerr = 0
115 nerr = 0
116 nfail = int(fail_m.group(1))
116 nfail = int(fail_m.group(1))
117 return nerr, nfail
117 return nerr, nfail
118
118
119 both_m = re.search(r'^FAILED \(errors=(\d+), failures=(\d+)\)', txt,
119 both_m = re.search(r'^FAILED \(errors=(\d+), failures=(\d+)\)', txt,
120 re.MULTILINE)
120 re.MULTILINE)
121 if both_m:
121 if both_m:
122 nerr = int(both_m.group(1))
122 nerr = int(both_m.group(1))
123 nfail = int(both_m.group(2))
123 nfail = int(both_m.group(2))
124 return nerr, nfail
124 return nerr, nfail
125
125
126 # If the input didn't match any of these forms, assume no error/failures
126 # If the input didn't match any of these forms, assume no error/failures
127 return 0, 0
127 return 0, 0
128
128
129
129
130 # So nose doesn't think this is a test
130 # So nose doesn't think this is a test
131 parse_test_output.__test__ = False
131 parse_test_output.__test__ = False
132
132
133
133
134 def default_argv():
134 def default_argv():
135 """Return a valid default argv for creating testing instances of ipython"""
135 """Return a valid default argv for creating testing instances of ipython"""
136
136
137 return ['--quick', # so no config file is loaded
137 return ['--quick', # so no config file is loaded
138 # Other defaults to minimize side effects on stdout
138 # Other defaults to minimize side effects on stdout
139 '--colors=NoColor', '--no-term-title','--no-banner',
139 '--colors=NoColor', '--no-term-title','--no-banner',
140 '--autocall=0']
140 '--autocall=0']
141
141
142
142
143 def default_config():
143 def default_config():
144 """Return a config object with good defaults for testing."""
144 """Return a config object with good defaults for testing."""
145 config = Config()
145 config = Config()
146 config.TerminalInteractiveShell.colors = 'NoColor'
146 config.TerminalInteractiveShell.colors = 'NoColor'
147 config.TerminalTerminalInteractiveShell.term_title = False,
147 config.TerminalTerminalInteractiveShell.term_title = False,
148 config.TerminalInteractiveShell.autocall = 0
148 config.TerminalInteractiveShell.autocall = 0
149 f = tempfile.NamedTemporaryFile(suffix=u'test_hist.sqlite', delete=False)
149 f = tempfile.NamedTemporaryFile(suffix=u'test_hist.sqlite', delete=False)
150 config.HistoryManager.hist_file = f.name
150 config.HistoryManager.hist_file = f.name
151 f.close()
151 f.close()
152 config.HistoryManager.db_cache_size = 10000
152 config.HistoryManager.db_cache_size = 10000
153 return config
153 return config
154
154
155
155
156 def get_ipython_cmd(as_string=False):
156 def get_ipython_cmd(as_string=False):
157 """
157 """
158 Return appropriate IPython command line name. By default, this will return
158 Return appropriate IPython command line name. By default, this will return
159 a list that can be used with subprocess.Popen, for example, but passing
159 a list that can be used with subprocess.Popen, for example, but passing
160 `as_string=True` allows for returning the IPython command as a string.
160 `as_string=True` allows for returning the IPython command as a string.
161
161
162 Parameters
162 Parameters
163 ----------
163 ----------
164 as_string: bool
164 as_string: bool
165 Flag to allow to return the command as a string.
165 Flag to allow to return the command as a string.
166 """
166 """
167 ipython_cmd = [sys.executable, "-m", "IPython"]
167 ipython_cmd = [sys.executable, "-m", "IPython"]
168
168
169 if as_string:
169 if as_string:
170 ipython_cmd = " ".join(ipython_cmd)
170 ipython_cmd = " ".join(ipython_cmd)
171
171
172 return ipython_cmd
172 return ipython_cmd
173
173
174 def ipexec(fname, options=None, commands=()):
174 def ipexec(fname, options=None, commands=()):
175 """Utility to call 'ipython filename'.
175 """Utility to call 'ipython filename'.
176
176
177 Starts IPython with a minimal and safe configuration to make startup as fast
177 Starts IPython with a minimal and safe configuration to make startup as fast
178 as possible.
178 as possible.
179
179
180 Note that this starts IPython in a subprocess!
180 Note that this starts IPython in a subprocess!
181
181
182 Parameters
182 Parameters
183 ----------
183 ----------
184 fname : str
184 fname : str
185 Name of file to be executed (should have .py or .ipy extension).
185 Name of file to be executed (should have .py or .ipy extension).
186
186
187 options : optional, list
187 options : optional, list
188 Extra command-line flags to be passed to IPython.
188 Extra command-line flags to be passed to IPython.
189
189
190 commands : optional, list
190 commands : optional, list
191 Commands to send in on stdin
191 Commands to send in on stdin
192
192
193 Returns
193 Returns
194 -------
194 -------
195 (stdout, stderr) of ipython subprocess.
195 (stdout, stderr) of ipython subprocess.
196 """
196 """
197 if options is None: options = []
197 if options is None: options = []
198
198
199 cmdargs = default_argv() + options
199 cmdargs = default_argv() + options
200
200
201 test_dir = os.path.dirname(__file__)
201 test_dir = os.path.dirname(__file__)
202
202
203 ipython_cmd = get_ipython_cmd()
203 ipython_cmd = get_ipython_cmd()
204 # Absolute path for filename
204 # Absolute path for filename
205 full_fname = os.path.join(test_dir, fname)
205 full_fname = os.path.join(test_dir, fname)
206 full_cmd = ipython_cmd + cmdargs + [full_fname]
206 full_cmd = ipython_cmd + cmdargs + [full_fname]
207 env = os.environ.copy()
207 env = os.environ.copy()
208 # FIXME: ignore all warnings in ipexec while we have shims
208 # FIXME: ignore all warnings in ipexec while we have shims
209 # should we keep suppressing warnings here, even after removing shims?
209 # should we keep suppressing warnings here, even after removing shims?
210 env['PYTHONWARNINGS'] = 'ignore'
210 env['PYTHONWARNINGS'] = 'ignore'
211 # env.pop('PYTHONWARNINGS', None) # Avoid extraneous warnings appearing on stderr
211 # env.pop('PYTHONWARNINGS', None) # Avoid extraneous warnings appearing on stderr
212 for k, v in env.items():
212 for k, v in env.items():
213 # Debug a bizarre failure we've seen on Windows:
213 # Debug a bizarre failure we've seen on Windows:
214 # TypeError: environment can only contain strings
214 # TypeError: environment can only contain strings
215 if not isinstance(v, str):
215 if not isinstance(v, str):
216 print(k, v)
216 print(k, v)
217 p = Popen(full_cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE, env=env)
217 p = Popen(full_cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE, env=env)
218 out, err = p.communicate(input=py3compat.str_to_bytes('\n'.join(commands)) or None)
218 out, err = p.communicate(input=py3compat.str_to_bytes('\n'.join(commands)) or None)
219 out, err = py3compat.bytes_to_str(out), py3compat.bytes_to_str(err)
219 out, err = py3compat.bytes_to_str(out), py3compat.bytes_to_str(err)
220 # `import readline` causes 'ESC[?1034h' to be output sometimes,
220 # `import readline` causes 'ESC[?1034h' to be output sometimes,
221 # so strip that out before doing comparisons
221 # so strip that out before doing comparisons
222 if out:
222 if out:
223 out = re.sub(r'\x1b\[[^h]+h', '', out)
223 out = re.sub(r'\x1b\[[^h]+h', '', out)
224 return out, err
224 return out, err
225
225
226
226
227 def ipexec_validate(fname, expected_out, expected_err='',
227 def ipexec_validate(fname, expected_out, expected_err='',
228 options=None, commands=()):
228 options=None, commands=()):
229 """Utility to call 'ipython filename' and validate output/error.
229 """Utility to call 'ipython filename' and validate output/error.
230
230
231 This function raises an AssertionError if the validation fails.
231 This function raises an AssertionError if the validation fails.
232
232
233 Note that this starts IPython in a subprocess!
233 Note that this starts IPython in a subprocess!
234
234
235 Parameters
235 Parameters
236 ----------
236 ----------
237 fname : str
237 fname : str
238 Name of the file to be executed (should have .py or .ipy extension).
238 Name of the file to be executed (should have .py or .ipy extension).
239
239
240 expected_out : str
240 expected_out : str
241 Expected stdout of the process.
241 Expected stdout of the process.
242
242
243 expected_err : optional, str
243 expected_err : optional, str
244 Expected stderr of the process.
244 Expected stderr of the process.
245
245
246 options : optional, list
246 options : optional, list
247 Extra command-line flags to be passed to IPython.
247 Extra command-line flags to be passed to IPython.
248
248
249 Returns
249 Returns
250 -------
250 -------
251 None
251 None
252 """
252 """
253
253
254 import nose.tools as nt
254 import nose.tools as nt
255
255
256 out, err = ipexec(fname, options, commands)
256 out, err = ipexec(fname, options, commands)
257 #print 'OUT', out # dbg
257 #print 'OUT', out # dbg
258 #print 'ERR', err # dbg
258 #print 'ERR', err # dbg
259 # If there are any errors, we must check those befor stdout, as they may be
259 # If there are any errors, we must check those befor stdout, as they may be
260 # more informative than simply having an empty stdout.
260 # more informative than simply having an empty stdout.
261 if err:
261 if err:
262 if expected_err:
262 if expected_err:
263 nt.assert_equal("\n".join(err.strip().splitlines()), "\n".join(expected_err.strip().splitlines()))
263 nt.assert_equal("\n".join(err.strip().splitlines()), "\n".join(expected_err.strip().splitlines()))
264 else:
264 else:
265 raise ValueError('Running file %r produced error: %r' %
265 raise ValueError('Running file %r produced error: %r' %
266 (fname, err))
266 (fname, err))
267 # If no errors or output on stderr was expected, match stdout
267 # If no errors or output on stderr was expected, match stdout
268 nt.assert_equal("\n".join(out.strip().splitlines()), "\n".join(expected_out.strip().splitlines()))
268 nt.assert_equal("\n".join(out.strip().splitlines()), "\n".join(expected_out.strip().splitlines()))
269
269
270
270
271 class TempFileMixin(object):
271 class TempFileMixin(object):
272 """Utility class to create temporary Python/IPython files.
272 """Utility class to create temporary Python/IPython files.
273
273
274 Meant as a mixin class for test cases."""
274 Meant as a mixin class for test cases."""
275
275
276 def mktmp(self, src, ext='.py'):
276 def mktmp(self, src, ext='.py'):
277 """Make a valid python temp file."""
277 """Make a valid python temp file."""
278 fname, f = temp_pyfile(src, ext)
278 fname, f = temp_pyfile(src, ext)
279 self.tmpfile = f
279 self.tmpfile = f
280 self.fname = fname
280 self.fname = fname
281
281
282 def tearDown(self):
282 def tearDown(self):
283 if hasattr(self, 'tmpfile'):
283 if hasattr(self, 'tmpfile'):
284 # If the tmpfile wasn't made because of skipped tests, like in
284 # If the tmpfile wasn't made because of skipped tests, like in
285 # win32, there's nothing to cleanup.
285 # win32, there's nothing to cleanup.
286 self.tmpfile.close()
286 self.tmpfile.close()
287 try:
287 try:
288 os.unlink(self.fname)
288 os.unlink(self.fname)
289 except:
289 except:
290 # On Windows, even though we close the file, we still can't
290 # On Windows, even though we close the file, we still can't
291 # delete it. I have no clue why
291 # delete it. I have no clue why
292 pass
292 pass
293
293
294 def __enter__(self):
294 def __enter__(self):
295 return self
295 return self
296
296
297 def __exit__(self, exc_type, exc_value, traceback):
297 def __exit__(self, exc_type, exc_value, traceback):
298 self.tearDown()
298 self.tearDown()
299
299
300
300
301 pair_fail_msg = ("Testing {0}\n\n"
301 pair_fail_msg = ("Testing {0}\n\n"
302 "In:\n"
302 "In:\n"
303 " {1!r}\n"
303 " {1!r}\n"
304 "Expected:\n"
304 "Expected:\n"
305 " {2!r}\n"
305 " {2!r}\n"
306 "Got:\n"
306 "Got:\n"
307 " {3!r}\n")
307 " {3!r}\n")
308 def check_pairs(func, pairs):
308 def check_pairs(func, pairs):
309 """Utility function for the common case of checking a function with a
309 """Utility function for the common case of checking a function with a
310 sequence of input/output pairs.
310 sequence of input/output pairs.
311
311
312 Parameters
312 Parameters
313 ----------
313 ----------
314 func : callable
314 func : callable
315 The function to be tested. Should accept a single argument.
315 The function to be tested. Should accept a single argument.
316 pairs : iterable
316 pairs : iterable
317 A list of (input, expected_output) tuples.
317 A list of (input, expected_output) tuples.
318
318
319 Returns
319 Returns
320 -------
320 -------
321 None. Raises an AssertionError if any output does not match the expected
321 None. Raises an AssertionError if any output does not match the expected
322 value.
322 value.
323 """
323 """
324 name = getattr(func, "func_name", getattr(func, "__name__", "<unknown>"))
324 name = getattr(func, "func_name", getattr(func, "__name__", "<unknown>"))
325 for inp, expected in pairs:
325 for inp, expected in pairs:
326 out = func(inp)
326 out = func(inp)
327 assert out == expected, pair_fail_msg.format(name, inp, expected, out)
327 assert out == expected, pair_fail_msg.format(name, inp, expected, out)
328
328
329
329
330 if py3compat.PY3:
330 if py3compat.PY3:
331 MyStringIO = StringIO
331 MyStringIO = StringIO
332 else:
332 else:
333 # In Python 2, stdout/stderr can have either bytes or unicode written to them,
333 # In Python 2, stdout/stderr can have either bytes or unicode written to them,
334 # so we need a class that can handle both.
334 # so we need a class that can handle both.
335 class MyStringIO(StringIO):
335 class MyStringIO(StringIO):
336 def write(self, s):
336 def write(self, s):
337 s = py3compat.cast_unicode(s, encoding=DEFAULT_ENCODING)
337 s = py3compat.cast_unicode(s, encoding=DEFAULT_ENCODING)
338 super(MyStringIO, self).write(s)
338 super(MyStringIO, self).write(s)
339
339
340 _re_type = type(re.compile(r''))
340 _re_type = type(re.compile(r''))
341
341
342 notprinted_msg = """Did not find {0!r} in printed output (on {1}):
342 notprinted_msg = """Did not find {0!r} in printed output (on {1}):
343 -------
343 -------
344 {2!s}
344 {2!s}
345 -------
345 -------
346 """
346 """
347
347
348 class AssertPrints(object):
348 class AssertPrints(object):
349 """Context manager for testing that code prints certain text.
349 """Context manager for testing that code prints certain text.
350
350
351 Examples
351 Examples
352 --------
352 --------
353 >>> with AssertPrints("abc", suppress=False):
353 >>> with AssertPrints("abc", suppress=False):
354 ... print("abcd")
354 ... print("abcd")
355 ... print("def")
355 ... print("def")
356 ...
356 ...
357 abcd
357 abcd
358 def
358 def
359 """
359 """
360 def __init__(self, s, channel='stdout', suppress=True):
360 def __init__(self, s, channel='stdout', suppress=True):
361 self.s = s
361 self.s = s
362 if isinstance(self.s, (py3compat.string_types, _re_type)):
362 if isinstance(self.s, (py3compat.string_types, _re_type)):
363 self.s = [self.s]
363 self.s = [self.s]
364 self.channel = channel
364 self.channel = channel
365 self.suppress = suppress
365 self.suppress = suppress
366
366
367 def __enter__(self):
367 def __enter__(self):
368 self.orig_stream = getattr(sys, self.channel)
368 self.orig_stream = getattr(sys, self.channel)
369 self.buffer = MyStringIO()
369 self.buffer = MyStringIO()
370 self.tee = Tee(self.buffer, channel=self.channel)
370 self.tee = Tee(self.buffer, channel=self.channel)
371 setattr(sys, self.channel, self.buffer if self.suppress else self.tee)
371 setattr(sys, self.channel, self.buffer if self.suppress else self.tee)
372
372
373 def __exit__(self, etype, value, traceback):
373 def __exit__(self, etype, value, traceback):
374 try:
374 try:
375 if value is not None:
375 if value is not None:
376 # If an error was raised, don't check anything else
376 # If an error was raised, don't check anything else
377 return False
377 return False
378 self.tee.flush()
378 self.tee.flush()
379 setattr(sys, self.channel, self.orig_stream)
379 setattr(sys, self.channel, self.orig_stream)
380 printed = self.buffer.getvalue()
380 printed = self.buffer.getvalue()
381 for s in self.s:
381 for s in self.s:
382 if isinstance(s, _re_type):
382 if isinstance(s, _re_type):
383 assert s.search(printed), notprinted_msg.format(s.pattern, self.channel, printed)
383 assert s.search(printed), notprinted_msg.format(s.pattern, self.channel, printed)
384 else:
384 else:
385 assert s in printed, notprinted_msg.format(s, self.channel, printed)
385 assert s in printed, notprinted_msg.format(s, self.channel, printed)
386 return False
386 return False
387 finally:
387 finally:
388 self.tee.close()
388 self.tee.close()
389
389
390 printed_msg = """Found {0!r} in printed output (on {1}):
390 printed_msg = """Found {0!r} in printed output (on {1}):
391 -------
391 -------
392 {2!s}
392 {2!s}
393 -------
393 -------
394 """
394 """
395
395
396 class AssertNotPrints(AssertPrints):
396 class AssertNotPrints(AssertPrints):
397 """Context manager for checking that certain output *isn't* produced.
397 """Context manager for checking that certain output *isn't* produced.
398
398
399 Counterpart of AssertPrints"""
399 Counterpart of AssertPrints"""
400 def __exit__(self, etype, value, traceback):
400 def __exit__(self, etype, value, traceback):
401 try:
401 try:
402 if value is not None:
402 if value is not None:
403 # If an error was raised, don't check anything else
403 # If an error was raised, don't check anything else
404 self.tee.close()
404 self.tee.close()
405 return False
405 return False
406 self.tee.flush()
406 self.tee.flush()
407 setattr(sys, self.channel, self.orig_stream)
407 setattr(sys, self.channel, self.orig_stream)
408 printed = self.buffer.getvalue()
408 printed = self.buffer.getvalue()
409 for s in self.s:
409 for s in self.s:
410 if isinstance(s, _re_type):
410 if isinstance(s, _re_type):
411 assert not s.search(printed),printed_msg.format(
411 assert not s.search(printed),printed_msg.format(
412 s.pattern, self.channel, printed)
412 s.pattern, self.channel, printed)
413 else:
413 else:
414 assert s not in printed, printed_msg.format(
414 assert s not in printed, printed_msg.format(
415 s, self.channel, printed)
415 s, self.channel, printed)
416 return False
416 return False
417 finally:
417 finally:
418 self.tee.close()
418 self.tee.close()
419
419
420 @contextmanager
420 @contextmanager
421 def mute_warn():
421 def mute_warn():
422 from IPython.utils import warn
422 from IPython.utils import warn
423 save_warn = warn.warn
423 save_warn = warn.warn
424 warn.warn = lambda *a, **kw: None
424 warn.warn = lambda *a, **kw: None
425 try:
425 try:
426 yield
426 yield
427 finally:
427 finally:
428 warn.warn = save_warn
428 warn.warn = save_warn
429
429
430 @contextmanager
430 @contextmanager
431 def make_tempfile(name):
431 def make_tempfile(name):
432 """ Create an empty, named, temporary file for the duration of the context.
432 """ Create an empty, named, temporary file for the duration of the context.
433 """
433 """
434 f = open(name, 'w')
434 f = open(name, 'w')
435 f.close()
435 f.close()
436 try:
436 try:
437 yield
437 yield
438 finally:
438 finally:
439 os.unlink(name)
439 os.unlink(name)
440
440
441 def fake_input(inputs):
441 def fake_input(inputs):
442 """Temporarily replace the input() function to return the given values
442 """Temporarily replace the input() function to return the given values
443
443
444 Use as a context manager:
444 Use as a context manager:
445
445
446 with fake_input(['result1', 'result2']):
446 with fake_input(['result1', 'result2']):
447 ...
447 ...
448
448
449 Values are returned in order. If input() is called again after the last value
449 Values are returned in order. If input() is called again after the last value
450 was used, EOFError is raised.
450 was used, EOFError is raised.
451 """
451 """
452 it = iter(inputs)
452 it = iter(inputs)
453 def mock_input(prompt=''):
453 def mock_input(prompt=''):
454 try:
454 try:
455 return next(it)
455 return next(it)
456 except StopIteration:
456 except StopIteration:
457 raise EOFError('No more inputs given')
457 raise EOFError('No more inputs given')
458
458
459 input_name = '%s.%s' % (py3compat.builtin_mod_name,
459 input_name = '%s.%s' % (py3compat.builtin_mod_name,
460 'input' if py3compat.PY3 else 'raw_input')
460 'input' if py3compat.PY3 else 'raw_input')
461 return patch(input_name, mock_input)
461 return patch(input_name, mock_input)
462
462
463 def help_output_test(subcommand=''):
463 def help_output_test(subcommand=''):
464 """test that `ipython [subcommand] -h` works"""
464 """test that `ipython [subcommand] -h` works"""
465 cmd = get_ipython_cmd() + [subcommand, '-h']
465 cmd = get_ipython_cmd() + [subcommand, '-h']
466 out, err, rc = get_output_error_code(cmd)
466 out, err, rc = get_output_error_code(cmd)
467 nt.assert_equal(rc, 0, err)
467 nt.assert_equal(rc, 0, err)
468 nt.assert_not_in("Traceback", err)
468 nt.assert_not_in("Traceback", err)
469 nt.assert_in("Options", out)
469 nt.assert_in("Options", out)
470 nt.assert_in("--help-all", out)
470 nt.assert_in("--help-all", out)
471 return out, err
471 return out, err
472
472
473
473
474 def help_all_output_test(subcommand=''):
474 def help_all_output_test(subcommand=''):
475 """test that `ipython [subcommand] --help-all` works"""
475 """test that `ipython [subcommand] --help-all` works"""
476 cmd = get_ipython_cmd() + [subcommand, '--help-all']
476 cmd = get_ipython_cmd() + [subcommand, '--help-all']
477 out, err, rc = get_output_error_code(cmd)
477 out, err, rc = get_output_error_code(cmd)
478 nt.assert_equal(rc, 0, err)
478 nt.assert_equal(rc, 0, err)
479 nt.assert_not_in("Traceback", err)
479 nt.assert_not_in("Traceback", err)
480 nt.assert_in("Options", out)
480 nt.assert_in("Options", out)
481 nt.assert_in("Class parameters", out)
481 nt.assert_in("Class", out)
482 return out, err
482 return out, err
483
483
General Comments 0
You need to be logged in to leave comments. Login now