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

168 lines, 98 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 tempfile
13import time
14
15import harness
16from harness import expect_prompt, register
17
18from test.spec_lib import log
19
20
21def add_foo_fn(sh):
22 sh.sendline('function foo() { echo "FOO"; }')
23 time.sleep(0.1)
24
25
26def send_bind(sh, opts, keymap=None):
27 "Helper method to send a bind command and sleep for a moment. W/ optional keymap."
28
29 if keymap:
30 sh.sendline(f"bind -m {keymap} {opts}")
31 else:
32 sh.sendline(f"bind {opts}")
33 time.sleep(0.1)
34
35
36@register(not_impl_shells=['dash', 'mksh'])
37def bind_plain(sh):
38 "test bind (w/out flags) for adding bindings to readline fns"
39 expect_prompt(sh)
40
41 # There aren't many readline fns that will work nicely with pexpect (e.g., cursor-based fns)
42 # Editing input seems like a reasonable choice
43 send_bind(sh, ''' '"\C-x\C-h": backward-delete-char' ''')
44 expect_prompt(sh)
45
46 sh.send("echo FOOM")
47 sh.sendcontrol('x')
48 sh.sendcontrol('h')
49 sh.sendline("P")
50 time.sleep(0.1)
51
52 sh.expect("FOOP")
53
54
55@register(not_impl_shells=['dash', 'mksh'])
56def bind_r(sh):
57 "test bind -r for removing bindings"
58 expect_prompt(sh)
59
60 add_foo_fn(sh)
61 expect_prompt(sh)
62
63 send_bind(sh, """-x '"\C-x\C-f": foo' """)
64 expect_prompt(sh)
65
66 sh.sendcontrol('x')
67 sh.sendcontrol('f')
68 time.sleep(0.1)
69 sh.expect("FOO")
70
71 send_bind(sh, '-r "\C-x\C-f" ')
72
73 sh.sendcontrol('x')
74 sh.sendcontrol('f')
75 time.sleep(0.1)
76
77 expect_prompt(sh)
78
79
80@register(not_impl_shells=['dash', 'mksh'])
81def bind_x(sh):
82 "test bind -x for setting bindings to custom shell functions"
83 expect_prompt(sh)
84
85 add_foo_fn(sh)
86 expect_prompt(sh)
87
88 send_bind(sh, """-x '"\C-x\C-f": foo' """)
89 expect_prompt(sh)
90
91 sh.sendcontrol('x')
92 sh.sendcontrol('f')
93 time.sleep(0.1)
94
95 sh.expect("FOO")
96
97
98@register(not_impl_shells=['dash', 'mksh'])
99def bind_u(sh):
100 "test bind -u for unsetting all bindings to a fn"
101 expect_prompt(sh)
102
103 send_bind(sh, "'\C-p: yank'")
104 expect_prompt(sh)
105
106 send_bind(sh, "-u yank")
107 expect_prompt(sh)
108
109 send_bind(sh, "-q yank")
110 sh.expect("yank is not bound to any keys")
111
112
113@register(not_impl_shells=['dash', 'mksh'])
114def bind_q(sh):
115 "test bind -q for querying bindings to a fn"
116 expect_prompt(sh)
117
118 # Probably bound, but we're not testing that precisely
119 send_bind(sh, "-q yank")
120 sh.expect(["yank can be invoked via", "yank is not bound to any keys"])
121
122 expect_prompt(sh)
123
124 # Probably NOT bound, but we're not testing that precisely
125 send_bind(sh, "-q dump-functions")
126 sh.expect([
127 "dump-functions can be invoked via",
128 "dump-functions is not bound to any keys"
129 ])
130
131
132@register(not_impl_shells=['dash', 'mksh'])
133def bind_m(sh):
134 "test bind -m for setting bindings in specific keymaps"
135 expect_prompt(sh)
136
137 send_bind(sh, "-u yank", "vi")
138 expect_prompt(sh)
139
140 send_bind(sh, "'\C-p: yank'", "emacs")
141 expect_prompt(sh)
142
143 send_bind(sh, "-q yank", "vi")
144 sh.expect("yank is not bound to any keys")
145 expect_prompt(sh)
146
147 send_bind(sh, "-q yank", "emacs")
148 sh.expect("yank can be invoked via")
149
150
151@register(not_impl_shells=['dash', 'mksh'])
152def bind_f(sh):
153 "test bind -f for setting bindings from an inputrc init file"
154 expect_prompt(sh)
155
156 send_bind(sh, "-f spec/testdata/bind/bind_f.inputrc")
157 expect_prompt(sh)
158
159 send_bind(sh, "-q downcase-word")
160 sh.expect('downcase-word can be invoked via.*"\\\C-o\\\C-s\\\C-h"')
161
162
163if __name__ == '__main__':
164 try:
165 sys.exit(harness.main(sys.argv))
166 except RuntimeError as e:
167 print('FATAL: %s' % e, file=sys.stderr)
168 sys.exit(1)