|
43 | 43 | init_per_testcase/2, end_per_testcase/2, |
44 | 44 | get_columns_and_rows/1, exit_initial/1, job_control_local/1, |
45 | 45 | job_control_remote/1,stop_during_init/1,wrap/1, |
46 | | - noshell_raw/1, |
| 46 | + noshell_raw/1, raw_signals/1, |
47 | 47 | shell_history/1, shell_history_resize/1, shell_history_eaccess/1, |
48 | 48 | shell_history_repair/1, shell_history_repair_corrupt/1, |
49 | 49 | shell_history_corrupt/1, |
@@ -154,7 +154,8 @@ groups() -> |
154 | 154 | external_editor, |
155 | 155 | external_editor_visual, |
156 | 156 | shell_ignore_pager_commands, |
157 | | - shell_help |
| 157 | + shell_help, |
| 158 | + raw_signals |
158 | 159 | ]}, |
159 | 160 | {tty_unicode,[parallel], |
160 | 161 | [{group,tty_tests}, |
@@ -2136,6 +2137,69 @@ noshell_raw(Config) -> |
2136 | 2137 | end, |
2137 | 2138 | ok. |
2138 | 2139 |
|
| 2140 | +%% Verify that control bytes reach the application in raw mode when |
| 2141 | +%% signal and flow-control handling is disabled. ctrl+o is VDISCARD on |
| 2142 | +%% POSIX: while IEXTEN is set the terminal driver swallows the byte, so |
| 2143 | +%% the reader would time out. Uses a noshell peer on a tmux pty so that |
| 2144 | +%% prim_tty is the only reader of the terminal. |
| 2145 | +raw_signals(Config) -> |
| 2146 | + Term = shell_test_lib:setup_tty( |
| 2147 | + [{args, ["-noshell", "-noinput"]} | Config]), |
| 2148 | + |
| 2149 | + try |
| 2150 | + %% Init the terminal in raw mode with signals disabled and start |
| 2151 | + %% a reader that reports the first byte it receives. |
| 2152 | + ok = shell_test_lib:rpc(Term, fun() -> |
| 2153 | + Self = self(), |
| 2154 | + spawn(fun() -> |
| 2155 | + %% prim_tty names its reader and writer processes after |
| 2156 | + %% the parent, so the parent must be registered. |
| 2157 | + register(raw_signals_probe, self()), |
| 2158 | + ok = prim_tty:load(), |
| 2159 | + TTY = prim_tty:init(#{input => raw, output => cooked, |
| 2160 | + signals => false}), |
| 2161 | + #{read := Ref} = prim_tty:handles(TTY), |
| 2162 | + Self ! ready, |
| 2163 | + ok = prim_tty:read(TTY, 1), |
| 2164 | + Res = receive |
| 2165 | + {Ref, {data, Data}} -> {ok, Data}; |
| 2166 | + {Ref, eof} -> eof |
| 2167 | + after 5000 -> timeout |
| 2168 | + end, |
| 2169 | + persistent_term:put({?MODULE, raw_signals}, Res) |
| 2170 | + end), |
| 2171 | + receive ready -> ok after 10000 -> timeout end |
| 2172 | + end), |
| 2173 | + |
| 2174 | + %% Send ctrl+o (0x0f) through the pty. With signals enabled this |
| 2175 | + %% byte is consumed by the terminal driver's discard function |
| 2176 | + %% and never reaches the application. tmux send-keys writes to |
| 2177 | + %% the master side, so the byte passes through the line |
| 2178 | + %% discipline of the pane. |
| 2179 | + shell_test_lib:send_tty(Term, "C-o"), |
| 2180 | + |
| 2181 | + %% The byte must be delivered to the reader. |
| 2182 | + {ok, <<$\^o>>} = await_raw_signals(Term) |
| 2183 | + after |
| 2184 | + shell_test_lib:stop_tty(Term) |
| 2185 | + end. |
| 2186 | + |
| 2187 | +await_raw_signals(Term) -> |
| 2188 | + await_raw_signals(Term, 50). |
| 2189 | + |
| 2190 | +await_raw_signals(_Term, 0) -> |
| 2191 | + not_set; |
| 2192 | +await_raw_signals(Term, N) -> |
| 2193 | + case shell_test_lib:rpc(Term, fun() -> |
| 2194 | + persistent_term:get({?MODULE, raw_signals}, not_set) |
| 2195 | + end) of |
| 2196 | + not_set -> |
| 2197 | + timer:sleep(100), |
| 2198 | + await_raw_signals(Term, N - 1); |
| 2199 | + Res -> |
| 2200 | + Res |
| 2201 | + end. |
| 2202 | + |
2139 | 2203 | get_until(start, NewChars) -> |
2140 | 2204 | get_until([], NewChars); |
2141 | 2205 | get_until(State, NewChars) -> |
|
0 commit comments