1
0
mirror of https://github.com/gdamore/tcell.git synced 2025-04-24 13:48:51 +08:00

fixes #27 Add a test framework & test code

This commit is contained in:
Garrett D'Amore 2015-10-05 22:53:22 -07:00
parent b19d7067f2
commit 69be119a27
6 changed files with 897 additions and 16 deletions

View File

@ -7,7 +7,7 @@
[![Gitter](https://img.shields.io/badge/gitter-join-brightgreen.svg)](https://gitter.im/gdamore/tcell)
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/gdamore/tcell)
> _Tcell is a work in progress (Beta).
> _Tcell is a work in progress (Gamma).
> Please use with caution; interfaces may change in before final release.
> That said, our confidence in Tcell's stability is increasing. If you
> would like to use it in your own application, it is recommended that
@ -15,7 +15,7 @@
Package tcell provides a cell based view for text terminals, like xterm.
It was inspired by termbox, but differs from termbox in some important
ways. It also adds signficant functionality beyond termbox.
ways. It also adds substantial functionality beyond termbox.
## Pure Go Terminfo Database
@ -50,12 +50,18 @@ use with programs that use exec, or otherwise need to manipulate the
tty streams. This model is also much closer to idiomatic Go, leading
to fewer surprises.
## Richer Unicode support
## Richer Unicode & non-Unicode support
Tcell includes enhanced support for Unicode, include wide characters and
combining characters, provided your terminal can support them. Note that
Windows terminals generally don't support the full Unicode repertoire.
It will also convert to and from Unicode locales, so that the program
can work with UTF-8 internally, and get reasonable output in other locales.
We try hard to convert to native characters on both input and output, and
on output we even make use of the alternate character set to facilitate
drawing certain characters.
## More Function Keys
It also has richer support for a larger number of
@ -63,7 +69,7 @@ special keys that some terminals can send.
## Better color handling
Tcell will respect your terminal's color space as specified within your terminfo
Tcell will respect your terminal's color space as specified within your terminfo
entries, so that for example attempts to emit color sequences on VT100 terminals
won't result in unintended consequences.
@ -71,6 +77,9 @@ In Windows mode, we support 16 colors, underline, bold, dim, and reverse,
instead of just termbox's 8 colors with reverse. (Note that there is some
conflation with bold/dim and colors.)
Tcell maps 16 colors down to 8, for Terminals that need it. (The upper
8 colors are just brighter versions of the lower 8.)
## Better mouse support
It supports enhanced mouse tracking mode, so your application can receive
@ -80,7 +89,8 @@ regular mouse motion events, and wheel events, if your terminal supports it.
I started this project originally by submitting patches to the author of
go-termbox, but due to some fundamental differences of opinion, I thought
it might be simpler just to start from scratch.
it might be simpler just to start from scratch. At this point, Tcell has
far exceeded the capabilities of termbox.
## Termbox compatibility
@ -89,13 +99,14 @@ directory. To use it, try importing "github.com/gdamore/tcell/termbox"
instead. Most termbox-go programs will probably work without further
modification.
## Working with Unicode
## Working With Unicode
This version of the tcells expects that your terminal can support Unicode
on output. That is, if you submit Unicode sequences to it, it will attempt
send Unicode to the terminal. This works for modern xterm and other emulators,
but legacy systems may have poor results. I'm interested to hear reports from
folks who need support for other character sets.
Internally Tcell uses UTF-8, just like Go. However, it understands how to
convert to and from other character sets, using the capabilities of
the golang.org/x/text/encoding packages. Your application must supply
them, as the full set of the most common ones bloats the program by about
2MB. If you're lazy, and want them all anyway, see the tcell/encoding
sub package.
## Wide & Combining Characters
@ -108,8 +119,7 @@ results are undefined. (Normally the wide character will not be displayed.)
Experience has shown that the vanilla Windows 8 console application does not
support any of these characters properly, but at least some options like
ConEmu do support Wide characters at least. Combining characters are
disabled for Windows in the release.
ConEmu do support Wide characters at least.
## Colors
@ -125,9 +135,6 @@ know; it wouldn't be hard to add that if there is need.
Reasonable attempts have been made to minimize sending data to terminals,
avoiding repeated sequences or drawing the same cell on refresh updates.
Windows still needs some work here, as it can make numerous system calls
a bit less efficiently than it could.
## Terminfo
(Not relevent for Windows users.)
@ -177,6 +184,13 @@ consoles, all support this quite nicely. On other platforms you might
find that only mouse click and release events are reported, with
no intervening motion events. It really depends on your terminal.
## Testablity
There is a SimulationScreen, that can be used to simulate a real screen
for automated testing. The supplied tests do this. The simulation contains
event delivery, screen resizing support, and capabilities to inject events
and examine "physical" screen contents.
## Platforms
On POSIX systems, a POSIX termios implementation with /dev/tty is required.
@ -197,3 +211,7 @@ fully compatible consule implementation. We expect that Windows 10
ships with a less crippled implementation than prior releases -- we haven't
tested that, lacking Windows 10 ourselves.
The nacl and plan9 platforms won't work, but compilation stubs are supplied
for folks that want to include parts of this in software targetting those
platforms. The test screens will work, but as we don't know how to allocate
a real screen object on those platforms, NewScreen() will fail.

