diff options
| author | Dax Raad <[email protected]> | 2025-11-02 18:43:17 -0500 |
|---|---|---|
| committer | Dax Raad <[email protected]> | 2025-11-02 18:43:33 -0500 |
| commit | f68374ad2223ddc213bdea9519ca6a699819ee0e (patch) | |
| tree | 04f0fe21b8e12cd62d7274961bb0cff64f966f40 /packages/tui/input/key_test.go | |
| parent | 5e86c9b7916f75c7ad227b80eab18c7c54fc8ffe (diff) | |
| download | opencode-f68374ad2223ddc213bdea9519ca6a699819ee0e.tar.gz opencode-f68374ad2223ddc213bdea9519ca6a699819ee0e.zip | |
DELETE GO BUBBLETEA CRAP HOORAY
Diffstat (limited to 'packages/tui/input/key_test.go')
| -rw-r--r-- | packages/tui/input/key_test.go | 880 |
1 files changed, 0 insertions, 880 deletions
diff --git a/packages/tui/input/key_test.go b/packages/tui/input/key_test.go deleted file mode 100644 index b09f2f859..000000000 --- a/packages/tui/input/key_test.go +++ /dev/null @@ -1,880 +0,0 @@ -package input - -import ( - "bytes" - "context" - "errors" - "flag" - "fmt" - "image/color" - "io" - "math/rand" - "reflect" - "regexp" - "runtime" - "sort" - "strings" - "sync" - "testing" - "time" - - "github.com/charmbracelet/x/ansi" - "github.com/charmbracelet/x/ansi/kitty" -) - -var sequences = buildKeysTable(FlagTerminfo, "dumb") - -func TestKeyString(t *testing.T) { - t.Run("alt+space", func(t *testing.T) { - k := KeyPressEvent{Code: KeySpace, Mod: ModAlt} - if got := k.String(); got != "alt+space" { - t.Fatalf(`expected a "alt+space", got %q`, got) - } - }) - - t.Run("runes", func(t *testing.T) { - k := KeyPressEvent{Code: 'a', Text: "a"} - if got := k.String(); got != "a" { - t.Fatalf(`expected an "a", got %q`, got) - } - }) - - t.Run("invalid", func(t *testing.T) { - k := KeyPressEvent{Code: 99999} - if got := k.String(); got != "𘚟" { - t.Fatalf(`expected a "unknown", got %q`, got) - } - }) - - t.Run("space", func(t *testing.T) { - k := KeyPressEvent{Code: KeySpace, Text: " "} - if got := k.String(); got != "space" { - t.Fatalf(`expected a "space", got %q`, got) - } - }) - - t.Run("shift+space", func(t *testing.T) { - k := KeyPressEvent{Code: KeySpace, Mod: ModShift} - if got := k.String(); got != "shift+space" { - t.Fatalf(`expected a "shift+space", got %q`, got) - } - }) - - t.Run("?", func(t *testing.T) { - k := KeyPressEvent{Code: '/', Mod: ModShift, Text: "?"} - if got := k.String(); got != "?" { - t.Fatalf(`expected a "?", got %q`, got) - } - }) -} - -type seqTest struct { - seq []byte - Events []Event -} - -var f3CurPosRegexp = regexp.MustCompile(`\x1b\[1;(\d+)R`) - -// buildBaseSeqTests returns sequence tests that are valid for the -// detectSequence() function. -func buildBaseSeqTests() []seqTest { - td := []seqTest{} - for seq, key := range sequences { - k := KeyPressEvent(key) - st := seqTest{seq: []byte(seq), Events: []Event{k}} - - // XXX: This is a special case to handle F3 key sequence and cursor - // position report having the same sequence. See [parseCsi] for more - // information. - if f3CurPosRegexp.MatchString(seq) { - st.Events = []Event{k, CursorPositionEvent{Y: 0, X: int(key.Mod)}} - } - td = append(td, st) - } - - // Additional special cases. - td = append(td, - // Unrecognized CSI sequence. - seqTest{ - []byte{'\x1b', '[', '-', '-', '-', '-', 'X'}, - []Event{ - UnknownEvent([]byte{'\x1b', '[', '-', '-', '-', '-', 'X'}), - }, - }, - // A lone space character. - seqTest{ - []byte{' '}, - []Event{ - KeyPressEvent{Code: KeySpace, Text: " "}, - }, - }, - // An escape character with the alt modifier. - seqTest{ - []byte{'\x1b', ' '}, - []Event{ - KeyPressEvent{Code: KeySpace, Mod: ModAlt}, - }, - }, - ) - return td -} - -func TestParseSequence(t *testing.T) { - td := buildBaseSeqTests() - td = append(td, - // Background color. - seqTest{ - []byte("\x1b]11;rgb:1234/1234/1234\x07"), - []Event{BackgroundColorEvent{ - Color: color.RGBA{R: 0x12, G: 0x12, B: 0x12, A: 0xff}, - }}, - }, - seqTest{ - []byte("\x1b]11;rgb:1234/1234/1234\x1b\\"), - []Event{BackgroundColorEvent{ - Color: color.RGBA{R: 0x12, G: 0x12, B: 0x12, A: 0xff}, - }}, - }, - seqTest{ - []byte("\x1b]11;rgb:1234/1234/1234\x1b"), // Incomplete sequences are ignored. - []Event{ - UnknownEvent("\x1b]11;rgb:1234/1234/1234\x1b"), - }, - }, - - // Kitty Graphics response. - seqTest{ - []byte("\x1b_Ga=t;OK\x1b\\"), - []Event{KittyGraphicsEvent{ - Options: kitty.Options{Action: kitty.Transmit}, - Payload: []byte("OK"), - }}, - }, - seqTest{ - []byte("\x1b_Gi=99,I=13;OK\x1b\\"), - []Event{KittyGraphicsEvent{ - Options: kitty.Options{ID: 99, Number: 13}, - Payload: []byte("OK"), - }}, - }, - seqTest{ - []byte("\x1b_Gi=1337,q=1;EINVAL:your face\x1b\\"), - []Event{KittyGraphicsEvent{ - Options: kitty.Options{ID: 1337, Quite: 1}, - Payload: []byte("EINVAL:your face"), - }}, - }, - - // Xterm modifyOtherKeys CSI 27 ; <modifier> ; <code> ~ - seqTest{ - []byte("\x1b[27;3;20320~"), - []Event{KeyPressEvent{Code: 'ä½ ', Mod: ModAlt}}, - }, - seqTest{ - []byte("\x1b[27;3;65~"), - []Event{KeyPressEvent{Code: 'A', Mod: ModAlt}}, - }, - seqTest{ - []byte("\x1b[27;3;8~"), - []Event{KeyPressEvent{Code: KeyBackspace, Mod: ModAlt}}, - }, - seqTest{ - []byte("\x1b[27;3;27~"), - []Event{KeyPressEvent{Code: KeyEscape, Mod: ModAlt}}, - }, - seqTest{ - []byte("\x1b[27;3;127~"), - []Event{KeyPressEvent{Code: KeyBackspace, Mod: ModAlt}}, - }, - - // Xterm report window text area size. - seqTest{ - []byte("\x1b[4;24;80t"), - []Event{ - WindowOpEvent{Op: 4, Args: []int{24, 80}}, - }, - }, - - // Kitty keyboard / CSI u (fixterms) - seqTest{ - []byte("\x1b[1B"), - []Event{KeyPressEvent{Code: KeyDown}}, - }, - seqTest{ - []byte("\x1b[1;B"), - []Event{KeyPressEvent{Code: KeyDown}}, - }, - seqTest{ - []byte("\x1b[1;4B"), - []Event{KeyPressEvent{Mod: ModShift | ModAlt, Code: KeyDown}}, - }, - seqTest{ - []byte("\x1b[1;4:1B"), - []Event{KeyPressEvent{Mod: ModShift | ModAlt, Code: KeyDown}}, - }, - seqTest{ - []byte("\x1b[1;4:2B"), - []Event{KeyPressEvent{Mod: ModShift | ModAlt, Code: KeyDown, IsRepeat: true}}, - }, - seqTest{ - []byte("\x1b[1;4:3B"), - []Event{KeyReleaseEvent{Mod: ModShift | ModAlt, Code: KeyDown}}, - }, - seqTest{ - []byte("\x1b[8~"), - []Event{KeyPressEvent{Code: KeyEnd}}, - }, - seqTest{ - []byte("\x1b[8;~"), - []Event{KeyPressEvent{Code: KeyEnd}}, - }, - seqTest{ - []byte("\x1b[8;10~"), - []Event{KeyPressEvent{Mod: ModShift | ModMeta, Code: KeyEnd}}, - }, - seqTest{ - []byte("\x1b[27;4u"), - []Event{KeyPressEvent{Mod: ModShift | ModAlt, Code: KeyEscape}}, - }, - seqTest{ - []byte("\x1b[127;4u"), - []Event{KeyPressEvent{Mod: ModShift | ModAlt, Code: KeyBackspace}}, - }, - seqTest{ - []byte("\x1b[57358;4u"), - []Event{KeyPressEvent{Mod: ModShift | ModAlt, Code: KeyCapsLock}}, - }, - seqTest{ - []byte("\x1b[9;2u"), - []Event{KeyPressEvent{Mod: ModShift, Code: KeyTab}}, - }, - seqTest{ - []byte("\x1b[195;u"), - []Event{KeyPressEvent{Text: "Ã", Code: 'Ã'}}, - }, - seqTest{ - []byte("\x1b[20320;2u"), - []Event{KeyPressEvent{Text: "ä½ ", Mod: ModShift, Code: 'ä½ '}}, - }, - seqTest{ - []byte("\x1b[195;:1u"), - []Event{KeyPressEvent{Text: "Ã", Code: 'Ã'}}, - }, - seqTest{ - []byte("\x1b[195;2:3u"), - []Event{KeyReleaseEvent{Code: 'Ã', Text: "Ã", Mod: ModShift}}, - }, - seqTest{ - []byte("\x1b[195;2:2u"), - []Event{KeyPressEvent{Code: 'Ã', Text: "Ã", IsRepeat: true, Mod: ModShift}}, - }, - seqTest{ - []byte("\x1b[195;2:1u"), - []Event{KeyPressEvent{Code: 'Ã', Text: "Ã", Mod: ModShift}}, - }, - seqTest{ - []byte("\x1b[195;2:3u"), - []Event{KeyReleaseEvent{Code: 'Ã', Text: "Ã", Mod: ModShift}}, - }, - seqTest{ - []byte("\x1b[97;2;65u"), - []Event{KeyPressEvent{Code: 'a', Text: "A", Mod: ModShift}}, - }, - seqTest{ - []byte("\x1b[97;;229u"), - []Event{KeyPressEvent{Code: 'a', Text: "Ã¥"}}, - }, - - // focus/blur - seqTest{ - []byte{'\x1b', '[', 'I'}, - []Event{ - FocusEvent{}, - }, - }, - seqTest{ - []byte{'\x1b', '[', 'O'}, - []Event{ - BlurEvent{}, - }, - }, - // Mouse event. - seqTest{ - []byte{'\x1b', '[', 'M', byte(32) + 0b0100_0000, byte(65), byte(49)}, - []Event{ - MouseWheelEvent{X: 32, Y: 16, Button: MouseWheelUp}, - }, - }, - // SGR Mouse event. - seqTest{ - []byte("\x1b[<0;33;17M"), - []Event{ - MouseClickEvent{X: 32, Y: 16, Button: MouseLeft}, - }, - }, - // Runes. - seqTest{ - []byte{'a'}, - []Event{ - KeyPressEvent{Code: 'a', Text: "a"}, - }, - }, - seqTest{ - []byte{'\x1b', 'a'}, - []Event{ - KeyPressEvent{Code: 'a', Mod: ModAlt}, - }, - }, - seqTest{ - []byte{'a', 'a', 'a'}, - []Event{ - KeyPressEvent{Code: 'a', Text: "a"}, - KeyPressEvent{Code: 'a', Text: "a"}, - KeyPressEvent{Code: 'a', Text: "a"}, - }, - }, - // Multi-byte rune. - seqTest{ - []byte("☃"), - []Event{ - KeyPressEvent{Code: '☃', Text: "☃"}, - }, - }, - seqTest{ - []byte("\x1b☃"), - []Event{ - KeyPressEvent{Code: '☃', Mod: ModAlt}, - }, - }, - // Standalone control characters. - seqTest{ - []byte{'\x1b'}, - []Event{ - KeyPressEvent{Code: KeyEscape}, - }, - }, - seqTest{ - []byte{ansi.SOH}, - []Event{ - KeyPressEvent{Code: 'a', Mod: ModCtrl}, - }, - }, - seqTest{ - []byte{'\x1b', ansi.SOH}, - []Event{ - KeyPressEvent{Code: 'a', Mod: ModCtrl | ModAlt}, - }, - }, - seqTest{ - []byte{ansi.NUL}, - []Event{ - KeyPressEvent{Code: KeySpace, Mod: ModCtrl}, - }, - }, - seqTest{ - []byte{'\x1b', ansi.NUL}, - []Event{ - KeyPressEvent{Code: KeySpace, Mod: ModCtrl | ModAlt}, - }, - }, - // C1 control characters. - seqTest{ - []byte{'\x80'}, - []Event{ - KeyPressEvent{Code: rune(0x80 - '@'), Mod: ModCtrl | ModAlt}, - }, - }, - ) - - if runtime.GOOS != "windows" { - // Sadly, utf8.DecodeRune([]byte(0xfe)) returns a valid rune on windows. - // This is incorrect, but it makes our test fail if we try it out. - td = append(td, seqTest{ - []byte{'\xfe'}, - []Event{ - UnknownEvent(rune(0xfe)), - }, - }) - } - - var p Parser - for _, tc := range td { - t.Run(fmt.Sprintf("%q", string(tc.seq)), func(t *testing.T) { - var events []Event - buf := tc.seq - for len(buf) > 0 { - width, Event := p.parseSequence(buf) - switch Event := Event.(type) { - case MultiEvent: - events = append(events, Event...) - default: - events = append(events, Event) - } - buf = buf[width:] - } - if !reflect.DeepEqual(tc.Events, events) { - t.Errorf("\nexpected event for %q:\n %#v\ngot:\n %#v", tc.seq, tc.Events, events) - } - }) - } -} - -func TestReadLongInput(t *testing.T) { - expect := make([]Event, 1000) - for i := range 1000 { - expect[i] = KeyPressEvent{Code: 'a', Text: "a"} - } - input := strings.Repeat("a", 1000) - drv, err := NewReader(strings.NewReader(input), "dumb", 0) - if err != nil { - t.Fatalf("unexpected input driver error: %v", err) - } - - var Events []Event - for { - events, err := drv.ReadEvents() - if err == io.EOF { - break - } - if err != nil { - t.Fatalf("unexpected input error: %v", err) - } - Events = append(Events, events...) - } - - if !reflect.DeepEqual(expect, Events) { - t.Errorf("unexpected messages, expected:\n %+v\ngot:\n %+v", expect, Events) - } -} - -func TestReadInput(t *testing.T) { - type test struct { - keyname string - in []byte - out []Event - } - testData := []test{ - { - "a", - []byte{'a'}, - []Event{ - KeyPressEvent{Code: 'a', Text: "a"}, - }, - }, - { - "space", - []byte{' '}, - []Event{ - KeyPressEvent{Code: KeySpace, Text: " "}, - }, - }, - { - "a alt+a", - []byte{'a', '\x1b', 'a'}, - []Event{ - KeyPressEvent{Code: 'a', Text: "a"}, - KeyPressEvent{Code: 'a', Mod: ModAlt}, - }, - }, - { - "a alt+a a", - []byte{'a', '\x1b', 'a', 'a'}, - []Event{ - KeyPressEvent{Code: 'a', Text: "a"}, - KeyPressEvent{Code: 'a', Mod: ModAlt}, - KeyPressEvent{Code: 'a', Text: "a"}, - }, - }, - { - "ctrl+a", - []byte{byte(ansi.SOH)}, - []Event{ - KeyPressEvent{Code: 'a', Mod: ModCtrl}, - }, - }, - { - "ctrl+a ctrl+b", - []byte{byte(ansi.SOH), byte(ansi.STX)}, - []Event{ - KeyPressEvent{Code: 'a', Mod: ModCtrl}, - KeyPressEvent{Code: 'b', Mod: ModCtrl}, - }, - }, - { - "alt+a", - []byte{byte(0x1b), 'a'}, - []Event{ - KeyPressEvent{Code: 'a', Mod: ModAlt}, - }, - }, - { - "a b c d", - []byte{'a', 'b', 'c', 'd'}, - []Event{ - KeyPressEvent{Code: 'a', Text: "a"}, - KeyPressEvent{Code: 'b', Text: "b"}, - KeyPressEvent{Code: 'c', Text: "c"}, - KeyPressEvent{Code: 'd', Text: "d"}, - }, - }, - { - "up", - []byte("\x1b[A"), - []Event{ - KeyPressEvent{Code: KeyUp}, - }, - }, - { - "wheel up", - []byte{'\x1b', '[', 'M', byte(32) + 0b0100_0000, byte(65), byte(49)}, - []Event{ - MouseWheelEvent{X: 32, Y: 16, Button: MouseWheelUp}, - }, - }, - { - "left motion release", - []byte{ - '\x1b', '[', 'M', byte(32) + 0b0010_0000, byte(32 + 33), byte(16 + 33), - '\x1b', '[', 'M', byte(32) + 0b0000_0011, byte(64 + 33), byte(32 + 33), - }, - []Event{ - MouseMotionEvent{X: 32, Y: 16, Button: MouseLeft}, - MouseReleaseEvent{X: 64, Y: 32, Button: MouseNone}, - }, - }, - { - "shift+tab", - []byte{'\x1b', '[', 'Z'}, - []Event{ - KeyPressEvent{Code: KeyTab, Mod: ModShift}, - }, - }, - { - "enter", - []byte{'\r'}, - []Event{KeyPressEvent{Code: KeyEnter}}, - }, - { - "alt+enter", - []byte{'\x1b', '\r'}, - []Event{ - KeyPressEvent{Code: KeyEnter, Mod: ModAlt}, - }, - }, - { - "insert", - []byte{'\x1b', '[', '2', '~'}, - []Event{ - KeyPressEvent{Code: KeyInsert}, - }, - }, - { - "ctrl+alt+a", - []byte{'\x1b', byte(ansi.SOH)}, - []Event{ - KeyPressEvent{Code: 'a', Mod: ModCtrl | ModAlt}, - }, - }, - { - "CSI?----X?", - []byte{'\x1b', '[', '-', '-', '-', '-', 'X'}, - []Event{UnknownEvent([]byte{'\x1b', '[', '-', '-', '-', '-', 'X'})}, - }, - // Powershell sequences. - { - "up", - []byte{'\x1b', 'O', 'A'}, - []Event{KeyPressEvent{Code: KeyUp}}, - }, - { - "down", - []byte{'\x1b', 'O', 'B'}, - []Event{KeyPressEvent{Code: KeyDown}}, - }, - { - "right", - []byte{'\x1b', 'O', 'C'}, - []Event{KeyPressEvent{Code: KeyRight}}, - }, - { - "left", - []byte{'\x1b', 'O', 'D'}, - []Event{KeyPressEvent{Code: KeyLeft}}, - }, - { - "alt+enter", - []byte{'\x1b', '\x0d'}, - []Event{KeyPressEvent{Code: KeyEnter, Mod: ModAlt}}, - }, - { - "alt+backspace", - []byte{'\x1b', '\x7f'}, - []Event{KeyPressEvent{Code: KeyBackspace, Mod: ModAlt}}, - }, - { - "ctrl+space", - []byte{'\x00'}, - []Event{KeyPressEvent{Code: KeySpace, Mod: ModCtrl}}, - }, - { - "ctrl+alt+space", - []byte{'\x1b', '\x00'}, - []Event{KeyPressEvent{Code: KeySpace, Mod: ModCtrl | ModAlt}}, - }, - { - "esc", - []byte{'\x1b'}, - []Event{KeyPressEvent{Code: KeyEscape}}, - }, - { - "alt+esc", - []byte{'\x1b', '\x1b'}, - []Event{KeyPressEvent{Code: KeyEscape, Mod: ModAlt}}, - }, - { - "a b o", - []byte{ - '\x1b', '[', '2', '0', '0', '~', - 'a', ' ', 'b', - '\x1b', '[', '2', '0', '1', '~', - 'o', - }, - []Event{ - PasteStartEvent{}, - PasteEvent("a b"), - PasteEndEvent{}, - KeyPressEvent{Code: 'o', Text: "o"}, - }, - }, - { - "a\x03\nb", - []byte{ - '\x1b', '[', '2', '0', '0', '~', - 'a', '\x03', '\n', 'b', - '\x1b', '[', '2', '0', '1', '~', - }, - []Event{ - PasteStartEvent{}, - PasteEvent("a\x03\nb"), - PasteEndEvent{}, - }, - }, - { - "?0xfe?", - []byte{'\xfe'}, - []Event{ - UnknownEvent(rune(0xfe)), - }, - }, - { - "a ?0xfe? b", - []byte{'a', '\xfe', ' ', 'b'}, - []Event{ - KeyPressEvent{Code: 'a', Text: "a"}, - UnknownEvent(rune(0xfe)), - KeyPressEvent{Code: KeySpace, Text: " "}, - KeyPressEvent{Code: 'b', Text: "b"}, - }, - }, - } - - for i, td := range testData { - t.Run(fmt.Sprintf("%d: %s", i, td.keyname), func(t *testing.T) { - Events := testReadInputs(t, bytes.NewReader(td.in)) - var buf strings.Builder - for i, Event := range Events { - if i > 0 { - buf.WriteByte(' ') - } - if s, ok := Event.(fmt.Stringer); ok { - buf.WriteString(s.String()) - } else { - fmt.Fprintf(&buf, "%#v:%T", Event, Event) - } - } - - if len(Events) != len(td.out) { - t.Fatalf("unexpected message list length: got %d, expected %d\n got: %#v\n expected: %#v\n", len(Events), len(td.out), Events, td.out) - } - - if !reflect.DeepEqual(td.out, Events) { - t.Fatalf("expected:\n%#v\ngot:\n%#v", td.out, Events) - } - }) - } -} - -func testReadInputs(t *testing.T, input io.Reader) []Event { - // We'll check that the input reader finishes at the end - // without error. - var wg sync.WaitGroup - var inputErr error - ctx, cancel := context.WithCancel(context.Background()) - defer func() { - cancel() - wg.Wait() - if inputErr != nil && !errors.Is(inputErr, io.EOF) { - t.Fatalf("unexpected input error: %v", inputErr) - } - }() - - dr, err := NewReader(input, "dumb", 0) - if err != nil { - t.Fatalf("unexpected input driver error: %v", err) - } - - // The messages we're consuming. - EventsC := make(chan Event) - - // Start the reader in the background. - wg.Add(1) - go func() { - defer wg.Done() - var events []Event - events, inputErr = dr.ReadEvents() - out: - for _, ev := range events { - select { - case EventsC <- ev: - case <-ctx.Done(): - break out - } - } - EventsC <- nil - }() - - var Events []Event -loop: - for { - select { - case Event := <-EventsC: - if Event == nil { - // end of input marker for the test. - break loop - } - Events = append(Events, Event) - case <-time.After(2 * time.Second): - t.Errorf("timeout waiting for input event") - break loop - } - } - return Events -} - -// randTest defines the test input and expected output for a sequence -// of interleaved control sequences and control characters. -type randTest struct { - data []byte - lengths []int - names []string -} - -// seed is the random seed to randomize the input. This helps check -// that all the sequences get ultimately exercised. -var seed = flag.Int64("seed", 0, "random seed (0 to autoselect)") - -// genRandomData generates a randomized test, with a random seed unless -// the seed flag was set. -func genRandomData(logfn func(int64), length int) randTest { - // We'll use a random source. However, we give the user the option - // to override it to a specific value for reproducibility. - s := *seed - if s == 0 { - s = time.Now().UnixNano() - } - // Inform the user so they know what to reuse to get the same data. - logfn(s) - return genRandomDataWithSeed(s, length) -} - -// genRandomDataWithSeed generates a randomized test with a fixed seed. -func genRandomDataWithSeed(s int64, length int) randTest { - src := rand.NewSource(s) - r := rand.New(src) - - // allseqs contains all the sequences, in sorted order. We sort - // to make the test deterministic (when the seed is also fixed). - type seqpair struct { - seq string - name string - } - var allseqs []seqpair - for seq, key := range sequences { - allseqs = append(allseqs, seqpair{seq, key.String()}) - } - sort.Slice(allseqs, func(i, j int) bool { return allseqs[i].seq < allseqs[j].seq }) - - // res contains the computed test. - var res randTest - - for len(res.data) < length { - alt := r.Intn(2) - prefix := "" - esclen := 0 - if alt == 1 { - prefix = "alt+" - esclen = 1 - } - kind := r.Intn(3) - switch kind { - case 0: - // A control character. - if alt == 1 { - res.data = append(res.data, '\x1b') - } - res.data = append(res.data, 1) - res.names = append(res.names, "ctrl+"+prefix+"a") - res.lengths = append(res.lengths, 1+esclen) - - case 1, 2: - // A sequence. - seqi := r.Intn(len(allseqs)) - s := allseqs[seqi] - if strings.Contains(s.name, "alt+") || strings.Contains(s.name, "meta+") { - esclen = 0 - prefix = "" - alt = 0 - } - if alt == 1 { - res.data = append(res.data, '\x1b') - } - res.data = append(res.data, s.seq...) - if strings.HasPrefix(s.name, "ctrl+") { - prefix = "ctrl+" + prefix - } - name := prefix + strings.TrimPrefix(s.name, "ctrl+") - res.names = append(res.names, name) - res.lengths = append(res.lengths, len(s.seq)+esclen) - } - } - return res -} - -func FuzzParseSequence(f *testing.F) { - var p Parser - for seq := range sequences { - f.Add(seq) - } - f.Add("\x1b]52;?\x07") // OSC 52 - f.Add("\x1b]11;rgb:0000/0000/0000\x1b\\") // OSC 11 - f.Add("\x1bP>|charm terminal(0.1.2)\x1b\\") // DCS (XTVERSION) - f.Add("\x1b_Gi=123\x1b\\") // APC - f.Fuzz(func(t *testing.T, seq string) { - n, _ := p.parseSequence([]byte(seq)) - if n == 0 && seq != "" { - t.Errorf("expected a non-zero width for %q", seq) - } - }) -} - -// BenchmarkDetectSequenceMap benchmarks the map-based sequence -// detector. -func BenchmarkDetectSequenceMap(b *testing.B) { - var p Parser - td := genRandomDataWithSeed(123, 10000) - for i := 0; i < b.N; i++ { - for j, w := 0, 0; j < len(td.data); j += w { - w, _ = p.parseSequence(td.data[j:]) - } - } -} |
