cmd/gogio: rip out cgo deps for x11

Instead rely on more tiny standalone tools. In this case, scrot lets us
take png screenshots, and works well.

On the plus side, we remove some large X Go deps, and we don't need
nearly as much code.

While at it, skip if any of the tools are missing, and actually defer
the cleanup funcs so that they run when we fail the test early.

Signed-off-by: Daniel Martí <mvdan@mvdan.cc>
This commit is contained in:
Daniel Martí
2019-11-01 23:16:49 +00:00
committed by Elias Naur
parent 837b5f6bab
commit b8e996074b
4 changed files with 27 additions and 49 deletions
-4
View File
@@ -4,10 +4,6 @@ go 1.13
require ( require (
gioui.org v0.0.0-20191030194120-b53c2a7c9d56 gioui.org v0.0.0-20191030194120-b53c2a7c9d56
github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298 // indirect
github.com/BurntSushi/graphics-go v0.0.0-20160129215708-b43f31a4a966 // indirect
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802
github.com/BurntSushi/xgbutil v0.0.0-20190907113008-ad855c713046
github.com/chromedp/cdproto v0.0.0-20191009033829-c22f49c9ff0a github.com/chromedp/cdproto v0.0.0-20191009033829-c22f49c9ff0a
github.com/chromedp/chromedp v0.5.1 github.com/chromedp/chromedp v0.5.1
golang.org/x/image v0.0.0-20190802002840-cff245a6509b golang.org/x/image v0.0.0-20190802002840-cff245a6509b
-6
View File
@@ -1,14 +1,8 @@
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
gioui.org v0.0.0-20191030194120-b53c2a7c9d56 h1:ZC5qDUrd0+64qlceTlMC1DM3bjEqAMohfP+3aE3U5OE= gioui.org v0.0.0-20191030194120-b53c2a7c9d56 h1:ZC5qDUrd0+64qlceTlMC1DM3bjEqAMohfP+3aE3U5OE=
gioui.org v0.0.0-20191030194120-b53c2a7c9d56/go.mod h1:KqFFi2Dq5gYA3FJ0sDOt8OBXoMsuxMtE8v2f0JExXAY= gioui.org v0.0.0-20191030194120-b53c2a7c9d56/go.mod h1:KqFFi2Dq5gYA3FJ0sDOt8OBXoMsuxMtE8v2f0JExXAY=
github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298 h1:1qlsVAQJXZHsaM8b6OLVo6muQUQd4CwkH/D3fnnbHXA=
github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298/go.mod h1:D+QujdIlUNfa0igpNMk6UIvlb6C252URs4yupRUV4lQ=
github.com/BurntSushi/graphics-go v0.0.0-20160129215708-b43f31a4a966 h1:lTG4HQym5oPKjL7nGs+csTgiDna685ZXjxijkne828g=
github.com/BurntSushi/graphics-go v0.0.0-20160129215708-b43f31a4a966/go.mod h1:Mid70uvE93zn9wgF92A/r5ixgnvX8Lh68fxp9KQBaI0=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/BurntSushi/xgbutil v0.0.0-20190907113008-ad855c713046 h1:O/r2Sj+8QcMF7V5IcmiE2sMFV2q3J47BEirxbXJAdzA=
github.com/BurntSushi/xgbutil v0.0.0-20190907113008-ad855c713046/go.mod h1:uw9h2sd4WWHOPdJ13MQpwK5qYWKYDumDqxWWIknEQ+k=
github.com/chromedp/cdproto v0.0.0-20191009033829-c22f49c9ff0a h1:AuIGvB6IuWpMEdfKQ+t77D6dzLpNftzxAsktehYyWn8= github.com/chromedp/cdproto v0.0.0-20191009033829-c22f49c9ff0a h1:AuIGvB6IuWpMEdfKQ+t77D6dzLpNftzxAsktehYyWn8=
github.com/chromedp/cdproto v0.0.0-20191009033829-c22f49c9ff0a/go.mod h1:PfAWWKJqjlGFYJEidUM6aVIWPr0EpobeyVWEEmplX7g= github.com/chromedp/cdproto v0.0.0-20191009033829-c22f49c9ff0a/go.mod h1:PfAWWKJqjlGFYJEidUM6aVIWPr0EpobeyVWEEmplX7g=
github.com/chromedp/chromedp v0.5.1 h1:PAqhoCWCHzRphYnmmxLSiYk7EEwDplCm4woTCCaV2cQ= github.com/chromedp/chromedp v0.5.1 h1:PAqhoCWCHzRphYnmmxLSiYk7EEwDplCm4woTCCaV2cQ=
+5 -5
View File
@@ -39,11 +39,15 @@ type TestDriver interface {
func runEndToEndTest(t *testing.T, driver TestDriver) { func runEndToEndTest(t *testing.T, driver TestDriver) {
width, height := 800, 600 width, height := 800, 600
cleanups := driver.Start(t, "testdata/red.go", width, height) cleanups := driver.Start(t, "testdata/red.go", width, height)
for _, cleanup := range cleanups {
defer cleanup()
}
// The colors are split in four rectangular sections. Check the corners // The colors are split in four rectangular sections. Check the corners
// of each of the sections. We check the corners left to right, top to // of each of the sections. We check the corners left to right, top to
// bottom, like when reading left-to-right text. // bottom, like when reading left-to-right text.
wantColors := func(topLeft, topRight, botLeft, botRight color.RGBA) { wantColors := func(topLeft, topRight, botLeft, botRight color.RGBA) {
t.Helper()
img := driver.Screenshot() img := driver.Screenshot()
size := img.Bounds().Size() size := img.Bounds().Size()
// We expect to receive a width*height screenshot. // We expect to receive a width*height screenshot.
@@ -98,14 +102,10 @@ func runEndToEndTest(t *testing.T, driver TestDriver) {
driver.Click(1*(width/4), 1*(height/4)) driver.Click(1*(width/4), 1*(height/4))
driver.Click(3*(width/4), 3*(height/4)) driver.Click(3*(width/4), 3*(height/4))
wantColors(red, white, black, red) wantColors(red, white, black, red)
// Run the cleanup funcs from last to first, as if they were defers.
for i := len(cleanups) - 1; i >= 0; i-- {
cleanups[i]()
}
} }
func wantColor(t *testing.T, img image.Image, x, y int, want color.Color) { func wantColor(t *testing.T, img image.Image, x, y int, want color.Color) {
t.Helper()
r, g, b, _ := want.RGBA() r, g, b, _ := want.RGBA()
got := img.At(x, y) got := img.At(x, y)
r_, g_, b_, _ := got.RGBA() r_, g_, b_, _ := got.RGBA()
+22 -34
View File
@@ -11,6 +11,7 @@ import (
"context" "context"
"fmt" "fmt"
"image" "image"
"image/png"
"io" "io"
"io/ioutil" "io/ioutil"
"math/rand" "math/rand"
@@ -20,20 +21,12 @@ import (
"sync" "sync"
"testing" "testing"
"time" "time"
"github.com/BurntSushi/xgb"
"github.com/BurntSushi/xgb/xproto"
"github.com/BurntSushi/xgbutil"
"github.com/BurntSushi/xgbutil/xgraphics"
) )
type X11TestDriver struct { type X11TestDriver struct {
t *testing.T t *testing.T
display string display string
// conn holds the connection to X.
conn *xgbutil.XUtil
} }
func (d *X11TestDriver) Start(t_ *testing.T, path string, width, height int) (cleanups []func()) { func (d *X11TestDriver) Start(t_ *testing.T, path string, width, height int) (cleanups []func()) {
@@ -57,8 +50,15 @@ func (d *X11TestDriver) Start(t_ *testing.T, path string, width, height int) (cl
xflags = append(xflags, "-screen", fmt.Sprintf("%dx%d", width, height)) xflags = append(xflags, "-screen", fmt.Sprintf("%dx%d", width, height))
} }
xflags = append(xflags, d.display) xflags = append(xflags, d.display)
if _, err := exec.LookPath(xprog); err != nil {
d.t.Skipf("%s needed to run with -headless=%t", xprog, *headless) for _, prog := range []string{
xprog, // to run the X server
"scrot", // to take screenshots
"xdotool", // to send input
} {
if _, err := exec.LookPath(prog); err != nil {
d.t.Skipf("%s needed to run", prog)
}
} }
// First, build the app. // First, build the app.
@@ -99,9 +99,8 @@ func (d *X11TestDriver) Start(t_ *testing.T, path string, width, height int) (cl
// Wait for up to 1s (100 * 10ms) for the X server to be ready. // Wait for up to 1s (100 * 10ms) for the X server to be ready.
for i := 0; ; i++ { for i := 0; ; i++ {
time.Sleep(10 * time.Millisecond) time.Sleep(10 * time.Millisecond)
// This socket path isn't terribly portable, but the xgb // This socket path isn't terribly portable, but it's
// library we use does the same, and we only really care // okay for now.
// about Linux here.
socket := fmt.Sprintf("/tmp/.X11-unix/X%s", d.display[1:]) socket := fmt.Sprintf("/tmp/.X11-unix/X%s", d.display[1:])
if _, err := os.Stat(socket); err == nil { if _, err := os.Stat(socket); err == nil {
break break
@@ -127,7 +126,7 @@ func (d *X11TestDriver) Start(t_ *testing.T, path string, width, height int) (cl
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
cmd := exec.CommandContext(ctx, bin) cmd := exec.CommandContext(ctx, bin)
out := &bytes.Buffer{} out := &bytes.Buffer{}
cmd.Env = append(os.Environ(), "DISPLAY="+d.display) cmd.Env = []string{"DISPLAY=" + d.display}
cmd.Stdout = out cmd.Stdout = out
cmd.Stderr = out cmd.Stderr = out
if err := cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
@@ -145,24 +144,6 @@ func (d *X11TestDriver) Start(t_ *testing.T, path string, width, height int) (cl
}() }()
} }
// Finally, connect to the X server.
xgb.Logger.SetOutput(testLogWriter{d.t})
xgbutil.Logger.SetOutput(testLogWriter{d.t})
conn, err := xgbutil.NewConnDisplay(d.display)
if err != nil {
d.t.Fatal(err)
}
d.conn = conn
cleanups = append(cleanups, func() {
conn.Conn().Close()
// TODO(mvdan): Figure out a way to remove this sleep
// without introducing a panic. The xgb code will
// encounter a panic if the Xorg server exits before xgb
// has shut down fully.
// See: https://github.com/BurntSushi/xgb/pull/44
time.Sleep(10 * time.Millisecond)
})
// Wait for the gio app to render. // Wait for the gio app to render.
// TODO(mvdan): synchronize with the app instead // TODO(mvdan): synchronize with the app instead
time.Sleep(400 * time.Millisecond) time.Sleep(400 * time.Millisecond)
@@ -171,7 +152,14 @@ func (d *X11TestDriver) Start(t_ *testing.T, path string, width, height int) (cl
} }
func (d *X11TestDriver) Screenshot() image.Image { func (d *X11TestDriver) Screenshot() image.Image {
img, err := xgraphics.NewDrawable(d.conn, xproto.Drawable(d.conn.RootWin())) cmd := exec.Command("scrot", "--silent", "--overwrite", "/dev/stdout")
cmd.Env = []string{"DISPLAY=" + d.display}
out, err := cmd.CombinedOutput()
if err != nil {
d.t.Errorf("%s", out)
d.t.Fatal(err)
}
img, err := png.Decode(bytes.NewReader(out))
if err != nil { if err != nil {
d.t.Fatal(err) d.t.Fatal(err)
} }
@@ -184,7 +172,7 @@ func (d *X11TestDriver) xdotool(args ...interface{}) {
strs[i] = fmt.Sprint(arg) strs[i] = fmt.Sprint(arg)
} }
cmd := exec.Command("xdotool", strs...) cmd := exec.Command("xdotool", strs...)
cmd.Env = append(os.Environ(), "DISPLAY="+d.display) cmd.Env = []string{"DISPLAY=" + d.display}
if out, err := cmd.CombinedOutput(); err != nil { if out, err := cmd.CombinedOutput(); err != nil {
d.t.Errorf("%s", out) d.t.Errorf("%s", out)
d.t.Fatal(err) d.t.Fatal(err)