refactor: move gadb to hrp pkg

This commit is contained in:
debugtalk
2022-10-23 21:51:21 +08:00
parent 6e1348dc1e
commit 896f39c3f2
16 changed files with 1506 additions and 8 deletions

View File

@@ -5,10 +5,10 @@ import (
"fmt"
"os"
"github.com/electricbubble/gadb"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v4/hrp/pkg/gadb"
"github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
)

View File

@@ -1,5 +1,5 @@
# boomer
This module is initially forked from [myzhan/boomer] and made a lot of changes.
This module is initially forked from [myzhan/boomer@v1.6.0] and made a lot of changes.
[myzhan/boomer]: https://github.com/myzhan/boomer
[myzhan/boomer@v1.6.0]: https://github.com/myzhan/boomer/tree/v1.6.0

5
hrp/pkg/gadb/README.md Normal file
View File

@@ -0,0 +1,5 @@
# gadb
This module is initially forked from [electricbubble/gadb@v0.0.7].
[electricbubble/gadb@v0.0.7]: https://github.com/electricbubble/gadb/tree/v0.0.7

216
hrp/pkg/gadb/client.go Normal file
View File

@@ -0,0 +1,216 @@
package gadb
import (
"fmt"
"strconv"
"strings"
)
const AdbServerPort = 5037
const AdbDaemonPort = 5555
type Client struct {
host string
port int
}
func NewClient() (Client, error) {
return NewClientWith("localhost")
}
func NewClientWith(host string, port ...int) (adbClient Client, err error) {
if len(port) == 0 {
port = []int{AdbServerPort}
}
adbClient.host = host
adbClient.port = port[0]
var tp transport
if tp, err = adbClient.createTransport(); err != nil {
return Client{}, err
}
defer func() { _ = tp.Close() }()
return
}
func (c Client) ServerVersion() (version int, err error) {
var resp string
if resp, err = c.executeCommand("host:version"); err != nil {
return 0, err
}
var v int64
if v, err = strconv.ParseInt(resp, 16, 64); err != nil {
return 0, err
}
version = int(v)
return
}
func (c Client) DeviceSerialList() (serials []string, err error) {
var resp string
if resp, err = c.executeCommand("host:devices"); err != nil {
return
}
lines := strings.Split(resp, "\n")
serials = make([]string, 0, len(lines))
for i := range lines {
fields := strings.Fields(lines[i])
if len(fields) < 2 {
continue
}
serials = append(serials, fields[0])
}
return
}
func (c Client) DeviceList() (devices []Device, err error) {
var resp string
if resp, err = c.executeCommand("host:devices-l"); err != nil {
return
}
lines := strings.Split(resp, "\n")
devices = make([]Device, 0, len(lines))
for i := range lines {
line := strings.TrimSpace(lines[i])
if line == "" {
continue
}
fields := strings.Fields(line)
if len(fields) < 5 || len(fields[0]) == 0 {
debugLog(fmt.Sprintf("can't parse: %s", line))
continue
}
sliceAttrs := fields[2:]
mapAttrs := map[string]string{}
for _, field := range sliceAttrs {
split := strings.Split(field, ":")
key, val := split[0], split[1]
mapAttrs[key] = val
}
devices = append(devices, Device{adbClient: c, serial: fields[0], attrs: mapAttrs})
}
return
}
func (c Client) ForwardList() (deviceForward []DeviceForward, err error) {
var resp string
if resp, err = c.executeCommand("host:list-forward"); err != nil {
return nil, err
}
lines := strings.Split(resp, "\n")
deviceForward = make([]DeviceForward, 0, len(lines))
for i := range lines {
line := strings.TrimSpace(lines[i])
if line == "" {
continue
}
fields := strings.Fields(line)
deviceForward = append(deviceForward, DeviceForward{Serial: fields[0], Local: fields[1], Remote: fields[2]})
}
return
}
func (c Client) ForwardKillAll() (err error) {
_, err = c.executeCommand("host:killforward-all", true)
return
}
func (c Client) Connect(ip string, port ...int) (err error) {
if len(port) == 0 {
port = []int{AdbDaemonPort}
}
var resp string
if resp, err = c.executeCommand(fmt.Sprintf("host:connect:%s:%d", ip, port[0])); err != nil {
return err
}
if !strings.HasPrefix(resp, "connected to") && !strings.HasPrefix(resp, "already connected to") {
return fmt.Errorf("adb connect: %s", resp)
}
return
}
func (c Client) Disconnect(ip string, port ...int) (err error) {
cmd := fmt.Sprintf("host:disconnect:%s", ip)
if len(port) != 0 {
cmd = fmt.Sprintf("host:disconnect:%s:%d", ip, port[0])
}
var resp string
if resp, err = c.executeCommand(cmd); err != nil {
return err
}
if !strings.HasPrefix(resp, "disconnected") {
return fmt.Errorf("adb disconnect: %s", resp)
}
return
}
func (c Client) DisconnectAll() (err error) {
var resp string
if resp, err = c.executeCommand("host:disconnect:"); err != nil {
return err
}
if !strings.HasPrefix(resp, "disconnected everything") {
return fmt.Errorf("adb disconnect all: %s", resp)
}
return
}
func (c Client) KillServer() (err error) {
var tp transport
if tp, err = c.createTransport(); err != nil {
return err
}
defer func() { _ = tp.Close() }()
err = tp.Send("host:kill")
return
}
func (c Client) createTransport() (tp transport, err error) {
return newTransport(fmt.Sprintf("%s:%d", c.host, c.port))
}
func (c Client) executeCommand(command string, onlyVerifyResponse ...bool) (resp string, err error) {
if len(onlyVerifyResponse) == 0 {
onlyVerifyResponse = []bool{false}
}
var tp transport
if tp, err = c.createTransport(); err != nil {
return "", err
}
defer func() { _ = tp.Close() }()
if err = tp.Send(command); err != nil {
return "", err
}
if err = tp.VerifyResponse(); err != nil {
return "", err
}
if onlyVerifyResponse[0] {
return
}
if resp, err = tp.UnpackString(); err != nil {
return "", err
}
return
}

