Files
gio-patched/widget/editor_test.go
T
Elias Naur 851255f7a6 widget: tolerate nil shader in Editor movement methods
Fixes gio#142

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2020-06-28 22:34:58 +02:00

169 lines
4.1 KiB
Go

// SPDX-License-Identifier: Unlicense OR MIT
package widget
import (
"fmt"
"image"
"math/rand"
"reflect"
"testing"
"testing/quick"
"unicode"
"gioui.org/f32"
"gioui.org/font/gofont"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/text"
"gioui.org/unit"
)
func TestEditor(t *testing.T) {
e := new(Editor)
gtx := layout.Context{
Ops: new(op.Ops),
Constraints: layout.Exact(image.Pt(100, 100)),
}
cache := text.NewCache(gofont.Collection())
fontSize := unit.Px(10)
font := text.Font{}
e.SetText("æbc\naøå•")
e.Layout(gtx, cache, font, fontSize)
assertCaret(t, e, 0, 0, 0)
e.moveEnd()
assertCaret(t, e, 0, 3, len("æbc"))
e.Move(+1)
assertCaret(t, e, 1, 0, len("æbc\n"))
e.Move(-1)
assertCaret(t, e, 0, 3, len("æbc"))
e.moveLines(+1)
assertCaret(t, e, 1, 3, len("æbc\naøå"))
e.moveEnd()
assertCaret(t, e, 1, 4, len("æbc\naøå•"))
e.Move(+1)
assertCaret(t, e, 1, 4, len("æbc\naøå•"))
// Ensure that password masking does not affect caret behavior
e.Move(-3)
assertCaret(t, e, 1, 1, len("æbc\na"))
e.Mask = '*'
e.Layout(gtx, cache, font, fontSize)
assertCaret(t, e, 1, 1, len("æbc\na"))
e.Move(-3)
assertCaret(t, e, 0, 2, len("æb"))
e.Mask = '\U0001F92B'
e.Layout(gtx, cache, font, fontSize)
e.moveEnd()
assertCaret(t, e, 0, 3, len("æbc"))
// When a password mask is applied, it should replace all visible glyphs
for i, line := range e.lines {
for j, glyph := range line.Layout {
if glyph.Rune != e.Mask && !unicode.IsSpace(glyph.Rune) {
t.Errorf("glyph at (%d, %d) is unmasked rune %d", i, j, glyph.Rune)
}
}
}
}
// assertCaret asserts that the editor caret is at a particular line
// and column, and that the byte position matches as well.
func assertCaret(t *testing.T, e *Editor, line, col, bytes int) {
t.Helper()
gotLine, gotCol := e.CaretPos()
if gotLine != line || gotCol != col {
t.Errorf("caret at (%d, %d), expected (%d, %d)", gotLine, gotCol, line, col)
}
if bytes != e.rr.caret {
t.Errorf("caret at buffer position %d, expected %d", e.rr.caret, bytes)
}
}
type editMutation int
const (
setText editMutation = iota
moveRune
moveLine
movePage
moveStart
moveEnd
moveCoord
moveLast // Mark end; never generated.
)
func TestEditorCaretConsistency(t *testing.T) {
gtx := layout.Context{
Ops: new(op.Ops),
Constraints: layout.Exact(image.Pt(100, 100)),
}
cache := text.NewCache(gofont.Collection())
fontSize := unit.Px(10)
font := text.Font{}
for _, a := range []text.Alignment{text.Start, text.Middle, text.End} {
e := &Editor{
Alignment: a,
}
e.Layout(gtx, cache, font, fontSize)
consistent := func() error {
t.Helper()
gotLine, gotCol := e.CaretPos()
gotCoords := e.CaretCoords()
wantLine, wantCol, wantX, wantY := e.layoutCaret()
wantCoords := f32.Pt(float32(wantX)/64, float32(wantY))
if wantLine == gotLine && wantCol == gotCol && gotCoords == wantCoords {
return nil
}
return fmt.Errorf("caret (%d,%d) pos %s, want (%d,%d) pos %s", gotLine, gotCol, gotCoords, wantLine, wantCol, wantCoords)
}
if err := consistent(); err != nil {
t.Errorf("initial editor inconsistency (alignment %s): %v", a, err)
}
move := func(mutation editMutation, str string, distance int8, x, y uint16) bool {
switch mutation {
case setText:
e.SetText(str)
e.Layout(gtx, cache, font, fontSize)
case moveRune:
e.Move(int(distance))
case moveLine:
e.moveLines(int(distance))
case movePage:
e.movePages(int(distance))
case moveStart:
e.moveStart()
case moveEnd:
e.moveEnd()
case moveCoord:
e.moveCoord(image.Pt(int(x), int(y)))
default:
return false
}
if err := consistent(); err != nil {
t.Error(err)
return false
}
return true
}
if err := quick.Check(move, nil); err != nil {
t.Errorf("editor inconsistency (alignment %s): %v", a, err)
}
}
}
func TestEditorNoLayout(t *testing.T) {
var e Editor
e.SetText("hi!\n")
e.Move(1)
}
// Generate generates a value of itself, for testing/quick.
func (editMutation) Generate(rand *rand.Rand, size int) reflect.Value {
t := editMutation(rand.Intn(int(moveLast)))
return reflect.ValueOf(t)
}