| import sys |
| import unittest |
| |
| if sys.platform != "win32": |
| raise unittest.SkipTest("test only relevant on win32") |
| |
| |
| import itertools |
| 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 |
| |
| |
| 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() |