aminal/buffer/buffer_test.go

708 lines
18 KiB
Go

package buffer
import (
"strings"
"testing"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/assert"
)
func TestTabbing(t *testing.T) {
b := NewBuffer(NewTerminalState(30, 3, CellAttributes{}, 1000))
b.Write([]rune("hello")...)
b.Tab()
b.Write([]rune("x")...)
b.Tab()
b.Write([]rune("goodbye")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hell")...)
b.Tab()
b.Write([]rune("xxx")...)
b.Tab()
b.Write([]rune("good")...)
b.CarriageReturn()
b.NewLine()
expected := `
hello x goodbye
hell xxx good
`
lines := b.GetVisibleLines()
strs := []string{}
for _, l := range lines {
strs = append(strs, l.String())
}
require.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(strings.Join(strs, "\n")))
}
func TestOffsets(t *testing.T) {
b := NewBuffer(NewTerminalState(10, 3, CellAttributes{}, 1000))
b.Write([]rune("hello")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello")...)
assert.Equal(t, uint16(10), b.ViewWidth())
assert.Equal(t, uint16(10), b.Width())
assert.Equal(t, uint16(3), b.ViewHeight())
assert.Equal(t, 5, b.Height())
}
func TestBufferCreation(t *testing.T) {
b := NewBuffer(NewTerminalState(10, 20, CellAttributes{}, 1000))
assert.Equal(t, uint16(10), b.Width())
assert.Equal(t, uint16(20), b.ViewHeight())
assert.Equal(t, uint16(0), b.CursorColumn())
assert.Equal(t, uint16(0), b.CursorLine())
assert.NotNil(t, b.lines)
}
func TestBufferWriteIncrementsCursorCorrectly(t *testing.T) {
b := NewBuffer(NewTerminalState(5, 4, CellAttributes{}, 1000))
/*01234
|-----
0|xxxxx
1|
2|
3|
|-----
*/
b.Write('x')
require.Equal(t, uint16(1), b.CursorColumn())
require.Equal(t, uint16(0), b.CursorLine())
b.Write('x')
require.Equal(t, uint16(2), b.CursorColumn())
require.Equal(t, uint16(0), b.CursorLine())
b.Write('x')
require.Equal(t, uint16(3), b.CursorColumn())
require.Equal(t, uint16(0), b.CursorLine())
b.Write('x')
require.Equal(t, uint16(4), b.CursorColumn())
require.Equal(t, uint16(0), b.CursorLine())
b.Write('x')
require.Equal(t, uint16(5), b.CursorColumn())
require.Equal(t, uint16(0), b.CursorLine())
b.Write('x')
require.Equal(t, uint16(1), b.CursorColumn())
require.Equal(t, uint16(1), b.CursorLine())
b.Write('x')
require.Equal(t, uint16(2), b.CursorColumn())
require.Equal(t, uint16(1), b.CursorLine())
lines := b.GetVisibleLines()
require.Equal(t, 2, len(lines))
assert.Equal(t, "xxxxx", lines[0].String())
assert.Equal(t, "xx", lines[1].String())
}
func TestWritingNewLineAsFirstRuneOnWrappedLine(t *testing.T) {
b := NewBuffer(NewTerminalState(3, 20, CellAttributes{}, 1000))
b.terminalState.LineFeedMode = false
b.Write('a', 'b', 'c')
assert.Equal(t, uint16(3), b.terminalState.cursorX)
assert.Equal(t, uint16(0), b.terminalState.cursorY)
b.NewLine()
assert.Equal(t, uint16(0), b.terminalState.cursorX)
assert.Equal(t, uint16(1), b.terminalState.cursorY)
b.Write('d', 'e', 'f')
assert.Equal(t, uint16(3), b.terminalState.cursorX)
assert.Equal(t, uint16(1), b.terminalState.cursorY)
b.NewLine()
assert.Equal(t, uint16(0), b.terminalState.cursorX)
assert.Equal(t, uint16(2), b.terminalState.cursorY)
require.Equal(t, 3, len(b.lines))
assert.Equal(t, "abc", b.lines[0].String())
assert.Equal(t, "def", b.lines[1].String())
}
func TestWritingNewLineAsSecondRuneOnWrappedLine(t *testing.T) {
b := NewBuffer(NewTerminalState(3, 20, CellAttributes{}, 1000))
b.terminalState.LineFeedMode = false
/*
|abc
|d
|ef
|
|
|z
*/
b.Write('a', 'b', 'c', 'd')
b.NewLine()
b.Write('e', 'f')
b.NewLine()
b.NewLine()
b.NewLine()
b.Write('z')
assert.Equal(t, "abc", b.lines[0].String())
assert.Equal(t, "d", b.lines[1].String())
assert.Equal(t, "ef", b.lines[2].String())
assert.Equal(t, "", b.lines[3].String())
assert.Equal(t, "", b.lines[4].String())
assert.Equal(t, "z", b.lines[5].String())
}
func TestSetPosition(t *testing.T) {
b := NewBuffer(NewTerminalState(120, 80, CellAttributes{}, 1000))
assert.Equal(t, 0, int(b.CursorColumn()))
assert.Equal(t, 0, int(b.CursorLine()))
b.SetPosition(60, 10)
assert.Equal(t, 60, int(b.CursorColumn()))
assert.Equal(t, 10, int(b.CursorLine()))
b.SetPosition(0, 0)
assert.Equal(t, 0, int(b.CursorColumn()))
assert.Equal(t, 0, int(b.CursorLine()))
b.SetPosition(120, 90)
assert.Equal(t, 119, int(b.CursorColumn()))
assert.Equal(t, 79, int(b.CursorLine()))
}
func TestMovePosition(t *testing.T) {
b := NewBuffer(NewTerminalState(120, 80, CellAttributes{}, 1000))
assert.Equal(t, 0, int(b.CursorColumn()))
assert.Equal(t, 0, int(b.CursorLine()))
b.MovePosition(-1, -1)
assert.Equal(t, 0, int(b.CursorColumn()))
assert.Equal(t, 0, int(b.CursorLine()))
b.MovePosition(30, 20)
assert.Equal(t, 30, int(b.CursorColumn()))
assert.Equal(t, 20, int(b.CursorLine()))
b.MovePosition(30, 20)
assert.Equal(t, 60, int(b.CursorColumn()))
assert.Equal(t, 40, int(b.CursorLine()))
b.MovePosition(-1, -1)
assert.Equal(t, 59, int(b.CursorColumn()))
assert.Equal(t, 39, int(b.CursorLine()))
b.MovePosition(100, 100)
assert.Equal(t, 119, int(b.CursorColumn()))
assert.Equal(t, 79, int(b.CursorLine()))
}
func TestVisibleLines(t *testing.T) {
b := NewBuffer(NewTerminalState(80, 10, CellAttributes{}, 1000))
b.Write([]rune("hello 1")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 2")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 3")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 4")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 5")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 6")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 7")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 8")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 9")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 10")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 11")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 12")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 13")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 14")...)
lines := b.GetVisibleLines()
require.Equal(t, 10, len(lines))
assert.Equal(t, "hello 5", lines[0].String())
assert.Equal(t, "hello 14", lines[9].String())
}
func TestClearWithoutFullView(t *testing.T) {
b := NewBuffer(NewTerminalState(80, 10, CellAttributes{}, 1000))
b.Write([]rune("hello 1")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 1")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 1")...)
b.Clear()
lines := b.GetVisibleLines()
for _, line := range lines {
assert.Equal(t, "", line.String())
}
}
func TestClearWithFullView(t *testing.T) {
b := NewBuffer(NewTerminalState(80, 5, CellAttributes{}, 1000))
b.Write([]rune("hello 1")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 1")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 1")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 1")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 1")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 1")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 1")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 1")...)
b.Clear()
lines := b.GetVisibleLines()
for _, line := range lines {
assert.Equal(t, "", line.String())
}
}
func TestCarriageReturn(t *testing.T) {
b := NewBuffer(NewTerminalState(80, 20, CellAttributes{}, 1000))
b.Write([]rune("hello!")...)
b.CarriageReturn()
b.Write([]rune("secret")...)
lines := b.GetVisibleLines()
assert.Equal(t, "secret", lines[0].String())
}
func TestCarriageReturnOnFullLine(t *testing.T) {
b := NewBuffer(NewTerminalState(20, 20, CellAttributes{}, 1000))
b.Write([]rune("abcdeabcdeabcdeabcde")...)
b.CarriageReturn()
b.Write([]rune("xxxxxxxxxxxxxxxxxxxx")...)
lines := b.GetVisibleLines()
assert.Equal(t, "xxxxxxxxxxxxxxxxxxxx", lines[0].String())
}
func TestCarriageReturnOnFullLastLine(t *testing.T) {
b := NewBuffer(NewTerminalState(20, 2, CellAttributes{}, 1000))
b.NewLine()
b.Write([]rune("abcdeabcdeabcdeabcde")...)
b.CarriageReturn()
b.Write([]rune("xxxxxxxxxxxxxxxxxxxx")...)
lines := b.GetVisibleLines()
assert.Equal(t, "", lines[0].String())
assert.Equal(t, "xxxxxxxxxxxxxxxxxxxx", lines[1].String())
}
func TestCarriageReturnOnWrappedLine(t *testing.T) {
b := NewBuffer(NewTerminalState(80, 6, CellAttributes{}, 1000))
b.Write([]rune("hello!")...)
b.CarriageReturn()
b.Write([]rune("secret")...)
lines := b.GetVisibleLines()
assert.Equal(t, "secret", lines[0].String())
}
func TestCarriageReturnOnLineThatDoesntExist(t *testing.T) {
b := NewBuffer(NewTerminalState(6, 10, CellAttributes{}, 1000))
b.terminalState.cursorY = 3
b.CarriageReturn()
assert.Equal(t, uint16(0), b.terminalState.cursorX)
assert.Equal(t, uint16(3), b.terminalState.cursorY)
}
func TestGetCell(t *testing.T) {
b := NewBuffer(NewTerminalState(80, 20, CellAttributes{}, 1000))
b.Write([]rune("Hello")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("there")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("something...")...)
cell := b.GetCell(8, 2)
require.NotNil(t, cell)
assert.Equal(t, 'g', cell.Rune())
}
func TestGetCellWithHistory(t *testing.T) {
b := NewBuffer(NewTerminalState(80, 2, CellAttributes{}, 1000))
b.Write([]rune("Hello")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("there")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("something...")...)
cell := b.GetCell(8, 1)
require.NotNil(t, cell)
assert.Equal(t, 'g', cell.Rune())
}
func TestGetCellWithBadCursor(t *testing.T) {
b := NewBuffer(NewTerminalState(80, 2, CellAttributes{}, 1000))
b.Write([]rune("Hello\r\nthere\r\nsomething...")...)
require.Nil(t, b.GetCell(8, 3))
require.Nil(t, b.GetCell(90, 0))
}
func TestCursorAttr(t *testing.T) {
b := NewBuffer(NewTerminalState(80, 2, CellAttributes{}, 1000))
assert.Equal(t, &b.terminalState.CursorAttr, b.CursorAttr())
}
func TestCursorPositionQuerying(t *testing.T) {
b := NewBuffer(NewTerminalState(80, 20, CellAttributes{}, 1000))
b.terminalState.cursorX = 17
b.terminalState.cursorY = 9
assert.Equal(t, b.terminalState.cursorX, b.CursorColumn())
assert.Equal(t, b.terminalState.cursorY, b.CursorLine())
}
func TestRawPositionQuerying(t *testing.T) {
b := NewBuffer(NewTerminalState(80, 5, CellAttributes{}, 1000))
b.Write([]rune("a")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("a")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("a")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("a")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("a")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("a")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("a")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("a")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("a")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("a")...)
b.terminalState.cursorX = 3
b.terminalState.cursorY = 4
assert.Equal(t, uint64(9), b.RawLine())
}
// CSI 2 K
func TestEraseLine(t *testing.T) {
b := NewBuffer(NewTerminalState(80, 5, CellAttributes{}, 1000))
b.Write([]rune("hello, this is a test")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("this line should be deleted")...)
b.EraseLine()
assert.Equal(t, "hello, this is a test", b.lines[0].String())
assert.Equal(t, "", b.lines[1].String())
}
// CSI 1 K
func TestEraseLineToCursor(t *testing.T) {
b := NewBuffer(NewTerminalState(80, 5, CellAttributes{}, 1000))
b.Write([]rune("hello, this is a test")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("deleted")...)
b.MovePosition(-3, 0)
b.EraseLineToCursor()
assert.Equal(t, "hello, this is a test", b.lines[0].String())
assert.Equal(t, "\x00\x00\x00\x00\x00ed", b.lines[1].String())
}
// CSI 0 K
func TestEraseLineAfterCursor(t *testing.T) {
b := NewBuffer(NewTerminalState(80, 5, CellAttributes{}, 1000))
b.Write([]rune("hello, this is a test")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("deleted")...)
b.MovePosition(-3, 0)
b.EraseLineFromCursor()
assert.Equal(t, "hello, this is a test", b.lines[0].String())
assert.Equal(t, "dele", b.lines[1].String())
}
func TestEraseDisplay(t *testing.T) {
b := NewBuffer(NewTerminalState(80, 5, CellAttributes{}, 1000))
b.Write([]rune("hello")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("asdasd")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("thing")...)
b.MovePosition(2, 1)
b.EraseDisplay()
lines := b.GetVisibleLines()
for _, line := range lines {
assert.Equal(t, "", line.String())
}
}
func TestEraseDisplayToCursor(t *testing.T) {
b := NewBuffer(NewTerminalState(80, 5, CellAttributes{}, 1000))
b.Write([]rune("hello")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("asdasd")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("thing")...)
b.MovePosition(-2, 0)
b.EraseDisplayToCursor()
lines := b.GetVisibleLines()
assert.Equal(t, "", lines[0].String())
assert.Equal(t, "", lines[1].String())
assert.Equal(t, "\x00\x00\x00\x00g", lines[2].String())
}
func TestEraseDisplayFromCursor(t *testing.T) {
b := NewBuffer(NewTerminalState(80, 5, CellAttributes{}, 1000))
b.Write([]rune("hello")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("asdasd")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("things")...)
b.MovePosition(-3, -1)
b.EraseDisplayFromCursor()
lines := b.GetVisibleLines()
assert.Equal(t, "hello", lines[0].String())
assert.Equal(t, "asd", lines[1].String())
assert.Equal(t, "", lines[2].String())
}
func TestBackspace(t *testing.T) {
b := NewBuffer(NewTerminalState(80, 5, CellAttributes{}, 1000))
b.Write([]rune("hello")...)
b.Backspace()
b.Backspace()
b.Write([]rune("p")...)
lines := b.GetVisibleLines()
assert.Equal(t, "helpo", lines[0].String())
}
func TestHorizontalResizeView(t *testing.T) {
b := NewBuffer(NewTerminalState(80, 10, CellAttributes{}, 1000))
// 60 characters
b.Write([]rune(`hellohellohellohellohellohellohellohellohellohellohellohello`)...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune(`goodbyegoodbye`)...)
require.Equal(t, uint16(14), b.terminalState.cursorX)
require.Equal(t, uint16(1), b.terminalState.cursorY)
b.ResizeView(40, 10)
expected := `hellohellohellohellohellohellohellohello
hellohellohellohello
goodbyegoodbye`
require.Equal(t, uint16(14), b.terminalState.cursorX)
require.Equal(t, uint16(2), b.terminalState.cursorY)
lines := b.GetVisibleLines()
strs := []string{}
for _, l := range lines {
strs = append(strs, l.String())
}
require.Equal(t, expected, strings.Join(strs, "\n"))
b.ResizeView(20, 10)
expected = `hellohellohellohello
hellohellohellohello
hellohellohellohello
goodbyegoodbye`
lines = b.GetVisibleLines()
strs = []string{}
for _, l := range lines {
strs = append(strs, l.String())
}
require.Equal(t, expected, strings.Join(strs, "\n"))
b.ResizeView(10, 10)
expected = `hellohello
hellohello
hellohello
hellohello
hellohello
hellohello
goodbyegoo
dbye`
lines = b.GetVisibleLines()
strs = []string{}
for _, l := range lines {
strs = append(strs, l.String())
}
require.Equal(t, expected, strings.Join(strs, "\n"))
b.ResizeView(80, 20)
expected = `hellohellohellohellohellohellohellohellohellohellohellohello
goodbyegoodbye`
lines = b.GetVisibleLines()
strs = []string{}
for _, l := range lines {
strs = append(strs, l.String())
}
require.Equal(t, expected, strings.Join(strs, "\n"))
require.Equal(t, uint16(1), b.terminalState.cursorY)
require.Equal(t, uint16(14), b.terminalState.cursorX)
}
/*
hellohellohellohellohellohellohellohellohellohellohellohello
goodbyegoo
dbye
*/
func TestBufferMaxLines(t *testing.T) {
b := NewBuffer(NewTerminalState(80, 2, CellAttributes{}, 2))
b.terminalState.LineFeedMode = false
b.Write([]rune("hello")...)
b.NewLine()
b.Write([]rune("funny")...)
b.NewLine()
b.Write([]rune("world")...)
assert.Equal(t, 2, len(b.lines))
assert.Equal(t, "funny", b.lines[0].String())
assert.Equal(t, "world", b.lines[1].String())
}
func makeBufferForTestingSelection() *Buffer {
b := NewBuffer(NewTerminalState(80, 10, CellAttributes{}, 10))
b.terminalState.LineFeedMode = false
b.Write([]rune("The quick brown")...)
b.NewLine()
b.Write([]rune("fox jumps over")...)
b.NewLine()
b.Write([]rune("the lazy dog")...)
return b
}
func TestSelectingChars(t *testing.T) {
b := makeBufferForTestingSelection()
b.StartSelection(2, 0, SelectionChar)
b.ExtendSelection(4, 1, true)
assert.Equal(t, "e quick brown\nfox j", b.GetSelectedText())
}
func TestSelectingWordsDown(t *testing.T) {
b := makeBufferForTestingSelection()
b.StartSelection(6, 1, SelectionWord)
b.ExtendSelection(5, 2, true)
assert.Equal(t, "jumps over\nthe lazy", b.GetSelectedText())
}
func TestSelectingWordsUp(t *testing.T) {
b := makeBufferForTestingSelection()
b.StartSelection(5, 2, SelectionWord)
b.ExtendSelection(6, 1, true)
assert.Equal(t, "jumps over\nthe lazy", b.GetSelectedText())
}
func TestSelectingLinesDown(t *testing.T) {
b := makeBufferForTestingSelection()
b.StartSelection(6, 1, SelectionLine)
b.ExtendSelection(4, 2, true)
assert.Equal(t, "fox jumps over\nthe lazy dog", b.GetSelectedText())
}
func TestSelectingLineUp(t *testing.T) {
b := makeBufferForTestingSelection()
b.StartSelection(8, 2, SelectionLine)
b.ExtendSelection(3, 1, true)
assert.Equal(t, "fox jumps over\nthe lazy dog", b.GetSelectedText())
}
func TestSelectingAfterText(t *testing.T) {
b := makeBufferForTestingSelection()
b.StartSelection(6, 3, SelectionChar)
b.ExtendSelection(6, 3, true)
start, end := b.getActualSelection()
assert.Equal(t, start.Col, 0)
assert.Equal(t, start.Line, 3)
assert.Equal(t, end.Col, 79)
assert.Equal(t, end.Line, 3)
}