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

234 lines, 135 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('[[ -v READLINE_LINE ]] && echo "READLINE_LINE is set" || echo "READLINE_LINE is unset"')
138 sh.expect("READLINE_LINE is unset")
139
140
141@register(not_impl_shells=['dash', 'mksh'])
142def bind_x_readline_point(sh):
143 "test bind -x for correctly setting $READLINE_POINT for the cmd"
144 cmd_str = 'abcdefghijklmnop'
145 expected_rl_point = len(cmd_str)
146
147 expect_prompt(sh)
148
149 send_bind(sh, """-x '"\C-x\C-f": echo Cursor point at: $READLINE_POINT' """)
150 expect_prompt(sh)
151
152 sh.send(cmd_str)
153
154 sh.sendcontrol('x')
155 sh.sendcontrol('f')
156 time.sleep(0.1)
157
158 sh.expect("Cursor point at: " + str(expected_rl_point))
159
160 sh.sendline('[[ -v READLINE_POINT ]] && echo "READLINE_POINT is set" || echo "READLINE_POINT is unset"')
161 sh.expect("READLINE_POINT is unset")
162
163
164@register(not_impl_shells=['dash', 'mksh'])
165def bind_u(sh):
166 "test bind -u for unsetting all bindings to a fn"
167 expect_prompt(sh)
168
169 send_bind(sh, "'\C-p: yank'")
170 expect_prompt(sh)
171
172 send_bind(sh, "-u yank")
173 expect_prompt(sh)
174
175 send_bind(sh, "-q yank")
176 sh.expect("yank is not bound to any keys")
177
178
179@register(not_impl_shells=['dash', 'mksh'])
180def bind_q(sh):
181 "test bind -q for querying bindings to a fn"
182 expect_prompt(sh)
183
184 # Probably bound, but we're not testing that precisely
185 send_bind(sh, "-q yank")
186 sh.expect(["yank can be invoked via", "yank is not bound to any keys"])
187
188 expect_prompt(sh)
189
190 # Probably NOT bound, but we're not testing that precisely
191 send_bind(sh, "-q dump-functions")
192 sh.expect([
193 "dump-functions can be invoked via",
194 "dump-functions is not bound to any keys"
195 ])
196
197
198@register(not_impl_shells=['dash', 'mksh'])
199def bind_m(sh):
200 "test bind -m for setting bindings in specific keymaps"
201 expect_prompt(sh)
202
203 send_bind(sh, "-u yank", "vi")
204 expect_prompt(sh)
205
206 send_bind(sh, "'\C-p: yank'", "emacs")
207 expect_prompt(sh)
208
209 send_bind(sh, "-q yank", "vi")
210 sh.expect("yank is not bound to any keys")
211 expect_prompt(sh)
212
213 send_bind(sh, "-q yank", "emacs")
214 sh.expect("yank can be invoked via")
215
216
217@register(not_impl_shells=['dash', 'mksh'])
218def bind_f(sh):
219 "test bind -f for setting bindings from an inputrc init file"
220 expect_prompt(sh)
221
222 send_bind(sh, "-f spec/testdata/bind/bind_f.inputrc")
223 expect_prompt(sh)
224
225 send_bind(sh, "-q downcase-word")
226 sh.expect('downcase-word can be invoked via.*"\\\C-o\\\C-s\\\C-h"')
227
228
229if __name__ == '__main__':
230 try:
231 sys.exit(harness.main(sys.argv))
232 except RuntimeError as e:
233 print('FATAL: %s' % e, file=sys.stderr)
234 sys.exit(1)