From 919078ca1faa8391981ba0be3fd94e1d0fb3ea50 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Tue, 7 May 2024 13:34:33 +0100 Subject: [PATCH 1/2] gh-111201: Add more tests to test_pyrepl to cover key translation --- Lib/test/test_pyrepl.py | 169 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/Lib/test/test_pyrepl.py b/Lib/test/test_pyrepl.py index 3cba37c70e9557..b0c85794474631 100644 --- a/Lib/test/test_pyrepl.py +++ b/Lib/test/test_pyrepl.py @@ -23,6 +23,8 @@ from _pyrepl.readline import ReadlineAlikeReader, ReadlineConfig from _pyrepl.simple_interact import _strip_final_indent from _pyrepl.unix_eventqueue import EventQueue +from _pyrepl.input import KeymapTranslator +from _pyrepl.keymap import parse_keys, compile_keymap def more_lines(unicodetext, namespace=None): @@ -931,6 +933,173 @@ def test_setpos_fromxy_in_wrapped_line(self): reader.setpos_from_xy(0, 1) self.assertEqual(reader.pos, 9) +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()) + +class TestParseKeys(unittest.TestCase): + def test_single_character(self): + self.assertEqual(parse_keys("a"), ["a"]) + self.assertEqual(parse_keys("b"), ["b"]) + self.assertEqual(parse_keys("1"), ["1"]) + + def test_escape_sequences(self): + self.assertEqual(parse_keys("\\n"), ["\n"]) + self.assertEqual(parse_keys("\\t"), ["\t"]) + self.assertEqual(parse_keys("\\\\"), ["\\"]) + self.assertEqual(parse_keys("\\'"), ["'"]) + self.assertEqual(parse_keys('\\"'), ['"']) + + def test_control_sequences(self): + self.assertEqual(parse_keys("\\C-a"), ["\x01"]) + self.assertEqual(parse_keys("\\C-b"), ["\x02"]) + self.assertEqual(parse_keys("\\C-c"), ["\x03"]) + + 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_keynames(self): + self.assertEqual(parse_keys("\\"), ["up"]) + self.assertEqual(parse_keys("\\"), ["down"]) + self.assertEqual(parse_keys("\\"), ["left"]) + self.assertEqual(parse_keys("\\"), ["right"]) + + def test_combinations(self): + self.assertEqual(parse_keys("\\C-a\\n\\"), ["\x01", "\n", "up"]) + self.assertEqual(parse_keys("\\M-a\\t\\"), ["\033", "a", "\t", "down"]) + + +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"}}}) + if __name__ == "__main__": unittest.main() From 9f9bc819d0af861a54abc2d886adb1b8f04a1a9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Mon, 20 May 2024 20:34:03 +0200 Subject: [PATCH 2/2] blacklit paradise --- Lib/test/test_pyrepl.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_pyrepl.py b/Lib/test/test_pyrepl.py index ddce46e55c5961..1daf3a8b498119 100644 --- a/Lib/test/test_pyrepl.py +++ b/Lib/test/test_pyrepl.py @@ -618,10 +618,10 @@ def test_updown_arrow_with_completion_menu(self): events = itertools.chain( code_to_events(code), [ - Event(evt='key', data='up', raw=bytearray(b'\x1bOA')), + Event(evt="key", data="up", raw=bytearray(b"\x1bOA")), Event(evt="key", data="down", raw=bytearray(b"\x1bOB")), ], - code_to_events("\n") + code_to_events("\n"), ) reader = self.prepare_reader(events, namespace=namespace) output = multiline_input(reader, namespace) @@ -1018,10 +1018,12 @@ def test_setpos_fromxy_in_wrapped_line(self): 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')), - ]) + 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_equals(reader, "") @@ -1124,6 +1126,7 @@ def test_empty(self): translator.get() self.assertTrue(translator.empty()) + class TestParseKeys(unittest.TestCase): def test_single_character(self): self.assertEqual(parse_keys("a"), ["a"]) @@ -1195,5 +1198,5 @@ def test_nested_multiple_keymaps(self): self.assertEqual(result, {b"a": {b"b": {b"c": "action"}}}) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main()