reader_unix_test.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. //go:build !windows && !plan9
  2. package term
  3. import (
  4. "os"
  5. "strings"
  6. "testing"
  7. "src.elv.sh/pkg/testutil"
  8. "src.elv.sh/pkg/ui"
  9. )
  10. var readEventTests = []struct {
  11. input string
  12. want Event
  13. }{
  14. // Simple graphical key.
  15. {"x", K('x')},
  16. {"X", K('X')},
  17. {" ", K(' ')},
  18. // Ctrl key.
  19. {"\001", K('A', ui.Ctrl)},
  20. {"\033", K('[', ui.Ctrl)},
  21. // Special Ctrl keys that do not obey the usual 0x40 rule.
  22. {"\000", K('`', ui.Ctrl)},
  23. {"\x1e", K('6', ui.Ctrl)},
  24. {"\x1f", K('/', ui.Ctrl)},
  25. // Ambiguous Ctrl keys; the reader uses the non-Ctrl form as canonical.
  26. {"\n", K('\n')},
  27. {"\t", K('\t')},
  28. {"\x7f", K('\x7f')}, // backspace
  29. // Alt plus simple graphical key.
  30. {"\033a", K('a', ui.Alt)},
  31. {"\033[", K('[', ui.Alt)},
  32. // G3-style key.
  33. {"\033OA", K(ui.Up)},
  34. {"\033OH", K(ui.Home)},
  35. // G3-style key with leading Escape.
  36. {"\033\033OA", K(ui.Up, ui.Alt)},
  37. {"\033\033OH", K(ui.Home, ui.Alt)},
  38. // Alt-O. This is handled as a special case because it looks like a G3-style
  39. // key.
  40. {"\033O", K('O', ui.Alt)},
  41. // CSI-sequence key identified by the ending rune.
  42. {"\033[A", K(ui.Up)},
  43. {"\033[H", K(ui.Home)},
  44. // Modifiers.
  45. {"\033[1;0A", K(ui.Up)},
  46. {"\033[1;1A", K(ui.Up)},
  47. {"\033[1;2A", K(ui.Up, ui.Shift)},
  48. {"\033[1;3A", K(ui.Up, ui.Alt)},
  49. {"\033[1;4A", K(ui.Up, ui.Shift, ui.Alt)},
  50. {"\033[1;5A", K(ui.Up, ui.Ctrl)},
  51. {"\033[1;6A", K(ui.Up, ui.Shift, ui.Ctrl)},
  52. {"\033[1;7A", K(ui.Up, ui.Alt, ui.Ctrl)},
  53. {"\033[1;8A", K(ui.Up, ui.Shift, ui.Alt, ui.Ctrl)},
  54. // The modifiers below should be for Meta, but we conflate Alt and Meta.
  55. {"\033[1;9A", K(ui.Up, ui.Alt)},
  56. {"\033[1;10A", K(ui.Up, ui.Shift, ui.Alt)},
  57. {"\033[1;11A", K(ui.Up, ui.Alt)},
  58. {"\033[1;12A", K(ui.Up, ui.Shift, ui.Alt)},
  59. {"\033[1;13A", K(ui.Up, ui.Alt, ui.Ctrl)},
  60. {"\033[1;14A", K(ui.Up, ui.Shift, ui.Alt, ui.Ctrl)},
  61. {"\033[1;15A", K(ui.Up, ui.Alt, ui.Ctrl)},
  62. {"\033[1;16A", K(ui.Up, ui.Shift, ui.Alt, ui.Ctrl)},
  63. // CSI-sequence key with one argument, ending in '~'.
  64. {"\033[1~", K(ui.Home)},
  65. {"\033[11~", K(ui.F1)},
  66. // Modified.
  67. {"\033[1;2~", K(ui.Home, ui.Shift)},
  68. // Urxvt-flavor modifier, shifting the '~' to reflect the modifier
  69. {"\033[1$", K(ui.Home, ui.Shift)},
  70. {"\033[1^", K(ui.Home, ui.Ctrl)},
  71. {"\033[1@", K(ui.Home, ui.Shift, ui.Ctrl)},
  72. // With a leading Escape.
  73. {"\033\033[1~", K(ui.Home, ui.Alt)},
  74. // CSI-sequence key with three arguments and ending in '~'. The first
  75. // argument is always 27, the second identifies the modifier and the last
  76. // identifies the key.
  77. {"\033[27;4;63~", K(';', ui.Shift, ui.Alt)},
  78. // Cursor Position Report.
  79. {"\033[3;4R", CursorPosition{3, 4}},
  80. // Paste setting.
  81. {"\033[200~", PasteSetting(true)},
  82. {"\033[201~", PasteSetting(false)},
  83. // Mouse event.
  84. {"\033[M\x00\x23\x24", MouseEvent{Pos{4, 3}, true, 0, 0}},
  85. // Other buttons.
  86. {"\033[M\x01\x23\x24", MouseEvent{Pos{4, 3}, true, 1, 0}},
  87. // Button up.
  88. {"\033[M\x03\x23\x24", MouseEvent{Pos{4, 3}, false, -1, 0}},
  89. // Modified.
  90. {"\033[M\x04\x23\x24", MouseEvent{Pos{4, 3}, true, 0, ui.Shift}},
  91. {"\033[M\x08\x23\x24", MouseEvent{Pos{4, 3}, true, 0, ui.Alt}},
  92. {"\033[M\x10\x23\x24", MouseEvent{Pos{4, 3}, true, 0, ui.Ctrl}},
  93. {"\033[M\x14\x23\x24", MouseEvent{Pos{4, 3}, true, 0, ui.Shift | ui.Ctrl}},
  94. // SGR-style mouse event.
  95. {"\033[<0;3;4M", MouseEvent{Pos{4, 3}, true, 0, 0}},
  96. // Other buttons.
  97. {"\033[<1;3;4M", MouseEvent{Pos{4, 3}, true, 1, 0}},
  98. // Button up.
  99. {"\033[<0;3;4m", MouseEvent{Pos{4, 3}, false, 0, 0}},
  100. // Modified.
  101. {"\033[<4;3;4M", MouseEvent{Pos{4, 3}, true, 0, ui.Shift}},
  102. {"\033[<16;3;4M", MouseEvent{Pos{4, 3}, true, 0, ui.Ctrl}},
  103. }
  104. func TestReader_ReadEvent(t *testing.T) {
  105. r, w := setupReader(t)
  106. for _, test := range readEventTests {
  107. t.Run(test.input, func(t *testing.T) {
  108. w.WriteString(test.input)
  109. ev, err := r.ReadEvent()
  110. if ev != test.want {
  111. t.Errorf("got event %v, want %v", ev, test.want)
  112. }
  113. if err != nil {
  114. t.Errorf("got err %v, want %v", err, nil)
  115. }
  116. })
  117. }
  118. }
  119. var readEventBadSeqTests = []struct {
  120. input string
  121. wantErrMsg string
  122. }{
  123. // mouse event should have exactly 3 bytes after \033[M
  124. {"\033[M", "incomplete mouse event"},
  125. {"\033[M1", "incomplete mouse event"},
  126. {"\033[M12", "incomplete mouse event"},
  127. // CSI needs to be terminated by something that is not a parameter
  128. {"\033[1", "incomplete CSI"},
  129. {"\033[;", "incomplete CSI"},
  130. {"\033[1;", "incomplete CSI"},
  131. // CPR should have exactly 2 parameters
  132. {"\033[1R", "bad CPR"},
  133. {"\033[1;2;3R", "bad CPR"},
  134. // SGR mouse event should have exactly 3 parameters
  135. {"\033[<1;2m", "bad SGR mouse event"},
  136. // csiSeqByLast should have 0 or 2 parameters
  137. {"\033[1;2;3A", "bad CSI"},
  138. // csiSeqByLast with 2 parameters should have first parameter = 1
  139. {"\033[2;1A", "bad CSI"},
  140. // xterm-style modifier should be 0 to 16
  141. {"\033[1;17A", "bad CSI"},
  142. // unknown CSI terminator
  143. {"\033[x", "bad CSI"},
  144. // G3 allows a small list of allowed bytes after \033O
  145. {"\033Ox", "bad G3"},
  146. }
  147. func TestReader_ReadEvent_BadSeq(t *testing.T) {
  148. r, w := setupReader(t)
  149. for _, test := range readEventBadSeqTests {
  150. t.Run(test.input, func(t *testing.T) {
  151. w.WriteString(test.input)
  152. ev, err := r.ReadEvent()
  153. if err == nil {
  154. t.Fatalf("got nil err with event %v, want non-nil error", ev)
  155. }
  156. errMsg := err.Error()
  157. if !strings.HasPrefix(errMsg, test.wantErrMsg) {
  158. t.Errorf("got err with message %v, want message starting with %v",
  159. errMsg, test.wantErrMsg)
  160. }
  161. })
  162. }
  163. }
  164. func TestReader_ReadRawEvent(t *testing.T) {
  165. rd, w := setupReader(t)
  166. for _, test := range readEventTests {
  167. input := test.input
  168. t.Run(input, func(t *testing.T) {
  169. w.WriteString(input)
  170. for _, r := range input {
  171. ev, err := rd.ReadRawEvent()
  172. if err != nil {
  173. t.Errorf("got error %v, want nil", err)
  174. }
  175. if ev != K(r) {
  176. t.Errorf("got event %v, want %v", ev, K(r))
  177. }
  178. }
  179. })
  180. }
  181. }
  182. func setupReader(t *testing.T) (Reader, *os.File) {
  183. pr, pw := testutil.MustPipe()
  184. r := NewReader(pr)
  185. t.Cleanup(func() {
  186. r.Close()
  187. pr.Close()
  188. pw.Close()
  189. })
  190. return r, pw
  191. }