github.com/cybriq/giocore@v0.0.7-0.20210703034601-cfb9cb5f3900/cmd/gogio/android_test.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  package main_test
     4  
     5  import (
     6  	"bytes"
     7  	"context"
     8  	"fmt"
     9  	"image"
    10  	"image/png"
    11  	"os"
    12  	"os/exec"
    13  	"path/filepath"
    14  	"regexp"
    15  )
    16  
    17  type AndroidTestDriver struct {
    18  	driverBase
    19  
    20  	sdkDir  string
    21  	adbPath string
    22  }
    23  
    24  var rxAdbDevice = regexp.MustCompile(`(.*)\s+device$`)
    25  
    26  func (d *AndroidTestDriver) Start(path string) {
    27  	d.sdkDir = os.Getenv("ANDROID_SDK_ROOT")
    28  	if d.sdkDir == "" {
    29  		d.Skipf("Android SDK is required; set $ANDROID_SDK_ROOT")
    30  	}
    31  	d.adbPath = filepath.Join(d.sdkDir, "platform-tools", "adb")
    32  	if _, err := os.Stat(d.adbPath); os.IsNotExist(err) {
    33  		d.Skipf("adb not found")
    34  	}
    35  
    36  	devOut := bytes.TrimSpace(d.adb("devices"))
    37  	devices := rxAdbDevice.FindAllSubmatch(devOut, -1)
    38  	switch len(devices) {
    39  	case 0:
    40  		d.Skipf("no Android devices attached via adb; skipping")
    41  	case 1:
    42  	default:
    43  		d.Skipf("multiple Android devices attached via adb; skipping")
    44  	}
    45  
    46  	// If the device is attached but asleep, it's probably just charging.
    47  	// Don't use it; the screen needs to be on and unlocked for the test to
    48  	// work.
    49  	if !bytes.Contains(
    50  		d.adb("shell", "dumpsys", "power"),
    51  		[]byte(" mWakefulness=Awake"),
    52  	) {
    53  		d.Skipf("Android device isn't awake; skipping")
    54  	}
    55  
    56  	// First, build the app.
    57  	apk := filepath.Join(d.tempDir("gio-endtoend-android"), "e2e.apk")
    58  	d.gogio("-target=android", "-appid="+appid, "-o="+apk, path)
    59  
    60  	// Make sure the app isn't installed already, and try to uninstall it
    61  	// when we finish. Previous failed test runs might have left the app.
    62  	d.tryUninstall()
    63  	d.adb("install", apk)
    64  	d.Cleanup(d.tryUninstall)
    65  
    66  	// Force our e2e app to be fullscreen, so that the android system bar at
    67  	// the top doesn't mess with our screenshots.
    68  	// TODO(mvdan): is there a way to do this via gio, so that we don't need
    69  	// to set up a global Android setting via the shell?
    70  	d.adb("shell", "settings", "put", "global", "policy_control", "immersive.full="+appid)
    71  
    72  	// Make sure the app isn't already running.
    73  	d.adb("shell", "pm", "clear", appid)
    74  
    75  	// Start listening for log messages.
    76  	{
    77  		ctx, cancel := context.WithCancel(context.Background())
    78  		cmd := exec.CommandContext(ctx, d.adbPath,
    79  			"logcat",
    80  			"-s",       // suppress other logs
    81  			"-T1",      // don't show previous log messages
    82  			appid+":*", // show all logs from our gio app ID
    83  		)
    84  		output, err := cmd.StdoutPipe()
    85  		if err != nil {
    86  			d.Fatal(err)
    87  		}
    88  		cmd.Stderr = cmd.Stdout
    89  		d.output = output
    90  		if err := cmd.Start(); err != nil {
    91  			d.Fatal(err)
    92  		}
    93  		d.Cleanup(cancel)
    94  	}
    95  
    96  	// Start the app.
    97  	d.adb("shell", "monkey", "-p", appid, "1")
    98  
    99  	// Wait for the gio app to render.
   100  	d.waitForFrame()
   101  }
   102  
   103  func (d *AndroidTestDriver) Screenshot() image.Image {
   104  	out := d.adb("shell", "screencap", "-p")
   105  	img, err := png.Decode(bytes.NewReader(out))
   106  	if err != nil {
   107  		d.Fatal(err)
   108  	}
   109  	return img
   110  }
   111  
   112  func (d *AndroidTestDriver) tryUninstall() {
   113  	cmd := exec.Command(d.adbPath, "shell", "pm", "uninstall", appid)
   114  	out, err := cmd.CombinedOutput()
   115  	if err != nil {
   116  		if bytes.Contains(out, []byte("Unknown package")) {
   117  			// The package is not installed. Don't log anything.
   118  			return
   119  		}
   120  		d.Logf("could not uninstall: %v\n%s", err, out)
   121  	}
   122  }
   123  
   124  func (d *AndroidTestDriver) adb(args ...interface{}) []byte {
   125  	strs := []string{}
   126  	for _, arg := range args {
   127  		strs = append(strs, fmt.Sprint(arg))
   128  	}
   129  	cmd := exec.Command(d.adbPath, strs...)
   130  	out, err := cmd.CombinedOutput()
   131  	if err != nil {
   132  		d.Errorf("%s", out)
   133  		d.Fatal(err)
   134  	}
   135  	return out
   136  }
   137  
   138  func (d *AndroidTestDriver) Click(x, y int) {
   139  	d.adb("shell", "input", "tap", x, y)
   140  
   141  	// Wait for the gio app to render after this click.
   142  	d.waitForFrame()
   143  }