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  }