fish-shell/tests/pexpects/torn_escapes.py
ridiculousfish 1bdd629326 Prevent signals from tearing multi-char bindings
Say the user has a multi-char binding (typically an escape sequence), and a
signal arrives partway through the binding. The signal has an event handler
which enques some readline event, for example, `repaint`. Prior to this
change, the readline event would cause the multi-char binding to fail. This
would cause bits of the escape sequence to be printed to the screen.

Fix this by noticing when a sequence was "interrupted" by a non-char event,
and then rotating a sequence of such interruptions to the front of the
queue.

Fixes #8628
2022-02-05 13:18:36 -08:00

81 lines
1.8 KiB
Python

#!/usr/bin/env python3
import os
import signal
from pexpect_helper import SpawnedProc
sp = SpawnedProc()
send, sendline, sleep, expect_prompt, expect_str, expect_re = (
sp.send,
sp.sendline,
sp.sleep,
sp.expect_prompt,
sp.expect_str,
sp.expect_re,
)
# Ensure that signals don't tear escape sequences. See #8628.
expect_prompt()
# Allow for long delays before matching escape.
sendline(r"set -g fish_escape_delay_ms 2000")
expect_prompt()
# Set up a handler for SIGUSR1.
sendline(r"set -g sigusr1_count 0")
expect_prompt()
sendline(
r"""
function usr1_handler --on-signal SIGUSR1;
set sigusr1_count (math $sigusr1_count + 1);
echo Got SIGUSR1 $sigusr1_count;
commandline -f repaint;
end
""".strip().replace(
"\n", os.linesep
)
)
expect_prompt()
# Set up a wacky binding with an escape.
sendline(r"function wacky_handler; echo Wacky Handler; end")
expect_prompt()
sendline(r"bind abc\edef wacky_handler")
expect_prompt()
# We can respond to SIGUSR1.
os.kill(sp.spawn.pid, signal.SIGUSR1)
expect_str(r"Got SIGUSR1 1")
sendline(r"")
expect_prompt()
# Our wacky binding works.
send("abc\x1bdef")
expect_str(r"Wacky Handler")
sendline(r"")
expect_prompt()
# Now we interleave the sequence with SIGUSR1.
# What we expect to happen is that the signal is "shuffled to the front" ahead of the key events.
send("abc")
sleep(0.05)
os.kill(sp.spawn.pid, signal.SIGUSR1)
sleep(0.05)
send("\x1bdef")
expect_str(r"Got SIGUSR1 2")
expect_str(r"Wacky Handler")
sendline(r"")
expect_prompt()
# As before but it comes after the ESC.
# The signal will arrive while we are waiting in getch_timed().
send("abc\x1b")
sleep(0.05)
os.kill(sp.spawn.pid, signal.SIGUSR1)
sleep(0.05)
send("def")
expect_str(r"Got SIGUSR1 3")
expect_str(r"Wacky Handler")
sendline(r"")
expect_prompt()