summaryrefslogtreecommitdiffhomepage
path: root/packages/tui/input/key_test.go
diff options
context:
space:
mode:
authorDax Raad <[email protected]>2025-11-02 18:43:17 -0500
committerDax Raad <[email protected]>2025-11-02 18:43:33 -0500
commitf68374ad2223ddc213bdea9519ca6a699819ee0e (patch)
tree04f0fe21b8e12cd62d7274961bb0cff64f966f40 /packages/tui/input/key_test.go
parent5e86c9b7916f75c7ad227b80eab18c7c54fc8ffe (diff)
downloadopencode-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.go880
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:])
- }
- }
-}