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