okami.mom/ultraviolet

git clone https://git.nkp.pet/okami.mom/ultraviolet

nekomimi.petfix(input): keep control strings whole when they span reads7bfbaab

main
2.1 KiB74 linesraw
1package uv
2
3import (
4	"io"
5	"reflect"
6	"strings"
7	"testing"
8	"time"
9)
10
11// Control strings split across reads, the way a large OSC 5522 clipboard
12// reply arrives over ssh.
13
14// stalledReader returns one chunk per Read, pausing before each like a slow
15// network link.
16type stalledReader struct {
17	chunks []string
18	pause  time.Duration
19}
20
21func (r *stalledReader) Read(p []byte) (int, error) {
22	if len(r.chunks) == 0 {
23		return 0, io.EOF
24	}
25	time.Sleep(r.pause)
26	n := copy(p, r.chunks[0])
27	r.chunks = r.chunks[1:]
28	return n, nil
29}
30
31func TestDecodeWaitsForSplitStringTerminator(t *testing.T) {
32	for name, seq := range map[string]string{
33		"osc": "\x1b]5522;type=read:status=DATA;QUJD\x1b",
34		"apc": "\x1b_Gi=1;OK\x1b",
35		"dcs": "\x1bP1+r5463=31\x1b",
36	} {
37		t.Run(name, func(t *testing.T) {
38			var d EventDecoder
39			n, ev := d.Decode([]byte(seq))
40			if _, ok := ev.(UnknownEvent); !ok || n != len(seq) {
41				t.Fatalf("Decode(%q) = %d, %T; want an incomplete UnknownEvent over the whole buffer", seq, n, ev)
42			}
43		})
44	}
45}
46
47func TestStreamEventsKeepsOscAcrossStalledReads(t *testing.T) {
48	payload := strings.Repeat("QUJD", 2000)
49	osc := "\x1b]5522;type=read:status=DATA:id=x;" + payload + "\x1b\\"
50	half := len(osc) / 2
51	chunks := []string{
52		osc[:half],             // stall mid payload
53		osc[half : len(osc)-1], // stall between ESC and the backslash of ST
54		osc[len(osc)-1:] + "a",
55	}
56	events := testReadInputs(t, &stalledReader{chunks: chunks, pause: 4 * DefaultEscTimeout})
57	want := []Event{UnknownOscEvent(osc), KeyPressEvent{Code: 'a', Text: "a"}}
58	if !reflect.DeepEqual(events, want) {
59		t.Fatalf("events = %#v\nwant %#v", events, want)
60	}
61}
62
63func TestStreamEventsFlushesStalledClipboardOscAfterItsTimeout(t *testing.T) {
64	saved := pendingClipboardOscTimeout
65	pendingClipboardOscTimeout = 2 * DefaultEscTimeout
66	t.Cleanup(func() { pendingClipboardOscTimeout = saved })
67
68	head := "\x1b]5522;type=read:status=DATA:id=x;QUJD"
69	events := testReadInputs(t, &stalledReader{chunks: []string{head, "a"}, pause: 6 * DefaultEscTimeout})
70	want := []Event{UnknownEvent(head), KeyPressEvent{Code: 'a', Text: "a"}}
71	if !reflect.DeepEqual(events, want) {
72		t.Fatalf("events = %#v\nwant %#v", events, want)
73	}
74}