Skip to content

Commit f4d353c

Browse files
Add tests for special rewind cases
1 parent 2ebb430 commit f4d353c

File tree

2 files changed

+150
-4
lines changed

2 files changed

+150
-4
lines changed

bpython/curtsiesfrontend/repl.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@
5151

5252
logger = logging.getLogger(__name__)
5353

54-
HELP_MESSAGE = """
54+
INCONSISTENT_HISTORY_MSG = u"#<---History inconsistent with output shown--->"
55+
CONTIGUITY_BROKEN_MSG = u"#<---History contiguity broken by rewind--->"
56+
HELP_MESSAGE = u"""
5557
Thanks for using bpython!
5658
5759
See http://bpython-interpreter.org/ for info, http://docs.bpython-interpreter.org/ for docs, and https://github.com/bpython/bpython for source.

bpython/test/test_curtsies_painting.py

Lines changed: 147 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,22 @@
22
import unittest
33
import sys
44
import os
5+
from contextlib import contextmanager
56

6-
from curtsies.formatstringarray import FormatStringTest, fsarray
7+
try:
8+
from unittest import skip
9+
except ImportError:
10+
def skip(f):
11+
return lambda self: None
712

13+
from curtsies.formatstringarray import FormatStringTest, fsarray
814
from curtsies.fmtfuncs import *
15+
from curtsies.events import RefreshRequestEvent
916

1017
from bpython import config
1118
from bpython.curtsiesfrontend.repl import Repl
1219
from bpython.repl import History
20+
from bpython.curtsiesfrontend.repl import INCONSISTENT_HISTORY_MSG, CONTIGUITY_BROKEN_MSG
1321

1422
def setup_config():
1523
config_struct = config.Struct()
@@ -30,6 +38,9 @@ def assert_paint(self, screen, cursor_row_col):
3038
def assert_paint_ignoring_formatting(self, screen, cursor_row_col):
3139
array, cursor_pos = self.repl.paint()
3240
self.assertFSArraysEqualIgnoringFormatting(array, screen)
41+
self.assertEqual(cursor_pos, cursor_row_col)
42+
43+
class TestCurtsiesPaintingSimple(TestCurtsiesPainting):
3344

3445
def test_startup(self):
3546
screen = fsarray([cyan('>>> '), cyan('Welcome to')])
@@ -48,7 +59,7 @@ def test_run_line(self):
4859
[self.repl.add_normal_character(c) for c in '1 + 1']
4960
self.repl.on_enter()
5061
screen = fsarray([u'>>> 1 + 1', '2', 'Welcome to'])
51-
self.assert_paint_ignoring_formatting(screen, (0, 9))
62+
self.assert_paint_ignoring_formatting(screen, (1, 1))
5263
finally:
5364
sys.stdout = orig_stdout
5465