77
event_test.go Normal file
View File

@ -0,0 +1,77 @@
// Copyright 2015 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tcell
import (
"testing"
"time"
. "github.com/smartystreets/goconvey/convey"
)
func eventLoop(s SimulationScreen, evch chan Event) {
for {
ev := s.PollEvent()
if ev == nil {
close(evch)
return
}
select {
case evch <- ev:
case <-time.After(time.Second):
}
}
}
func TestMouseEvents(t *testing.T) {
Convey("Mouse events", t, WithScreen(t, "", func(s SimulationScreen) {
Convey("Size should be valid", func() {
x, y := s.Size()
So(x, ShouldEqual, 80)
So(y, ShouldEqual, 25)
})
s.EnableMouse()
s.InjectMouse(4, 9, Button1, ModCtrl)
evch := make(chan Event)
em := &EventMouse{}
done := false
go eventLoop(s, evch)
for !done {
select {
case ev := <-evch:
if evm, ok := ev.(*EventMouse); ok {
em = evm
done = true
}
continue
case <-time.After(time.Second):
done = true
}
}
Convey("We got our mouse event", func() {
So(em, ShouldNotBeNil)
x, y := em.Position()
So(x, ShouldEqual, 4)
So(y, ShouldEqual, 9)
So(em.Buttons(), ShouldEqual, Button1)
So(em.Modifiers(), ShouldEqual, ModCtrl)
})
}))
}

151
sim_test.go Normal file
View File

