From 4e9e5a3124c7ab2b6d1b100175b1642fbc8bb6d9 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 26 Dec 2022 16:49:34 +0800 Subject: [PATCH 1/6] change: replace gadb log --- hrp/pkg/gadb/client.go | 10 +++++-- hrp/pkg/gadb/client_test.go | 18 ------------ hrp/pkg/gadb/device_test.go | 18 ------------ hrp/pkg/gadb/gadb.go | 17 ------------ hrp/pkg/gadb/sync_transport.go | 50 ++++++++++++++++++---------------- hrp/pkg/gadb/transport.go | 9 +++--- hrp/pkg/gadb/transport_test.go | 2 -- 7 files changed, 37 insertions(+), 87 deletions(-) delete mode 100644 hrp/pkg/gadb/gadb.go diff --git a/hrp/pkg/gadb/client.go b/hrp/pkg/gadb/client.go index 951f13b1..4543484a 100644 --- a/hrp/pkg/gadb/client.go +++ b/hrp/pkg/gadb/client.go @@ -4,10 +4,14 @@ import ( "fmt" "strconv" "strings" + + "github.com/rs/zerolog/log" ) -const AdbServerPort = 5037 -const AdbDaemonPort = 5555 +const ( + AdbServerPort = 5037 + AdbDaemonPort = 5555 +) type Client struct { host string @@ -86,7 +90,7 @@ func (c Client) DeviceList() (devices []Device, err error) { fields := strings.Fields(line) if len(fields) < 5 || len(fields[0]) == 0 { - debugLog(fmt.Sprintf("can't parse: %s", line)) + log.Error().Str("line", line).Msg("get unexpected line") continue } diff --git a/hrp/pkg/gadb/client_test.go b/hrp/pkg/gadb/client_test.go index 61b613b2..404af6a8 100644 --- a/hrp/pkg/gadb/client_test.go +++ b/hrp/pkg/gadb/client_test.go @@ -7,8 +7,6 @@ import ( ) func TestClient_ServerVersion(t *testing.T) { - SetDebug(true) - adbClient, err := NewClient() if err != nil { t.Fatal(err) @@ -23,8 +21,6 @@ func TestClient_ServerVersion(t *testing.T) { } func TestClient_DeviceSerialList(t *testing.T) { - SetDebug(true) - adbClient, err := NewClient() if err != nil { t.Fatal(err) @@ -41,8 +37,6 @@ func TestClient_DeviceSerialList(t *testing.T) { } func TestClient_DeviceList(t *testing.T) { - SetDebug(true) - adbClient, err := NewClient() if err != nil { t.Fatal(err) @@ -59,8 +53,6 @@ func TestClient_DeviceList(t *testing.T) { } func TestClient_ForwardList(t *testing.T) { - SetDebug(true) - adbClient, err := NewClient() if err != nil { t.Fatal(err) @@ -77,8 +69,6 @@ func TestClient_ForwardList(t *testing.T) { } func TestClient_ForwardKillAll(t *testing.T) { - SetDebug(true) - adbClient, err := NewClient() if err != nil { t.Fatal(err) @@ -96,8 +86,6 @@ func TestClient_Connect(t *testing.T) { t.Fatal(err) } - SetDebug(true) - err = adbClient.Connect("192.168.1.28") if err != nil { t.Fatal(err) @@ -110,8 +98,6 @@ func TestClient_Disconnect(t *testing.T) { t.Fatal(err) } - SetDebug(true) - err = adbClient.Disconnect("192.168.1.28") if err != nil { t.Fatal(err) @@ -124,8 +110,6 @@ func TestClient_DisconnectAll(t *testing.T) { t.Fatal(err) } - SetDebug(true) - err = adbClient.DisconnectAll() if err != nil { t.Fatal(err) @@ -133,8 +117,6 @@ func TestClient_DisconnectAll(t *testing.T) { } func TestClient_KillServer(t *testing.T) { - SetDebug(true) - adbClient, err := NewClient() if err != nil { t.Fatal(err) diff --git a/hrp/pkg/gadb/device_test.go b/hrp/pkg/gadb/device_test.go index 7df8ef08..70ae4889 100644 --- a/hrp/pkg/gadb/device_test.go +++ b/hrp/pkg/gadb/device_test.go @@ -103,7 +103,6 @@ func TestDevice_Usb(t *testing.T) { dev := devices[i] t.Log(dev.Serial(), dev.Usb(), dev.IsUsb()) } - } func TestDevice_DeviceInfo(t *testing.T) { @@ -134,8 +133,6 @@ func TestDevice_Forward(t *testing.T) { t.Fatal(err) } - SetDebug(true) - localPort := 61000 err = devices[0].Forward(localPort, 6790) if err != nil { @@ -159,8 +156,6 @@ func TestDevice_ForwardList(t *testing.T) { t.Fatal(err) } - SetDebug(true) - for i := range devices { dev := devices[i] forwardList, err := dev.ForwardList() @@ -182,8 +177,6 @@ func TestDevice_ForwardKill(t *testing.T) { t.Fatal(err) } - SetDebug(true) - err = devices[0].ForwardKill(6790) if err != nil { t.Fatal(err) @@ -213,8 +206,6 @@ func TestDevice_RunShellCommand(t *testing.T) { // t.Log("\n"+dev.serial, cmdOutput) // } - // SetDebug(true) - dev := devices[len(devices)-1] dev = devices[0] @@ -225,7 +216,6 @@ func TestDevice_RunShellCommand(t *testing.T) { t.Fatal(dev.serial, err) } t.Log("\n⬇️"+dev.serial+"⬇️\n", cmdOutput) - } func TestDevice_EnableAdbOverTCP(t *testing.T) { @@ -242,8 +232,6 @@ func TestDevice_EnableAdbOverTCP(t *testing.T) { dev := devices[len(devices)-1] dev = devices[0] - SetDebug(true) - err = dev.EnableAdbOverTCP() if err != nil { t.Fatal(err) @@ -264,8 +252,6 @@ func TestDevice_List(t *testing.T) { dev := devices[len(devices)-1] dev = devices[0] - SetDebug(true) - // fileEntries, err := dev.List("/sdcard") fileEntries, err := dev.List("/sdcard/Download") if err != nil { @@ -291,8 +277,6 @@ func TestDevice_Push(t *testing.T) { dev := devices[len(devices)-1] dev = devices[0] - SetDebug(true) - file, _ := os.Open("/Users/hero/Documents/temp/MuMu共享文件夹/test.txt") err = dev.PushFile(file, "/sdcard/Download/push.txt", time.Now()) if err != nil { @@ -319,8 +303,6 @@ func TestDevice_Pull(t *testing.T) { dev := devices[len(devices)-1] dev = devices[0] - SetDebug(true) - buffer := bytes.NewBufferString("") err = dev.Pull("/sdcard/Download/hello.txt", buffer) if err != nil { diff --git a/hrp/pkg/gadb/gadb.go b/hrp/pkg/gadb/gadb.go deleted file mode 100644 index 14b224ee..00000000 --- a/hrp/pkg/gadb/gadb.go +++ /dev/null @@ -1,17 +0,0 @@ -package gadb - -import "log" - -var debugFlag = false - -// SetDebug set debug mode -func SetDebug(debug bool) { - debugFlag = debug -} - -func debugLog(msg string) { - if !debugFlag { - return - } - log.Println("[DEBUG] [gadb] " + msg) -} diff --git a/hrp/pkg/gadb/sync_transport.go b/hrp/pkg/gadb/sync_transport.go index 5eceee95..6e55df6b 100644 --- a/hrp/pkg/gadb/sync_transport.go +++ b/hrp/pkg/gadb/sync_transport.go @@ -8,6 +8,8 @@ import ( "io" "net" "time" + + "github.com/rs/zerolog/log" ) type syncTransport struct { @@ -29,7 +31,7 @@ func (sync syncTransport) Send(command, data string) (err error) { } msg.WriteString(data) - debugLog(fmt.Sprintf("--> %s", msg.String())) + log.Debug().Str("msg", msg.String()).Msg("sync run adb command") return _send(sync.sock, msg.Bytes()) } @@ -56,7 +58,7 @@ func (sync syncTransport) SendStatus(statusCode string, n uint32) (err error) { if err = binary.Write(msg, binary.LittleEndian, n); err != nil { return fmt.Errorf("sync transport write: %w", err) } - debugLog(fmt.Sprintf("--> %s", msg.String())) + log.Debug().Str("msg", msg.String()).Msg("sync send adb status") return _send(sync.sock, msg.Bytes()) } @@ -65,7 +67,6 @@ func (sync syncTransport) sendChunk(buffer []byte) (err error) { if err = binary.Write(msg, binary.LittleEndian, int32(len(buffer))); err != nil { return fmt.Errorf("sync transport write: %w", err) } - debugLog(fmt.Sprintf("--> %s ......", msg.String())) msg.Write(buffer) return _send(sync.sock, msg.Bytes()) } @@ -76,22 +77,22 @@ func (sync syncTransport) VerifyStatus() (err error) { return err } - log := bytes.NewBufferString(fmt.Sprintf("<-- %s", status)) + logs := bytes.NewBufferString(fmt.Sprintf("<-- %s", status)) defer func() { - debugLog(log.String()) + fmt.Println(logs.String()) }() var tmpUint32 uint32 if tmpUint32, err = sync.ReadUint32(); err != nil { return fmt.Errorf("sync transport read (status): %w", err) } - log.WriteString(fmt.Sprintf(" %d\t", tmpUint32)) + logs.WriteString(fmt.Sprintf(" %d\t", tmpUint32)) var msg string if msg, err = sync.ReadStringN(int(tmpUint32)); err != nil { return err } - log.WriteString(msg) + logs.WriteString(msg) if status == "FAIL" { err = fmt.Errorf("sync verify status (fail): %s", msg) @@ -139,8 +140,10 @@ func (sync syncTransport) readChunk() (chunk []byte, err error) { return nil, err } - log := bytes.NewBufferString("") - defer func() { debugLog(log.String()) }() + logs := bytes.NewBufferString("") + defer func() { + fmt.Println(logs.String()) + }() var tmpUint32 uint32 if tmpUint32, err = sync.ReadUint32(); err != nil { @@ -148,35 +151,34 @@ func (sync syncTransport) readChunk() (chunk []byte, err error) { } if status == "FAIL" { - log.WriteString(fmt.Sprintf("<-- %s\t%d\t", status, tmpUint32)) + logs.WriteString(fmt.Sprintf("<-- %s\t%d\t", status, tmpUint32)) var sError string if sError, err = sync.ReadStringN(int(tmpUint32)); err != nil { return nil, fmt.Errorf("read chunk (error message): %w", err) } err = fmt.Errorf("status (fail): %s", sError) - log.WriteString(sError) + logs.WriteString(sError) return } switch status { case "DONE": - log.WriteString(fmt.Sprintf("<-- %s", status)) + logs.WriteString(fmt.Sprintf("<-- %s", status)) err = syncReadChunkDone return case "DATA": - log.WriteString(fmt.Sprintf("<-- %s\t%d\t", status, tmpUint32)) + logs.WriteString(fmt.Sprintf("<-- %s\t%d\t", status, tmpUint32)) if chunk, err = sync.ReadBytesN(int(tmpUint32)); err != nil { return nil, err } default: - log.WriteString(fmt.Sprintf("<-- %s\t%d\t", status, tmpUint32)) + logs.WriteString(fmt.Sprintf("<-- %s\t%d\t", status, tmpUint32)) err = errors.New("unknown error") } - log.WriteString("......") + logs.WriteString("......") return - } func (sync syncTransport) ReadDirectoryEntry() (entry DeviceFileInfo, err error) { @@ -185,43 +187,43 @@ func (sync syncTransport) ReadDirectoryEntry() (entry DeviceFileInfo, err error) return DeviceFileInfo{}, err } - log := bytes.NewBufferString(fmt.Sprintf("<-- %s", status)) + logs := bytes.NewBufferString(fmt.Sprintf("<-- %s", status)) defer func() { - debugLog(log.String()) + fmt.Println(logs.String()) }() if status == "DONE" { return } - log = bytes.NewBufferString(fmt.Sprintf("<-- %s\t", status)) + logs = bytes.NewBufferString(fmt.Sprintf("<-- %s\t", status)) if err = binary.Read(sync.sock, binary.LittleEndian, &entry.Mode); err != nil { return DeviceFileInfo{}, fmt.Errorf("sync transport read (mode): %w", err) } - log.WriteString(entry.Mode.String() + "\t") + logs.WriteString(entry.Mode.String() + "\t") if entry.Size, err = sync.ReadUint32(); err != nil { return DeviceFileInfo{}, fmt.Errorf("sync transport read (size): %w", err) } - log.WriteString(fmt.Sprintf("%10d", entry.Size) + "\t") + logs.WriteString(fmt.Sprintf("%10d", entry.Size) + "\t") var tmpUint32 uint32 if tmpUint32, err = sync.ReadUint32(); err != nil { return DeviceFileInfo{}, fmt.Errorf("sync transport read (time): %w", err) } entry.LastModified = time.Unix(int64(tmpUint32), 0) - log.WriteString(entry.LastModified.String() + "\t") + logs.WriteString(entry.LastModified.String() + "\t") if tmpUint32, err = sync.ReadUint32(); err != nil { return DeviceFileInfo{}, fmt.Errorf("sync transport read (file name length): %w", err) } - log.WriteString(fmt.Sprintf("%d\t", tmpUint32)) + logs.WriteString(fmt.Sprintf("%d\t", tmpUint32)) if entry.Name, err = sync.ReadStringN(int(tmpUint32)); err != nil { return DeviceFileInfo{}, fmt.Errorf("sync transport read (file name): %w", err) } - log.WriteString(entry.Name + "\t") + logs.WriteString(entry.Name + "\t") return } diff --git a/hrp/pkg/gadb/transport.go b/hrp/pkg/gadb/transport.go index 31726772..abf3adf6 100644 --- a/hrp/pkg/gadb/transport.go +++ b/hrp/pkg/gadb/transport.go @@ -8,6 +8,8 @@ import ( "net" "strconv" "time" + + "github.com/rs/zerolog/log" ) var ErrConnBroken = errors.New("socket connection broken") @@ -32,7 +34,7 @@ func newTransport(address string, readTimeout ...time.Duration) (tp transport, e func (t transport) Send(command string) (err error) { msg := fmt.Sprintf("%04x%s", len(command), command) - debugLog(fmt.Sprintf("--> %s", command)) + log.Debug().Str("cmd", command).Msg("run adb command") return _send(t.sock, []byte(msg)) } @@ -42,7 +44,6 @@ func (t transport) VerifyResponse() (err error) { return err } if status == "OKAY" { - debugLog(fmt.Sprintf("<-- %s", status)) return nil } @@ -51,7 +52,7 @@ func (t transport) VerifyResponse() (err error) { return err } err = fmt.Errorf("command failed: %s", sError) - debugLog(fmt.Sprintf("<-- %s %s", status, sError)) + log.Error().Str("status", status).Str("err", sError).Msg("verify adb response failed") return } @@ -63,7 +64,6 @@ func (t transport) ReadStringAll() (s string, err error) { func (t transport) ReadBytesAll() (raw []byte, err error) { raw, err = ioutil.ReadAll(t.sock) - debugLog(fmt.Sprintf("\r%s", raw)) return } @@ -84,7 +84,6 @@ func (t transport) UnpackBytes() (raw []byte, err error) { } raw, err = t.ReadBytesN(int(size)) - debugLog(fmt.Sprintf("\r%s", raw)) return } diff --git a/hrp/pkg/gadb/transport_test.go b/hrp/pkg/gadb/transport_test.go index 96986365..143cd438 100644 --- a/hrp/pkg/gadb/transport_test.go +++ b/hrp/pkg/gadb/transport_test.go @@ -7,8 +7,6 @@ import ( ) func Test_transport_VerifyResponse(t *testing.T) { - SetDebug(true) - transport, err := newTransport("localhost:5037") if err != nil { t.Fatal(err) From 4db4a71b3d9c5e4a0c76ec193f1525699447f188 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 26 Dec 2022 17:08:33 +0800 Subject: [PATCH 2/6] feat: run UI automation with adb for Android --- hrp/pkg/uixt/android_driver.go | 80 ++++++++++++++++++++++------------ 1 file changed, 51 insertions(+), 29 deletions(-) diff --git a/hrp/pkg/uixt/android_driver.go b/hrp/pkg/uixt/android_driver.go index 45c7e519..6c9f046c 100644 --- a/hrp/pkg/uixt/android_driver.go +++ b/hrp/pkg/uixt/android_driver.go @@ -245,11 +245,13 @@ 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.adbDevice.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") - if err != nil { - _, err = ud.adbDevice.RunShellCommand("input", "keyevent", KEYCODE_BACK) - } return } @@ -314,10 +316,13 @@ func (ud *uiaDriver) Homescreen() (err error) { } func (ud *uiaDriver) PressKeyCode(keyCode KeyCode, metaState KeyMeta, flags ...KeyFlag) (err error) { - return ud._pressKeyCode(keyCode, metaState, flags...) -} + // adb shell input keyevent + _, err = ud.adbDevice.RunShellCommand( + "input", "keyevent", fmt.Sprintf("%d", keyCode)) + if err == nil { + return nil + } -func (ud *uiaDriver) _pressKeyCode(keyCode KeyCode, metaState KeyMeta, flags ...KeyFlag) (err error) { // register(postHandler, new PressKeyCodeAsync("/wd/hub/session/:sessionId/appium/device/press_keycode")) data := map[string]interface{}{ "keycode": keyCode, @@ -359,6 +364,13 @@ func (ud *uiaDriver) Tap(x, y int, options ...DataOption) error { } func (ud *uiaDriver) TapFloat(x, y float64, options ...DataOption) (err error) { + // adb shell input tap x y + _, err = ud.adbDevice.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, @@ -400,12 +412,6 @@ func (ud *uiaDriver) TouchAndHoldFloat(x, y float64, second ...float64) (err err return } -func (ud *uiaDriver) _drag(data map[string]interface{}) (err error) { - // register(postHandler, new Drag("/wd/hub/session/:sessionId/touch/drag")) - _, err = ud.httpPOST(data, "/session", ud.sessionId, "touch/drag") - return -} - // Drag performs a swipe from one coordinate to another coordinate. You can control // the smoothness and speed of the swipe by specifying the number of steps. // Each step execution is throttled to 5 milliseconds per step, so for a 100 @@ -425,22 +431,8 @@ func (ud *uiaDriver) DragFloat(fromX, fromY, toX, toY float64, options ...DataOp // new data options in post data for extra uiautomator configurations newData := NewData(data, options...) - return ud._drag(newData) -} - -func (ud *uiaDriver) _swipe(startX, startY, endX, endY interface{}, options ...DataOption) (err error) { - // register(postHandler, new Swipe("/wd/hub/session/:sessionId/touch/perform")) - data := map[string]interface{}{ - "startX": startX, - "startY": startY, - "endX": endX, - "endY": endY, - } - - // new data options in post data for extra uiautomator configurations - newData := NewData(data, options...) - - _, err = ud.httpPOST(newData, "/session", ud.sessionId, "touch/perform") + // register(postHandler, new Drag("/wd/hub/session/:sessionId/touch/drag")) + _, err = ud.httpPOST(newData, "/session", ud.sessionId, "touch/drag") return } @@ -453,7 +445,29 @@ func (ud *uiaDriver) Swipe(fromX, fromY, toX, toY int, options ...DataOption) er } func (ud *uiaDriver) SwipeFloat(fromX, fromY, toX, toY float64, options ...DataOption) error { - return ud._swipe(fromX, fromY, toX, toY, options...) + // adb shell input swipe fromX fromY toX toY + _, err := ud.adbDevice.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, + "startY": fromY, + "endX": toX, + "endY": toY, + } + + // new data options in post data for extra uiautomator configurations + newData := NewData(data, options...) + + _, err = ud.httpPOST(newData, "/session", ud.sessionId, "touch/perform") + return err } func (ud *uiaDriver) ForceTouch(x, y int, pressure float64, second ...float64) error { @@ -551,6 +565,14 @@ func (ud *uiaDriver) SetRotation(rotation Rotation) (err error) { } func (ud *uiaDriver) Screenshot() (raw *bytes.Buffer, err error) { + // adb shell screencap -p + resp, err := ud.adbDevice.RunShellCommandWithBytes( + "screencap", "-p", + ) + if err == nil { + return bytes.NewBuffer(resp), nil + } + // register(getHandler, new CaptureScreenshot("/wd/hub/session/:sessionId/screenshot")) var rawResp rawResponse if rawResp, err = ud.httpGET("/session", ud.sessionId, "screenshot"); err != nil { From 9e12befbc578ffff033c9d0b4802f6ae8a4faf68 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 26 Dec 2022 18:49:36 +0800 Subject: [PATCH 3/6] fix: ignore failure when create UIAutomator session --- hrp/pkg/uixt/android_driver.go | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/hrp/pkg/uixt/android_driver.go b/hrp/pkg/uixt/android_driver.go index 6c9f046c..144d00a3 100644 --- a/hrp/pkg/uixt/android_driver.go +++ b/hrp/pkg/uixt/android_driver.go @@ -60,12 +60,15 @@ func NewUIADriver(capabilities Capabilities, urlPrefix string) (driver *uiaDrive return nil, fmt.Errorf("adb forward: %w", err) } driver.client = convertToHTTPClient(conn) - if session, err := driver.NewSession(capabilities); err != nil { - return nil, err - } else { + + session, err := driver.NewSession(capabilities) + if err == nil { driver.sessionId = session.SessionId + } else { + log.Warn().Msg( + "create UIAutomator session failed, use adb driver instead") } - return + return driver, nil } type BatteryStatus int @@ -221,6 +224,18 @@ func (ud *uiaDriver) BatteryInfo() (batteryInfo BatteryInfo, err error) { } func (ud *uiaDriver) WindowSize() (size Size, err error) { + // adb shell wm size + resp, err := ud.adbDevice.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 { From e26f79e32660d114f9cf4a7f2624fbcf2503cdeb Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 26 Dec 2022 20:22:59 +0800 Subject: [PATCH 4/6] fix: specify uia2 ip and port --- hrp/pkg/gadb/device.go | 10 ++++- hrp/pkg/gadb/transport.go | 1 - hrp/pkg/uixt/android_device.go | 33 ++++++++------- hrp/pkg/uixt/android_driver.go | 75 ++++++++-------------------------- hrp/pkg/uixt/android_test.go | 37 ----------------- hrp/pkg/uixt/interface.go | 1 - hrp/pkg/uixt/ios_driver.go | 12 ------ hrp/pkg/uixt/ios_test.go | 12 ------ 8 files changed, 43 insertions(+), 138 deletions(-) diff --git a/hrp/pkg/gadb/device.go b/hrp/pkg/gadb/device.go index 9d07bd2e..59c4d1e6 100644 --- a/hrp/pkg/gadb/device.go +++ b/hrp/pkg/gadb/device.go @@ -7,6 +7,8 @@ import ( "os" "strings" "time" + + "github.com/rs/zerolog/log" ) type DeviceFileInfo struct { @@ -20,7 +22,7 @@ func (info DeviceFileInfo) IsDir() bool { return (info.Mode & (1 << 14)) == (1 << 14) } -const DefaultFileMode = os.FileMode(0664) +const DefaultFileMode = os.FileMode(0o664) type DeviceState string @@ -147,6 +149,9 @@ func (d Device) RunShellCommandWithBytes(cmd string, args ...string) ([]byte, er if strings.TrimSpace(cmd) == "" { return nil, errors.New("adb shell: command cannot be empty") } + log.Debug().Str("cmd", + fmt.Sprintf("adb -s %s shell %s", d.serial, cmd)). + Msg("run adb command") raw, err := d.executeCommand(fmt.Sprintf("shell:%s", cmd)) return raw, err } @@ -156,6 +161,9 @@ func (d Device) EnableAdbOverTCP(port ...int) (err error) { port = []int{AdbDaemonPort} } + log.Info().Str("cmd", + fmt.Sprintf("adb -s %s tcpip %d", d.serial, port[0])). + Msg("enable adb over tcp") _, err = d.executeCommand(fmt.Sprintf("tcpip:%d", port[0]), true) return } diff --git a/hrp/pkg/gadb/transport.go b/hrp/pkg/gadb/transport.go index abf3adf6..ae900429 100644 --- a/hrp/pkg/gadb/transport.go +++ b/hrp/pkg/gadb/transport.go @@ -34,7 +34,6 @@ func newTransport(address string, readTimeout ...time.Duration) (tp transport, e func (t transport) Send(command string) (err error) { msg := fmt.Sprintf("%04x%s", len(command), command) - log.Debug().Str("cmd", command).Msg("run adb command") return _send(t.sock, []byte(msg)) } diff --git a/hrp/pkg/uixt/android_device.go b/hrp/pkg/uixt/android_device.go index 6cf2f9e9..f5380edf 100644 --- a/hrp/pkg/uixt/android_device.go +++ b/hrp/pkg/uixt/android_device.go @@ -20,6 +20,7 @@ import ( var ( AdbServerHost = "localhost" AdbServerPort = gadb.AdbServerPort // 5037 + UIA2ServerHost = "localhost" UIA2ServerPort = 6790 DeviceTempPath = "/data/local/tmp" ) @@ -34,15 +35,15 @@ func WithSerialNumber(serial string) AndroidDeviceOption { } } -func WithAdbIP(ip string) AndroidDeviceOption { +func WithUIA2IP(ip string) AndroidDeviceOption { return func(device *AndroidDevice) { - device.IP = ip + device.UIA2IP = ip } } -func WithAdbPort(port int) AndroidDeviceOption { +func WithUIA2Port(port int) AndroidDeviceOption { return func(device *AndroidDevice) { - device.Port = port + device.UIA2Port = port } } @@ -56,11 +57,11 @@ func GetAndroidDeviceOptions(dev *AndroidDevice) (deviceOptions []AndroidDeviceO if dev.SerialNumber != "" { deviceOptions = append(deviceOptions, WithSerialNumber(dev.SerialNumber)) } - if dev.IP != "" { - deviceOptions = append(deviceOptions, WithAdbIP(dev.IP)) + if dev.UIA2IP != "" { + deviceOptions = append(deviceOptions, WithUIA2IP(dev.UIA2IP)) } - if dev.Port != 0 { - deviceOptions = append(deviceOptions, WithAdbPort(dev.Port)) + if dev.UIA2Port != 0 { + deviceOptions = append(deviceOptions, WithUIA2Port(dev.UIA2Port)) } if dev.LogOn { deviceOptions = append(deviceOptions, WithAdbLogOn(true)) @@ -81,8 +82,8 @@ func NewAndroidDevice(options ...AndroidDeviceOption) (device *AndroidDevice, er } device = &AndroidDevice{ - Port: UIA2ServerPort, - IP: AdbServerHost, + UIA2IP: UIA2ServerHost, + UIA2Port: UIA2ServerPort, } for _, option := range options { option(device) @@ -118,9 +119,8 @@ type AndroidDevice struct { d gadb.Device logcat *AdbLogcat SerialNumber string `json:"serial,omitempty" yaml:"serial,omitempty"` - IP string `json:"ip,omitempty" yaml:"ip,omitempty"` - Port int `json:"port,omitempty" yaml:"port,omitempty"` - MjpegPort int `json:"mjpeg_port,omitempty" yaml:"mjpeg_port,omitempty"` + 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"` } @@ -174,9 +174,8 @@ func (dev *AndroidDevice) NewUSBDriver(capabilities Capabilities) (driver *uiaDr _ = dev.d.ForwardKill(localPort) return nil, errors.Wrap(code.AndroidDeviceUSBDriverError, err.Error()) } - driver.adbDevice = dev.d + driver.adbClient = dev.d driver.logcat = dev.logcat - driver.localPort = localPort return driver, nil } @@ -184,11 +183,11 @@ func (dev *AndroidDevice) NewUSBDriver(capabilities Capabilities) (driver *uiaDr // 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) { - rawURL := fmt.Sprintf("http://%s:%d/wd/hub", dev.IP, dev.Port) + rawURL := fmt.Sprintf("http://%s:%d/wd/hub", dev.UIA2IP, dev.UIA2Port) if driver, err = NewUIADriver(capabilities, rawURL); err != nil { return nil, err } - driver.adbDevice = dev.d + driver.adbClient = dev.d return driver, nil } diff --git a/hrp/pkg/uixt/android_driver.go b/hrp/pkg/uixt/android_driver.go index 144d00a3..25c5db35 100644 --- a/hrp/pkg/uixt/android_driver.go +++ b/hrp/pkg/uixt/android_driver.go @@ -34,9 +34,8 @@ var errDriverNotImplemented = errors.New("driver method not implemented") type uiaDriver struct { Driver - adbDevice gadb.Device + adbClient gadb.Device logcat *AdbLogcat - localPort int } func NewUIADriver(capabilities Capabilities, urlPrefix string) (driver *uiaDriver, err error) { @@ -126,44 +125,6 @@ func (ud *uiaDriver) NewSession(capabilities Capabilities) (sessionInfo SessionI return SessionInfo{SessionId: sessionID}, nil } -func (ud *uiaDriver) ActiveSession() (sessionInfo SessionInfo, err error) { - // [[FBRoute GET:@""] respondWithTarget:self action:@selector(handleGetActiveSession:)] - return SessionInfo{SessionId: ud.sessionId}, nil -} - -func (ud *uiaDriver) SessionIDs() (sessionIDs []string, err error) { - // register(getHandler, new GetSessions("/wd/hub/sessions")) - var rawResp rawResponse - if rawResp, err = ud.httpGET("/sessions"); err != nil { - return nil, err - } - reply := new(struct{ Value []struct{ SessionId string } }) - if err = json.Unmarshal(rawResp, reply); err != nil { - return nil, err - } - - sessionIDs = make([]string, len(reply.Value)) - for i := range reply.Value { - sessionIDs[i] = reply.Value[i].SessionId - } - return -} - -func (ud *uiaDriver) SessionDetails() (scrollData map[string]interface{}, err error) { - // register(getHandler, new GetSessionDetails("/wd/hub/session/:sessionId")) - var rawResp rawResponse - if rawResp, err = ud.httpGET("/session", ud.sessionId); err != nil { - return nil, err - } - reply := new(struct{ Value map[string]interface{} }) - if err = json.Unmarshal(rawResp, reply); err != nil { - return nil, err - } - - scrollData = reply.Value - return -} - func (ud *uiaDriver) DeleteSession() (err error) { // TODO return errDriverNotImplemented @@ -225,7 +186,7 @@ func (ud *uiaDriver) BatteryInfo() (batteryInfo BatteryInfo, err error) { func (ud *uiaDriver) WindowSize() (size Size, err error) { // adb shell wm size - resp, err := ud.adbDevice.RunShellCommand("wm", "size") + resp, err := ud.adbClient.RunShellCommand("wm", "size") if err == nil { // Physical size: 1080x2340 s := strings.Trim(strings.Split(resp, ": ")[1], "\n") @@ -261,7 +222,7 @@ 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.adbDevice.RunShellCommand("input", "keyevent", KEYCODE_BACK) + _, err = ud.adbClient.RunShellCommand("input", "keyevent", KEYCODE_BACK) if err == nil { return nil } @@ -271,33 +232,33 @@ func (ud *uiaDriver) PressBack(options ...DataOption) (err error) { } func (ud *uiaDriver) StartCamera() (err error) { - if _, err = ud.adbDevice.RunShellCommand("rm", "-r", "/sdcard/DCIM/Camera"); err != nil { + 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.adbDevice.RunShellCommand("getprop", "ro.build.version.release"); err != nil { + if version, err = ud.adbClient.RunShellCommand("getprop", "ro.build.version.release"); err != nil { return err } if version == "11" || version == "12" { - if _, err = ud.adbDevice.RunShellCommand("am", "start", "-a", "android.media.action.STILL_IMAGE_CAMERA"); err != nil { + 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.adbDevice.RunShellCommand("input", "swipe", "750", "1000", "250", "1000"); err != nil { + if _, err = ud.adbClient.RunShellCommand("input", "swipe", "750", "1000", "250", "1000"); err != nil { return err } time.Sleep(5 * time.Second) - if _, err = ud.adbDevice.RunShellCommand("input", "keyevent", KEYCODE_CAMERA); err != nil { + if _, err = ud.adbClient.RunShellCommand("input", "keyevent", KEYCODE_CAMERA); err != nil { return err } return } else { - if _, err = ud.adbDevice.RunShellCommand("am", "start", "-a", "android.media.action.VIDEO_CAPTURE"); err != nil { + 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.adbDevice.RunShellCommand("input", "keyevent", KEYCODE_CAMERA); err != nil { + if _, err = ud.adbClient.RunShellCommand("input", "keyevent", KEYCODE_CAMERA); err != nil { return err } return @@ -315,12 +276,12 @@ func (ud *uiaDriver) StopCamera() (err error) { } // kill samsung shell command - if _, err = ud.adbDevice.RunShellCommand("am", "force-stop", "com.sec.android.app.camera"); err != nil { + 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.adbDevice.RunShellCommand("am", "force-stop", "com.android.camera2"); err != nil { + if _, err = ud.adbClient.RunShellCommand("am", "force-stop", "com.android.camera2"); err != nil { return err } return @@ -332,7 +293,7 @@ func (ud *uiaDriver) Homescreen() (err error) { func (ud *uiaDriver) PressKeyCode(keyCode KeyCode, metaState KeyMeta, flags ...KeyFlag) (err error) { // adb shell input keyevent - _, err = ud.adbDevice.RunShellCommand( + _, err = ud.adbClient.RunShellCommand( "input", "keyevent", fmt.Sprintf("%d", keyCode)) if err == nil { return nil @@ -355,7 +316,7 @@ func (ud *uiaDriver) PressKeyCode(keyCode KeyCode, metaState KeyMeta, flags ...K func (ud *uiaDriver) AppLaunch(bundleId string) (err error) { // 不指定 Activity 名称启动(启动主 Activity) // adb shell monkey -p -c android.intent.category.LAUNCHER 1 - sOutput, err := ud.adbDevice.RunShellCommand( + sOutput, err := ud.adbClient.RunShellCommand( "monkey", "-p", bundleId, "-c", "android.intent.category.LAUNCHER", "1", ) if err != nil { @@ -370,7 +331,7 @@ func (ud *uiaDriver) AppLaunch(bundleId string) (err error) { func (ud *uiaDriver) AppTerminate(bundleId string) (successful bool, err error) { // 强制停止应用,停止 相关的进程 // adb shell am force-stop - _, err = ud.adbDevice.RunShellCommand("am", "force-stop", bundleId) + _, err = ud.adbClient.RunShellCommand("am", "force-stop", bundleId) return err == nil, err } @@ -380,7 +341,7 @@ func (ud *uiaDriver) Tap(x, y int, options ...DataOption) error { func (ud *uiaDriver) TapFloat(x, y float64, options ...DataOption) (err error) { // adb shell input tap x y - _, err = ud.adbDevice.RunShellCommand( + _, err = ud.adbClient.RunShellCommand( "input", "tap", fmt.Sprintf("%.1f", x), fmt.Sprintf("%.1f", y)) if err == nil { return nil @@ -461,7 +422,7 @@ 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.adbDevice.RunShellCommand( + _, err := ud.adbClient.RunShellCommand( "input", "swipe", fmt.Sprintf("%.1f", fromX), fmt.Sprintf("%.1f", fromY), fmt.Sprintf("%.1f", toX), fmt.Sprintf("%.1f", toY), @@ -581,7 +542,7 @@ func (ud *uiaDriver) SetRotation(rotation Rotation) (err error) { func (ud *uiaDriver) Screenshot() (raw *bytes.Buffer, err error) { // adb shell screencap -p - resp, err := ud.adbDevice.RunShellCommandWithBytes( + resp, err := ud.adbClient.RunShellCommandWithBytes( "screencap", "-p", ) if err == nil { diff --git a/hrp/pkg/uixt/android_test.go b/hrp/pkg/uixt/android_test.go index aa119e0f..1382ca42 100644 --- a/hrp/pkg/uixt/android_test.go +++ b/hrp/pkg/uixt/android_test.go @@ -66,43 +66,6 @@ func TestDriver_Status(t *testing.T) { } } -func TestDriver_SessionIDs(t *testing.T) { - driver, err := NewUIADriver(nil, uiaServerURL) - if err != nil { - t.Fatal(err) - } - - sessions, err := driver.SessionIDs() - if err != nil { - t.Fatal(err) - } - if len(sessions) == 0 { - t.Fatal("should have at least one") - } - t.Log(len(sessions), sessions) -} - -func TestDriver_SessionDetails(t *testing.T) { - // firstMatchEntry := make(map[string]interface{}) - // firstMatchEntry["package"] = "com.android.settings" - // firstMatchEntry["activity"] = "com.android.settings/.Settings" - // caps = Capabilities{ - // "firstMatch": []interface{}{firstMatchEntry}, - // "alwaysMatch": struct{}{}, - // } - driver, err := NewUIADriver(nil, uiaServerURL) - if err != nil { - t.Fatal(err) - } - - scrollData, err := driver.SessionDetails() - if err != nil { - t.Fatal(err) - } - - t.Log(scrollData) -} - func TestDriver_Screenshot(t *testing.T) { driver, err := NewUIADriver(nil, uiaServerURL) if err != nil { diff --git a/hrp/pkg/uixt/interface.go b/hrp/pkg/uixt/interface.go index d324872f..3b2005fb 100644 --- a/hrp/pkg/uixt/interface.go +++ b/hrp/pkg/uixt/interface.go @@ -595,7 +595,6 @@ type WebDriver interface { // NewSession starts a new session and returns the SessionInfo. NewSession(capabilities Capabilities) (SessionInfo, error) - ActiveSession() (SessionInfo, error) // DeleteSession Kills application associated with that session and removes session // 1) alertsMonitor disable // 2) testedApplicationBundleId terminate diff --git a/hrp/pkg/uixt/ios_driver.go b/hrp/pkg/uixt/ios_driver.go index fc3aede9..16aa884c 100644 --- a/hrp/pkg/uixt/ios_driver.go +++ b/hrp/pkg/uixt/ios_driver.go @@ -69,18 +69,6 @@ func (wd *wdaDriver) NewSession(capabilities Capabilities) (sessionInfo SessionI return } -func (wd *wdaDriver) ActiveSession() (sessionInfo SessionInfo, err error) { - // [[FBRoute GET:@""] respondWithTarget:self action:@selector(handleGetActiveSession:)] - var rawResp rawResponse - if rawResp, err = wd.httpGET("/session", wd.sessionId); err != nil { - return SessionInfo{}, err - } - if sessionInfo, err = rawResp.valueConvertToSessionInfo(); err != nil { - return SessionInfo{}, err - } - return -} - func (wd *wdaDriver) DeleteSession() (err error) { // [[FBRoute DELETE:@""] respondWithTarget:self action:@selector(handleDeleteSession:)] _, err = wd.httpDELETE("/session", wd.sessionId) diff --git a/hrp/pkg/uixt/ios_test.go b/hrp/pkg/uixt/ios_test.go index 23fff97b..c894f5aa 100644 --- a/hrp/pkg/uixt/ios_test.go +++ b/hrp/pkg/uixt/ios_test.go @@ -68,18 +68,6 @@ func TestNewUSBDriver(t *testing.T) { // t.Log(driver.IsWdaHealthy()) } -func Test_remoteWD_ActiveSession(t *testing.T) { - setup(t) - - sessionInfo, err := driver.ActiveSession() - if err != nil { - t.Fatal(err) - } - if len(sessionInfo.SessionId) == 0 { - t.Fatal(sessionInfo) - } -} - func Test_remoteWD_DeleteSession(t *testing.T) { setup(t) From 64f1a28605be4ab75cdaf9fa1a73224369aa8c3e Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 26 Dec 2022 21:57:47 +0800 Subject: [PATCH 5/6] feat: run Android UI automation with adb by default, add uixt.WithUIA2(true) option to use uiautomator2 --- docs/CHANGELOG.md | 9 + examples/uitest/demo_android_douyin_test.go | 2 +- hrp/internal/version/VERSION | 2 +- hrp/pkg/uixt/android_adb_driver.go | 337 ++++++++++++++++++ hrp/pkg/uixt/android_device.go | 50 ++- hrp/pkg/uixt/android_key.go | 2 + hrp/pkg/uixt/android_test.go | 20 +- ...droid_driver.go => android_uia2_driver.go} | 211 +---------- hrp/pkg/uixt/interface.go | 3 - hrp/pkg/uixt/ios_driver.go | 28 +- hrp/pkg/uixt/ocr_vedem_test.go | 4 +- 11 files changed, 424 insertions(+), 244 deletions(-) create mode 100644 hrp/pkg/uixt/android_adb_driver.go rename hrp/pkg/uixt/{android_driver.go => android_uia2_driver.go} (72%) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 95f7bc5c..cdf53f67 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -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** diff --git a/examples/uitest/demo_android_douyin_test.go b/examples/uitest/demo_android_douyin_test.go index 8ef5d2c4..7d7c94f4 100644 --- a/examples/uitest/demo_android_douyin_test.go +++ b/examples/uitest/demo_android_douyin_test.go @@ -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(). diff --git a/hrp/internal/version/VERSION b/hrp/internal/version/VERSION index 9198ad67..b0136b0c 100644 --- a/hrp/internal/version/VERSION +++ b/hrp/internal/version/VERSION @@ -1 +1 @@ -v4.3.1 \ No newline at end of file +v4.3.2 \ No newline at end of file diff --git a/hrp/pkg/uixt/android_adb_driver.go b/hrp/pkg/uixt/android_adb_driver.go new file mode 100644 index 00000000..7aba3126 --- /dev/null +++ b/hrp/pkg/uixt/android_adb_driver.go @@ -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 + _, 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 -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) { + // 强制停止应用,停止 相关的进程 + // adb shell am force-stop + _, 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 +} diff --git a/hrp/pkg/uixt/android_device.go b/hrp/pkg/uixt/android_device.go index f5380edf..0df9162d 100644 --- a/hrp/pkg/uixt/android_device.go +++ b/hrp/pkg/uixt/android_device.go @@ -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 { diff --git a/hrp/pkg/uixt/android_key.go b/hrp/pkg/uixt/android_key.go index 07f0d5b7..6304de9d 100644 --- a/hrp/pkg/uixt/android_key.go +++ b/hrp/pkg/uixt/android_key.go @@ -1,5 +1,7 @@ package uixt +// See https://developer.android.com/reference/android/view/KeyEvent + type KeyMeta int const ( diff --git a/hrp/pkg/uixt/android_test.go b/hrp/pkg/uixt/android_test.go index 1382ca42..44d8d3ac 100644 --- a/hrp/pkg/uixt/android_test.go +++ b/hrp/pkg/uixt/android_test.go @@ -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) } diff --git a/hrp/pkg/uixt/android_driver.go b/hrp/pkg/uixt/android_uia2_driver.go similarity index 72% rename from hrp/pkg/uixt/android_driver.go rename to hrp/pkg/uixt/android_uia2_driver.go index 25c5db35..bf451577 100644 --- a/hrp/pkg/uixt/android_driver.go +++ b/hrp/pkg/uixt/android_uia2_driver.go @@ -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 - _, 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 -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) { - // 强制停止应用,停止 相关的进程 - // adb shell am force-stop - _, 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 -} diff --git a/hrp/pkg/uixt/interface.go b/hrp/pkg/uixt/interface.go index 3b2005fb..865ed9fb 100644 --- a/hrp/pkg/uixt/interface.go +++ b/hrp/pkg/uixt/interface.go @@ -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) diff --git a/hrp/pkg/uixt/ios_driver.go b/hrp/pkg/uixt/ios_driver.go index 16aa884c..53339d8c 100644 --- a/hrp/pkg/uixt/ios_driver.go +++ b/hrp/pkg/uixt/ios_driver.go @@ -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 diff --git a/hrp/pkg/uixt/ocr_vedem_test.go b/hrp/pkg/uixt/ocr_vedem_test.go index f3fe1d32..fa409fc9 100644 --- a/hrp/pkg/uixt/ocr_vedem_test.go +++ b/hrp/pkg/uixt/ocr_vedem_test.go @@ -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) } From 994a91bb84c10bf4e1e3912282d432870c754c84 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 26 Dec 2022 22:15:40 +0800 Subject: [PATCH 6/6] feat: input text with adb --- hrp/pkg/uixt/android_adb_driver.go | 8 +-- hrp/pkg/uixt/android_uia2_driver.go | 89 ----------------------------- 2 files changed, 4 insertions(+), 93 deletions(-) diff --git a/hrp/pkg/uixt/android_adb_driver.go b/hrp/pkg/uixt/android_adb_driver.go index 7aba3126..5c80e1ef 100644 --- a/hrp/pkg/uixt/android_adb_driver.go +++ b/hrp/pkg/uixt/android_adb_driver.go @@ -132,12 +132,11 @@ func (ad *adbDriver) StopCamera() (err error) { } // kill samsung shell command - if _, err = ad.adbClient.RunShellCommand("am", "force-stop", "com.sec.android.app.camera"); err != nil { + if _, err = ad.AppTerminate("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 { + if _, err = ad.AppTerminate("com.android.camera2"); err != nil { return err } return @@ -248,7 +247,8 @@ func (ad *adbDriver) GetPasteboard(contentType PasteboardType) (raw *bytes.Buffe } func (ad *adbDriver) SendKeys(text string, options ...DataOption) (err error) { - err = errDriverNotImplemented + // adb shell input text + _, err = ad.adbClient.RunShellCommand("input", "text", text) return } diff --git a/hrp/pkg/uixt/android_uia2_driver.go b/hrp/pkg/uixt/android_uia2_driver.go index bf451577..daf9e8bc 100644 --- a/hrp/pkg/uixt/android_uia2_driver.go +++ b/hrp/pkg/uixt/android_uia2_driver.go @@ -142,11 +142,6 @@ func (ud *uiaDriver) DeviceInfo() (deviceInfo DeviceInfo, err error) { return } -func (ud *uiaDriver) Location() (location Location, err error) { - // TODO - return location, errDriverNotImplemented -} - func (ud *uiaDriver) BatteryInfo() (batteryInfo BatteryInfo, err error) { // register(getHandler, new GetBatteryInfo("/wd/hub/session/:sessionId/appium/device/battery_info")) var rawResp rawResponse @@ -178,15 +173,6 @@ func (ud *uiaDriver) WindowSize() (size Size, err error) { return } -func (ud *uiaDriver) Screen() (screen Screen, err error) { - // TODO - return screen, errDriverNotImplemented -} - -func (ud *uiaDriver) Scale() (scale float64, err error) { - return 1, nil -} - // PressBack simulates a short press on the BACK button. func (ud *uiaDriver) PressBack(options ...DataOption) (err error) { // register(postHandler, new PressBack("/wd/hub/session/:sessionId/back")) @@ -230,15 +216,6 @@ func (ud *uiaDriver) TapFloat(x, y float64, options ...DataOption) (err error) { return } -func (ud *uiaDriver) DoubleTap(x, y int) error { - return ud.DoubleTapFloat(float64(x), float64(y)) -} - -func (ud *uiaDriver) DoubleTapFloat(x, y float64) (err error) { - // TODO - return errDriverNotImplemented -} - func (ud *uiaDriver) TouchAndHold(x, y int, second ...float64) (err error) { return ud.TouchAndHoldFloat(float64(x), float64(y), second...) } @@ -307,15 +284,6 @@ func (ud *uiaDriver) SwipeFloat(fromX, fromY, toX, toY float64, options ...DataO return err } -func (ud *uiaDriver) ForceTouch(x, y int, pressure float64, second ...float64) error { - return ud.ForceTouchFloat(float64(x), float64(y), pressure, second...) -} - -func (ud *uiaDriver) ForceTouchFloat(x, y, pressure float64, second ...float64) (err error) { - // TODO - return errDriverNotImplemented -} - func (ud *uiaDriver) SetPasteboard(contentType PasteboardType, content string) (err error) { lbl := content @@ -376,11 +344,6 @@ func (ud *uiaDriver) Input(text string, options ...DataOption) (err error) { return ud.SendKeys(text, options...) } -func (ud *uiaDriver) PressButton(devBtn DeviceButton) (err error) { - // TODO - return errDriverNotImplemented -} - func (ud *uiaDriver) Rotation() (rotation Rotation, err error) { // register(getHandler, new GetRotation("/wd/hub/session/:sessionId/rotation")) var rawResp rawResponse @@ -396,20 +359,7 @@ func (ud *uiaDriver) Rotation() (rotation Rotation, err error) { return } -func (ud *uiaDriver) SetRotation(rotation Rotation) (err error) { - // TODO - return errDriverNotImplemented -} - func (ud *uiaDriver) Screenshot() (raw *bytes.Buffer, err error) { - // adb shell screencap -p - resp, err := ud.adbClient.RunShellCommandWithBytes( - "screencap", "-p", - ) - if err == nil { - return bytes.NewBuffer(resp), nil - } - // register(getHandler, new CaptureScreenshot("/wd/hub/session/:sessionId/screenshot")) var rawResp rawResponse if rawResp, err = ud.httpGET("/session", ud.sessionId, "screenshot"); err != nil { @@ -445,42 +395,3 @@ func (ud *uiaDriver) Source(srcOpt ...SourceOption) (source string, err error) { source = reply.Value return } - -func (ud *uiaDriver) AccessibleSource() (source string, err error) { - // TODO - return source, errDriverNotImplemented -} - -func (ud *uiaDriver) HealthCheck() (err error) { - // TODO - return errDriverNotImplemented -} - -func (ud *uiaDriver) GetAppiumSettings() (settings map[string]interface{}, err error) { - // register(getHandler, new GetSettings("/wd/hub/session/:sessionId/appium/settings")) - var rawResp rawResponse - if rawResp, err = ud.httpGET("/session", ud.sessionId, "appium/settings"); err != nil { - return nil, err - } - reply := new(struct{ Value map[string]interface{} }) - if err = json.Unmarshal(rawResp, reply); err != nil { - return nil, err - } - - settings = reply.Value - return -} - -func (ud *uiaDriver) SetAppiumSettings(settings map[string]interface{}) (ret map[string]interface{}, err error) { - data := map[string]interface{}{ - "settings": settings, - } - // register(postHandler, new UpdateSettings("/wd/hub/session/:sessionId/appium/settings")) - _, err = ud.httpPOST(data, "/session", ud.sessionId, "appium/settings") - return -} - -func (ud *uiaDriver) IsHealthy() (healthy bool, err error) { - // TODO - return healthy, errDriverNotImplemented -}