145
hrp/pkg/gadb/client_test.go Normal file
View File

@@ -0,0 +1,145 @@
package gadb
import (
"testing"
)
func TestClient_ServerVersion(t *testing.T) {
SetDebug(true)
adbClient, err := NewClient()
if err != nil {
t.Fatal(err)
}
adbServerVersion, err := adbClient.ServerVersion()
if err != nil {
t.Fatal(err)
}
t.Log(adbServerVersion)
}
func TestClient_DeviceSerialList(t *testing.T) {
SetDebug(true)
adbClient, err := NewClient()
if err != nil {
t.Fatal(err)
}
serials, err := adbClient.DeviceSerialList()
if err != nil {
t.Fatal(err)
}
for i := range serials {
t.Log(serials[i])
}
}
func TestClient_DeviceList(t *testing.T) {
SetDebug(true)
adbClient, err := NewClient()
if err != nil {
t.Fatal(err)
}
devices, err := adbClient.DeviceList()
if err != nil {
t.Fatal(err)
}
for i := range devices {
t.Log(devices[i].serial, devices[i].DeviceInfo())
}
}
func TestClient_ForwardList(t *testing.T) {
SetDebug(true)
adbClient, err := NewClient()
if err != nil {
t.Fatal(err)
}
deviceForwardList, err := adbClient.ForwardList()
if err != nil {
t.Fatal(err)
}
for i := range deviceForwardList {
t.Log(deviceForwardList[i])
}
}
func TestClient_ForwardKillAll(t *testing.T) {
SetDebug(true)
adbClient, err := NewClient()
if err != nil {
t.Fatal(err)
}
err = adbClient.ForwardKillAll()
if err != nil {
t.Fatal(err)
}
}
func TestClient_Connect(t *testing.T) {
adbClient, err := NewClient()
if err != nil {
t.Fatal(err)
}
SetDebug(true)
err = adbClient.Connect("192.168.1.28")
if err != nil {
t.Fatal(err)
}
}
func TestClient_Disconnect(t *testing.T) {
adbClient, err := NewClient()
if err != nil {
t.Fatal(err)
}
SetDebug(true)
err = adbClient.Disconnect("192.168.1.28")
if err != nil {
t.Fatal(err)
}
}
func TestClient_DisconnectAll(t *testing.T) {
adbClient, err := NewClient()
if err != nil {
t.Fatal(err)
}
SetDebug(true)
err = adbClient.DisconnectAll()
if err != nil {
t.Fatal(err)
}
}
func TestClient_KillServer(t *testing.T) {
SetDebug(true)
adbClient, err := NewClient()
if err != nil {
t.Fatal(err)
}
err = adbClient.KillServer()
if err != nil {
t.Fatal(err)
}
}