@ -0,0 +1,151 @@
// Copyright 2015 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tcell
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func WithScreen(t *testing.T, charset string, fn func(s SimulationScreen)) func() {
return func() {
s := NewSimulationScreen(charset)
So(s, ShouldNotBeNil)
e := s.Init()
So(e, ShouldBeNil)
Reset(func() {
s.Fini()
})
fn(s)
}
}
func TestInitScreen(t *testing.T) {
Convey("Init a screen", t, WithScreen(t, "", func(s SimulationScreen) {
Convey("Size should be valid", func() {
x, y := s.Size()
So(x, ShouldEqual, 80)
So(y, ShouldEqual, 25)
})
Convey("Default charset is UTF-8", func() {
So(s.CharacterSet(), ShouldEqual, "UTF-8")
})
Convey("Backing size is correct", func() {
b, x, y := s.GetContents()
So(b, ShouldNotBeNil)
So(x, ShouldEqual, 80)
So(y, ShouldEqual, 25)
So(len(b), ShouldEqual, x*y)
})
}))
}
func TestClearScreen(t *testing.T) {
Convey("Clear screen", t, WithScreen(t, "", func(s SimulationScreen) {
s.Clear()
b, x, y := s.GetContents()
So(b, ShouldNotBeNil)
So(x, ShouldEqual, 80)
So(y, ShouldEqual, 25)
So(len(b), ShouldEqual, x*y)
s.Sync()
nmatch := 0
for i := 0; i < x*y; i++ {
if len(b[i].Runes) == 0 {
nmatch++
}
}
So(nmatch, ShouldEqual, x*y)
nmatch = 0
for i := 0; i < x*y; i++ {
if len(b[i].Bytes) == 1 {
nmatch++
}
}
So(nmatch, ShouldEqual, x*y)
nmatch = 0
for i := 0; i < x*y; i++ {
if b[i].Style == StyleDefault {
nmatch++
}
}
So(nmatch, ShouldEqual, x*y)
So(b[0].Bytes[0], ShouldEqual, ' ')
}))
}
func TestSetCell(t *testing.T) {
st := StyleDefault.Background(ColorRed).Blink(true)
Convey("Set contents", t, WithScreen(t, "", func(s SimulationScreen) {
s.SetCell(2, 5, st, '@')
b, x, y := s.GetContents()
So(len(b), ShouldEqual, x*y)
So(x, ShouldEqual, 80)
So(y, ShouldEqual, 25)
s.Show()
sc := &b[5*80+2]
So(len(sc.Runes), ShouldEqual, 1)
So(len(sc.Bytes), ShouldEqual, 1)
So(sc.Bytes[0], ShouldEqual, '@')
So(sc.Runes[0], ShouldEqual, '@')
So(sc.Style, ShouldEqual, st)
}))
}
func TestResize(t *testing.T) {
st := StyleDefault.Background(ColorYellow).Underline(true)
Convey("Resize", t, WithScreen(t, "", func(s SimulationScreen) {
s.SetCell(2, 5, st, '&')
b, x, y := s.GetContents()
So(len(b), ShouldEqual, x*y)
So(x, ShouldEqual, 80)
So(y, ShouldEqual, 25)
s.Show()
sc := &b[5*80+2]
So(len(sc.Runes), ShouldEqual, 1)
So(len(sc.Bytes), ShouldEqual, 1)
So(sc.Bytes[0], ShouldEqual, '&')
So(sc.Runes[0], ShouldEqual, '&')
So(sc.Style, ShouldEqual, st)
Convey("Do resize", func() {
s.Resize(30, 10)
s.Show()
b2, x2, y2 := s.GetContents()
So(b2, ShouldNotEqual, b)
So(x2, ShouldEqual, 30)
So(y2, ShouldEqual, 10)
sc2 := &b[5*80+2]
So(len(sc2.Runes), ShouldEqual, 1)
So(len(sc2.Bytes), ShouldEqual, 1)
So(sc2.Bytes[0], ShouldEqual, '&')
So(sc2.Runes[0], ShouldEqual, '&')
So(sc2.Style, ShouldEqual, st)
})
}))
}

502
simulation.go Normal file
View File

