Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 

351 рядки
7.2 KiB

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