mirror of
				https://codeberg.org/forgejo/forgejo.git
				synced 2025-11-04 08:21:11 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			269 lines
		
	
	
	
		
			5.2 KiB
		
	
	
	
		
			Go
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			269 lines
		
	
	
	
		
			5.2 KiB
		
	
	
	
		
			Go
		
	
	
		
			Executable file
		
	
	
	
	
// Copyright 2011 The Go Authors. All rights reserved.
 | 
						|
// Use of this source code is governed by a BSD-style
 | 
						|
// license that can be found in the LICENSE file.
 | 
						|
 | 
						|
package terminal
 | 
						|
 | 
						|
import (
 | 
						|
	"io"
 | 
						|
	"testing"
 | 
						|
)
 | 
						|
 | 
						|
type MockTerminal struct {
 | 
						|
	toSend       []byte
 | 
						|
	bytesPerRead int
 | 
						|
	received     []byte
 | 
						|
}
 | 
						|
 | 
						|
func (c *MockTerminal) Read(data []byte) (n int, err error) {
 | 
						|
	n = len(data)
 | 
						|
	if n == 0 {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	if n > len(c.toSend) {
 | 
						|
		n = len(c.toSend)
 | 
						|
	}
 | 
						|
	if n == 0 {
 | 
						|
		return 0, io.EOF
 | 
						|
	}
 | 
						|
	if c.bytesPerRead > 0 && n > c.bytesPerRead {
 | 
						|
		n = c.bytesPerRead
 | 
						|
	}
 | 
						|
	copy(data, c.toSend[:n])
 | 
						|
	c.toSend = c.toSend[n:]
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
func (c *MockTerminal) Write(data []byte) (n int, err error) {
 | 
						|
	c.received = append(c.received, data...)
 | 
						|
	return len(data), nil
 | 
						|
}
 | 
						|
 | 
						|
func TestClose(t *testing.T) {
 | 
						|
	c := &MockTerminal{}
 | 
						|
	ss := NewTerminal(c, "> ")
 | 
						|
	line, err := ss.ReadLine()
 | 
						|
	if line != "" {
 | 
						|
		t.Errorf("Expected empty line but got: %s", line)
 | 
						|
	}
 | 
						|
	if err != io.EOF {
 | 
						|
		t.Errorf("Error should have been EOF but got: %s", err)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
var keyPressTests = []struct {
 | 
						|
	in             string
 | 
						|
	line           string
 | 
						|
	err            error
 | 
						|
	throwAwayLines int
 | 
						|
}{
 | 
						|
	{
 | 
						|
		err: io.EOF,
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:   "\r",
 | 
						|
		line: "",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:   "foo\r",
 | 
						|
		line: "foo",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:   "a\x1b[Cb\r", // right
 | 
						|
		line: "ab",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:   "a\x1b[Db\r", // left
 | 
						|
		line: "ba",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:   "a\177b\r", // backspace
 | 
						|
		line: "b",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "\x1b[A\r", // up
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in: "\x1b[B\r", // down
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:   "line\x1b[A\x1b[B\r", // up then down
 | 
						|
		line: "line",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:             "line1\rline2\x1b[A\r", // recall previous line.
 | 
						|
		line:           "line1",
 | 
						|
		throwAwayLines: 1,
 | 
						|
	},
 | 
						|
	{
 | 
						|
		// recall two previous lines and append.
 | 
						|
		in:             "line1\rline2\rline3\x1b[A\x1b[Axxx\r",
 | 
						|
		line:           "line1xxx",
 | 
						|
		throwAwayLines: 2,
 | 
						|
	},
 | 
						|
	{
 | 
						|
		// Ctrl-A to move to beginning of line followed by ^K to kill
 | 
						|
		// line.
 | 
						|
		in:   "a b \001\013\r",
 | 
						|
		line: "",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		// Ctrl-A to move to beginning of line, Ctrl-E to move to end,
 | 
						|
		// finally ^K to kill nothing.
 | 
						|
		in:   "a b \001\005\013\r",
 | 
						|
		line: "a b ",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:   "\027\r",
 | 
						|
		line: "",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:   "a\027\r",
 | 
						|
		line: "",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:   "a \027\r",
 | 
						|
		line: "",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:   "a b\027\r",
 | 
						|
		line: "a ",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:   "a b \027\r",
 | 
						|
		line: "a ",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:   "one two thr\x1b[D\027\r",
 | 
						|
		line: "one two r",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:   "\013\r",
 | 
						|
		line: "",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:   "a\013\r",
 | 
						|
		line: "a",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:   "ab\x1b[D\013\r",
 | 
						|
		line: "a",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:   "Ξεσκεπάζω\r",
 | 
						|
		line: "Ξεσκεπάζω",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:             "£\r\x1b[A\177\r", // non-ASCII char, enter, up, backspace.
 | 
						|
		line:           "",
 | 
						|
		throwAwayLines: 1,
 | 
						|
	},
 | 
						|
	{
 | 
						|
		in:             "£\r££\x1b[A\x1b[B\177\r", // non-ASCII char, enter, 2x non-ASCII, up, down, backspace, enter.
 | 
						|
		line:           "£",
 | 
						|
		throwAwayLines: 1,
 | 
						|
	},
 | 
						|
	{
 | 
						|
		// Ctrl-D at the end of the line should be ignored.
 | 
						|
		in:   "a\004\r",
 | 
						|
		line: "a",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		// a, b, left, Ctrl-D should erase the b.
 | 
						|
		in:   "ab\x1b[D\004\r",
 | 
						|
		line: "a",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		// a, b, c, d, left, left, ^U should erase to the beginning of
 | 
						|
		// the line.
 | 
						|
		in:   "abcd\x1b[D\x1b[D\025\r",
 | 
						|
		line: "cd",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		// Bracketed paste mode: control sequences should be returned
 | 
						|
		// verbatim in paste mode.
 | 
						|
		in:   "abc\x1b[200~de\177f\x1b[201~\177\r",
 | 
						|
		line: "abcde\177",
 | 
						|
	},
 | 
						|
	{
 | 
						|
		// Enter in bracketed paste mode should still work.
 | 
						|
		in:             "abc\x1b[200~d\refg\x1b[201~h\r",
 | 
						|
		line:           "efgh",
 | 
						|
		throwAwayLines: 1,
 | 
						|
	},
 | 
						|
	{
 | 
						|
		// Lines consisting entirely of pasted data should be indicated as such.
 | 
						|
		in:   "\x1b[200~a\r",
 | 
						|
		line: "a",
 | 
						|
		err:  ErrPasteIndicator,
 | 
						|
	},
 | 
						|
}
 | 
						|
 | 
						|
func TestKeyPresses(t *testing.T) {
 | 
						|
	for i, test := range keyPressTests {
 | 
						|
		for j := 1; j < len(test.in); j++ {
 | 
						|
			c := &MockTerminal{
 | 
						|
				toSend:       []byte(test.in),
 | 
						|
				bytesPerRead: j,
 | 
						|
			}
 | 
						|
			ss := NewTerminal(c, "> ")
 | 
						|
			for k := 0; k < test.throwAwayLines; k++ {
 | 
						|
				_, err := ss.ReadLine()
 | 
						|
				if err != nil {
 | 
						|
					t.Errorf("Throwaway line %d from test %d resulted in error: %s", k, i, err)
 | 
						|
				}
 | 
						|
			}
 | 
						|
			line, err := ss.ReadLine()
 | 
						|
			if line != test.line {
 | 
						|
				t.Errorf("Line resulting from test %d (%d bytes per read) was '%s', expected '%s'", i, j, line, test.line)
 | 
						|
				break
 | 
						|
			}
 | 
						|
			if err != test.err {
 | 
						|
				t.Errorf("Error resulting from test %d (%d bytes per read) was '%v', expected '%v'", i, j, err, test.err)
 | 
						|
				break
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestPasswordNotSaved(t *testing.T) {
 | 
						|
	c := &MockTerminal{
 | 
						|
		toSend:       []byte("password\r\x1b[A\r"),
 | 
						|
		bytesPerRead: 1,
 | 
						|
	}
 | 
						|
	ss := NewTerminal(c, "> ")
 | 
						|
	pw, _ := ss.ReadPassword("> ")
 | 
						|
	if pw != "password" {
 | 
						|
		t.Fatalf("failed to read password, got %s", pw)
 | 
						|
	}
 | 
						|
	line, _ := ss.ReadLine()
 | 
						|
	if len(line) > 0 {
 | 
						|
		t.Fatalf("password was saved in history")
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
var setSizeTests = []struct {
 | 
						|
	width, height int
 | 
						|
}{
 | 
						|
	{40, 13},
 | 
						|
	{80, 24},
 | 
						|
	{132, 43},
 | 
						|
}
 | 
						|
 | 
						|
func TestTerminalSetSize(t *testing.T) {
 | 
						|
	for _, setSize := range setSizeTests {
 | 
						|
		c := &MockTerminal{
 | 
						|
			toSend:       []byte("password\r\x1b[A\r"),
 | 
						|
			bytesPerRead: 1,
 | 
						|
		}
 | 
						|
		ss := NewTerminal(c, "> ")
 | 
						|
		ss.SetSize(setSize.width, setSize.height)
 | 
						|
		pw, _ := ss.ReadPassword("Password: ")
 | 
						|
		if pw != "password" {
 | 
						|
			t.Fatalf("failed to read password, got %s", pw)
 | 
						|
		}
 | 
						|
		if string(c.received) != "Password: \r\n" {
 | 
						|
			t.Errorf("failed to set the temporary prompt expected %q, got %q", "Password: ", c.received)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 |