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