mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 15:45:38 +00:00
49000ae4a3
Since Wine is heavily tied to X11, we build its end-to-end test driver on top of X11's. We use the same mechanism to start an X server, take screenshots, and issue clicks. Its only quirk is that it was difficult to get the screenshots to line up with Gio's window. The comments cover what we ended up with. The display dimensions are now part of driverBase, so that methods other than Start can also use them - this is necessary for the wine driver to crop screenshots. We also use a sleep for now; a comment explains why, and a TODO is left for future Dan to deal with. What we have now works, and I've spent enough hours on this patch as it is. Adding Wine to CI, and ensuring that the test passes there, is left for a follow-up patch. Signed-off-by: Daniel Martí <mvdan@mvdan.cc>
151 lines
3.6 KiB
Go
151 lines
3.6 KiB
Go
// SPDX-License-Identifier: Unlicense OR MIT
|
|
|
|
package main_test
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"image"
|
|
"image/png"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
type AndroidTestDriver struct {
|
|
driverBase
|
|
|
|
sdkDir string
|
|
adbPath string
|
|
}
|
|
|
|
var rxAdbDevice = regexp.MustCompile(`(.*)\s+device$`)
|
|
|
|
func (d *AndroidTestDriver) Start(path string) {
|
|
d.sdkDir = os.Getenv("ANDROID_HOME")
|
|
if d.sdkDir == "" {
|
|
d.Skipf("Android SDK is required; set $ANDROID_HOME")
|
|
}
|
|
d.adbPath = filepath.Join(d.sdkDir, "platform-tools", "adb")
|
|
|
|
devOut := bytes.TrimSpace(d.adb("devices"))
|
|
devices := rxAdbDevice.FindAllSubmatch(devOut, -1)
|
|
switch len(devices) {
|
|
case 0:
|
|
d.Skipf("no Android devices attached via adb; skipping")
|
|
case 1:
|
|
default:
|
|
d.Skipf("multiple Android devices attached via adb; skipping")
|
|
}
|
|
|
|
// If the device is attached but asleep, it's probably just charging.
|
|
// Don't use it; the screen needs to be on and unlocked for the test to
|
|
// work.
|
|
if !bytes.Contains(
|
|
d.adb("shell", "dumpsys", "power"),
|
|
[]byte(" mWakefulness=Awake"),
|
|
) {
|
|
d.Skipf("Android device isn't awake; skipping")
|
|
}
|
|
|
|
// First, build the app.
|
|
apk := filepath.Join(d.tempDir("gio-endtoend-android"), "e2e.apk")
|
|
d.gogio("-target=android", "-appid="+appid, "-o="+apk, path)
|
|
|
|
// Make sure the app isn't installed already, and try to uninstall it
|
|
// when we finish. Previous failed test runs might have left the app.
|
|
d.tryUninstall()
|
|
d.adb("install", apk)
|
|
d.Cleanup(d.tryUninstall)
|
|
|
|
// Force our e2e app to be fullscreen, so that the android system bar at
|
|
// the top doesn't mess with our screenshots.
|
|
// TODO(mvdan): is there a way to do this via gio, so that we don't need
|
|
// to set up a global Android setting via the shell?
|
|
d.adb("shell", "settings", "put", "global", "policy_control", "immersive.full="+appid)
|
|
|
|
// Make sure the app isn't already running.
|
|
d.adb("shell", "pm", "clear", appid)
|
|
|
|
// Start listening for log messages.
|
|
{
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cmd := exec.CommandContext(ctx, d.adbPath,
|
|
"logcat",
|
|
"-s", // suppress other logs
|
|
"-T1", // don't show prevoius log messages
|
|
"gio:*", // show all logs from gio
|
|
)
|
|
logcat, err := cmd.StdoutPipe()
|
|
if err != nil {
|
|
d.Fatal(err)
|
|
}
|
|
cmd.Stderr = os.Stderr
|
|
if err := cmd.Start(); err != nil {
|
|
d.Fatal(err)
|
|
}
|
|
d.Cleanup(cancel)
|
|
go func() {
|
|
scanner := bufio.NewScanner(logcat)
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
if strings.HasSuffix(line, ": frame ready") {
|
|
d.frameNotifs <- true
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
// Start the app.
|
|
d.adb("shell", "monkey", "-p", appid, "1")
|
|
|
|
// Wait for the gio app to render.
|
|
d.waitForFrame()
|
|
}
|
|
|
|
func (d *AndroidTestDriver) Screenshot() image.Image {
|
|
out := d.adb("shell", "screencap", "-p")
|
|
img, err := png.Decode(bytes.NewReader(out))
|
|
if err != nil {
|
|
d.Fatal(err)
|
|
}
|
|
return img
|
|
}
|
|
|
|
func (d *AndroidTestDriver) tryUninstall() {
|
|
cmd := exec.Command(d.adbPath, "shell", "pm", "uninstall", appid)
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
if bytes.Contains(out, []byte("Unknown package")) {
|
|
// The package is not installed. Don't log anything.
|
|
return
|
|
}
|
|
d.Logf("could not uninstall: %v\n%s", err, out)
|
|
}
|
|
}
|
|
|
|
func (d *AndroidTestDriver) adb(args ...interface{}) []byte {
|
|
strs := []string{}
|
|
for _, arg := range args {
|
|
strs = append(strs, fmt.Sprint(arg))
|
|
}
|
|
cmd := exec.Command(d.adbPath, strs...)
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
d.Errorf("%s", out)
|
|
d.Fatal(err)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (d *AndroidTestDriver) Click(x, y int) {
|
|
d.adb("shell", "input", "tap", x, y)
|
|
|
|
// Wait for the gio app to render after this click.
|
|
d.waitForFrame()
|
|
}
|