summaryrefslogtreecommitdiffstats
path: root/Lib/test/test_pyrepl
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/test/test_pyrepl')
-rw-r--r--Lib/test/test_pyrepl/__init__.py12
-rw-r--r--Lib/test/test_pyrepl/support.py2
-rw-r--r--Lib/test/test_pyrepl/test_pyrepl.py5
-rw-r--r--Lib/test/test_pyrepl/test_unix_console.py11
-rw-r--r--Lib/test/test_pyrepl/test_unix_eventqueue.py10
-rw-r--r--Lib/test/test_pyrepl/test_windows_console.py331
6 files changed, 357 insertions, 14 deletions
diff --git a/Lib/test/test_pyrepl/__init__.py b/Lib/test/test_pyrepl/__init__.py
index fa38b86..8359d98 100644
--- a/Lib/test/test_pyrepl/__init__.py
+++ b/Lib/test/test_pyrepl/__init__.py
@@ -1,12 +1,14 @@
import os
+import sys
from test.support import requires, load_package_tests
from test.support.import_helper import import_module
-# Optionally test pyrepl. This currently requires that the
-# 'curses' resource be given on the regrtest command line using the -u
-# option. Additionally, we need to attempt to import curses and readline.
-requires("curses")
-curses = import_module("curses")
+if sys.platform != "win32":
+ # On non-Windows platforms, testing pyrepl currently requires that the
+ # 'curses' resource be given on the regrtest command line using the -u
+ # option. Additionally, we need to attempt to import curses and readline.
+ requires("curses")
+ curses = import_module("curses")
def load_tests(*args):
diff --git a/Lib/test/test_pyrepl/support.py b/Lib/test/test_pyrepl/support.py
index 7553904..d2f5429 100644
--- a/Lib/test/test_pyrepl/support.py
+++ b/Lib/test/test_pyrepl/support.py
@@ -55,7 +55,7 @@ def prepare_reader(console: Console, **kwargs):
return reader
-def prepare_console(events: Iterable[Event], **kwargs):
+def prepare_console(events: Iterable[Event], **kwargs) -> MagicMock | Console:
console = MagicMock()
console.get_event.side_effect = events
console.height = 100
diff --git a/Lib/test/test_pyrepl/test_pyrepl.py b/Lib/test/test_pyrepl/test_pyrepl.py
index bdcabf9..aa27220 100644
--- a/Lib/test/test_pyrepl/test_pyrepl.py
+++ b/Lib/test/test_pyrepl/test_pyrepl.py
@@ -508,14 +508,15 @@ class TestPyReplCompleter(TestCase):
reader = ReadlineAlikeReader(console=console, config=config)
return reader
+ @patch("rlcompleter._readline_available", False)
def test_simple_completion(self):
- events = code_to_events("os.geten\t\n")
+ events = code_to_events("os.getpid\t\n")
namespace = {"os": os}
reader = self.prepare_reader(events, namespace)
output = multiline_input(reader, namespace)
- self.assertEqual(output, "os.getenv")
+ self.assertEqual(output, "os.getpid()")
def test_completion_with_many_options(self):
# Test with something that initially displays many options
diff --git a/Lib/test/test_pyrepl/test_unix_console.py b/Lib/test/test_pyrepl/test_unix_console.py
index e1faa00..d0b98f1 100644
--- a/Lib/test/test_pyrepl/test_unix_console.py
+++ b/Lib/test/test_pyrepl/test_unix_console.py
@@ -1,12 +1,16 @@
import itertools
+import sys
+import unittest
from functools import partial
from unittest import TestCase
from unittest.mock import MagicMock, call, patch, ANY
from .support import handle_all_events, code_to_events
-from _pyrepl.console import Event
-from _pyrepl.unix_console import UnixConsole
-
+try:
+ from _pyrepl.console import Event
+ from _pyrepl.unix_console import UnixConsole
+except ImportError:
+ pass
def unix_console(events, **kwargs):
console = UnixConsole()
@@ -67,6 +71,7 @@ TERM_CAPABILITIES = {
}
+@unittest.skipIf(sys.platform == "win32", "No Unix event queue on Windows")
@patch("_pyrepl.curses.tigetstr", lambda s: TERM_CAPABILITIES.get(s))
@patch(
"_pyrepl.curses.tparm",
diff --git a/Lib/test/test_pyrepl/test_unix_eventqueue.py b/Lib/test/test_pyrepl/test_unix_eventqueue.py
index c06536b..301f799 100644
--- a/Lib/test/test_pyrepl/test_unix_eventqueue.py
+++ b/Lib/test/test_pyrepl/test_unix_eventqueue.py
@@ -1,11 +1,15 @@
import tempfile
import unittest
+import sys
from unittest.mock import patch
-from _pyrepl.console import Event
-from _pyrepl.unix_eventqueue import EventQueue
-
+try:
+ from _pyrepl.console import Event
+ from _pyrepl.unix_eventqueue import EventQueue
+except ImportError:
+ pass
+@unittest.skipIf(sys.platform == "win32", "No Unix event queue on Windows")
@patch("_pyrepl.curses.tigetstr", lambda x: b"")
class TestUnixEventQueue(unittest.TestCase):
def setUp(self):
diff --git a/Lib/test/test_pyrepl/test_windows_console.py b/Lib/test/test_pyrepl/test_windows_console.py
new file mode 100644
index 0000000..e87dfe9
--- /dev/null
+++ b/Lib/test/test_pyrepl/test_windows_console.py
@@ -0,0 +1,331 @@
+import itertools
+import sys
+import unittest
+from _pyrepl.console import Event, Console
+from _pyrepl.windows_console import (
+ MOVE_LEFT,
+ MOVE_RIGHT,
+ MOVE_UP,
+ MOVE_DOWN,
+ ERASE_IN_LINE,
+)
+from functools import partial
+from typing import Iterable
+from unittest import TestCase, main
+from unittest.mock import MagicMock, call, patch, ANY
+
+from .support import handle_all_events, code_to_events
+
+try:
+ from _pyrepl.console import Event
+ from _pyrepl.windows_console import WindowsConsole
+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()