1package tui
2
3import (
4 "io"
5 "testing"
6 "time"
7)
8
9func TestParseAppearanceOSC(t *testing.T) {
10 events := ParseAppearanceOSC([]byte("\x1b]4;1;rgb:ff/00/00;12;#123456\x1b\\"))
11 if len(events) != 2 {
12 t.Fatalf("events = %#v, want two palette entries", events)
13 }
14 if events[0].Slot != 1 || events[0].Color != ColorRGB(255, 0, 0) {
15 t.Fatalf("first event = %#v", events[0])
16 }
17 if events[1].Slot != 12 || events[1].Color != ColorRGB(0x12, 0x34, 0x56) {
18 t.Fatalf("second event = %#v", events[1])
19 }
20}
21
22func TestParseAppearanceCSI(t *testing.T) {
23 scheme, mode := ParseAppearanceCSI([]byte("\x1b[?997;2n"))
24 if scheme == nil || !scheme.Light || mode != nil {
25 t.Fatalf("scheme parse = %#v, %#v", scheme, mode)
26 }
27 scheme, mode = ParseAppearanceCSI([]byte("\x1b[?2031;2$y"))
28 if scheme != nil || mode == nil || mode.Status != 2 {
29 t.Fatalf("mode parse = %#v, %#v", scheme, mode)
30 }
31}
32
33type appearanceBytes struct {
34 data []byte
35 minPeek time.Duration
36}
37
38func (s *appearanceBytes) read() (byte, error) {
39 if len(s.data) == 0 {
40 return 0, io.EOF
41 }
42 b := s.data[0]
43 s.data = s.data[1:]
44 return b, nil
45}
46
47func (s *appearanceBytes) peek(timeout time.Duration) (byte, bool, error) {
48 if timeout < s.minPeek {
49 return 0, false, nil
50 }
51 if len(s.data) == 0 {
52 return 0, false, nil
53 }
54 b := s.data[0]
55 s.data = s.data[1:]
56 return b, true, nil
57}
58
59func TestAppearanceSourceConsumesOnlyPendingReplies(t *testing.T) {
60 input := &appearanceBytes{data: []byte("a\x1b]11;#123456\ab")}
61 parser := &AppearanceParser{}
62 parser.SetPendingColors(true)
63 var events []InputEvent
64 source := NewAppearanceSource(input.read, input.peek, parser, func(event InputEvent) { events = append(events, event) })
65 var got []byte
66 for {
67 b, err := source.ReadByte()
68 if err == io.EOF {
69 break
70 }
71 if err != nil {
72 t.Fatal(err)
73 }
74 got = append(got, b)
75 }
76 if string(got) != "ab" {
77 t.Fatalf("remaining bytes = %q, want ab", got)
78 }
79 if len(events) != 1 || events[0].Color == nil || events[0].Color.Kind != 11 {
80 t.Fatalf("events = %#v", events)
81 }
82}
83
84func TestAppearanceSourceAcceptsDelayedReplyWhileQueryPending(t *testing.T) {
85 input := &appearanceBytes{data: []byte("\x1b]11;#123456\a"), minPeek: 2 * time.Millisecond}
86 parser := &AppearanceParser{}
87 parser.SetPendingColors(true)
88 var events []InputEvent
89 source := NewAppearanceSource(input.read, input.peek, parser, func(event InputEvent) { events = append(events, event) })
90 if _, err := source.ReadByte(); err != io.EOF {
91 t.Fatalf("ReadByte error = %v, want EOF after consuming reply", err)
92 }
93 if len(events) != 1 || events[0].Color == nil || events[0].Color.Kind != 11 {
94 t.Fatalf("delayed reply events = %#v", events)
95 }
96}
97
98func TestAppearanceSourceMarksOnlyPendingSchemeReplySolicited(t *testing.T) {
99 input := &appearanceBytes{data: []byte("\x1b[?997;1n\x1b[?997;2n")}
100 parser := &AppearanceParser{}
101 parser.SetPendingScheme(true)
102 parser.SetNotifications(true)
103 var events []InputEvent
104 source := NewAppearanceSource(input.read, input.peek, parser, func(event InputEvent) { events = append(events, event) })
105 for {
106 _, err := source.ReadByte()
107 if err == io.EOF {
108 break
109 }
110 if err != nil {
111 t.Fatal(err)
112 }
113 }
114 if len(events) != 2 || events[0].Scheme == nil || events[1].Scheme == nil || !events[0].Scheme.Solicited || events[1].Scheme.Solicited {
115 t.Fatalf("scheme events = %#v", events)
116 }
117}
118
119func TestAppearanceSourceLeavesPasteOpaque(t *testing.T) {
120 input := &appearanceBytes{data: []byte("\x1b[200~x\x1b]11;#123456\ay\x1b[201~")}
121 parser := &AppearanceParser{}
122 parser.SetPendingColors(true)
123 var events []InputEvent
124 source := NewAppearanceSource(input.read, input.peek, parser, func(event InputEvent) { events = append(events, event) })
125 reader := NewReader(source.ReadByte)
126 key, err := reader.Read()
127 if err != nil {
128 t.Fatal(err)
129 }
130 if key.Kind != KeyPaste || key.Paste != "x\x1b]11;#123456\ay" {
131 t.Fatalf("paste = %#v", key)
132 }
133 if len(events) != 0 {
134 t.Fatalf("appearance events leaked from paste: %#v", events)
135 }
136}