mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	
		
			
	
	
		
			331 lines
		
	
	
	
		
			9.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			331 lines
		
	
	
	
		
			9.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|   | import itertools | ||
|  | import sys | ||
|  | import unittest | ||
|  | from functools import partial | ||
|  | from typing import Iterable | ||
|  | from unittest import TestCase | ||
|  | from unittest.mock import MagicMock, call | ||
|  | 
 | ||
|  | from .support import handle_all_events, code_to_events | ||
|  | 
 | ||
|  | try: | ||
|  |     from _pyrepl.console import Event, Console | ||
|  |     from _pyrepl.windows_console import ( | ||
|  |         WindowsConsole, | ||
|  |         MOVE_LEFT, | ||
|  |         MOVE_RIGHT, | ||
|  |         MOVE_UP, | ||
|  |         MOVE_DOWN, | ||
|  |         ERASE_IN_LINE, | ||
|  |     ) | ||
|  | except ImportError: | ||
|  |     pass | ||
|  | 
 | ||
|  | 
 | ||
|  | @unittest.skipIf(sys.platform != "win32", "Test class specifically for Windows") | ||
|  | class WindowsConsoleTests(TestCase): | ||
|  |     def console(self, events, **kwargs) -> Console: | ||
|  |         console = WindowsConsole() | ||
|  |         console.get_event = MagicMock(side_effect=events) | ||
|  |         console._scroll = MagicMock() | ||
|  |         console._hide_cursor = MagicMock() | ||
|  |         console._show_cursor = MagicMock() | ||
|  |         console._getscrollbacksize = MagicMock(42) | ||
|  |         console.out = MagicMock() | ||
|  | 
 | ||
|  |         height = kwargs.get("height", 25) | ||
|  |         width = kwargs.get("width", 80) | ||
|  |         console.getheightwidth = MagicMock(side_effect=lambda: (height, width)) | ||
|  | 
 | ||
|  |         console.prepare() | ||
|  |         for key, val in kwargs.items(): | ||
|  |             setattr(console, key, val) | ||
|  |         return console | ||
|  | 
 | ||
|  |     def handle_events(self, events: Iterable[Event], **kwargs): | ||
|  |         return handle_all_events(events, partial(self.console, **kwargs)) | ||
|  | 
 | ||
|  |     def handle_events_narrow(self, events): | ||
|  |         return self.handle_events(events, width=5) | ||
|  | 
 | ||
|  |     def handle_events_short(self, events): | ||
|  |         return self.handle_events(events, height=1) | ||
|  | 
 | ||
|  |     def handle_events_height_3(self, events): | ||
|  |         return self.handle_events(events, height=3) | ||
|  | 
 | ||
|  |     def test_simple_addition(self): | ||
|  |         code = "12+34" | ||
|  |         events = code_to_events(code) | ||
|  |         _, con = self.handle_events(events) | ||
|  |         con.out.write.assert_any_call(b"1") | ||
|  |         con.out.write.assert_any_call(b"2") | ||
|  |         con.out.write.assert_any_call(b"+") | ||
|  |         con.out.write.assert_any_call(b"3") | ||
|  |         con.out.write.assert_any_call(b"4") | ||
|  |         con.restore() | ||
|  | 
 | ||
|  |     def test_wrap(self): | ||
|  |         code = "12+34" | ||
|  |         events = code_to_events(code) | ||
|  |         _, con = self.handle_events_narrow(events) | ||
|  |         con.out.write.assert_any_call(b"1") | ||
|  |         con.out.write.assert_any_call(b"2") | ||
|  |         con.out.write.assert_any_call(b"+") | ||
|  |         con.out.write.assert_any_call(b"3") | ||
|  |         con.out.write.assert_any_call(b"\\") | ||
|  |         con.out.write.assert_any_call(b"\n") | ||
|  |         con.out.write.assert_any_call(b"4") | ||
|  |         con.restore() | ||
|  | 
 | ||
|  |     def test_resize_wider(self): | ||
|  |         code = "1234567890" | ||
|  |         events = code_to_events(code) | ||
|  |         reader, console = self.handle_events_narrow(events) | ||
|  | 
 | ||
|  |         console.height = 20 | ||
|  |         console.width = 80 | ||
|  |         console.getheightwidth = MagicMock(lambda _: (20, 80)) | ||
|  | 
 | ||
|  |         def same_reader(_): | ||
|  |             return reader | ||
|  | 
 | ||
|  |         def same_console(events): | ||
|  |             console.get_event = MagicMock(side_effect=events) | ||
|  |             return console | ||
|  | 
 | ||
|  |         _, con = handle_all_events( | ||
|  |             [Event(evt="resize", data=None)], | ||
|  |             prepare_reader=same_reader, | ||
|  |             prepare_console=same_console, | ||
|  |         ) | ||
|  | 
 | ||
|  |         con.out.write.assert_any_call(self.move_right(2)) | ||
|  |         con.out.write.assert_any_call(self.move_up(2)) | ||
|  |         con.out.write.assert_any_call(b"567890") | ||
|  | 
 | ||