@ -0,0 +1,502 @@
// Copyright 2015 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tcell
import (
"errors"
"sync"
"unicode/utf8"
"golang.org/x/text/transform"
)
// NewSimulationScreen returns a SimulationScreen. Note that
// SimulationScreen is also a Screen.
func NewSimulationScreen(charset string) SimulationScreen {
if charset == "" {
charset = "UTF-8"
}
s := &simscreen{charset: charset}
return s
}
// SimulationScreen represents a screen simulation. This is intended to
// be a superset of normal Screens, but also adds some important interfaces
// for testing.
type SimulationScreen interface {
// InjectKeyBytes injects a stream of bytes corresponding to
// the native encoding (see charset). It turns true if the entire
// set of bytes were processed and delivered as KeyEvents, false
// if any bytes were not fully understood. Any bytes that are not
// fully converted are discarded.
InjectKeyBytes(buf []byte) bool
// InjectKey injects a key event. The rune is a UTF-8 rune, post
// any translation.
InjectKey(key Key, r rune, mod ModMask)
// InjectMouse injects a mouse event.
InjectMouse(x, y int, buttons ButtonMask, mod ModMask)
// Resize resizes the underlying physical screen. It also causes
// a resize event to be injected during the next Show() or Sync().
// A new physical contents array will be allocated (with data from
// the old copied), so any prior value obtained with GetContents
// won't be used anymore
Resize(width, height int)
// GetContents returns screen contents as an array of
// cells, along with the physical width & height. Note that the
// physical contents will be used until the next time Resize()
// is called.
GetContents() (cells []SimCell, width int, height int)
// GetCursor returns the cursor details.
GetCursor() (x int, y int, visible bool)
Screen
}
// SimCell represents a simulated screen cell. The purpose of this
// is to track on screen content.
type SimCell struct {
// Bytes is the actual character bytes. Normally this is
// rune data, but it could be be data in another encoding system.
Bytes []byte
// Style is the style used to display the data.
Style Style
// Runes is the list of runes, unadulterated, in UTF-8.
Runes []rune
}
type simscreen struct {
logw int
logh int
physw int
physh int
style Style
evch chan Event
quit chan struct{}
front []SimCell
back []Cell
clear bool
cursorx int
cursory int
cursorvis bool
mouse bool
charset string
encoder transform.Transformer
decoder transform.Transformer
fillchar rune
fillstyle Style
sync.Mutex
}
func (s *simscreen) Init() error {
s.evch = make(chan Event, 10)
s.fillchar = 'X'
s.fillstyle = StyleDefault
s.mouse = false
s.logw = 80
s.logh = 25
s.physw = 80
s.physh = 25
s.cursorx = -1
s.cursory = -1
s.style = StyleDefault
switch s.charset {
case "UTF-8", "US-ASCII":
s.encoder = nil
s.decoder = nil
default:
if enc := GetEncoding(s.charset); enc != nil {
s.encoder = enc.NewEncoder()
s.decoder = enc.NewDecoder()
} else {
return errors.New("no support for charset " + s.charset)
}
}
s.front = make([]SimCell, s.physw*s.physh)
s.back = ResizeCells(nil, 0, 0, s.logw, s.logh)
return nil
}
func (s *simscreen) Fini() {
if s.quit != nil {
close(s.quit)
}
s.logw = 0
s.logh = 0
s.physw = 0
s.physh = 0
s.front = nil
s.back = nil
}
func (s *simscreen) SetStyle(style Style) {
s.Lock()
s.style = style
s.Unlock()
}
func (s *simscreen) Clear() {
s.Lock()
ClearCells(s.back, s.style)
s.Unlock()
}
func (s *simscreen) SetCell(x, y int, style Style, ch ...rune) {
s.Lock()
if x < 0 || y < 0 || x >= s.logw || y >= s.logh {
s.Unlock()
return
}
cell := &s.back[(y*s.logw)+x]
cell.SetCell(ch, style)
s.Unlock()
}
func (s *simscreen) PutCell(x, y int, cell *Cell) {
s.Lock()
if x < 0 || y < 0 || x >= s.logw || y >= s.logh {
s.Unlock()
return
}
cp := &s.back[(y*s.logw)+x]
cp.PutStyle(cell.Style)
cp.PutChars(cell.Ch)
s.Unlock()
}
func (s *simscreen) GetCell(x, y int) *Cell {
s.Lock()
if x < 0 || y < 0 || x >= s.logw || y >= s.logh {
s.Unlock()
return nil
}
cell := s.back[(y*s.logw)+x]
s.Unlock()
return &cell
}
func (s *simscreen) drawCell(x, y int, cell *Cell) {
if x >= s.physw || y >= s.physh || x < 0 || y < 0 {
return
}
simc := &s.front[(y*s.physw)+x]
if cell.Style == StyleDefault {
simc.Style = s.style
} else {
simc.Style = cell.Style
}
simc.Runes = nil
simc.Runes = append(simc.Runes, cell.Ch...)
// now emit runes - taking care to not overrun width with a
// wide character, and to ensure that we emit exactly one regular
// character followed up by any residual combing characters
width := int(cell.Width)
simc.Bytes = nil
if len(cell.Ch) == 0 {
simc.Bytes = []byte{' '}
return
}
if width > 1 && x >= s.physw-1 {
simc.Runes = []rune{' '}
simc.Bytes = []byte{' '}
return
}
enc := s.encoder
ubuf := make([]byte, 12)
lbuf := make([]byte, 12)
for _, r := range simc.Runes {
l := utf8.EncodeRune(ubuf, r)
if enc == nil {
simc.Bytes = append(simc.Bytes, ubuf[:l]...)
if s.charset == "US-ASCII" {
return
}
continue
}
nout, _, _ := enc.Transform(lbuf, ubuf[:l], true)
if nout == 1 && lbuf[0] == '\x1a' {
// replacement character
if simc.Bytes == nil {
simc.Bytes = append(simc.Bytes, '?')
}
} else if nout > 0 {
simc.Bytes = append(simc.Bytes, lbuf[:nout]...)
}
}
}
func (s *simscreen) ShowCursor(x, y int) {
s.Lock()
s.cursorx, s.cursory = x, y
s.showCursor()
s.Unlock()
}
func (s *simscreen) HideCursor() {
s.ShowCursor(-1, -1)
}
func (s *simscreen) showCursor() {
x, y := s.cursorx, s.cursory
if x < 0 || y < 0 || x >= s.physw || y >= s.physh {
s.cursorvis = false
} else {
s.cursorvis = true
}
}
func (s *simscreen) hideCursor() {
// does not update cursor position
s.cursorvis = false
}
func (s *simscreen) Show() {
s.Lock()
s.resize()
s.draw()
s.Unlock()
}
func (s *simscreen) clearScreen() {
// We emulate a hardware clear by filling with a specific pattern
for i := range s.front {
s.front[i].Style = s.fillstyle
s.front[i].Runes = []rune{s.fillchar}
s.front[i].Bytes = []byte{byte(s.fillchar)}
}
s.clear = false
}
func (s *simscreen) draw() {
// hide the cursor while we move stuff around
s.hideCursor()
if s.clear {
s.clearScreen()
}
for row := 0; row < s.logh; row++ {
for col := 0; col < s.logw; col++ {
cell := &s.back[(row*s.logw)+col]
if !cell.Dirty {
continue
}
s.drawCell(col, row, cell)
if cell.Width > 1 {
col++
}
cell.Dirty = false
}
}
// restore the cursor
s.showCursor()
}
func (s *simscreen) EnableMouse() {
s.mouse = true
}
func (s *simscreen) DisableMouse() {
s.mouse = false
}
func (s *simscreen) Size() (int, int) {
s.Lock()
w, h := s.logw, s.logh
s.Unlock()
return w, h
}
func (s *simscreen) resize() {
var ev Event
w, h := s.physw, s.physh
if w != s.logw || h != s.logh {
ev = NewEventResize(w, h)
s.back = ResizeCells(s.back, s.logw, s.logh, w, h)
s.logw = w
s.logh = h
}
if ev != nil {
s.PostEvent(ev)
}
}
func (s *simscreen) Colors() int {
return 256
}
func (s *simscreen) PollEvent() Event {
select {
case <-s.quit:
return nil
case ev := <-s.evch:
return ev
}
}
func (s *simscreen) PostEvent(ev Event) {
select {
case s.evch <- ev:
default:
// drop the event on the floor
}
}
func (s *simscreen) InjectMouse(x, y int, buttons ButtonMask, mod ModMask) {
ev := NewEventMouse(x, y, buttons, mod)
s.PostEvent(ev)
}
func (s *simscreen) InjectKey(key Key, r rune, mod ModMask) {
ev := NewEventKey(KeyRune, r, ModNone)
s.PostEvent(ev)
}
func (s *simscreen) InjectKeyBytes(b []byte) bool {
failed := false
outer:
for len(b) > 0 {
if b[0] >= ' ' && b[0] <= 0x7F {
// printable ASCII easy to deal with -- no encodings
ev := NewEventKey(KeyRune, rune(b[0]), ModNone)
s.PostEvent(ev)
b = b[1:]
continue
}
if b[0] < 0x80 {
mod := ModNone
// No encodings start with low numbered values
if Key(b[0]) >= KeyCtrlA && Key(b[0]) <= KeyCtrlZ {
mod = ModCtrl
}
ev := NewEventKey(Key(b[0]), 0, mod)
s.PostEvent(ev)
continue
}
switch s.charset {
case "UTF-8":
r, l := utf8.DecodeRune(b)
if r == utf8.RuneError && (l == 0 || l == 1) {
failed = true
// yank off one byte
b = b[1:]
} else {
b = b[l:]
ev := NewEventKey(KeyRune, r, ModNone)
s.PostEvent(ev)
continue
}
case "US-ASCII":
// ASCII cannot generate this, so most likely it was
// entered as an Alt sequence
ev := NewEventKey(KeyRune, rune(b[0]-128), ModAlt)
s.PostEvent(ev)
b = b[1:]
continue
default:
utfb := make([]byte, len(b)*4) // worst case
dec := s.decoder
if dec == nil {
failed = true
b = b[1:]
continue
}
// take care to consume at *most* a single rune
for l := 1; l < len(b); l++ {
dec.Reset()
nout, nin, _ := dec.Transform(utfb, b[:l], true)
if nout != 0 {
r, _ := utf8.DecodeRune(utfb[:nout])
ev := NewEventKey(KeyRune, r, ModNone)
s.PostEvent(ev)
b = b[nin:]
continue outer
}
}
failed = true
b = b[1:]
continue
}
}
return failed == false
}
func (s *simscreen) Sync() {
s.Lock()
s.clear = true
s.resize()
InvalidateCells(s.back)
s.draw()
s.Unlock()
}
func (s *simscreen) CharacterSet() string {
return s.charset
}
func (s *simscreen) Resize(w, h int) {
s.Lock()
newc := make([]SimCell, w*h)
for row := 0; row < h && row < s.physh; row++ {
for col := 0; col < w && col < s.physw; col++ {
newc[(row*w)+col] = s.front[(row*s.physw)+col]
}
}
s.physw = w
s.physh = h
s.Unlock()
}
func (s *simscreen) GetContents() ([]SimCell, int, int) {
s.Lock()
cells, w, h := s.front, s.physw, s.physh
s.Unlock()
return cells, w, h
}
func (s *simscreen) GetCursor() (int, int, bool) {
s.Lock()
x, y, vis := s.cursorx, s.cursory, s.cursorvis
s.Unlock()
return x, y, vis
}

