mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-06 09:55:40 +00:00
cmd/gogio: start using a TestDriver e2e interface
Now we implement the "red background" end-to-end test exactly once. While at it, start using a 800x600 window size, which is a bit more realistic than 600x600, and will catch if we got either dimension wrong. The interface only has two methods for now, but it will be expanded in the future to also support input such as clicks. Keeping state in the test driver, such as a context or a connection, is a bit awkward but necessary so that we don't have to repeat arguments over and over. The same applies to testing.T. Signed-off-by: Daniel Martí <mvdan@mvdan.cc>
This commit is contained in:
+64
-56
@@ -10,6 +10,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"image"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
@@ -26,8 +27,15 @@ import (
|
||||
"github.com/BurntSushi/xgbutil/xgraphics"
|
||||
)
|
||||
|
||||
func TestX11(t *testing.T) {
|
||||
t.Parallel()
|
||||
type X11TestDriver struct {
|
||||
t *testing.T
|
||||
|
||||
// conn holds the connection to X.
|
||||
conn *xgbutil.XUtil
|
||||
}
|
||||
|
||||
func (d *X11TestDriver) Start(t_ *testing.T, path string, width, height int) (cleanups []func()) {
|
||||
d.t = t_
|
||||
|
||||
// Pick a random display number between 1 and 100,000. Most machines
|
||||
// will only be using :0, so there's only a 0.001% chance of two
|
||||
@@ -39,31 +47,31 @@ func TestX11(t *testing.T) {
|
||||
xflags := []string{"-wr"}
|
||||
if *headless {
|
||||
xprog = "Xvfb" // virtual X server
|
||||
xflags = append(xflags, "-screen", "0", "600x600x24")
|
||||
xflags = append(xflags, "-screen", "0", fmt.Sprintf("%dx%dx24", width, height))
|
||||
} else {
|
||||
xprog = "Xephyr" // nested X server as a window
|
||||
xflags = append(xflags, "-screen", "600x600")
|
||||
xflags = append(xflags, "-screen", fmt.Sprintf("%dx%d", width, height))
|
||||
}
|
||||
xflags = append(xflags, display)
|
||||
if _, err := exec.LookPath(xprog); err != nil {
|
||||
t.Skipf("%s needed to run with -headless=%t", xprog, *headless)
|
||||
d.t.Skipf("%s needed to run with -headless=%t", xprog, *headless)
|
||||
}
|
||||
|
||||
// First, build the app.
|
||||
dir, err := ioutil.TempDir("", "gio-endtoend-x11")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
d.t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
cleanups = append(cleanups, func() { os.RemoveAll(dir) })
|
||||
|
||||
bin := filepath.Join(dir, "red")
|
||||
cmd := exec.Command("go", "build", "-o="+bin, "testdata/red.go")
|
||||
cmd := exec.Command("go", "build", "-o="+bin, path)
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
t.Fatalf("could not build app: %s:\n%s", err, out)
|
||||
d.t.Fatalf("could not build app: %s:\n%s", err, out)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
defer wg.Wait()
|
||||
cleanups = append(cleanups, wg.Wait)
|
||||
|
||||
// First, start the X server.
|
||||
{
|
||||
@@ -73,16 +81,16 @@ func TestX11(t *testing.T) {
|
||||
cmd.Stdout = combined
|
||||
cmd.Stderr = combined
|
||||
if err := cmd.Start(); err != nil {
|
||||
t.Fatal(err)
|
||||
d.t.Fatal(err)
|
||||
}
|
||||
defer cancel()
|
||||
defer func() {
|
||||
cleanups = append(cleanups, cancel)
|
||||
cleanups = append(cleanups, func() {
|
||||
// Give Xserver a chance to exit gracefully, cleaning up
|
||||
// after itself in /tmp. After 10ms, the deferred cancel
|
||||
// above will signal an os.Kill.
|
||||
cmd.Process.Signal(os.Interrupt)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}()
|
||||
})
|
||||
|
||||
// Wait for up to 1s (100 * 10ms) for the X server to be ready.
|
||||
for i := 0; ; i++ {
|
||||
@@ -95,7 +103,7 @@ func TestX11(t *testing.T) {
|
||||
break
|
||||
}
|
||||
if i >= 100 {
|
||||
t.Fatalf("timed out waiting for %s", socket)
|
||||
d.t.Fatalf("timed out waiting for %s", socket)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +112,7 @@ func TestX11(t *testing.T) {
|
||||
if err := cmd.Wait(); err != nil && ctx.Err() == nil {
|
||||
// Print all output and error.
|
||||
io.Copy(os.Stdout, combined)
|
||||
t.Error(err)
|
||||
d.t.Error(err)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
@@ -119,58 +127,58 @@ func TestX11(t *testing.T) {
|
||||
cmd.Stdout = out
|
||||
cmd.Stderr = out
|
||||
if err := cmd.Start(); err != nil {
|
||||
t.Fatal(err)
|
||||
d.t.Fatal(err)
|
||||
}
|
||||
defer cancel()
|
||||
cleanups = append(cleanups, cancel)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
if err := cmd.Wait(); err != nil && ctx.Err() == nil {
|
||||
// Print all output and error.
|
||||
io.Copy(os.Stdout, out)
|
||||
t.Error(err)
|
||||
d.t.Error(err)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
// Finally, run our tests. A connection to the X server is used to
|
||||
// interact with it.
|
||||
{
|
||||
xgb.Logger.SetOutput(testLogWriter{t})
|
||||
xgbutil.Logger.SetOutput(testLogWriter{t})
|
||||
xu, err := xgbutil.NewConnDisplay(display)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
xu.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.
|
||||
// TODO(mvdan): do this properly, e.g. via waiting for log lines
|
||||
// from the gio program.
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
img, err := xgraphics.NewDrawable(xu, xproto.Drawable(xu.RootWin()))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
size := img.Bounds().Size()
|
||||
wantSize := 600 // 300px at 2.0 scaling factor
|
||||
if size.X != wantSize || size.Y != wantSize {
|
||||
t.Fatalf("expected dimensions to be %d*%d, got %d*%d",
|
||||
wantSize, wantSize, size.X, size.Y)
|
||||
}
|
||||
wantColor(t, img, 5, 5, 0xdede, 0xadad, 0xbebe)
|
||||
wantColor(t, img, 595, 595, 0xdede, 0xadad, 0xbebe)
|
||||
// Finally, connect to the X server.
|
||||
xgb.Logger.SetOutput(testLogWriter{d.t})
|
||||
xgbutil.Logger.SetOutput(testLogWriter{d.t})
|
||||
conn, err := xgbutil.NewConnDisplay(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.
|
||||
// TODO(mvdan): do this properly, e.g. via waiting for log lines
|
||||
// from the gio program.
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
return cleanups
|
||||
}
|
||||
|
||||
func (d *X11TestDriver) Screenshot() image.Image {
|
||||
img, err := xgraphics.NewDrawable(d.conn, xproto.Drawable(d.conn.RootWin()))
|
||||
if err != nil {
|
||||
d.t.Fatal(err)
|
||||
}
|
||||
return img
|
||||
}
|
||||
|
||||
func TestX11(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
runEndToEndTest(t, &X11TestDriver{})
|
||||
}
|
||||
|
||||
// testLogWriter is a bit of a hack to redirect libraries that use a *log.Logger
|
||||
|
||||
Reference in New Issue
Block a user