@@ -62,4 +73,137 @@ def test_completion(self):
6273
u'└───────────────────────┘',
6374
u'',
6475
u'Welcome to bpython! Press <F1> f']
65-
self.assert_paint_ignoring_formatting(screen, (0, 9))
76+
self.assert_paint_ignoring_formatting(screen, (0, 4))
77+
78+
@contextmanager
79+
def output_to_repl(repl):
80+
old_out, old_err = sys.stdout, sys.stderr
81+
try:
82+
sys.stdout, sys.stderr = repl.stdout, repl.stderr
83+
yield
84+
finally:
85+
sys.stdout, sys.stderr = old_out, old_err
86+
87+
class TestCurtsiesRewindRedraw(TestCurtsiesPainting):
88+
def refresh(self, when='now'):
89+
self.refresh_requests.append(RefreshRequestEvent(when=when))
90+
91+
def send_refreshes(self):
92+
while self.refresh_requests:
93+
self.repl.process_event(self.refresh_requests.pop())
94+
95+
def enter(self, line=None):
96+
"""Enter a line of text, avoiding autocompletion windows
97+
98+
autocomplete could still happen if the entered line has
99+
autocompletion that would happen then, but intermediate
100+
stages won't happen"""
101+
if line is not None:
102+
self.repl.current_line = line
103+
with output_to_repl(self.repl):
104+
self.repl.on_enter()
105+
self.send_refreshes()
106+
107+
def undo(self):
108+
with output_to_repl(self.repl):
109+
self.repl.undo()
110+
self.send_refreshes()
111+
112+
def setUp(self):
113+
self.refresh_requests = []
114+
self.repl = Repl(banner='', config=setup_config(), request_refresh=self.refresh)
115+
self.repl.rl_history = History() # clear history
116+
self.repl.height, self.repl.width = (5, 32)
117+
118+
def test_rewind(self):
119+
self.repl.current_line = '1 + 1'
120+
self.enter()
121+
screen = [u'>>> 1 + 1',
122+
u'2',
123+
u'>>> ']
124+
self.assert_paint_ignoring_formatting(screen, (2, 4))
125+
self.repl.undo()
126+
screen = [u'>>> ']
127+
self.assert_paint_ignoring_formatting(screen, (0, 4))
128+
129+
@skip('wrong message')
130+
def test_rewind_contiguity_loss(self):
131+
self.enter('1 + 1')
132+
self.enter('2 + 2')
133+
self.enter('def foo(x):')
134+
self.repl.current_line = ' return x + 1'
135+
screen = [u'>>> 1 + 1',
136+
u'2',
137+
u'>>> 2 + 2',
138+
u'4',
139+
u'>>> def foo(x):',
140+
u'... return x + 1']
141+
self.assert_paint_ignoring_formatting(screen, (5, 8))
142+
self.repl.scroll_offset = 1
143+
self.assert_paint_ignoring_formatting(screen[1:], (4, 8))
144+
self.undo()
145+
screen = [u'2',
146+
u'>>> 2 + 2',
147+
u'4',
148+
u'>>> ']
149+
self.assert_paint_ignoring_formatting(screen, (3, 4))
150+
self.undo()
151+
screen = [u'2',
152+
u'>>> ']
153+
self.assert_paint_ignoring_formatting(screen, (1, 4))
154+
self.undo()
155+
screen = [CONTIGUITY_BROKEN_MSG[:self.repl.width],
156+
u'>>> ',
157+
u'',
158+
u'',
159+
u'',
160+
u' '] #TODO why is that there? Necessary?
161+
self.assert_paint_ignoring_formatting(screen, (1, 4))
162+
screen = [u'>>> ']
163+
self.assert_paint_ignoring_formatting(screen, (0, 4))
164+
165+
def test_inconsistent_history_doesnt_happen_if_onscreen(self):
166+
self.enter("1 + 1")
167+
screen = [u">>> 1 + 1",
168+
u'2',
169+
u'>>> ']
170+
self.assert_paint_ignoring_formatting(screen, (2, 4))
171+
self.enter("2 + 2")
172+
screen = [u">>> 1 + 1",
173+
u'2',
174+
u'>>> 2 + 2',
175+
u'4',
176+
u'>>> ']
177+
self.assert_paint_ignoring_formatting(screen, (4, 4))
178+
self.repl.display_lines[0] = self.repl.display_lines[0] * 2
179+
self.undo()
180+
screen = [u">>> 1 + 1",
181+
u'2',
182+
u'>>> ']
183+
self.assert_paint_ignoring_formatting(screen, (2, 4))
184+
185+
@skip('why is everything moved up?')
186+
def test_rewind_inconsistent_history(self):
187+
self.enter("1 + 1")
188+
self.enter("2 + 2")
189+
self.enter("3 + 3")
190+
screen = [u">>> 1 + 1",
191+
u'2',
192+
u'>>> 2 + 2',
193+
u'4',
194+
u'>>> 3 + 3',
195+
u'6',
196+
u'>>> ']
197+
self.assert_paint_ignoring_formatting(screen, (6, 4))
198+
self.repl.scroll_offset += len(screen) - self.repl.height
199+
self.assert_paint_ignoring_formatting(screen[2:], (4, 4))
200+
self.repl.display_lines[0] = self.repl.display_lines[0] * 2
201+
self.undo()
202+
screen = [INCONSISTENT_HISTORY_MSG[:self.repl.width],
203+
u'>>> 2 + 2',
204+
u'4',
205+
u'>>> ',
206+
u'',
207+
u'']
208+
self.assert_paint_ignoring_formatting(screen, (5, 4))
209+

0 commit comments

Comments
 (0)