Skip to content

Commit 0acecdc

Browse files
committed
Add test case
1 parent cfc3423 commit 0acecdc

File tree

1 file changed

+73
-1
lines changed

1 file changed

+73
-1
lines changed

Lib/test/test_pyrepl/test_pyrepl.py

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import tempfile
1313
from pkgutil import ModuleInfo
1414
from unittest import TestCase, skipUnless, skipIf, SkipTest
15-
from unittest.mock import patch
15+
from unittest.mock import Mock, patch
1616
from test.support import force_not_colorized, make_clean_env, Py_DEBUG
1717
from test.support import has_subprocess_support, SHORT_TIMEOUT, STDLIB_DIR
1818
from test.support.import_helper import import_module
@@ -2105,3 +2105,75 @@ def test_ctrl_d_single_line_end_no_newline(self):
21052105
)
21062106
reader, _ = handle_all_events(events)
21072107
self.assertEqual("hello", "".join(reader.buffer))
2108+
2109+
2110+
@skipUnless(sys.platform == "win32", "Windows console VT behavior only")
2111+
class TestWindowsConsoleVtEolWrap(TestCase):
2112+
"""
2113+
When a line exactly fills the terminal width, VT terminals differ on whether
2114+
the cursor immediately wraps to the next row. In VT mode we must synchronize
2115+
our logical cursor position with the real console cursor.
2116+
"""
2117+
def _make_console_like(self, *, width: int, offset: int, vt: bool):
2118+
from _pyrepl import windows_console as wc
2119+
2120+
con = object.__new__(wc.WindowsConsole)
2121+
2122+
# Minimal state needed by __write_changed_line()
2123+
con.width = width
2124+
con.screen = []
2125+
con.posxy = (0, 0)
2126+
setattr(con, "_WindowsConsole__offset", offset)
2127+
setattr(con, "_WindowsConsole__vt_support", vt)
2128+
2129+
# Stub out side-effecting methods used by __write_changed_line()
2130+
con._hide_cursor = Mock()
2131+
con._erase_to_end = Mock()
2132+
con._move_relative = Mock()
2133+
con.move_cursor = Mock()
2134+
setattr(con, "_WindowsConsole__write", Mock())
2135+
2136+
return con, wc
2137+
2138+
def test_vt_exact_width_line_did_wrap(self):
2139+
# Terminal wrapped to next row: posxy should become (0, y+1).
2140+
width = 10
2141+
y = 3
2142+
con, wc = self._make_console_like(width=width, offset=0, vt=True)
2143+
2144+
def fake_gcsbi(_h, info):
2145+
info.dwCursorPosition.X = 0
2146+
# Visible window top = 0, cursor now on next visible row
2147+
info.srWindow.Top = 0
2148+
info.dwCursorPosition.Y = y + 1
2149+
return True
2150+
2151+
with patch.object(wc, "GetConsoleScreenBufferInfo", side_effect=fake_gcsbi), \
2152+
patch.object(wc, "OutHandle", 1):
2153+
old = ""
2154+
new = "a" * width
2155+
wc.WindowsConsole._WindowsConsole__write_changed_line(con, y, old, new, 0)
2156+
self.assertEqual(con.posxy, (0, y + 1))
2157+
self.assertNotIn((0, y + 1), [c.args for c in con._move_relative.mock_calls])
2158+
2159+
def test_vt_exact_width_line_did_not_wrap(self):
2160+
# Terminal did NOT wrap yet: posxy should stay at (width, y).
2161+
width = 10
2162+
y = 3
2163+
con, wc = self._make_console_like(width=width, offset=0, vt=True)
2164+
2165+
def fake_gcsbi(_h, info):
2166+
info.dwCursorPosition.X = width
2167+
info.srWindow.Top = 0
2168+
# Cursor remains on the same visible row
2169+
info.dwCursorPosition.Y = y
2170+
return True
2171+
2172+
with patch.object(wc, "GetConsoleScreenBufferInfo", side_effect=fake_gcsbi), \
2173+
patch.object(wc, "OutHandle", 1):
2174+
old = ""
2175+
new = "a" * width
2176+
wc.WindowsConsole._WindowsConsole__write_changed_line(con, y, old, new, 0)
2177+
2178+
self.assertEqual(con.posxy, (width, y))
2179+
self.assertNotIn((0, y + 1), [c.args for c in con._move_relative.mock_calls])

0 commit comments

Comments
 (0)