##// END OF EJS Templates
Fix shellapp tests
Thomas Kluyver -
Show More
@@ -1,70 +1,68 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for shellapp module.
2 """Tests for shellapp module.
3
3
4 Authors
4 Authors
5 -------
5 -------
6 * Bradley Froehle
6 * Bradley Froehle
7 """
7 """
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2012 The IPython Development Team
9 # Copyright (C) 2012 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18 import unittest
18 import unittest
19
19
20 from IPython.testing import decorators as dec
20 from IPython.testing import decorators as dec
21 from IPython.testing import tools as tt
21 from IPython.testing import tools as tt
22 from IPython.utils.py3compat import PY3
22 from IPython.utils.py3compat import PY3
23
23
24 sqlite_err_maybe = dec.module_not_available('sqlite3')
24 sqlite_err_maybe = dec.module_not_available('sqlite3')
25 SQLITE_NOT_AVAILABLE_ERROR = ('WARNING: IPython History requires SQLite,'
25 SQLITE_NOT_AVAILABLE_ERROR = ('WARNING: IPython History requires SQLite,'
26 ' your history will not be saved\n')
26 ' your history will not be saved\n')
27
27
28 class TestFileToRun(unittest.TestCase, tt.TempFileMixin):
28 class TestFileToRun(unittest.TestCase, tt.TempFileMixin):
29 """Test the behavior of the file_to_run parameter."""
29 """Test the behavior of the file_to_run parameter."""
30
30
31 def test_py_script_file_attribute(self):
31 def test_py_script_file_attribute(self):
32 """Test that `__file__` is set when running `ipython file.py`"""
32 """Test that `__file__` is set when running `ipython file.py`"""
33 src = "print(__file__)\n"
33 src = "print(__file__)\n"
34 self.mktmp(src)
34 self.mktmp(src)
35
35
36 err = SQLITE_NOT_AVAILABLE_ERROR if sqlite_err_maybe else None
36 err = SQLITE_NOT_AVAILABLE_ERROR if sqlite_err_maybe else None
37 tt.ipexec_validate(self.fname, self.fname, err)
37 tt.ipexec_validate(self.fname, self.fname, err)
38
38
39 def test_ipy_script_file_attribute(self):
39 def test_ipy_script_file_attribute(self):
40 """Test that `__file__` is set when running `ipython file.ipy`"""
40 """Test that `__file__` is set when running `ipython file.ipy`"""
41 src = "print(__file__)\n"
41 src = "print(__file__)\n"
42 self.mktmp(src, ext='.ipy')
42 self.mktmp(src, ext='.ipy')
43
43
44 err = SQLITE_NOT_AVAILABLE_ERROR if sqlite_err_maybe else None
44 err = SQLITE_NOT_AVAILABLE_ERROR if sqlite_err_maybe else None
45 tt.ipexec_validate(self.fname, self.fname, err)
45 tt.ipexec_validate(self.fname, self.fname, err)
46
46
47 # The commands option to ipexec_validate doesn't work on Windows, and it
47 # The commands option to ipexec_validate doesn't work on Windows, and it
48 # doesn't seem worth fixing
48 # doesn't seem worth fixing
49 @dec.skip_win32
49 @dec.skip_win32
50 def test_py_script_file_attribute_interactively(self):
50 def test_py_script_file_attribute_interactively(self):
51 """Test that `__file__` is not set after `ipython -i file.py`"""
51 """Test that `__file__` is not set after `ipython -i file.py`"""
52 src = "True\n"
52 src = "True\n"
53 self.mktmp(src)
53 self.mktmp(src)
54
54
55 out = 'In [1]: False\n\nIn [2]:'
55 out, err = tt.ipexec(self.fname, options=['-i'],
56 err = SQLITE_NOT_AVAILABLE_ERROR if sqlite_err_maybe else None
57 tt.ipexec_validate(self.fname, out, err, options=['-i'],
58 commands=['"__file__" in globals()', 'exit()'])
56 commands=['"__file__" in globals()', 'exit()'])
57 self.assertIn("False", out)
59
58
60 @dec.skip_win32
59 @dec.skip_win32
61 @dec.skipif(PY3)
60 @dec.skipif(PY3)
62 def test_py_script_file_compiler_directive(self):
61 def test_py_script_file_compiler_directive(self):
63 """Test `__future__` compiler directives with `ipython -i file.py`"""
62 """Test `__future__` compiler directives with `ipython -i file.py`"""
64 src = "from __future__ import division\n"
63 src = "from __future__ import division\n"
65 self.mktmp(src)
64 self.mktmp(src)
66
65
67 out = 'In [1]: float\n\nIn [2]:'
66 out, err = tt.ipexec(self.fname, options=['-i'],
68 err = SQLITE_NOT_AVAILABLE_ERROR if sqlite_err_maybe else None
69 tt.ipexec_validate(self.fname, out, err, options=['-i'],
70 commands=['type(1/2)', 'exit()'])
67 commands=['type(1/2)', 'exit()'])
68 self.assertIn('float', out)
@@ -1,474 +1,468 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 traitlets.config.loader import Config
39 from traitlets.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, commands=()):
180 def ipexec(fname, options=None, commands=()):
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 commands : optional, list
196 commands : optional, list
197 Commands to send in on stdin
197 Commands to send in on stdin
198
198
199 Returns
199 Returns
200 -------
200 -------
201 (stdout, stderr) of ipython subprocess.
201 (stdout, stderr) of ipython subprocess.
202 """
202 """
203 if options is None: options = []
203 if options is None: options = []
204
204
205 # For these subprocess calls, eliminate all prompt printing so we only see
205 cmdargs = default_argv() + options
206 # output from script execution
207 prompt_opts = [ '--PromptManager.in_template=""',
208 '--PromptManager.in2_template=""',
209 '--PromptManager.out_template=""'
210 ]
211 cmdargs = default_argv() + prompt_opts + options
212
206
213 test_dir = os.path.dirname(__file__)
207 test_dir = os.path.dirname(__file__)
214
208
215 ipython_cmd = get_ipython_cmd()
209 ipython_cmd = get_ipython_cmd()
216 # Absolute path for filename
210 # Absolute path for filename
217 full_fname = os.path.join(test_dir, fname)
211 full_fname = os.path.join(test_dir, fname)
218 full_cmd = ipython_cmd + cmdargs + [full_fname]
212 full_cmd = ipython_cmd + cmdargs + [full_fname]
219 env = os.environ.copy()
213 env = os.environ.copy()
220 # FIXME: ignore all warnings in ipexec while we have shims
214 # FIXME: ignore all warnings in ipexec while we have shims
221 # should we keep suppressing warnings here, even after removing shims?
215 # should we keep suppressing warnings here, even after removing shims?
222 env['PYTHONWARNINGS'] = 'ignore'
216 env['PYTHONWARNINGS'] = 'ignore'
223 # env.pop('PYTHONWARNINGS', None) # Avoid extraneous warnings appearing on stderr
217 # env.pop('PYTHONWARNINGS', None) # Avoid extraneous warnings appearing on stderr
224 for k, v in env.items():
218 for k, v in env.items():
225 # Debug a bizarre failure we've seen on Windows:
219 # Debug a bizarre failure we've seen on Windows:
226 # TypeError: environment can only contain strings
220 # TypeError: environment can only contain strings
227 if not isinstance(v, str):
221 if not isinstance(v, str):
228 print(k, v)
222 print(k, v)
229 p = Popen(full_cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE, env=env)
223 p = Popen(full_cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE, env=env)
230 out, err = p.communicate(input=py3compat.str_to_bytes('\n'.join(commands)) or None)
224 out, err = p.communicate(input=py3compat.str_to_bytes('\n'.join(commands)) or None)
231 out, err = py3compat.bytes_to_str(out), py3compat.bytes_to_str(err)
225 out, err = py3compat.bytes_to_str(out), py3compat.bytes_to_str(err)
232 # `import readline` causes 'ESC[?1034h' to be output sometimes,
226 # `import readline` causes 'ESC[?1034h' to be output sometimes,
233 # so strip that out before doing comparisons
227 # so strip that out before doing comparisons
234 if out:
228 if out:
235 out = re.sub(r'\x1b\[[^h]+h', '', out)
229 out = re.sub(r'\x1b\[[^h]+h', '', out)
236 return out, err
230 return out, err
237
231
238
232
239 def ipexec_validate(fname, expected_out, expected_err='',
233 def ipexec_validate(fname, expected_out, expected_err='',
240 options=None, commands=()):
234 options=None, commands=()):
241 """Utility to call 'ipython filename' and validate output/error.
235 """Utility to call 'ipython filename' and validate output/error.
242
236
243 This function raises an AssertionError if the validation fails.
237 This function raises an AssertionError if the validation fails.
244
238
245 Note that this starts IPython in a subprocess!
239 Note that this starts IPython in a subprocess!
246
240
247 Parameters
241 Parameters
248 ----------
242 ----------
249 fname : str
243 fname : str
250 Name of the file to be executed (should have .py or .ipy extension).
244 Name of the file to be executed (should have .py or .ipy extension).
251
245
252 expected_out : str
246 expected_out : str
253 Expected stdout of the process.
247 Expected stdout of the process.
254
248
255 expected_err : optional, str
249 expected_err : optional, str
256 Expected stderr of the process.
250 Expected stderr of the process.
257
251
258 options : optional, list
252 options : optional, list
259 Extra command-line flags to be passed to IPython.
253 Extra command-line flags to be passed to IPython.
260
254
261 Returns
255 Returns
262 -------
256 -------
263 None
257 None
264 """
258 """
265
259
266 import nose.tools as nt
260 import nose.tools as nt
267
261
268 out, err = ipexec(fname, options, commands)
262 out, err = ipexec(fname, options, commands)
269 #print 'OUT', out # dbg
263 #print 'OUT', out # dbg
270 #print 'ERR', err # dbg
264 #print 'ERR', err # dbg
271 # If there are any errors, we must check those befor stdout, as they may be
265 # If there are any errors, we must check those befor stdout, as they may be
272 # more informative than simply having an empty stdout.
266 # more informative than simply having an empty stdout.
273 if err:
267 if err:
274 if expected_err:
268 if expected_err:
275 nt.assert_equal("\n".join(err.strip().splitlines()), "\n".join(expected_err.strip().splitlines()))
269 nt.assert_equal("\n".join(err.strip().splitlines()), "\n".join(expected_err.strip().splitlines()))
276 else:
270 else:
277 raise ValueError('Running file %r produced error: %r' %
271 raise ValueError('Running file %r produced error: %r' %
278 (fname, err))
272 (fname, err))
279 # If no errors or output on stderr was expected, match stdout
273 # If no errors or output on stderr was expected, match stdout
280 nt.assert_equal("\n".join(out.strip().splitlines()), "\n".join(expected_out.strip().splitlines()))
274 nt.assert_equal("\n".join(out.strip().splitlines()), "\n".join(expected_out.strip().splitlines()))
281
275
282
276
283 class TempFileMixin(object):
277 class TempFileMixin(object):
284 """Utility class to create temporary Python/IPython files.
278 """Utility class to create temporary Python/IPython files.
285
279
286 Meant as a mixin class for test cases."""
280 Meant as a mixin class for test cases."""
287
281
288 def mktmp(self, src, ext='.py'):
282 def mktmp(self, src, ext='.py'):
289 """Make a valid python temp file."""
283 """Make a valid python temp file."""
290 fname, f = temp_pyfile(src, ext)
284 fname, f = temp_pyfile(src, ext)
291 self.tmpfile = f
285 self.tmpfile = f
292 self.fname = fname
286 self.fname = fname
293
287
294 def tearDown(self):
288 def tearDown(self):
295 if hasattr(self, 'tmpfile'):
289 if hasattr(self, 'tmpfile'):
296 # If the tmpfile wasn't made because of skipped tests, like in
290 # If the tmpfile wasn't made because of skipped tests, like in
297 # win32, there's nothing to cleanup.
291 # win32, there's nothing to cleanup.
298 self.tmpfile.close()
292 self.tmpfile.close()
299 try:
293 try:
300 os.unlink(self.fname)
294 os.unlink(self.fname)
301 except:
295 except:
302 # On Windows, even though we close the file, we still can't
296 # On Windows, even though we close the file, we still can't
303 # delete it. I have no clue why
297 # delete it. I have no clue why
304 pass
298 pass
305
299
306 def __enter__(self):
300 def __enter__(self):
307 return self
301 return self
308
302
309 def __exit__(self, exc_type, exc_value, traceback):
303 def __exit__(self, exc_type, exc_value, traceback):
310 self.tearDown()
304 self.tearDown()
311
305
312
306
313 pair_fail_msg = ("Testing {0}\n\n"
307 pair_fail_msg = ("Testing {0}\n\n"
314 "In:\n"
308 "In:\n"
315 " {1!r}\n"
309 " {1!r}\n"
316 "Expected:\n"
310 "Expected:\n"
317 " {2!r}\n"
311 " {2!r}\n"
318 "Got:\n"
312 "Got:\n"
319 " {3!r}\n")
313 " {3!r}\n")
320 def check_pairs(func, pairs):
314 def check_pairs(func, pairs):
321 """Utility function for the common case of checking a function with a
315 """Utility function for the common case of checking a function with a
322 sequence of input/output pairs.
316 sequence of input/output pairs.
323
317
324 Parameters
318 Parameters
325 ----------
319 ----------
326 func : callable
320 func : callable
327 The function to be tested. Should accept a single argument.
321 The function to be tested. Should accept a single argument.
328 pairs : iterable
322 pairs : iterable
329 A list of (input, expected_output) tuples.
323 A list of (input, expected_output) tuples.
330
324
331 Returns
325 Returns
332 -------
326 -------
333 None. Raises an AssertionError if any output does not match the expected
327 None. Raises an AssertionError if any output does not match the expected
334 value.
328 value.
335 """
329 """
336 name = getattr(func, "func_name", getattr(func, "__name__", "<unknown>"))
330 name = getattr(func, "func_name", getattr(func, "__name__", "<unknown>"))
337 for inp, expected in pairs:
331 for inp, expected in pairs:
338 out = func(inp)
332 out = func(inp)
339 assert out == expected, pair_fail_msg.format(name, inp, expected, out)
333 assert out == expected, pair_fail_msg.format(name, inp, expected, out)
340
334
341
335
342 if py3compat.PY3:
336 if py3compat.PY3:
343 MyStringIO = StringIO
337 MyStringIO = StringIO
344 else:
338 else:
345 # In Python 2, stdout/stderr can have either bytes or unicode written to them,
339 # In Python 2, stdout/stderr can have either bytes or unicode written to them,
346 # so we need a class that can handle both.
340 # so we need a class that can handle both.
347 class MyStringIO(StringIO):
341 class MyStringIO(StringIO):
348 def write(self, s):
342 def write(self, s):
349 s = py3compat.cast_unicode(s, encoding=DEFAULT_ENCODING)
343 s = py3compat.cast_unicode(s, encoding=DEFAULT_ENCODING)
350 super(MyStringIO, self).write(s)
344 super(MyStringIO, self).write(s)
351
345
352 _re_type = type(re.compile(r''))
346 _re_type = type(re.compile(r''))
353
347
354 notprinted_msg = """Did not find {0!r} in printed output (on {1}):
348 notprinted_msg = """Did not find {0!r} in printed output (on {1}):
355 -------
349 -------
356 {2!s}
350 {2!s}
357 -------
351 -------
358 """
352 """
359
353
360 class AssertPrints(object):
354 class AssertPrints(object):
361 """Context manager for testing that code prints certain text.
355 """Context manager for testing that code prints certain text.
362
356
363 Examples
357 Examples
364 --------
358 --------
365 >>> with AssertPrints("abc", suppress=False):
359 >>> with AssertPrints("abc", suppress=False):
366 ... print("abcd")
360 ... print("abcd")
367 ... print("def")
361 ... print("def")
368 ...
362 ...
369 abcd
363 abcd
370 def
364 def
371 """
365 """
372 def __init__(self, s, channel='stdout', suppress=True):
366 def __init__(self, s, channel='stdout', suppress=True):
373 self.s = s
367 self.s = s
374 if isinstance(self.s, (py3compat.string_types, _re_type)):
368 if isinstance(self.s, (py3compat.string_types, _re_type)):
375 self.s = [self.s]
369 self.s = [self.s]
376 self.channel = channel
370 self.channel = channel
377 self.suppress = suppress
371 self.suppress = suppress
378
372
379 def __enter__(self):
373 def __enter__(self):
380 self.orig_stream = getattr(sys, self.channel)
374 self.orig_stream = getattr(sys, self.channel)
381 self.buffer = MyStringIO()
375 self.buffer = MyStringIO()
382 self.tee = Tee(self.buffer, channel=self.channel)
376 self.tee = Tee(self.buffer, channel=self.channel)
383 setattr(sys, self.channel, self.buffer if self.suppress else self.tee)
377 setattr(sys, self.channel, self.buffer if self.suppress else self.tee)
384
378
385 def __exit__(self, etype, value, traceback):
379 def __exit__(self, etype, value, traceback):
386 try:
380 try:
387 if value is not None:
381 if value is not None:
388 # If an error was raised, don't check anything else
382 # If an error was raised, don't check anything else
389 return False
383 return False
390 self.tee.flush()
384 self.tee.flush()
391 setattr(sys, self.channel, self.orig_stream)
385 setattr(sys, self.channel, self.orig_stream)
392 printed = self.buffer.getvalue()
386 printed = self.buffer.getvalue()
393 for s in self.s:
387 for s in self.s:
394 if isinstance(s, _re_type):
388 if isinstance(s, _re_type):
395 assert s.search(printed), notprinted_msg.format(s.pattern, self.channel, printed)
389 assert s.search(printed), notprinted_msg.format(s.pattern, self.channel, printed)
396 else:
390 else:
397 assert s in printed, notprinted_msg.format(s, self.channel, printed)
391 assert s in printed, notprinted_msg.format(s, self.channel, printed)
398 return False
392 return False
399 finally:
393 finally:
400 self.tee.close()
394 self.tee.close()
401
395
402 printed_msg = """Found {0!r} in printed output (on {1}):
396 printed_msg = """Found {0!r} in printed output (on {1}):
403 -------
397 -------
404 {2!s}
398 {2!s}
405 -------
399 -------
406 """
400 """
407
401
408 class AssertNotPrints(AssertPrints):
402 class AssertNotPrints(AssertPrints):
409 """Context manager for checking that certain output *isn't* produced.
403 """Context manager for checking that certain output *isn't* produced.
410
404
411 Counterpart of AssertPrints"""
405 Counterpart of AssertPrints"""
412 def __exit__(self, etype, value, traceback):
406 def __exit__(self, etype, value, traceback):
413 try:
407 try:
414 if value is not None:
408 if value is not None:
415 # If an error was raised, don't check anything else
409 # If an error was raised, don't check anything else
416 self.tee.close()
410 self.tee.close()
417 return False
411 return False
418 self.tee.flush()
412 self.tee.flush()
419 setattr(sys, self.channel, self.orig_stream)
413 setattr(sys, self.channel, self.orig_stream)
420 printed = self.buffer.getvalue()
414 printed = self.buffer.getvalue()
421 for s in self.s:
415 for s in self.s:
422 if isinstance(s, _re_type):
416 if isinstance(s, _re_type):
423 assert not s.search(printed),printed_msg.format(
417 assert not s.search(printed),printed_msg.format(
424 s.pattern, self.channel, printed)
418 s.pattern, self.channel, printed)
425 else:
419 else:
426 assert s not in printed, printed_msg.format(
420 assert s not in printed, printed_msg.format(
427 s, self.channel, printed)
421 s, self.channel, printed)
428 return False
422 return False
429 finally:
423 finally:
430 self.tee.close()
424 self.tee.close()
431
425
432 @contextmanager
426 @contextmanager
433 def mute_warn():
427 def mute_warn():
434 from IPython.utils import warn
428 from IPython.utils import warn
435 save_warn = warn.warn
429 save_warn = warn.warn
436 warn.warn = lambda *a, **kw: None
430 warn.warn = lambda *a, **kw: None
437 try:
431 try:
438 yield
432 yield
439 finally:
433 finally:
440 warn.warn = save_warn
434 warn.warn = save_warn
441
435
442 @contextmanager
436 @contextmanager
443 def make_tempfile(name):
437 def make_tempfile(name):
444 """ Create an empty, named, temporary file for the duration of the context.
438 """ Create an empty, named, temporary file for the duration of the context.
445 """
439 """
446 f = open(name, 'w')
440 f = open(name, 'w')
447 f.close()
441 f.close()
448 try:
442 try:
449 yield
443 yield
450 finally:
444 finally:
451 os.unlink(name)
445 os.unlink(name)
452
446
453
447
454 def help_output_test(subcommand=''):
448 def help_output_test(subcommand=''):
455 """test that `ipython [subcommand] -h` works"""
449 """test that `ipython [subcommand] -h` works"""
456 cmd = get_ipython_cmd() + [subcommand, '-h']
450 cmd = get_ipython_cmd() + [subcommand, '-h']
457 out, err, rc = get_output_error_code(cmd)
451 out, err, rc = get_output_error_code(cmd)
458 nt.assert_equal(rc, 0, err)
452 nt.assert_equal(rc, 0, err)
459 nt.assert_not_in("Traceback", err)
453 nt.assert_not_in("Traceback", err)
460 nt.assert_in("Options", out)
454 nt.assert_in("Options", out)
461 nt.assert_in("--help-all", out)
455 nt.assert_in("--help-all", out)
462 return out, err
456 return out, err
463
457
464
458
465 def help_all_output_test(subcommand=''):
459 def help_all_output_test(subcommand=''):
466 """test that `ipython [subcommand] --help-all` works"""
460 """test that `ipython [subcommand] --help-all` works"""
467 cmd = get_ipython_cmd() + [subcommand, '--help-all']
461 cmd = get_ipython_cmd() + [subcommand, '--help-all']
468 out, err, rc = get_output_error_code(cmd)
462 out, err, rc = get_output_error_code(cmd)
469 nt.assert_equal(rc, 0, err)
463 nt.assert_equal(rc, 0, err)
470 nt.assert_not_in("Traceback", err)
464 nt.assert_not_in("Traceback", err)
471 nt.assert_in("Options", out)
465 nt.assert_in("Options", out)
472 nt.assert_in("Class parameters", out)
466 nt.assert_in("Class parameters", out)
473 return out, err
467 return out, err
474
468
General Comments 0
You need to be logged in to leave comments. Login now