|  |         con.restore() | ||
|  | 
 | ||
|  |     def test_resize_narrower(self): | ||
|  |         code = "1234567890" | ||
|  |         events = code_to_events(code) | ||
|  |         reader, console = self.handle_events(events) | ||
|  | 
 | ||
|  |         console.height = 20 | ||
|  |         console.width = 4 | ||
|  |         console.getheightwidth = MagicMock(lambda _: (20, 4)) | ||
|  | 
 | ||
|  |         def same_reader(_): | ||
|  |             return reader | ||
|  | 
 | ||
|  |         def same_console(events): | ||
|  |             console.get_event = MagicMock(side_effect=events) | ||
|  |             return console | ||
|  | 
 | ||
|  |         _, con = handle_all_events( | ||
|  |             [Event(evt="resize", data=None)], | ||
|  |             prepare_reader=same_reader, | ||
|  |             prepare_console=same_console, | ||
|  |         ) | ||
|  | 
 | ||
|  |         con.out.write.assert_any_call(b"456\\") | ||
|  |         con.out.write.assert_any_call(b"789\\") | ||
|  | 
 | ||
|  |         con.restore() | ||
|  | 
 | ||
|  |     def test_cursor_left(self): | ||
|  |         code = "1" | ||
|  |         events = itertools.chain( | ||
|  |             code_to_events(code), | ||
|  |             [Event(evt="key", data="left", raw=bytearray(b"\x1bOD"))], | ||
|  |         ) | ||
|  |         _, con = self.handle_events(events) | ||
|  |         con.out.write.assert_any_call(self.move_left()) | ||
|  |         con.restore() | ||
|  | 
 | ||
|  |     def test_cursor_left_right(self): | ||
|  |         code = "1" | ||
|  |         events = itertools.chain( | ||
|  |             code_to_events(code), | ||
|  |             [ | ||
|  |                 Event(evt="key", data="left", raw=bytearray(b"\x1bOD")), | ||
|  |                 Event(evt="key", data="right", raw=bytearray(b"\x1bOC")), | ||
|  |             ], | ||
|  |         ) | ||
|  |         _, con = self.handle_events(events) | ||
|  |         con.out.write.assert_any_call(self.move_left()) | ||
|  |         con.out.write.assert_any_call(self.move_right()) | ||
|  |         con.restore() | ||
|  | 
 | ||
|  |     def test_cursor_up(self): | ||
|  |         code = "1\n2+3" | ||
|  |         events = itertools.chain( | ||
|  |             code_to_events(code), | ||
|  |             [Event(evt="key", data="up", raw=bytearray(b"\x1bOA"))], | ||
|  |         ) | ||
|  |         _, con = self.handle_events(events) | ||
|  |         con.out.write.assert_any_call(self.move_up()) | ||
|  |         con.restore() | ||
|  | 
 | ||
|  |     def test_cursor_up_down(self): | ||
|  |         code = "1\n2+3" | ||
|  |         events = itertools.chain( | ||
|  |             code_to_events(code), | ||
|  |             [ | ||
|  |                 Event(evt="key", data="up", raw=bytearray(b"\x1bOA")), | ||
|  |                 Event(evt="key", data="down", raw=bytearray(b"\x1bOB")), | ||
|  |             ], | ||
|  |         ) | ||
|  |         _, con = self.handle_events(events) | ||
|  |         con.out.write.assert_any_call(self.move_up()) | ||
|  |         con.out.write.assert_any_call(self.move_down()) | ||
|  |         con.restore() | ||
|  | 
 | ||
|  |     def test_cursor_back_write(self): | ||
|  |         events = itertools.chain( | ||
|  |             code_to_events("1"), | ||
|  |             [Event(evt="key", data="left", raw=bytearray(b"\x1bOD"))], | ||
|  |             code_to_events("2"), | ||
|  |         ) | ||
|  |         _, con = self.handle_events(events) | ||
|  |         con.out.write.assert_any_call(b"1") | ||
|  |         con.out.write.assert_any_call(self.move_left()) | ||
|  |         con.out.write.assert_any_call(b"21") | ||
|  |         con.restore() | ||
|  | 
 | ||
|  |     def test_multiline_function_move_up_short_terminal(self): | ||
|  |         # fmt: off | ||
|  |         code = ( | ||
|  |             "def f():\n" | ||
|  |             "  foo" | ||
|  |         ) | ||
|  |         # fmt: on | ||
|  | 
 | ||
|  |         events = itertools.chain( | ||
|  |             code_to_events(code), | ||
|  |             [ | ||
|  |                 Event(evt="key", data="up", raw=bytearray(b"\x1bOA")), | ||
|  |                 Event(evt="scroll", data=None), | ||
|  |             ], | ||
|  |         ) | ||
|  |         _, con = self.handle_events_short(events) | ||
|  |         con.out.write.assert_any_call(self.move_left(5)) | ||
|  |         con.out.write.assert_any_call(self.move_up()) | ||
|  |         con.restore() | ||
|  | 
 | ||
