File: vt_test.go

package info (click to toggle)
golang-github-hinshun-vt10x 0.0~git20180809.d55458d%2Bds1-2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 160 kB
  • sloc: makefile: 3
file content (141 lines) | stat: -rw-r--r-- 2,959 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package vt10x

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"regexp"
	"strconv"
	"strings"
	"testing"

	"github.com/stretchr/testify/require"
	"golang.org/x/crypto/ssh/terminal"
)

func extractStr(t *State, x0, x1, row int) string {
	var s []rune
	for i := x0; i <= x1; i++ {
		c, _, _ := t.Cell(i, row)
		s = append(s, c)
	}
	return string(s)
}

func TestPlainChars(t *testing.T) {
	var st State
	term, err := Create(&st, nil)
	if err != nil {
		t.Fatal(err)
	}
	expected := "Hello world!"
	_, err = term.Write([]byte(expected))
	if err != nil && err != io.EOF {
		t.Fatal(err)
	}
	actual := extractStr(&st, 0, len(expected)-1, 0)
	if expected != actual {
		t.Fatal(actual)
	}
}

func TestNewline(t *testing.T) {
	var st State
	term, err := Create(&st, nil)
	if err != nil {
		t.Fatal(err)
	}
	expected := "Hello world!\n...and more."
	_, err = term.Write([]byte("\033[20h")) // set CRLF mode
	if err != nil && err != io.EOF {
		t.Fatal(err)
	}
	_, err = term.Write([]byte(expected))
	if err != nil && err != io.EOF {
		t.Fatal(err)
	}

	split := strings.Split(expected, "\n")
	actual := extractStr(&st, 0, len(split[0])-1, 0)
	actual += "\n"
	actual += extractStr(&st, 0, len(split[1])-1, 1)
	if expected != actual {
		t.Fatal(actual)
	}

	// A newline with a color set should not make the next line that color,
	// which used to happen if it caused a scroll event.
	st.moveTo(0, st.rows-1)
	_, err = term.Write([]byte("\033[1;37m\n$ \033[m"))
	if err != nil && err != io.EOF {
		t.Fatal(err)
	}
	_, fg, bg := st.Cell(st.Cursor())
	if fg != DefaultFG {
		t.Fatal(st.cur.x, st.cur.y, fg, bg)
	}
}

var (
	dsrPattern = regexp.MustCompile(`(\d+);(\d+)`)
)

type Coord struct {
	row int
	col int
}

func TestVTCPR(t *testing.T) {
	c, _, err := NewVT10XConsole()
	require.NoError(t, err)
	defer c.Close()

	go func() {
		c.ExpectEOF()
	}()

	coord, err := cpr(c.Tty())
	require.NoError(t, err)
	require.Equal(t, 1, coord.row)
	require.Equal(t, 1, coord.col)
}

// cpr is an example application that requests for the cursor position report.
func cpr(tty *os.File) (*Coord, error) {
	oldState, err := terminal.MakeRaw(int(tty.Fd()))
	if err != nil {
		return nil, err
	}

	defer terminal.Restore(int(tty.Fd()), oldState)

	// ANSI escape sequence for DSR - Device Status Report
	// https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences
	fmt.Fprint(tty, "\x1b[6n")

	// Reports the cursor position (CPR) to the application as (as though typed at
	// the keyboard) ESC[n;mR, where n is the row and m is the column.
	reader := bufio.NewReader(tty)
	text, err := reader.ReadSlice('R')
	if err != nil {
		return nil, err
	}

	matches := dsrPattern.FindStringSubmatch(string(text))
	if len(matches) != 3 {
		return nil, fmt.Errorf("incorrect number of matches: %d", len(matches))
	}

	col, err := strconv.Atoi(matches[2])
	if err != nil {
		return nil, err
	}

	row, err := strconv.Atoi(matches[1])
	if err != nil {
		return nil, err
	}

	return &Coord{row, col}, nil
}