299
hrp/pkg/gadb/device.go Normal file
View File

@@ -0,0 +1,299 @@
package gadb
import (
"errors"
"fmt"
"io"
"os"
"strings"
"time"
)
type DeviceFileInfo struct {
Name string
Mode os.FileMode
Size uint32
LastModified time.Time
}
func (info DeviceFileInfo) IsDir() bool {
return (info.Mode & (1 << 14)) == (1 << 14)
}
const DefaultFileMode = os.FileMode(0664)
type DeviceState string
const (
StateUnknown DeviceState = "UNKNOWN"
StateOnline DeviceState = "online"
StateOffline DeviceState = "offline"
StateDisconnected DeviceState = "disconnected"
)
var deviceStateStrings = map[string]DeviceState{
"": StateDisconnected,
"offline": StateOffline,
"device": StateOnline,
}
func deviceStateConv(k string) (deviceState DeviceState) {
var ok bool
if deviceState, ok = deviceStateStrings[k]; !ok {
return StateUnknown
}
return
}
type DeviceForward struct {
Serial string
Local string
Remote string
// LocalProtocol string
// RemoteProtocol string
}
type Device struct {
adbClient Client
serial string
attrs map[string]string
}
func (d Device) Product() string {
return d.attrs["product"]
}
func (d Device) Model() string {
return d.attrs["model"]
}
func (d Device) Usb() string {
return d.attrs["usb"]
}
func (d Device) transportId() string {
return d.attrs["transport_id"]
}
func (d Device) DeviceInfo() map[string]string {
return d.attrs
}
func (d Device) Serial() string {
// resp, err := d.adbClient.executeCommand(fmt.Sprintf("host-serial:%s:get-serialno", d.serial))
return d.serial
}
func (d Device) IsUsb() bool {
return d.Usb() != ""
}
func (d Device) State() (DeviceState, error) {
resp, err := d.adbClient.executeCommand(fmt.Sprintf("host-serial:%s:get-state", d.serial))
return deviceStateConv(resp), err
}
func (d Device) DevicePath() (string, error) {
resp, err := d.adbClient.executeCommand(fmt.Sprintf("host-serial:%s:get-devpath", d.serial))
return resp, err
}
func (d Device) Forward(localPort, remotePort int, noRebind ...bool) (err error) {
command := ""
local := fmt.Sprintf("tcp:%d", localPort)
remote := fmt.Sprintf("tcp:%d", remotePort)
if len(noRebind) != 0 && noRebind[0] {
command = fmt.Sprintf("host-serial:%s:forward:norebind:%s;%s", d.serial, local, remote)
} else {
command = fmt.Sprintf("host-serial:%s:forward:%s;%s", d.serial, local, remote)
}
_, err = d.adbClient.executeCommand(command, true)
return
}
func (d Device) ForwardList() (deviceForwardList []DeviceForward, err error) {
var forwardList []DeviceForward
if forwardList, err = d.adbClient.ForwardList(); err != nil {
return nil, err
}
deviceForwardList = make([]DeviceForward, 0, len(deviceForwardList))
for i := range forwardList {
if forwardList[i].Serial == d.serial {
deviceForwardList = append(deviceForwardList, forwardList[i])
}
}
// resp, err := d.adbClient.executeCommand(fmt.Sprintf("host-serial:%s:list-forward", d.serial))
return
}
func (d Device) ForwardKill(localPort int) (err error) {
local := fmt.Sprintf("tcp:%d", localPort)
_, err = d.adbClient.executeCommand(fmt.Sprintf("host-serial:%s:killforward:%s", d.serial, local), true)
return
}
func (d Device) RunShellCommand(cmd string, args ...string) (string, error) {
raw, err := d.RunShellCommandWithBytes(cmd, args...)
return string(raw), err
}
func (d Device) RunShellCommandWithBytes(cmd string, args ...string) ([]byte, error) {
if len(args) > 0 {
cmd = fmt.Sprintf("%s %s", cmd, strings.Join(args, " "))
}
if strings.TrimSpace(cmd) == "" {
return nil, errors.New("adb shell: command cannot be empty")
}
raw, err := d.executeCommand(fmt.Sprintf("shell:%s", cmd))
return raw, err
}
func (d Device) EnableAdbOverTCP(port ...int) (err error) {
if len(port) == 0 {
port = []int{AdbDaemonPort}
}
_, err = d.executeCommand(fmt.Sprintf("tcpip:%d", port[0]), true)
return
}
func (d Device) createDeviceTransport() (tp transport, err error) {
if tp, err = newTransport(fmt.Sprintf("%s:%d", d.adbClient.host, d.adbClient.port)); err != nil {
return transport{}, err
}
if err = tp.Send(fmt.Sprintf("host:transport:%s", d.serial)); err != nil {
return transport{}, err
}
err = tp.VerifyResponse()
return
}
func (d Device) executeCommand(command string, onlyVerifyResponse ...bool) (raw []byte, err error) {
if len(onlyVerifyResponse) == 0 {
onlyVerifyResponse = []bool{false}
}
var tp transport
if tp, err = d.createDeviceTransport(); err != nil {
return nil, err
}
defer func() { _ = tp.Close() }()
if err = tp.Send(command); err != nil {
return nil, err
}
if err = tp.VerifyResponse(); err != nil {
return nil, err
}
if onlyVerifyResponse[0] {
return
}
raw, err = tp.ReadBytesAll()
return
}
func (d Device) List(remotePath string) (devFileInfos []DeviceFileInfo, err error) {
var tp transport
if tp, err = d.createDeviceTransport(); err != nil {
return nil, err
}
defer func() { _ = tp.Close() }()
var sync syncTransport
if sync, err = tp.CreateSyncTransport(); err != nil {
return nil, err
}
defer func() { _ = sync.Close() }()
if err = sync.Send("LIST", remotePath); err != nil {
return nil, err
}
devFileInfos = make([]DeviceFileInfo, 0)
var entry DeviceFileInfo
for entry, err = sync.ReadDirectoryEntry(); err == nil; entry, err = sync.ReadDirectoryEntry() {
if entry == (DeviceFileInfo{}) {
break
}
devFileInfos = append(devFileInfos, entry)
}
return
}
func (d Device) PushFile(local *os.File, remotePath string, modification ...time.Time) (err error) {
if len(modification) == 0 {
var stat os.FileInfo
if stat, err = local.Stat(); err != nil {
return err
}
modification = []time.Time{stat.ModTime()}
}
return d.Push(local, remotePath, modification[0], DefaultFileMode)
}
func (d Device) Push(source io.Reader, remotePath string, modification time.Time, mode ...os.FileMode) (err error) {
if len(mode) == 0 {
mode = []os.FileMode{DefaultFileMode}
}
var tp transport
if tp, err = d.createDeviceTransport(); err != nil {
return err
}
defer func() { _ = tp.Close() }()
var sync syncTransport
if sync, err = tp.CreateSyncTransport(); err != nil {
return err
}
defer func() { _ = sync.Close() }()
data := fmt.Sprintf("%s,%d", remotePath, mode[0])
if err = sync.Send("SEND", data); err != nil {
return err
}
if err = sync.SendStream(source); err != nil {
return
}
if err = sync.SendStatus("DONE", uint32(modification.Unix())); err != nil {
return
}
if err = sync.VerifyStatus(); err != nil {
return
}
return
}
func (d Device) Pull(remotePath string, dest io.Writer) (err error) {
var tp transport
if tp, err = d.createDeviceTransport(); err != nil {
return err
}
defer func() { _ = tp.Close() }()
var sync syncTransport
if sync, err = tp.CreateSyncTransport(); err != nil {
return err
}
defer func() { _ = sync.Close() }()
if err = sync.Send("RECV", remotePath); err != nil {
return err
}
err = sync.WriteStream(dest)
return
}

