mirror of https://github.com/gogits/gogs.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
269 lines
5.2 KiB
269 lines
5.2 KiB
// Copyright 2011 The Go Authors. All rights reserved. |
|
// Use of this source code is governed by a BSD-style |
|
// license that can be found in the LICENSE file. |
|
|
|
package terminal |
|
|
|
import ( |
|
"io" |
|
"testing" |
|
) |
|
|
|
type MockTerminal struct { |
|
toSend []byte |
|
bytesPerRead int |
|
received []byte |
|
} |
|
|
|
func (c *MockTerminal) Read(data []byte) (n int, err error) { |
|
n = len(data) |
|
if n == 0 { |
|
return |
|
} |
|
if n > len(c.toSend) { |
|
n = len(c.toSend) |
|
} |
|
if n == 0 { |
|
return 0, io.EOF |
|
} |
|
if c.bytesPerRead > 0 && n > c.bytesPerRead { |
|
n = c.bytesPerRead |
|
} |
|
copy(data, c.toSend[:n]) |
|
c.toSend = c.toSend[n:] |
|
return |
|
} |
|
|
|
func (c *MockTerminal) Write(data []byte) (n int, err error) { |
|
c.received = append(c.received, data...) |
|
return len(data), nil |
|
} |
|
|
|
func TestClose(t *testing.T) { |
|
c := &MockTerminal{} |
|
ss := NewTerminal(c, "> ") |
|
line, err := ss.ReadLine() |
|
if line != "" { |
|
t.Errorf("Expected empty line but got: %s", line) |
|
} |
|
if err != io.EOF { |
|
t.Errorf("Error should have been EOF but got: %s", err) |
|
} |
|
} |
|
|
|
var keyPressTests = []struct { |
|
in string |
|
line string |
|
err error |
|
throwAwayLines int |
|
}{ |
|
{ |
|
err: io.EOF, |
|
}, |
|
{ |
|
in: "\r", |
|
line: "", |
|
}, |
|
{ |
|
in: "foo\r", |
|
line: "foo", |
|
}, |
|
{ |
|
in: "a\x1b[Cb\r", // right |
|
line: "ab", |
|
}, |
|
{ |
|
in: "a\x1b[Db\r", // left |
|
line: "ba", |
|
}, |
|
{ |
|
in: "a\177b\r", // backspace |
|
line: "b", |
|
}, |
|
{ |
|
in: "\x1b[A\r", // up |
|
}, |
|
{ |
|
in: "\x1b[B\r", // down |
|
}, |
|
{ |
|
in: "line\x1b[A\x1b[B\r", // up then down |
|
line: "line", |
|
}, |
|
{ |
|
in: "line1\rline2\x1b[A\r", // recall previous line. |
|
line: "line1", |
|
throwAwayLines: 1, |
|
}, |
|
{ |
|
// recall two previous lines and append. |
|
in: "line1\rline2\rline3\x1b[A\x1b[Axxx\r", |
|
line: "line1xxx", |
|
throwAwayLines: 2, |
|
}, |
|
{ |
|
// Ctrl-A to move to beginning of line followed by ^K to kill |
|
// line. |
|
in: "a b \001\013\r", |
|
line: "", |
|
}, |
|
{ |
|
// Ctrl-A to move to beginning of line, Ctrl-E to move to end, |
|
// finally ^K to kill nothing. |
|
in: "a b \001\005\013\r", |
|
line: "a b ", |
|
}, |
|
{ |
|
in: "\027\r", |
|
line: "", |
|
}, |
|
{ |
|
in: "a\027\r", |
|
line: "", |
|
}, |
|
{ |
|
in: "a \027\r", |
|
line: "", |
|
}, |
|
{ |
|
in: "a b\027\r", |
|
line: "a ", |
|
}, |
|
{ |
|
in: "a b \027\r", |
|
line: "a ", |
|
}, |
|
{ |
|
in: "one two thr\x1b[D\027\r", |
|
line: "one two r", |
|
}, |
|
{ |
|
in: "\013\r", |
|
line: "", |
|
}, |
|
{ |
|
in: "a\013\r", |
|
line: "a", |
|
}, |
|
{ |
|
in: "ab\x1b[D\013\r", |
|
line: "a", |
|
}, |
|
{ |
|
in: "Ξεσκεπάζω\r", |
|
line: "Ξεσκεπάζω", |
|
}, |
|
{ |
|
in: "£\r\x1b[A\177\r", // non-ASCII char, enter, up, backspace. |
|
line: "", |
|
throwAwayLines: 1, |
|
}, |
|
{ |
|
in: "£\r££\x1b[A\x1b[B\177\r", // non-ASCII char, enter, 2x non-ASCII, up, down, backspace, enter. |
|
line: "£", |
|
throwAwayLines: 1, |
|
}, |
|
{ |
|
// Ctrl-D at the end of the line should be ignored. |
|
in: "a\004\r", |
|
line: "a", |
|
}, |
|
{ |
|
// a, b, left, Ctrl-D should erase the b. |
|
in: "ab\x1b[D\004\r", |
|
line: "a", |
|
}, |
|
{ |
|
// a, b, c, d, left, left, ^U should erase to the beginning of |
|
// the line. |
|
in: "abcd\x1b[D\x1b[D\025\r", |
|
line: "cd", |
|
}, |
|
{ |
|
// Bracketed paste mode: control sequences should be returned |
|
// verbatim in paste mode. |
|
in: "abc\x1b[200~de\177f\x1b[201~\177\r", |
|
line: "abcde\177", |
|
}, |
|
{ |
|
// Enter in bracketed paste mode should still work. |
|
in: "abc\x1b[200~d\refg\x1b[201~h\r", |
|
line: "efgh", |
|
throwAwayLines: 1, |
|
}, |
|
{ |
|
// Lines consisting entirely of pasted data should be indicated as such. |
|
in: "\x1b[200~a\r", |
|
line: "a", |
|
err: ErrPasteIndicator, |
|
}, |
|
} |
|
|
|
func TestKeyPresses(t *testing.T) { |
|
for i, test := range keyPressTests { |
|
for j := 1; j < len(test.in); j++ { |
|
c := &MockTerminal{ |
|
toSend: []byte(test.in), |
|
bytesPerRead: j, |
|
} |
|
ss := NewTerminal(c, "> ") |
|
for k := 0; k < test.throwAwayLines; k++ { |
|
_, err := ss.ReadLine() |
|
if err != nil { |
|
t.Errorf("Throwaway line %d from test %d resulted in error: %s", k, i, err) |
|
} |
|
} |
|
line, err := ss.ReadLine() |
|
if line != test.line { |
|
t.Errorf("Line resulting from test %d (%d bytes per read) was '%s', expected '%s'", i, j, line, test.line) |
|
break |
|
} |
|
if err != test.err { |
|
t.Errorf("Error resulting from test %d (%d bytes per read) was '%v', expected '%v'", i, j, err, test.err) |
|
break |
|
} |
|
} |
|
} |
|
} |
|
|
|
func TestPasswordNotSaved(t *testing.T) { |
|
c := &MockTerminal{ |
|
toSend: []byte("password\r\x1b[A\r"), |
|
bytesPerRead: 1, |
|
} |
|
ss := NewTerminal(c, "> ") |
|
pw, _ := ss.ReadPassword("> ") |
|
if pw != "password" { |
|
t.Fatalf("failed to read password, got %s", pw) |
|
} |
|
line, _ := ss.ReadLine() |
|
if len(line) > 0 { |
|
t.Fatalf("password was saved in history") |
|
} |
|
} |
|
|
|
var setSizeTests = []struct { |
|
width, height int |
|
}{ |
|
{40, 13}, |
|
{80, 24}, |
|
{132, 43}, |
|
} |
|
|
|
func TestTerminalSetSize(t *testing.T) { |
|
for _, setSize := range setSizeTests { |
|
c := &MockTerminal{ |
|
toSend: []byte("password\r\x1b[A\r"), |
|
bytesPerRead: 1, |
|
} |
|
ss := NewTerminal(c, "> ") |
|
ss.SetSize(setSize.width, setSize.height) |
|
pw, _ := ss.ReadPassword("Password: ") |
|
if pw != "password" { |
|
t.Fatalf("failed to read password, got %s", pw) |
|
} |
|
if string(c.received) != "Password: \r\n" { |
|
t.Errorf("failed to set the temporary prompt expected %q, got %q", "Password: ", c.received) |
|
} |
|
} |
|
}
|
|
|