blob: c1864efaab1dace2b7d48f018d788cc03f7e5449 [file] [log] [blame]
Ronald Oussoren2efd9242009-09-20 14:53:22 +00001"""
2Very minimal unittests for parts of the readline module.
Ronald Oussoren2efd9242009-09-20 14:53:22 +00003"""
Martin Panter37126862016-05-15 03:05:36 +00004from contextlib import ExitStack
Martin Panterf0dbf7a2016-05-15 01:26:25 +00005from errno import EIO
Victor Stinnera3c80ce2014-07-24 12:23:56 +02006import os
Martin Panterf0dbf7a2016-05-15 01:26:25 +00007import selectors
8import subprocess
9import sys
Benjamin Peterson33f8f152014-11-26 13:58:16 -060010import tempfile
Ronald Oussoren2efd9242009-09-20 14:53:22 +000011import unittest
Martin Panter2e1d8682016-05-15 13:21:25 +000012from test.support import import_module, unlink
Berker Peksagce643912015-05-06 06:33:17 +030013from test.support.script_helper import assert_python_ok
Ronald Oussoren2efd9242009-09-20 14:53:22 +000014
Mark Dickinson2d7062e2009-10-26 12:01:06 +000015# Skip tests if there is no readline module
16readline = import_module('readline')
Ronald Oussoren2efd9242009-09-20 14:53:22 +000017
18class TestHistoryManipulation (unittest.TestCase):
Victor Stinnera3c80ce2014-07-24 12:23:56 +020019 """
20 These tests were added to check that the libedit emulation on OSX and the
21 "real" readline have the same interface for history manipulation. That's
22 why the tests cover only a small subset of the interface.
23 """
R David Murrayf8b9dfd2011-03-14 17:10:22 -040024
Benjamin Peterson5b535072014-11-26 14:35:56 -060025 @unittest.skipUnless(hasattr(readline, "clear_history"),
26 "The history update test cannot be run because the "
27 "clear_history method is not available.")
Ronald Oussoren2efd9242009-09-20 14:53:22 +000028 def testHistoryUpdates(self):
Georg Brandl7b250a52012-08-11 11:02:14 +020029 readline.clear_history()
Ronald Oussoren2efd9242009-09-20 14:53:22 +000030
31 readline.add_history("first line")
32 readline.add_history("second line")
33
34 self.assertEqual(readline.get_history_item(0), None)
35 self.assertEqual(readline.get_history_item(1), "first line")
36 self.assertEqual(readline.get_history_item(2), "second line")
37
38 readline.replace_history_item(0, "replaced line")
39 self.assertEqual(readline.get_history_item(0), None)
40 self.assertEqual(readline.get_history_item(1), "replaced line")
41 self.assertEqual(readline.get_history_item(2), "second line")
42
43 self.assertEqual(readline.get_current_history_length(), 2)
44
45 readline.remove_history_item(0)
46 self.assertEqual(readline.get_history_item(0), None)
47 self.assertEqual(readline.get_history_item(1), "second line")
48
49 self.assertEqual(readline.get_current_history_length(), 1)
50
Ned Deily8007cbc2014-11-26 13:02:33 -080051 @unittest.skipUnless(hasattr(readline, "append_history_file"),
Benjamin Petersond1e22ba2014-11-26 14:35:12 -060052 "append_history not available")
Benjamin Peterson33f8f152014-11-26 13:58:16 -060053 def test_write_read_append(self):
54 hfile = tempfile.NamedTemporaryFile(delete=False)
55 hfile.close()
56 hfilename = hfile.name
57 self.addCleanup(unlink, hfilename)
58
59 # test write-clear-read == nop
60 readline.clear_history()
61 readline.add_history("first line")
62 readline.add_history("second line")
63 readline.write_history_file(hfilename)
64
65 readline.clear_history()
66 self.assertEqual(readline.get_current_history_length(), 0)
67
68 readline.read_history_file(hfilename)
69 self.assertEqual(readline.get_current_history_length(), 2)
70 self.assertEqual(readline.get_history_item(1), "first line")
71 self.assertEqual(readline.get_history_item(2), "second line")
72
73 # test append
74 readline.append_history_file(1, hfilename)
75 readline.clear_history()
76 readline.read_history_file(hfilename)
77 self.assertEqual(readline.get_current_history_length(), 3)
78 self.assertEqual(readline.get_history_item(1), "first line")
79 self.assertEqual(readline.get_history_item(2), "second line")
80 self.assertEqual(readline.get_history_item(3), "second line")
81
82 # test 'no such file' behaviour
83 os.unlink(hfilename)
84 with self.assertRaises(FileNotFoundError):
85 readline.append_history_file(1, hfilename)
86
87 # write_history_file can create the target
88 readline.write_history_file(hfilename)
89
Ronald Oussoren2efd9242009-09-20 14:53:22 +000090
Victor Stinnera3c80ce2014-07-24 12:23:56 +020091class TestReadline(unittest.TestCase):
Antoine Pitrou7e8b8672014-11-04 14:52:10 +010092
93 @unittest.skipIf(readline._READLINE_VERSION < 0x0600
94 and "libedit" not in readline.__doc__,
95 "not supported in this library version")
Victor Stinnera3c80ce2014-07-24 12:23:56 +020096 def test_init(self):
97 # Issue #19884: Ensure that the ANSI sequence "\033[1034h" is not
98 # written into stdout when the readline module is imported and stdout
99 # is redirected to a pipe.
100 rc, stdout, stderr = assert_python_ok('-c', 'import readline',
101 TERM='xterm-256color')
102 self.assertEqual(stdout, b'')
103
Martin Panterf0dbf7a2016-05-15 01:26:25 +0000104 auto_history_script = """\
105import readline
106readline.set_auto_history({})
107input()
108print("History length:", readline.get_current_history_length())
109"""
110
111 def test_auto_history_enabled(self):
112 output = run_pty(self.auto_history_script.format(True))
113 self.assertIn(b"History length: 1\r\n", output)
114
115 def test_auto_history_disabled(self):
116 output = run_pty(self.auto_history_script.format(False))
117 self.assertIn(b"History length: 0\r\n", output)
118
119
120def run_pty(script, input=b"dummy input\r"):
121 pty = import_module('pty')
122 output = bytearray()
123 [master, slave] = pty.openpty()
124 args = (sys.executable, '-c', script)
125 proc = subprocess.Popen(args, stdin=slave, stdout=slave, stderr=slave)
126 os.close(slave)
Martin Panter37126862016-05-15 03:05:36 +0000127 with ExitStack() as cleanup:
128 cleanup.enter_context(proc)
Martin Panter79f561d2016-05-15 15:04:58 +0000129 def terminate(proc):
130 try:
131 proc.terminate()
132 except ProcessLookupError:
133 # Workaround for Open/Net BSD bug (Issue 16762)
134 pass
135 cleanup.callback(terminate, proc)
Martin Panter37126862016-05-15 03:05:36 +0000136 cleanup.callback(os.close, master)
Martin Panter79f561d2016-05-15 15:04:58 +0000137 # Avoid using DefaultSelector and PollSelector. Kqueue() does not
138 # work with pseudo-terminals on OS X < 10.9 (Issue 20365) and Open
139 # BSD (Issue 20667). Poll() does not work with OS X 10.6 or 10.4
140 # either (Issue 20472). Hopefully the file descriptor is low enough
141 # to use with select().
142 sel = cleanup.enter_context(selectors.SelectSelector())
Martin Panterf0dbf7a2016-05-15 01:26:25 +0000143 sel.register(master, selectors.EVENT_READ | selectors.EVENT_WRITE)
144 os.set_blocking(master, False)
145 while True:
146 for [_, events] in sel.select():
147 if events & selectors.EVENT_READ:
148 try:
149 chunk = os.read(master, 0x10000)
150 except OSError as err:
151 # Linux raises EIO when the slave is closed
152 if err.errno != EIO:
153 raise
154 chunk = b""
155 if not chunk:
Martin Panterf0dbf7a2016-05-15 01:26:25 +0000156 return output
157 output.extend(chunk)
158 if events & selectors.EVENT_WRITE:
159 input = input[os.write(master, input):]
160 if not input:
161 sel.modify(master, selectors.EVENT_READ)
162
Victor Stinnera3c80ce2014-07-24 12:23:56 +0200163
Ronald Oussoren2efd9242009-09-20 14:53:22 +0000164if __name__ == "__main__":
Zachary Ware38c707e2015-04-13 15:00:43 -0500165 unittest.main()