##// END OF EJS Templates
Mechanism for testing terminal interact loop in-process....
Thomas Kluyver -
Show More
@@ -17,12 +17,68 b' Authors'
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18 # stdlib
18 # stdlib
19 import sys
19 import sys
20 import types
20 import unittest
21 import unittest
21
22
23 from IPython.core.inputtransformer import InputTransformer
22 from IPython.testing.decorators import skipif
24 from IPython.testing.decorators import skipif
23 from IPython.utils import py3compat
25 from IPython.utils import py3compat
24 from IPython.testing import tools as tt
26 from IPython.testing import tools as tt
25
27
28 # Decorator for interaction loop tests -----------------------------------------
29
30 class mock_input_helper(object):
31 """Machinery for tests of the main interact loop.
32
33 Used by the mock_input decorator.
34 """
35 def __init__(self, testgen):
36 self.testgen = testgen
37 self.exception = None
38 self.ip = get_ipython()
39
40 def __enter__(self):
41 self.orig_raw_input = self.ip.raw_input
42 self.ip.raw_input = self.fake_input
43 return self
44
45 def __exit__(self, etype, value, tb):
46 self.ip.raw_input = self.orig_raw_input
47
48 def fake_input(self, prompt):
49 try:
50 return next(self.testgen)
51 except StopIteration:
52 self.ip.exit_now = True
53 return u''
54 except:
55 self.exception = sys.exc_info()
56 self.ip.exit_now = True
57 return u''
58
59 def mock_input(testfunc):
60 """Decorator for tests of the main interact loop.
61
62 Write the test as a generator, yield-ing the input strings, which IPython
63 will see as if they were typed in at the prompt.
64 """
65 def test_method(self):
66 testgen = testfunc(self)
67 with mock_input_helper(testgen) as mih:
68 mih.ip.interact(display_banner=False)
69
70 if mih.exception is not None:
71 # Re-raise captured exception
72 etype, value, tb = mih.exception
73 import traceback
74 traceback.print_tb(tb, file=sys.stdout)
75 del tb # Avoid reference loop
76 raise value
77
78 return test_method
79
80 # Test classes -----------------------------------------------------------------
81
26 class InteractiveShellTestCase(unittest.TestCase):
82 class InteractiveShellTestCase(unittest.TestCase):
27 def rl_hist_entries(self, rl, n):
83 def rl_hist_entries(self, rl, n):
28 """Get last n readline history entries as a list"""
84 """Get last n readline history entries as a list"""
@@ -171,6 +227,42 b' class InteractiveShellTestCase(unittest.TestCase):'
171 expected = [ py3compat.unicode_to_str(e, enc) for e in expected ]
227 expected = [ py3compat.unicode_to_str(e, enc) for e in expected ]
172 self.assertEqual(hist, expected)
228 self.assertEqual(hist, expected)
173
229
230 @mock_input
231 def test_inputtransformer_syntaxerror(self):
232 ip = get_ipython()
233 transformer = SyntaxErrorTransformer()
234 ip.input_splitter.python_line_transforms.append(transformer)
235 ip.input_transformer_manager.python_line_transforms.append(transformer)
236
237 try:
238 #raise Exception
239 with tt.AssertPrints('4', suppress=False):
240 yield u'print(2*2)'
241
242 with tt.AssertPrints('SyntaxError: input contains', suppress=False):
243 yield u'print(2345) # syntaxerror'
244
245 with tt.AssertPrints('16', suppress=False):
246 yield u'print(4*4)'
247
248 finally:
249 ip.input_splitter.python_line_transforms.remove(transformer)
250 ip.input_transformer_manager.python_line_transforms.remove(transformer)
251
252
253 class SyntaxErrorTransformer(InputTransformer):
254 def push(self, line):
255 pos = line.find('syntaxerror')
256 if pos >= 0:
257 e = SyntaxError('input contains "syntaxerror"')
258 e.text = line
259 e.offset = pos + 1
260 raise e
261 return line
262
263 def reset(self):
264 pass
265
174 class TerminalMagicsTestCase(unittest.TestCase):
266 class TerminalMagicsTestCase(unittest.TestCase):
175 def test_paste_magics_message(self):
267 def test_paste_magics_message(self):
176 """Test that an IndentationError while using paste magics doesn't
268 """Test that an IndentationError while using paste magics doesn't
@@ -173,7 +173,7 b' def get_ipython_cmd(as_string=False):'
173
173
174 return ipython_cmd
174 return ipython_cmd
175
175
176 def ipexec(fname, options=None, pipe=False):
176 def ipexec(fname, options=None):
177 """Utility to call 'ipython filename'.
177 """Utility to call 'ipython filename'.
178
178
179 Starts IPython with a minimal and safe configuration to make startup as fast
179 Starts IPython with a minimal and safe configuration to make startup as fast
@@ -189,9 +189,6 b' def ipexec(fname, options=None, pipe=False):'
189 options : optional, list
189 options : optional, list
190 Extra command-line flags to be passed to IPython.
190 Extra command-line flags to be passed to IPython.
191
191
192 pipe : optional, boolean
193 Pipe fname into IPython as stdin instead of calling it as external file
194
195 Returns
192 Returns
196 -------
193 -------
197 (stdout, stderr) of ipython subprocess.
194 (stdout, stderr) of ipython subprocess.
@@ -211,12 +208,8 b' def ipexec(fname, options=None, pipe=False):'
211 ipython_cmd = get_ipython_cmd()
208 ipython_cmd = get_ipython_cmd()
212 # Absolute path for filename
209 # Absolute path for filename
213 full_fname = os.path.join(test_dir, fname)
210 full_fname = os.path.join(test_dir, fname)
214 if pipe:
211 full_cmd = ipython_cmd + cmdargs + [full_fname]
215 full_cmd = ipython_cmd + cmdargs
212 p = Popen(full_cmd, stdout=PIPE, stderr=PIPE)
216 p = Popen(full_cmd, stdin=open(full_fname), stdout=PIPE, stderr=PIPE)
217 else:
218 full_cmd = ipython_cmd + cmdargs + [full_fname]
219 p = Popen(full_cmd, stdout=PIPE, stderr=PIPE)
220 out, err = p.communicate()
213 out, err = p.communicate()
221 out, err = py3compat.bytes_to_str(out), py3compat.bytes_to_str(err)
214 out, err = py3compat.bytes_to_str(out), py3compat.bytes_to_str(err)
222 # `import readline` causes 'ESC[?1034h' to be output sometimes,
215 # `import readline` causes 'ESC[?1034h' to be output sometimes,
@@ -42,6 +42,11 b' class IOStream:'
42 for meth in filter(clone, dir(stream)):
42 for meth in filter(clone, dir(stream)):
43 setattr(self, meth, getattr(stream, meth))
43 setattr(self, meth, getattr(stream, meth))
44
44
45 def __repr__(self):
46 cls = self.__class__
47 tpl = '{mod}.{cls}({args})'
48 return tpl.format(mod=cls.__module__, cls=cls.__name__, args=self.stream)
49
45 def write(self,data):
50 def write(self,data):
46 try:
51 try:
47 self._swrite(data)
52 self._swrite(data)
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now