mirror of
https://github.com/httprunner/httprunner.git
synced 2026-09-05 07:26:55 +08:00
move ghdc to pkg
This commit is contained in:
@@ -2,7 +2,6 @@
|
|||||||
*.exe
|
*.exe
|
||||||
*.exe~
|
*.exe~
|
||||||
*.dll
|
*.dll
|
||||||
*.so
|
|
||||||
*.dylib
|
*.dylib
|
||||||
*.apk
|
*.apk
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
v5.0.0-beta-2503052133
|
v5.0.0-beta-2503052140
|
||||||
|
|||||||
-1
Submodule pkg/ghdc deleted from ecb76cf5bd
@@ -0,0 +1,86 @@
|
|||||||
|
# ghdc
|
||||||
|
|
||||||
|
ghdc 是一个用于与鸿蒙设备进行交互的工具,封装了各种 HDC(鸿蒙的 ADB)命令和 UI 自动化能力。
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
ghdc \
|
||||||
|
├── client.go 封装 hdc list targets 等非设备命令 \
|
||||||
|
├── device.go 封装 hdc -t connectkey shell 等指定设备的命令 \
|
||||||
|
└── uidevice.go 封装设备所有自动化能力
|
||||||
|
|
||||||
|
## hdc 命令调用
|
||||||
|
目前支持的能力:
|
||||||
|
- 获取设备
|
||||||
|
- 文件传输
|
||||||
|
- shell 命令
|
||||||
|
- 端口挂载
|
||||||
|
- 属性获取(brand, model, 版本等)
|
||||||
|
- 截图
|
||||||
|
|
||||||
|
`hdc`与鸿蒙的关系和`adb`与安卓关系一致,其架构与 adb server 相同,分为 client 和 server。`hdc start` 会启动一个 hdc server,监听本地的 8710 端口。我们也是和该 8710 端口通信执行 HDC 命令。目前支持常用的 hdc 能力,并可扩展至所有 hdc 能力。
|
||||||
|
|
||||||
|
```shell
|
||||||
|
hdc -m -s ::ffff:127.0.0.1:8710
|
||||||
|
```
|
||||||
|
hdc 命令分为两类:
|
||||||
|
- 不指定设备的命令:例如 `hdc list targets`。逻辑封装在 client.go 中。与 gadb 类似,所有的命令通过 RPC 方式与 hdc server 直接通信。
|
||||||
|
- 指定设备的命令:例如 `hdc -t connectkey shell`。逻辑封装在 device.go 中。这些命令会在与 server 建连时指定执行的设备。
|
||||||
|
|
||||||
|
## UI Test 自动化能力
|
||||||
|
目前支持的能力:
|
||||||
|
- 点击\滑动\输入
|
||||||
|
- 按键操作
|
||||||
|
- 手势操作
|
||||||
|
- TouchDown/TouchMove/TouchUp 屏幕操作
|
||||||
|
- 屏幕旋转
|
||||||
|
- 音量设置
|
||||||
|
- 图片流获取
|
||||||
|
- 控件信息监听
|
||||||
|
- 简单控件操作
|
||||||
|
|
||||||
|
Harmony Next 内置了 UI Test 服务,提供了所有常用的自动化能力。并且这个服务也部分开源,支持二次开发。由于协议未开源,我们通过逆向工程绕过 JS API,直接通过 socket 与 UI Test 服务进行通信,操作手机。代码能力封装在 uidevice.go 中。
|
||||||
|
|
||||||
|
UI Test 协议分为两类:
|
||||||
|
- 无 session 单次返回长连接:例如点击、滑动等,发出命令即可,返回一个执行成功或失败。
|
||||||
|
- 有 session 多次返回长连接:例如获取屏幕图片流、监听控件信息变化等。
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/httprunner/httprunner/v5/pkg/ghdc"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
client, err := ghdc.NewClient()
|
||||||
|
checkErr(err, "fail to connect hdc server")
|
||||||
|
|
||||||
|
devices, err := client.DeviceList()
|
||||||
|
checkErr(err)
|
||||||
|
|
||||||
|
if len(devices) == 0 {
|
||||||
|
log.Fatalln("list of devices is empty")
|
||||||
|
}
|
||||||
|
dev := devices[0]
|
||||||
|
driver, err := ghdc.NewUIDriver(dev)
|
||||||
|
checkErr(err, "fail to init device uiDriver")
|
||||||
|
|
||||||
|
err = driver.Touch(225, 1715)
|
||||||
|
checkErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
```
|
||||||
Binary file not shown.
@@ -0,0 +1,166 @@
|
|||||||
|
package ghdc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const HdcServerPort = 8710
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
host string
|
||||||
|
port int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient() (Client, error) {
|
||||||
|
return NewClientWith("localhost")
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClientWith(host string, port ...int) (hdClient Client, err error) {
|
||||||
|
if len(port) == 0 {
|
||||||
|
port = []int{HdcServerPort}
|
||||||
|
}
|
||||||
|
hdClient.host = host
|
||||||
|
hdClient.port = port[0]
|
||||||
|
|
||||||
|
var tp transport
|
||||||
|
if tp, err = hdClient.createTransport(); err != nil {
|
||||||
|
return Client{}, err
|
||||||
|
}
|
||||||
|
defer func() { _ = tp.Close() }()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) ServerVersion() (version string, err error) {
|
||||||
|
return c.executeCommand("version")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) DeviceSerialList() (serials []string, err error) {
|
||||||
|
var resp string
|
||||||
|
if resp, err = c.executeCommand("list targets"); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lines := strings.Split(resp, "\n")
|
||||||
|
serials = make([]string, 0, len(lines))
|
||||||
|
|
||||||
|
for i := range lines {
|
||||||
|
if lines[i] == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields := strings.Fields(lines[i])
|
||||||
|
serials = append(serials, fields[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) DeviceList() (devices []Device, err error) {
|
||||||
|
var resp string
|
||||||
|
if resp, err = c.executeCommand("list targets -v"); 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) < 4 || len(fields[0]) == 0 {
|
||||||
|
debugLog(fmt.Sprintf("can't parse: %s", line))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if fields[2] == "Offline" {
|
||||||
|
log.Printf("device [%s] Offline ", fields[0])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if fields[1] == "UART" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
slickAttrs := make(map[string]string)
|
||||||
|
slickAttrs["usb"] = fields[1]
|
||||||
|
slickAttrs["device_status"] = fields[2]
|
||||||
|
device, err := NewDevice(c, fields[0], slickAttrs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
devices = append(devices, device)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) ForwardList() (deviceForward []DeviceForward, err error) {
|
||||||
|
var resp string
|
||||||
|
if resp, err = c.executeCommand("fport ls"); 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])
|
||||||
|
line = strings.ReplaceAll(line, "'", "")
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
deviceForward = append(deviceForward, DeviceForward{Local: fields[0], Remote: fields[1]})
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) Connect(ip string, port ...int) (err error) {
|
||||||
|
if len(port) == 0 {
|
||||||
|
port = []int{HdcServerPort}
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp string
|
||||||
|
if resp, err = c.executeCommand(fmt.Sprintf("tconn %s:%d", ip, port[0])); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(resp, "connected to") && !strings.HasPrefix(resp, "already connected to") {
|
||||||
|
return fmt.Errorf("hd connect: %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.SendCommand("kill")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) createTransport() (tp transport, err error) {
|
||||||
|
return newTransport(fmt.Sprintf("%s:%d", c.host, c.port), false, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Client) executeCommand(command string) (resp string, err error) {
|
||||||
|
var tp transport
|
||||||
|
if tp, err = c.createTransport(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer func() { _ = tp.Close() }()
|
||||||
|
|
||||||
|
if err = tp.SendCommand(command); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return tp.ReadStringAll()
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
package ghdc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestClient_ServerVersion(t *testing.T) {
|
||||||
|
SetDebug(true)
|
||||||
|
|
||||||
|
hdcClient, err := NewClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hdServerVersion, err := hdcClient.ServerVersion()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log(hdServerVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClient_DeviceSerialList(t *testing.T) {
|
||||||
|
SetDebug(true)
|
||||||
|
|
||||||
|
hdcClient, err := NewClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
serials, err := hdcClient.DeviceSerialList()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range serials {
|
||||||
|
t.Log(serials[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClient_DeviceList(t *testing.T) {
|
||||||
|
SetDebug(true)
|
||||||
|
|
||||||
|
hdcClient, err := NewClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
devices, err := hdcClient.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)
|
||||||
|
|
||||||
|
hdcClient, err := NewClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceForwardList, err := hdcClient.ForwardList()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range deviceForwardList {
|
||||||
|
t.Log(deviceForwardList[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClient_Connect(t *testing.T) {
|
||||||
|
hdcClient, err := NewClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
SetDebug(true)
|
||||||
|
|
||||||
|
err = hdcClient.Connect("192.168.1.28")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClient_KillServer(t *testing.T) {
|
||||||
|
SetDebug(true)
|
||||||
|
|
||||||
|
hdcClient, err := NewClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = hdcClient.KillServer()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package ghdc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConnectionPool struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
conns chan net.Conn
|
||||||
|
host string
|
||||||
|
port string
|
||||||
|
maxConns int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConnectionPool(host string, port string, maxConns int) (*ConnectionPool, error) {
|
||||||
|
pool := &ConnectionPool{
|
||||||
|
conns: make(chan net.Conn, maxConns),
|
||||||
|
host: host,
|
||||||
|
port: port,
|
||||||
|
maxConns: maxConns,
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < maxConns; i++ {
|
||||||
|
conn, err := pool.newConnection()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pool.conns <- conn
|
||||||
|
}
|
||||||
|
|
||||||
|
return pool, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ConnectionPool) newConnection() (net.Conn, error) {
|
||||||
|
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%s", p.host, p.port), 5*time.Second)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ConnectionPool) getConnection() (net.Conn, error) {
|
||||||
|
select {
|
||||||
|
case conn := <-p.conns:
|
||||||
|
return conn, nil
|
||||||
|
default:
|
||||||
|
// 如果池中没有可用连接,创建一个新的连接
|
||||||
|
return p.newConnection()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ConnectionPool) releaseConnection(conn net.Conn) {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case p.conns <- conn:
|
||||||
|
// 放回连接池成功
|
||||||
|
default:
|
||||||
|
// 连接池已满,关闭连接
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ConnectionPool) close() {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
|
||||||
|
close(p.conns)
|
||||||
|
for conn := range p.conns {
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,618 @@
|
|||||||
|
package ghdc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"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)
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeviceState string
|
||||||
|
|
||||||
|
const (
|
||||||
|
StateOnline DeviceState = "Online"
|
||||||
|
StateOffline DeviceState = "Offline"
|
||||||
|
)
|
||||||
|
|
||||||
|
var deviceStateStrings = map[string]DeviceState{
|
||||||
|
"Offline": StateOffline,
|
||||||
|
"Connected": StateOnline,
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeyCode int
|
||||||
|
|
||||||
|
const (
|
||||||
|
KEYCODE_FN KeyCode = 0
|
||||||
|
KEYCODE_UNKNOWN KeyCode = -1
|
||||||
|
KEYCODE_HOME KeyCode = 1
|
||||||
|
KEYCODE_BACK KeyCode = 2
|
||||||
|
KEYCODE_MEDIA_PLAY_PAUSE KeyCode = 10
|
||||||
|
KEYCODE_MEDIA_STOP KeyCode = 11
|
||||||
|
KEYCODE_MEDIA_NEXT KeyCode = 12
|
||||||
|
KEYCODE_MEDIA_PREVIOUS KeyCode = 13
|
||||||
|
KEYCODE_MEDIA_REWIND KeyCode = 14
|
||||||
|
KEYCODE_MEDIA_FAST_FORWARD KeyCode = 15
|
||||||
|
KEYCODE_VOLUME_UP KeyCode = 16
|
||||||
|
KEYCODE_VOLUME_DOWN KeyCode = 17
|
||||||
|
KEYCODE_POWER KeyCode = 18
|
||||||
|
KEYCODE_CAMERA KeyCode = 19
|
||||||
|
KEYCODE_VOLUME_MUTE KeyCode = 22
|
||||||
|
KEYCODE_MUTE KeyCode = 23
|
||||||
|
KEYCODE_BRIGHTNESS_UP KeyCode = 40
|
||||||
|
KEYCODE_BRIGHTNESS_DOWN KeyCode = 41
|
||||||
|
KEYCODE_NUM_0 KeyCode = 2000
|
||||||
|
KEYCODE_NUM_1 KeyCode = 2001
|
||||||
|
KEYCODE_NUM_2 KeyCode = 2002
|
||||||
|
KEYCODE_NUM_3 KeyCode = 2003
|
||||||
|
KEYCODE_NUM_4 KeyCode = 2004
|
||||||
|
KEYCODE_NUM_5 KeyCode = 2005
|
||||||
|
KEYCODE_NUM_6 KeyCode = 2006
|
||||||
|
KEYCODE_NUM_7 KeyCode = 2007
|
||||||
|
KEYCODE_NUM_8 KeyCode = 2008
|
||||||
|
KEYCODE_NUM_9 KeyCode = 2009
|
||||||
|
KEYCODE_STAR KeyCode = 2010
|
||||||
|
KEYCODE_POUND KeyCode = 2011
|
||||||
|
KEYCODE_DPAD_UP KeyCode = 2012
|
||||||
|
KEYCODE_DPAD_DOWN KeyCode = 2013
|
||||||
|
KEYCODE_DPAD_LEFT KeyCode = 2014
|
||||||
|
KEYCODE_DPAD_RIGHT KeyCode = 2015
|
||||||
|
KEYCODE_DPAD_CENTER KeyCode = 2016
|
||||||
|
KEYCODE_A KeyCode = 2017
|
||||||
|
KEYCODE_B KeyCode = 2018
|
||||||
|
KEYCODE_C KeyCode = 2019
|
||||||
|
KEYCODE_D KeyCode = 2020
|
||||||
|
KEYCODE_E KeyCode = 2021
|
||||||
|
KEYCODE_F KeyCode = 2022
|
||||||
|
KEYCODE_G KeyCode = 2023
|
||||||
|
KEYCODE_H KeyCode = 2024
|
||||||
|
KEYCODE_I KeyCode = 2025
|
||||||
|
KEYCODE_J KeyCode = 2026
|
||||||
|
KEYCODE_K KeyCode = 2027
|
||||||
|
KEYCODE_L KeyCode = 2028
|
||||||
|
KEYCODE_M KeyCode = 2029
|
||||||
|
KEYCODE_N KeyCode = 2030
|
||||||
|
KEYCODE_O KeyCode = 2031
|
||||||
|
KEYCODE_P KeyCode = 2032
|
||||||
|
KEYCODE_Q KeyCode = 2033
|
||||||
|
KEYCODE_R KeyCode = 2034
|
||||||
|
KEYCODE_S KeyCode = 2035
|
||||||
|
KEYCODE_T KeyCode = 2036
|
||||||
|
KEYCODE_U KeyCode = 2037
|
||||||
|
KEYCODE_V KeyCode = 2038
|
||||||
|
KEYCODE_W KeyCode = 2039
|
||||||
|
KEYCODE_X KeyCode = 2040
|
||||||
|
KEYCODE_Y KeyCode = 2041
|
||||||
|
KEYCODE_Z KeyCode = 2042
|
||||||
|
KEYCODE_COMMA KeyCode = 2043
|
||||||
|
KEYCODE_PERIOD KeyCode = 2044
|
||||||
|
KEYCODE_ALT_LEFT KeyCode = 2045
|
||||||
|
KEYCODE_ALT_RIGHT KeyCode = 2046
|
||||||
|
KEYCODE_SHIFT_LEFT KeyCode = 2047
|
||||||
|
KEYCODE_SHIFT_RIGHT KeyCode = 2048
|
||||||
|
KEYCODE_TAB KeyCode = 2049
|
||||||
|
KEYCODE_SPACE KeyCode = 2050
|
||||||
|
KEYCODE_SYM KeyCode = 2051
|
||||||
|
KEYCODE_EXPLORER KeyCode = 2052
|
||||||
|
KEYCODE_ENVELOPE KeyCode = 2053
|
||||||
|
KEYCODE_ENTER KeyCode = 2054
|
||||||
|
KEYCODE_DEL KeyCode = 2055
|
||||||
|
KEYCODE_GRAVE KeyCode = 2056
|
||||||
|
KEYCODE_MINUS KeyCode = 2057
|
||||||
|
KEYCODE_EQUALS KeyCode = 2058
|
||||||
|
KEYCODE_LEFT_BRACKET KeyCode = 2059
|
||||||
|
KEYCODE_RIGHT_BRACKET KeyCode = 2060
|
||||||
|
KEYCODE_BACKSLASH KeyCode = 2061
|
||||||
|
KEYCODE_SEMICOLON KeyCode = 2062
|
||||||
|
KEYCODE_APOSTROPHE KeyCode = 2063
|
||||||
|
KEYCODE_SLASH KeyCode = 2064
|
||||||
|
KEYCODE_AT KeyCode = 2065
|
||||||
|
KEYCODE_PLUS KeyCode = 2066
|
||||||
|
KEYCODE_MENU KeyCode = 2067
|
||||||
|
KEYCODE_PAGE_UP KeyCode = 2068
|
||||||
|
KEYCODE_PAGE_DOWN KeyCode = 2069
|
||||||
|
KEYCODE_ESCAPE KeyCode = 2070
|
||||||
|
KEYCODE_FORWARD_DEL KeyCode = 2071
|
||||||
|
KEYCODE_CTRL_LEFT KeyCode = 2072
|
||||||
|
KEYCODE_CTRL_RIGHT KeyCode = 2073
|
||||||
|
KEYCODE_CAPS_LOCK KeyCode = 2074
|
||||||
|
KEYCODE_SCROLL_LOCK KeyCode = 2075
|
||||||
|
KEYCODE_META_LEFT KeyCode = 2076
|
||||||
|
KEYCODE_META_RIGHT KeyCode = 2077
|
||||||
|
KEYCODE_FUNCTION KeyCode = 2078
|
||||||
|
KEYCODE_SYSRQ KeyCode = 2079
|
||||||
|
KEYCODE_BREAK KeyCode = 2080
|
||||||
|
KEYCODE_MOVE_HOME KeyCode = 2081
|
||||||
|
KEYCODE_MOVE_END KeyCode = 2082
|
||||||
|
KEYCODE_INSERT KeyCode = 2083
|
||||||
|
KEYCODE_FORWARD KeyCode = 2084
|
||||||
|
KEYCODE_MEDIA_PLAY KeyCode = 2085
|
||||||
|
KEYCODE_MEDIA_PAUSE KeyCode = 2086
|
||||||
|
KEYCODE_MEDIA_CLOSE KeyCode = 2087
|
||||||
|
KEYCODE_MEDIA_EJECT KeyCode = 2088
|
||||||
|
KEYCODE_MEDIA_RECORD KeyCode = 2089
|
||||||
|
KEYCODE_F1 KeyCode = 2090
|
||||||
|
KEYCODE_F2 KeyCode = 2091
|
||||||
|
KEYCODE_F3 KeyCode = 2092
|
||||||
|
KEYCODE_F4 KeyCode = 2093
|
||||||
|
KEYCODE_F5 KeyCode = 2094
|
||||||
|
KEYCODE_F6 KeyCode = 2095
|
||||||
|
KEYCODE_F7 KeyCode = 2096
|
||||||
|
KEYCODE_F8 KeyCode = 2097
|
||||||
|
KEYCODE_F9 KeyCode = 2098
|
||||||
|
KEYCODE_F10 KeyCode = 2099
|
||||||
|
KEYCODE_F11 KeyCode = 2100
|
||||||
|
KEYCODE_F12 KeyCode = 2101
|
||||||
|
KEYCODE_NUM_LOCK KeyCode = 2102
|
||||||
|
KEYCODE_NUMPAD_0 KeyCode = 2103
|
||||||
|
KEYCODE_NUMPAD_1 KeyCode = 2104
|
||||||
|
KEYCODE_NUMPAD_2 KeyCode = 2105
|
||||||
|
KEYCODE_NUMPAD_3 KeyCode = 2106
|
||||||
|
KEYCODE_NUMPAD_4 KeyCode = 2107
|
||||||
|
KEYCODE_NUMPAD_5 KeyCode = 2108
|
||||||
|
KEYCODE_NUMPAD_6 KeyCode = 2109
|
||||||
|
KEYCODE_NUMPAD_7 KeyCode = 2110
|
||||||
|
KEYCODE_NUMPAD_8 KeyCode = 2111
|
||||||
|
KEYCODE_NUMPAD_9 KeyCode = 2112
|
||||||
|
KEYCODE_NUMPAD_DIVIDE KeyCode = 2113
|
||||||
|
KEYCODE_NUMPAD_MULTIPLY KeyCode = 2114
|
||||||
|
KEYCODE_NUMPAD_SUBTRACT KeyCode = 2115
|
||||||
|
KEYCODE_NUMPAD_ADD KeyCode = 2116
|
||||||
|
KEYCODE_NUMPAD_DOT KeyCode = 2117
|
||||||
|
KEYCODE_NUMPAD_COMMA KeyCode = 2118
|
||||||
|
KEYCODE_NUMPAD_ENTER KeyCode = 2119
|
||||||
|
KEYCODE_NUMPAD_EQUALS KeyCode = 2120
|
||||||
|
KEYCODE_NUMPAD_LEFT_PAREN KeyCode = 2121
|
||||||
|
KEYCODE_NUMPAD_RIGHT_PAREN KeyCode = 2122
|
||||||
|
KEYCODE_VIRTUAL_MULTITASK KeyCode = 2210
|
||||||
|
KEYCODE_SLEEP KeyCode = 2600
|
||||||
|
KEYCODE_ZENKAKU_HANKAKU KeyCode = 2601
|
||||||
|
KEYCODE_ND KeyCode = 2602
|
||||||
|
KEYCODE_RO KeyCode = 2603
|
||||||
|
KEYCODE_KATAKANA KeyCode = 2604
|
||||||
|
KEYCODE_HIRAGANA KeyCode = 2605
|
||||||
|
KEYCODE_HENKAN KeyCode = 2606
|
||||||
|
KEYCODE_KATAKANA_HIRAGANA KeyCode = 2607
|
||||||
|
KEYCODE_MUHENKAN KeyCode = 2608
|
||||||
|
KEYCODE_LINEFEED KeyCode = 2609
|
||||||
|
KEYCODE_MACRO KeyCode = 2610
|
||||||
|
KEYCODE_NUMPAD_PLUSMINUS KeyCode = 2611
|
||||||
|
KEYCODE_SCALE KeyCode = 2612
|
||||||
|
KEYCODE_HANGUEL KeyCode = 2613
|
||||||
|
KEYCODE_HANJA KeyCode = 2614
|
||||||
|
KEYCODE_YEN KeyCode = 2615
|
||||||
|
KEYCODE_STOP KeyCode = 2616
|
||||||
|
KEYCODE_AGAIN KeyCode = 2617
|
||||||
|
KEYCODE_PROPS KeyCode = 2618
|
||||||
|
KEYCODE_UNDO KeyCode = 2619
|
||||||
|
KEYCODE_COPY KeyCode = 2620
|
||||||
|
KEYCODE_OPEN KeyCode = 2621
|
||||||
|
KEYCODE_PASTE KeyCode = 2622
|
||||||
|
KEYCODE_FIND KeyCode = 2623
|
||||||
|
KEYCODE_CUT KeyCode = 2624
|
||||||
|
KEYCODE_HELP KeyCode = 2625
|
||||||
|
KEYCODE_CALC KeyCode = 2626
|
||||||
|
KEYCODE_FILE KeyCode = 2627
|
||||||
|
KEYCODE_BOOKMARKS KeyCode = 2628
|
||||||
|
KEYCODE_NEXT KeyCode = 2629
|
||||||
|
KEYCODE_PLAYPAUSE KeyCode = 2630
|
||||||
|
KEYCODE_PREVIOUS KeyCode = 2631
|
||||||
|
KEYCODE_STOPCD KeyCode = 2632
|
||||||
|
KEYCODE_CONFIG KeyCode = 2634
|
||||||
|
KEYCODE_REFRESH KeyCode = 2635
|
||||||
|
KEYCODE_EXIT KeyCode = 2636
|
||||||
|
KEYCODE_EDIT KeyCode = 2637
|
||||||
|
KEYCODE_SCROLLUP KeyCode = 2638
|
||||||
|
KEYCODE_SCROLLDOWN KeyCode = 2639
|
||||||
|
KEYCODE_NEW KeyCode = 2640
|
||||||
|
KEYCODE_REDO KeyCode = 2641
|
||||||
|
KEYCODE_CLOSE KeyCode = 2642
|
||||||
|
KEYCODE_PLAY KeyCode = 2643
|
||||||
|
KEYCODE_BASSBOOST KeyCode = 2644
|
||||||
|
KEYCODE_PRINT KeyCode = 2645
|
||||||
|
KEYCODE_CHAT KeyCode = 2646
|
||||||
|
KEYCODE_FINANCE KeyCode = 2647
|
||||||
|
KEYCODE_CANCEL KeyCode = 2648
|
||||||
|
KEYCODE_KBDILLUM_TOGGLE KeyCode = 2649
|
||||||
|
KEYCODE_KBDILLUM_DOWN KeyCode = 2650
|
||||||
|
KEYCODE_KBDILLUM_UP KeyCode = 2651
|
||||||
|
KEYCODE_SEND KeyCode = 2652
|
||||||
|
KEYCODE_REPLY KeyCode = 2653
|
||||||
|
KEYCODE_FORWARDMAIL KeyCode = 2654
|
||||||
|
KEYCODE_SAVE KeyCode = 2655
|
||||||
|
KEYCODE_DOCUMENTS KeyCode = 2656
|
||||||
|
KEYCODE_VIDEO_NEXT KeyCode = 2657
|
||||||
|
KEYCODE_VIDEO_PREV KeyCode = 2658
|
||||||
|
KEYCODE_BRIGHTNESS_CYCLE KeyCode = 2659
|
||||||
|
KEYCODE_BRIGHTNESS_ZERO KeyCode = 2660
|
||||||
|
KEYCODE_DISPLAY_OFF KeyCode = 2661
|
||||||
|
KEYCODE_BTN_MISC KeyCode = 2662
|
||||||
|
KEYCODE_GOTO KeyCode = 2663
|
||||||
|
KEYCODE_INFO KeyCode = 2664
|
||||||
|
KEYCODE_PROGRAM KeyCode = 2665
|
||||||
|
KEYCODE_PVR KeyCode = 2666
|
||||||
|
KEYCODE_SUBTITLE KeyCode = 2667
|
||||||
|
KEYCODE_FULL_SCREEN KeyCode = 2668
|
||||||
|
KEYCODE_KEYBOARD KeyCode = 2669
|
||||||
|
KEYCODE_ASPECT_RATIO KeyCode = 2670
|
||||||
|
KEYCODE_PC KeyCode = 2671
|
||||||
|
KEYCODE_TV KeyCode = 2672
|
||||||
|
KEYCODE_TV2 KeyCode = 2673
|
||||||
|
KEYCODE_VCR KeyCode = 2674
|
||||||
|
KEYCODE_VCR2 KeyCode = 2675
|
||||||
|
KEYCODE_SAT KeyCode = 2676
|
||||||
|
KEYCODE_CD KeyCode = 2677
|
||||||
|
KEYCODE_TAPE KeyCode = 2678
|
||||||
|
KEYCODE_TUNER KeyCode = 2679
|
||||||
|
KEYCODE_PLAYER KeyCode = 2680
|
||||||
|
KEYCODE_DVD KeyCode = 2681
|
||||||
|
KEYCODE_AUDIO KeyCode = 2682
|
||||||
|
KEYCODE_VIDEO KeyCode = 2683
|
||||||
|
KEYCODE_MEMO KeyCode = 2684
|
||||||
|
KEYCODE_CALENDAR KeyCode = 2685
|
||||||
|
KEYCODE_RED KeyCode = 2686
|
||||||
|
KEYCODE_GREEN KeyCode = 2687
|
||||||
|
KEYCODE_YELLOW KeyCode = 2688
|
||||||
|
KEYCODE_BLUE KeyCode = 2689
|
||||||
|
KEYCODE_CHANNELUP KeyCode = 2690
|
||||||
|
KEYCODE_CHANNELDOWN KeyCode = 2691
|
||||||
|
KEYCODE_LAST KeyCode = 2692
|
||||||
|
KEYCODE_RESTART KeyCode = 2693
|
||||||
|
KEYCODE_SLOW KeyCode = 2694
|
||||||
|
KEYCODE_SHUFFLE KeyCode = 2695
|
||||||
|
KEYCODE_VIDEOPHONE KeyCode = 2696
|
||||||
|
KEYCODE_GAMES KeyCode = 2697
|
||||||
|
KEYCODE_ZOOMIN KeyCode = 2698
|
||||||
|
KEYCODE_ZOOMOUT KeyCode = 2699
|
||||||
|
KEYCODE_ZOOMRESET KeyCode = 2700
|
||||||
|
KEYCODE_WORDPROCESSOR KeyCode = 2701
|
||||||
|
KEYCODE_EDITOR KeyCode = 2702
|
||||||
|
KEYCODE_SPREADSHEET KeyCode = 2703
|
||||||
|
KEYCODE_GRAPHICSEDITOR KeyCode = 2704
|
||||||
|
KEYCODE_PRESENTATION KeyCode = 2705
|
||||||
|
KEYCODE_DATABASE KeyCode = 2706
|
||||||
|
KEYCODE_NEWS KeyCode = 2707
|
||||||
|
KEYCODE_VOICEMAIL KeyCode = 2708
|
||||||
|
KEYCODE_ADDRESSBOOK KeyCode = 2709
|
||||||
|
KEYCODE_MESSENGER KeyCode = 2710
|
||||||
|
KEYCODE_BRIGHTNESS_TOGGLE KeyCode = 2711
|
||||||
|
KEYCODE_SPELLCHECK KeyCode = 2712
|
||||||
|
KEYCODE_COFFEE KeyCode = 2713
|
||||||
|
KEYCODE_MEDIA_REPEAT KeyCode = 2714
|
||||||
|
KEYCODE_IMAGES KeyCode = 2715
|
||||||
|
KEYCODE_BUTTONCONFIG KeyCode = 2716
|
||||||
|
KEYCODE_TASKMANAGER KeyCode = 2717
|
||||||
|
KEYCODE_JOURNAL KeyCode = 2718
|
||||||
|
KEYCODE_CONTROLPANEL KeyCode = 2719
|
||||||
|
KEYCODE_APPSELECT KeyCode = 2720
|
||||||
|
KEYCODE_SCREENSAVER KeyCode = 2721
|
||||||
|
KEYCODE_ASSISTANT KeyCode = 2722
|
||||||
|
KEYCODE_KBD_LAYOUT_NEXT KeyCode = 2723
|
||||||
|
KEYCODE_BRIGHTNESS_MIN KeyCode = 2724
|
||||||
|
KEYCODE_BRIGHTNESS_MAX KeyCode = 2725
|
||||||
|
KEYCODE_KBDINPUTASSIST_PREV KeyCode = 2726
|
||||||
|
KEYCODE_KBDINPUTASSIST_NEXT KeyCode = 2727
|
||||||
|
KEYCODE_KBDINPUTASSIST_PREVGROUP KeyCode = 2728
|
||||||
|
KEYCODE_KBDINPUTASSIST_NEXTGROUP KeyCode = 2729
|
||||||
|
KEYCODE_KBDINPUTASSIST_ACCEPT KeyCode = 2730
|
||||||
|
KEYCODE_KBDINPUTASSIST_CANCEL KeyCode = 2731
|
||||||
|
KEYCODE_FRONT KeyCode = 2800
|
||||||
|
KEYCODE_SETUP KeyCode = 2801
|
||||||
|
KEYCODE_WAKE_UP KeyCode = 2802
|
||||||
|
KEYCODE_SENDFILE KeyCode = 2803
|
||||||
|
KEYCODE_DELETEFILE KeyCode = 2804
|
||||||
|
KEYCODE_XFER KeyCode = 2805
|
||||||
|
KEYCODE_PROG1 KeyCode = 2806
|
||||||
|
KEYCODE_PROG2 KeyCode = 2807
|
||||||
|
KEYCODE_MSDOS KeyCode = 2808
|
||||||
|
KEYCODE_SCREENLOCK KeyCode = 2809
|
||||||
|
KEYCODE_DIRECTION_ROTATE_DISPLAY KeyCode = 2810
|
||||||
|
KEYCODE_CYCLEWINDOWS KeyCode = 2811
|
||||||
|
KEYCODE_COMPUTER KeyCode = 2812
|
||||||
|
KEYCODE_EJECTCLOSECD KeyCode = 2813
|
||||||
|
KEYCODE_ISO KeyCode = 2814
|
||||||
|
KEYCODE_MOVE KeyCode = 2815
|
||||||
|
KEYCODE_F13 KeyCode = 2816
|
||||||
|
KEYCODE_F14 KeyCode = 2817
|
||||||
|
KEYCODE_F15 KeyCode = 2818
|
||||||
|
KEYCODE_F16 KeyCode = 2819
|
||||||
|
KEYCODE_F17 KeyCode = 2820
|
||||||
|
KEYCODE_F18 KeyCode = 2821
|
||||||
|
KEYCODE_F19 KeyCode = 2822
|
||||||
|
KEYCODE_F20 KeyCode = 2823
|
||||||
|
KEYCODE_F21 KeyCode = 2824
|
||||||
|
KEYCODE_F22 KeyCode = 2825
|
||||||
|
KEYCODE_F23 KeyCode = 2826
|
||||||
|
KEYCODE_F24 KeyCode = 2827
|
||||||
|
KEYCODE_PROG3 KeyCode = 2828
|
||||||
|
KEYCODE_PROG4 KeyCode = 2829
|
||||||
|
KEYCODE_DASHBOARD KeyCode = 2830
|
||||||
|
KEYCODE_SUSPEND KeyCode = 2831
|
||||||
|
KEYCODE_HP KeyCode = 2832
|
||||||
|
KEYCODE_SOUND KeyCode = 2833
|
||||||
|
KEYCODE_QUESTION KeyCode = 2834
|
||||||
|
KEYCODE_CONNECT KeyCode = 2836
|
||||||
|
KEYCODE_SPORT KeyCode = 2837
|
||||||
|
KEYCODE_SHOP KeyCode = 2838
|
||||||
|
KEYCODE_ALTERASE KeyCode = 2839
|
||||||
|
KEYCODE_SWITCHVIDEOMODE KeyCode = 2841
|
||||||
|
KEYCODE_BATTERY KeyCode = 2842
|
||||||
|
KEYCODE_BLUETOOTH KeyCode = 2843
|
||||||
|
KEYCODE_WLAN KeyCode = 2844
|
||||||
|
KEYCODE_UWB KeyCode = 2845
|
||||||
|
KEYCODE_WWAN_WIMAX KeyCode = 2846
|
||||||
|
KEYCODE_RFKILL KeyCode = 2847
|
||||||
|
KEYCODE_CHANNEL KeyCode = 3001
|
||||||
|
KEYCODE_BTN_0 KeyCode = 3100
|
||||||
|
KEYCODE_BTN_1 KeyCode = 3101
|
||||||
|
KEYCODE_BTN_2 KeyCode = 3102
|
||||||
|
KEYCODE_BTN_3 KeyCode = 3103
|
||||||
|
KEYCODE_BTN_4 KeyCode = 3104
|
||||||
|
KEYCODE_BTN_5 KeyCode = 3105
|
||||||
|
KEYCODE_BTN_6 KeyCode = 3106
|
||||||
|
KEYCODE_BTN_7 KeyCode = 3107
|
||||||
|
KEYCODE_BTN_8 KeyCode = 3108
|
||||||
|
KEYCODE_BTN_9 KeyCode = 3109
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeviceForward struct {
|
||||||
|
Local string
|
||||||
|
Remote string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Device struct {
|
||||||
|
hdClient Client
|
||||||
|
serial string
|
||||||
|
attrs map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDevice(hdClient Client, serial string, attrs map[string]string) (Device, error) {
|
||||||
|
device := Device{hdClient: hdClient, serial: serial, attrs: attrs}
|
||||||
|
model, err := device.RunShellCommand("param get const.product.model")
|
||||||
|
if err != nil {
|
||||||
|
return device, err
|
||||||
|
}
|
||||||
|
attrs["model"] = model
|
||||||
|
|
||||||
|
brand, err := device.RunShellCommand("param get const.product.brand")
|
||||||
|
if err != nil {
|
||||||
|
return device, err
|
||||||
|
}
|
||||||
|
attrs["brand"] = brand
|
||||||
|
|
||||||
|
sdkVersion, err := device.RunShellCommand("param get const.product.software.version")
|
||||||
|
if err != nil {
|
||||||
|
return device, err
|
||||||
|
}
|
||||||
|
attrs["sdkVersion"] = sdkVersion
|
||||||
|
|
||||||
|
osVersion, err := device.RunShellCommand("param get const.ohos.apiversion")
|
||||||
|
if err != nil {
|
||||||
|
return device, err
|
||||||
|
}
|
||||||
|
attrs["osVersion"] = osVersion
|
||||||
|
|
||||||
|
cpu, err := device.RunShellCommand("param get const.product.cpu.abilist")
|
||||||
|
if err != nil {
|
||||||
|
return device, err
|
||||||
|
}
|
||||||
|
attrs["cpu"] = cpu
|
||||||
|
|
||||||
|
product, err := device.RunShellCommand("param get const.product.name")
|
||||||
|
if err != nil {
|
||||||
|
return device, err
|
||||||
|
}
|
||||||
|
attrs["product"] = product
|
||||||
|
|
||||||
|
_, err = device.RunShellCommand("setenforce 1")
|
||||||
|
if err != nil {
|
||||||
|
return device, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return device, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Device) HasAttribute(key string) bool {
|
||||||
|
_, ok := d.attrs[key]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Device) Product() (string, error) {
|
||||||
|
if d.HasAttribute("product") {
|
||||||
|
return d.attrs["product"], nil
|
||||||
|
}
|
||||||
|
return "", errors.New("does not have attribute: product")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Device) Model() (string, error) {
|
||||||
|
if d.HasAttribute("model") {
|
||||||
|
return d.attrs["model"], nil
|
||||||
|
}
|
||||||
|
return "", errors.New("does not have attribute: model")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Device) Usb() (string, error) {
|
||||||
|
if d.HasAttribute("usb") {
|
||||||
|
return d.attrs["usb"], nil
|
||||||
|
}
|
||||||
|
return "", errors.New("does not have attribute: usb")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Device) DeviceInfo() map[string]string {
|
||||||
|
return d.attrs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Device) Serial() string {
|
||||||
|
return d.serial
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Device) IsUsb() (bool, error) {
|
||||||
|
usb, err := d.Usb()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return usb != "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Device) Screenshot(localPath string) error {
|
||||||
|
tmpPath := fmt.Sprintf("/data/local/tmp/hypium_tmp_shot_%d.jpeg", time.Now().Unix())
|
||||||
|
_, err := d.RunShellCommand("snapshot_display", "-f", tmpPath)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("failed to take screencap \n%v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = d.PullFile(tmpPath, localPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, _ = d.RunShellCommand("rm", "-rf", "tmpPath")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Device) Install(localPath string) error {
|
||||||
|
res, err := d.ExecuteCommand(fmt.Sprintf("install -r %s", localPath))
|
||||||
|
if err != nil || !strings.Contains(res, "success") {
|
||||||
|
err = fmt.Errorf("failed to install %s %v, Msg: %s", localPath, err, res)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Device) Forward(remotePort int) (localPort int, err error) {
|
||||||
|
remote := fmt.Sprintf("tcp:%d", remotePort)
|
||||||
|
localPort, err = GetFreePort()
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("failed to get free port \n%v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
command := ""
|
||||||
|
local := fmt.Sprintf("tcp:%d", localPort)
|
||||||
|
|
||||||
|
command = fmt.Sprintf("fport %s %s", local, remote)
|
||||||
|
_, err = d.ExecuteCommand(command)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Device) ForwardKill(localPort int) (err error) {
|
||||||
|
local := fmt.Sprintf("tcp:%d", localPort)
|
||||||
|
_, err = d.hdClient.executeCommand(fmt.Sprintf("-t %s fport rm %s", d.serial, local))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Device) RunShellCommand(cmd string, args ...string) (string, error) {
|
||||||
|
if len(args) > 0 {
|
||||||
|
cmd = fmt.Sprintf("%s %s", cmd, strings.Join(args, " "))
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(cmd) == "" {
|
||||||
|
return "", errors.New("hd shell: command cannot be empty")
|
||||||
|
}
|
||||||
|
return d.ExecuteCommand(fmt.Sprintf("shell %s", cmd))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Device) createDeviceTransport() (tp transport, err error) {
|
||||||
|
return newTransport(fmt.Sprintf("%s:%d", d.hdClient.host, d.hdClient.port), false, d.serial)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Device) createDeviceAliveTransport() (tp transport, err error) {
|
||||||
|
return newTransport(fmt.Sprintf("%s:%d", d.hdClient.host, d.hdClient.port), true, d.serial)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Device) createUitestTransport() (uTp uitestTransport, err error) {
|
||||||
|
port, err := d.Forward(8012)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("failed to forward uitest port \n%v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return newUitestTransport(d.hdClient.host, fmt.Sprintf("%d", port))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Device) createUitestKitTransport() (uTp uitestKitTransport, err error) {
|
||||||
|
port, err := d.Forward(8012)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("failed to forward uitest port \n%v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return newUitestKitTransport(d.serial, d.hdClient.host, fmt.Sprintf("%d", port))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Device) ExecuteCommand(command string) (resp string, err error) {
|
||||||
|
var tp transport
|
||||||
|
if tp, err = d.createDeviceTransport(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer func() { _ = tp.Close() }()
|
||||||
|
time.Sleep(1 * time.Millisecond)
|
||||||
|
if err = tp.SendCommand(command); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
resp, err = tp.ReadStringAll()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if strings.Contains(resp, "[Fail]") {
|
||||||
|
return resp, fmt.Errorf("failed to execute command 「%s」 \nerror: %s", command, resp)
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Device) PushFile(localPath string, remotePath string) (err error) {
|
||||||
|
var tp transport
|
||||||
|
if tp, err = d.createDeviceTransport(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() { _ = tp.Close() }()
|
||||||
|
if err = tp.SendCommand(fmt.Sprintf("file send %s %s", localPath, remotePath)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = tp.ReadAll()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Device) PullFile(remotePath string, localPath string) (err error) {
|
||||||
|
var tp transport
|
||||||
|
if tp, err = d.createDeviceTransport(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() { _ = tp.Close() }()
|
||||||
|
|
||||||
|
if err = tp.SendCommand(fmt.Sprintf("file recv %s %s", remotePath, localPath)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
res, err := tp.ReadStringAll()
|
||||||
|
if err == nil {
|
||||||
|
if strings.Contains(res, "Fail") {
|
||||||
|
return fmt.Errorf("failed to pull: msg: %s", res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFreePort() (int, error) {
|
||||||
|
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("resolve tcp addr failed \n%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
l, err := net.ListenTCP("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("listen tcp addr failed \n%v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = l.Close()
|
||||||
|
}()
|
||||||
|
return l.Addr().(*net.TCPAddr).Port, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,253 @@
|
|||||||
|
package ghdc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDevice_Product(t *testing.T) {
|
||||||
|
hdClient, err := NewClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
devices, err := hdClient.DeviceList()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range devices {
|
||||||
|
dev := devices[i]
|
||||||
|
product, err := dev.Product()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(dev.Serial(), product)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_Model(t *testing.T) {
|
||||||
|
hdClient, err := NewClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
devices, err := hdClient.DeviceList()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range devices {
|
||||||
|
dev := devices[i]
|
||||||
|
model, err := dev.Model()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(dev.Serial(), model)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_Usb(t *testing.T) {
|
||||||
|
hdClient, err := NewClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
devices, err := hdClient.DeviceList()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range devices {
|
||||||
|
dev := devices[i]
|
||||||
|
usb, err := dev.Usb()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
isUsb, err := dev.IsUsb()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(dev.Serial(), usb, isUsb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_DeviceInfo(t *testing.T) {
|
||||||
|
hdClient, err := NewClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
devices, err := hdClient.DeviceList()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range devices {
|
||||||
|
dev := devices[i]
|
||||||
|
t.Log(dev.DeviceInfo())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_Forward(t *testing.T) {
|
||||||
|
hdClient, err := NewClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
devices, err := hdClient.DeviceList()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(devices) == 0 {
|
||||||
|
t.Fatal("not found available device")
|
||||||
|
}
|
||||||
|
SetDebug(true)
|
||||||
|
|
||||||
|
localPort := 61000
|
||||||
|
localPort, err = devices[0].Forward(6790)
|
||||||
|
t.Log(fmt.Sprintf("forward local port %d \n", localPort))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = devices[0].ForwardKill(localPort)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_ForwardKill(t *testing.T) {
|
||||||
|
hdClient, err := NewClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
devices, err := hdClient.DeviceList()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(devices) == 0 {
|
||||||
|
t.Fatal("not found available device")
|
||||||
|
}
|
||||||
|
SetDebug(true)
|
||||||
|
|
||||||
|
err = devices[0].ForwardKill(6790)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_RunShellCommand(t *testing.T) {
|
||||||
|
hdClient, err := NewClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
devices, err := hdClient.DeviceList()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(devices) == 0 {
|
||||||
|
t.Fatal("not found available device")
|
||||||
|
}
|
||||||
|
dev := devices[0]
|
||||||
|
|
||||||
|
cmdOutput, err := dev.RunShellCommand("pwd")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(dev.serial, err)
|
||||||
|
}
|
||||||
|
t.Log("\n⬇️"+dev.serial+"⬇️\n", cmdOutput)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_Push(t *testing.T) {
|
||||||
|
hdClient, err := NewClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
devices, err := hdClient.DeviceList()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(devices) == 0 {
|
||||||
|
t.Fatal("not found available device")
|
||||||
|
}
|
||||||
|
dev := devices[0]
|
||||||
|
|
||||||
|
SetDebug(true)
|
||||||
|
|
||||||
|
err = dev.PushFile("/tmp/test.txt", "/data/local/tmp/push.txt")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_Pull(t *testing.T) {
|
||||||
|
hdClient, err := NewClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
devices, err := hdClient.DeviceList()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(devices) == 0 {
|
||||||
|
t.Fatal("not found available device")
|
||||||
|
}
|
||||||
|
dev := devices[0]
|
||||||
|
|
||||||
|
SetDebug(true)
|
||||||
|
|
||||||
|
err = dev.PullFile("/data/local/tmp/push.txt", "/tmp/test2.txt")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_Screenshot(t *testing.T) {
|
||||||
|
hdClient, err := NewClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
devices, err := hdClient.DeviceList()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(devices) == 0 {
|
||||||
|
t.Fatal("not found available device")
|
||||||
|
}
|
||||||
|
dev := devices[0]
|
||||||
|
|
||||||
|
SetDebug(true)
|
||||||
|
|
||||||
|
err = dev.Screenshot("/tmp/test.jpeg")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_GetSoVersion(t *testing.T) {
|
||||||
|
hdClient, err := NewClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
devices, err := hdClient.DeviceList()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(devices) == 0 {
|
||||||
|
t.Fatal("not found available device")
|
||||||
|
}
|
||||||
|
dev := devices[0]
|
||||||
|
|
||||||
|
SetDebug(true)
|
||||||
|
res, err := dev.RunShellCommand("cat data/local/tmp/agent.so |grep -a UITEST_AGENT_LIBRARY")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(res)
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package ghdc
|
||||||
|
|
||||||
|
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] [ghd] " + msg)
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
4.1.4.0
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
package ghdc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrConnBroken = errors.New("socket connection broken")
|
||||||
|
|
||||||
|
var DefaultReadTimeout time.Duration = 60 * time.Second
|
||||||
|
|
||||||
|
var DATA_UNIT_LENGTH = 4
|
||||||
|
|
||||||
|
type transport struct {
|
||||||
|
sock net.Conn
|
||||||
|
readTimeout time.Duration
|
||||||
|
connectKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTransport(address string, alive bool, connectKey string, readTimeout ...time.Duration) (tp transport, err error) {
|
||||||
|
if len(readTimeout) == 0 {
|
||||||
|
readTimeout = []time.Duration{DefaultReadTimeout}
|
||||||
|
}
|
||||||
|
tp.readTimeout = readTimeout[0]
|
||||||
|
tp.connectKey = connectKey
|
||||||
|
if tp.sock, err = net.Dial("tcp", address); err != nil {
|
||||||
|
err = fmt.Errorf("ghdc transport: %w", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = _handleShake(tp.sock, connectKey); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if alive {
|
||||||
|
if err = tp.setAlive(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func _handleShake(sock net.Conn, connectKey string) (err error) {
|
||||||
|
data, err := _readN(sock, 48)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !(data[4] == 79 && data[5] == 72 && data[6] == 79 && data[7] == 83 && data[9] == 72 && data[10] == 68 && data[11] == 67) {
|
||||||
|
return fmt.Errorf("handle shake error")
|
||||||
|
}
|
||||||
|
bannerStr := []byte("OHOS HDC\x00\x00\x00\x00")
|
||||||
|
connectKeyBytes256 := [256]byte{}
|
||||||
|
copy(connectKeyBytes256[:], connectKey)
|
||||||
|
size := 12 + 256
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
if err = binary.Write(buffer, binary.BigEndian, uint32(size)); err != nil {
|
||||||
|
return fmt.Errorf("transport write: %w", err)
|
||||||
|
}
|
||||||
|
if err = binary.Write(buffer, binary.BigEndian, bannerStr); err != nil {
|
||||||
|
return fmt.Errorf("transport write: %w", err)
|
||||||
|
}
|
||||||
|
if err = binary.Write(buffer, binary.BigEndian, connectKeyBytes256); err != nil {
|
||||||
|
return fmt.Errorf("transport write: %w", err)
|
||||||
|
}
|
||||||
|
return _send(sock, buffer.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t transport) setAlive() (err error) {
|
||||||
|
return _sendCommand(t.sock, "alive")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t transport) SendCommand(command string) (err error) {
|
||||||
|
return _sendCommand(t.sock, command)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _sendCommand(writer io.Writer, command string) (err error) {
|
||||||
|
command += "\x00"
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
if err = binary.Write(buf, binary.BigEndian, uint32(len(command))); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = binary.Write(buf, binary.BigEndian, []byte(command)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
debugLog(fmt.Sprintf("--> %s", command))
|
||||||
|
return _send(writer, buf.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t transport) SendBytes(data []byte) (err error) {
|
||||||
|
length := uint32(len(data))
|
||||||
|
|
||||||
|
newData := make([]byte, 4+len(data))
|
||||||
|
|
||||||
|
binary.BigEndian.PutUint32(newData[:4], length)
|
||||||
|
|
||||||
|
copy(newData[4:], data)
|
||||||
|
return _send(t.sock, newData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t transport) ReadStringAll() (s string, err error) {
|
||||||
|
var raw []byte
|
||||||
|
raw, err = _readAll(t.sock)
|
||||||
|
return string(raw), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t transport) ReadAll() (raw []byte, err error) {
|
||||||
|
return _readAll(t.sock)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _readAll(reader io.Reader) (raw []byte, err error) {
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
for true {
|
||||||
|
lengthBuf := make([]byte, 4)
|
||||||
|
_, err := io.ReadFull(reader, lengthBuf)
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
return buffer.Bytes(), nil
|
||||||
|
} else if errors.Is(err, io.ErrUnexpectedEOF) {
|
||||||
|
err = fmt.Errorf("reached unexpected EOF, read partial data: %s %v", string(buffer.Bytes()), err)
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
length := binary.BigEndian.Uint32(lengthBuf)
|
||||||
|
|
||||||
|
data, err := _readN(reader, int(length))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buffer.Write(data)
|
||||||
|
|
||||||
|
}
|
||||||
|
return buffer.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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.RehdytesN(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.RehdytesN(size); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(raw), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t transport) RehdytesN(size int) (raw []byte, err error) {
|
||||||
|
_ = t.sock.SetReadDeadline(time.Now().Add(t.readTimeout))
|
||||||
|
return _readN(t.sock, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _readResponse(reader io.Reader) error {
|
||||||
|
raw, err := _readN(reader, DATA_UNIT_LENGTH)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read response length: %w", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(raw[0:4], []byte("OKAY")) {
|
||||||
|
fmt.Printf("failed to push file: %s\n", string(raw))
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
raw, err = _readAll(reader)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read response data: %w", err)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("read response error %s", string(raw))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t transport) Close() (err error) {
|
||||||
|
if t.sock == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return t.sock.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
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 _read(reader io.Reader) (data []byte, err error) {
|
||||||
|
buf := make([]byte, 4*1024*1024)
|
||||||
|
n, err := reader.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return buf[:n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
@@ -0,0 +1,927 @@
|
|||||||
|
package ghdc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"hash/fnv"
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed agent.so
|
||||||
|
var agentSO embed.FS
|
||||||
|
|
||||||
|
type UitestRequest struct {
|
||||||
|
Module string `json:"module,omitempty"`
|
||||||
|
Method string `json:"method,omitempty"`
|
||||||
|
Params interface{} `json:"params,omitempty"`
|
||||||
|
RequestId string `json:"request_id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UitestResponse struct {
|
||||||
|
Result interface{} `json:"result,omitempty"`
|
||||||
|
Exception *UitestException `json:"exception,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UitestKitResponse struct {
|
||||||
|
Result interface{} `json:"result,omitempty"`
|
||||||
|
Exception string `json:"exception,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UitestException struct {
|
||||||
|
Message string `json:"message,omitempty"`
|
||||||
|
Code int `json:"code,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UIDriver struct {
|
||||||
|
Device
|
||||||
|
uTp *uitestTransport
|
||||||
|
uKTp *uitestKitTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
type Dimension struct {
|
||||||
|
Width int `json:"width,omitempty"`
|
||||||
|
Height int `json:"height,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUIDriver(device Device) (d *UIDriver, err error) {
|
||||||
|
d = new(UIDriver)
|
||||||
|
d.Device = device
|
||||||
|
err = d.prepareDevice()
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("[uitest] failed to prepare device \n%v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uTp, err := device.createUitestTransport()
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("[uitest] failed to create uitest transport \n%v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uKTp, err := device.createUitestKitTransport()
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("[uitest] failed to create uitest kit transport \n%v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.uTp = &uTp
|
||||||
|
d.uKTp = &uKTp
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) Close() {
|
||||||
|
if d.uTp != nil {
|
||||||
|
d.uTp.Close()
|
||||||
|
}
|
||||||
|
if d.uKTp != nil {
|
||||||
|
d.uKTp.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) Reconnect() error {
|
||||||
|
d.Close()
|
||||||
|
uTp, err := d.createUitestTransport()
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("[uitest] failed to create uitest transport \n%v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
uKTp, err := d.createUitestKitTransport()
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("[uitest] failed to create uitest kit transport \n%v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.uTp = &uTp
|
||||||
|
d.uKTp = &uKTp
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) prepareDevice() error {
|
||||||
|
uitestPid, err := d.Device.RunShellCommand("pidof uitest")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
uitestPid = strings.TrimSpace(uitestPid)
|
||||||
|
|
||||||
|
isLowerVersion, err := d.needUpdateLib()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if uitestPid != "" && !isLowerVersion {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = d.Device.RunShellCommand("param set persist.ace.testmode.enabled 1")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if isLowerVersion {
|
||||||
|
if uitestPid != "" {
|
||||||
|
_, err = d.Device.RunShellCommand("kill -9 " + uitestPid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
uitestPid = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
err = d.updateLib()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if uitestPid == "" {
|
||||||
|
_, err = d.Device.RunShellCommand("uitest start-daemon singleness")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) isServerRunning() bool {
|
||||||
|
res, err := d.Device.RunShellCommand("top -H -n 1 -p $(pidof uitest)")
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if strings.Contains(res, "rpc-") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) updateLib() error {
|
||||||
|
tmpDir := os.TempDir()
|
||||||
|
soFileName := filepath.Join(tmpDir, "agent.so")
|
||||||
|
soRaw, err := agentSO.ReadFile("agent.so")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = os.WriteFile(soFileName, soRaw, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error writing file:", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = d.Device.RunShellCommand("rm /data/tmp/local/agent.so")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = d.Device.PushFile(soFileName, "/data/local/tmp/agent.so")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) needUpdateLib() (res bool, err error) {
|
||||||
|
deviceVersionStr, err := d.Device.RunShellCommand("cat data/local/tmp/agent.so |grep -a UITEST_AGENT_LIBRARY")
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
soRaw, err := agentSO.ReadFile("agent.so")
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
// 定义要搜索的字符串
|
||||||
|
searchString := "UITEST_AGENT_LIBRARY"
|
||||||
|
|
||||||
|
// 将二进制内容转换为字符串
|
||||||
|
content := string(soRaw)
|
||||||
|
|
||||||
|
// 按行分割内容
|
||||||
|
lines := strings.Split(content, "\n")
|
||||||
|
|
||||||
|
// 搜索包含特定字符串的行
|
||||||
|
for _, line := range lines {
|
||||||
|
if strings.Contains(line, searchString) {
|
||||||
|
update := false
|
||||||
|
deviceVersion, err := getVersion(deviceVersionStr)
|
||||||
|
if err != nil {
|
||||||
|
update = true
|
||||||
|
}
|
||||||
|
lowestVersion, err := getVersion(line)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if update || lowestVersion[0] > deviceVersion[0] || lowestVersion[1] > deviceVersion[1] || lowestVersion[2] > deviceVersion[2] {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) supportDevice() error {
|
||||||
|
rootDir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
raw, err := os.ReadFile(filepath.Join(rootDir, "minUiTestVersion.txt"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
lowestVersion := string(raw)
|
||||||
|
uitestVersion, err := d.Device.RunShellCommand("uitest --version")
|
||||||
|
if lowestVersion > uitestVersion {
|
||||||
|
return fmt.Errorf("not supprt uitest lowest version %s, device version %s", lowestVersion, uitestVersion)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) createDriver() (driver string, err error) {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "Driver.create",
|
||||||
|
"this": nil,
|
||||||
|
"args": []string{},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
res, err := d.sendUitestReq(newHypiumRequest(params, "callHypiumApi"))
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("[uitest] failed to create driver")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if res.Exception != nil {
|
||||||
|
err = fmt.Errorf("[uitest] failed to create driver msg: %s", res.Exception.Message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
driver = res.Result.(string)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) createFocused() (onName string, err error) {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "On.focused",
|
||||||
|
"this": "On#seed",
|
||||||
|
"args": []bool{true},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
res, err := d.sendUitestReq(newHypiumRequest(params, "callHypiumApi"))
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("[uitest] failed to create focused")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if res.Exception != nil {
|
||||||
|
err = fmt.Errorf("[uitest] failed to create focused msg: %s", res.Exception.Message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
onName = res.Result.(string)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) findComponent(driverName, onName string) (componentName string, err error) {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "Driver.waitForComponent",
|
||||||
|
"this": driverName,
|
||||||
|
"args": []any{onName, 5000},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
res, err := d.sendUitestReq(newHypiumRequest(params, "callHypiumApi"))
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("[uitest] failed to create focused")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if res.Exception != nil {
|
||||||
|
err = fmt.Errorf("[uitest] failed to create focused msg: %s", res.Exception.Message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
componentName = res.Result.(string)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) createPointMatrix(pointerMatrix *PointerMatrix) (pointMatrixName string, err error) {
|
||||||
|
fingers, steps := pointerMatrix.fingerIndexStats()
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "PointerMatrix.create",
|
||||||
|
"this": nil,
|
||||||
|
"args": []int{fingers, steps},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
res, err := d.sendUitestReq(newHypiumRequest(params, "callHypiumApi"))
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("[uitest] failed to create PointerMatrix")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if res.Exception != nil {
|
||||||
|
err = fmt.Errorf("[uitest] failed to create PointerMatrix msg: %s", res.Exception.Message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pointMatrixName = res.Result.(string)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) releaseObj(obj []string) error {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "BackendObjectsCleaner",
|
||||||
|
"this": nil,
|
||||||
|
"args": obj,
|
||||||
|
}
|
||||||
|
res, err := d.sendUitestReq(newHypiumRequest(params, "callHypiumApi"))
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("[uitest] failed to release driver")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if res.Exception != nil {
|
||||||
|
err = fmt.Errorf("[uitest] failed to release driver msg: %s", res.Exception.Message)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) Touch(x, y int) error {
|
||||||
|
driverName, err := d.createDriver()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = d.releaseObj([]string{driverName})
|
||||||
|
}()
|
||||||
|
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "Driver.click",
|
||||||
|
"this": driverName,
|
||||||
|
"args": []int{x, y},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
res, err := d.sendUitestReq(newHypiumRequest(params, "callHypiumApi"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if res.Exception != nil {
|
||||||
|
return fmt.Errorf("[uitest] failed to touch (%d, %d): %s", x, y, res.Exception.Message)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) Drag(fromX, fromY, toX, toY int, duration float64) error {
|
||||||
|
driverName, err := d.createDriver()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = d.releaseObj([]string{driverName})
|
||||||
|
}()
|
||||||
|
|
||||||
|
distance := math.Sqrt(math.Pow(float64(fromX-toX), 2) + math.Pow(float64(fromX-toX), 2))
|
||||||
|
speed := int(distance / duration)
|
||||||
|
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "Driver.drag",
|
||||||
|
"this": driverName,
|
||||||
|
"args": []int{fromX, fromY, toX, toY, speed},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
res, err := d.sendUitestReq(newHypiumRequest(params, "callHypiumApi"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if res.Exception != nil {
|
||||||
|
return fmt.Errorf("[uitest] failed to Drag from (%d, %d) to (%d, %d): %s", fromX, fromY, toX, toY, res.Exception.Message)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) PressKey(key KeyCode) error {
|
||||||
|
driverName, err := d.createDriver()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = d.releaseObj([]string{driverName})
|
||||||
|
}()
|
||||||
|
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "Driver.triggerKey",
|
||||||
|
"this": driverName,
|
||||||
|
"args": []KeyCode{key},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
res, err := d.sendUitestReq(newHypiumRequest(params, "callHypiumApi"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if res.Exception != nil {
|
||||||
|
return fmt.Errorf("[uitest] failed to Press Key code:%d: %s", key, res.Exception.Message)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) PressKeys(keys []KeyCode) error {
|
||||||
|
driverName, err := d.createDriver()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = d.releaseObj([]string{driverName})
|
||||||
|
}()
|
||||||
|
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "Driver.triggerCombineKeys",
|
||||||
|
"this": driverName,
|
||||||
|
"args": keys,
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
res, err := d.sendUitestReq(newHypiumRequest(params, "callHypiumApi"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if res.Exception != nil {
|
||||||
|
return fmt.Errorf("[uitest] failed to Press Key code:%v: %s", keys, res.Exception.Message)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) InjectGesture(gesture *Gesture, speedArg ...int) error {
|
||||||
|
return d.InjectMultiGesture([]*Gesture{gesture}, speedArg...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) InjectMultiGesture(gestures []*Gesture, speedArg ...int) error {
|
||||||
|
speed := 2000
|
||||||
|
var releaseObj []string
|
||||||
|
defer func() {
|
||||||
|
if len(releaseObj) > 0 {
|
||||||
|
_ = d.releaseObj(releaseObj)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if len(speedArg) > 0 && speedArg[0] > 0 {
|
||||||
|
speed = speedArg[0]
|
||||||
|
}
|
||||||
|
driverName, err := d.createDriver()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
releaseObj = append(releaseObj, driverName)
|
||||||
|
|
||||||
|
pointerMatrix := d.gestureToPointMatrix(gestures)
|
||||||
|
|
||||||
|
pointerMatrixName, err := d.createPointMatrix(pointerMatrix)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
releaseObj = append(releaseObj, pointerMatrixName)
|
||||||
|
|
||||||
|
for step, point := range pointerMatrix.points {
|
||||||
|
err = d.setPoint(pointerMatrixName, point.index, step, point.point)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = d.injectMultiPointerAction(driverName, pointerMatrixName, speed)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) InputText(text string) error {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "Driver.inputText",
|
||||||
|
"this": nil,
|
||||||
|
"args": []any{map[string]interface{}{
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
}, text},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := d.sendUitestReq(newHypiumRequest(params, "callHypiumApi"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if res.Exception != nil {
|
||||||
|
return fmt.Errorf("[uitest] failed to input text %s: %s", text, res.Exception.Message)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) InputTextOnFocused(text string) error {
|
||||||
|
driverName, err := d.createDriver()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = d.releaseObj([]string{driverName})
|
||||||
|
}()
|
||||||
|
|
||||||
|
onName, err := d.createFocused()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = d.releaseObj([]string{onName})
|
||||||
|
}()
|
||||||
|
componentName, err := d.findComponent(driverName, onName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = d.releaseObj([]string{componentName})
|
||||||
|
}()
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "Component.inputText",
|
||||||
|
"this": componentName,
|
||||||
|
"args": []string{text},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := d.sendUitestReq(newHypiumRequest(params, "callHypiumApi"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if res.Exception != nil {
|
||||||
|
return fmt.Errorf("[uitest] failed to input text %s: %s", text, res.Exception.Message)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) TouchDown(x, y int) error {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "touchDown",
|
||||||
|
"this": nil,
|
||||||
|
"args": map[string]interface{}{
|
||||||
|
"x": x,
|
||||||
|
"y": y,
|
||||||
|
},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
return d.sendUitestKitNoResult(params, "Gestures", DEFAULT, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) TouchDownAsync(x, y int) {
|
||||||
|
go func() {
|
||||||
|
err := d.TouchDown(x, y)
|
||||||
|
if err != nil {
|
||||||
|
debugLog(fmt.Sprintf("%v", err))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) TouchMove(x, y int) error {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "touchMove",
|
||||||
|
"this": nil,
|
||||||
|
"args": map[string]interface{}{
|
||||||
|
"x": x,
|
||||||
|
"y": y,
|
||||||
|
},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
return d.sendUitestKitNoResult(params, "Gestures", DEFAULT, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) TouchMoveAsync(x, y int) {
|
||||||
|
go func() {
|
||||||
|
err := d.TouchMove(x, y)
|
||||||
|
if err != nil {
|
||||||
|
debugLog(fmt.Sprintf("%v", err))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) TouchUp(x, y int) error {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "touchUp",
|
||||||
|
"this": nil,
|
||||||
|
"args": map[string]interface{}{
|
||||||
|
"x": x,
|
||||||
|
"y": y,
|
||||||
|
},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
return d.sendUitestKitNoResult(params, "Gestures", DEFAULT, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) TouchUpAsync(x, y int) {
|
||||||
|
go func() {
|
||||||
|
err := d.TouchUp(x, y)
|
||||||
|
if err != nil {
|
||||||
|
debugLog(fmt.Sprintf("%v", err))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) PressRecentApp() error {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "pressRecentApp",
|
||||||
|
"this": nil,
|
||||||
|
"args": map[string]interface{}{},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
return d.sendUitestKitNoResult(params, "Gestures", DEFAULT, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) PressBack() error {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "pressBack",
|
||||||
|
"this": nil,
|
||||||
|
"args": map[string]interface{}{},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
return d.sendUitestKitNoResult(params, "Gestures", DEFAULT, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) PressPowerKey() error {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "pressPowerKey",
|
||||||
|
"this": nil,
|
||||||
|
"args": map[string]interface{}{},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
return d.sendUitestKitNoResult(params, "CtrlCmd", DEFAULT, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) GetDisplayRotation() (direction int, err error) {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "getDisplayRotation",
|
||||||
|
"this": nil,
|
||||||
|
"args": map[string]interface{}{},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
res, err := d.sendUitestKit(params, "CtrlCmd", DEFAULT, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if res.Result == false {
|
||||||
|
err = fmt.Errorf("[uitest] failed to exec method getDisplayRotation msg: %s", res.Exception)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
direction = (int)(res.Result.(float64))
|
||||||
|
return direction, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) UpVolume() error {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "upVolume",
|
||||||
|
"this": nil,
|
||||||
|
"args": map[string]interface{}{},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
return d.sendUitestKitNoResult(params, "CtrlCmd", DEFAULT, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) DownVolume() error {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "downVolume",
|
||||||
|
"this": nil,
|
||||||
|
"args": map[string]interface{}{},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
return d.sendUitestKitNoResult(params, "CtrlCmd", DEFAULT, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) RotationDisplay(direction int) error {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "rotationDisplay",
|
||||||
|
"this": nil,
|
||||||
|
"args": map[string]interface{}{
|
||||||
|
"direction": direction,
|
||||||
|
},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
return d.sendUitestKitNoResult(params, "CtrlCmd", DEFAULT, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) GetDisplaySize() (display Dimension, err error) {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "getDisplaySize",
|
||||||
|
"this": nil,
|
||||||
|
"args": map[string]interface{}{},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
res, err := d.sendUitestKit(params, "CtrlCmd", DEFAULT, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if res.Result == false {
|
||||||
|
err = fmt.Errorf("[uitest] failed to exec method getDisplaySize msg: %s", res.Exception)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
raw, err := json.Marshal(res.Result)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(raw, &display)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) StartCaptureScreen(callback UitestKitCallback) error {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "startCaptureScreen",
|
||||||
|
"this": nil,
|
||||||
|
"args": map[string]interface{}{},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
return d.sendUitestKitNoResult(params, "Captures", SCREEN_CAPTURE, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) StopCaptureScreen() error {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "stopCaptureScreen",
|
||||||
|
"this": nil,
|
||||||
|
"args": map[string]interface{}{},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
return d.sendUitestKitNoResult(params, "Captures", DEFAULT, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) StartCaptureUiAction(callback UitestKitCallback) error {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "startCaptureUiAction",
|
||||||
|
"this": nil,
|
||||||
|
"args": map[string]interface{}{},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
return d.sendUitestKitNoResult(params, "Captures", UI_ACTION_CAPTURE, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) StopCaptureUiAction() error {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "stopCaptureUiAction",
|
||||||
|
"this": nil,
|
||||||
|
"args": map[string]interface{}{},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
return d.sendUitestKitNoResult(params, "Captures", DEFAULT, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) CaptureLayout() (layout interface{}, err error) {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "captureLayout",
|
||||||
|
"this": nil,
|
||||||
|
"args": map[string]interface{}{},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
res, err := d.sendUitestKit(params, "Captures", DEFAULT, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if res.Result == false {
|
||||||
|
err = fmt.Errorf("[uitest] failed to exec method captureLayout msg: %s", res.Exception)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.Result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) sendUitestKitNoResult(params map[string]interface{}, method string, reqType ReqTypeEnum, callback UitestKitCallback) error {
|
||||||
|
res, err := d.sendUitestKit(params, method, reqType, callback)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if res.Result == false {
|
||||||
|
err = fmt.Errorf("[uitest] failed to exec method %s params %v msg: %s", method, params, res.Exception)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) sendUitestReq(req UitestRequest) (res UitestResponse, err error) {
|
||||||
|
res, err = d.uTp.SendReq(req)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("[uitest] failed to send req first. try reconnect \n%v \n", err)
|
||||||
|
if err = d.Reconnect(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err = d.uTp.SendReq(req)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) sendUitestKit(params map[string]interface{}, method string, reqType ReqTypeEnum, callback UitestKitCallback) (response UitestKitResponse, err error) {
|
||||||
|
request := newHypiumRequest(params, method)
|
||||||
|
requestStr, err := request.ToString()
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("[uitest] failed to create req while exec method %s %v", method, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sessionId := hashCode(fmt.Sprintf("%s%d", requestStr, time.Now().Unix()))
|
||||||
|
if sessionId <= (1 << 24) {
|
||||||
|
sessionId += 1 << 24
|
||||||
|
}
|
||||||
|
err = d.uKTp.registerCallback(reqType, sessionId, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("[uitest] failed to register callback try reconnect %s %v", method, err)
|
||||||
|
if err = d.Reconnect(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = d.uKTp.registerCallback(reqType, sessionId, nil); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res, err := d.uKTp.sendMessage(reqType, sessionId, requestStr)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("[uitest] failed to send message while exec method %s sessionId: %d %v", method, sessionId, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = d.uKTp.registerCallback(reqType, sessionId, callback)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("[uitest] failed to register callback while exec method %s %v", method, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) gestureToPointMatrix(gestures []*Gesture) *PointerMatrix {
|
||||||
|
pointerMatrix := &PointerMatrix{}
|
||||||
|
for fingerIndex, gestures := range gestures {
|
||||||
|
var curPoint Point
|
||||||
|
for _, gestureStep := range gestures.steps {
|
||||||
|
if gestureStep.GestureType == "start" {
|
||||||
|
pointerMatrix.setPoint(gestureStep.Point, fingerIndex, gestureStep.Duration)
|
||||||
|
curPoint = gestureStep.Point
|
||||||
|
}
|
||||||
|
if gestureStep.GestureType == "move" {
|
||||||
|
toPoint := gestureStep.Point
|
||||||
|
offsetX := toPoint.X - curPoint.X
|
||||||
|
offsetY := toPoint.Y - curPoint.Y
|
||||||
|
steps := gestureStep.calculateSteps()
|
||||||
|
for i := 0; i < steps-1; i++ {
|
||||||
|
curPoint = Point{X: curPoint.X + (offsetX / steps), Y: curPoint.Y + (offsetY / steps)}
|
||||||
|
pointerMatrix.setPoint(curPoint, fingerIndex, EVENT_INJECTION_DELAY_MS)
|
||||||
|
}
|
||||||
|
curPoint = toPoint
|
||||||
|
if steps == 1 {
|
||||||
|
pointerMatrix.setPoint(curPoint, fingerIndex, gestureStep.Duration%EVENT_INJECTION_DELAY_MS)
|
||||||
|
} else {
|
||||||
|
pointerMatrix.setPoint(curPoint, fingerIndex, EVENT_INJECTION_DELAY_MS+(gestureStep.Duration%EVENT_INJECTION_DELAY_MS))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if gestureStep.GestureType == "pause" {
|
||||||
|
steps := gestureStep.calculateSteps()
|
||||||
|
for i := 0; i < steps-1; i++ {
|
||||||
|
pointerMatrix.setPoint(curPoint, fingerIndex, EVENT_INJECTION_DELAY_MS)
|
||||||
|
}
|
||||||
|
pointerMatrix.setPoint(curPoint, fingerIndex, EVENT_INJECTION_DELAY_MS+(gestureStep.Duration%EVENT_INJECTION_DELAY_MS))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pointerMatrix
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) setPoint(pointerMatrixName string, fingerIndex int, step int, point Point) error {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "PointerMatrix.setPoint",
|
||||||
|
"this": pointerMatrixName,
|
||||||
|
"args": []any{fingerIndex, step, point},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
res, err := d.sendUitestReq(newHypiumRequest(params, "callHypiumApi"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if res.Exception != nil {
|
||||||
|
return fmt.Errorf("[uitest] failed to setPoint from: %s", res.Exception.Message)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *UIDriver) injectMultiPointerAction(driverName, pointerMatrixName string, speed int) error {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"api": "Driver.injectMultiPointerAction",
|
||||||
|
"this": driverName,
|
||||||
|
"args": []any{pointerMatrixName, speed},
|
||||||
|
"message_type": "hypium",
|
||||||
|
}
|
||||||
|
res, err := d.sendUitestReq(newHypiumRequest(params, "callHypiumApi"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if res.Exception != nil {
|
||||||
|
return fmt.Errorf("[uitest] failed to injectMultiPointerAction from: %s", res.Exception.Message)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func hashCode(s string) uint32 {
|
||||||
|
h := fnv.New32a()
|
||||||
|
_, _ = h.Write([]byte(s))
|
||||||
|
return h.Sum32()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r UitestRequest) ToString() (result string, err error) {
|
||||||
|
data, err := json.Marshal(r)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error: \n%v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return string(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getVersion(str string) (version []string, err error) {
|
||||||
|
index := strings.Index(str, "@")
|
||||||
|
if index == -1 {
|
||||||
|
err = fmt.Errorf("invalid version str")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
version = strings.Split(str[index+1:], ".")
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,274 @@
|
|||||||
|
package ghdc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
client Client
|
||||||
|
device Device
|
||||||
|
driver *UIDriver
|
||||||
|
)
|
||||||
|
|
||||||
|
func setUp(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
client, err = NewClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
devices, err := client.DeviceList()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(devices) == 0 {
|
||||||
|
t.Fatal("not found available device")
|
||||||
|
}
|
||||||
|
device = devices[0]
|
||||||
|
driver, err = NewUIDriver(device)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_Touch(t *testing.T) {
|
||||||
|
setUp(t)
|
||||||
|
err := driver.Touch(1038, 798)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_Drag(t *testing.T) {
|
||||||
|
setUp(t)
|
||||||
|
err := driver.Drag(800, 1000, 200, 1000, 0.2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type CaptureScreenCallback struct {
|
||||||
|
count int
|
||||||
|
mux sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnData handles the data received
|
||||||
|
func (cb *CaptureScreenCallback) OnData(data []byte) {
|
||||||
|
cb.mux.Lock()
|
||||||
|
cb.count++
|
||||||
|
cb.mux.Unlock()
|
||||||
|
fmt.Printf("Data received: %s\n", string(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
// onError handles the error received
|
||||||
|
func (cb *CaptureScreenCallback) OnError(err error) {
|
||||||
|
fmt.Printf("Error received: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cb *CaptureScreenCallback) startCounter() {
|
||||||
|
ticker := time.NewTicker(1 * time.Second)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for range ticker.C {
|
||||||
|
cb.mux.Lock()
|
||||||
|
fmt.Printf("Screen Data received count in the last second: %d\n", cb.count)
|
||||||
|
cb.count = 0
|
||||||
|
cb.mux.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_StartCaptureScreen(t *testing.T) {
|
||||||
|
setUp(t)
|
||||||
|
err := driver.StopCaptureScreen()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
callback := &CaptureScreenCallback{}
|
||||||
|
err = driver.StartCaptureScreen(callback)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
callback.startCounter()
|
||||||
|
time.Sleep(1 * time.Minute)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_StartCaptureUIAction(t *testing.T) {
|
||||||
|
setUp(t)
|
||||||
|
err := driver.StopCaptureUiAction()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
callback := &CaptureScreenCallback{}
|
||||||
|
err = driver.StartCaptureUiAction(callback)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Minute)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_TouchDownMoveUp(t *testing.T) {
|
||||||
|
setUp(t)
|
||||||
|
|
||||||
|
for i := 0; i < 10000; i++ {
|
||||||
|
// time.Sleep(1 * time.Second)
|
||||||
|
debugLog(fmt.Sprintf("running... time %d", i))
|
||||||
|
err := driver.TouchDown(225, 1700)
|
||||||
|
if err != nil {
|
||||||
|
debugLog(err.Error())
|
||||||
|
// t.Fatal(err)
|
||||||
|
}
|
||||||
|
time.Sleep(20 * time.Millisecond)
|
||||||
|
err = driver.TouchMove(325, 1700)
|
||||||
|
if err != nil {
|
||||||
|
debugLog(err.Error())
|
||||||
|
// t.Fatal(err)
|
||||||
|
}
|
||||||
|
time.Sleep(20 * time.Millisecond)
|
||||||
|
err = driver.TouchMove(425, 1700)
|
||||||
|
if err != nil {
|
||||||
|
debugLog(err.Error())
|
||||||
|
// t.Fatal(err)
|
||||||
|
}
|
||||||
|
time.Sleep(20 * time.Millisecond)
|
||||||
|
err = driver.TouchMove(525, 1700)
|
||||||
|
if err != nil {
|
||||||
|
debugLog(err.Error())
|
||||||
|
// t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = driver.TouchUp(625, 1700)
|
||||||
|
if err != nil {
|
||||||
|
debugLog(err.Error())
|
||||||
|
// t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_TouchDownUp(t *testing.T) {
|
||||||
|
setUp(t)
|
||||||
|
err := driver.TouchDown(200, 2000)
|
||||||
|
if err != nil {
|
||||||
|
debugLog(err.Error())
|
||||||
|
}
|
||||||
|
err = driver.TouchUp(200, 2000)
|
||||||
|
if err != nil {
|
||||||
|
debugLog(err.Error())
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_TouchDownMoveUpAsync(t *testing.T) {
|
||||||
|
setUp(t)
|
||||||
|
driver.TouchDown(225, 1700)
|
||||||
|
time.Sleep(60 * time.Millisecond)
|
||||||
|
driver.TouchMoveAsync(325, 1700)
|
||||||
|
time.Sleep(60 * time.Millisecond)
|
||||||
|
driver.TouchMoveAsync(425, 1700)
|
||||||
|
time.Sleep(60 * time.Millisecond)
|
||||||
|
driver.TouchMoveAsync(525, 1700)
|
||||||
|
time.Sleep(60 * time.Millisecond)
|
||||||
|
driver.TouchUpAsync(625, 1700)
|
||||||
|
time.Sleep(4 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_GetDisplay(t *testing.T) {
|
||||||
|
setUp(t)
|
||||||
|
display, err := driver.GetDisplaySize()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(display)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_GetRotation(t *testing.T) {
|
||||||
|
setUp(t)
|
||||||
|
rotation, err := driver.GetDisplayRotation()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(rotation)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_PressRecentApp(t *testing.T) {
|
||||||
|
setUp(t)
|
||||||
|
err := driver.PressRecentApp()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_PressBack(t *testing.T) {
|
||||||
|
setUp(t)
|
||||||
|
err := driver.PressBack()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_PressPowerKey(t *testing.T) {
|
||||||
|
setUp(t)
|
||||||
|
err := driver.PressPowerKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_UpVolume(t *testing.T) {
|
||||||
|
setUp(t)
|
||||||
|
err := driver.UpVolume()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_DownVolume(t *testing.T) {
|
||||||
|
setUp(t)
|
||||||
|
err := driver.DownVolume()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_CaptureLayout(t *testing.T) {
|
||||||
|
setUp(t)
|
||||||
|
layout, err := driver.CaptureLayout()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(layout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_InputText(t *testing.T) {
|
||||||
|
setUp(t)
|
||||||
|
err := driver.InputText("abcdef")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_InjectPoint(t *testing.T) {
|
||||||
|
setUp(t)
|
||||||
|
err := driver.InjectGesture(NewGesture().Start(Point{800, 2000}).MoveTo(Point{200, 2000}, 2000))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_PressKey(t *testing.T) {
|
||||||
|
setUp(t)
|
||||||
|
err := driver.PressKey(KEYCODE_NUM_1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevice_PressKeys(t *testing.T) {
|
||||||
|
setUp(t)
|
||||||
|
err := driver.PressKeys([]KeyCode{KEYCODE_SHIFT_LEFT, KEYCODE_NUM_1})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package ghdc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
var EVENT_INJECTION_DELAY_MS = 50
|
||||||
|
|
||||||
|
type Point struct {
|
||||||
|
X int `json:"x,omitempty"`
|
||||||
|
Y int `json:"y,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Point) distance(p0 Point) float64 {
|
||||||
|
return math.Sqrt(math.Pow(float64(p.X-p0.X), 2) + math.Pow(float64(p.Y-p0.Y), 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
type GestureStep struct {
|
||||||
|
Point
|
||||||
|
GestureType string
|
||||||
|
Duration int
|
||||||
|
}
|
||||||
|
|
||||||
|
type EmptyGesture struct{}
|
||||||
|
|
||||||
|
type Gesture struct {
|
||||||
|
steps []*GestureStep
|
||||||
|
}
|
||||||
|
|
||||||
|
type PointerMatrix struct {
|
||||||
|
points []*FingerPoint
|
||||||
|
}
|
||||||
|
|
||||||
|
type FingerPoint struct {
|
||||||
|
index int
|
||||||
|
point Point
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGesture() *EmptyGesture {
|
||||||
|
return new(EmptyGesture)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *EmptyGesture) Start(point Point) *Gesture {
|
||||||
|
gesture := &Gesture{}
|
||||||
|
gesture.steps = append(gesture.steps, &GestureStep{Point: point, GestureType: "start"})
|
||||||
|
return gesture
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Gesture) MoveTo(point Point, duration int) *Gesture {
|
||||||
|
g.steps = append(g.steps, &GestureStep{Point: point, GestureType: "move", Duration: duration})
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Gesture) Pause(duration int) *Gesture {
|
||||||
|
g.steps = append(g.steps, &GestureStep{GestureType: "pause", Duration: duration})
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gs *GestureStep) calculateSteps() int {
|
||||||
|
return (gs.Duration / EVENT_INJECTION_DELAY_MS) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *PointerMatrix) setPoint(point Point, fingerIndex, duration int) {
|
||||||
|
point.X += 65536 * duration
|
||||||
|
pm.points = append(pm.points, &FingerPoint{index: fingerIndex, point: point})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *PointerMatrix) fingerIndexStats() (fingers int, maxSteps int) {
|
||||||
|
indexMap := make(map[int]int)
|
||||||
|
|
||||||
|
for _, fp := range pm.points {
|
||||||
|
indexMap[fp.index]++
|
||||||
|
}
|
||||||
|
|
||||||
|
fingers = len(indexMap)
|
||||||
|
for _, count := range indexMap {
|
||||||
|
if count > maxSteps {
|
||||||
|
maxSteps = count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fingers, maxSteps
|
||||||
|
}
|
||||||
@@ -0,0 +1,345 @@
|
|||||||
|
package ghdc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"container/list"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type uitestKitTransport struct {
|
||||||
|
connectionPool *ConnectionPool
|
||||||
|
socketMap map[string]*SocketContext
|
||||||
|
serial string
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
type SocketContext struct {
|
||||||
|
conn net.Conn
|
||||||
|
socketId string
|
||||||
|
writeLock sync.Mutex
|
||||||
|
|
||||||
|
callbackMap map[string]UitestKitCallback
|
||||||
|
queue *responseList
|
||||||
|
}
|
||||||
|
|
||||||
|
type response struct {
|
||||||
|
sessionId uint32
|
||||||
|
payload []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type responseList struct {
|
||||||
|
list *list.List
|
||||||
|
lock sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *responseList) Clear() {
|
||||||
|
r.lock.Lock()
|
||||||
|
defer r.lock.Unlock()
|
||||||
|
r.list.Init()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *responseList) push(v interface{}) {
|
||||||
|
defer r.lock.Unlock()
|
||||||
|
r.lock.Lock()
|
||||||
|
r.list.PushBack(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *responseList) traverse(action func(response response)) {
|
||||||
|
r.lock.Lock()
|
||||||
|
defer r.lock.Unlock()
|
||||||
|
for e := r.list.Front(); e != nil; e = e.Next() {
|
||||||
|
action(e.Value.(response))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *responseList) remove(sessionId uint32) *response {
|
||||||
|
r.lock.Lock()
|
||||||
|
defer r.lock.Unlock()
|
||||||
|
for e := r.list.Front(); e != nil; e = e.Next() {
|
||||||
|
response := e.Value.(response)
|
||||||
|
if response.sessionId == sessionId {
|
||||||
|
r.list.Remove(e)
|
||||||
|
return &response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type UitestKitCallback interface {
|
||||||
|
OnData([]byte)
|
||||||
|
OnError(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReqTypeEnum int
|
||||||
|
|
||||||
|
const (
|
||||||
|
DEFAULT ReqTypeEnum = iota
|
||||||
|
SCREEN_CAPTURE
|
||||||
|
UI_ACTION_CAPTURE
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
HEADER_BYTES = "_uitestkit_rpc_message_head_"
|
||||||
|
TAILER_BYTES = "_uitestkit_rpc_message_tail_"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newUitestKitTransport(serial string, host string, port string) (uKtp uitestKitTransport, err error) {
|
||||||
|
pool, err := newConnectionPool(host, port, 3)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("[uitest] failed to init connection pool \n%v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uKtp.connectionPool = pool
|
||||||
|
uKtp.socketMap = make(map[string]*SocketContext)
|
||||||
|
uKtp.serial = serial
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uKtp *uitestKitTransport) initializeSocket(reqType ReqTypeEnum) error {
|
||||||
|
socketId := fmt.Sprintf("socket_%d_%s", reqType, uKtp.serial)
|
||||||
|
uKtp.mu.Lock()
|
||||||
|
defer uKtp.mu.Unlock()
|
||||||
|
if uKtp.socketMap[socketId] != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
connection, err := uKtp.connectionPool.getConnection()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
uKtp.socketMap[socketId] = &SocketContext{socketId: socketId, conn: connection, callbackMap: make(map[string]UitestKitCallback), queue: newResponseList()}
|
||||||
|
go uKtp.receive(reqType)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uKtp *uitestKitTransport) disconnect(reqType ReqTypeEnum) {
|
||||||
|
socketId := fmt.Sprintf("socket_%d_%s", reqType, uKtp.serial)
|
||||||
|
uKtp.mu.Lock()
|
||||||
|
defer uKtp.mu.Unlock()
|
||||||
|
socketContext := uKtp.socketMap[socketId]
|
||||||
|
if socketContext == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
socketContext.Close()
|
||||||
|
delete(uKtp.socketMap, socketId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uKtp *uitestKitTransport) registerCallback(reqType ReqTypeEnum, sessionId uint32, callback UitestKitCallback) error {
|
||||||
|
socketId := fmt.Sprintf("socket_%d_%s", reqType, uKtp.serial)
|
||||||
|
socketContext := uKtp.socketMap[socketId]
|
||||||
|
if socketContext == nil {
|
||||||
|
if err := uKtp.initializeSocket(reqType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
socketContext = uKtp.socketMap[socketId]
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
socketContext.writeLock.Lock()
|
||||||
|
res := socketContext.queue.remove(sessionId)
|
||||||
|
socketContext.writeLock.Unlock()
|
||||||
|
if res != nil && callback != nil {
|
||||||
|
callback.OnData(res.payload)
|
||||||
|
}
|
||||||
|
if res == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
socketContext.writeLock.Lock()
|
||||||
|
socketContext.callbackMap[strconv.Itoa(int(sessionId))] = callback
|
||||||
|
socketContext.writeLock.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uKtp *uitestKitTransport) receive(reqType ReqTypeEnum) {
|
||||||
|
socketId := fmt.Sprintf("socket_%d_%s", reqType, uKtp.serial)
|
||||||
|
defer uKtp.disconnect(reqType)
|
||||||
|
socketContext := uKtp.socketMap[socketId]
|
||||||
|
var receiveError error
|
||||||
|
defer func() {
|
||||||
|
if receiveError != nil {
|
||||||
|
socketContext.onException(receiveError)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if socketContext == nil {
|
||||||
|
if receiveError = uKtp.initializeSocket(reqType); receiveError != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
socketContext = uKtp.socketMap[socketId]
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
headerSize := len(HEADER_BYTES)
|
||||||
|
raw, err := _readN(socketContext.conn, headerSize+8)
|
||||||
|
if err != nil {
|
||||||
|
receiveError = err
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if len(raw) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
header := raw[:len(HEADER_BYTES)]
|
||||||
|
if !bytes.Equal(header, []byte(HEADER_BYTES)) {
|
||||||
|
receiveError = fmt.Errorf("verify message head failed on channel: %s", socketId)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
var sessionId, length uint32
|
||||||
|
if receiveError = binary.Read(bytes.NewReader(raw[headerSize:headerSize+4]), binary.BigEndian, &sessionId); receiveError != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if receiveError = binary.Read(bytes.NewReader(raw[headerSize+4:headerSize+8]), binary.BigEndian, &length); receiveError != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
payload, err := _readN(socketContext.conn, int(length))
|
||||||
|
if err != nil {
|
||||||
|
receiveError = err
|
||||||
|
break
|
||||||
|
}
|
||||||
|
tail, err := _readN(socketContext.conn, len(TAILER_BYTES))
|
||||||
|
if err != nil {
|
||||||
|
receiveError = err
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if !bytes.Equal(tail, []byte(TAILER_BYTES)) {
|
||||||
|
receiveError = fmt.Errorf("verify message tail failed on channel: %s", socketId)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
socketContext.writeLock.Lock()
|
||||||
|
callback := socketContext.callbackMap[strconv.Itoa(int(sessionId))]
|
||||||
|
socketContext.writeLock.Unlock()
|
||||||
|
if callback != nil {
|
||||||
|
callback.OnData(payload)
|
||||||
|
} else {
|
||||||
|
socketContext.queue.push(response{sessionId: sessionId, payload: payload})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uKtp *uitestKitTransport) sendMessage(reqType ReqTypeEnum, sessionId uint32, message string) (response UitestKitResponse, err error) {
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
uKtp.disconnect(reqType)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err = uKtp._sendMessage(reqType, sessionId, message); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return uKtp.receiveMessage(reqType, sessionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uKtp *uitestKitTransport) _sendMessage(reqType ReqTypeEnum, sessionId uint32, message string) (err error) {
|
||||||
|
socketId := fmt.Sprintf("socket_%d_%s", reqType, uKtp.serial)
|
||||||
|
socketContext := uKtp.socketMap[socketId]
|
||||||
|
if socketContext == nil {
|
||||||
|
if err = uKtp.initializeSocket(reqType); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
socketContext = uKtp.socketMap[socketId]
|
||||||
|
}
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
if err = binary.Write(buffer, binary.BigEndian, []byte(HEADER_BYTES)); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = binary.Write(buffer, binary.BigEndian, sessionId); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = binary.Write(buffer, binary.BigEndian, uint32(len(message))); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = binary.Write(buffer, binary.BigEndian, []byte(message)); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = binary.Write(buffer, binary.BigEndian, []byte(TAILER_BYTES)); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
socketContext.writeLock.Lock()
|
||||||
|
defer socketContext.writeLock.Unlock()
|
||||||
|
return _send(socketContext.conn, buffer.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *SocketContext) Close() {
|
||||||
|
sc.writeLock.Lock()
|
||||||
|
defer sc.writeLock.Unlock()
|
||||||
|
|
||||||
|
if sc.conn != nil {
|
||||||
|
_ = sc.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空callbackMap
|
||||||
|
for key := range sc.callbackMap {
|
||||||
|
delete(sc.callbackMap, key)
|
||||||
|
}
|
||||||
|
sc.callbackMap = nil
|
||||||
|
|
||||||
|
// 清理队列
|
||||||
|
if sc.queue != nil {
|
||||||
|
sc.queue.Clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uKtp *uitestKitTransport) Close() {
|
||||||
|
uKtp.mu.Lock()
|
||||||
|
defer uKtp.mu.Unlock()
|
||||||
|
|
||||||
|
// 关闭所有的SocketContext
|
||||||
|
if uKtp.socketMap != nil {
|
||||||
|
for _, socketContext := range uKtp.socketMap {
|
||||||
|
socketContext.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭连接池
|
||||||
|
if uKtp.connectionPool != nil {
|
||||||
|
uKtp.connectionPool.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
uKtp.socketMap = nil
|
||||||
|
uKtp.connectionPool = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uKtp *uitestKitTransport) receiveMessage(reqType ReqTypeEnum, sessionId uint32) (response UitestKitResponse, err error) {
|
||||||
|
socketId := fmt.Sprintf("socket_%d_%s", reqType, uKtp.serial)
|
||||||
|
socketContext := uKtp.socketMap[socketId]
|
||||||
|
if socketContext == nil {
|
||||||
|
err = fmt.Errorf("failed to read message. not found target connection")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 创建一个计时器,设置超时时间为 10 秒
|
||||||
|
timeout := time.After(2 * time.Second)
|
||||||
|
|
||||||
|
// 创建一个 ticker,每秒触发一次
|
||||||
|
ticker := time.NewTicker(10 * time.Millisecond)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-timeout:
|
||||||
|
err = fmt.Errorf("failed to read message in 2 second")
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
res := socketContext.queue.remove(sessionId)
|
||||||
|
if res != nil {
|
||||||
|
err = json.Unmarshal(res.payload, &response)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *SocketContext) onException(err error) {
|
||||||
|
for _, callback := range sc.callbackMap {
|
||||||
|
if callback != nil {
|
||||||
|
callback.OnError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newResponseList() *responseList {
|
||||||
|
return &responseList{list: list.New()}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package ghdc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type uitestTransport struct {
|
||||||
|
connectionPool *ConnectionPool
|
||||||
|
readTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func newUitestTransport(host string, port string, readTimeout ...time.Duration) (uTp uitestTransport, err error) {
|
||||||
|
if len(readTimeout) == 0 {
|
||||||
|
readTimeout = []time.Duration{DefaultReadTimeout}
|
||||||
|
}
|
||||||
|
uTp.readTimeout = readTimeout[0]
|
||||||
|
pool, err := newConnectionPool(host, port, 2)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("[uitest] failed to init connection pool \n%v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uTp.connectionPool = pool
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func newHypiumRequest(params interface{}, method string) UitestRequest {
|
||||||
|
return UitestRequest{
|
||||||
|
Module: "com.ohos.devicetest.hypiumApiHelper",
|
||||||
|
Method: method,
|
||||||
|
Params: params,
|
||||||
|
RequestId: MD5(fmt.Sprintf("%d", time.Now().UnixNano())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uTp *uitestTransport) SendReq(req UitestRequest) (res UitestResponse, err error) {
|
||||||
|
requestBytes, err := json.Marshal(req)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("[uitest] failed to marshal %v request %v", req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
requestBytes = append(requestBytes, '\n')
|
||||||
|
conn, err := uTp.connectionPool.getConnection()
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("[uitest] failed to get connection \n%v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
uTp.connectionPool.releaseConnection(conn)
|
||||||
|
}()
|
||||||
|
_ = conn.SetReadDeadline(time.Now().Add(uTp.readTimeout))
|
||||||
|
debugLog(fmt.Sprintf("[uitest] send Request %v", req))
|
||||||
|
err = _send(conn, requestBytes)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("[uitest] failed to get send %v request %v", req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
raw, err := _read(conn)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("[uitest] failed to read %v response %v", req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res = UitestResponse{}
|
||||||
|
err = json.Unmarshal(raw, &res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uTp *uitestTransport) Close() {
|
||||||
|
if uTp.connectionPool != nil {
|
||||||
|
uTp.connectionPool.close()
|
||||||
|
}
|
||||||
|
uTp.connectionPool = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func MD5(str string) string {
|
||||||
|
hash := md5.New()
|
||||||
|
hash.Write([]byte(str))
|
||||||
|
return hex.EncodeToString(hash.Sum(nil))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user