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 }