Skip to content

Commit b305e86

Browse files
lysnikolaouestyxx
authored andcommitted
pythongh-111201: Add tests for unix console class in pyrepl (python#118653)
1 parent d0892ad commit b305e86

File tree

1 file changed

+290
-2
lines changed

1 file changed

+290
-2
lines changed

Lib/test/test_pyrepl.py

Lines changed: 290 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from code import InteractiveConsole
88
from functools import partial
99
from unittest import TestCase
10-
from unittest.mock import MagicMock, patch
10+
from unittest.mock import MagicMock, call, patch, ANY
1111

1212
from test.support import requires
1313
from test.support.import_helper import import_module
@@ -22,6 +22,7 @@
2222
from _pyrepl.console import Console, Event
2323
from _pyrepl.readline import ReadlineAlikeReader, ReadlineConfig
2424
from _pyrepl.simple_interact import _strip_final_indent
25+
from _pyrepl.unix_console import UnixConsole
2526
from _pyrepl.unix_eventqueue import EventQueue
2627
from _pyrepl.input import KeymapTranslator
2728
from _pyrepl.keymap import parse_keys, compile_keymap
@@ -105,7 +106,8 @@ def handle_all_events(
105106

106107

107108
handle_events_narrow_console = partial(
108-
handle_all_events, prepare_console=partial(prepare_mock_console, width=10)
109+
handle_all_events,
110+
prepare_console=partial(prepare_mock_console, width=10),
109111
)
110112

111113

@@ -1227,5 +1229,291 @@ def test_nested_multiple_keymaps(self):
12271229
self.assertEqual(result, {b"a": {b"b": {b"c": "action"}}})
12281230

12291231

1232+
def unix_console(events, **kwargs):
1233+
console = UnixConsole()
1234+
console.get_event = MagicMock(side_effect=events)
1235+
1236+
height = kwargs.get("height", 25)
1237+
width = kwargs.get("width", 80)
1238+
console.getheightwidth = MagicMock(side_effect=lambda: (height, width))
1239+
1240+
console.prepare()
1241+
for key, val in kwargs.items():
1242+
setattr(console, key, val)
1243+
return console
1244+
1245+
1246+
handle_events_unix_console = partial(
1247+
handle_all_events,
1248+
prepare_console=partial(unix_console),
1249+
)
1250+
handle_events_narrow_unix_console = partial(
1251+
handle_all_events,
1252+
prepare_console=partial(unix_console, width=5),
1253+
)
1254+
handle_events_short_unix_console = partial(
1255+
handle_all_events,
1256+
prepare_console=partial(unix_console, height=1),
1257+
)
1258+
handle_events_unix_console_height_3 = partial(
1259+
handle_all_events, prepare_console=partial(unix_console, height=3)
1260+
)
1261+
1262+
1263+
TERM_CAPABILITIES = {
1264+
"bel": b"\x07",
1265+
"civis": b"\x1b[?25l",
1266+
"clear": b"\x1b[H\x1b[2J",
1267+
"cnorm": b"\x1b[?12l\x1b[?25h",
1268+
"cub": b"\x1b[%p1%dD",
1269+
"cub1": b"\x08",
1270+
"cud": b"\x1b[%p1%dB",
1271+
"cud1": b"\n",
1272+
"cuf": b"\x1b[%p1%dC",
1273+
"cuf1": b"\x1b[C",
1274+
"cup": b"\x1b[%i%p1%d;%p2%dH",
1275+
"cuu": b"\x1b[%p1%dA",
1276+
"cuu1": b"\x1b[A",
1277+
"dch1": b"\x1b[P",
1278+
"dch": b"\x1b[%p1%dP",
1279+
"el": b"\x1b[K",
1280+
"hpa": b"\x1b[%i%p1%dG",
1281+
"ich": b"\x1b[%p1%d@",
1282+
"ich1": None,
1283+
"ind": b"\n",
1284+
"pad": None,
1285+
"ri": b"\x1bM",
1286+
"rmkx": b"\x1b[?1l\x1b>",
1287+
"smkx": b"\x1b[?1h\x1b=",
1288+
}
1289+
1290+
1291+
@patch("_pyrepl.curses.tigetstr", lambda s: TERM_CAPABILITIES.get(s))
1292+
@patch(
1293+
"_pyrepl.curses.tparm",
1294+
lambda s, *args: s + b":" + b",".join(str(i).encode() for i in args),
1295+
)
1296+
@patch("_pyrepl.curses.setupterm", lambda a, b: None)
1297+
@patch(
1298+
"termios.tcgetattr",
1299+
lambda _: [
1300+
27394,
1301+
3,
1302+
19200,
1303+
536872399,
1304+
38400,
1305+
38400,
1306+
[
1307+
b"\x04",
1308+
b"\xff",
1309+
b"\xff",
1310+
b"\x7f",
1311+
b"\x17",
1312+
b"\x15",
1313+
b"\x12",
1314+
b"\x00",
1315+
b"\x03",
1316+
b"\x1c",
1317+
b"\x1a",
1318+
b"\x19",
1319+
b"\x11",
1320+
b"\x13",
1321+
b"\x16",
1322+
b"\x0f",
1323+
b"\x01",
1324+
b"\x00",
1325+
b"\x14",
1326+
b"\x00",
1327+
],
1328+
],
1329+
)
1330+
@patch("termios.tcsetattr", lambda a, b, c: None)
1331+
@patch("os.write")
1332+
class TestConsole(TestCase):
1333+
def test_simple_addition(self, _os_write):
1334+
code = "12+34"
1335+
events = code_to_events(code)
1336+
_, _ = handle_events_unix_console(events)
1337+
_os_write.assert_any_call(ANY, b"1")
1338+
_os_write.assert_any_call(ANY, b"2")
1339+
_os_write.assert_any_call(ANY, b"+")
1340+
_os_write.assert_any_call(ANY, b"3")
1341+
_os_write.assert_any_call(ANY, b"4")
1342+
1343+
def test_wrap(self, _os_write):
1344+
code = "12+34"
1345+
events = code_to_events(code)
1346+
_, _ = handle_events_narrow_unix_console(events)
1347+
_os_write.assert_any_call(ANY, b"1")
1348+
_os_write.assert_any_call(ANY, b"2")
1349+
_os_write.assert_any_call(ANY, b"+")
1350+
_os_write.assert_any_call(ANY, b"3")
1351+
_os_write.assert_any_call(ANY, b"\\")
1352+
_os_write.assert_any_call(ANY, b"\n")
1353+
_os_write.assert_any_call(ANY, b"4")
1354+
1355+
def test_cursor_left(self, _os_write):
1356+
code = "1"
1357+
events = itertools.chain(
1358+
code_to_events(code),
1359+
[Event(evt="key", data="left", raw=bytearray(b"\x1bOD"))],
1360+
)
1361+
_, _ = handle_events_unix_console(events)
1362+
_os_write.assert_any_call(ANY, TERM_CAPABILITIES["cub"] + b":1")
1363+
1364+
def test_cursor_left_right(self, _os_write):
1365+
code = "1"
1366+
events = itertools.chain(
1367+
code_to_events(code),
1368+
[
1369+
Event(evt="key", data="left", raw=bytearray(b"\x1bOD")),
1370+
Event(evt="key", data="right", raw=bytearray(b"\x1bOC")),
1371+
],
1372+
)
1373+
_, _ = handle_events_unix_console(events)
1374+
_os_write.assert_any_call(ANY, TERM_CAPABILITIES["cub"] + b":1")
1375+
_os_write.assert_any_call(ANY, TERM_CAPABILITIES["cuf"] + b":1")
1376+
1377+
def test_cursor_up(self, _os_write):
1378+
code = "1\n2+3"
1379+
events = itertools.chain(
1380+
code_to_events(code),
1381+
[Event(evt="key", data="up", raw=bytearray(b"\x1bOA"))],
1382+
)
1383+
_, _ = handle_events_unix_console(events)
1384+
_os_write.assert_any_call(ANY, TERM_CAPABILITIES["cuu"] + b":1")
1385+
1386+
def test_cursor_up_down(self, _os_write):
1387+
code = "1\n2+3"
1388+
events = itertools.chain(
1389+
code_to_events(code),
1390+
[
1391+
Event(evt="key", data="up", raw=bytearray(b"\x1bOA")),
1392+
Event(evt="key", data="down", raw=bytearray(b"\x1bOB")),
1393+
],
1394+
)
1395+
_, _ = handle_events_unix_console(events)
1396+
_os_write.assert_any_call(ANY, TERM_CAPABILITIES["cuu"] + b":1")
1397+
_os_write.assert_any_call(ANY, TERM_CAPABILITIES["cud"] + b":1")
1398+
1399+
def test_cursor_back_write(self, _os_write):
1400+
events = itertools.chain(
1401+
code_to_events("1"),
1402+
[Event(evt="key", data="left", raw=bytearray(b"\x1bOD"))],
1403+
code_to_events("2"),
1404+
)
1405+
_, _ = handle_events_unix_console(events)
1406+
_os_write.assert_any_call(ANY, b"1")
1407+
_os_write.assert_any_call(ANY, TERM_CAPABILITIES["cub"] + b":1")
1408+
_os_write.assert_any_call(ANY, b"2")
1409+
1410+
def test_multiline_function_move_up_short_terminal(self, _os_write):
1411+
# fmt: off
1412+
code = (
1413+
"def f():\n"
1414+
" foo"
1415+
)
1416+
# fmt: on
1417+
1418+
events = itertools.chain(
1419+
code_to_events(code),
1420+
[
1421+
Event(evt="key", data="up", raw=bytearray(b"\x1bOA")),
1422+
Event(evt="scroll", data=None),
1423+
],
1424+
)
1425+
_, _ = handle_events_short_unix_console(events)
1426+
_os_write.assert_any_call(ANY, TERM_CAPABILITIES["ri"] + b":")
1427+
1428+
def test_multiline_function_move_up_down_short_terminal(self, _os_write):
1429+
# fmt: off
1430+
code = (
1431+
"def f():\n"
1432+
" foo"
1433+
)
1434+
# fmt: on
1435+
1436+
events = itertools.chain(
1437+
code_to_events(code),
1438+
[
1439+
Event(evt="key", data="up", raw=bytearray(b"\x1bOA")),
1440+
Event(evt="scroll", data=None),
1441+
Event(evt="key", data="down", raw=bytearray(b"\x1bOB")),
1442+
Event(evt="scroll", data=None),
1443+
],
1444+
)
1445+
_, _ = handle_events_short_unix_console(events)
1446+
_os_write.assert_any_call(ANY, TERM_CAPABILITIES["ri"] + b":")
1447+
_os_write.assert_any_call(ANY, TERM_CAPABILITIES["ind"] + b":")
1448+
1449+
def test_resize_bigger_on_multiline_function(self, _os_write):
1450+
# fmt: off
1451+
code = (
1452+
"def f():\n"
1453+
" foo"
1454+
)
1455+
# fmt: on
1456+
1457+
events = itertools.chain(code_to_events(code))
1458+
reader, console = handle_events_short_unix_console(events)
1459+
1460+
console.height = 2
1461+
console.getheightwidth = MagicMock(lambda _: (2, 80))
1462+
1463+
def same_reader(_):
1464+
return reader
1465+
1466+
def same_console(events):
1467+
console.get_event = MagicMock(side_effect=events)
1468+
return console
1469+
1470+
_, _ = handle_all_events(
1471+
[Event(evt="resize", data=None)],
1472+
prepare_reader=same_reader,
1473+
prepare_console=same_console,
1474+
)
1475+
_os_write.assert_has_calls(
1476+
[
1477+
call(ANY, TERM_CAPABILITIES["ri"] + b":"),
1478+
call(ANY, TERM_CAPABILITIES["cup"] + b":0,0"),
1479+
call(ANY, b"def f():"),
1480+
]
1481+
)
1482+
1483+
def test_resize_smaller_on_multiline_function(self, _os_write):
1484+
# fmt: off
1485+
code = (
1486+
"def f():\n"
1487+
" foo"
1488+
)
1489+
# fmt: on
1490+
1491+
events = itertools.chain(code_to_events(code))
1492+
reader, console = handle_events_unix_console_height_3(events)
1493+
1494+
console.height = 1
1495+
console.getheightwidth = MagicMock(lambda _: (1, 80))
1496+
1497+
def same_reader(_):
1498+
return reader
1499+
1500+
def same_console(events):
1501+
console.get_event = MagicMock(side_effect=events)
1502+
return console
1503+
1504+
_, _ = handle_all_events(
1505+
[Event(evt="resize", data=None)],
1506+
prepare_reader=same_reader,
1507+
prepare_console=same_console,
1508+
)
1509+
_os_write.assert_has_calls(
1510+
[
1511+
call(ANY, TERM_CAPABILITIES["ind"] + b":"),
1512+
call(ANY, TERM_CAPABILITIES["cup"] + b":0,0"),
1513+
call(ANY, b" foo"),
1514+
]
1515+
)
1516+
1517+
12301518
if __name__ == "__main__":
12311519
unittest.main()

0 commit comments

Comments
 (0)