package app import "testing" func TestMatchCtrlK(t *testing.T) { cases := []struct { name string chunk string offset int wantMatch bool wantAdvance int }{ {"legacy lone byte", "\x0b", 0, true, 1}, {"legacy followed by text", "\x0bx", 0, true, 1}, {"kitty plain Ctrl-K", "\x1b[107;5u", 0, true, 8}, {"kitty with press event", "\x1b[107;5:1u", 0, true, 10}, {"kitty with key release", "\x1b[107;5:3u", 0, false, 0}, {"kitty with extra shift", "\x1b[107;6u", 0, false, 0}, {"kitty no modifier", "\x1b[107u", 0, false, 0}, {"kitty wrong key", "\x1b[108;5u", 0, false, 0}, {"kitty with associated text trailing group", "\x1b[107;5;107u", 0, true, 12}, {"modifyOtherKeys Ctrl-K", "\x1b[27;5;107~", 0, true, 11}, {"modifyOtherKeys wrong mods", "\x1b[27;6;107~", 0, false, 0}, {"unrelated CSI", "\x1b[A", 0, false, 0}, {"plain ascii", "k", 0, false, 0}, {"empty", "", 0, false, 0}, {"incomplete CSI", "\x1b[107;5", 0, false, 0}, {"offset past legacy", "x\x0b", 1, true, 1}, {"offset past kitty prefix", "x\x1b[107;5u", 1, true, 8}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { got, adv := matchCtrlK([]byte(tc.chunk), tc.offset) if got != tc.wantMatch || adv != tc.wantAdvance { t.Fatalf("matchCtrlK(%q, %d) = (%v, %d); want (%v, %d)", tc.chunk, tc.offset, got, adv, tc.wantMatch, tc.wantAdvance) } }) } } func TestParseSGRMouseWheel(t *testing.T) { cases := []struct { params string want int ok bool }{ {"64;1;1", -3, true}, // wheel up {"65;1;1", 3, true}, // wheel down {"68;1;1", -3, true}, // shift+wheel up {"69;1;1", 3, true}, // shift+wheel down {"80;1;1", -3, true}, // ctrl+wheel up {"81;1;1", 3, true}, // ctrl+wheel down {"0;5;7", 0, false}, // left press {"2;5;7", 0, false}, // right press {"32;5;7", 0, false}, // drag {"", 0, false}, // empty {"abc;1;1", 0, false}, // garbage button } for _, c := range cases { got, ok := parseSGRMouseWheel([]byte(c.params)) if ok != c.ok || got != c.want { t.Errorf("parseSGRMouseWheel(%q) = (%d,%v), want (%d,%v)", c.params, got, ok, c.want, c.ok) } } } func TestMatchCtrlKConsecutive(t *testing.T) { // Two kitty Ctrl-K sequences back to back, the chord case. chunk := []byte("\x1b[107;5u\x1b[107;5u") hit, adv := matchCtrlK(chunk, 0) if !hit || adv != 8 { t.Fatalf("first: hit=%v adv=%d", hit, adv) } hit2, adv2 := matchCtrlK(chunk, adv) if !hit2 || adv2 != 8 { t.Fatalf("second: hit=%v adv=%d", hit2, adv2) } if adv+adv2 != len(chunk) { t.Fatalf("expected to cover the whole chunk, got %d/%d", adv+adv2, len(chunk)) } }