widget: update text indexing expectations

The typesetting package has smarter line wrapping now, which is making our
test text require fewer lines to display. We needed to update the expected
data accordingly.

I've also added a feature that takes a screenshot of the rendered output of
one of our most complex cursoring tests. This will make it much easier to
verify its behavior in the future. This feature currently only triggers if
the test case fails.

Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
This commit is contained in:
Chris Waldon
2024-12-17 09:54:10 -05:00
parent 26cddb00b1
commit 1d0d5f0383
+65 -47
View File
@@ -2,12 +2,18 @@ package widget
import ( import (
"bytes" "bytes"
"image"
"image/png"
"io" "io"
"os"
"testing" "testing"
nsareg "eliasnaur.com/font/noto/sans/arabic/regular" nsareg "eliasnaur.com/font/noto/sans/arabic/regular"
"gioui.org/font" "gioui.org/font"
"gioui.org/font/opentype" "gioui.org/font/opentype"
"gioui.org/gpu/headless"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/text" "gioui.org/text"
"golang.org/x/image/font/gofont/goregular" "golang.org/x/image/font/gofont/goregular"
"golang.org/x/image/math/fixed" "golang.org/x/image/math/fixed"
@@ -16,11 +22,11 @@ import (
// makePosTestText returns two bidi samples of shaped text at the given // makePosTestText returns two bidi samples of shaped text at the given
// font size and wrapped to the given line width. The runeLimit, if nonzero, // font size and wrapped to the given line width. The runeLimit, if nonzero,
// truncates the sample text to ensure shorter output for expensive tests. // truncates the sample text to ensure shorter output for expensive tests.
func makePosTestText(fontSize, lineWidth int, alignOpposite bool) (source string, bidiLTR, bidiRTL []text.Glyph) { func makePosTestText(fontSize, lineWidth int, alignOpposite bool) (shaper *text.Shaper, source string, bidiLTR, bidiRTL []text.Glyph) {
ltrFace, _ := opentype.Parse(goregular.TTF) ltrFace, _ := opentype.Parse(goregular.TTF)
rtlFace, _ := opentype.Parse(nsareg.TTF) rtlFace, _ := opentype.Parse(nsareg.TTF)
shaper := text.NewShaper(text.NoSystemFonts(), text.WithCollection([]font.FontFace{ shaper = text.NewShaper(text.NoSystemFonts(), text.WithCollection([]font.FontFace{
{ {
Font: font.Font{Typeface: "LTR"}, Font: font.Font{Typeface: "LTR"},
Face: ltrFace, Face: ltrFace,
@@ -58,7 +64,7 @@ func makePosTestText(fontSize, lineWidth int, alignOpposite bool) (source string
for g, ok := shaper.NextGlyph(); ok; g, ok = shaper.NextGlyph() { for g, ok := shaper.NextGlyph(); ok; g, ok = shaper.NextGlyph() {
bidiRTL = append(bidiRTL, g) bidiRTL = append(bidiRTL, g)
} }
return bidiSource, bidiLTR, bidiRTL return shaper, bidiSource, bidiLTR, bidiRTL
} }
// makeAccountingTestText shapes text designed to stress rune accounting // makeAccountingTestText shapes text designed to stress rune accounting
@@ -260,7 +266,7 @@ func TestIndexPositionWhitespace(t *testing.T) {
func TestIndexPositionBidi(t *testing.T) { func TestIndexPositionBidi(t *testing.T) {
fontSize := 16 fontSize := 16
lineWidth := fontSize * 10 lineWidth := fontSize * 10
_, bidiLTRText, bidiRTLText := makePosTestText(fontSize, lineWidth, false) shaper, _, bidiLTRText, bidiRTLText := makePosTestText(fontSize, lineWidth, false)
type testcase struct { type testcase struct {
name string name string
glyphs []text.Glyph glyphs []text.Glyph
@@ -284,13 +290,12 @@ func TestIndexPositionBidi(t *testing.T) {
name: "bidi rtl", name: "bidi rtl",
glyphs: bidiRTLText, glyphs: bidiRTLText,
expectedXs: []fixed.Int26_6{ expectedXs: []fixed.Int26_6{
2646, 3272, 3842, 4412, 4697, 5267, 5837, 6090, 6602, 7114, 2646, 2380, 1577, 985, 687, 266, // Positions on line 0. // Line 0
5718, 6344, 6914, 7484, 7769, 8339, 8909, 9162, 9674, 10186, 5718, 5452, 4649, 4057, 3759, 3338, 3072, 2304, 1536, 768, 0,
7867, 7099, 6331, 5563, 4795, 4510, 4212, 3914, 3648, 2281, 2566, 3136, 3648, 2281, 2015, 1709, 1117, 266, // Positions on line 1. // Line 1
9170, 8872, 8574, 8308, 6941, 7226, 7796, 8308, 6941, 6675, 6369, 5777, 4926, 4660, 3892, 3124, 2356, 1588, 1303, 788, 406, 0,
8794, 8026, 7258, 6490, 5722, 5437, 4922, 4540, 4134, 3868, 0, 290, 860, 1430, 1715, 1989, 2559, 3071, 3583, // Positions on line 2. // Line 2
324, 614, 1184, 1754, 2039, 2313, 2883, 3395, 3907, 4192, 4762, 5332, 5902, 324, 0,
324, 894, 1464, 2034, 324, 0, // Positions on line 3.
}, },
}, },
} { } {
@@ -337,6 +342,35 @@ func TestIndexPositionBidi(t *testing.T) {
printPositions(t, gi.positions) printPositions(t, gi.positions)
if t.Failed() { if t.Failed() {
printGlyphs(t, tc.glyphs) printGlyphs(t, tc.glyphs)
width := lineWidth
height := 100
cap := image.NewRGBA(image.Rect(0, 0, width, height))
w, _ := headless.NewWindow(width, height)
defer w.Release()
ops := new(op.Ops)
gtx := layout.Context{
Constraints: layout.Constraints{Max: image.Pt(width, height)},
Ops: ops,
}
it := textIterator{viewport: image.Rectangle{Max: image.Point{X: width, Y: height}}}
for _, g := range tc.glyphs {
it.processGlyph(g, true)
}
var glyphs [32]text.Glyph
line := glyphs[:0]
for _, g := range gi.glyphs {
var ok bool
if line, ok = it.paintGlyph(gtx, shaper, g, line); !ok {
break
}
}
w.Frame(ops)
w.Screenshot(cap)
b := new(bytes.Buffer)
_ = png.Encode(b, cap)
screenshotName := tc.name + ".png"
_ = os.WriteFile(screenshotName, b.Bytes(), 0o644)
t.Logf("wrote %q", screenshotName)
} }
}) })
} }
@@ -345,8 +379,8 @@ func TestIndexPositionBidi(t *testing.T) {
func TestIndexPositionLines(t *testing.T) { func TestIndexPositionLines(t *testing.T) {
fontSize := 16 fontSize := 16
lineWidth := fontSize * 10 lineWidth := fontSize * 10
source1, bidiLTRText, bidiRTLText := makePosTestText(fontSize, lineWidth, false) _, source1, bidiLTRText, bidiRTLText := makePosTestText(fontSize, lineWidth, false)
source2, bidiLTRTextOpp, bidiRTLTextOpp := makePosTestText(fontSize, lineWidth, true) _, source2, bidiLTRTextOpp, bidiRTLTextOpp := makePosTestText(fontSize, lineWidth, true)
type testcase struct { type testcase struct {
name string name string
source string source string
@@ -379,7 +413,7 @@ func TestIndexPositionLines(t *testing.T) {
xOff: fixed.Int26_6(0), xOff: fixed.Int26_6(0),
yOff: 60, yOff: 60,
glyphs: 18, glyphs: 18,
width: fixed.Int26_6(8813), width: fixed.Int26_6(8528),
ascent: fixed.Int26_6(1407), ascent: fixed.Int26_6(1407),
descent: fixed.Int26_6(756), descent: fixed.Int26_6(756),
}, },
@@ -401,32 +435,24 @@ func TestIndexPositionLines(t *testing.T) {
{ {
xOff: fixed.Int26_6(0), xOff: fixed.Int26_6(0),
yOff: 22, yOff: 22,
glyphs: 15, glyphs: 20,
width: fixed.Int26_6(7114), width: fixed.Int26_6(10186),
ascent: fixed.Int26_6(1407), ascent: fixed.Int26_6(1407),
descent: fixed.Int26_6(756), descent: fixed.Int26_6(756),
}, },
{ {
xOff: fixed.Int26_6(0), xOff: fixed.Int26_6(0),
yOff: 41, yOff: 41,
glyphs: 15, glyphs: 19,
width: fixed.Int26_6(7867), width: fixed.Int26_6(9170),
ascent: fixed.Int26_6(1407), ascent: fixed.Int26_6(1407),
descent: fixed.Int26_6(756), descent: fixed.Int26_6(756),
}, },
{ {
xOff: fixed.Int26_6(0), xOff: fixed.Int26_6(0),
yOff: 60, yOff: 60,
glyphs: 18, glyphs: 13,
width: fixed.Int26_6(8794), width: fixed.Int26_6(5902),
ascent: fixed.Int26_6(1407),
descent: fixed.Int26_6(756),
},
{
xOff: fixed.Int26_6(0),
yOff: 79,
glyphs: 4,
width: fixed.Int26_6(2034),
ascent: fixed.Int26_6(968), ascent: fixed.Int26_6(968),
descent: fixed.Int26_6(216), descent: fixed.Int26_6(216),
}, },
@@ -454,10 +480,10 @@ func TestIndexPositionLines(t *testing.T) {
descent: fixed.Int26_6(756), descent: fixed.Int26_6(756),
}, },
{ {
xOff: fixed.Int26_6(1427), xOff: fixed.Int26_6(1712),
yOff: 60, yOff: 60,
glyphs: 18, glyphs: 18,
width: fixed.Int26_6(8813), width: fixed.Int26_6(8528),
ascent: fixed.Int26_6(1407), ascent: fixed.Int26_6(1407),
descent: fixed.Int26_6(756), descent: fixed.Int26_6(756),
}, },
@@ -477,34 +503,26 @@ func TestIndexPositionLines(t *testing.T) {
glyphs: bidiRTLTextOpp, glyphs: bidiRTLTextOpp,
expectedLines: []lineInfo{ expectedLines: []lineInfo{
{ {
xOff: fixed.Int26_6(3126), xOff: fixed.Int26_6(54),
yOff: 22, yOff: 22,
glyphs: 15, glyphs: 20,
width: fixed.Int26_6(7114), width: fixed.Int26_6(10186),
ascent: fixed.Int26_6(1407), ascent: fixed.Int26_6(1407),
descent: fixed.Int26_6(756), descent: fixed.Int26_6(756),
}, },
{ {
xOff: fixed.Int26_6(2373), xOff: fixed.Int26_6(1070),
yOff: 41, yOff: 41,
glyphs: 15, glyphs: 19,
width: fixed.Int26_6(7867), width: fixed.Int26_6(9170),
ascent: fixed.Int26_6(1407), ascent: fixed.Int26_6(1407),
descent: fixed.Int26_6(756), descent: fixed.Int26_6(756),
}, },
{ {
xOff: fixed.Int26_6(1446), xOff: fixed.Int26_6(4338),
yOff: 60, yOff: 60,
glyphs: 18, glyphs: 13,
width: fixed.Int26_6(8794), width: fixed.Int26_6(5902),
ascent: fixed.Int26_6(1407),
descent: fixed.Int26_6(756),
},
{
xOff: fixed.Int26_6(8206),
yOff: 79,
glyphs: 4,
width: fixed.Int26_6(2034),
ascent: fixed.Int26_6(968), ascent: fixed.Int26_6(968),
descent: fixed.Int26_6(216), descent: fixed.Int26_6(216),
}, },