key_test.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880
  1. package input
  2. import (
  3. "bytes"
  4. "context"
  5. "errors"
  6. "flag"
  7. "fmt"
  8. "image/color"
  9. "io"
  10. "math/rand"
  11. "reflect"
  12. "regexp"
  13. "runtime"
  14. "sort"
  15. "strings"
  16. "sync"
  17. "testing"
  18. "time"
  19. "github.com/charmbracelet/x/ansi"
  20. "github.com/charmbracelet/x/ansi/kitty"
  21. )
  22. var sequences = buildKeysTable(FlagTerminfo, "dumb")
  23. func TestKeyString(t *testing.T) {
  24. t.Run("alt+space", func(t *testing.T) {
  25. k := KeyPressEvent{Code: KeySpace, Mod: ModAlt}
  26. if got := k.String(); got != "alt+space" {
  27. t.Fatalf(`expected a "alt+space", got %q`, got)
  28. }
  29. })
  30. t.Run("runes", func(t *testing.T) {
  31. k := KeyPressEvent{Code: 'a', Text: "a"}
  32. if got := k.String(); got != "a" {
  33. t.Fatalf(`expected an "a", got %q`, got)
  34. }
  35. })
  36. t.Run("invalid", func(t *testing.T) {
  37. k := KeyPressEvent{Code: 99999}
  38. if got := k.String(); got != "𘚟" {
  39. t.Fatalf(`expected a "unknown", got %q`, got)
  40. }
  41. })
  42. t.Run("space", func(t *testing.T) {
  43. k := KeyPressEvent{Code: KeySpace, Text: " "}
  44. if got := k.String(); got != "space" {
  45. t.Fatalf(`expected a "space", got %q`, got)
  46. }
  47. })
  48. t.Run("shift+space", func(t *testing.T) {
  49. k := KeyPressEvent{Code: KeySpace, Mod: ModShift}
  50. if got := k.String(); got != "shift+space" {
  51. t.Fatalf(`expected a "shift+space", got %q`, got)
  52. }
  53. })
  54. t.Run("?", func(t *testing.T) {
  55. k := KeyPressEvent{Code: '/', Mod: ModShift, Text: "?"}
  56. if got := k.String(); got != "?" {
  57. t.Fatalf(`expected a "?", got %q`, got)
  58. }
  59. })
  60. }
  61. type seqTest struct {
  62. seq []byte
  63. Events []Event
  64. }
  65. var f3CurPosRegexp = regexp.MustCompile(`\x1b\[1;(\d+)R`)
  66. // buildBaseSeqTests returns sequence tests that are valid for the
  67. // detectSequence() function.
  68. func buildBaseSeqTests() []seqTest {
  69. td := []seqTest{}
  70. for seq, key := range sequences {
  71. k := KeyPressEvent(key)
  72. st := seqTest{seq: []byte(seq), Events: []Event{k}}
  73. // XXX: This is a special case to handle F3 key sequence and cursor
  74. // position report having the same sequence. See [parseCsi] for more
  75. // information.
  76. if f3CurPosRegexp.MatchString(seq) {
  77. st.Events = []Event{k, CursorPositionEvent{Y: 0, X: int(key.Mod)}}
  78. }
  79. td = append(td, st)
  80. }
  81. // Additional special cases.
  82. td = append(td,
  83. // Unrecognized CSI sequence.
  84. seqTest{
  85. []byte{'\x1b', '[', '-', '-', '-', '-', 'X'},
  86. []Event{
  87. UnknownEvent([]byte{'\x1b', '[', '-', '-', '-', '-', 'X'}),
  88. },
  89. },
  90. // A lone space character.
  91. seqTest{
  92. []byte{' '},
  93. []Event{
  94. KeyPressEvent{Code: KeySpace, Text: " "},
  95. },
  96. },
  97. // An escape character with the alt modifier.
  98. seqTest{
  99. []byte{'\x1b', ' '},
  100. []Event{
  101. KeyPressEvent{Code: KeySpace, Mod: ModAlt},
  102. },
  103. },
  104. )
  105. return td
  106. }
  107. func TestParseSequence(t *testing.T) {
  108. td := buildBaseSeqTests()
  109. td = append(td,
  110. // Background color.
  111. seqTest{
  112. []byte("\x1b]11;rgb:1234/1234/1234\x07"),
  113. []Event{BackgroundColorEvent{
  114. Color: color.RGBA{R: 0x12, G: 0x12, B: 0x12, A: 0xff},
  115. }},
  116. },
  117. seqTest{
  118. []byte("\x1b]11;rgb:1234/1234/1234\x1b\\"),
  119. []Event{BackgroundColorEvent{
  120. Color: color.RGBA{R: 0x12, G: 0x12, B: 0x12, A: 0xff},
  121. }},
  122. },
  123. seqTest{
  124. []byte("\x1b]11;rgb:1234/1234/1234\x1b"), // Incomplete sequences are ignored.
  125. []Event{
  126. UnknownEvent("\x1b]11;rgb:1234/1234/1234\x1b"),
  127. },
  128. },
  129. // Kitty Graphics response.
  130. seqTest{
  131. []byte("\x1b_Ga=t;OK\x1b\\"),
  132. []Event{KittyGraphicsEvent{
  133. Options: kitty.Options{Action: kitty.Transmit},
  134. Payload: []byte("OK"),
  135. }},
  136. },
  137. seqTest{
  138. []byte("\x1b_Gi=99,I=13;OK\x1b\\"),
  139. []Event{KittyGraphicsEvent{
  140. Options: kitty.Options{ID: 99, Number: 13},
  141. Payload: []byte("OK"),
  142. }},
  143. },
  144. seqTest{
  145. []byte("\x1b_Gi=1337,q=1;EINVAL:your face\x1b\\"),
  146. []Event{KittyGraphicsEvent{
  147. Options: kitty.Options{ID: 1337, Quite: 1},
  148. Payload: []byte("EINVAL:your face"),
  149. }},
  150. },
  151. // Xterm modifyOtherKeys CSI 27 ; <modifier> ; <code> ~
  152. seqTest{
  153. []byte("\x1b[27;3;20320~"),
  154. []Event{KeyPressEvent{Code: '你', Mod: ModAlt}},
  155. },
  156. seqTest{
  157. []byte("\x1b[27;3;65~"),
  158. []Event{KeyPressEvent{Code: 'A', Mod: ModAlt}},
  159. },
  160. seqTest{
  161. []byte("\x1b[27;3;8~"),
  162. []Event{KeyPressEvent{Code: KeyBackspace, Mod: ModAlt}},
  163. },
  164. seqTest{
  165. []byte("\x1b[27;3;27~"),
  166. []Event{KeyPressEvent{Code: KeyEscape, Mod: ModAlt}},
  167. },
  168. seqTest{
  169. []byte("\x1b[27;3;127~"),
  170. []Event{KeyPressEvent{Code: KeyBackspace, Mod: ModAlt}},
  171. },
  172. // Xterm report window text area size.
  173. seqTest{
  174. []byte("\x1b[4;24;80t"),
  175. []Event{
  176. WindowOpEvent{Op: 4, Args: []int{24, 80}},
  177. },
  178. },
  179. // Kitty keyboard / CSI u (fixterms)
  180. seqTest{
  181. []byte("\x1b[1B"),
  182. []Event{KeyPressEvent{Code: KeyDown}},
  183. },
  184. seqTest{
  185. []byte("\x1b[1;B"),
  186. []Event{KeyPressEvent{Code: KeyDown}},
  187. },
  188. seqTest{
  189. []byte("\x1b[1;4B"),
  190. []Event{KeyPressEvent{Mod: ModShift | ModAlt, Code: KeyDown}},
  191. },
  192. seqTest{
  193. []byte("\x1b[1;4:1B"),
  194. []Event{KeyPressEvent{Mod: ModShift | ModAlt, Code: KeyDown}},
  195. },
  196. seqTest{
  197. []byte("\x1b[1;4:2B"),
  198. []Event{KeyPressEvent{Mod: ModShift | ModAlt, Code: KeyDown, IsRepeat: true}},
  199. },
  200. seqTest{
  201. []byte("\x1b[1;4:3B"),
  202. []Event{KeyReleaseEvent{Mod: ModShift | ModAlt, Code: KeyDown}},
  203. },
  204. seqTest{
  205. []byte("\x1b[8~"),
  206. []Event{KeyPressEvent{Code: KeyEnd}},
  207. },
  208. seqTest{
  209. []byte("\x1b[8;~"),
  210. []Event{KeyPressEvent{Code: KeyEnd}},
  211. },
  212. seqTest{
  213. []byte("\x1b[8;10~"),
  214. []Event{KeyPressEvent{Mod: ModShift | ModMeta, Code: KeyEnd}},
  215. },
  216. seqTest{
  217. []byte("\x1b[27;4u"),
  218. []Event{KeyPressEvent{Mod: ModShift | ModAlt, Code: KeyEscape}},
  219. },
  220. seqTest{
  221. []byte("\x1b[127;4u"),
  222. []Event{KeyPressEvent{Mod: ModShift | ModAlt, Code: KeyBackspace}},
  223. },
  224. seqTest{
  225. []byte("\x1b[57358;4u"),
  226. []Event{KeyPressEvent{Mod: ModShift | ModAlt, Code: KeyCapsLock}},
  227. },
  228. seqTest{
  229. []byte("\x1b[9;2u"),
  230. []Event{KeyPressEvent{Mod: ModShift, Code: KeyTab}},
  231. },
  232. seqTest{
  233. []byte("\x1b[195;u"),
  234. []Event{KeyPressEvent{Text: "Ã", Code: 'Ã'}},
  235. },
  236. seqTest{
  237. []byte("\x1b[20320;2u"),
  238. []Event{KeyPressEvent{Text: "你", Mod: ModShift, Code: '你'}},
  239. },
  240. seqTest{
  241. []byte("\x1b[195;:1u"),
  242. []Event{KeyPressEvent{Text: "Ã", Code: 'Ã'}},
  243. },
  244. seqTest{
  245. []byte("\x1b[195;2:3u"),
  246. []Event{KeyReleaseEvent{Code: 'Ã', Text: "Ã", Mod: ModShift}},
  247. },
  248. seqTest{
  249. []byte("\x1b[195;2:2u"),
  250. []Event{KeyPressEvent{Code: 'Ã', Text: "Ã", IsRepeat: true, Mod: ModShift}},
  251. },
  252. seqTest{
  253. []byte("\x1b[195;2:1u"),
  254. []Event{KeyPressEvent{Code: 'Ã', Text: "Ã", Mod: ModShift}},
  255. },
  256. seqTest{
  257. []byte("\x1b[195;2:3u"),
  258. []Event{KeyReleaseEvent{Code: 'Ã', Text: "Ã", Mod: ModShift}},
  259. },
  260. seqTest{
  261. []byte("\x1b[97;2;65u"),
  262. []Event{KeyPressEvent{Code: 'a', Text: "A", Mod: ModShift}},
  263. },
  264. seqTest{
  265. []byte("\x1b[97;;229u"),
  266. []Event{KeyPressEvent{Code: 'a', Text: "å"}},
  267. },
  268. // focus/blur
  269. seqTest{
  270. []byte{'\x1b', '[', 'I'},
  271. []Event{
  272. FocusEvent{},
  273. },
  274. },
  275. seqTest{
  276. []byte{'\x1b', '[', 'O'},
  277. []Event{
  278. BlurEvent{},
  279. },
  280. },
  281. // Mouse event.
  282. seqTest{
  283. []byte{'\x1b', '[', 'M', byte(32) + 0b0100_0000, byte(65), byte(49)},
  284. []Event{
  285. MouseWheelEvent{X: 32, Y: 16, Button: MouseWheelUp},
  286. },
  287. },
  288. // SGR Mouse event.
  289. seqTest{
  290. []byte("\x1b[<0;33;17M"),
  291. []Event{
  292. MouseClickEvent{X: 32, Y: 16, Button: MouseLeft},
  293. },
  294. },
  295. // Runes.
  296. seqTest{
  297. []byte{'a'},
  298. []Event{
  299. KeyPressEvent{Code: 'a', Text: "a"},
  300. },
  301. },
  302. seqTest{
  303. []byte{'\x1b', 'a'},
  304. []Event{
  305. KeyPressEvent{Code: 'a', Mod: ModAlt},
  306. },
  307. },
  308. seqTest{
  309. []byte{'a', 'a', 'a'},
  310. []Event{
  311. KeyPressEvent{Code: 'a', Text: "a"},
  312. KeyPressEvent{Code: 'a', Text: "a"},
  313. KeyPressEvent{Code: 'a', Text: "a"},
  314. },
  315. },
  316. // Multi-byte rune.
  317. seqTest{
  318. []byte("☃"),
  319. []Event{
  320. KeyPressEvent{Code: '☃', Text: "☃"},
  321. },
  322. },
  323. seqTest{
  324. []byte("\x1b☃"),
  325. []Event{
  326. KeyPressEvent{Code: '☃', Mod: ModAlt},
  327. },
  328. },
  329. // Standalone control characters.
  330. seqTest{
  331. []byte{'\x1b'},
  332. []Event{
  333. KeyPressEvent{Code: KeyEscape},
  334. },
  335. },
  336. seqTest{
  337. []byte{ansi.SOH},
  338. []Event{
  339. KeyPressEvent{Code: 'a', Mod: ModCtrl},
  340. },
  341. },
  342. seqTest{
  343. []byte{'\x1b', ansi.SOH},
  344. []Event{
  345. KeyPressEvent{Code: 'a', Mod: ModCtrl | ModAlt},
  346. },
  347. },
  348. seqTest{
  349. []byte{ansi.NUL},
  350. []Event{
  351. KeyPressEvent{Code: KeySpace, Mod: ModCtrl},
  352. },
  353. },
  354. seqTest{
  355. []byte{'\x1b', ansi.NUL},
  356. []Event{
  357. KeyPressEvent{Code: KeySpace, Mod: ModCtrl | ModAlt},
  358. },
  359. },
  360. // C1 control characters.
  361. seqTest{
  362. []byte{'\x80'},
  363. []Event{
  364. KeyPressEvent{Code: rune(0x80 - '@'), Mod: ModCtrl | ModAlt},
  365. },
  366. },
  367. )
  368. if runtime.GOOS != "windows" {
  369. // Sadly, utf8.DecodeRune([]byte(0xfe)) returns a valid rune on windows.
  370. // This is incorrect, but it makes our test fail if we try it out.
  371. td = append(td, seqTest{
  372. []byte{'\xfe'},
  373. []Event{
  374. UnknownEvent(rune(0xfe)),
  375. },
  376. })
  377. }
  378. var p Parser
  379. for _, tc := range td {
  380. t.Run(fmt.Sprintf("%q", string(tc.seq)), func(t *testing.T) {
  381. var events []Event
  382. buf := tc.seq
  383. for len(buf) > 0 {
  384. width, Event := p.parseSequence(buf)
  385. switch Event := Event.(type) {
  386. case MultiEvent:
  387. events = append(events, Event...)
  388. default:
  389. events = append(events, Event)
  390. }
  391. buf = buf[width:]
  392. }
  393. if !reflect.DeepEqual(tc.Events, events) {
  394. t.Errorf("\nexpected event for %q:\n %#v\ngot:\n %#v", tc.seq, tc.Events, events)
  395. }
  396. })
  397. }
  398. }
  399. func TestReadLongInput(t *testing.T) {
  400. expect := make([]Event, 1000)
  401. for i := range 1000 {
  402. expect[i] = KeyPressEvent{Code: 'a', Text: "a"}
  403. }
  404. input := strings.Repeat("a", 1000)
  405. drv, err := NewReader(strings.NewReader(input), "dumb", 0)
  406. if err != nil {
  407. t.Fatalf("unexpected input driver error: %v", err)
  408. }
  409. var Events []Event
  410. for {
  411. events, err := drv.ReadEvents()
  412. if err == io.EOF {
  413. break
  414. }
  415. if err != nil {
  416. t.Fatalf("unexpected input error: %v", err)
  417. }
  418. Events = append(Events, events...)
  419. }
  420. if !reflect.DeepEqual(expect, Events) {
  421. t.Errorf("unexpected messages, expected:\n %+v\ngot:\n %+v", expect, Events)
  422. }
  423. }
  424. func TestReadInput(t *testing.T) {
  425. type test struct {
  426. keyname string
  427. in []byte
  428. out []Event
  429. }
  430. testData := []test{
  431. {
  432. "a",
  433. []byte{'a'},
  434. []Event{
  435. KeyPressEvent{Code: 'a', Text: "a"},
  436. },
  437. },
  438. {
  439. "space",
  440. []byte{' '},
  441. []Event{
  442. KeyPressEvent{Code: KeySpace, Text: " "},
  443. },
  444. },
  445. {
  446. "a alt+a",
  447. []byte{'a', '\x1b', 'a'},
  448. []Event{
  449. KeyPressEvent{Code: 'a', Text: "a"},
  450. KeyPressEvent{Code: 'a', Mod: ModAlt},
  451. },
  452. },
  453. {
  454. "a alt+a a",
  455. []byte{'a', '\x1b', 'a', 'a'},
  456. []Event{
  457. KeyPressEvent{Code: 'a', Text: "a"},
  458. KeyPressEvent{Code: 'a', Mod: ModAlt},
  459. KeyPressEvent{Code: 'a', Text: "a"},
  460. },
  461. },
  462. {
  463. "ctrl+a",
  464. []byte{byte(ansi.SOH)},
  465. []Event{
  466. KeyPressEvent{Code: 'a', Mod: ModCtrl},
  467. },
  468. },
  469. {
  470. "ctrl+a ctrl+b",
  471. []byte{byte(ansi.SOH), byte(ansi.STX)},
  472. []Event{
  473. KeyPressEvent{Code: 'a', Mod: ModCtrl},
  474. KeyPressEvent{Code: 'b', Mod: ModCtrl},
  475. },
  476. },
  477. {
  478. "alt+a",
  479. []byte{byte(0x1b), 'a'},
  480. []Event{
  481. KeyPressEvent{Code: 'a', Mod: ModAlt},
  482. },
  483. },
  484. {
  485. "a b c d",
  486. []byte{'a', 'b', 'c', 'd'},
  487. []Event{
  488. KeyPressEvent{Code: 'a', Text: "a"},
  489. KeyPressEvent{Code: 'b', Text: "b"},
  490. KeyPressEvent{Code: 'c', Text: "c"},
  491. KeyPressEvent{Code: 'd', Text: "d"},
  492. },
  493. },
  494. {
  495. "up",
  496. []byte("\x1b[A"),
  497. []Event{
  498. KeyPressEvent{Code: KeyUp},
  499. },
  500. },
  501. {
  502. "wheel up",
  503. []byte{'\x1b', '[', 'M', byte(32) + 0b0100_0000, byte(65), byte(49)},
  504. []Event{
  505. MouseWheelEvent{X: 32, Y: 16, Button: MouseWheelUp},
  506. },
  507. },
  508. {
  509. "left motion release",
  510. []byte{
  511. '\x1b', '[', 'M', byte(32) + 0b0010_0000, byte(32 + 33), byte(16 + 33),
  512. '\x1b', '[', 'M', byte(32) + 0b0000_0011, byte(64 + 33), byte(32 + 33),
  513. },
  514. []Event{
  515. MouseMotionEvent{X: 32, Y: 16, Button: MouseLeft},
  516. MouseReleaseEvent{X: 64, Y: 32, Button: MouseNone},
  517. },
  518. },
  519. {
  520. "shift+tab",
  521. []byte{'\x1b', '[', 'Z'},
  522. []Event{
  523. KeyPressEvent{Code: KeyTab, Mod: ModShift},
  524. },
  525. },
  526. {
  527. "enter",
  528. []byte{'\r'},
  529. []Event{KeyPressEvent{Code: KeyEnter}},
  530. },
  531. {
  532. "alt+enter",
  533. []byte{'\x1b', '\r'},
  534. []Event{
  535. KeyPressEvent{Code: KeyEnter, Mod: ModAlt},
  536. },
  537. },
  538. {
  539. "insert",
  540. []byte{'\x1b', '[', '2', '~'},
  541. []Event{
  542. KeyPressEvent{Code: KeyInsert},
  543. },
  544. },
  545. {
  546. "ctrl+alt+a",
  547. []byte{'\x1b', byte(ansi.SOH)},
  548. []Event{
  549. KeyPressEvent{Code: 'a', Mod: ModCtrl | ModAlt},
  550. },
  551. },
  552. {
  553. "CSI?----X?",
  554. []byte{'\x1b', '[', '-', '-', '-', '-', 'X'},
  555. []Event{UnknownEvent([]byte{'\x1b', '[', '-', '-', '-', '-', 'X'})},
  556. },
  557. // Powershell sequences.
  558. {
  559. "up",
  560. []byte{'\x1b', 'O', 'A'},
  561. []Event{KeyPressEvent{Code: KeyUp}},
  562. },
  563. {
  564. "down",
  565. []byte{'\x1b', 'O', 'B'},
  566. []Event{KeyPressEvent{Code: KeyDown}},
  567. },
  568. {
  569. "right",
  570. []byte{'\x1b', 'O', 'C'},
  571. []Event{KeyPressEvent{Code: KeyRight}},
  572. },
  573. {
  574. "left",
  575. []byte{'\x1b', 'O', 'D'},
  576. []Event{KeyPressEvent{Code: KeyLeft}},
  577. },
  578. {
  579. "alt+enter",
  580. []byte{'\x1b', '\x0d'},
  581. []Event{KeyPressEvent{Code: KeyEnter, Mod: ModAlt}},
  582. },
  583. {
  584. "alt+backspace",
  585. []byte{'\x1b', '\x7f'},
  586. []Event{KeyPressEvent{Code: KeyBackspace, Mod: ModAlt}},
  587. },
  588. {
  589. "ctrl+space",
  590. []byte{'\x00'},
  591. []Event{KeyPressEvent{Code: KeySpace, Mod: ModCtrl}},
  592. },
  593. {
  594. "ctrl+alt+space",
  595. []byte{'\x1b', '\x00'},
  596. []Event{KeyPressEvent{Code: KeySpace, Mod: ModCtrl | ModAlt}},
  597. },
  598. {
  599. "esc",
  600. []byte{'\x1b'},
  601. []Event{KeyPressEvent{Code: KeyEscape}},
  602. },
  603. {
  604. "alt+esc",
  605. []byte{'\x1b', '\x1b'},
  606. []Event{KeyPressEvent{Code: KeyEscape, Mod: ModAlt}},
  607. },
  608. {
  609. "a b o",
  610. []byte{
  611. '\x1b', '[', '2', '0', '0', '~',
  612. 'a', ' ', 'b',
  613. '\x1b', '[', '2', '0', '1', '~',
  614. 'o',
  615. },
  616. []Event{
  617. PasteStartEvent{},
  618. PasteEvent("a b"),
  619. PasteEndEvent{},
  620. KeyPressEvent{Code: 'o', Text: "o"},
  621. },
  622. },
  623. {
  624. "a\x03\nb",
  625. []byte{
  626. '\x1b', '[', '2', '0', '0', '~',
  627. 'a', '\x03', '\n', 'b',
  628. '\x1b', '[', '2', '0', '1', '~',
  629. },
  630. []Event{
  631. PasteStartEvent{},
  632. PasteEvent("a\x03\nb"),
  633. PasteEndEvent{},
  634. },
  635. },
  636. {
  637. "?0xfe?",
  638. []byte{'\xfe'},
  639. []Event{
  640. UnknownEvent(rune(0xfe)),
  641. },
  642. },
  643. {
  644. "a ?0xfe? b",
  645. []byte{'a', '\xfe', ' ', 'b'},
  646. []Event{
  647. KeyPressEvent{Code: 'a', Text: "a"},
  648. UnknownEvent(rune(0xfe)),
  649. KeyPressEvent{Code: KeySpace, Text: " "},
  650. KeyPressEvent{Code: 'b', Text: "b"},
  651. },
  652. },
  653. }
  654. for i, td := range testData {
  655. t.Run(fmt.Sprintf("%d: %s", i, td.keyname), func(t *testing.T) {
  656. Events := testReadInputs(t, bytes.NewReader(td.in))
  657. var buf strings.Builder
  658. for i, Event := range Events {
  659. if i > 0 {
  660. buf.WriteByte(' ')
  661. }
  662. if s, ok := Event.(fmt.Stringer); ok {
  663. buf.WriteString(s.String())
  664. } else {
  665. fmt.Fprintf(&buf, "%#v:%T", Event, Event)
  666. }
  667. }
  668. if len(Events) != len(td.out) {
  669. 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)
  670. }
  671. if !reflect.DeepEqual(td.out, Events) {
  672. t.Fatalf("expected:\n%#v\ngot:\n%#v", td.out, Events)
  673. }
  674. })
  675. }
  676. }
  677. func testReadInputs(t *testing.T, input io.Reader) []Event {
  678. // We'll check that the input reader finishes at the end
  679. // without error.
  680. var wg sync.WaitGroup
  681. var inputErr error
  682. ctx, cancel := context.WithCancel(context.Background())
  683. defer func() {
  684. cancel()
  685. wg.Wait()
  686. if inputErr != nil && !errors.Is(inputErr, io.EOF) {
  687. t.Fatalf("unexpected input error: %v", inputErr)
  688. }
  689. }()
  690. dr, err := NewReader(input, "dumb", 0)
  691. if err != nil {
  692. t.Fatalf("unexpected input driver error: %v", err)
  693. }
  694. // The messages we're consuming.
  695. EventsC := make(chan Event)
  696. // Start the reader in the background.
  697. wg.Add(1)
  698. go func() {
  699. defer wg.Done()
  700. var events []Event
  701. events, inputErr = dr.ReadEvents()
  702. out:
  703. for _, ev := range events {
  704. select {
  705. case EventsC <- ev:
  706. case <-ctx.Done():
  707. break out
  708. }
  709. }
  710. EventsC <- nil
  711. }()
  712. var Events []Event
  713. loop:
  714. for {
  715. select {
  716. case Event := <-EventsC:
  717. if Event == nil {
  718. // end of input marker for the test.
  719. break loop
  720. }
  721. Events = append(Events, Event)
  722. case <-time.After(2 * time.Second):
  723. t.Errorf("timeout waiting for input event")
  724. break loop
  725. }
  726. }
  727. return Events
  728. }
  729. // randTest defines the test input and expected output for a sequence
  730. // of interleaved control sequences and control characters.
  731. type randTest struct {
  732. data []byte
  733. lengths []int
  734. names []string
  735. }
  736. // seed is the random seed to randomize the input. This helps check
  737. // that all the sequences get ultimately exercised.
  738. var seed = flag.Int64("seed", 0, "random seed (0 to autoselect)")
  739. // genRandomData generates a randomized test, with a random seed unless
  740. // the seed flag was set.
  741. func genRandomData(logfn func(int64), length int) randTest {
  742. // We'll use a random source. However, we give the user the option
  743. // to override it to a specific value for reproducibility.
  744. s := *seed
  745. if s == 0 {
  746. s = time.Now().UnixNano()
  747. }
  748. // Inform the user so they know what to reuse to get the same data.
  749. logfn(s)
  750. return genRandomDataWithSeed(s, length)
  751. }
  752. // genRandomDataWithSeed generates a randomized test with a fixed seed.
  753. func genRandomDataWithSeed(s int64, length int) randTest {
  754. src := rand.NewSource(s)
  755. r := rand.New(src)
  756. // allseqs contains all the sequences, in sorted order. We sort
  757. // to make the test deterministic (when the seed is also fixed).
  758. type seqpair struct {
  759. seq string
  760. name string
  761. }
  762. var allseqs []seqpair
  763. for seq, key := range sequences {
  764. allseqs = append(allseqs, seqpair{seq, key.String()})
  765. }
  766. sort.Slice(allseqs, func(i, j int) bool { return allseqs[i].seq < allseqs[j].seq })
  767. // res contains the computed test.
  768. var res randTest
  769. for len(res.data) < length {
  770. alt := r.Intn(2)
  771. prefix := ""
  772. esclen := 0
  773. if alt == 1 {
  774. prefix = "alt+"
  775. esclen = 1
  776. }
  777. kind := r.Intn(3)
  778. switch kind {
  779. case 0:
  780. // A control character.
  781. if alt == 1 {
  782. res.data = append(res.data, '\x1b')
  783. }
  784. res.data = append(res.data, 1)
  785. res.names = append(res.names, "ctrl+"+prefix+"a")
  786. res.lengths = append(res.lengths, 1+esclen)
  787. case 1, 2:
  788. // A sequence.
  789. seqi := r.Intn(len(allseqs))
  790. s := allseqs[seqi]
  791. if strings.Contains(s.name, "alt+") || strings.Contains(s.name, "meta+") {
  792. esclen = 0
  793. prefix = ""
  794. alt = 0
  795. }
  796. if alt == 1 {
  797. res.data = append(res.data, '\x1b')
  798. }
  799. res.data = append(res.data, s.seq...)
  800. if strings.HasPrefix(s.name, "ctrl+") {
  801. prefix = "ctrl+" + prefix
  802. }
  803. name := prefix + strings.TrimPrefix(s.name, "ctrl+")
  804. res.names = append(res.names, name)
  805. res.lengths = append(res.lengths, len(s.seq)+esclen)
  806. }
  807. }
  808. return res
  809. }
  810. func FuzzParseSequence(f *testing.F) {
  811. var p Parser
  812. for seq := range sequences {
  813. f.Add(seq)
  814. }
  815. f.Add("\x1b]52;?\x07") // OSC 52
  816. f.Add("\x1b]11;rgb:0000/0000/0000\x1b\\") // OSC 11
  817. f.Add("\x1bP>|charm terminal(0.1.2)\x1b\\") // DCS (XTVERSION)
  818. f.Add("\x1b_Gi=123\x1b\\") // APC
  819. f.Fuzz(func(t *testing.T, seq string) {
  820. n, _ := p.parseSequence([]byte(seq))
  821. if n == 0 && seq != "" {
  822. t.Errorf("expected a non-zero width for %q", seq)
  823. }
  824. })
  825. }
  826. // BenchmarkDetectSequenceMap benchmarks the map-based sequence
  827. // detector.
  828. func BenchmarkDetectSequenceMap(b *testing.B) {
  829. var p Parser
  830. td := genRandomDataWithSeed(123, 10000)
  831. for i := 0; i < b.N; i++ {
  832. for j, w := 0, 0; j < len(td.data); j += w {
  833. w, _ = p.parseSequence(td.data[j:])
  834. }
  835. }
  836. }