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