blob: f8e627a9f0a2fd849fa43fd060726f90419b7f6a [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 Panter2e1d8682016-05-15 13:21:25 +0000129 cleanup.callback(proc.terminate)
Martin Panter37126862016-05-15 03:05:36 +0000130 cleanup.callback(os.close, master)
Martin Panter2e1d8682016-05-15 13:21:25 +0000131 # Avoid using DefaultSelector, because it may choose a kqueue()
132 # implementation, which does not work with pseudo-terminals on OS X
133 # < 10.9 (Issue 20365) and Open BSD (Issue 20667).
134 sel = getattr(selectors, "PollSelector", selectors.DefaultSelector)()
135 cleanup.enter_context(sel)
Martin Panterf0dbf7a2016-05-15 01:26:25 +0000136 sel.register(master, selectors.EVENT_READ | selectors.EVENT_WRITE)
137 os.set_blocking(master, False)
138 while True:
139 for [_, events] in sel.select():
140 if events & selectors.EVENT_READ:
141 try:
142 chunk = os.read(master, 0x10000)
143 except OSError as err:
144 # Linux raises EIO when the slave is closed
145 if err.errno != EIO:
146 raise
147 chunk = b""
148 if not chunk:
Martin Panterf0dbf7a2016-05-15 01:26:25 +0000149 return output
150 output.extend(chunk)
151 if events & selectors.EVENT_WRITE:
152 input = input[os.write(master, input):]
153 if not input:
154 sel.modify(master, selectors.EVENT_READ)
155
Victor Stinnera3c80ce2014-07-24 12:23:56 +0200156
Ronald Oussoren2efd9242009-09-20 14:53:22 +0000157if __name__ == "__main__":
Zachary Ware38c707e2015-04-13 15:00:43 -0500158 unittest.main()