332
hrp/pkg/gadb/device_test.go Normal file
View File

@@ -0,0 +1,332 @@
package gadb
import (
"bytes"
"io/ioutil"
"os"
"strings"
"testing"
"time"
)
func TestDevice_State(t *testing.T) {
adbClient, err := NewClient()
if err != nil {
t.Fatal(err)
}
devices, err := adbClient.DeviceList()
if err != nil {
t.Fatal(err)
}
for i := range devices {
dev := devices[i]
state, err := dev.State()
if err != nil {
t.Fatal(err)
}
t.Log(dev.Serial(), state)
}
}
func TestDevice_DevicePath(t *testing.T) {
adbClient, err := NewClient()
if err != nil {
t.Fatal(err)
}
devices, err := adbClient.DeviceList()
if err != nil {
t.Fatal(err)
}
for i := range devices {
dev := devices[i]
devPath, err := dev.DevicePath()
if err != nil {
t.Fatal(err)
}
t.Log(dev.Serial(), devPath)
}
}
func TestDevice_Product(t *testing.T) {
adbClient, err := NewClient()
if err != nil {
t.Fatal(err)
}
devices, err := adbClient.DeviceList()
if err != nil {
t.Fatal(err)
}
for i := range devices {
dev := devices[i]
product := dev.Product()
t.Log(dev.Serial(), product)
}
}
func TestDevice_Model(t *testing.T) {
adbClient, err := NewClient()
if err != nil {
t.Fatal(err)
}
devices, err := adbClient.DeviceList()
if err != nil {
t.Fatal(err)
}
for i := range devices {
dev := devices[i]
t.Log(dev.Serial(), dev.Model())
}
}
func TestDevice_Usb(t *testing.T) {
adbClient, err := NewClient()
if err != nil {
t.Fatal(err)
}
devices, err := adbClient.DeviceList()
if err != nil {
t.Fatal(err)
}
for i := range devices {
dev := devices[i]
t.Log(dev.Serial(), dev.Usb(), dev.IsUsb())
}
}
func TestDevice_DeviceInfo(t *testing.T) {
adbClient, err := NewClient()
if err != nil {
t.Fatal(err)
}
devices, err := adbClient.DeviceList()
if err != nil {
t.Fatal(err)
}
for i := range devices {
dev := devices[i]
t.Log(dev.DeviceInfo())
}
}
func TestDevice_Forward(t *testing.T) {
adbClient, err := NewClient()
if err != nil {
t.Fatal(err)
}
devices, err := adbClient.DeviceList()
if err != nil {
t.Fatal(err)
}
SetDebug(true)
localPort := 61000
err = devices[0].Forward(localPort, 6790)
if err != nil {
t.Fatal(err)
}
err = devices[0].ForwardKill(localPort)
if err != nil {
t.Fatal(err)
}
}
func TestDevice_ForwardList(t *testing.T) {
adbClient, err := NewClient()
if err != nil {
t.Fatal(err)
}
devices, err := adbClient.DeviceList()
if err != nil {
t.Fatal(err)
}
SetDebug(true)
for i := range devices {
dev := devices[i]
forwardList, err := dev.ForwardList()
if err != nil {
t.Fatal(err)
}
t.Log(dev.serial, "->", forwardList)
}
}
func TestDevice_ForwardKill(t *testing.T) {
adbClient, err := NewClient()
if err != nil {
t.Fatal(err)
}
devices, err := adbClient.DeviceList()
if err != nil {
t.Fatal(err)
}
SetDebug(true)
err = devices[0].ForwardKill(6790)
if err != nil {
t.Fatal(err)
}
}
func TestDevice_RunShellCommand(t *testing.T) {
adbClient, err := NewClient()
if err != nil {
t.Fatal(err)
}
devices, err := adbClient.DeviceList()
if err != nil {
t.Fatal(err)
}
// for i := range devices {
// dev := devices[i]
// // cmdOutput, err := dev.RunShellCommand(`pm list packages | grep "bili"`)
// // cmdOutput, err := dev.RunShellCommand(`pm list packages`, `| grep "bili"`)
// // cmdOutput, err := dev.RunShellCommand("dumpsys activity | grep mFocusedActivity")
// cmdOutput, err := dev.RunShellCommand("monkey", "-p", "tv.danmaku.bili", "-c", "android.intent.category.LAUNCHER", "1")
// if err != nil {
// t.Fatal(dev.serial, err)
// }
// t.Log("\n"+dev.serial, cmdOutput)
// }
// SetDebug(true)
dev := devices[len(devices)-1]
dev = devices[0]
// cmdOutput, err := dev.RunShellCommand("monkey", "-p", "tv.danmaku.bili", "-c", "android.intent.category.LAUNCHER", "1")
cmdOutput, err := dev.RunShellCommand("ls /sdcard")
// cmdOutput, err := dev.RunShellCommandWithBytes("screencap -p")
if err != nil {
t.Fatal(dev.serial, err)
}
t.Log("\n⬇"+dev.serial+"⬇️\n", cmdOutput)
}
func TestDevice_EnableAdbOverTCP(t *testing.T) {
adbClient, err := NewClient()
if err != nil {
t.Fatal(err)
}
devices, err := adbClient.DeviceList()
if err != nil {
t.Fatal(err)
}
dev := devices[len(devices)-1]
dev = devices[0]
SetDebug(true)
err = dev.EnableAdbOverTCP()
if err != nil {
t.Fatal(err)
}
}
func TestDevice_List(t *testing.T) {
adbClient, err := NewClient()
if err != nil {
t.Fatal(err)
}
devices, err := adbClient.DeviceList()
if err != nil {
t.Fatal(err)
}
dev := devices[len(devices)-1]
dev = devices[0]
SetDebug(true)
// fileEntries, err := dev.List("/sdcard")
fileEntries, err := dev.List("/sdcard/Download")
if err != nil {
t.Fatal(err)
}
for i := range fileEntries {
t.Log(fileEntries[i].Name, "\t", fileEntries[i].IsDir())
}
}
func TestDevice_Push(t *testing.T) {
adbClient, err := NewClient()
if err != nil {
t.Fatal(err)
}
devices, err := adbClient.DeviceList()
if err != nil {
t.Fatal(err)
}
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 {
t.Fatal(err)
}
err = dev.Push(strings.NewReader("world"), "/sdcard/Download/hello.txt", time.Now())
if err != nil {
t.Fatal(err)
}
}
func TestDevice_Pull(t *testing.T) {
adbClient, err := NewClient()
if err != nil {
t.Fatal(err)
}
devices, err := adbClient.DeviceList()
if err != nil {
t.Fatal(err)
}
dev := devices[len(devices)-1]
dev = devices[0]
SetDebug(true)
buffer := bytes.NewBufferString("")
err = dev.Pull("/sdcard/Download/hello.txt", buffer)
if err != nil {
t.Fatal(err)
}
userHomeDir, _ := os.UserHomeDir()
if err = ioutil.WriteFile(userHomeDir+"/Desktop/hello.txt", buffer.Bytes(), DefaultFileMode); err != nil {
t.Fatal(err)
}
}

