7
7
from code import InteractiveConsole
8
8
from functools import partial
9
9
from unittest import TestCase
10
- from unittest .mock import MagicMock , patch
10
+ from unittest .mock import MagicMock , call , patch , ANY
11
11
12
12
from test .support import requires
13
13
from test .support .import_helper import import_module
22
22
from _pyrepl .console import Console , Event
23
23
from _pyrepl .readline import ReadlineAlikeReader , ReadlineConfig
24
24
from _pyrepl .simple_interact import _strip_final_indent
25
+ from _pyrepl .unix_console import UnixConsole
25
26
from _pyrepl .unix_eventqueue import EventQueue
26
27
from _pyrepl .input import KeymapTranslator
27
28
from _pyrepl .keymap import parse_keys , compile_keymap
@@ -105,7 +106,8 @@ def handle_all_events(
105
106
106
107
107
108
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 ),
109
111
)
110
112
111
113
@@ -1198,5 +1200,291 @@ def test_nested_multiple_keymaps(self):
1198
1200
self .assertEqual (result , {b"a" : {b"b" : {b"c" : "action" }}})
1199
1201
1200
1202
1203
+ def unix_console (events , ** kwargs ):
1204
+ console = UnixConsole ()
1205
+ console .get_event = MagicMock (side_effect = events )
1206
+
1207
+ height = kwargs .get ("height" , 25 )
1208
+ width = kwargs .get ("width" , 80 )
1209
+ console .getheightwidth = MagicMock (side_effect = lambda : (height , width ))
1210
+
1211
+ console .prepare ()
1212
+ for key , val in kwargs .items ():
1213
+ setattr (console , key , val )
1214
+ return console
1215
+
1216
+
1217
+ handle_events_unix_console = partial (
1218
+ handle_all_events ,
1219
+ prepare_console = partial (unix_console ),
1220
+ )
1221
+ handle_events_narrow_unix_console = partial (
1222
+ handle_all_events ,
1223
+ prepare_console = partial (unix_console , width = 5 ),
1224
+ )
1225
+ handle_events_short_unix_console = partial (
1226
+ handle_all_events ,
1227
+ prepare_console = partial (unix_console , height = 1 ),
1228
+ )
1229
+ handle_events_unix_console_height_3 = partial (
1230
+ handle_all_events , prepare_console = partial (unix_console , height = 3 )
1231
+ )
1232
+
1233
+
1234
+ TERM_CAPABILITIES = {
1235
+ "bel" : b"\x07 " ,
1236
+ "civis" : b"\x1b [?25l" ,
1237
+ "clear" : b"\x1b [H\x1b [2J" ,
1238
+ "cnorm" : b"\x1b [?12l\x1b [?25h" ,
1239
+ "cub" : b"\x1b [%p1%dD" ,
1240
+ "cub1" : b"\x08 " ,
1241
+ "cud" : b"\x1b [%p1%dB" ,
1242
+ "cud1" : b"\n " ,
1243
+ "cuf" : b"\x1b [%p1%dC" ,
1244
+ "cuf1" : b"\x1b [C" ,
1245
+ "cup" : b"\x1b [%i%p1%d;%p2%dH" ,
1246
+ "cuu" : b"\x1b [%p1%dA" ,
1247
+ "cuu1" : b"\x1b [A" ,
1248
+ "dch1" : b"\x1b [P" ,
1249
+ "dch" : b"\x1b [%p1%dP" ,
1250
+ "el" : b"\x1b [K" ,
1251
+ "hpa" : b"\x1b [%i%p1%dG" ,
1252
+ "ich" : b"\x1b [%p1%d@" ,
1253
+ "ich1" : None ,
1254
+ "ind" : b"\n " ,
1255
+ "pad" : None ,
1256
+ "ri" : b"\x1b M" ,
1257
+ "rmkx" : b"\x1b [?1l\x1b >" ,
1258
+ "smkx" : b"\x1b [?1h\x1b =" ,
1259
+ }
1260
+
1261
+
1262
+ @patch ("_pyrepl.curses.tigetstr" , lambda s : TERM_CAPABILITIES .get (s ))
1263
+ @patch (
1264
+ "_pyrepl.curses.tparm" ,
1265
+ lambda s , * args : s + b":" + b"," .join (str (i ).encode () for i in args ),
1266
+ )
1267
+ @patch ("_pyrepl.curses.setupterm" , lambda a , b : None )
1268
+ @patch (
1269
+ "termios.tcgetattr" ,
1270
+ lambda _ : [
1271
+ 27394 ,
1272
+ 3 ,
1273
+ 19200 ,
1274
+ 536872399 ,
1275
+ 38400 ,
1276
+ 38400 ,
1277
+ [
1278
+ b"\x04 " ,
1279
+ b"\xff " ,
1280
+ b"\xff " ,
1281
+ b"\x7f " ,
1282
+ b"\x17 " ,
1283
+ b"\x15 " ,
1284
+ b"\x12 " ,
1285
+ b"\x00 " ,
1286
+ b"\x03 " ,
1287
+ b"\x1c " ,
1288
+ b"\x1a " ,
1289
+ b"\x19 " ,
1290
+ b"\x11 " ,
1291
+ b"\x13 " ,
1292
+ b"\x16 " ,
1293
+ b"\x0f " ,
1294
+ b"\x01 " ,
1295
+ b"\x00 " ,
1296
+ b"\x14 " ,
1297
+ b"\x00 " ,
1298
+ ],
1299
+ ],
1300
+ )
1301
+ @patch ("termios.tcsetattr" , lambda a , b , c : None )
1302
+ @patch ("os.write" )
1303
+ class TestConsole (TestCase ):
1304
+ def test_simple_addition (self , _os_write ):
1305
+ code = "12+34"
1306
+ events = code_to_events (code )
1307
+ _ , _ = handle_events_unix_console (events )
1308
+ _os_write .assert_any_call (ANY , b"1" )
1309
+ _os_write .assert_any_call (ANY , b"2" )
1310
+ _os_write .assert_any_call (ANY , b"+" )
1311
+ _os_write .assert_any_call (ANY , b"3" )
1312
+ _os_write .assert_any_call (ANY , b"4" )
1313
+
1314
+ def test_wrap (self , _os_write ):
1315
+ code = "12+34"
1316
+ events = code_to_events (code )
1317
+ _ , _ = handle_events_narrow_unix_console (events )
1318
+ _os_write .assert_any_call (ANY , b"1" )
1319
+ _os_write .assert_any_call (ANY , b"2" )
1320
+ _os_write .assert_any_call (ANY , b"+" )
1321
+ _os_write .assert_any_call (ANY , b"3" )
1322
+ _os_write .assert_any_call (ANY , b"\\ " )
1323
+ _os_write .assert_any_call (ANY , b"\n " )
1324
+ _os_write .assert_any_call (ANY , b"4" )
1325
+
1326
+ def test_cursor_left (self , _os_write ):
1327
+ code = "1"
1328
+ events = itertools .chain (
1329
+ code_to_events (code ),
1330
+ [Event (evt = "key" , data = "left" , raw = bytearray (b"\x1b OD" ))],
1331
+ )
1332
+ _ , _ = handle_events_unix_console (events )
1333
+ _os_write .assert_any_call (ANY , TERM_CAPABILITIES ["cub" ] + b":1" )
1334
+
1335
+ def test_cursor_left_right (self , _os_write ):
1336
+ code = "1"
1337
+ events = itertools .chain (
1338
+ code_to_events (code ),
1339
+ [
1340
+ Event (evt = "key" , data = "left" , raw = bytearray (b"\x1b OD" )),
1341
+ Event (evt = "key" , data = "right" , raw = bytearray (b"\x1b OC" )),
1342
+ ],
1343
+ )
1344
+ _ , _ = handle_events_unix_console (events )
1345
+ _os_write .assert_any_call (ANY , TERM_CAPABILITIES ["cub" ] + b":1" )
1346
+ _os_write .assert_any_call (ANY , TERM_CAPABILITIES ["cuf" ] + b":1" )
1347
+
1348
+ def test_cursor_up (self , _os_write ):
1349
+ code = "1\n 2+3"
1350
+ events = itertools .chain (
1351
+ code_to_events (code ),
1352
+ [Event (evt = "key" , data = "up" , raw = bytearray (b"\x1b OA" ))],
1353
+ )
1354
+ _ , _ = handle_events_unix_console (events )
1355
+ _os_write .assert_any_call (ANY , TERM_CAPABILITIES ["cuu" ] + b":1" )
1356
+
1357
+ def test_cursor_up_down (self , _os_write ):
1358
+ code = "1\n 2+3"
1359
+ events = itertools .chain (
1360
+ code_to_events (code ),
1361
+ [
1362
+ Event (evt = "key" , data = "up" , raw = bytearray (b"\x1b OA" )),
1363
+ Event (evt = "key" , data = "down" , raw = bytearray (b"\x1b OB" )),
1364
+ ],
1365
+ )
1366
+ _ , _ = handle_events_unix_console (events )
1367
+ _os_write .assert_any_call (ANY , TERM_CAPABILITIES ["cuu" ] + b":1" )
1368
+ _os_write .assert_any_call (ANY , TERM_CAPABILITIES ["cud" ] + b":1" )
1369
+
1370
+ def test_cursor_back_write (self , _os_write ):
1371
+ events = itertools .chain (
1372
+ code_to_events ("1" ),
1373
+ [Event (evt = "key" , data = "left" , raw = bytearray (b"\x1b OD" ))],
1374
+ code_to_events ("2" ),
1375
+ )
1376
+ _ , _ = handle_events_unix_console (events )
1377
+ _os_write .assert_any_call (ANY , b"1" )
1378
+ _os_write .assert_any_call (ANY , TERM_CAPABILITIES ["cub" ] + b":1" )
1379
+ _os_write .assert_any_call (ANY , b"2" )
1380
+
1381
+ def test_multiline_function_move_up_short_terminal (self , _os_write ):
1382
+ # fmt: off
1383
+ code = (
1384
+ "def f():\n "
1385
+ " foo"
1386
+ )
1387
+ # fmt: on
1388
+
1389
+ events = itertools .chain (
1390
+ code_to_events (code ),
1391
+ [
1392
+ Event (evt = "key" , data = "up" , raw = bytearray (b"\x1b OA" )),
1393
+ Event (evt = "scroll" , data = None ),
1394
+ ],
1395
+ )
1396
+ _ , _ = handle_events_short_unix_console (events )
1397
+ _os_write .assert_any_call (ANY , TERM_CAPABILITIES ["ri" ] + b":" )
1398
+
1399
+ def test_multiline_function_move_up_down_short_terminal (self , _os_write ):
1400
+ # fmt: off
1401
+ code = (
1402
+ "def f():\n "
1403
+ " foo"
1404
+ )
1405
+ # fmt: on
1406
+
1407
+ events = itertools .chain (
1408
+ code_to_events (code ),
1409
+ [
1410
+ Event (evt = "key" , data = "up" , raw = bytearray (b"\x1b OA" )),
1411
+ Event (evt = "scroll" , data = None ),
1412
+ Event (evt = "key" , data = "down" , raw = bytearray (b"\x1b OB" )),
1413
+ Event (evt = "scroll" , data = None ),
1414
+ ],
1415
+ )
1416
+ _ , _ = handle_events_short_unix_console (events )
1417
+ _os_write .assert_any_call (ANY , TERM_CAPABILITIES ["ri" ] + b":" )
1418
+ _os_write .assert_any_call (ANY , TERM_CAPABILITIES ["ind" ] + b":" )
1419
+
1420
+ def test_resize_bigger_on_multiline_function (self , _os_write ):
1421
+ # fmt: off
1422
+ code = (
1423
+ "def f():\n "
1424
+ " foo"
1425
+ )
1426
+ # fmt: on
1427
+
1428
+ events = itertools .chain (code_to_events (code ))
1429
+ reader , console = handle_events_short_unix_console (events )
1430
+
1431
+ console .height = 2
1432
+ console .getheightwidth = MagicMock (lambda _ : (2 , 80 ))
1433
+
1434
+ def same_reader (_ ):
1435
+ return reader
1436
+
1437
+ def same_console (events ):
1438
+ console .get_event = MagicMock (side_effect = events )
1439
+ return console
1440
+
1441
+ _ , _ = handle_all_events (
1442
+ [Event (evt = "resize" , data = None )],
1443
+ prepare_reader = same_reader ,
1444
+ prepare_console = same_console ,
1445
+ )
1446
+ _os_write .assert_has_calls (
1447
+ [
1448
+ call (ANY , TERM_CAPABILITIES ["ri" ] + b":" ),
1449
+ call (ANY , TERM_CAPABILITIES ["cup" ] + b":0,0" ),
1450
+ call (ANY , b"def f():" ),
1451
+ ]
1452
+ )
1453
+
1454
+ def test_resize_smaller_on_multiline_function (self , _os_write ):
1455
+ # fmt: off
1456
+ code = (
1457
+ "def f():\n "
1458
+ " foo"
1459
+ )
1460
+ # fmt: on
1461
+
1462
+ events = itertools .chain (code_to_events (code ))
1463
+ reader , console = handle_events_unix_console_height_3 (events )
1464
+
1465
+ console .height = 1
1466
+ console .getheightwidth = MagicMock (lambda _ : (1 , 80 ))
1467
+
1468
+ def same_reader (_ ):
1469
+ return reader
1470
+
1471
+ def same_console (events ):
1472
+ console .get_event = MagicMock (side_effect = events )
1473
+ return console
1474
+
1475
+ _ , _ = handle_all_events (
1476
+ [Event (evt = "resize" , data = None )],
1477
+ prepare_reader = same_reader ,
1478
+ prepare_console = same_console ,
1479
+ )
1480
+ _os_write .assert_has_calls (
1481
+ [
1482
+ call (ANY , TERM_CAPABILITIES ["ind" ] + b":" ),
1483
+ call (ANY , TERM_CAPABILITIES ["cup" ] + b":0,0" ),
1484
+ call (ANY , b" foo" ),
1485
+ ]
1486
+ )
1487
+
1488
+
1201
1489
if __name__ == "__main__" :
1202
1490
unittest .main ()
0 commit comments