mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-02 07:57:29 +00:00
170e86142c
For example, if the test app fails to start on wayland, we'd block ~forever (ten minutes) waiting for it to render its first frame. We don't have a good solution right now. But at least we can use a relatively short timeout, to help out the human who rightfully expects a result within ten seconds. While at it, remove a sway "get_seats" command, which was a leftover from my debugging of what input devices are available when running headless. Signed-off-by: Daniel Martí <mvdan@mvdan.cc>
169 lines
5.0 KiB
Go
169 lines
5.0 KiB
Go
// SPDX-License-Identifier: Unlicense OR MIT
|
|
|
|
package main_test
|
|
|
|
import (
|
|
"flag"
|
|
"image"
|
|
"image/color"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
var raceEnabled = false
|
|
|
|
var headless = flag.Bool("headless", true, "run end-to-end tests in headless mode")
|
|
|
|
// TestDriver is implemented by each of the platforms we can run end-to-end
|
|
// tests on. None of its methods return any errors, as the errors are directly
|
|
// reported to testing.T via methods like Fatal.
|
|
type TestDriver interface {
|
|
// Start provides the test driver with a testing.T, as well as the path
|
|
// to the Gio app to use for the test. The app will be run with the
|
|
// given width and height, and the platform's background should be
|
|
// white.
|
|
//
|
|
// When the function returns, the gio app must be ready to use on the
|
|
// platform, with its initial frame fully drawn.
|
|
Start(t *testing.T, path string, width, height int)
|
|
|
|
// Screenshot takes a screenshot of the Gio app on the platform.
|
|
Screenshot() image.Image
|
|
|
|
// Click performs a pointer click at the specified coordinates,
|
|
// including both press and release. It returns when the next frame is
|
|
// fully drawn.
|
|
Click(x, y int)
|
|
}
|
|
|
|
func TestEndToEnd(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skipf("end-to-end tests tend to be slow")
|
|
}
|
|
|
|
t.Parallel()
|
|
|
|
// Keep this list local, to not reuse TestDriver objects.
|
|
subtests := []struct {
|
|
name string
|
|
driver TestDriver
|
|
}{
|
|
{"X11", &X11TestDriver{}},
|
|
{"Wayland", &WaylandTestDriver{}},
|
|
{"JS", &JSTestDriver{}},
|
|
}
|
|
|
|
for _, subtest := range subtests {
|
|
t.Run(subtest.name, func(t *testing.T) {
|
|
subtest := subtest // copy the changing loop variable
|
|
t.Parallel()
|
|
runEndToEndTest(t, subtest.driver)
|
|
})
|
|
}
|
|
}
|
|
|
|
func runEndToEndTest(t *testing.T, driver TestDriver) {
|
|
size := image.Point{X: 800, Y: 600}
|
|
driver.Start(t, "testdata/red.go", size.X, size.Y)
|
|
|
|
// 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
|
|
// bottom, like when reading left-to-right text.
|
|
wantColors := func(topLeft, topRight, botLeft, botRight color.RGBA) {
|
|
t.Helper()
|
|
img := driver.Screenshot()
|
|
size_ := img.Bounds().Size()
|
|
if size_ != size {
|
|
if !*headless {
|
|
// Some non-headless drivers, like Sway, may get
|
|
// their window resized by the host window manager.
|
|
// Run the rest of the test with the new size.
|
|
size = size_
|
|
} else {
|
|
t.Fatalf("expected dimensions to be %v, got %v",
|
|
size, size_)
|
|
}
|
|
}
|
|
{
|
|
minX, minY := 5, 5
|
|
maxX, maxY := (size.X/2)-5, (size.Y/2)-5
|
|
wantColor(t, img, minX, minY, topLeft)
|
|
wantColor(t, img, maxX, minY, topLeft)
|
|
wantColor(t, img, minX, maxY, topLeft)
|
|
wantColor(t, img, maxX, maxY, topLeft)
|
|
}
|
|
{
|
|
minX, minY := (size.X/2)+5, 5
|
|
maxX, maxY := size.X-5, (size.Y/2)-5
|
|
wantColor(t, img, minX, minY, topRight)
|
|
wantColor(t, img, maxX, minY, topRight)
|
|
wantColor(t, img, minX, maxY, topRight)
|
|
wantColor(t, img, maxX, maxY, topRight)
|
|
}
|
|
{
|
|
minX, minY := 5, (size.Y/2)+5
|
|
maxX, maxY := (size.X/2)-5, size.Y-5
|
|
wantColor(t, img, minX, minY, botLeft)
|
|
wantColor(t, img, maxX, minY, botLeft)
|
|
wantColor(t, img, minX, maxY, botLeft)
|
|
wantColor(t, img, maxX, maxY, botLeft)
|
|
}
|
|
{
|
|
minX, minY := (size.X/2)+5, (size.Y/2)+5
|
|
maxX, maxY := size.X-5, size.Y-5
|
|
wantColor(t, img, minX, minY, botRight)
|
|
wantColor(t, img, maxX, minY, botRight)
|
|
wantColor(t, img, minX, maxY, botRight)
|
|
wantColor(t, img, maxX, maxY, botRight)
|
|
}
|
|
}
|
|
|
|
beef := color.RGBA{R: 0xde, G: 0xad, B: 0xbe}
|
|
white := color.RGBA{R: 0xff, G: 0xff, B: 0xff}
|
|
black := color.RGBA{R: 0x00, G: 0x00, B: 0x00}
|
|
gray := color.RGBA{R: 0xbb, G: 0xbb, B: 0xbb}
|
|
red := color.RGBA{R: 0xff, G: 0x00, B: 0x00}
|
|
|
|
// These are the four colors at the beginning.
|
|
wantColors(beef, white, black, gray)
|
|
|
|
// Click the first and last sections to turn them red.
|
|
// TODO(mvdan): implement this properly in the Wayland driver; swaymsg
|
|
// almost works to automate clicks, but the button presses end up in the
|
|
// wrong coordinates.
|
|
if _, ok := driver.(*WaylandTestDriver); ok {
|
|
return
|
|
}
|
|
driver.Click(1*(size.X/4), 1*(size.Y/4))
|
|
driver.Click(3*(size.X/4), 3*(size.Y/4))
|
|
wantColors(red, white, black, red)
|
|
}
|
|
|
|
func wantColor(t *testing.T, img image.Image, x, y int, want color.Color) {
|
|
t.Helper()
|
|
r, g, b, _ := want.RGBA()
|
|
got := img.At(x, y)
|
|
r_, g_, b_, _ := got.RGBA()
|
|
if r_ != r || g_ != g || b_ != b {
|
|
t.Errorf("got 0x%04x%04x%04x at (%d,%d), want 0x%04x%04x%04x",
|
|
r_, g_, b_, x, y, r, g, b)
|
|
}
|
|
}
|
|
|
|
func waitForFrame(t *testing.T, frameNotifs <-chan bool) {
|
|
// Unfortunately, there isn't a way to select on a test failing, since
|
|
// testing.T doesn't have anything like a context or a "done" channel.
|
|
//
|
|
// We can't let selects block forever, since the default -test.timeout
|
|
// is ten minutes - far too long for tests that take seconds.
|
|
//
|
|
// For now, a static short timeout is better than nothing. 2s is plenty
|
|
// for our simple test app to render on any device.
|
|
|
|
select {
|
|
case <-frameNotifs:
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatalf("timed out waiting for a frame to be ready")
|
|
}
|
|
}
|