View File

@@ -0,0 +1,59 @@
package main
import (
"log"
"os"
"strings"
"github.com/httprunner/httprunner/v4/hrp/pkg/gadb"
)
func main() {
adbClient, err := gadb.NewClient()
checkErr(err, "fail to connect adb server")
devices, err := adbClient.DeviceList()
checkErr(err)
if len(devices) == 0 {
log.Fatalln("list of devices is empty")
}
dev := devices[0]
userHomeDir, _ := os.UserHomeDir()
apk, err := os.Open(userHomeDir + "/Desktop/xuexi_android_10002068.apk")
checkErr(err)
log.Println("starting to push apk")
remotePath := "/data/local/tmp/xuexi_android_10002068.apk"
err = dev.PushFile(apk, remotePath)
checkErr(err, "adb push")
log.Println("push completed")
log.Println("starting to install apk")
shellOutput, err := dev.RunShellCommand("pm install", remotePath)
checkErr(err, "pm install")
if !strings.Contains(shellOutput, "Success") {
log.Fatalln("fail to install: ", shellOutput)
}
log.Println("install completed")
}
func checkErr(err error, msg ...string) {
if err == nil {
return
}
var output string
if len(msg) != 0 {
output = msg[0] + " "
}
output += err.Error()
log.Fatalln(output)
}

