Removed the Requirement to Install Python and NodeJS (Now Bundled with Borealis)
This commit is contained in:
15
Dependencies/Python/Lib/test/test_pyrepl/__init__.py
vendored
Normal file
15
Dependencies/Python/Lib/test/test_pyrepl/__init__.py
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
import os
|
||||
import sys
|
||||
from test.support import requires, load_package_tests
|
||||
from test.support.import_helper import import_module
|
||||
|
||||
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):
|
||||
return load_package_tests(os.path.dirname(__file__), *args)
|
4
Dependencies/Python/Lib/test/test_pyrepl/__main__.py
vendored
Normal file
4
Dependencies/Python/Lib/test/test_pyrepl/__main__.py
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
import unittest
|
||||
from test.test_pyrepl import load_tests
|
||||
|
||||
unittest.main()
|
175
Dependencies/Python/Lib/test/test_pyrepl/support.py
vendored
Normal file
175
Dependencies/Python/Lib/test/test_pyrepl/support.py
vendored
Normal file
@ -0,0 +1,175 @@
|
||||
import os
|
||||
from code import InteractiveConsole
|
||||
from functools import partial
|
||||
from typing import Iterable
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from _pyrepl.console import Console, Event
|
||||
from _pyrepl.readline import ReadlineAlikeReader, ReadlineConfig
|
||||
from _pyrepl.simple_interact import _strip_final_indent
|
||||
from _pyrepl.utils import unbracket, ANSI_ESCAPE_SEQUENCE
|
||||
|
||||
|
||||
class ScreenEqualMixin:
|
||||
def assert_screen_equal(
|
||||
self, reader: ReadlineAlikeReader, expected: str, clean: bool = False
|
||||
):
|
||||
actual = clean_screen(reader) if clean else reader.screen
|
||||
expected = expected.split("\n")
|
||||
self.assertListEqual(actual, expected)
|
||||
|
||||
|
||||
def multiline_input(reader: ReadlineAlikeReader, namespace: dict | None = None):
|
||||
saved = reader.more_lines
|
||||
try:
|
||||
reader.more_lines = partial(more_lines, namespace=namespace)
|
||||
reader.ps1 = reader.ps2 = ">>> "
|
||||
reader.ps3 = reader.ps4 = "... "
|
||||
return reader.readline()
|
||||
finally:
|
||||
reader.more_lines = saved
|
||||
reader.paste_mode = False
|
||||
|
||||
|
||||
def more_lines(text: str, namespace: dict | None = None):
|
||||
if namespace is None:
|
||||
namespace = {}
|
||||
src = _strip_final_indent(text)
|
||||
console = InteractiveConsole(namespace, filename="<stdin>")
|
||||
try:
|
||||
code = console.compile(src, "<stdin>", "single")
|
||||
except (OverflowError, SyntaxError, ValueError):
|
||||
return False
|
||||
else:
|
||||
return code is None
|
||||
|
||||
|
||||
def code_to_events(code: str):
|
||||
for c in code:
|
||||
yield Event(evt="key", data=c, raw=bytearray(c.encode("utf-8")))
|
||||
|
||||
|
||||
def clean_screen(reader: ReadlineAlikeReader) -> list[str]:
|
||||
"""Cleans color and console characters out of a screen output.
|
||||
|
||||
This is useful for screen testing, it increases the test readability since
|
||||
it strips out all the unreadable side of the screen.
|
||||
"""
|
||||
output = []
|
||||
for line in reader.screen:
|
||||
line = unbracket(line, including_content=True)
|
||||
line = ANSI_ESCAPE_SEQUENCE.sub("", line)
|
||||
for prefix in (reader.ps1, reader.ps2, reader.ps3, reader.ps4):
|
||||
if line.startswith(prefix):
|
||||
line = line[len(prefix):]
|
||||
break
|
||||
output.append(line)
|
||||
return output
|
||||
|
||||
|
||||
def prepare_reader(console: Console, **kwargs):
|
||||
config = ReadlineConfig(readline_completer=kwargs.pop("readline_completer", None))
|
||||
reader = ReadlineAlikeReader(console=console, config=config)
|
||||
reader.more_lines = partial(more_lines, namespace=None)
|
||||
reader.paste_mode = True # Avoid extra indents
|
||||
|
||||
def get_prompt(lineno, cursor_on_line) -> str:
|
||||
return ""
|
||||
|
||||
reader.get_prompt = get_prompt # Remove prompt for easier calculations of (x, y)
|
||||
|
||||
for key, val in kwargs.items():
|
||||
setattr(reader, key, val)
|
||||
|
||||
return reader
|
||||
|
||||
|
||||
def prepare_console(events: Iterable[Event], **kwargs) -> MagicMock | Console:
|
||||
console = MagicMock()
|
||||
console.get_event.side_effect = events
|
||||
console.height = 100
|
||||
console.width = 80
|
||||
for key, val in kwargs.items():
|
||||
setattr(console, key, val)
|
||||
return console
|
||||
|
||||
|
||||
def handle_all_events(
|
||||
events, prepare_console=prepare_console, prepare_reader=prepare_reader
|
||||
):
|
||||
console = prepare_console(events)
|
||||
reader = prepare_reader(console)
|
||||
try:
|
||||
while True:
|
||||
reader.handle1()
|
||||
except StopIteration:
|
||||
pass
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
return reader, console
|
||||
|
||||
|
||||
handle_events_narrow_console = partial(
|
||||
handle_all_events,
|
||||
prepare_console=partial(prepare_console, width=10),
|
||||
)
|
||||
|
||||
reader_no_colors = partial(prepare_reader, can_colorize=False)
|
||||
reader_force_colors = partial(prepare_reader, can_colorize=True)
|
||||
|
||||
|
||||
class FakeConsole(Console):
|
||||
def __init__(self, events, encoding="utf-8") -> None:
|
||||
self.events = iter(events)
|
||||
self.encoding = encoding
|
||||
self.screen = []
|
||||
self.height = 100
|
||||
self.width = 80
|
||||
|
||||
def get_event(self, block: bool = True) -> Event | None:
|
||||
return next(self.events)
|
||||
|
||||
def getpending(self) -> Event:
|
||||
return self.get_event(block=False)
|
||||
|
||||
def getheightwidth(self) -> tuple[int, int]:
|
||||
return self.height, self.width
|
||||
|
||||
def refresh(self, screen: list[str], xy: tuple[int, int]) -> None:
|
||||
pass
|
||||
|
||||
def prepare(self) -> None:
|
||||
pass
|
||||
|
||||
def restore(self) -> None:
|
||||
pass
|
||||
|
||||
def move_cursor(self, x: int, y: int) -> None:
|
||||
pass
|
||||
|
||||
def set_cursor_vis(self, visible: bool) -> None:
|
||||
pass
|
||||
|
||||
def push_char(self, char: int | bytes) -> None:
|
||||
pass
|
||||
|
||||
def beep(self) -> None:
|
||||
pass
|
||||
|
||||
def clear(self) -> None:
|
||||
pass
|
||||
|
||||
def finish(self) -> None:
|
||||
pass
|
||||
|
||||
def flushoutput(self) -> None:
|
||||
pass
|
||||
|
||||
def forgetinput(self) -> None:
|
||||
pass
|
||||
|
||||
def wait(self, timeout: float | None = None) -> bool:
|
||||
return True
|
||||
|
||||
def repaint(self) -> None:
|
||||
pass
|
102
Dependencies/Python/Lib/test/test_pyrepl/test_input.py
vendored
Normal file
102
Dependencies/Python/Lib/test/test_pyrepl/test_input.py
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
import unittest
|
||||
|
||||
from _pyrepl.console import Event
|
||||
from _pyrepl.input import KeymapTranslator
|
||||
|
||||
|
||||
class KeymapTranslatorTests(unittest.TestCase):
|
||||
def test_push_single_key(self):
|
||||
keymap = [("a", "command_a")]
|
||||
translator = KeymapTranslator(keymap)
|
||||
evt = Event("key", "a")
|
||||
translator.push(evt)
|
||||
result = translator.get()
|
||||
self.assertEqual(result, ("command_a", ["a"]))
|
||||
|
||||
def test_push_multiple_keys(self):
|
||||
keymap = [("ab", "command_ab")]
|
||||
translator = KeymapTranslator(keymap)
|
||||
evt1 = Event("key", "a")
|
||||
evt2 = Event("key", "b")
|
||||
translator.push(evt1)
|
||||
translator.push(evt2)
|
||||
result = translator.get()
|
||||
self.assertEqual(result, ("command_ab", ["a", "b"]))
|
||||
|
||||
def test_push_invalid_key(self):
|
||||
keymap = [("a", "command_a")]
|
||||
translator = KeymapTranslator(keymap)
|
||||
evt = Event("key", "b")
|
||||
translator.push(evt)
|
||||
result = translator.get()
|
||||
self.assertEqual(result, (None, ["b"]))
|
||||
|
||||
def test_push_invalid_key_with_stack(self):
|
||||
keymap = [("ab", "command_ab")]
|
||||
translator = KeymapTranslator(keymap)
|
||||
evt1 = Event("key", "a")
|
||||
evt2 = Event("key", "c")
|
||||
translator.push(evt1)
|
||||
translator.push(evt2)
|
||||
result = translator.get()
|
||||
self.assertEqual(result, (None, ["a", "c"]))
|
||||
|
||||
def test_push_character_key(self):
|
||||
keymap = [("a", "command_a")]
|
||||
translator = KeymapTranslator(keymap)
|
||||
evt = Event("key", "a")
|
||||
translator.push(evt)
|
||||
result = translator.get()
|
||||
self.assertEqual(result, ("command_a", ["a"]))
|
||||
|
||||
def test_push_character_key_with_stack(self):
|
||||
keymap = [("ab", "command_ab")]
|
||||
translator = KeymapTranslator(keymap)
|
||||
evt1 = Event("key", "a")
|
||||
evt2 = Event("key", "b")
|
||||
evt3 = Event("key", "c")
|
||||
translator.push(evt1)
|
||||
translator.push(evt2)
|
||||
translator.push(evt3)
|
||||
result = translator.get()
|
||||
self.assertEqual(result, ("command_ab", ["a", "b"]))
|
||||
|
||||
def test_push_transition_key(self):
|
||||
keymap = [("a", {"b": "command_ab"})]
|
||||
translator = KeymapTranslator(keymap)
|
||||
evt1 = Event("key", "a")
|
||||
evt2 = Event("key", "b")
|
||||
translator.push(evt1)
|
||||
translator.push(evt2)
|
||||
result = translator.get()
|
||||
self.assertEqual(result, ("command_ab", ["a", "b"]))
|
||||
|
||||
def test_push_transition_key_interrupted(self):
|
||||
keymap = [("a", {"b": "command_ab"})]
|
||||
translator = KeymapTranslator(keymap)
|
||||
evt1 = Event("key", "a")
|
||||
evt2 = Event("key", "c")
|
||||
evt3 = Event("key", "b")
|
||||
translator.push(evt1)
|
||||
translator.push(evt2)
|
||||
translator.push(evt3)
|
||||
result = translator.get()
|
||||
self.assertEqual(result, (None, ["a", "c"]))
|
||||
|
||||
def test_push_invalid_key_with_unicode_category(self):
|
||||
keymap = [("a", "command_a")]
|
||||
translator = KeymapTranslator(keymap)
|
||||
evt = Event("key", "\u0003") # Control character
|
||||
translator.push(evt)
|
||||
result = translator.get()
|
||||
self.assertEqual(result, (None, ["\u0003"]))
|
||||
|
||||
def test_empty(self):
|
||||
keymap = [("a", "command_a")]
|
||||
translator = KeymapTranslator(keymap)
|
||||
self.assertTrue(translator.empty())
|
||||
evt = Event("key", "a")
|
||||
translator.push(evt)
|
||||
self.assertFalse(translator.empty())
|
||||
translator.get()
|
||||
self.assertTrue(translator.empty())
|
275
Dependencies/Python/Lib/test/test_pyrepl/test_interact.py
vendored
Normal file
275
Dependencies/Python/Lib/test/test_pyrepl/test_interact.py
vendored
Normal file
@ -0,0 +1,275 @@
|
||||
import contextlib
|
||||
import io
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
from textwrap import dedent
|
||||
|
||||
from test.support import force_not_colorized
|
||||
|
||||
from _pyrepl.console import InteractiveColoredConsole
|
||||
from _pyrepl.simple_interact import _more_lines
|
||||
|
||||
class TestSimpleInteract(unittest.TestCase):
|
||||
def test_multiple_statements(self):
|
||||
namespace = {}
|
||||
code = dedent("""\
|
||||
class A:
|
||||
def foo(self):
|
||||
|
||||
|
||||
pass
|
||||
|
||||
class B:
|
||||
def bar(self):
|
||||
pass
|
||||
|
||||
a = 1
|
||||
a
|
||||
""")
|
||||
console = InteractiveColoredConsole(namespace, filename="<stdin>")
|
||||
f = io.StringIO()
|
||||
with (
|
||||
patch.object(InteractiveColoredConsole, "showsyntaxerror") as showsyntaxerror,
|
||||
patch.object(InteractiveColoredConsole, "runsource", wraps=console.runsource) as runsource,
|
||||
contextlib.redirect_stdout(f),
|
||||
):
|
||||
more = console.push(code, filename="<stdin>", _symbol="single") # type: ignore[call-arg]
|
||||
self.assertFalse(more)
|
||||
showsyntaxerror.assert_not_called()
|
||||
|
||||
|
||||
def test_multiple_statements_output(self):
|
||||
namespace = {}
|
||||
code = dedent("""\
|
||||
b = 1
|
||||
b
|
||||
a = 1
|
||||
a
|
||||
""")
|
||||
console = InteractiveColoredConsole(namespace, filename="<stdin>")
|
||||
f = io.StringIO()
|
||||
with contextlib.redirect_stdout(f):
|
||||
more = console.push(code, filename="<stdin>", _symbol="single") # type: ignore[call-arg]
|
||||
self.assertFalse(more)
|
||||
self.assertEqual(f.getvalue(), "1\n")
|
||||
|
||||
@force_not_colorized
|
||||
def test_multiple_statements_fail_early(self):
|
||||
console = InteractiveColoredConsole()
|
||||
code = dedent("""\
|
||||
raise Exception('foobar')
|
||||
print('spam', 'eggs', sep='&')
|
||||
""")
|
||||
f = io.StringIO()
|
||||
with contextlib.redirect_stderr(f):
|
||||
console.runsource(code)
|
||||
self.assertIn('Exception: foobar', f.getvalue())
|
||||
self.assertNotIn('spam&eggs', f.getvalue())
|
||||
|
||||
def test_empty(self):
|
||||
namespace = {}
|
||||
code = ""
|
||||
console = InteractiveColoredConsole(namespace, filename="<stdin>")
|
||||
f = io.StringIO()
|
||||
with contextlib.redirect_stdout(f):
|
||||
more = console.push(code, filename="<stdin>", _symbol="single") # type: ignore[call-arg]
|
||||
self.assertFalse(more)
|
||||
self.assertEqual(f.getvalue(), "")
|
||||
|
||||
def test_runsource_compiles_and_runs_code(self):
|
||||
console = InteractiveColoredConsole()
|
||||
source = "print('Hello, world!')"
|
||||
with patch.object(console, "runcode") as mock_runcode:
|
||||
console.runsource(source)
|
||||
mock_runcode.assert_called_once()
|
||||
|
||||
def test_runsource_returns_false_for_successful_compilation(self):
|
||||
console = InteractiveColoredConsole()
|
||||
source = "print('Hello, world!')"
|
||||
f = io.StringIO()
|
||||
with contextlib.redirect_stdout(f):
|
||||
result = console.runsource(source)
|
||||
self.assertFalse(result)
|
||||
|
||||
@force_not_colorized
|
||||
def test_runsource_returns_false_for_failed_compilation(self):
|
||||
console = InteractiveColoredConsole()
|
||||
source = "print('Hello, world!'"
|
||||
f = io.StringIO()
|
||||
with contextlib.redirect_stderr(f):
|
||||
result = console.runsource(source)
|
||||
self.assertFalse(result)
|
||||
self.assertIn('SyntaxError', f.getvalue())
|
||||
|
||||
@force_not_colorized
|
||||
def test_runsource_show_syntax_error_location(self):
|
||||
console = InteractiveColoredConsole()
|
||||
source = "def f(x, x): ..."
|
||||
f = io.StringIO()
|
||||
with contextlib.redirect_stderr(f):
|
||||
result = console.runsource(source)
|
||||
self.assertFalse(result)
|
||||
r = """
|
||||
def f(x, x): ...
|
||||
^
|
||||
SyntaxError: duplicate argument 'x' in function definition"""
|
||||
self.assertIn(r, f.getvalue())
|
||||
|
||||
def test_runsource_shows_syntax_error_for_failed_compilation(self):
|
||||
console = InteractiveColoredConsole()
|
||||
source = "print('Hello, world!'"
|
||||
with patch.object(console, "showsyntaxerror") as mock_showsyntaxerror:
|
||||
console.runsource(source)
|
||||
mock_showsyntaxerror.assert_called_once()
|
||||
source = dedent("""\
|
||||
match 1:
|
||||
case {0: _, 0j: _}:
|
||||
pass
|
||||
""")
|
||||
with patch.object(console, "showsyntaxerror") as mock_showsyntaxerror:
|
||||
console.runsource(source)
|
||||
mock_showsyntaxerror.assert_called_once()
|
||||
|
||||
def test_runsource_survives_null_bytes(self):
|
||||
console = InteractiveColoredConsole()
|
||||
source = "\x00\n"
|
||||
f = io.StringIO()
|
||||
with contextlib.redirect_stdout(f), contextlib.redirect_stderr(f):
|
||||
result = console.runsource(source)
|
||||
self.assertFalse(result)
|
||||
self.assertIn("source code string cannot contain null bytes", f.getvalue())
|
||||
|
||||
def test_no_active_future(self):
|
||||
console = InteractiveColoredConsole()
|
||||
source = dedent("""\
|
||||
x: int = 1
|
||||
print(__annotations__)
|
||||
""")
|
||||
f = io.StringIO()
|
||||
with contextlib.redirect_stdout(f):
|
||||
result = console.runsource(source)
|
||||
self.assertFalse(result)
|
||||
self.assertEqual(f.getvalue(), "{'x': <class 'int'>}\n")
|
||||
|
||||
def test_future_annotations(self):
|
||||
console = InteractiveColoredConsole()
|
||||
source = dedent("""\
|
||||
from __future__ import annotations
|
||||
def g(x: int): ...
|
||||
print(g.__annotations__)
|
||||
""")
|
||||
f = io.StringIO()
|
||||
with contextlib.redirect_stdout(f):
|
||||
result = console.runsource(source)
|
||||
self.assertFalse(result)
|
||||
self.assertEqual(f.getvalue(), "{'x': 'int'}\n")
|
||||
|
||||
def test_future_barry_as_flufl(self):
|
||||
console = InteractiveColoredConsole()
|
||||
f = io.StringIO()
|
||||
with contextlib.redirect_stdout(f):
|
||||
result = console.runsource("from __future__ import barry_as_FLUFL\n")
|
||||
result = console.runsource("""print("black" <> 'blue')\n""")
|
||||
self.assertFalse(result)
|
||||
self.assertEqual(f.getvalue(), "True\n")
|
||||
|
||||
|
||||
class TestMoreLines(unittest.TestCase):
|
||||
def test_invalid_syntax_single_line(self):
|
||||
namespace = {}
|
||||
code = "if foo"
|
||||
console = InteractiveColoredConsole(namespace, filename="<stdin>")
|
||||
self.assertFalse(_more_lines(console, code))
|
||||
|
||||
def test_empty_line(self):
|
||||
namespace = {}
|
||||
code = ""
|
||||
console = InteractiveColoredConsole(namespace, filename="<stdin>")
|
||||
self.assertFalse(_more_lines(console, code))
|
||||
|
||||
def test_valid_single_statement(self):
|
||||
namespace = {}
|
||||
code = "foo = 1"
|
||||
console = InteractiveColoredConsole(namespace, filename="<stdin>")
|
||||
self.assertFalse(_more_lines(console, code))
|
||||
|
||||
def test_multiline_single_assignment(self):
|
||||
namespace = {}
|
||||
code = dedent("""\
|
||||
foo = [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
]""")
|
||||
console = InteractiveColoredConsole(namespace, filename="<stdin>")
|
||||
self.assertFalse(_more_lines(console, code))
|
||||
|
||||
def test_multiline_single_block(self):
|
||||
namespace = {}
|
||||
code = dedent("""\
|
||||
def foo():
|
||||
'''docs'''
|
||||
|
||||
return 1""")
|
||||
console = InteractiveColoredConsole(namespace, filename="<stdin>")
|
||||
self.assertTrue(_more_lines(console, code))
|
||||
|
||||
def test_multiple_statements_single_line(self):
|
||||
namespace = {}
|
||||
code = "foo = 1;bar = 2"
|
||||
console = InteractiveColoredConsole(namespace, filename="<stdin>")
|
||||
self.assertFalse(_more_lines(console, code))
|
||||
|
||||
def test_multiple_statements(self):
|
||||
namespace = {}
|
||||
code = dedent("""\
|
||||
import time
|
||||
|
||||
foo = 1""")
|
||||
console = InteractiveColoredConsole(namespace, filename="<stdin>")
|
||||
self.assertTrue(_more_lines(console, code))
|
||||
|
||||
def test_multiple_blocks(self):
|
||||
namespace = {}
|
||||
code = dedent("""\
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class Point:
|
||||
x: float
|
||||
y: float""")
|
||||
console = InteractiveColoredConsole(namespace, filename="<stdin>")
|
||||
self.assertTrue(_more_lines(console, code))
|
||||
|
||||
def test_multiple_blocks_empty_newline(self):
|
||||
namespace = {}
|
||||
code = dedent("""\
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class Point:
|
||||
x: float
|
||||
y: float
|
||||
""")
|
||||
console = InteractiveColoredConsole(namespace, filename="<stdin>")
|
||||
self.assertFalse(_more_lines(console, code))
|
||||
|
||||
def test_multiple_blocks_indented_newline(self):
|
||||
namespace = {}
|
||||
code = (
|
||||
"from dataclasses import dataclass\n"
|
||||
"\n"
|
||||
"@dataclass\n"
|
||||
"class Point:\n"
|
||||
" x: float\n"
|
||||
" y: float\n"
|
||||
" "
|
||||
)
|
||||
console = InteractiveColoredConsole(namespace, filename="<stdin>")
|
||||
self.assertFalse(_more_lines(console, code))
|
||||
|
||||
def test_incomplete_statement(self):
|
||||
namespace = {}
|
||||
code = "if foo:"
|
||||
console = InteractiveColoredConsole(namespace, filename="<stdin>")
|
||||
self.assertTrue(_more_lines(console, code))
|
120
Dependencies/Python/Lib/test/test_pyrepl/test_keymap.py
vendored
Normal file
120
Dependencies/Python/Lib/test/test_pyrepl/test_keymap.py
vendored
Normal file
@ -0,0 +1,120 @@
|
||||
import string
|
||||
import unittest
|
||||
|
||||
from _pyrepl.keymap import _keynames, _escapes, parse_keys, compile_keymap, KeySpecError
|
||||
|
||||
|
||||
class TestParseKeys(unittest.TestCase):
|
||||
def test_single_character(self):
|
||||
"""Ensure that single ascii characters or single digits are parsed as single characters."""
|
||||
test_cases = [(key, [key]) for key in string.ascii_letters + string.digits]
|
||||
for test_key, expected_keys in test_cases:
|
||||
with self.subTest(f"{test_key} should be parsed as {expected_keys}"):
|
||||
self.assertEqual(parse_keys(test_key), expected_keys)
|
||||
|
||||
def test_keynames(self):
|
||||
"""Ensure that keynames are parsed to their corresponding mapping.
|
||||
|
||||
A keyname is expected to be of the following form: \\<keyname> such as \\<left>
|
||||
which would get parsed as "left".
|
||||
"""
|
||||
test_cases = [(f"\\<{keyname}>", [parsed_keyname]) for keyname, parsed_keyname in _keynames.items()]
|
||||
for test_key, expected_keys in test_cases:
|
||||
with self.subTest(f"{test_key} should be parsed as {expected_keys}"):
|
||||
self.assertEqual(parse_keys(test_key), expected_keys)
|
||||
|
||||
def test_escape_sequences(self):
|
||||
"""Ensure that escaping sequences are parsed to their corresponding mapping."""
|
||||
test_cases = [(f"\\{escape}", [parsed_escape]) for escape, parsed_escape in _escapes.items()]
|
||||
for test_key, expected_keys in test_cases:
|
||||
with self.subTest(f"{test_key} should be parsed as {expected_keys}"):
|
||||
self.assertEqual(parse_keys(test_key), expected_keys)
|
||||
|
||||
def test_control_sequences(self):
|
||||
"""Ensure that supported control sequences are parsed successfully."""
|
||||
keys = ["@", "[", "]", "\\", "^", "_", "\\<space>", "\\<delete>"]
|
||||
keys.extend(string.ascii_letters)
|
||||
test_cases = [(f"\\C-{key}", chr(ord(key) & 0x1F)) for key in []]
|
||||
for test_key, expected_keys in test_cases:
|
||||
with self.subTest(f"{test_key} should be parsed as {expected_keys}"):
|
||||
self.assertEqual(parse_keys(test_key), expected_keys)
|
||||
|
||||
def test_meta_sequences(self):
|
||||
self.assertEqual(parse_keys("\\M-a"), ["\033", "a"])
|
||||
self.assertEqual(parse_keys("\\M-b"), ["\033", "b"])
|
||||
self.assertEqual(parse_keys("\\M-c"), ["\033", "c"])
|
||||
|
||||
def test_combinations(self):
|
||||
self.assertEqual(parse_keys("\\C-a\\n\\<up>"), ["\x01", "\n", "up"])
|
||||
self.assertEqual(parse_keys("\\M-a\\t\\<down>"), ["\033", "a", "\t", "down"])
|
||||
|
||||
def test_keyspec_errors(self):
|
||||
cases = [
|
||||
("\\Ca", "\\C must be followed by `-'"),
|
||||
("\\ca", "\\C must be followed by `-'"),
|
||||
("\\C-\\C-", "doubled \\C-"),
|
||||
("\\Ma", "\\M must be followed by `-'"),
|
||||
("\\ma", "\\M must be followed by `-'"),
|
||||
("\\M-\\M-", "doubled \\M-"),
|
||||
("\\<left", "unterminated \\<"),
|
||||
("\\<unsupported>", "unrecognised keyname"),
|
||||
("\\大", "unknown backslash escape"),
|
||||
("\\C-\\<backspace>", "\\C- followed by invalid key")
|
||||
]
|
||||
for test_keys, expected_err in cases:
|
||||
with self.subTest(f"{test_keys} should give error {expected_err}"):
|
||||
with self.assertRaises(KeySpecError) as e:
|
||||
parse_keys(test_keys)
|
||||
self.assertIn(expected_err, str(e.exception))
|
||||
|
||||
def test_index_errors(self):
|
||||
test_cases = ["\\", "\\C", "\\C-\\C"]
|
||||
for test_keys in test_cases:
|
||||
with self.assertRaises(IndexError):
|
||||
parse_keys(test_keys)
|
||||
|
||||
|
||||
class TestCompileKeymap(unittest.TestCase):
|
||||
def test_empty_keymap(self):
|
||||
keymap = {}
|
||||
result = compile_keymap(keymap)
|
||||
self.assertEqual(result, {})
|
||||
|
||||
def test_single_keymap(self):
|
||||
keymap = {b"a": "action"}
|
||||
result = compile_keymap(keymap)
|
||||
self.assertEqual(result, {b"a": "action"})
|
||||
|
||||
def test_nested_keymap(self):
|
||||
keymap = {b"a": {b"b": "action"}}
|
||||
result = compile_keymap(keymap)
|
||||
self.assertEqual(result, {b"a": {b"b": "action"}})
|
||||
|
||||
def test_empty_value(self):
|
||||
keymap = {b"a": {b"": "action"}}
|
||||
result = compile_keymap(keymap)
|
||||
self.assertEqual(result, {b"a": {b"": "action"}})
|
||||
|
||||
def test_multiple_empty_values(self):
|
||||
keymap = {b"a": {b"": "action1", b"b": "action2"}}
|
||||
result = compile_keymap(keymap)
|
||||
self.assertEqual(result, {b"a": {b"": "action1", b"b": "action2"}})
|
||||
|
||||
def test_multiple_keymaps(self):
|
||||
keymap = {b"a": {b"b": "action1", b"c": "action2"}}
|
||||
result = compile_keymap(keymap)
|
||||
self.assertEqual(result, {b"a": {b"b": "action1", b"c": "action2"}})
|
||||
|
||||
def test_nested_multiple_keymaps(self):
|
||||
keymap = {b"a": {b"b": {b"c": "action"}}}
|
||||
result = compile_keymap(keymap)
|
||||
self.assertEqual(result, {b"a": {b"b": {b"c": "action"}}})
|
||||
|
||||
def test_clashing_definitions(self):
|
||||
km = {b'a': 'c', b'a' + b'b': 'd'}
|
||||
with self.assertRaises(KeySpecError):
|
||||
compile_keymap(km)
|
||||
|
||||
def test_non_bytes_key(self):
|
||||
with self.assertRaises(TypeError):
|
||||
compile_keymap({123: 'a'})
|
1358
Dependencies/Python/Lib/test/test_pyrepl/test_pyrepl.py
vendored
Normal file
1358
Dependencies/Python/Lib/test/test_pyrepl/test_pyrepl.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
357
Dependencies/Python/Lib/test/test_pyrepl/test_reader.py
vendored
Normal file
357
Dependencies/Python/Lib/test/test_pyrepl/test_reader.py
vendored
Normal file
@ -0,0 +1,357 @@
|
||||
import itertools
|
||||
import functools
|
||||
import rlcompleter
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from .support import handle_all_events, handle_events_narrow_console
|
||||
from .support import ScreenEqualMixin, code_to_events
|
||||
from .support import prepare_reader, prepare_console
|
||||
from _pyrepl.console import Event
|
||||
from _pyrepl.reader import Reader
|
||||
|
||||
|
||||
class TestReader(ScreenEqualMixin, TestCase):
|
||||
def test_calc_screen_wrap_simple(self):
|
||||
events = code_to_events(10 * "a")
|
||||
reader, _ = handle_events_narrow_console(events)
|
||||
self.assert_screen_equal(reader, f"{9*"a"}\\\na")
|
||||
|
||||
def test_calc_screen_wrap_wide_characters(self):
|
||||
events = code_to_events(8 * "a" + "樂")
|
||||
reader, _ = handle_events_narrow_console(events)
|
||||
self.assert_screen_equal(reader, f"{8*"a"}\\\n樂")
|
||||
|
||||
def test_calc_screen_wrap_three_lines(self):
|
||||
events = code_to_events(20 * "a")
|
||||
reader, _ = handle_events_narrow_console(events)
|
||||
self.assert_screen_equal(reader, f"{9*"a"}\\\n{9*"a"}\\\naa")
|
||||
|
||||
def test_calc_screen_prompt_handling(self):
|
||||
def prepare_reader_keep_prompts(*args, **kwargs):
|
||||
reader = prepare_reader(*args, **kwargs)
|
||||
del reader.get_prompt
|
||||
reader.ps1 = ">>> "
|
||||
reader.ps2 = ">>> "
|
||||
reader.ps3 = "... "
|
||||
reader.ps4 = ""
|
||||
reader.can_colorize = False
|
||||
reader.paste_mode = False
|
||||
return reader
|
||||
|
||||
events = code_to_events("if some_condition:\nsome_function()")
|
||||
reader, _ = handle_events_narrow_console(
|
||||
events,
|
||||
prepare_reader=prepare_reader_keep_prompts,
|
||||
)
|
||||
# fmt: off
|
||||
self.assert_screen_equal(
|
||||
reader,
|
||||
(
|
||||
">>> if so\\\n"
|
||||
"me_condit\\\n"
|
||||
"ion:\n"
|
||||
"... s\\\n"
|
||||
"ome_funct\\\n"
|
||||
"ion()"
|
||||
)
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
def test_calc_screen_wrap_three_lines_mixed_character(self):
|
||||
# fmt: off
|
||||
code = (
|
||||
"def f():\n"
|
||||
f" {8*"a"}\n"
|
||||
f" {5*"樂"}"
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
events = code_to_events(code)
|
||||
reader, _ = handle_events_narrow_console(events)
|
||||
|
||||
# fmt: off
|
||||
self.assert_screen_equal(
|
||||
reader,
|
||||
(
|
||||
"def f():\n"
|
||||
f" {7*"a"}\\\n"
|
||||
"a\n"
|
||||
f" {3*"樂"}\\\n"
|
||||
"樂樂"
|
||||
),
|
||||
clean=True,
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
def test_calc_screen_backspace(self):
|
||||
events = itertools.chain(
|
||||
code_to_events("aaa"),
|
||||
[
|
||||
Event(evt="key", data="backspace", raw=bytearray(b"\x7f")),
|
||||
],
|
||||
)
|
||||
reader, _ = handle_all_events(events)
|
||||
self.assert_screen_equal(reader, "aa")
|
||||
|
||||
def test_calc_screen_wrap_removes_after_backspace(self):
|
||||
events = itertools.chain(
|
||||
code_to_events(10 * "a"),
|
||||
[
|
||||
Event(evt="key", data="backspace", raw=bytearray(b"\x7f")),
|
||||
],
|
||||
)
|
||||
reader, _ = handle_events_narrow_console(events)
|
||||
self.assert_screen_equal(reader, 9 * "a")
|
||||
|
||||
def test_calc_screen_backspace_in_second_line_after_wrap(self):
|
||||
events = itertools.chain(
|
||||
code_to_events(11 * "a"),
|
||||
[
|
||||
Event(evt="key", data="backspace", raw=bytearray(b"\x7f")),
|
||||
],
|
||||
)
|
||||
reader, _ = handle_events_narrow_console(events)
|
||||
self.assert_screen_equal(reader, f"{9*"a"}\\\na")
|
||||
|
||||
def test_setpos_for_xy_simple(self):
|
||||
events = code_to_events("11+11")
|
||||
reader, _ = handle_all_events(events)
|
||||
reader.setpos_from_xy(0, 0)
|
||||
self.assertEqual(reader.pos, 0)
|
||||
|
||||
def test_control_characters(self):
|
||||
code = 'flag = "🏳️🌈"'
|
||||
events = code_to_events(code)
|
||||
reader, _ = handle_all_events(events)
|
||||
self.assert_screen_equal(reader, 'flag = "🏳️\\u200d🌈"', clean=True)
|
||||
|
||||
def test_setpos_from_xy_multiple_lines(self):
|
||||
# fmt: off
|
||||
code = (
|
||||
"def foo():\n"
|
||||
" return 1"
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
events = code_to_events(code)
|
||||
reader, _ = handle_all_events(events)
|
||||
reader.setpos_from_xy(2, 1)
|
||||
self.assertEqual(reader.pos, 13)
|
||||
|
||||
def test_setpos_from_xy_after_wrap(self):
|
||||
# fmt: off
|
||||
code = (
|
||||
"def foo():\n"
|
||||
" hello"
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
events = code_to_events(code)
|
||||
reader, _ = handle_events_narrow_console(events)
|
||||
reader.setpos_from_xy(2, 2)
|
||||
self.assertEqual(reader.pos, 13)
|
||||
|
||||
def test_setpos_fromxy_in_wrapped_line(self):
|
||||
# fmt: off
|
||||
code = (
|
||||
"def foo():\n"
|
||||
" hello"
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
events = code_to_events(code)
|
||||
reader, _ = handle_events_narrow_console(events)
|
||||
reader.setpos_from_xy(0, 1)
|
||||
self.assertEqual(reader.pos, 9)
|
||||
|
||||
def test_up_arrow_after_ctrl_r(self):
|
||||
events = iter(
|
||||
[
|
||||
Event(evt="key", data="\x12", raw=bytearray(b"\x12")),
|
||||
Event(evt="key", data="up", raw=bytearray(b"\x1bOA")),
|
||||
]
|
||||
)
|
||||
|
||||
reader, _ = handle_all_events(events)
|
||||
self.assert_screen_equal(reader, "")
|
||||
|
||||
def test_newline_within_block_trailing_whitespace(self):
|
||||
# fmt: off
|
||||
code = (
|
||||
"def foo():\n"
|
||||
"a = 1\n"
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
events = itertools.chain(
|
||||
code_to_events(code),
|
||||
[
|
||||
# go to the end of the first line
|
||||
Event(evt="key", data="up", raw=bytearray(b"\x1bOA")),
|
||||
Event(evt="key", data="up", raw=bytearray(b"\x1bOA")),
|
||||
Event(evt="key", data="\x05", raw=bytearray(b"\x1bO5")),
|
||||
# new lines in-block shouldn't terminate the block
|
||||
Event(evt="key", data="\n", raw=bytearray(b"\n")),
|
||||
Event(evt="key", data="\n", raw=bytearray(b"\n")),
|
||||
# end of line 2
|
||||
Event(evt="key", data="down", raw=bytearray(b"\x1bOB")),
|
||||
Event(evt="key", data="\x05", raw=bytearray(b"\x1bO5")),
|
||||
# a double new line in-block should terminate the block
|
||||
# even if its followed by whitespace
|
||||
Event(evt="key", data="\n", raw=bytearray(b"\n")),
|
||||
Event(evt="key", data="\n", raw=bytearray(b"\n")),
|
||||
],
|
||||
)
|
||||
|
||||
no_paste_reader = functools.partial(prepare_reader, paste_mode=False)
|
||||
reader, _ = handle_all_events(events, prepare_reader=no_paste_reader)
|
||||
|
||||
expected = (
|
||||
"def foo():\n"
|
||||
" \n"
|
||||
" \n"
|
||||
" a = 1\n"
|
||||
" \n"
|
||||
" " # HistoricalReader will trim trailing whitespace
|
||||
)
|
||||
self.assert_screen_equal(reader, expected, clean=True)
|
||||
self.assertTrue(reader.finished)
|
||||
|
||||
def test_input_hook_is_called_if_set(self):
|
||||
input_hook = MagicMock()
|
||||
|
||||
def _prepare_console(events):
|
||||
console = MagicMock()
|
||||
console.get_event.side_effect = events
|
||||
console.height = 100
|
||||
console.width = 80
|
||||
console.input_hook = input_hook
|
||||
return console
|
||||
|
||||
events = code_to_events("a")
|
||||
reader, _ = handle_all_events(events, prepare_console=_prepare_console)
|
||||
|
||||
self.assertEqual(len(input_hook.mock_calls), 4)
|
||||
|
||||
def test_keyboard_interrupt_clears_screen(self):
|
||||
namespace = {"itertools": itertools}
|
||||
code = "import itertools\nitertools."
|
||||
events = itertools.chain(
|
||||
code_to_events(code),
|
||||
[
|
||||
# Two tabs for completion
|
||||
Event(evt="key", data="\t", raw=bytearray(b"\t")),
|
||||
Event(evt="key", data="\t", raw=bytearray(b"\t")),
|
||||
Event(evt="key", data="\x03", raw=bytearray(b"\x03")), # Ctrl-C
|
||||
],
|
||||
)
|
||||
console = prepare_console(events)
|
||||
reader = prepare_reader(
|
||||
console,
|
||||
readline_completer=rlcompleter.Completer(namespace).complete,
|
||||
)
|
||||
try:
|
||||
# we're not using handle_all_events() here to be able to
|
||||
# follow the KeyboardInterrupt sequence of events. Normally this
|
||||
# happens in simple_interact.run_multiline_interactive_console.
|
||||
while True:
|
||||
reader.handle1()
|
||||
except KeyboardInterrupt:
|
||||
# at this point the completions are still visible
|
||||
self.assertTrue(len(reader.screen) > 2)
|
||||
reader.refresh()
|
||||
# after the refresh, they are gone
|
||||
self.assertEqual(len(reader.screen), 2)
|
||||
self.assert_screen_equal(reader, code, clean=True)
|
||||
else:
|
||||
self.fail("KeyboardInterrupt not raised.")
|
||||
|
||||
def test_prompt_length(self):
|
||||
# Handles simple ASCII prompt
|
||||
ps1 = ">>> "
|
||||
prompt, l = Reader.process_prompt(ps1)
|
||||
self.assertEqual(prompt, ps1)
|
||||
self.assertEqual(l, 4)
|
||||
|
||||
# Handles ANSI escape sequences
|
||||
ps1 = "\033[0;32m>>> \033[0m"
|
||||
prompt, l = Reader.process_prompt(ps1)
|
||||
self.assertEqual(prompt, "\033[0;32m>>> \033[0m")
|
||||
self.assertEqual(l, 4)
|
||||
|
||||
# Handles ANSI escape sequences bracketed in \001 .. \002
|
||||
ps1 = "\001\033[0;32m\002>>> \001\033[0m\002"
|
||||
prompt, l = Reader.process_prompt(ps1)
|
||||
self.assertEqual(prompt, "\033[0;32m>>> \033[0m")
|
||||
self.assertEqual(l, 4)
|
||||
|
||||
# Handles wide characters in prompt
|
||||
ps1 = "樂>> "
|
||||
prompt, l = Reader.process_prompt(ps1)
|
||||
self.assertEqual(prompt, ps1)
|
||||
self.assertEqual(l, 5)
|
||||
|
||||
# Handles wide characters AND ANSI sequences together
|
||||
ps1 = "\001\033[0;32m\002樂>\001\033[0m\002> "
|
||||
prompt, l = Reader.process_prompt(ps1)
|
||||
self.assertEqual(prompt, "\033[0;32m樂>\033[0m> ")
|
||||
self.assertEqual(l, 5)
|
||||
|
||||
def test_completions_updated_on_key_press(self):
|
||||
namespace = {"itertools": itertools}
|
||||
code = "itertools."
|
||||
events = itertools.chain(
|
||||
code_to_events(code),
|
||||
[
|
||||
# Two tabs for completion
|
||||
Event(evt="key", data="\t", raw=bytearray(b"\t")),
|
||||
Event(evt="key", data="\t", raw=bytearray(b"\t")),
|
||||
],
|
||||
code_to_events("a"),
|
||||
)
|
||||
|
||||
completing_reader = functools.partial(
|
||||
prepare_reader,
|
||||
readline_completer=rlcompleter.Completer(namespace).complete,
|
||||
)
|
||||
reader, _ = handle_all_events(events, prepare_reader=completing_reader)
|
||||
|
||||
actual = reader.screen
|
||||
self.assertEqual(len(actual), 2)
|
||||
self.assertEqual(actual[0], f"{code}a")
|
||||
self.assertEqual(actual[1].rstrip(), "itertools.accumulate(")
|
||||
|
||||
def test_key_press_on_tab_press_once(self):
|
||||
namespace = {"itertools": itertools}
|
||||
code = "itertools."
|
||||
events = itertools.chain(
|
||||
code_to_events(code),
|
||||
[
|
||||
Event(evt="key", data="\t", raw=bytearray(b"\t")),
|
||||
],
|
||||
code_to_events("a"),
|
||||
)
|
||||
|
||||
completing_reader = functools.partial(
|
||||
prepare_reader,
|
||||
readline_completer=rlcompleter.Completer(namespace).complete,
|
||||
)
|
||||
reader, _ = handle_all_events(events, prepare_reader=completing_reader)
|
||||
|
||||
self.assert_screen_equal(reader, f"{code}a")
|
||||
|
||||
def test_pos2xy_with_no_columns(self):
|
||||
console = prepare_console([])
|
||||
reader = prepare_reader(console)
|
||||
# Simulate a resize to 0 columns
|
||||
reader.screeninfo = []
|
||||
self.assertEqual(reader.pos2xy(), (0, 0))
|
||||
|
||||
def test_setpos_from_xy_for_non_printing_char(self):
|
||||
code = "# non \u200c printing character"
|
||||
events = code_to_events(code)
|
||||
|
||||
reader, _ = handle_all_events(events)
|
||||
reader.setpos_from_xy(8, 0)
|
||||
self.assertEqual(reader.pos, 7)
|
329
Dependencies/Python/Lib/test/test_pyrepl/test_unix_console.py
vendored
Normal file
329
Dependencies/Python/Lib/test/test_pyrepl/test_unix_console.py
vendored
Normal file
@ -0,0 +1,329 @@
|
||||
import itertools
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
from functools import partial
|
||||
from test.support import os_helper
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock, call, patch, ANY
|
||||
|
||||
from .support import handle_all_events, code_to_events, reader_no_colors
|
||||
|
||||
try:
|
||||
from _pyrepl.console import Event
|
||||
from _pyrepl.unix_console import UnixConsole
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def unix_console(events, **kwargs):
|
||||
console = UnixConsole()
|
||||
console.get_event = MagicMock(side_effect=events)
|
||||
|
||||
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
|
||||
|
||||
|
||||
handle_events_unix_console = partial(
|
||||
handle_all_events,
|
||||
prepare_console=partial(unix_console),
|
||||
)
|
||||
handle_events_narrow_unix_console = partial(
|
||||
handle_all_events,
|
||||
prepare_console=partial(unix_console, width=5),
|
||||
)
|
||||
handle_events_short_unix_console = partial(
|
||||
handle_all_events,
|
||||
prepare_console=partial(unix_console, height=1),
|
||||
)
|
||||
handle_events_unix_console_height_3 = partial(
|
||||
handle_all_events, prepare_console=partial(unix_console, height=3)
|
||||
)
|
||||
|
||||
|
||||
TERM_CAPABILITIES = {
|
||||
"bel": b"\x07",
|
||||
"civis": b"\x1b[?25l",
|
||||
"clear": b"\x1b[H\x1b[2J",
|
||||
"cnorm": b"\x1b[?12l\x1b[?25h",
|
||||
"cub": b"\x1b[%p1%dD",
|
||||
"cub1": b"\x08",
|
||||
"cud": b"\x1b[%p1%dB",
|
||||
"cud1": b"\n",
|
||||
"cuf": b"\x1b[%p1%dC",
|
||||
"cuf1": b"\x1b[C",
|
||||
"cup": b"\x1b[%i%p1%d;%p2%dH",
|
||||
"cuu": b"\x1b[%p1%dA",
|
||||
"cuu1": b"\x1b[A",
|
||||
"dch1": b"\x1b[P",
|
||||
"dch": b"\x1b[%p1%dP",
|
||||
"el": b"\x1b[K",
|
||||
"hpa": b"\x1b[%i%p1%dG",
|
||||
"ich": b"\x1b[%p1%d@",
|
||||
"ich1": None,
|
||||
"ind": b"\n",
|
||||
"pad": None,
|
||||
"ri": b"\x1bM",
|
||||
"rmkx": b"\x1b[?1l\x1b>",
|
||||
"smkx": b"\x1b[?1h\x1b=",
|
||||
}
|
||||
|
||||
|
||||
@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",
|
||||
lambda s, *args: s + b":" + b",".join(str(i).encode() for i in args),
|
||||
)
|
||||
@patch("_pyrepl.curses.setupterm", lambda a, b: None)
|
||||
@patch(
|
||||
"termios.tcgetattr",
|
||||
lambda _: [
|
||||
27394,
|
||||
3,
|
||||
19200,
|
||||
536872399,
|
||||
38400,
|
||||
38400,
|
||||
[
|
||||
b"\x04",
|
||||
b"\xff",
|
||||
b"\xff",
|
||||
b"\x7f",
|
||||
b"\x17",
|
||||
b"\x15",
|
||||
b"\x12",
|
||||
b"\x00",
|
||||
b"\x03",
|
||||
b"\x1c",
|
||||
b"\x1a",
|
||||
b"\x19",
|
||||
b"\x11",
|
||||
b"\x13",
|
||||
b"\x16",
|
||||
b"\x0f",
|
||||
b"\x01",
|
||||
b"\x00",
|
||||
b"\x14",
|
||||
b"\x00",
|
||||
],
|
||||
],
|
||||
)
|
||||
@patch("termios.tcsetattr", lambda a, b, c: None)
|
||||
@patch("os.write")
|
||||
class TestConsole(TestCase):
|
||||
def test_simple_addition(self, _os_write):
|
||||
code = "12+34"
|
||||
events = code_to_events(code)
|
||||
_, con = handle_events_unix_console(events)
|
||||
_os_write.assert_any_call(ANY, b"1")
|
||||
_os_write.assert_any_call(ANY, b"2")
|
||||
_os_write.assert_any_call(ANY, b"+")
|
||||
_os_write.assert_any_call(ANY, b"3")
|
||||
_os_write.assert_any_call(ANY, b"4")
|
||||
con.restore()
|
||||
|
||||
def test_wrap(self, _os_write):
|
||||
code = "12+34"
|
||||
events = code_to_events(code)
|
||||
_, con = handle_events_narrow_unix_console(events)
|
||||
_os_write.assert_any_call(ANY, b"1")
|
||||
_os_write.assert_any_call(ANY, b"2")
|
||||
_os_write.assert_any_call(ANY, b"+")
|
||||
_os_write.assert_any_call(ANY, b"3")
|
||||
_os_write.assert_any_call(ANY, b"\\")
|
||||
_os_write.assert_any_call(ANY, b"\n")
|
||||
_os_write.assert_any_call(ANY, b"4")
|
||||
con.restore()
|
||||
|
||||
def test_cursor_left(self, _os_write):
|
||||
code = "1"
|
||||
events = itertools.chain(
|
||||
code_to_events(code),
|
||||
[Event(evt="key", data="left", raw=bytearray(b"\x1bOD"))],
|
||||
)
|
||||
_, con = handle_events_unix_console(events)
|
||||
_os_write.assert_any_call(ANY, TERM_CAPABILITIES["cub"] + b":1")
|
||||
con.restore()
|
||||
|
||||
def test_cursor_left_right(self, _os_write):
|
||||
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 = handle_events_unix_console(events)
|
||||
_os_write.assert_any_call(ANY, TERM_CAPABILITIES["cub"] + b":1")
|
||||
_os_write.assert_any_call(ANY, TERM_CAPABILITIES["cuf"] + b":1")
|
||||
con.restore()
|
||||
|
||||
def test_cursor_up(self, _os_write):
|
||||
code = "1\n2+3"
|
||||
events = itertools.chain(
|
||||
code_to_events(code),
|
||||
[Event(evt="key", data="up", raw=bytearray(b"\x1bOA"))],
|
||||
)
|
||||
_, con = handle_events_unix_console(events)
|
||||
_os_write.assert_any_call(ANY, TERM_CAPABILITIES["cuu"] + b":1")
|
||||
con.restore()
|
||||
|
||||
def test_cursor_up_down(self, _os_write):
|
||||
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 = handle_events_unix_console(events)
|
||||
_os_write.assert_any_call(ANY, TERM_CAPABILITIES["cuu"] + b":1")
|
||||
_os_write.assert_any_call(ANY, TERM_CAPABILITIES["cud"] + b":1")
|
||||
con.restore()
|
||||
|
||||
def test_cursor_back_write(self, _os_write):
|
||||
events = itertools.chain(
|
||||
code_to_events("1"),
|
||||
[Event(evt="key", data="left", raw=bytearray(b"\x1bOD"))],
|
||||
code_to_events("2"),
|
||||
)
|
||||
_, con = handle_events_unix_console(events)
|
||||
_os_write.assert_any_call(ANY, b"1")
|
||||
_os_write.assert_any_call(ANY, TERM_CAPABILITIES["cub"] + b":1")
|
||||
_os_write.assert_any_call(ANY, b"2")
|
||||
con.restore()
|
||||
|
||||
def test_multiline_function_move_up_short_terminal(self, _os_write):
|
||||
# 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 = handle_events_short_unix_console(events)
|
||||
_os_write.assert_any_call(ANY, TERM_CAPABILITIES["ri"] + b":")
|
||||
con.restore()
|
||||
|
||||
def test_multiline_function_move_up_down_short_terminal(self, _os_write):
|
||||
# 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 = handle_events_short_unix_console(events)
|
||||
_os_write.assert_any_call(ANY, TERM_CAPABILITIES["ri"] + b":")
|
||||
_os_write.assert_any_call(ANY, TERM_CAPABILITIES["ind"] + b":")
|
||||
con.restore()
|
||||
|
||||
def test_resize_bigger_on_multiline_function(self, _os_write):
|
||||
# fmt: off
|
||||
code = (
|
||||
"def f():\n"
|
||||
" foo"
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
events = itertools.chain(code_to_events(code))
|
||||
reader, console = handle_events_short_unix_console(
|
||||
events, prepare_reader=reader_no_colors
|
||||
)
|
||||
|
||||
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,
|
||||
)
|
||||
_os_write.assert_has_calls(
|
||||
[
|
||||
call(ANY, TERM_CAPABILITIES["ri"] + b":"),
|
||||
call(ANY, TERM_CAPABILITIES["cup"] + b":0,0"),
|
||||
call(ANY, b"def f():"),
|
||||
]
|
||||
)
|
||||
console.restore()
|
||||
con.restore()
|
||||
|
||||
def test_resize_smaller_on_multiline_function(self, _os_write):
|
||||
# fmt: off
|
||||
code = (
|
||||
"def f():\n"
|
||||
" foo"
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
events = itertools.chain(code_to_events(code))
|
||||
reader, console = handle_events_unix_console_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,
|
||||
)
|
||||
_os_write.assert_has_calls(
|
||||
[
|
||||
call(ANY, TERM_CAPABILITIES["ind"] + b":"),
|
||||
call(ANY, TERM_CAPABILITIES["cup"] + b":0,0"),
|
||||
call(ANY, b" foo"),
|
||||
]
|
||||
)
|
||||
console.restore()
|
||||
con.restore()
|
||||
|
||||
def test_getheightwidth_with_invalid_environ(self, _os_write):
|
||||
# gh-128636
|
||||
console = UnixConsole()
|
||||
with os_helper.EnvironmentVarGuard() as env:
|
||||
env["LINES"] = ""
|
||||
self.assertIsInstance(console.getheightwidth(), tuple)
|
||||
env["COLUMNS"] = ""
|
||||
self.assertIsInstance(console.getheightwidth(), tuple)
|
||||
os.environ = []
|
||||
self.assertIsInstance(console.getheightwidth(), tuple)
|
117
Dependencies/Python/Lib/test/test_pyrepl/test_unix_eventqueue.py
vendored
Normal file
117
Dependencies/Python/Lib/test/test_pyrepl/test_unix_eventqueue.py
vendored
Normal file
@ -0,0 +1,117 @@
|
||||
import tempfile
|
||||
import unittest
|
||||
import sys
|
||||
from unittest.mock import patch
|
||||
|
||||
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):
|
||||
self.file = tempfile.TemporaryFile()
|
||||
|
||||
def tearDown(self) -> None:
|
||||
self.file.close()
|
||||
|
||||
def test_get(self):
|
||||
eq = EventQueue(self.file.fileno(), "utf-8")
|
||||
event = Event("key", "a", b"a")
|
||||
eq.insert(event)
|
||||
self.assertEqual(eq.get(), event)
|
||||
|
||||
def test_empty(self):
|
||||
eq = EventQueue(self.file.fileno(), "utf-8")
|
||||
self.assertTrue(eq.empty())
|
||||
eq.insert(Event("key", "a", b"a"))
|
||||
self.assertFalse(eq.empty())
|
||||
|
||||
def test_flush_buf(self):
|
||||
eq = EventQueue(self.file.fileno(), "utf-8")
|
||||
eq.buf.extend(b"test")
|
||||
self.assertEqual(eq.flush_buf(), b"test")
|
||||
self.assertEqual(eq.buf, bytearray())
|
||||
|
||||
def test_insert(self):
|
||||
eq = EventQueue(self.file.fileno(), "utf-8")
|
||||
event = Event("key", "a", b"a")
|
||||
eq.insert(event)
|
||||
self.assertEqual(eq.events[0], event)
|
||||
|
||||
@patch("_pyrepl.unix_eventqueue.keymap")
|
||||
def test_push_with_key_in_keymap(self, mock_keymap):
|
||||
mock_keymap.compile_keymap.return_value = {"a": "b"}
|
||||
eq = EventQueue(self.file.fileno(), "utf-8")
|
||||
eq.keymap = {b"a": "b"}
|
||||
eq.push("a")
|
||||
mock_keymap.compile_keymap.assert_called()
|
||||
self.assertEqual(eq.events[0].evt, "key")
|
||||
self.assertEqual(eq.events[0].data, "b")
|
||||
|
||||
@patch("_pyrepl.unix_eventqueue.keymap")
|
||||
def test_push_without_key_in_keymap(self, mock_keymap):
|
||||
mock_keymap.compile_keymap.return_value = {"a": "b"}
|
||||
eq = EventQueue(self.file.fileno(), "utf-8")
|
||||
eq.keymap = {b"c": "d"}
|
||||
eq.push("a")
|
||||
mock_keymap.compile_keymap.assert_called()
|
||||
self.assertEqual(eq.events[0].evt, "key")
|
||||
self.assertEqual(eq.events[0].data, "a")
|
||||
|
||||
@patch("_pyrepl.unix_eventqueue.keymap")
|
||||
def test_push_with_keymap_in_keymap(self, mock_keymap):
|
||||
mock_keymap.compile_keymap.return_value = {"a": "b"}
|
||||
eq = EventQueue(self.file.fileno(), "utf-8")
|
||||
eq.keymap = {b"a": {b"b": "c"}}
|
||||
eq.push("a")
|
||||
mock_keymap.compile_keymap.assert_called()
|
||||
self.assertTrue(eq.empty())
|
||||
eq.push("b")
|
||||
self.assertEqual(eq.events[0].evt, "key")
|
||||
self.assertEqual(eq.events[0].data, "c")
|
||||
eq.push("d")
|
||||
self.assertEqual(eq.events[1].evt, "key")
|
||||
self.assertEqual(eq.events[1].data, "d")
|
||||
|
||||
@patch("_pyrepl.unix_eventqueue.keymap")
|
||||
def test_push_with_keymap_in_keymap_and_escape(self, mock_keymap):
|
||||
mock_keymap.compile_keymap.return_value = {"a": "b"}
|
||||
eq = EventQueue(self.file.fileno(), "utf-8")
|
||||
eq.keymap = {b"a": {b"b": "c"}}
|
||||
eq.push("a")
|
||||
mock_keymap.compile_keymap.assert_called()
|
||||
self.assertTrue(eq.empty())
|
||||
eq.flush_buf()
|
||||
eq.push("\033")
|
||||
self.assertEqual(eq.events[0].evt, "key")
|
||||
self.assertEqual(eq.events[0].data, "\033")
|
||||
eq.push("b")
|
||||
self.assertEqual(eq.events[1].evt, "key")
|
||||
self.assertEqual(eq.events[1].data, "b")
|
||||
|
||||
def test_push_special_key(self):
|
||||
eq = EventQueue(self.file.fileno(), "utf-8")
|
||||
eq.keymap = {}
|
||||
eq.push("\x1b")
|
||||
eq.push("[")
|
||||
eq.push("A")
|
||||
self.assertEqual(eq.events[0].evt, "key")
|
||||
self.assertEqual(eq.events[0].data, "\x1b")
|
||||
|
||||
def test_push_unrecognized_escape_sequence(self):
|
||||
eq = EventQueue(self.file.fileno(), "utf-8")
|
||||
eq.keymap = {}
|
||||
eq.push("\x1b")
|
||||
eq.push("[")
|
||||
eq.push("Z")
|
||||
self.assertEqual(len(eq.events), 3)
|
||||
self.assertEqual(eq.events[0].evt, "key")
|
||||
self.assertEqual(eq.events[0].data, "\x1b")
|
||||
self.assertEqual(eq.events[1].evt, "key")
|
||||
self.assertEqual(eq.events[1].data, "[")
|
||||
self.assertEqual(eq.events[2].evt, "key")
|
||||
self.assertEqual(eq.events[2].data, "Z")
|
27
Dependencies/Python/Lib/test/test_pyrepl/test_utils.py
vendored
Normal file
27
Dependencies/Python/Lib/test/test_pyrepl/test_utils.py
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
from unittest import TestCase
|
||||
|
||||
from _pyrepl.utils import str_width, wlen
|
||||
|
||||
|
||||
class TestUtils(TestCase):
|
||||
def test_str_width(self):
|
||||
characters = ['a', '1', '_', '!', '\x1a', '\u263A', '\uffb9']
|
||||
for c in characters:
|
||||
self.assertEqual(str_width(c), 1)
|
||||
|
||||
characters = [chr(99989), chr(99999)]
|
||||
for c in characters:
|
||||
self.assertEqual(str_width(c), 2)
|
||||
|
||||
def test_wlen(self):
|
||||
for c in ['a', 'b', '1', '!', '_']:
|
||||
self.assertEqual(wlen(c), 1)
|
||||
self.assertEqual(wlen('\x1a'), 2)
|
||||
|
||||
char_east_asian_width_N = chr(3800)
|
||||
self.assertEqual(wlen(char_east_asian_width_N), 1)
|
||||
char_east_asian_width_W = chr(4352)
|
||||
self.assertEqual(wlen(char_east_asian_width_W), 2)
|
||||
|
||||
self.assertEqual(wlen('hello'), 5)
|
||||
self.assertEqual(wlen('hello' + '\x1a'), 7)
|
348
Dependencies/Python/Lib/test/test_pyrepl/test_windows_console.py
vendored
Normal file
348
Dependencies/Python/Lib/test/test_pyrepl/test_windows_console.py
vendored
Normal file
@ -0,0 +1,348 @@
|
||||
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")
|
||||
|
||||
def test_multiline_ctrl_z(self):
|
||||
# see gh-126332
|
||||
code = "abcdefghi"
|
||||
|
||||
events = itertools.chain(
|
||||
code_to_events(code),
|
||||
[
|
||||
Event(evt="key", data='\x1a', raw=bytearray(b'\x1a')),
|
||||
Event(evt="key", data='\x1a', raw=bytearray(b'\x1a')),
|
||||
],
|
||||
)
|
||||
reader, _ = self.handle_events_narrow(events)
|
||||
self.assertEqual(reader.cxy, (2, 3))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
Reference in New Issue
Block a user