• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import string
2import unittest
3
4from _pyrepl.keymap import _keynames, _escapes, parse_keys, compile_keymap, KeySpecError
5
6
7class TestParseKeys(unittest.TestCase):
8    def test_single_character(self):
9        """Ensure that single ascii characters or single digits are parsed as single characters."""
10        test_cases = [(key, [key]) for key in string.ascii_letters + string.digits]
11        for test_key, expected_keys in test_cases:
12            with self.subTest(f"{test_key} should be parsed as {expected_keys}"):
13                self.assertEqual(parse_keys(test_key), expected_keys)
14
15    def test_keynames(self):
16        """Ensure that keynames are parsed to their corresponding mapping.
17
18        A keyname is expected to be of the following form: \\<keyname> such as \\<left>
19        which would get parsed as "left".
20        """
21        test_cases = [(f"\\<{keyname}>", [parsed_keyname]) for keyname, parsed_keyname in _keynames.items()]
22        for test_key, expected_keys in test_cases:
23            with self.subTest(f"{test_key} should be parsed as {expected_keys}"):
24                self.assertEqual(parse_keys(test_key), expected_keys)
25
26    def test_escape_sequences(self):
27        """Ensure that escaping sequences are parsed to their corresponding mapping."""
28        test_cases = [(f"\\{escape}", [parsed_escape]) for escape, parsed_escape in _escapes.items()]
29        for test_key, expected_keys in test_cases:
30            with self.subTest(f"{test_key} should be parsed as {expected_keys}"):
31                self.assertEqual(parse_keys(test_key), expected_keys)
32
33    def test_control_sequences(self):
34        """Ensure that supported control sequences are parsed successfully."""
35        keys = ["@", "[", "]", "\\", "^", "_", "\\<space>", "\\<delete>"]
36        keys.extend(string.ascii_letters)
37        test_cases = [(f"\\C-{key}", chr(ord(key) & 0x1F)) for key in []]
38        for test_key, expected_keys in test_cases:
39            with self.subTest(f"{test_key} should be parsed as {expected_keys}"):
40                self.assertEqual(parse_keys(test_key), expected_keys)
41
42    def test_meta_sequences(self):
43        self.assertEqual(parse_keys("\\M-a"), ["\033", "a"])
44        self.assertEqual(parse_keys("\\M-b"), ["\033", "b"])
45        self.assertEqual(parse_keys("\\M-c"), ["\033", "c"])
46
47    def test_combinations(self):
48        self.assertEqual(parse_keys("\\C-a\\n\\<up>"), ["\x01", "\n", "up"])
49        self.assertEqual(parse_keys("\\M-a\\t\\<down>"), ["\033", "a", "\t", "down"])
50
51    def test_keyspec_errors(self):
52        cases = [
53            ("\\Ca", "\\C must be followed by `-'"),
54            ("\\ca", "\\C must be followed by `-'"),
55            ("\\C-\\C-", "doubled \\C-"),
56            ("\\Ma", "\\M must be followed by `-'"),
57            ("\\ma", "\\M must be followed by `-'"),
58            ("\\M-\\M-", "doubled \\M-"),
59            ("\\<left", "unterminated \\<"),
60            ("\\<unsupported>", "unrecognised keyname"),
61            ("\\大", "unknown backslash escape"),
62            ("\\C-\\<backspace>", "\\C- followed by invalid key")
63        ]
64        for test_keys, expected_err in cases:
65            with self.subTest(f"{test_keys} should give error {expected_err}"):
66                with self.assertRaises(KeySpecError) as e:
67                    parse_keys(test_keys)
68                self.assertIn(expected_err, str(e.exception))
69
70    def test_index_errors(self):
71        test_cases = ["\\", "\\C", "\\C-\\C"]
72        for test_keys in test_cases:
73            with self.assertRaises(IndexError):
74                parse_keys(test_keys)
75
76
77class TestCompileKeymap(unittest.TestCase):
78    def test_empty_keymap(self):
79        keymap = {}
80        result = compile_keymap(keymap)
81        self.assertEqual(result, {})
82
83    def test_single_keymap(self):
84        keymap = {b"a": "action"}
85        result = compile_keymap(keymap)
86        self.assertEqual(result, {b"a": "action"})
87
88    def test_nested_keymap(self):
89        keymap = {b"a": {b"b": "action"}}
90        result = compile_keymap(keymap)
91        self.assertEqual(result, {b"a": {b"b": "action"}})
92
93    def test_empty_value(self):
94        keymap = {b"a": {b"": "action"}}
95        result = compile_keymap(keymap)
96        self.assertEqual(result, {b"a": {b"": "action"}})
97
98    def test_multiple_empty_values(self):
99        keymap = {b"a": {b"": "action1", b"b": "action2"}}
100        result = compile_keymap(keymap)
101        self.assertEqual(result, {b"a": {b"": "action1", b"b": "action2"}})
102
103    def test_multiple_keymaps(self):
104        keymap = {b"a": {b"b": "action1", b"c": "action2"}}
105        result = compile_keymap(keymap)
106        self.assertEqual(result, {b"a": {b"b": "action1", b"c": "action2"}})
107
108    def test_nested_multiple_keymaps(self):
109        keymap = {b"a": {b"b": {b"c": "action"}}}
110        result = compile_keymap(keymap)
111        self.assertEqual(result, {b"a": {b"b": {b"c": "action"}}})
112
113    def test_clashing_definitions(self):
114        km = {b'a': 'c', b'a' + b'b': 'd'}
115        with self.assertRaises(KeySpecError):
116            compile_keymap(km)
117
118    def test_non_bytes_key(self):
119        with self.assertRaises(TypeError):
120            compile_keymap({123: 'a'})
121