»Core Development>Code coverage>Lib/test/test_readline.py

Python code coverage for Lib/test/test_readline.py

#countcontent
1n/a"""
2n/aVery minimal unittests for parts of the readline module.
3n/a"""
4n/afrom contextlib import ExitStack
5n/afrom errno import EIO
6n/aimport os
7n/aimport selectors
8n/aimport subprocess
9n/aimport sys
10n/aimport tempfile
11n/aimport unittest
12n/afrom test.support import import_module, unlink, TESTFN
13n/afrom test.support.script_helper import assert_python_ok
14n/a
15n/a# Skip tests if there is no readline module
16n/areadline = import_module('readline')
17n/a
18n/ais_editline = readline.__doc__ and "libedit" in readline.__doc__
19n/a
20n/a@unittest.skipUnless(hasattr(readline, "clear_history"),
21n/a "The history update test cannot be run because the "
22n/a "clear_history method is not available.")
23n/aclass TestHistoryManipulation (unittest.TestCase):
24n/a """
25n/a These tests were added to check that the libedit emulation on OSX and the
26n/a "real" readline have the same interface for history manipulation. That's
27n/a why the tests cover only a small subset of the interface.
28n/a """
29n/a
30n/a def testHistoryUpdates(self):
31n/a readline.clear_history()
32n/a
33n/a readline.add_history("first line")
34n/a readline.add_history("second line")
35n/a
36n/a self.assertEqual(readline.get_history_item(0), None)
37n/a self.assertEqual(readline.get_history_item(1), "first line")
38n/a self.assertEqual(readline.get_history_item(2), "second line")
39n/a
40n/a readline.replace_history_item(0, "replaced line")
41n/a self.assertEqual(readline.get_history_item(0), None)
42n/a self.assertEqual(readline.get_history_item(1), "replaced line")
43n/a self.assertEqual(readline.get_history_item(2), "second line")
44n/a
45n/a self.assertEqual(readline.get_current_history_length(), 2)
46n/a
47n/a readline.remove_history_item(0)
48n/a self.assertEqual(readline.get_history_item(0), None)
49n/a self.assertEqual(readline.get_history_item(1), "second line")
50n/a
51n/a self.assertEqual(readline.get_current_history_length(), 1)
52n/a
53n/a @unittest.skipUnless(hasattr(readline, "append_history_file"),
54n/a "append_history not available")
55n/a def test_write_read_append(self):
56n/a hfile = tempfile.NamedTemporaryFile(delete=False)
57n/a hfile.close()
58n/a hfilename = hfile.name
59n/a self.addCleanup(unlink, hfilename)
60n/a
61n/a # test write-clear-read == nop
62n/a readline.clear_history()
63n/a readline.add_history("first line")
64n/a readline.add_history("second line")
65n/a readline.write_history_file(hfilename)
66n/a
67n/a readline.clear_history()
68n/a self.assertEqual(readline.get_current_history_length(), 0)
69n/a
70n/a readline.read_history_file(hfilename)
71n/a self.assertEqual(readline.get_current_history_length(), 2)
72n/a self.assertEqual(readline.get_history_item(1), "first line")
73n/a self.assertEqual(readline.get_history_item(2), "second line")
74n/a
75n/a # test append
76n/a readline.append_history_file(1, hfilename)
77n/a readline.clear_history()
78n/a readline.read_history_file(hfilename)
79n/a self.assertEqual(readline.get_current_history_length(), 3)
80n/a self.assertEqual(readline.get_history_item(1), "first line")
81n/a self.assertEqual(readline.get_history_item(2), "second line")
82n/a self.assertEqual(readline.get_history_item(3), "second line")
83n/a
84n/a # test 'no such file' behaviour
85n/a os.unlink(hfilename)
86n/a with self.assertRaises(FileNotFoundError):
87n/a readline.append_history_file(1, hfilename)
88n/a
89n/a # write_history_file can create the target
90n/a readline.write_history_file(hfilename)
91n/a
92n/a def test_nonascii_history(self):
93n/a readline.clear_history()
94n/a try:
95n/a readline.add_history("entrée 1")
96n/a except UnicodeEncodeError as err:
97n/a self.skipTest("Locale cannot encode test data: " + format(err))
98n/a readline.add_history("entrée 2")
99n/a readline.replace_history_item(1, "entrée 22")
100n/a readline.write_history_file(TESTFN)
101n/a self.addCleanup(os.remove, TESTFN)
102n/a readline.clear_history()
103n/a readline.read_history_file(TESTFN)
104n/a if is_editline:
105n/a # An add_history() call seems to be required for get_history_
106n/a # item() to register items from the file
107n/a readline.add_history("dummy")
108n/a self.assertEqual(readline.get_history_item(1), "entrée 1")
109n/a self.assertEqual(readline.get_history_item(2), "entrée 22")
110n/a
111n/a
112n/aclass TestReadline(unittest.TestCase):
113n/a
114n/a @unittest.skipIf(readline._READLINE_VERSION < 0x0601 and not is_editline,
115n/a "not supported in this library version")
116n/a def test_init(self):
117n/a # Issue #19884: Ensure that the ANSI sequence "\033[1034h" is not
118n/a # written into stdout when the readline module is imported and stdout
119n/a # is redirected to a pipe.
120n/a rc, stdout, stderr = assert_python_ok('-c', 'import readline',
121n/a TERM='xterm-256color')
122n/a self.assertEqual(stdout, b'')
123n/a
124n/a auto_history_script = """\
125n/aimport readline
126n/areadline.set_auto_history({})
127n/ainput()
128n/aprint("History length:", readline.get_current_history_length())
129n/a"""
130n/a
131n/a def test_auto_history_enabled(self):
132n/a output = run_pty(self.auto_history_script.format(True))
133n/a self.assertIn(b"History length: 1\r\n", output)
134n/a
135n/a def test_auto_history_disabled(self):
136n/a output = run_pty(self.auto_history_script.format(False))
137n/a self.assertIn(b"History length: 0\r\n", output)
138n/a
139n/a def test_nonascii(self):
140n/a try:
141n/a readline.add_history("\xEB\xEF")
142n/a except UnicodeEncodeError as err:
143n/a self.skipTest("Locale cannot encode test data: " + format(err))
144n/a
145n/a script = r"""import readline
146n/a
147n/ais_editline = readline.__doc__ and "libedit" in readline.__doc__
148n/ainserted = "[\xEFnserted]"
149n/amacro = "|t\xEB[after]"
150n/aset_pre_input_hook = getattr(readline, "set_pre_input_hook", None)
151n/aif is_editline or not set_pre_input_hook:
152n/a # The insert_line() call via pre_input_hook() does nothing with Editline,
153n/a # so include the extra text that would have been inserted here
154n/a macro = inserted + macro
155n/a
156n/aif is_editline:
157n/a readline.parse_and_bind(r'bind ^B ed-prev-char')
158n/a readline.parse_and_bind(r'bind "\t" rl_complete')
159n/a readline.parse_and_bind(r'bind -s ^A "{}"'.format(macro))
160n/aelse:
161n/a readline.parse_and_bind(r'Control-b: backward-char')
162n/a readline.parse_and_bind(r'"\t": complete')
163n/a readline.parse_and_bind(r'set disable-completion off')
164n/a readline.parse_and_bind(r'set show-all-if-ambiguous off')
165n/a readline.parse_and_bind(r'set show-all-if-unmodified off')
166n/a readline.parse_and_bind(r'Control-a: "{}"'.format(macro))
167n/a
168n/adef pre_input_hook():
169n/a readline.insert_text(inserted)
170n/a readline.redisplay()
171n/aif set_pre_input_hook:
172n/a set_pre_input_hook(pre_input_hook)
173n/a
174n/adef completer(text, state):
175n/a if text == "t\xEB":
176n/a if state == 0:
177n/a print("text", ascii(text))
178n/a print("line", ascii(readline.get_line_buffer()))
179n/a print("indexes", readline.get_begidx(), readline.get_endidx())
180n/a return "t\xEBnt"
181n/a if state == 1:
182n/a return "t\xEBxt"
183n/a if text == "t\xEBx" and state == 0:
184n/a return "t\xEBxt"
185n/a return None
186n/areadline.set_completer(completer)
187n/a
188n/adef display(substitution, matches, longest_match_length):
189n/a print("substitution", ascii(substitution))
190n/a print("matches", ascii(matches))
191n/areadline.set_completion_display_matches_hook(display)
192n/a
193n/aprint("result", ascii(input()))
194n/aprint("history", ascii(readline.get_history_item(1)))
195n/a"""
196n/a
197n/a input = b"\x01" # Ctrl-A, expands to "|t\xEB[after]"
198n/a input += b"\x02" * len("[after]") # Move cursor back
199n/a input += b"\t\t" # Display possible completions
200n/a input += b"x\t" # Complete "t\xEBx" -> "t\xEBxt"
201n/a input += b"\r"
202n/a output = run_pty(script, input)
203n/a self.assertIn(b"text 't\\xeb'\r\n", output)
204n/a self.assertIn(b"line '[\\xefnserted]|t\\xeb[after]'\r\n", output)
205n/a self.assertIn(b"indexes 11 13\r\n", output)
206n/a if not is_editline and hasattr(readline, "set_pre_input_hook"):
207n/a self.assertIn(b"substitution 't\\xeb'\r\n", output)
208n/a self.assertIn(b"matches ['t\\xebnt', 't\\xebxt']\r\n", output)
209n/a expected = br"'[\xefnserted]|t\xebxt[after]'"
210n/a self.assertIn(b"result " + expected + b"\r\n", output)
211n/a self.assertIn(b"history " + expected + b"\r\n", output)
212n/a
213n/a
214n/adef run_pty(script, input=b"dummy input\r"):
215n/a pty = import_module('pty')
216n/a output = bytearray()
217n/a [master, slave] = pty.openpty()
218n/a args = (sys.executable, '-c', script)
219n/a proc = subprocess.Popen(args, stdin=slave, stdout=slave, stderr=slave)
220n/a os.close(slave)
221n/a with ExitStack() as cleanup:
222n/a cleanup.enter_context(proc)
223n/a def terminate(proc):
224n/a try:
225n/a proc.terminate()
226n/a except ProcessLookupError:
227n/a # Workaround for Open/Net BSD bug (Issue 16762)
228n/a pass
229n/a cleanup.callback(terminate, proc)
230n/a cleanup.callback(os.close, master)
231n/a # Avoid using DefaultSelector and PollSelector. Kqueue() does not
232n/a # work with pseudo-terminals on OS X < 10.9 (Issue 20365) and Open
233n/a # BSD (Issue 20667). Poll() does not work with OS X 10.6 or 10.4
234n/a # either (Issue 20472). Hopefully the file descriptor is low enough
235n/a # to use with select().
236n/a sel = cleanup.enter_context(selectors.SelectSelector())
237n/a sel.register(master, selectors.EVENT_READ | selectors.EVENT_WRITE)
238n/a os.set_blocking(master, False)
239n/a while True:
240n/a for [_, events] in sel.select():
241n/a if events & selectors.EVENT_READ:
242n/a try:
243n/a chunk = os.read(master, 0x10000)
244n/a except OSError as err:
245n/a # Linux raises EIO when slave is closed (Issue 5380)
246n/a if err.errno != EIO:
247n/a raise
248n/a chunk = b""
249n/a if not chunk:
250n/a return output
251n/a output.extend(chunk)
252n/a if events & selectors.EVENT_WRITE:
253n/a try:
254n/a input = input[os.write(master, input):]
255n/a except OSError as err:
256n/a # Apparently EIO means the slave was closed
257n/a if err.errno != EIO:
258n/a raise
259n/a input = b"" # Stop writing
260n/a if not input:
261n/a sel.modify(master, selectors.EVENT_READ)
262n/a
263n/a
264n/aif __name__ == "__main__":
265n/a unittest.main()