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.
5 // +build aix darwin dragonfly freebsd linux,!appengine netbsd openbsd windows plan9 solaris
17 type MockTerminal struct {
23 func (c *MockTerminal) Read(data []byte) (n int, err error) {
28 if n > len(c.toSend) {
34 if c.bytesPerRead > 0 && n > c.bytesPerRead {
37 copy(data, c.toSend[:n])
38 c.toSend = c.toSend[n:]
42 func (c *MockTerminal) Write(data []byte) (n int, err error) {
43 c.received = append(c.received, data...)
47 func TestClose(t *testing.T) {
49 ss := NewTerminal(c, "> ")
50 line, err := ss.ReadLine()
52 t.Errorf("Expected empty line but got: %s", line)
55 t.Errorf("Error should have been EOF but got: %s", err)
59 var keyPressTests = []struct {
77 in: "a\x1b[Cb\r", // right
81 in: "a\x1b[Db\r", // left
85 in: "a\177b\r", // backspace
92 in: "\x1b[B\r", // down
101 in: "line\x1b[A\x1b[B\r", // up then down
105 in: "line1\rline2\x1b[A\r", // recall previous line.
110 // recall two previous lines and append.
111 in: "line1\rline2\rline3\x1b[A\x1b[Axxx\r",
116 // Ctrl-A to move to beginning of line followed by ^K to kill
118 in: "a b \001\013\r",
122 // Ctrl-A to move to beginning of line, Ctrl-E to move to end,
123 // finally ^K to kill nothing.
124 in: "a b \001\005\013\r",
148 in: "one two thr\x1b[D\027\r",
160 in: "ab\x1b[D\013\r",
164 in: "Ξεσκεπάζω\r",
165 line: "Ξεσκεπάζω",
168 in: "£\r\x1b[A\177\r", // non-ASCII char, enter, up, backspace.
173 in: "£\r££\x1b[A\x1b[B\177\r", // non-ASCII char, enter, 2x non-ASCII, up, down, backspace, enter.
178 // Ctrl-D at the end of the line should be ignored.
183 // a, b, left, Ctrl-D should erase the b.
184 in: "ab\x1b[D\004\r",
188 // a, b, c, d, left, left, ^U should erase to the beginning of
190 in: "abcd\x1b[D\x1b[D\025\r",
194 // Bracketed paste mode: control sequences should be returned
195 // verbatim in paste mode.
196 in: "abc\x1b[200~de\177f\x1b[201~\177\r",
200 // Enter in bracketed paste mode should still work.
201 in: "abc\x1b[200~d\refg\x1b[201~h\r",
206 // Lines consisting entirely of pasted data should be indicated as such.
209 err: ErrPasteIndicator,
213 func TestKeyPresses(t *testing.T) {
214 for i, test := range keyPressTests {
215 for j := 1; j < len(test.in); j++ {
217 toSend: []byte(test.in),
220 ss := NewTerminal(c, "> ")
221 for k := 0; k < test.throwAwayLines; k++ {
222 _, err := ss.ReadLine()
224 t.Errorf("Throwaway line %d from test %d resulted in error: %s", k, i, err)
227 line, err := ss.ReadLine()
228 if line != test.line {
229 t.Errorf("Line resulting from test %d (%d bytes per read) was '%s', expected '%s'", i, j, line, test.line)
233 t.Errorf("Error resulting from test %d (%d bytes per read) was '%v', expected '%v'", i, j, err, test.err)
240 func TestPasswordNotSaved(t *testing.T) {
242 toSend: []byte("password\r\x1b[A\r"),
245 ss := NewTerminal(c, "> ")
246 pw, _ := ss.ReadPassword("> ")
247 if pw != "password" {
248 t.Fatalf("failed to read password, got %s", pw)
250 line, _ := ss.ReadLine()
252 t.Fatalf("password was saved in history")
256 var setSizeTests = []struct {
264 func TestTerminalSetSize(t *testing.T) {
265 for _, setSize := range setSizeTests {
267 toSend: []byte("password\r\x1b[A\r"),
270 ss := NewTerminal(c, "> ")
271 ss.SetSize(setSize.width, setSize.height)
272 pw, _ := ss.ReadPassword("Password: ")
273 if pw != "password" {
274 t.Fatalf("failed to read password, got %s", pw)
276 if string(c.received) != "Password: \r\n" {
277 t.Errorf("failed to set the temporary prompt expected %q, got %q", "Password: ", c.received)
282 func TestReadPasswordLineEnd(t *testing.T) {
283 var tests = []struct {
289 {"test\r\n", "test"},
290 {"testtesttesttes\n", "testtesttesttes"},
291 {"testtesttesttes\r\n", "testtesttesttes"},
292 {"testtesttesttesttest\n", "testtesttesttesttest"},
293 {"testtesttesttesttest\r\n", "testtesttesttesttest"},
295 for _, test := range tests {
296 buf := new(bytes.Buffer)
297 if _, err := buf.WriteString(test.input); err != nil {
301 have, err := readPasswordLine(buf)
303 t.Errorf("readPasswordLine(%q) failed: %v", test.input, err)
306 if string(have) != test.want {
307 t.Errorf("readPasswordLine(%q) returns %q, but %q is expected", test.input, string(have), test.want)
311 if _, err = buf.WriteString(test.input); err != nil {
314 have, err = readPasswordLine(buf)
316 t.Errorf("readPasswordLine(%q) failed: %v", test.input, err)
319 if string(have) != test.want {
320 t.Errorf("readPasswordLine(%q) returns %q, but %q is expected", test.input, string(have), test.want)
326 func TestMakeRawState(t *testing.T) {
327 fd := int(os.Stdout.Fd())
329 t.Skip("stdout is not a terminal; skipping test")
332 st, err := GetState(fd)
334 t.Fatalf("failed to get terminal state from GetState: %s", err)
337 if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") {
338 t.Skip("MakeRaw not allowed on iOS; skipping test")
341 defer Restore(fd, st)
342 raw, err := MakeRaw(fd)
344 t.Fatalf("failed to get terminal state from MakeRaw: %s", err)
348 t.Errorf("states do not match; was %v, expected %v", raw, st)
352 func TestOutputNewlines(t *testing.T) {
353 // \n should be changed to \r\n in terminal output.
354 buf := new(bytes.Buffer)
355 term := NewTerminal(buf, ">")
357 term.Write([]byte("1\n2\n"))
358 output := string(buf.Bytes())
359 const expected = "1\r\n2\r\n"
361 if output != expected {
362 t.Errorf("incorrect output: was %q, expected %q", output, expected)