github.com/carlanton/docker@v1.8.0-rc1/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 received
   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  }