~eliasnaur/gio

ref: 6a9a87046221602d1a323887cd619e75c81cc24e gio/cmd/gogio/android_test.go -rw-r--r-- 3.4 KiB
6a9a8704Sebastien Binet app{,/internal/window}: make app.Main blocking on desktop platforms 2 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// SPDX-License-Identifier: Unlicense OR MIT

package main_test

import (
	"bytes"
	"context"
	"fmt"
	"image"
	"image/png"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
)

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
			appid+":*", // show all logs from our gio app ID
		)
		output, err := cmd.StdoutPipe()
		if err != nil {
			d.Fatal(err)
		}
		cmd.Stderr = cmd.Stdout
		d.output = output
		if err := cmd.Start(); err != nil {
			d.Fatal(err)
		}
		d.Cleanup(cancel)
	}

	// 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()
}