17
hrp/pkg/gadb/gadb.go Normal file
View File

@@ -0,0 +1,17 @@
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)
}

View File

@@ -0,0 +1,252 @@
package gadb
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"net"
"time"
)
type syncTransport struct {
sock net.Conn
readTimeout time.Duration
}
func newSyncTransport(sock net.Conn, readTimeout time.Duration) syncTransport {
return syncTransport{sock: sock, readTimeout: readTimeout}
}
func (sync syncTransport) Send(command, data string) (err error) {
if len(command) != 4 {
return errors.New("sync commands must have length 4")
}
msg := bytes.NewBufferString(command)
if err = binary.Write(msg, binary.LittleEndian, int32(len(data))); err != nil {
return fmt.Errorf("sync transport write: %w", err)
}
msg.WriteString(data)
debugLog(fmt.Sprintf("--> %s", msg.String()))
return _send(sync.sock, msg.Bytes())
}
func (sync syncTransport) SendStream(reader io.Reader) (err error) {
syncMaxChunkSize := 64 * 1024
for err == nil {
tmp := make([]byte, syncMaxChunkSize)
var n int
n, err = reader.Read(tmp)
if err == io.EOF {
err = nil
break
}
if err == nil {
err = sync.sendChunk(tmp[:n])
}
}
return
}
func (sync syncTransport) SendStatus(statusCode string, n uint32) (err error) {
msg := bytes.NewBufferString(statusCode)
if err = binary.Write(msg, binary.LittleEndian, n); err != nil {
return fmt.Errorf("sync transport write: %w", err)
}
debugLog(fmt.Sprintf("--> %s", msg.String()))
return _send(sync.sock, msg.Bytes())
}
func (sync syncTransport) sendChunk(buffer []byte) (err error) {
msg := bytes.NewBufferString("DATA")
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())
}
func (sync syncTransport) VerifyStatus() (err error) {
var status string
if status, err = sync.ReadStringN(4); err != nil {
return err
}
log := bytes.NewBufferString(fmt.Sprintf("<-- %s", status))
defer func() {
debugLog(log.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))
var msg string
if msg, err = sync.ReadStringN(int(tmpUint32)); err != nil {
return err
}
log.WriteString(msg)
if status == "FAIL" {
err = fmt.Errorf("sync verify status (fail): %s", msg)
return
}
if status != "OKAY" {
err = fmt.Errorf("sync verify status: Unknown error: %s", msg)
return
}
return
}
var syncReadChunkDone = errors.New("sync read chunk done")
func (sync syncTransport) WriteStream(dest io.Writer) (err error) {
var chunk []byte
save := func() error {
if chunk, err = sync.readChunk(); err != nil && err != syncReadChunkDone {
return fmt.Errorf("sync read chunk: %w", err)
}
if err == syncReadChunkDone {
return err
}
if err = _send(dest, chunk); err != nil {
return fmt.Errorf("sync write stream: %w", err)
}
return nil
}
for err == nil {
err = save()
}
if err == syncReadChunkDone {
err = nil
}
return
}
func (sync syncTransport) readChunk() (chunk []byte, err error) {
var status string
if status, err = sync.ReadStringN(4); err != nil {
return nil, err
}
log := bytes.NewBufferString("")
defer func() { debugLog(log.String()) }()
var tmpUint32 uint32
if tmpUint32, err = sync.ReadUint32(); err != nil {
return nil, fmt.Errorf("read chunk (length): %w", err)
}
if status == "FAIL" {
log.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)
return
}
switch status {
case "DONE":
log.WriteString(fmt.Sprintf("<-- %s", status))
err = syncReadChunkDone
return
case "DATA":
log.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))
err = errors.New("unknown error")
}
log.WriteString("......")
return
}
func (sync syncTransport) ReadDirectoryEntry() (entry DeviceFileInfo, err error) {
var status string
if status, err = sync.ReadStringN(4); err != nil {
return DeviceFileInfo{}, err
}
log := bytes.NewBufferString(fmt.Sprintf("<-- %s", status))
defer func() {
debugLog(log.String())
}()
if status == "DONE" {
return
}
log = 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")
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")
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")
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))
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")
return
}
func (sync syncTransport) ReadUint32() (n uint32, err error) {
err = binary.Read(sync.sock, binary.LittleEndian, &n)
return
}
func (sync syncTransport) ReadStringN(size int) (s string, err error) {
var raw []byte
if raw, err = sync.ReadBytesN(size); err != nil {
return "", err
}
return string(raw), nil
}
func (sync syncTransport) ReadBytesN(size int) (raw []byte, err error) {
_ = sync.sock.SetReadDeadline(time.Now().Add(time.Second * sync.readTimeout))
return _readN(sync.sock, size)
}
func (sync syncTransport) Close() (err error) {
if sync.sock == nil {
return nil
}
return sync.sock.Close()
}