43
style_test.go Normal file
View File

@ -0,0 +1,43 @@
// Copyright 2015 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tcell
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestStyle(t *testing.T) {
Convey("Style checks", t, WithScreen(t, "", func(s SimulationScreen) {
style := StyleDefault
fg, bg, attr := style.Decompose()
So(fg, ShouldEqual, ColorDefault)
So(bg, ShouldEqual, ColorDefault)
So(attr, ShouldEqual, AttrNone)
s2 := style.
Background(ColorRed).
Foreground(ColorBlue).
Blink(true)
fg, bg, attr = s2.Decompose()
So(fg, ShouldEqual, ColorBlue)
So(bg, ShouldEqual, ColorRed)
So(attr, ShouldEqual, AttrBlink)
}))
}

90
terminfo_test.go Normal file
View File

@ -0,0 +1,90 @@
// Copyright 2015 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tcell
import (
"bytes"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestTerminfo(t *testing.T) {
// This terminfo entry is a stripped down version from
// xterm-256color, but I've added some of my own entries.
ti := &Terminfo{
Name: "simulation_test",
Columns: 80,
Lines: 24,
Colors: 256,
Bell: "\a",
Blink: "\x1b2ms$<2>",
Reverse: "\x1b[7m",
SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m",
SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m",
AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~",
Mouse: "\x1b[M",
MouseMode: "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;\x1b[?1000%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
PadChar: "\x00",
}
Convey("Terminfo parameter processing", t, func() {
// This tests %i, and basic parameter strings too
Convey("TGoto works", func() {
s := ti.TGoto(7, 9)
So(s, ShouldEqual, "\x1b[10;8H")
})
// This tests some conditionals
Convey("TParm colors work", func() {
s := ti.TParm(ti.SetFg, 7)
So(s, ShouldEqual, "\x1b[37m")
s = ti.TParm(ti.SetFg, 15)
So(s, ShouldEqual, "\x1b[97m")
s = ti.TParm(ti.SetFg, 200)
So(s, ShouldEqual, "\x1b[38;5;200m")
})
// This tests variables
Convey("TParm mouse mode works", func() {
s := ti.TParm(ti.MouseMode, 1)
So(s, ShouldEqual, "\x1b[?1000h\x1b[?1003h\x1b[?1006h")
s = ti.TParm(ti.MouseMode, 0)
So(s, ShouldEqual, "\x1b[?1000l\x1b[?1003l\x1b[?1006l")
})
})
Convey("Terminfo delay handling", t, func() {
Convey("19200 baud", func() {
buf := bytes.NewBuffer(nil)
ti.TPuts(buf, ti.Blink, 19200)
s := string(buf.Bytes())
So(s, ShouldEqual, "\x1b2ms\x00\x00\x00\x00")
})
Convey("50 baud", func() {
buf := bytes.NewBuffer(nil)
ti.TPuts(buf, ti.Blink, 50)
s := string(buf.Bytes())
So(s, ShouldEqual, "\x1b2ms")
})
})
}