|  |     def test_multiline_function_move_up_down_short_terminal(self): | ||
|  |         # fmt: off | ||
|  |         code = ( | ||
|  |             "def f():\n" | ||
|  |             "  foo" | ||
|  |         ) | ||
|  |         # fmt: on | ||
|  | 
 | ||
|  |         events = itertools.chain( | ||
|  |             code_to_events(code), | ||
|  |             [ | ||
|  |                 Event(evt="key", data="up", raw=bytearray(b"\x1bOA")), | ||
|  |                 Event(evt="scroll", data=None), | ||
|  |                 Event(evt="key", data="down", raw=bytearray(b"\x1bOB")), | ||
|  |                 Event(evt="scroll", data=None), | ||
|  |             ], | ||
|  |         ) | ||
|  |         _, con = self.handle_events_short(events) | ||
|  |         con.out.write.assert_any_call(self.move_left(8)) | ||
|  |         con.out.write.assert_any_call(self.erase_in_line()) | ||
|  |         con.restore() | ||
|  | 
 | ||
|  |     def test_resize_bigger_on_multiline_function(self): | ||
|  |         # fmt: off | ||
|  |         code = ( | ||
|  |             "def f():\n" | ||
|  |             "  foo" | ||
|  |         ) | ||
|  |         # fmt: on | ||
|  | 
 | ||
|  |         events = itertools.chain(code_to_events(code)) | ||
|  |         reader, console = self.handle_events_short(events) | ||
|  | 
 | ||
|  |         console.height = 2 | ||
|  |         console.getheightwidth = MagicMock(lambda _: (2, 80)) | ||
|  | 
 | ||
|  |         def same_reader(_): | ||
|  |             return reader | ||
|  | 
 | ||
|  |         def same_console(events): | ||
|  |             console.get_event = MagicMock(side_effect=events) | ||
|  |             return console | ||
|  | 
 | ||
|  |         _, con = handle_all_events( | ||
|  |             [Event(evt="resize", data=None)], | ||
|  |             prepare_reader=same_reader, | ||
|  |             prepare_console=same_console, | ||
|  |         ) | ||
|  |         con.out.write.assert_has_calls( | ||
|  |             [ | ||
|  |                 call(self.move_left(5)), | ||
|  |                 call(self.move_up()), | ||
|  |                 call(b"def f():"), | ||
|  |                 call(self.move_left(3)), | ||
|  |                 call(self.move_down()), | ||
|  |             ] | ||
|  |         ) | ||
|  |         console.restore() | ||
|  |         con.restore() | ||
|  | 
 | ||
|  |     def test_resize_smaller_on_multiline_function(self): | ||
|  |         # fmt: off | ||
|  |         code = ( | ||
|  |             "def f():\n" | ||
|  |             "  foo" | ||
|  |         ) | ||
|  |         # fmt: on | ||
|  | 
 | ||
|  |         events = itertools.chain(code_to_events(code)) | ||
|  |         reader, console = self.handle_events_height_3(events) | ||
|  | 
 | ||
|  |         console.height = 1 | ||
|  |         console.getheightwidth = MagicMock(lambda _: (1, 80)) | ||
|  | 
 | ||
|  |         def same_reader(_): | ||
|  |             return reader | ||
|  | 
 | ||
|  |         def same_console(events): | ||
|  |             console.get_event = MagicMock(side_effect=events) | ||
|  |             return console | ||
|  | 
 | ||
|  |         _, con = handle_all_events( | ||
|  |             [Event(evt="resize", data=None)], | ||
|  |             prepare_reader=same_reader, | ||
|  |             prepare_console=same_console, | ||
|  |         ) | ||
|  |         con.out.write.assert_has_calls( | ||
|  |             [ | ||
|  |                 call(self.move_left(5)), | ||
|  |                 call(self.move_up()), | ||
|  |                 call(self.erase_in_line()), | ||
|  |                 call(b"  foo"), | ||
|  |             ] | ||
|  |         ) | ||
|  |         console.restore() | ||
|  |         con.restore() | ||
|  | 
 | ||
|  |     def move_up(self, lines=1): | ||
|  |         return MOVE_UP.format(lines).encode("utf8") | ||
|  | 
 | ||
|  |     def move_down(self, lines=1): | ||
|  |         return MOVE_DOWN.format(lines).encode("utf8") | ||
|  | 
 | ||
|  |     def move_left(self, cols=1): | ||
|  |         return MOVE_LEFT.format(cols).encode("utf8") | ||
|  | 
 | ||
|  |     def move_right(self, cols=1): | ||
|  |         return MOVE_RIGHT.format(cols).encode("utf8") | ||
|  | 
 | ||
|  |     def erase_in_line(self): | ||
|  |         return ERASE_IN_LINE.encode("utf8") | ||
|  | 
 | ||
|  | 
 | ||
|  | if __name__ == "__main__": | ||
|  |     unittest.main() |