150
hrp/pkg/gadb/transport.go Normal file
View File

@@ -0,0 +1,150 @@
package gadb
import (
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"strconv"
"time"
)
var ErrConnBroken = errors.New("socket connection broken")
var DefaultAdbReadTimeout time.Duration = 60
type transport struct {
sock net.Conn
readTimeout time.Duration
}
func newTransport(address string, readTimeout ...time.Duration) (tp transport, err error) {
if len(readTimeout) == 0 {
readTimeout = []time.Duration{DefaultAdbReadTimeout}
}
tp.readTimeout = readTimeout[0]
if tp.sock, err = net.Dial("tcp", address); err != nil {
err = fmt.Errorf("adb transport: %w", err)
}
return
}
func (t transport) Send(command string) (err error) {
msg := fmt.Sprintf("%04x%s", len(command), command)
debugLog(fmt.Sprintf("--> %s", command))
return _send(t.sock, []byte(msg))
}
func (t transport) VerifyResponse() (err error) {
var status string
if status, err = t.ReadStringN(4); err != nil {
return err
}
if status == "OKAY" {
debugLog(fmt.Sprintf("<-- %s", status))
return nil
}
var sError string
if sError, err = t.UnpackString(); err != nil {
return err
}
err = fmt.Errorf("command failed: %s", sError)
debugLog(fmt.Sprintf("<-- %s %s", status, sError))
return
}
func (t transport) ReadStringAll() (s string, err error) {
var raw []byte
raw, err = t.ReadBytesAll()
return string(raw), err
}
func (t transport) ReadBytesAll() (raw []byte, err error) {
raw, err = ioutil.ReadAll(t.sock)
debugLog(fmt.Sprintf("\r%s", raw))
return
}
func (t transport) UnpackString() (s string, err error) {
var raw []byte
raw, err = t.UnpackBytes()
return string(raw), err
}
func (t transport) UnpackBytes() (raw []byte, err error) {
var length string
if length, err = t.ReadStringN(4); err != nil {
return nil, err
}
var size int64
if size, err = strconv.ParseInt(length, 16, 64); err != nil {
return nil, err
}
raw, err = t.ReadBytesN(int(size))
debugLog(fmt.Sprintf("\r%s", raw))
return
}
func (t transport) ReadStringN(size int) (s string, err error) {
var raw []byte
if raw, err = t.ReadBytesN(size); err != nil {
return "", err
}
return string(raw), nil
}
func (t transport) ReadBytesN(size int) (raw []byte, err error) {
_ = t.sock.SetReadDeadline(time.Now().Add(time.Second * t.readTimeout))
return _readN(t.sock, size)
}
func (t transport) Close() (err error) {
if t.sock == nil {
return nil
}
return t.sock.Close()
}
func (t transport) CreateSyncTransport() (sTp syncTransport, err error) {
if err = t.Send("sync:"); err != nil {
return syncTransport{}, err
}
if err = t.VerifyResponse(); err != nil {
return syncTransport{}, err
}
sTp = newSyncTransport(t.sock, t.readTimeout)
return
}
func _send(writer io.Writer, msg []byte) (err error) {
for totalSent := 0; totalSent < len(msg); {
var sent int
if sent, err = writer.Write(msg[totalSent:]); err != nil {
return err
}
if sent == 0 {
return ErrConnBroken
}
totalSent += sent
}
return
}
func _readN(reader io.Reader, size int) (raw []byte, err error) {
raw = make([]byte, 0, size)
for len(raw) < size {
buf := make([]byte, size-len(raw))
var n int
if n, err = io.ReadFull(reader, buf); err != nil {
return nil, err
}
if n == 0 {
return nil, ErrConnBroken
}
raw = append(raw, buf...)
}
return
}

View File

@@ -0,0 +1,26 @@
package gadb
import (
"testing"
)
func Test_transport_VerifyResponse(t *testing.T) {
SetDebug(true)
transport, err := newTransport("localhost:5037")
if err != nil {
t.Fatal(err)
}
defer transport.Close()
// err = transport.Send("host:123version")
err = transport.Send("host:version")
if err != nil {
t.Fatal(err)
}
err = transport.VerifyResponse()
if err != nil {
t.Fatal(err)
}
}

View File

@@ -9,13 +9,13 @@ import (
"reflect"
"strings"
"github.com/electricbubble/gadb"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v4/hrp/internal/code"
"github.com/httprunner/httprunner/v4/hrp/internal/json"
"github.com/httprunner/httprunner/v4/hrp/internal/myexec"
"github.com/httprunner/httprunner/v4/hrp/pkg/gadb"
)
var (

View File

@@ -11,11 +11,11 @@ import (
"strings"
"time"
"github.com/electricbubble/gadb"
"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"
)
var errDriverNotImplemented = errors.New("driver method not implemented")