github.com/mephux/docker@v1.6.0-rc5/pkg/term/winconsole/term_emulator_test.go (about) 1 package winconsole 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "testing" 9 ) 10 11 const ( 12 WRITE_OPERATION = iota 13 COMMAND_OPERATION = iota 14 ) 15 16 var languages = []string{ 17 "Български", 18 "Català", 19 "Čeština", 20 "Ελληνικά", 21 "Español", 22 "Esperanto", 23 "Euskara", 24 "Français", 25 "Galego", 26 "한국어", 27 "ქართული", 28 "Latviešu", 29 "Lietuvių", 30 "Magyar", 31 "Nederlands", 32 "日本語", 33 "Norsk bokmål", 34 "Norsk nynorsk", 35 "Polski", 36 "Português", 37 "Română", 38 "Русский", 39 "Slovenčina", 40 "Slovenščina", 41 "Српски", 42 "српскохрватски", 43 "Suomi", 44 "Svenska", 45 "ไทย", 46 "Tiếng Việt", 47 "Türkçe", 48 "Українська", 49 "中文", 50 } 51 52 // Mock terminal handler object 53 type mockTerminal struct { 54 OutputCommandSequence []terminalOperation 55 } 56 57 // Used for recording the callback data 58 type terminalOperation struct { 59 Operation int 60 Data []byte 61 Str string 62 } 63 64 func (mt *mockTerminal) record(operation int, data []byte) { 65 op := terminalOperation{ 66 Operation: operation, 67 Data: make([]byte, len(data)), 68 } 69 copy(op.Data, data) 70 op.Str = string(op.Data) 71 mt.OutputCommandSequence = append(mt.OutputCommandSequence, op) 72 } 73 74 func (mt *mockTerminal) HandleOutputCommand(fd uintptr, command []byte) (n int, err error) { 75 mt.record(COMMAND_OPERATION, command) 76 return len(command), nil 77 } 78 79 func (mt *mockTerminal) HandleInputSequence(fd uintptr, command []byte) (n int, err error) { 80 return 0, nil 81 } 82 83 func (mt *mockTerminal) WriteChars(fd uintptr, w io.Writer, p []byte) (n int, err error) { 84 mt.record(WRITE_OPERATION, p) 85 return len(p), nil 86 } 87 88 func (mt *mockTerminal) ReadChars(fd uintptr, w io.Reader, p []byte) (n int, err error) { 89 return len(p), nil 90 } 91 92 func assertTrue(t *testing.T, cond bool, format string, args ...interface{}) { 93 if !cond { 94 t.Errorf(format, args...) 95 } 96 } 97 98 // reflect.DeepEqual does not provide detailed information as to what excatly failed. 99 func assertBytesEqual(t *testing.T, expected, actual []byte, format string, args ...interface{}) { 100 match := true 101 mismatchIndex := 0 102 if len(expected) == len(actual) { 103 for i := 0; i < len(expected); i++ { 104 if expected[i] != actual[i] { 105 match = false 106 mismatchIndex = i 107 break 108 } 109 } 110 } else { 111 match = false 112 t.Errorf("Lengths don't match Expected=%d Actual=%d", len(expected), len(actual)) 113 } 114 if !match { 115 t.Errorf("Mismatch at index %d ", mismatchIndex) 116 t.Errorf("\tActual String = %s", string(actual)) 117 t.Errorf("\tExpected String = %s", string(expected)) 118 t.Errorf("\tActual = %v", actual) 119 t.Errorf("\tExpected = %v", expected) 120 t.Errorf(format, args) 121 } 122 } 123 124 // Just to make sure :) 125 func TestAssertEqualBytes(t *testing.T) { 126 data := []byte{9, 9, 1, 1, 1, 9, 9} 127 assertBytesEqual(t, data, data, "Self") 128 assertBytesEqual(t, data[1:4], data[1:4], "Self") 129 assertBytesEqual(t, []byte{1, 1}, []byte{1, 1}, "Simple match") 130 assertBytesEqual(t, []byte{1, 2, 3}, []byte{1, 2, 3}, "content mismatch") 131 assertBytesEqual(t, []byte{1, 1, 1}, data[2:5], "slice match") 132 } 133 134 /* 135 func TestAssertEqualBytesNegative(t *testing.T) { 136 AssertBytesEqual(t, []byte{1, 1}, []byte{1}, "Length mismatch") 137 AssertBytesEqual(t, []byte{1, 1}, []byte{1}, "Length mismatch") 138 AssertBytesEqual(t, []byte{1, 2, 3}, []byte{1, 1, 1}, "content mismatch") 139 }*/ 140 141 // Checks that the calls recieved 142 func assertHandlerOutput(t *testing.T, mock *mockTerminal, plainText string, commands ...string) { 143 text := make([]byte, 0, 3*len(plainText)) 144 cmdIndex := 0 145 for opIndex := 0; opIndex < len(mock.OutputCommandSequence); opIndex++ { 146 op := mock.OutputCommandSequence[opIndex] 147 if op.Operation == WRITE_OPERATION { 148 t.Logf("\nThe data is[%d] == %s", opIndex, string(op.Data)) 149 text = append(text[:], op.Data...) 150 } else { 151 assertTrue(t, mock.OutputCommandSequence[opIndex].Operation == COMMAND_OPERATION, "Operation should be command : %s", fmt.Sprintf("%+v", mock)) 152 assertBytesEqual(t, StringToBytes(commands[cmdIndex]), mock.OutputCommandSequence[opIndex].Data, "Command data should match") 153 cmdIndex++ 154 } 155 } 156 assertBytesEqual(t, StringToBytes(plainText), text, "Command data should match %#v", mock) 157 } 158 159 func StringToBytes(str string) []byte { 160 bytes := make([]byte, len(str)) 161 copy(bytes[:], str) 162 return bytes 163 } 164 165 func TestParseAnsiCommand(t *testing.T) { 166 // Note: if the parameter does not exist then the empty value is returned 167 168 c := parseAnsiCommand(StringToBytes("\x1Bm")) 169 assertTrue(t, c.Command == "m", "Command should be m") 170 assertTrue(t, "" == c.getParam(0), "should return empty string") 171 assertTrue(t, "" == c.getParam(1), "should return empty string") 172 173 // Escape sequence - ESC[ 174 c = parseAnsiCommand(StringToBytes("\x1B[m")) 175 assertTrue(t, c.Command == "m", "Command should be m") 176 assertTrue(t, "" == c.getParam(0), "should return empty string") 177 assertTrue(t, "" == c.getParam(1), "should return empty string") 178 179 // Escape sequence With empty parameters- ESC[ 180 c = parseAnsiCommand(StringToBytes("\x1B[;m")) 181 assertTrue(t, c.Command == "m", "Command should be m") 182 assertTrue(t, "" == c.getParam(0), "should return empty string") 183 assertTrue(t, "" == c.getParam(1), "should return empty string") 184 assertTrue(t, "" == c.getParam(2), "should return empty string") 185 186 // Escape sequence With empty muliple parameters- ESC[ 187 c = parseAnsiCommand(StringToBytes("\x1B[;;m")) 188 assertTrue(t, c.Command == "m", "Command should be m") 189 assertTrue(t, "" == c.getParam(0), "") 190 assertTrue(t, "" == c.getParam(1), "") 191 assertTrue(t, "" == c.getParam(2), "") 192 193 // Escape sequence With muliple parameters- ESC[ 194 c = parseAnsiCommand(StringToBytes("\x1B[1;2;3m")) 195 assertTrue(t, c.Command == "m", "Command should be m") 196 assertTrue(t, "1" == c.getParam(0), "") 197 assertTrue(t, "2" == c.getParam(1), "") 198 assertTrue(t, "3" == c.getParam(2), "") 199 200 // Escape sequence With muliple parameters- some missing 201 c = parseAnsiCommand(StringToBytes("\x1B[1;;3;;;6m")) 202 assertTrue(t, c.Command == "m", "Command should be m") 203 assertTrue(t, "1" == c.getParam(0), "") 204 assertTrue(t, "" == c.getParam(1), "") 205 assertTrue(t, "3" == c.getParam(2), "") 206 assertTrue(t, "" == c.getParam(3), "") 207 assertTrue(t, "" == c.getParam(4), "") 208 assertTrue(t, "6" == c.getParam(5), "") 209 } 210 211 func newBufferedMockTerm() (stdOut io.Writer, stdErr io.Writer, stdIn io.ReadCloser, mock *mockTerminal) { 212 var input bytes.Buffer 213 var output bytes.Buffer 214 var err bytes.Buffer 215 216 mock = &mockTerminal{ 217 OutputCommandSequence: make([]terminalOperation, 0, 256), 218 } 219 220 stdOut = &terminalWriter{ 221 wrappedWriter: &output, 222 emulator: mock, 223 command: make([]byte, 0, 256), 224 } 225 stdErr = &terminalWriter{ 226 wrappedWriter: &err, 227 emulator: mock, 228 command: make([]byte, 0, 256), 229 } 230 stdIn = &terminalReader{ 231 wrappedReader: ioutil.NopCloser(&input), 232 emulator: mock, 233 command: make([]byte, 0, 256), 234 } 235 236 return 237 } 238 239 func TestOutputSimple(t *testing.T) { 240 stdOut, _, _, mock := newBufferedMockTerm() 241 242 stdOut.Write(StringToBytes("Hello world")) 243 stdOut.Write(StringToBytes("\x1BmHello again")) 244 245 assertTrue(t, mock.OutputCommandSequence[0].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock) 246 assertBytesEqual(t, StringToBytes("Hello world"), mock.OutputCommandSequence[0].Data, "Write data should match") 247 248 assertTrue(t, mock.OutputCommandSequence[1].Operation == COMMAND_OPERATION, "Operation should be command : %+v", mock) 249 assertBytesEqual(t, StringToBytes("\x1Bm"), mock.OutputCommandSequence[1].Data, "Command data should match") 250 251 assertTrue(t, mock.OutputCommandSequence[2].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock) 252 assertBytesEqual(t, StringToBytes("Hello again"), mock.OutputCommandSequence[2].Data, "Write data should match") 253 } 254 255 func TestOutputSplitCommand(t *testing.T) { 256 stdOut, _, _, mock := newBufferedMockTerm() 257 258 stdOut.Write(StringToBytes("Hello world\x1B[1;2;3")) 259 stdOut.Write(StringToBytes("mHello again")) 260 261 assertTrue(t, mock.OutputCommandSequence[0].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock) 262 assertBytesEqual(t, StringToBytes("Hello world"), mock.OutputCommandSequence[0].Data, "Write data should match") 263 264 assertTrue(t, mock.OutputCommandSequence[1].Operation == COMMAND_OPERATION, "Operation should be command : %+v", mock) 265 assertBytesEqual(t, StringToBytes("\x1B[1;2;3m"), mock.OutputCommandSequence[1].Data, "Command data should match") 266 267 assertTrue(t, mock.OutputCommandSequence[2].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock) 268 assertBytesEqual(t, StringToBytes("Hello again"), mock.OutputCommandSequence[2].Data, "Write data should match") 269 } 270 271 func TestOutputMultipleCommands(t *testing.T) { 272 stdOut, _, _, mock := newBufferedMockTerm() 273 274 stdOut.Write(StringToBytes("Hello world")) 275 stdOut.Write(StringToBytes("\x1B[1;2;3m")) 276 stdOut.Write(StringToBytes("\x1B[J")) 277 stdOut.Write(StringToBytes("Hello again")) 278 279 assertTrue(t, mock.OutputCommandSequence[0].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock) 280 assertBytesEqual(t, StringToBytes("Hello world"), mock.OutputCommandSequence[0].Data, "Write data should match") 281 282 assertTrue(t, mock.OutputCommandSequence[1].Operation == COMMAND_OPERATION, "Operation should be command : %+v", mock) 283 assertBytesEqual(t, StringToBytes("\x1B[1;2;3m"), mock.OutputCommandSequence[1].Data, "Command data should match") 284 285 assertTrue(t, mock.OutputCommandSequence[2].Operation == COMMAND_OPERATION, "Operation should be command : %+v", mock) 286 assertBytesEqual(t, StringToBytes("\x1B[J"), mock.OutputCommandSequence[2].Data, "Command data should match") 287 288 assertTrue(t, mock.OutputCommandSequence[3].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock) 289 assertBytesEqual(t, StringToBytes("Hello again"), mock.OutputCommandSequence[3].Data, "Write data should match") 290 } 291 292 // Splits the given data in two chunks , makes two writes and checks the split data is parsed correctly 293 // checks output write/command is passed to handler correctly 294 func helpsTestOutputSplitChunksAtIndex(t *testing.T, i int, data []byte) { 295 t.Logf("\ni=%d", i) 296 stdOut, _, _, mock := newBufferedMockTerm() 297 298 t.Logf("\nWriting chunk[0] == %s", string(data[:i])) 299 t.Logf("\nWriting chunk[1] == %s", string(data[i:])) 300 stdOut.Write(data[:i]) 301 stdOut.Write(data[i:]) 302 303 assertTrue(t, mock.OutputCommandSequence[0].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock) 304 assertBytesEqual(t, data[:i], mock.OutputCommandSequence[0].Data, "Write data should match") 305 306 assertTrue(t, mock.OutputCommandSequence[1].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock) 307 assertBytesEqual(t, data[i:], mock.OutputCommandSequence[1].Data, "Write data should match") 308 } 309 310 // Splits the given data in three chunks , makes three writes and checks the split data is parsed correctly 311 // checks output write/command is passed to handler correctly 312 func helpsTestOutputSplitThreeChunksAtIndex(t *testing.T, data []byte, i int, j int) { 313 stdOut, _, _, mock := newBufferedMockTerm() 314 315 t.Logf("\nWriting chunk[0] == %s", string(data[:i])) 316 t.Logf("\nWriting chunk[1] == %s", string(data[i:j])) 317 t.Logf("\nWriting chunk[2] == %s", string(data[j:])) 318 stdOut.Write(data[:i]) 319 stdOut.Write(data[i:j]) 320 stdOut.Write(data[j:]) 321 322 assertTrue(t, mock.OutputCommandSequence[0].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock) 323 assertBytesEqual(t, data[:i], mock.OutputCommandSequence[0].Data, "Write data should match") 324 325 assertTrue(t, mock.OutputCommandSequence[1].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock) 326 assertBytesEqual(t, data[i:j], mock.OutputCommandSequence[1].Data, "Write data should match") 327 328 assertTrue(t, mock.OutputCommandSequence[2].Operation == WRITE_OPERATION, "Operation should be Write : %#v", mock) 329 assertBytesEqual(t, data[j:], mock.OutputCommandSequence[2].Data, "Write data should match") 330 } 331 332 // Splits the output into two parts and tests all such possible pairs 333 func helpsTestOutputSplitChunks(t *testing.T, data []byte) { 334 for i := 1; i < len(data)-1; i++ { 335 helpsTestOutputSplitChunksAtIndex(t, i, data) 336 } 337 } 338 339 // Splits the output in three parts and tests all such possible triples 340 func helpsTestOutputSplitThreeChunks(t *testing.T, data []byte) { 341 for i := 1; i < len(data)-2; i++ { 342 for j := i + 1; j < len(data)-1; j++ { 343 helpsTestOutputSplitThreeChunksAtIndex(t, data, i, j) 344 } 345 } 346 } 347 348 func helpsTestOutputSplitCommandsAtIndex(t *testing.T, data []byte, i int, plainText string, commands ...string) { 349 t.Logf("\ni=%d", i) 350 stdOut, _, _, mock := newBufferedMockTerm() 351 352 stdOut.Write(data[:i]) 353 stdOut.Write(data[i:]) 354 assertHandlerOutput(t, mock, plainText, commands...) 355 } 356 357 func helpsTestOutputSplitCommands(t *testing.T, data []byte, plainText string, commands ...string) { 358 for i := 1; i < len(data)-1; i++ { 359 helpsTestOutputSplitCommandsAtIndex(t, data, i, plainText, commands...) 360 } 361 } 362 363 func injectCommandAt(data string, i int, command string) string { 364 retValue := make([]byte, len(data)+len(command)+4) 365 retValue = append(retValue, data[:i]...) 366 retValue = append(retValue, data[i:]...) 367 return string(retValue) 368 } 369 370 func TestOutputSplitChunks(t *testing.T) { 371 data := StringToBytes("qwertyuiopasdfghjklzxcvbnm") 372 helpsTestOutputSplitChunks(t, data) 373 helpsTestOutputSplitChunks(t, StringToBytes("BBBBB")) 374 helpsTestOutputSplitThreeChunks(t, StringToBytes("ABCDE")) 375 } 376 377 func TestOutputSplitChunksIncludingCommands(t *testing.T) { 378 helpsTestOutputSplitCommands(t, StringToBytes("Hello world.\x1B[mHello again."), "Hello world.Hello again.", "\x1B[m") 379 helpsTestOutputSplitCommandsAtIndex(t, StringToBytes("Hello world.\x1B[mHello again."), 2, "Hello world.Hello again.", "\x1B[m") 380 } 381 382 func TestSplitChunkUnicode(t *testing.T) { 383 for _, l := range languages { 384 data := StringToBytes(l) 385 helpsTestOutputSplitChunks(t, data) 386 helpsTestOutputSplitThreeChunks(t, data) 387 } 388 }