feat: run Android UI automation with adb by default, add uixt.WithUIA2(true) option to use uiautomator2

This commit is contained in:
debugtalk
2022-12-26 21:57:47 +08:00
parent e26f79e326
commit 64f1a28605
11 changed files with 424 additions and 244 deletions

View File

@@ -1,5 +1,14 @@
# Release History
## v4.3.2 (2022-12-26)
**go version**
- feat: run Android UI automation with adb by default, add `uixt.WithUIA2(true)` option to use uiautomator2
- refactor: remove unused APIs in UI automation
- refactor: convert cases by specifying from/to format
- change: remove traceroute/curl sub commands
## v4.3.1 (2022-12-22)
**go version**

View File

@@ -12,7 +12,7 @@ import (
func TestAndroidDouYinLive(t *testing.T) {
testCase := &hrp.TestCase{
Config: hrp.NewConfig("通过 feed 头像进入抖音直播间").
SetAndroid(uixt.WithAdbLogOn(true)),
SetAndroid(uixt.WithUIA2(false), uixt.WithAdbLogOn(true)),
TestSteps: []hrp.IStep{
hrp.NewStep("启动抖音").
Android().

View File

@@ -1 +1 @@
v4.3.1
v4.3.2

View File

@@ -0,0 +1,337 @@
package uixt
import (
"bytes"
"fmt"
"strconv"
"strings"
"time"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v4/hrp/internal/code"
"github.com/httprunner/httprunner/v4/hrp/pkg/gadb"
)
type adbDriver struct {
Driver
adbClient gadb.Device
logcat *AdbLogcat
}
func NewAdbDriver() *adbDriver {
log.Info().Msg("init adb driver")
return &adbDriver{}
}
func (ad *adbDriver) NewSession(capabilities Capabilities) (sessionInfo SessionInfo, err error) {
err = errDriverNotImplemented
return
}
func (ad *adbDriver) DeleteSession() (err error) {
return errDriverNotImplemented
}
func (ad *adbDriver) Status() (deviceStatus DeviceStatus, err error) {
err = errDriverNotImplemented
return
}
func (ad *adbDriver) DeviceInfo() (deviceInfo DeviceInfo, err error) {
err = errDriverNotImplemented
return
}
func (ad *adbDriver) Location() (location Location, err error) {
err = errDriverNotImplemented
return
}
func (ad *adbDriver) BatteryInfo() (batteryInfo BatteryInfo, err error) {
err = errDriverNotImplemented
return
}
func (ad *adbDriver) WindowSize() (size Size, err error) {
// adb shell wm size
resp, err := ad.adbClient.RunShellCommand("wm", "size")
if err != nil {
return
}
// Physical size: 1080x2340
s := strings.Trim(strings.Split(resp, ": ")[1], "\n")
ss := strings.Split(s, "x")
width, _ := strconv.Atoi(ss[0])
height, _ := strconv.Atoi(ss[1])
size = Size{Width: width, Height: height}
return
}
func (ad *adbDriver) Screen() (screen Screen, err error) {
err = errDriverNotImplemented
return
}
func (ad *adbDriver) Scale() (scale float64, err error) {
return 1, nil
}
// PressBack simulates a short press on the BACK button.
func (ad *adbDriver) PressBack(options ...DataOption) (err error) {
// adb shell input keyevent 4
_, err = ad.adbClient.RunShellCommand("input", "keyevent", fmt.Sprintf("%d", KCBack))
return
}
func (ad *adbDriver) StartCamera() (err error) {
if _, err = ad.adbClient.RunShellCommand("rm", "-r", "/sdcard/DCIM/Camera"); err != nil {
return err
}
time.Sleep(5 * time.Second)
var version string
if version, err = ad.adbClient.RunShellCommand("getprop", "ro.build.version.release"); err != nil {
return err
}
if version == "11" || version == "12" {
if _, err = ad.adbClient.RunShellCommand("am", "start", "-a", "android.media.action.STILL_IMAGE_CAMERA"); err != nil {
return err
}
time.Sleep(5 * time.Second)
if _, err = ad.adbClient.RunShellCommand("input", "swipe", "750", "1000", "250", "1000"); err != nil {
return err
}
time.Sleep(5 * time.Second)
if _, err = ad.adbClient.RunShellCommand("input", "keyevent", fmt.Sprintf("%d", KCCamera)); err != nil {
return err
}
return
} else {
if _, err = ad.adbClient.RunShellCommand("am", "start", "-a", "android.media.action.VIDEO_CAPTURE"); err != nil {
return err
}
time.Sleep(5 * time.Second)
if _, err = ad.adbClient.RunShellCommand("input", "keyevent", fmt.Sprintf("%d", KCCamera)); err != nil {
return err
}
return
}
}
func (ad *adbDriver) StopCamera() (err error) {
err = ad.PressBack()
if err != nil {
return err
}
err = ad.Homescreen()
if err != nil {
return err
}
// kill samsung shell command
if _, err = ad.adbClient.RunShellCommand("am", "force-stop", "com.sec.android.app.camera"); err != nil {
return err
}
// kill other camera (huawei mi)
if _, err = ad.adbClient.RunShellCommand("am", "force-stop", "com.android.camera2"); err != nil {
return err
}
return
}
func (ad *adbDriver) Homescreen() (err error) {
return ad.PressKeyCode(KCHome, KMEmpty)
}
func (ad *adbDriver) PressKeyCode(keyCode KeyCode, metaState KeyMeta) (err error) {
// adb shell input keyevent <keyCode>
_, err = ad.adbClient.RunShellCommand(
"input", "keyevent", fmt.Sprintf("%d", keyCode))
return
}
func (ad *adbDriver) AppLaunch(bundleId string) (err error) {
// 不指定 Activity 名称启动(启动主 Activity
// adb shell monkey -p <packagename> -c android.intent.category.LAUNCHER 1
sOutput, err := ad.adbClient.RunShellCommand(
"monkey", "-p", bundleId, "-c", "android.intent.category.LAUNCHER", "1",
)
if err != nil {
return err
}
if strings.Contains(sOutput, "monkey aborted") {
return fmt.Errorf("app launch: %s", strings.TrimSpace(sOutput))
}
return nil
}
func (ad *adbDriver) AppTerminate(bundleId string) (successful bool, err error) {
// 强制停止应用,停止 <packagename> 相关的进程
// adb shell am force-stop <packagename>
_, err = ad.adbClient.RunShellCommand("am", "force-stop", bundleId)
return err == nil, err
}
func (ad *adbDriver) Tap(x, y int, options ...DataOption) error {
return ad.TapFloat(float64(x), float64(y), options...)
}
func (ad *adbDriver) TapFloat(x, y float64, options ...DataOption) (err error) {
// adb shell input tap x y
_, err = ad.adbClient.RunShellCommand(
"input", "tap", fmt.Sprintf("%.1f", x), fmt.Sprintf("%.1f", y))
return
}
func (ad *adbDriver) DoubleTap(x, y int) error {
return ad.DoubleTapFloat(float64(x), float64(y))
}
func (ad *adbDriver) DoubleTapFloat(x, y float64) (err error) {
err = errDriverNotImplemented
return
}
func (ad *adbDriver) TouchAndHold(x, y int, second ...float64) (err error) {
return ad.TouchAndHoldFloat(float64(x), float64(y), second...)
}
func (ad *adbDriver) TouchAndHoldFloat(x, y float64, second ...float64) (err error) {
err = errDriverNotImplemented
return
}
func (ad *adbDriver) Drag(fromX, fromY, toX, toY int, options ...DataOption) error {
return ad.DragFloat(float64(fromX), float64(fromY), float64(toX), float64(toY), options...)
}
func (ad *adbDriver) DragFloat(fromX, fromY, toX, toY float64, options ...DataOption) (err error) {
err = errDriverNotImplemented
return
}
func (ad *adbDriver) Swipe(fromX, fromY, toX, toY int, options ...DataOption) error {
return ad.SwipeFloat(float64(fromX), float64(fromY), float64(toX), float64(toY), options...)
}
func (ad *adbDriver) SwipeFloat(fromX, fromY, toX, toY float64, options ...DataOption) error {
// adb shell input swipe fromX fromY toX toY
_, err := ad.adbClient.RunShellCommand(
"input", "swipe",
fmt.Sprintf("%.1f", fromX), fmt.Sprintf("%.1f", fromY),
fmt.Sprintf("%.1f", toX), fmt.Sprintf("%.1f", toY),
)
return err
}
func (ad *adbDriver) ForceTouch(x, y int, pressure float64, second ...float64) error {
return ad.ForceTouchFloat(float64(x), float64(y), pressure, second...)
}
func (ad *adbDriver) ForceTouchFloat(x, y, pressure float64, second ...float64) (err error) {
err = errDriverNotImplemented
return
}
func (ad *adbDriver) SetPasteboard(contentType PasteboardType, content string) (err error) {
err = errDriverNotImplemented
return
}
func (ad *adbDriver) GetPasteboard(contentType PasteboardType) (raw *bytes.Buffer, err error) {
err = errDriverNotImplemented
return
}
func (ad *adbDriver) SendKeys(text string, options ...DataOption) (err error) {
err = errDriverNotImplemented
return
}
func (ad *adbDriver) Input(text string, options ...DataOption) (err error) {
return ad.SendKeys(text, options...)
}
func (ad *adbDriver) PressButton(devBtn DeviceButton) (err error) {
err = errDriverNotImplemented
return
}
func (ad *adbDriver) Rotation() (rotation Rotation, err error) {
err = errDriverNotImplemented
return
}
func (ad *adbDriver) SetRotation(rotation Rotation) (err error) {
err = errDriverNotImplemented
return
}
func (ad *adbDriver) Screenshot() (raw *bytes.Buffer, err error) {
// adb shell screencap -p
resp, err := ad.adbClient.RunShellCommandWithBytes(
"screencap", "-p",
)
if err == nil {
return bytes.NewBuffer(resp), nil
}
return nil, err
}
func (ad *adbDriver) Source(srcOpt ...SourceOption) (source string, err error) {
err = errDriverNotImplemented
return
}
func (ad *adbDriver) AccessibleSource() (source string, err error) {
err = errDriverNotImplemented
return
}
func (ad *adbDriver) HealthCheck() (err error) {
err = errDriverNotImplemented
return
}
func (ad *adbDriver) GetAppiumSettings() (settings map[string]interface{}, err error) {
err = errDriverNotImplemented
return
}
func (ad *adbDriver) SetAppiumSettings(settings map[string]interface{}) (ret map[string]interface{}, err error) {
err = errDriverNotImplemented
return
}
func (ad *adbDriver) IsHealthy() (healthy bool, err error) {
err = errDriverNotImplemented
return
}
func (ad *adbDriver) StartCaptureLog(identifier ...string) (err error) {
log.Info().Msg("start adb log recording")
err = ad.logcat.CatchLogcat()
if err != nil {
err = errors.Wrap(code.AndroidCaptureLogError,
fmt.Sprintf("start adb log recording failed: %v", err))
return err
}
return nil
}
func (ad *adbDriver) StopCaptureLog() (result interface{}, err error) {
log.Info().Msg("stop adb log recording")
err = ad.logcat.Stop()
if err != nil {
log.Error().Err(err).Msg("failed to get adb log recording")
err = errors.Wrap(code.AndroidCaptureLogError,
fmt.Sprintf("get adb log recording failed: %v", err))
return "", err
}
content := ad.logcat.logBuffer.String()
return ConvertPoints(content), nil
}

View File

@@ -35,6 +35,12 @@ func WithSerialNumber(serial string) AndroidDeviceOption {
}
}
func WithUIA2(uia2On bool) AndroidDeviceOption {
return func(device *AndroidDevice) {
device.UIA2 = uia2On
}
}
func WithUIA2IP(ip string) AndroidDeviceOption {
return func(device *AndroidDevice) {
device.UIA2IP = ip
@@ -57,6 +63,9 @@ func GetAndroidDeviceOptions(dev *AndroidDevice) (deviceOptions []AndroidDeviceO
if dev.SerialNumber != "" {
deviceOptions = append(deviceOptions, WithSerialNumber(dev.SerialNumber))
}
if dev.UIA2 {
deviceOptions = append(deviceOptions, WithUIA2(true))
}
if dev.UIA2IP != "" {
deviceOptions = append(deviceOptions, WithUIA2IP(dev.UIA2IP))
}
@@ -119,6 +128,7 @@ type AndroidDevice struct {
d gadb.Device
logcat *AdbLogcat
SerialNumber string `json:"serial,omitempty" yaml:"serial,omitempty"`
UIA2 bool `json:"uia2,omitempty" yaml:"uia2,omitempty"` // use uiautomator2
UIA2IP string `json:"uia2_ip,omitempty" yaml:"uia2_ip,omitempty"` // uiautomator2 server ip
UIA2Port int `json:"uia2_port,omitempty" yaml:"uia2_port,omitempty"` // uiautomator2 server port
LogOn bool `json:"log_on,omitempty" yaml:"log_on,omitempty"`
@@ -129,7 +139,12 @@ func (dev *AndroidDevice) UUID() string {
}
func (dev *AndroidDevice) NewDriver(capabilities Capabilities) (driverExt *DriverExt, err error) {
driver, err := dev.NewUSBDriver(capabilities)
var driver WebDriver
if dev.UIA2 {
driver, err = dev.NewUSBDriver(capabilities)
} else {
driver, err = dev.NewAdbDriver()
}
if err != nil {
return nil, errors.Wrap(err, "failed to init UIA driver")
}
@@ -155,8 +170,7 @@ func (dev *AndroidDevice) NewDriver(capabilities Capabilities) (driverExt *Drive
}
// NewUSBDriver creates new client via USB connected device, this will also start a new session.
// TODO: replace uiaDriver with WebDriver
func (dev *AndroidDevice) NewUSBDriver(capabilities Capabilities) (driver *uiaDriver, err error) {
func (dev *AndroidDevice) NewUSBDriver(capabilities Capabilities) (driver WebDriver, err error) {
var localPort int
if localPort, err = getFreePort(); err != nil {
return nil, errors.Wrap(code.AndroidDeviceUSBDriverError,
@@ -168,27 +182,37 @@ func (dev *AndroidDevice) NewUSBDriver(capabilities Capabilities) (driver *uiaDr
localPort, UIA2ServerPort, err))
}
rawURL := fmt.Sprintf("http://%s%d:6790/wd/hub", forwardToPrefix, localPort)
driver, err = NewUIADriver(capabilities, rawURL)
rawURL := fmt.Sprintf("http://%s%d:%d/wd/hub",
forwardToPrefix, localPort, UIA2ServerPort)
uiaDriver, err := NewUIADriver(capabilities, rawURL)
if err != nil {
_ = dev.d.ForwardKill(localPort)
return nil, errors.Wrap(code.AndroidDeviceUSBDriverError, err.Error())
}
driver.adbClient = dev.d
driver.logcat = dev.logcat
uiaDriver.adbClient = dev.d
uiaDriver.logcat = dev.logcat
return driver, nil
return uiaDriver, nil
}
// NewHTTPDriver creates new remote HTTP client, this will also start a new session.
// TODO: replace uiaDriver with WebDriver
func (dev *AndroidDevice) NewHTTPDriver(capabilities Capabilities) (driver *uiaDriver, err error) {
func (dev *AndroidDevice) NewHTTPDriver(capabilities Capabilities) (driver WebDriver, err error) {
rawURL := fmt.Sprintf("http://%s:%d/wd/hub", dev.UIA2IP, dev.UIA2Port)
if driver, err = NewUIADriver(capabilities, rawURL); err != nil {
uiaDriver, err := NewUIADriver(capabilities, rawURL)
if err != nil {
return nil, err
}
driver.adbClient = dev.d
return driver, nil
uiaDriver.adbClient = dev.d
uiaDriver.logcat = dev.logcat
return uiaDriver, nil
}
func (dev *AndroidDevice) NewAdbDriver() (driver WebDriver, err error) {
adbDriver := NewAdbDriver()
adbDriver.adbClient = dev.d
adbDriver.logcat = dev.logcat
return adbDriver, nil
}
func (dev *AndroidDevice) StartPerf() error {

View File

@@ -1,5 +1,7 @@
package uixt
// See https://developer.android.com/reference/android/view/KeyEvent
type KeyMeta int
const (

View File

@@ -49,7 +49,7 @@ func TestDriver_Quit(t *testing.T) {
t.Fatal(err)
}
if err = driver.Close(); err != nil {
if err = driver.DeleteSession(); err != nil {
t.Fatal(err)
}
}
@@ -335,17 +335,17 @@ func TestDeviceList(t *testing.T) {
func TestDriver_AppLaunch(t *testing.T) {
device, _ := NewAndroidDevice()
driver, err := device.NewUSBDriver(nil)
driver, err := device.NewDriver(nil)
if err != nil {
t.Fatal(err)
}
err = driver.AppLaunch("com.android.settings")
err = driver.Driver.AppLaunch("com.android.settings")
if err != nil {
t.Fatal(err)
}
raw, err := driver.Screenshot()
raw, err := driver.Driver.Screenshot()
if err != nil {
t.Fatal(err)
}
@@ -355,24 +355,24 @@ func TestDriver_AppLaunch(t *testing.T) {
func TestDriver_KeepAlive(t *testing.T) {
device, _ := NewAndroidDevice()
driver, err := device.NewUSBDriver(nil)
driver, err := device.NewDriver(nil)
if err != nil {
t.Fatal(err)
}
err = driver.AppLaunch("com.android.settings")
err = driver.Driver.AppLaunch("com.android.settings")
if err != nil {
t.Fatal(err)
}
_, err = driver.Screenshot()
_, err = driver.Driver.Screenshot()
if err != nil {
t.Fatal(err)
}
time.Sleep(60 * time.Second)
_, err = driver.Screenshot()
_, err = driver.Driver.Screenshot()
if err != nil {
t.Fatal(err)
}
@@ -380,12 +380,12 @@ func TestDriver_KeepAlive(t *testing.T) {
func TestDriver_AppTerminate(t *testing.T) {
device, _ := NewAndroidDevice()
driver, err := device.NewUSBDriver(nil)
driver, err := device.NewDriver(nil)
if err != nil {
t.Fatal(err)
}
_, err = driver.AppTerminate("tv.danmaku.bili")
_, err = driver.Driver.AppTerminate("tv.danmaku.bili")
if err != nil {
t.Fatal(err)
}

View File

@@ -9,36 +9,21 @@ import (
"net/url"
"strconv"
"strings"
"time"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v4/hrp/internal/code"
"github.com/httprunner/httprunner/v4/hrp/pkg/gadb"
)
// See https://developer.android.com/reference/android/view/KeyEvent
const (
KEYCODE_BACK string = "4"
KEYCODE_CAMERA string = "27"
KEYCODE_ALT_LEFT string = "57"
KEYCODE_ALT_RIGHT string = "58"
KEYCODE_MENU string = "82"
KEYCODE_BREAK string = "121"
KEYCODE_ALL_APPS string = "284"
)
var errDriverNotImplemented = errors.New("driver method not implemented")
type uiaDriver struct {
Driver
adbClient gadb.Device
logcat *AdbLogcat
adbDriver
}
func NewUIADriver(capabilities Capabilities, urlPrefix string) (driver *uiaDriver, err error) {
log.Info().Msg("init uiautomator2 driver")
if capabilities == nil {
capabilities = NewCapabilities()
}
@@ -98,17 +83,6 @@ func (bs BatteryStatus) String() string {
}
}
func (ud *uiaDriver) Close() (err error) {
if ud.sessionId == "" {
return nil
}
if _, err = ud.httpDELETE("/session", ud.sessionId); err == nil {
ud.sessionId = ""
}
return err
}
func (ud *uiaDriver) NewSession(capabilities Capabilities) (sessionInfo SessionInfo, err error) {
// register(postHandler, new NewSession("/wd/hub/session"))
var rawResp rawResponse
@@ -126,8 +100,14 @@ func (ud *uiaDriver) NewSession(capabilities Capabilities) (sessionInfo SessionI
}
func (ud *uiaDriver) DeleteSession() (err error) {
// TODO
return errDriverNotImplemented
if ud.sessionId == "" {
return nil
}
if _, err = ud.httpDELETE("/session", ud.sessionId); err == nil {
ud.sessionId = ""
}
return err
}
func (ud *uiaDriver) Status() (deviceStatus DeviceStatus, err error) {
@@ -185,18 +165,6 @@ func (ud *uiaDriver) BatteryInfo() (batteryInfo BatteryInfo, err error) {
}
func (ud *uiaDriver) WindowSize() (size Size, err error) {
// adb shell wm size
resp, err := ud.adbClient.RunShellCommand("wm", "size")
if err == nil {
// Physical size: 1080x2340
s := strings.Trim(strings.Split(resp, ": ")[1], "\n")
ss := strings.Split(s, "x")
width, _ := strconv.Atoi(ss[0])
height, _ := strconv.Atoi(ss[1])
size = Size{Width: width, Height: height}
return
}
// register(getHandler, new GetDeviceSize("/wd/hub/session/:sessionId/window/:windowHandle/size"))
var rawResp rawResponse
if rawResp, err = ud.httpGET("/session", ud.sessionId, "window/:windowHandle/size"); err != nil {
@@ -221,84 +189,16 @@ func (ud *uiaDriver) Scale() (scale float64, err error) {
// PressBack simulates a short press on the BACK button.
func (ud *uiaDriver) PressBack(options ...DataOption) (err error) {
// adb shell input keyevent 4
_, err = ud.adbClient.RunShellCommand("input", "keyevent", KEYCODE_BACK)
if err == nil {
return nil
}
// register(postHandler, new PressBack("/wd/hub/session/:sessionId/back"))
_, err = ud.httpPOST(nil, "/session", ud.sessionId, "back")
return
}
func (ud *uiaDriver) StartCamera() (err error) {
if _, err = ud.adbClient.RunShellCommand("rm", "-r", "/sdcard/DCIM/Camera"); err != nil {
return err
}
time.Sleep(5 * time.Second)
var version string
if version, err = ud.adbClient.RunShellCommand("getprop", "ro.build.version.release"); err != nil {
return err
}
if version == "11" || version == "12" {
if _, err = ud.adbClient.RunShellCommand("am", "start", "-a", "android.media.action.STILL_IMAGE_CAMERA"); err != nil {
return err
}
time.Sleep(5 * time.Second)
if _, err = ud.adbClient.RunShellCommand("input", "swipe", "750", "1000", "250", "1000"); err != nil {
return err
}
time.Sleep(5 * time.Second)
if _, err = ud.adbClient.RunShellCommand("input", "keyevent", KEYCODE_CAMERA); err != nil {
return err
}
return
} else {
if _, err = ud.adbClient.RunShellCommand("am", "start", "-a", "android.media.action.VIDEO_CAPTURE"); err != nil {
return err
}
time.Sleep(5 * time.Second)
if _, err = ud.adbClient.RunShellCommand("input", "keyevent", KEYCODE_CAMERA); err != nil {
return err
}
return
}
}
func (ud *uiaDriver) StopCamera() (err error) {
err = ud.PressBack()
if err != nil {
return err
}
err = ud.Homescreen()
if err != nil {
return err
}
// kill samsung shell command
if _, err = ud.adbClient.RunShellCommand("am", "force-stop", "com.sec.android.app.camera"); err != nil {
return err
}
// kill other camera (huawei mi)
if _, err = ud.adbClient.RunShellCommand("am", "force-stop", "com.android.camera2"); err != nil {
return err
}
return
}
func (ud *uiaDriver) Homescreen() (err error) {
return ud.PressKeyCode(KCHome, KMEmpty)
}
func (ud *uiaDriver) PressKeyCode(keyCode KeyCode, metaState KeyMeta, flags ...KeyFlag) (err error) {
// adb shell input keyevent <keyCode>
_, err = ud.adbClient.RunShellCommand(
"input", "keyevent", fmt.Sprintf("%d", keyCode))
if err == nil {
return nil
}
// register(postHandler, new PressKeyCodeAsync("/wd/hub/session/:sessionId/appium/device/press_keycode"))
data := map[string]interface{}{
"keycode": keyCode,
@@ -313,40 +213,11 @@ func (ud *uiaDriver) PressKeyCode(keyCode KeyCode, metaState KeyMeta, flags ...K
return
}
func (ud *uiaDriver) AppLaunch(bundleId string) (err error) {
// 不指定 Activity 名称启动(启动主 Activity
// adb shell monkey -p <packagename> -c android.intent.category.LAUNCHER 1
sOutput, err := ud.adbClient.RunShellCommand(
"monkey", "-p", bundleId, "-c", "android.intent.category.LAUNCHER", "1",
)
if err != nil {
return err
}
if strings.Contains(sOutput, "monkey aborted") {
return fmt.Errorf("app launch: %s", strings.TrimSpace(sOutput))
}
return nil
}
func (ud *uiaDriver) AppTerminate(bundleId string) (successful bool, err error) {
// 强制停止应用,停止 <packagename> 相关的进程
// adb shell am force-stop <packagename>
_, err = ud.adbClient.RunShellCommand("am", "force-stop", bundleId)
return err == nil, err
}
func (ud *uiaDriver) Tap(x, y int, options ...DataOption) error {
return ud.TapFloat(float64(x), float64(y), options...)
}
func (ud *uiaDriver) TapFloat(x, y float64, options ...DataOption) (err error) {
// adb shell input tap x y
_, err = ud.adbClient.RunShellCommand(
"input", "tap", fmt.Sprintf("%.1f", x), fmt.Sprintf("%.1f", y))
if err == nil {
return nil
}
// register(postHandler, new Tap("/wd/hub/session/:sessionId/appium/tap"))
data := map[string]interface{}{
"x": x,
@@ -421,16 +292,6 @@ func (ud *uiaDriver) Swipe(fromX, fromY, toX, toY int, options ...DataOption) er
}
func (ud *uiaDriver) SwipeFloat(fromX, fromY, toX, toY float64, options ...DataOption) error {
// adb shell input swipe fromX fromY toX toY
_, err := ud.adbClient.RunShellCommand(
"input", "swipe",
fmt.Sprintf("%.1f", fromX), fmt.Sprintf("%.1f", fromY),
fmt.Sprintf("%.1f", toX), fmt.Sprintf("%.1f", toY),
)
if err == nil {
return nil
}
// register(postHandler, new Swipe("/wd/hub/session/:sessionId/touch/perform"))
data := map[string]interface{}{
"startX": fromX,
@@ -442,7 +303,7 @@ func (ud *uiaDriver) SwipeFloat(fromX, fromY, toX, toY float64, options ...DataO
// new data options in post data for extra uiautomator configurations
newData := NewData(data, options...)
_, err = ud.httpPOST(newData, "/session", ud.sessionId, "touch/perform")
_, err := ud.httpPOST(newData, "/session", ud.sessionId, "touch/perform")
return err
}
@@ -623,53 +484,3 @@ func (ud *uiaDriver) IsHealthy() (healthy bool, err error) {
// TODO
return healthy, errDriverNotImplemented
}
func (ud *uiaDriver) WaitWithTimeoutAndInterval(condition Condition, timeout, interval time.Duration) error {
startTime := time.Now()
for {
done, err := condition(ud)
if err != nil {
return err
}
if done {
return nil
}
if elapsed := time.Since(startTime); elapsed > timeout {
return fmt.Errorf("timeout after %v", elapsed)
}
time.Sleep(interval)
}
}
func (ud *uiaDriver) WaitWithTimeout(condition Condition, timeout time.Duration) error {
return ud.WaitWithTimeoutAndInterval(condition, timeout, DefaultWaitInterval)
}
func (ud *uiaDriver) Wait(condition Condition) error {
return ud.WaitWithTimeoutAndInterval(condition, DefaultWaitTimeout, DefaultWaitInterval)
}
func (ud *uiaDriver) StartCaptureLog(identifier ...string) (err error) {
log.Info().Msg("start adb log recording")
err = ud.logcat.CatchLogcat()
if err != nil {
err = errors.Wrap(code.AndroidCaptureLogError,
fmt.Sprintf("start adb log recording failed: %v", err))
return err
}
return nil
}
func (ud *uiaDriver) StopCaptureLog() (result interface{}, err error) {
log.Info().Msg("stop adb log recording")
err = ud.logcat.Stop()
if err != nil {
log.Error().Err(err).Msg("failed to get adb log recording")
err = errors.Wrap(code.AndroidCaptureLogError,
fmt.Sprintf("get adb log recording failed: %v", err))
return "", err
}
content := ud.logcat.logBuffer.String()
return ConvertPoints(content), nil
}

View File

@@ -695,9 +695,6 @@ type WebDriver interface {
IsHealthy() (bool, error)
// Close inner connections properly
Close() error
// triggers the log capture and returns the log entries
StartCaptureLog(identifier ...string) (err error)
StopCaptureLog() (result interface{}, err error)

View File

@@ -35,20 +35,6 @@ func (wd *wdaDriver) GetMjpegClient() *http.Client {
return wd.mjpegClient
}
func (wd *wdaDriver) Close() error {
if wd.defaultConn != nil {
wd.defaultConn.Close()
}
if wd.mjpegUSBConn != nil {
wd.mjpegUSBConn.Close()
}
if wd.mjpegClient != nil {
wd.mjpegClient.CloseIdleConnections()
}
return wd.mjpegHTTPConn.Close()
}
func (wd *wdaDriver) NewSession(capabilities Capabilities) (sessionInfo SessionInfo, err error) {
// [[FBRoute POST:@"/session"].withoutSession respondWithTarget:self action:@selector(handleCreateSession:)]
data := make(map[string]interface{})
@@ -70,6 +56,20 @@ func (wd *wdaDriver) NewSession(capabilities Capabilities) (sessionInfo SessionI
}
func (wd *wdaDriver) DeleteSession() (err error) {
if wd.defaultConn != nil {
wd.defaultConn.Close()
}
if wd.mjpegUSBConn != nil {
wd.mjpegUSBConn.Close()
}
if wd.mjpegClient != nil {
wd.mjpegClient.CloseIdleConnections()
}
if wd.mjpegHTTPConn != nil {
wd.mjpegHTTPConn.Close()
}
// [[FBRoute DELETE:@""] respondWithTarget:self action:@selector(handleDeleteSession:)]
_, err = wd.httpDELETE("/session", wd.sessionId)
return

View File

@@ -24,12 +24,12 @@ func checkOCR(buff *bytes.Buffer) error {
func TestOCRWithScreenshot(t *testing.T) {
device, _ := NewAndroidDevice()
driver, err := device.NewUSBDriver(nil)
driver, err := device.NewDriver(nil)
if err != nil {
t.Fatal(err)
}
raw, err := driver.Screenshot()
raw, err := driver.Driver.Screenshot()
if err != nil {
t.Fatal(err)
}