22import unittest
33import sys
44import 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
814from curtsies .fmtfuncs import *
15+ from curtsies .events import RefreshRequestEvent
916
1017from bpython import config
1118from bpython .curtsiesfrontend .repl import Repl
1219from bpython .repl import History
20+ from bpython .curtsiesfrontend .repl import INCONSISTENT_HISTORY_MSG , CONTIGUITY_BROKEN_MSG
1321
1422def 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