mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 15:45:38 +00:00
cea8dc374b
Right now it's very similar to the JS test on Chrome. Like it, this one just runs the "red.go" gio app, takes a screenshot, and expects to see red. It also supports the -headless flag; when true, Xvfb is used and it's entirely headless and hidden. Otherwise, Xephyr is used and once can see the test in action. If the tool isn't installed, the test is skipped. We need to add xgb as a dependency, so that we can connect to the X server and interact with it, like taking screenshots. Finally, this is an initial version, and a number of TODOs are left for a later time. They'll get fixed in follow-up patches. While at it, start making all tests parallel, since the end-to-end tests take about a second each and neither are very cpu-intensive. Signed-off-by: Daniel Martí <mvdan@mvdan.cc>
142 lines
3.9 KiB
Go
142 lines
3.9 KiB
Go
// SPDX-License-Identifier: Unlicense OR MIT
|
|
|
|
package main_test
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"flag"
|
|
"image"
|
|
"image/png"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/chromedp/cdproto/runtime"
|
|
"github.com/chromedp/chromedp"
|
|
|
|
_ "gioui.org/unit" // the build tool adds it to go.mod, so keep it there
|
|
)
|
|
|
|
var headless = flag.Bool("headless", true, "run end-to-end tests in headless mode")
|
|
|
|
func TestJSOnChrome(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// First, build the app.
|
|
dir, err := ioutil.TempDir("", "gio-endtoend-js")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(dir)
|
|
|
|
// TODO(mvdan): This is inefficient, as we link the gogio tool every time.
|
|
// Consider options in the future. On the plus side, this is simple.
|
|
cmd := exec.Command("go", "run", ".", "-target=js", "-o="+dir, "testdata/red.go")
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
t.Fatalf("could not build app: %s:\n%s", err, out)
|
|
}
|
|
|
|
// Second, start Chrome.
|
|
opts := append(chromedp.DefaultExecAllocatorOptions[:],
|
|
chromedp.Flag("headless", *headless),
|
|
|
|
// The default would be use-gl=desktop when there's a GPU we can
|
|
// use, falling back to use-gl=swiftshader otherwise or when we
|
|
// are running in headless mode. Swiftshader allows full WebGL
|
|
// support with just a CPU.
|
|
//
|
|
// Unfortunately, many Linux distros like Arch and Alpine
|
|
// package Chromium without Swiftshader, so we can't rely on the
|
|
// defaults above. use-gl=egl works on any machine with a GPU,
|
|
// even if we run Chrome in headless mode, which is OK for now.
|
|
//
|
|
// TODO(mvdan): remove all of this once these issues are fixed:
|
|
//
|
|
// https://bugs.archlinux.org/task/64307
|
|
// https://gitlab.alpinelinux.org/alpine/aports/issues/10920
|
|
chromedp.Flag("use-gl", "egl"),
|
|
)
|
|
|
|
actx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
|
|
defer cancel()
|
|
|
|
ctx, cancel := chromedp.NewContext(actx)
|
|
defer cancel()
|
|
|
|
if err := chromedp.Run(ctx); err != nil {
|
|
if errors.Is(err, exec.ErrNotFound) {
|
|
t.Skipf("test requires Chrome to be installed: %v", err)
|
|
return
|
|
}
|
|
t.Fatal(err)
|
|
}
|
|
chromedp.ListenTarget(ctx, func(ev interface{}) {
|
|
switch ev := ev.(type) {
|
|
case *runtime.EventConsoleAPICalled:
|
|
switch ev.Type {
|
|
case "log", "info", "warning", "error":
|
|
var args strings.Builder
|
|
for i, arg := range ev.Args {
|
|
if i > 0 {
|
|
args.WriteString(", ")
|
|
}
|
|
args.Write(arg.Value)
|
|
}
|
|
t.Logf("console %s: %s", ev.Type, args.String())
|
|
}
|
|
}
|
|
})
|
|
|
|
// Third, serve the app folder, set the browser tab dimensions, and
|
|
// navigate to the folder.
|
|
ts := httptest.NewServer(http.FileServer(http.Dir(dir)))
|
|
defer ts.Close()
|
|
|
|
if err := chromedp.Run(ctx,
|
|
// A small window with 2x HiDPI.
|
|
chromedp.EmulateViewport(300, 300, chromedp.EmulateScale(2.0)),
|
|
chromedp.Navigate(ts.URL),
|
|
); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Finally, run the test.
|
|
|
|
// 1: Once the canvas is ready, grab a screenshot to check that the
|
|
// entirety of the viewport is red, as per the background color.
|
|
var buf []byte
|
|
if err := chromedp.Run(ctx,
|
|
chromedp.WaitReady("canvas", chromedp.ByQuery),
|
|
chromedp.CaptureScreenshot(&buf),
|
|
); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
img, err := png.Decode(bytes.NewReader(buf))
|
|
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, 0xffff, 0x0, 0x0, 0xffff)
|
|
wantColor(t, img, 595, 595, 0xffff, 0x0, 0x0, 0xffff)
|
|
}
|
|
|
|
func wantColor(t *testing.T, img image.Image, x, y int, r, g, b, a uint32) {
|
|
color := img.At(x, y)
|
|
r_, g_, b_, a_ := color.RGBA()
|
|
if r_ != r || g_ != g || b_ != b || a_ != a {
|
|
t.Errorf("got 0x%04x%04x%04x%04x at (%d,%d), want 0x%04x%04x%04x%04x",
|
|
r_, g_, b_, a_, x, y, r, g, b, a)
|
|
}
|
|
}
|