terminal_test.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  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. // +build darwin dragonfly freebsd linux,!appengine netbsd openbsd windows plan9 solaris
  5. package terminal
  6. import (
  7. "bytes"
  8. "io"
  9. "os"
  10. "testing"
  11. )
  12. type MockTerminal struct {
  13. toSend []byte
  14. bytesPerRead int
  15. received []byte
  16. }
  17. func (c *MockTerminal) Read(data []byte) (n int, err error) {
  18. n = len(data)
  19. if n == 0 {
  20. return
  21. }
  22. if n > len(c.toSend) {
  23. n = len(c.toSend)
  24. }
  25. if n == 0 {
  26. return 0, io.EOF
  27. }
  28. if c.bytesPerRead > 0 && n > c.bytesPerRead {
  29. n = c.bytesPerRead
  30. }
  31. copy(data, c.toSend[:n])
  32. c.toSend = c.toSend[n:]
  33. return
  34. }
  35. func (c *MockTerminal) Write(data []byte) (n int, err error) {
  36. c.received = append(c.received, data...)
  37. return len(data), nil
  38. }
  39. func TestClose(t *testing.T) {
  40. c := &MockTerminal{}
  41. ss := NewTerminal(c, "> ")
  42. line, err := ss.ReadLine()
  43. if line != "" {
  44. t.Errorf("Expected empty line but got: %s", line)
  45. }
  46. if err != io.EOF {
  47. t.Errorf("Error should have been EOF but got: %s", err)
  48. }
  49. }
  50. var keyPressTests = []struct {
  51. in string
  52. line string
  53. err error
  54. throwAwayLines int
  55. }{
  56. {
  57. err: io.EOF,
  58. },
  59. {
  60. in: "\r",
  61. line: "",
  62. },
  63. {
  64. in: "foo\r",
  65. line: "foo",
  66. },
  67. {
  68. in: "a\x1b[Cb\r", // right
  69. line: "ab",
  70. },
  71. {
  72. in: "a\x1b[Db\r", // left
  73. line: "ba",
  74. },
  75. {
  76. in: "a\177b\r", // backspace
  77. line: "b",
  78. },
  79. {
  80. in: "\x1b[A\r", // up
  81. },
  82. {
  83. in: "\x1b[B\r", // down
  84. },
  85. {
  86. in: "line\x1b[A\x1b[B\r", // up then down
  87. line: "line",
  88. },
  89. {
  90. in: "line1\rline2\x1b[A\r", // recall previous line.
  91. line: "line1",
  92. throwAwayLines: 1,
  93. },
  94. {
  95. // recall two previous lines and append.
  96. in: "line1\rline2\rline3\x1b[A\x1b[Axxx\r",
  97. line: "line1xxx",
  98. throwAwayLines: 2,
  99. },
  100. {
  101. // Ctrl-A to move to beginning of line followed by ^K to kill
  102. // line.
  103. in: "a b \001\013\r",
  104. line: "",
  105. },
  106. {
  107. // Ctrl-A to move to beginning of line, Ctrl-E to move to end,
  108. // finally ^K to kill nothing.
  109. in: "a b \001\005\013\r",
  110. line: "a b ",
  111. },
  112. {
  113. in: "\027\r",
  114. line: "",
  115. },
  116. {
  117. in: "a\027\r",
  118. line: "",
  119. },
  120. {
  121. in: "a \027\r",
  122. line: "",
  123. },
  124. {
  125. in: "a b\027\r",
  126. line: "a ",
  127. },
  128. {
  129. in: "a b \027\r",
  130. line: "a ",
  131. },
  132. {
  133. in: "one two thr\x1b[D\027\r",
  134. line: "one two r",
  135. },
  136. {
  137. in: "\013\r",
  138. line: "",
  139. },
  140. {
  141. in: "a\013\r",
  142. line: "a",
  143. },
  144. {
  145. in: "ab\x1b[D\013\r",
  146. line: "a",
  147. },
  148. {
  149. in: "Ξεσκεπάζω\r",
  150. line: "Ξεσκεπάζω",
  151. },
  152. {
  153. in: "£\r\x1b[A\177\r", // non-ASCII char, enter, up, backspace.
  154. line: "",
  155. throwAwayLines: 1,
  156. },
  157. {
  158. in: "£\r££\x1b[A\x1b[B\177\r", // non-ASCII char, enter, 2x non-ASCII, up, down, backspace, enter.
  159. line: "£",
  160. throwAwayLines: 1,
  161. },
  162. {
  163. // Ctrl-D at the end of the line should be ignored.
  164. in: "a\004\r",
  165. line: "a",
  166. },
  167. {
  168. // a, b, left, Ctrl-D should erase the b.
  169. in: "ab\x1b[D\004\r",
  170. line: "a",
  171. },
  172. {
  173. // a, b, c, d, left, left, ^U should erase to the beginning of
  174. // the line.
  175. in: "abcd\x1b[D\x1b[D\025\r",
  176. line: "cd",
  177. },
  178. {
  179. // Bracketed paste mode: control sequences should be returned
  180. // verbatim in paste mode.
  181. in: "abc\x1b[200~de\177f\x1b[201~\177\r",
  182. line: "abcde\177",
  183. },
  184. {
  185. // Enter in bracketed paste mode should still work.
  186. in: "abc\x1b[200~d\refg\x1b[201~h\r",
  187. line: "efgh",
  188. throwAwayLines: 1,
  189. },
  190. {
  191. // Lines consisting entirely of pasted data should be indicated as such.
  192. in: "\x1b[200~a\r",
  193. line: "a",
  194. err: ErrPasteIndicator,
  195. },
  196. }
  197. func TestKeyPresses(t *testing.T) {
  198. for i, test := range keyPressTests {
  199. for j := 1; j < len(test.in); j++ {
  200. c := &MockTerminal{
  201. toSend: []byte(test.in),
  202. bytesPerRead: j,
  203. }
  204. ss := NewTerminal(c, "> ")
  205. for k := 0; k < test.throwAwayLines; k++ {
  206. _, err := ss.ReadLine()
  207. if err != nil {
  208. t.Errorf("Throwaway line %d from test %d resulted in error: %s", k, i, err)
  209. }
  210. }
  211. line, err := ss.ReadLine()
  212. if line != test.line {
  213. t.Errorf("Line resulting from test %d (%d bytes per read) was '%s', expected '%s'", i, j, line, test.line)
  214. break
  215. }
  216. if err != test.err {
  217. t.Errorf("Error resulting from test %d (%d bytes per read) was '%v', expected '%v'", i, j, err, test.err)
  218. break
  219. }
  220. }
  221. }
  222. }
  223. func TestPasswordNotSaved(t *testing.T) {
  224. c := &MockTerminal{
  225. toSend: []byte("password\r\x1b[A\r"),
  226. bytesPerRead: 1,
  227. }
  228. ss := NewTerminal(c, "> ")
  229. pw, _ := ss.ReadPassword("> ")
  230. if pw != "password" {
  231. t.Fatalf("failed to read password, got %s", pw)
  232. }
  233. line, _ := ss.ReadLine()
  234. if len(line) > 0 {
  235. t.Fatalf("password was saved in history")
  236. }
  237. }
  238. var setSizeTests = []struct {
  239. width, height int
  240. }{
  241. {40, 13},
  242. {80, 24},
  243. {132, 43},
  244. }
  245. func TestTerminalSetSize(t *testing.T) {
  246. for _, setSize := range setSizeTests {
  247. c := &MockTerminal{
  248. toSend: []byte("password\r\x1b[A\r"),
  249. bytesPerRead: 1,
  250. }
  251. ss := NewTerminal(c, "> ")
  252. ss.SetSize(setSize.width, setSize.height)
  253. pw, _ := ss.ReadPassword("Password: ")
  254. if pw != "password" {
  255. t.Fatalf("failed to read password, got %s", pw)
  256. }
  257. if string(c.received) != "Password: \r\n" {
  258. t.Errorf("failed to set the temporary prompt expected %q, got %q", "Password: ", c.received)
  259. }
  260. }
  261. }
  262. func TestReadPasswordLineEnd(t *testing.T) {
  263. var tests = []struct {
  264. input string
  265. want string
  266. }{
  267. {"\n", ""},
  268. {"\r\n", ""},
  269. {"test\r\n", "test"},
  270. {"testtesttesttes\n", "testtesttesttes"},
  271. {"testtesttesttes\r\n", "testtesttesttes"},
  272. {"testtesttesttesttest\n", "testtesttesttesttest"},
  273. {"testtesttesttesttest\r\n", "testtesttesttesttest"},
  274. }
  275. for _, test := range tests {
  276. buf := new(bytes.Buffer)
  277. if _, err := buf.WriteString(test.input); err != nil {
  278. t.Fatal(err)
  279. }
  280. have, err := readPasswordLine(buf)
  281. if err != nil {
  282. t.Errorf("readPasswordLine(%q) failed: %v", test.input, err)
  283. continue
  284. }
  285. if string(have) != test.want {
  286. t.Errorf("readPasswordLine(%q) returns %q, but %q is expected", test.input, string(have), test.want)
  287. continue
  288. }
  289. if _, err = buf.WriteString(test.input); err != nil {
  290. t.Fatal(err)
  291. }
  292. have, err = readPasswordLine(buf)
  293. if err != nil {
  294. t.Errorf("readPasswordLine(%q) failed: %v", test.input, err)
  295. continue
  296. }
  297. if string(have) != test.want {
  298. t.Errorf("readPasswordLine(%q) returns %q, but %q is expected", test.input, string(have), test.want)
  299. continue
  300. }
  301. }
  302. }
  303. func TestMakeRawState(t *testing.T) {
  304. fd := int(os.Stdout.Fd())
  305. if !IsTerminal(fd) {
  306. t.Skip("stdout is not a terminal; skipping test")
  307. }
  308. st, err := GetState(fd)
  309. if err != nil {
  310. t.Fatalf("failed to get terminal state from GetState: %s", err)
  311. }
  312. defer Restore(fd, st)
  313. raw, err := MakeRaw(fd)
  314. if err != nil {
  315. t.Fatalf("failed to get terminal state from MakeRaw: %s", err)
  316. }
  317. if *st != *raw {
  318. t.Errorf("states do not match; was %v, expected %v", raw, st)
  319. }
  320. }
  321. func TestOutputNewlines(t *testing.T) {
  322. // \n should be changed to \r\n in terminal output.
  323. buf := new(bytes.Buffer)
  324. term := NewTerminal(buf, ">")
  325. term.Write([]byte("1\n2\n"))
  326. output := string(buf.Bytes())
  327. const expected = "1\r\n2\r\n"
  328. if output != expected {
  329. t.Errorf("incorrect output: was %q, expected %q", output, expected)
  330. }
  331. }