1package vt
2
3import (
4 "strings"
5 "testing"
6
7 "github.com/stretchr/testify/require"
8)
9
10func TestScreenKittyKeyboardFlags(t *testing.T) {
11 tests := []struct {
12 name string
13 input string
14 want int
15 }{
16 {name: "default", input: "", want: 0},
17 {name: "push", input: "\x1b[>1u", want: 1},
18 {name: "push without flags", input: "\x1b[>u", want: 0},
19 {name: "push masks unknown bits", input: "\x1b[>255u", want: 31},
20 {name: "push twice", input: "\x1b[>1u\x1b[>11u", want: 11},
21 {name: "pop default one", input: "\x1b[>1u\x1b[>11u\x1b[<u", want: 1},
22 {name: "pop n", input: "\x1b[>1u\x1b[>3u\x1b[>11u\x1b[<2u", want: 1},
23 {name: "pop past empty", input: "\x1b[>1u\x1b[<5u", want: 0},
24 {name: "pop empty", input: "\x1b[<u", want: 0},
25 {name: "set replace on empty", input: "\x1b[=5u", want: 5},
26 {name: "set replace", input: "\x1b[>1u\x1b[=8;1u", want: 8},
27 {name: "set or", input: "\x1b[>1u\x1b[=8;2u", want: 9},
28 {name: "set clear", input: "\x1b[>11u\x1b[=2;3u", want: 9},
29 {name: "set unknown mode ignored", input: "\x1b[>1u\x1b[=8;4u", want: 1},
30 {name: "set replaces top only", input: "\x1b[>1u\x1b[>2u\x1b[=4u\x1b[<u", want: 1},
31 {name: "malformed flags are zero", input: "\x1b[>xu", want: 0},
32 {name: "plain CSI u restores cursor only", input: "\x1b[>3u\x1b[u", want: 3},
33 {name: "RIS resets", input: "\x1b[>3u\x1bc", want: 0},
34 {name: "alternate screen has its own stack", input: "\x1b[>1u\x1b[?1049h", want: 0},
35 {name: "alternate screen push", input: "\x1b[>1u\x1b[?1049h\x1b[>15u", want: 15},
36 {name: "leaving alternate restores main stack", input: "\x1b[>1u\x1b[?1049h\x1b[>15u\x1b[?1049l", want: 1},
37 }
38 for _, tt := range tests {
39 t.Run(tt.name, func(t *testing.T) {
40 s := NewScreen(10, 3)
41 s.Write([]byte(tt.input))
42 require.Equal(t, tt.want, s.KittyKeyboardFlags())
43 require.Equal(t, tt.want, s.Snapshot().Modes().KittyKeyboard)
44 })
45 }
46}
47
48func TestScreenKittyKeyboardStackIsBounded(t *testing.T) {
49 s := NewScreen(10, 3)
50 s.Write([]byte("\x1b[>1u"))
51 s.Write([]byte(strings.Repeat("\x1b[>2u", maxKittyKeyboardStack)))
52 require.Len(t, s.kittyKeyboard, maxKittyKeyboardStack)
53 s.Write([]byte(strings.Repeat("\x1b[<u", maxKittyKeyboardStack-1)))
54 require.Equal(t, 2, s.KittyKeyboardFlags(), "oldest entry was evicted")
55 s.Write([]byte("\x1b[<u"))
56 require.Equal(t, 0, s.KittyKeyboardFlags())
57}
58
59func TestScreenKittyKeyboardQuery(t *testing.T) {
60 tests := []struct {
61 name string
62 input string
63 want []string
64 }{
65 {name: "empty", input: "\x1b[?u", want: []string{"\x1b[?0u"}},
66 {name: "after push", input: "\x1b[>11u\x1b[?u", want: []string{"\x1b[?11u"}},
67 {name: "with params is ignored", input: "\x1b[?1u", want: nil},
68 {name: "split across writes", input: "\x1b[>3u\x1b[?", want: nil},
69 }
70 for _, tt := range tests {
71 t.Run(tt.name, func(t *testing.T) {
72 s := NewScreen(10, 3)
73 var got []string
74 s.OnResponse = func(b []byte) { got = append(got, string(b)) }
75 s.Write([]byte(tt.input))
76 require.Equal(t, tt.want, got)
77 })
78 }
79}