OILS / spec / stateful / bind.py View on Github | oils.pub

239 lines, 138 significant
1#!/usr/bin/env python3
2"""
3Interactively tests shell bindings.
4
5To invoke this file, run the shell wrapper:
6
7 test/stateful.sh bind-quick
8"""
9from __future__ import print_function
10
11import sys
12import time
13
14import harness
15from harness import expect_prompt, register
16
17from test.spec_lib import log
18
19
20def add_foo_fn(sh):
21 sh.sendline('function foo() { echo "FOO"; }')
22 time.sleep(0.1)
23
24
25def send_bind(sh, opts, keymap=None):
26 "Helper method to send a bind command and sleep for a moment. W/ optional keymap."
27
28 if keymap:
29 sh.sendline(f"bind -m {keymap} {opts}")
30 else:
31 sh.sendline(f"bind {opts}")
32 time.sleep(0.1)
33
34
35@register(not_impl_shells=['dash', 'mksh'])
36def bind_plain(sh):
37 "test bind (w/out flags) for adding bindings to readline fns"
38 expect_prompt(sh)
39
40 # There aren't many readline fns that will work nicely with pexpect (e.g., cursor-based fns)
41 # Editing input seems like a reasonable choice
42 send_bind(sh, ''' '"\C-x\C-h": backward-delete-char' ''')
43 expect_prompt(sh)
44
45 sh.send("echo FOOM")
46 sh.sendcontrol('x')
47 sh.sendcontrol('h')
48 sh.sendline("P")
49 time.sleep(0.1)
50
51 sh.expect("FOOP")
52
53
54@register(not_impl_shells=['dash', 'mksh'])
55def bind_r_for_bind_x_osh_fn(sh):
56 """
57 test bind -r for removing bindings to arbitrary cmds made with bind -x
58 (regular readline fn bind removal is tested in noninteractive builtin-bind.test.sh)
59 """
60 expect_prompt(sh)
61
62 add_foo_fn(sh)
63 expect_prompt(sh)
64
65 send_bind(sh, """-x '"\C-x\C-f": foo' """)
66 expect_prompt(sh)
67
68 sh.sendcontrol('x')
69 sh.sendcontrol('f')
70 time.sleep(0.1)
71 sh.expect("FOO")
72
73 send_bind(sh, '-r "\C-x\C-f" ')
74
75 sh.sendcontrol('x')
76 sh.sendcontrol('f')
77 time.sleep(0.1)
78
79 expect_prompt(sh)
80
81
82@register(not_impl_shells=['dash', 'mksh'])
83def bind_x(sh):
84 "test bind -x for setting bindings to custom shell functions"
85 expect_prompt(sh)
86
87 add_foo_fn(sh)
88 expect_prompt(sh)
89
90 send_bind(sh, """-x '"\C-x\C-f": foo' """)
91 expect_prompt(sh)
92
93 sh.sendcontrol('x')
94 sh.sendcontrol('f')
95 time.sleep(0.1)
96
97 sh.expect("FOO")
98
99
100@register(not_impl_shells=['dash', 'mksh'])
101def bind_x_runtime_envvar_vals(sh):
102 "test bind -x for using env var runtime values (e.g., 'echo $PWD' should change with dir)"
103 expect_prompt(sh)
104
105 sh.sendline("export BIND_X_VAR=foo")
106
107 send_bind(sh, """-x '"\C-x\C-f": echo $BIND_X_VAR' """)
108 expect_prompt(sh)
109
110 sh.sendline("export BIND_X_VAR=bar")
111 expect_prompt(sh)
112
113 sh.sendcontrol('x')
114 sh.sendcontrol('f')
115 time.sleep(0.1)
116
117 sh.expect("bar")
118
119
120@register(not_impl_shells=['dash', 'mksh'])
121def bind_x_readline_line(sh):
122 "test bind -x for correctly setting $READLINE_LINE for the cmd"
123 expect_prompt(sh)
124
125 send_bind(sh, """-x '"\C-x\C-f": echo Current line is: $READLINE_LINE' """)
126 expect_prompt(sh)
127
128 sh.send('abcdefghijklmnopqrstuvwxyz')
129
130 sh.sendcontrol('x')
131 sh.sendcontrol('f')
132 time.sleep(0.1)
133
134 # must not match any other output (like debug output or shell names)
135 sh.expect("Current line is: abcdefghijklmnopqrstuvwxyz")
136
137 sh.sendline(
138 '[[ -v READLINE_LINE ]] && echo "READLINE_LINE is set" || echo "READLINE_LINE is unset"'
139 )
140 sh.expect("READLINE_LINE is unset")
141
142
143@register(not_impl_shells=['dash', 'mksh'])
144def bind_x_readline_point(sh):
145 "test bind -x for correctly setting $READLINE_POINT for the cmd"
146 cmd_str = 'abcdefghijklmnop'
147 expected_rl_point = len(cmd_str)
148
149 expect_prompt(sh)
150
151 send_bind(sh,
152 """-x '"\C-x\C-f": echo Cursor point at: $READLINE_POINT' """)
153 expect_prompt(sh)
154
155 sh.send(cmd_str)
156
157 sh.sendcontrol('x')
158 sh.sendcontrol('f')
159 time.sleep(0.1)
160
161 sh.expect("Cursor point at: " + str(expected_rl_point))
162
163 sh.sendline(
164 '[[ -v READLINE_POINT ]] && echo "READLINE_POINT is set" || echo "READLINE_POINT is unset"'
165 )
166 sh.expect("READLINE_POINT is unset")
167
168
169@register(not_impl_shells=['dash', 'mksh'])
170def bind_u(sh):
171 "test bind -u for unsetting all bindings to a fn"
172 expect_prompt(sh)
173
174 send_bind(sh, "'\C-p: yank'")
175 expect_prompt(sh)
176
177 send_bind(sh, "-u yank")
178 expect_prompt(sh)
179
180 send_bind(sh, "-q yank")
181 sh.expect("yank is not bound to any keys")
182
183
184@register(not_impl_shells=['dash', 'mksh'])
185def bind_q(sh):
186 "test bind -q for querying bindings to a fn"
187 expect_prompt(sh)
188
189 # Probably bound, but we're not testing that precisely
190 send_bind(sh, "-q yank")
191 sh.expect(["yank can be invoked via", "yank is not bound to any keys"])
192
193 expect_prompt(sh)
194
195 # Probably NOT bound, but we're not testing that precisely
196 send_bind(sh, "-q dump-functions")
197 sh.expect([
198 "dump-functions can be invoked via",
199 "dump-functions is not bound to any keys"
200 ])
201
202
203@register(not_impl_shells=['dash', 'mksh'])
204def bind_m(sh):
205 "test bind -m for setting bindings in specific keymaps"
206 expect_prompt(sh)
207
208 send_bind(sh, "-u yank", "vi")
209 expect_prompt(sh)
210
211 send_bind(sh, "'\C-p: yank'", "emacs")
212 expect_prompt(sh)
213
214 send_bind(sh, "-q yank", "vi")
215 sh.expect("yank is not bound to any keys")
216 expect_prompt(sh)
217
218 send_bind(sh, "-q yank", "emacs")
219 sh.expect("yank can be invoked via")
220
221
222@register(not_impl_shells=['dash', 'mksh'])
223def bind_f(sh):
224 "test bind -f for setting bindings from an inputrc init file"
225 expect_prompt(sh)
226
227 send_bind(sh, "-f spec/testdata/bind/bind_f.inputrc")
228 expect_prompt(sh)
229
230 send_bind(sh, "-q downcase-word")
231 sh.expect('downcase-word can be invoked via.*"\\\C-o\\\C-s\\\C-h"')
232
233
234if __name__ == '__main__':
235 try:
236 sys.exit(harness.main(sys.argv))
237 except RuntimeError as e:
238 print('FATAL: %s' % e, file=sys.stderr)
239 sys.exit(1)