terminal_test.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. // Copyright 2011 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package terminal
  5. import (
  6. "io"
  7. "testing"
  8. )
  9. type MockTerminal struct {
  10. toSend []byte
  11. bytesPerRead int
  12. received []byte
  13. }
  14. func (c *MockTerminal) Read(data []byte) (n int, err error) {
  15. n = len(data)
  16. if n == 0 {
  17. return
  18. }
  19. if n > len(c.toSend) {
  20. n = len(c.toSend)
  21. }
  22. if n == 0 {
  23. return 0, io.EOF
  24. }
  25. if c.bytesPerRead > 0 && n > c.bytesPerRead {
  26. n = c.bytesPerRead
  27. }
  28. copy(data, c.toSend[:n])
  29. c.toSend = c.toSend[n:]
  30. return
  31. }
  32. func (c *MockTerminal) Write(data []byte) (n int, err error) {
  33. c.received = append(c.received, data...)
  34. return len(data), nil
  35. }
  36. func TestClose(t *testing.T) {
  37. c := &MockTerminal{}
  38. ss := NewTerminal(c, "> ")
  39. line, err := ss.ReadLine()
  40. if line != "" {
  41. t.Errorf("Expected empty line but got: %s", line)
  42. }
  43. if err != io.EOF {
  44. t.Errorf("Error should have been EOF but got: %s", err)
  45. }
  46. }
  47. var keyPressTests = []struct {
  48. in string
  49. line string
  50. err error
  51. throwAwayLines int
  52. }{
  53. {
  54. err: io.EOF,
  55. },
  56. {
  57. in: "\r",
  58. line: "",
  59. },
  60. {
  61. in: "foo\r",
  62. line: "foo",
  63. },
  64. {
  65. in: "a\x1b[Cb\r", // right
  66. line: "ab",
  67. },
  68. {
  69. in: "a\x1b[Db\r", // left
  70. line: "ba",
  71. },
  72. {
  73. in: "a\177b\r", // backspace
  74. line: "b",
  75. },
  76. {
  77. in: "\x1b[A\r", // up
  78. },
  79. {
  80. in: "\x1b[B\r", // down
  81. },
  82. {
  83. in: "line\x1b[A\x1b[B\r", // up then down
  84. line: "line",
  85. },
  86. {
  87. in: "line1\rline2\x1b[A\r", // recall previous line.
  88. line: "line1",
  89. throwAwayLines: 1,
  90. },
  91. {
  92. // recall two previous lines and append.
  93. in: "line1\rline2\rline3\x1b[A\x1b[Axxx\r",
  94. line: "line1xxx",
  95. throwAwayLines: 2,
  96. },
  97. {
  98. // Ctrl-A to move to beginning of line followed by ^K to kill
  99. // line.
  100. in: "a b \001\013\r",
  101. line: "",
  102. },
  103. {
  104. // Ctrl-A to move to beginning of line, Ctrl-E to move to end,
  105. // finally ^K to kill nothing.
  106. in: "a b \001\005\013\r",
  107. line: "a b ",
  108. },
  109. {
  110. in: "\027\r",
  111. line: "",
  112. },
  113. {
  114. in: "a\027\r",
  115. line: "",
  116. },
  117. {
  118. in: "a \027\r",
  119. line: "",
  120. },
  121. {
  122. in: "a b\027\r",
  123. line: "a ",
  124. },
  125. {
  126. in: "a b \027\r",
  127. line: "a ",
  128. },
  129. {
  130. in: "one two thr\x1b[D\027\r",
  131. line: "one two r",
  132. },
  133. {
  134. in: "\013\r",
  135. line: "",
  136. },
  137. {
  138. in: "a\013\r",
  139. line: "a",
  140. },
  141. {
  142. in: "ab\x1b[D\013\r",
  143. line: "a",
  144. },
  145. {
  146. in: "Ξεσκεπάζω\r",
  147. line: "Ξεσκεπάζω",
  148. },
  149. {
  150. in: "£\r\x1b[A\177\r", // non-ASCII char, enter, up, backspace.
  151. line: "",
  152. throwAwayLines: 1,
  153. },
  154. {
  155. in: "£\r££\x1b[A\x1b[B\177\r", // non-ASCII char, enter, 2x non-ASCII, up, down, backspace, enter.
  156. line: "£",
  157. throwAwayLines: 1,
  158. },
  159. {
  160. // Ctrl-D at the end of the line should be ignored.
  161. in: "a\004\r",
  162. line: "a",
  163. },
  164. {
  165. // a, b, left, Ctrl-D should erase the b.
  166. in: "ab\x1b[D\004\r",
  167. line: "a",
  168. },
  169. {
  170. // a, b, c, d, left, left, ^U should erase to the beginning of
  171. // the line.
  172. in: "abcd\x1b[D\x1b[D\025\r",
  173. line: "cd",
  174. },
  175. {
  176. // Bracketed paste mode: control sequences should be returned
  177. // verbatim in paste mode.
  178. in: "abc\x1b[200~de\177f\x1b[201~\177\r",
  179. line: "abcde\177",
  180. },
  181. {
  182. // Enter in bracketed paste mode should still work.
  183. in: "abc\x1b[200~d\refg\x1b[201~h\r",
  184. line: "efgh",
  185. throwAwayLines: 1,
  186. },
  187. {
  188. // Lines consisting entirely of pasted data should be indicated as such.
  189. in: "\x1b[200~a\r",
  190. line: "a",
  191. err: ErrPasteIndicator,
  192. },
  193. }
  194. func TestKeyPresses(t *testing.T) {
  195. for i, test := range keyPressTests {
  196. for j := 1; j < len(test.in); j++ {
  197. c := &MockTerminal{
  198. toSend: []byte(test.in),
  199. bytesPerRead: j,
  200. }
  201. ss := NewTerminal(c, "> ")
  202. for k := 0; k < test.throwAwayLines; k++ {
  203. _, err := ss.ReadLine()
  204. if err != nil {
  205. t.Errorf("Throwaway line %d from test %d resulted in error: %s", k, i, err)
  206. }
  207. }
  208. line, err := ss.ReadLine()
  209. if line != test.line {
  210. t.Errorf("Line resulting from test %d (%d bytes per read) was '%s', expected '%s'", i, j, line, test.line)
  211. break
  212. }
  213. if err != test.err {
  214. t.Errorf("Error resulting from test %d (%d bytes per read) was '%v', expected '%v'", i, j, err, test.err)
  215. break
  216. }
  217. }
  218. }
  219. }
  220. func TestPasswordNotSaved(t *testing.T) {
  221. c := &MockTerminal{
  222. toSend: []byte("password\r\x1b[A\r"),
  223. bytesPerRead: 1,
  224. }
  225. ss := NewTerminal(c, "> ")
  226. pw, _ := ss.ReadPassword("> ")
  227. if pw != "password" {
  228. t.Fatalf("failed to read password, got %s", pw)
  229. }
  230. line, _ := ss.ReadLine()
  231. if len(line) > 0 {
  232. t.Fatalf("password was saved in history")
  233. }
  234. }
  235. var setSizeTests = []struct {
  236. width, height int
  237. }{
  238. {40, 13},
  239. {80, 24},
  240. {132, 43},
  241. }
  242. func TestTerminalSetSize(t *testing.T) {
  243. for _, setSize := range setSizeTests {
  244. c := &MockTerminal{
  245. toSend: []byte("password\r\x1b[A\r"),
  246. bytesPerRead: 1,
  247. }
  248. ss := NewTerminal(c, "> ")
  249. ss.SetSize(setSize.width, setSize.height)
  250. pw, _ := ss.ReadPassword("Password: ")
  251. if pw != "password" {
  252. t.Fatalf("failed to read password, got %s", pw)
  253. }
  254. if string(c.received) != "Password: \r\n" {
  255. t.Errorf("failed to set the temporary prompt expected %q, got %q", "Password: ", c.received)
  256. }
  257. }
  258. }