refactor ios: replace gidevice with go-ios

This commit is contained in:
lilong.129
2024-11-23 14:12:07 +08:00
parent 8aac2181be
commit ef37d88e0b
118 changed files with 2202 additions and 11764 deletions

View File

@@ -0,0 +1,35 @@
//go:build localtest
package uitest
import (
"testing"
"github.com/httprunner/httprunner/v4/hrp"
"github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
)
func TestHamonyDouyinFeedTest(t *testing.T) {
testCase := &hrp.TestCase{
Config: hrp.NewConfig("点播_抖音_滑动场景_随机间隔_android").
WithVariables(map[string]interface{}{
"device": "a38c2c5c",
"query": "${ENV(query)}",
}).
SetAndroid(uixt.WithSerialNumber("$device")),
TestSteps: []hrp.IStep{
hrp.NewStep("启动抖音").
Android().
AppTerminate("com.ss.hm.ugc.aweme"),
},
}
if err := testCase.Dump2JSON("demo_android_swipe.json"); err != nil {
t.Fatal(err)
}
err := hrp.Run(t, testCase)
if err != nil {
t.Fatal(err)
}
}

View File

@@ -19,10 +19,6 @@ func TestIOSDouyinLive(t *testing.T) {
uixt.WithWDALogOn(true),
uixt.WithWDAPort(8700),
uixt.WithWDAMjpegPort(8800),
uixt.WithIOSPerfOptions(
uixt.WithIOSPerfSystemCPU(true),
uixt.WithIOSPerfSystemMem(true),
),
),
TestSteps: []hrp.IStep{
hrp.NewStep("启动抖音").

View File

@@ -18,13 +18,6 @@ func TestWDALog(t *testing.T) {
SetIOS(
uixt.WithWDALogOn(true),
uixt.WithWDAPort(8700), uixt.WithWDAMjpegPort(8800),
uixt.WithIOSPerfOptions(
uixt.WithIOSPerfSystemCPU(true),
uixt.WithIOSPerfSystemMem(true),
uixt.WithIOSPerfNetwork(true),
uixt.WithIOSPerfFPS(true),
),
uixt.WithXCTest("com.gtf.wda.runner.xctrunner"),
),
TestSteps: []hrp.IStep{
hrp.NewStep("启动抖音").

View File

@@ -37,33 +37,11 @@ func convertTimeToSeconds(timeStr string) (int, error) {
}
func initIOSDevice(uuid string) uixt.IDevice {
perfOptions := []uixt.IOSPerfOption{}
for _, p := range perf {
switch p {
case "sys_cpu":
perfOptions = append(perfOptions, uixt.WithIOSPerfSystemCPU(true))
case "sys_mem":
perfOptions = append(perfOptions, uixt.WithIOSPerfSystemMem(true))
case "sys_net":
perfOptions = append(perfOptions, uixt.WithIOSPerfSystemNetwork(true))
case "sys_disk":
perfOptions = append(perfOptions, uixt.WithIOSPerfSystemDisk(true))
case "network":
perfOptions = append(perfOptions, uixt.WithIOSPerfNetwork(true))
case "fps":
perfOptions = append(perfOptions, uixt.WithIOSPerfFPS(true))
case "gpu":
perfOptions = append(perfOptions, uixt.WithIOSPerfGPU(true))
}
}
perfOptions = append(perfOptions, uixt.WithIOSPerfOutputInterval(interval*1000))
device, err := uixt.NewIOSDevice(
uixt.WithUDID(uuid),
uixt.WithWDAPort(8700), uixt.WithWDAMjpegPort(8800),
uixt.WithResetHomeOnStartup(false), // not reset home on startup
uixt.WithIOSPerfOptions(perfOptions...),
uixt.WithXCTest("com.gtf.wda.runner.xctrunner"),
)
if err != nil {
log.Fatal().Err(err).Msg("failed to init ios device")
@@ -267,7 +245,6 @@ func (wc *WorldCupLive) dumpResult() error {
encoder.SetEscapeHTML(false)
encoder.SetIndent("", " ")
wc.PerfFile = wc.driver.Device.StopPerf()
err := encoder.Encode(wc)
if err != nil {
log.Error().Err(err).Msg("encode json failed")

View File

@@ -59,17 +59,6 @@ func TestIOSDouyinWorldCupLive(t *testing.T) {
uixt.WithWDALogOn(true),
uixt.WithWDAPort(8700),
uixt.WithWDAMjpegPort(8800),
uixt.WithXCTest("com.gtf.wda.runner.xctrunner"),
uixt.WithIOSPerfOptions(
uixt.WithIOSPerfNetwork(true),
// uixt.WithIOSPerfBundleID("com.ss.iphone.ugc.Aweme"),
),
uixt.WithIOSPcapOptions(
// uixt.WithIOSPcapAll(true),
// uixt.WithIOSPcapPID(1234),
// uixt.WithIOSPcapProcName("Awe"),
uixt.WithIOSPcapBundleID("com.ss.iphone.ugc.Aweme"),
),
),
TestSteps: []hrp.IStep{
hrp.NewStep("启动抖音").

40
go.mod
View File

@@ -1,12 +1,15 @@
module github.com/httprunner/httprunner/v4
go 1.22
go 1.22.0
toolchain go1.22.7
require (
code.byted.org/iesqa/ghdc v0.0.0-20241011071205-a1bec975e48c
code.byted.org/iesqa/ghdc v0.0.0-20241009025217-ecb76cf5bd27
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69
github.com/Masterminds/semver v1.5.0
github.com/andybalholm/brotli v1.0.4
github.com/danielpaulus/go-ios v1.0.144
github.com/denisbrodbeck/machineid v1.0.1
github.com/fatih/color v1.16.0
github.com/getsentry/sentry-go v0.13.0
@@ -29,9 +32,9 @@ require (
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/spf13/cobra v1.5.0
github.com/stretchr/testify v1.9.0
golang.org/x/net v0.25.0
golang.org/x/net v0.26.0
golang.org/x/oauth2 v0.8.0
golang.org/x/text v0.15.0
golang.org/x/text v0.16.0
google.golang.org/grpc v1.57.0
google.golang.org/protobuf v1.34.1
gopkg.in/yaml.v2 v2.4.0
@@ -45,6 +48,7 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/bytedance/sonic v1.11.6 // indirect
github.com/bytedance/sonic/loader v0.1.1 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
@@ -60,9 +64,13 @@ require (
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.20.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/grandcat/zeroconf v1.0.0 // indirect
github.com/hashicorp/go-hclog v1.5.0 // indirect
github.com/hashicorp/go-plugin v1.4.10 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
@@ -76,29 +84,47 @@ require (
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/miekg/dns v1.1.57 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/oklog/run v1.1.0 // indirect
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
github.com/quic-go/quic-go v0.40.1-0.20231203135336-87ef8ec48d55 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/tadglines/go-pkgs v0.0.0-20210623144937-b983b20f54f9 // indirect
github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/tklauser/numcpus v0.5.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect
go.uber.org/mock v0.3.0 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/crypto v0.24.0 // indirect
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect
gvisor.dev/gvisor v0.0.0-20240405191320-0878b34101b5 // indirect
software.sslmate.com/src/go-pkcs12 v0.2.0 // indirect
)
// replace github.com/httprunner/funplugin => ../funplugin

103
go.sum
View File

@@ -34,23 +34,15 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
code.byted.org/iesqa/ghdc v0.0.0-20240918093157-b4feef0e5af0 h1:qsKGQS3A530QpOXY80ogzzhVpf25Q4WfHusSlRyNvLU=
code.byted.org/iesqa/ghdc v0.0.0-20240918093157-b4feef0e5af0/go.mod h1:0IrKgKT75jmlpi9N0Mi5xWKctJuKHFM6f+ZMQe5vnNs=
code.byted.org/iesqa/ghdc v0.0.0-20240920083733-6b2acbe67b03 h1:VddSSSko1t4Z9LICi2p2IGEuXjqasHCEweFpk1iqGR4=
code.byted.org/iesqa/ghdc v0.0.0-20240920083733-6b2acbe67b03/go.mod h1:C2kq6TTE+JAOnqDorSwae1MQzRuex03RshuSUC2U/FY=
code.byted.org/iesqa/ghdc v0.0.0-20240925072739-7441749ee5a7 h1:QX1dADVurN1suyofdznrDEdz8QCRIGd2h/jU5xkGcQU=
code.byted.org/iesqa/ghdc v0.0.0-20240925072739-7441749ee5a7/go.mod h1:C2kq6TTE+JAOnqDorSwae1MQzRuex03RshuSUC2U/FY=
code.byted.org/iesqa/ghdc v0.0.0-20240930033019-572b6e5ca845 h1:AiHYk33kEbDC3+lIvGj0WJx47JvmuX27pOmWdd2/zAc=
code.byted.org/iesqa/ghdc v0.0.0-20240930033019-572b6e5ca845/go.mod h1:C2kq6TTE+JAOnqDorSwae1MQzRuex03RshuSUC2U/FY=
code.byted.org/iesqa/ghdc v0.0.0-20241008133702-1c71219a0303 h1:98fx5eC2fYzZKL48QMT1ykxBHii3JhY/b7QBFEpsC/8=
code.byted.org/iesqa/ghdc v0.0.0-20241008133702-1c71219a0303/go.mod h1:C2kq6TTE+JAOnqDorSwae1MQzRuex03RshuSUC2U/FY=
code.byted.org/iesqa/ghdc v0.0.0-20241009130937-5efa701d3f56 h1:KolYCn3C/sgWUotywSO4L/8r4lOAgP/2bh6YeIofOJY=
code.byted.org/iesqa/ghdc v0.0.0-20241009130937-5efa701d3f56/go.mod h1:C2kq6TTE+JAOnqDorSwae1MQzRuex03RshuSUC2U/FY=
code.byted.org/iesqa/ghdc v0.0.0-20241011071205-a1bec975e48c h1:MagwhUnFDkOB/feAUSagsFQuaiqmZDBY0fFvs1tj8uU=
code.byted.org/iesqa/ghdc v0.0.0-20241011071205-a1bec975e48c/go.mod h1:C2kq6TTE+JAOnqDorSwae1MQzRuex03RshuSUC2U/FY=
code.byted.org/iesqa/ghdc v0.0.0-20241009025217-ecb76cf5bd27 h1:+wNJiEXXIUP6luKJRA4tfwDqfnWUON6LIopKD9tvUns=
code.byted.org/iesqa/ghdc v0.0.0-20241009025217-ecb76cf5bd27/go.mod h1:C2kq6TTE+JAOnqDorSwae1MQzRuex03RshuSUC2U/FY=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69 h1:+tu3HOoMXB7RXEINRVIpxJCT+KdYiI7LAEAUrOw3dIU=
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69/go.mod h1:L1AbZdiDllfyYH5l5OkAaZtk7VkWe89bPJFmnDBNHxg=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -66,6 +58,8 @@ github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -84,11 +78,15 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/danielpaulus/go-ios v1.0.144 h1:gojkhtScKYMwC4YHKnoB7u910GmtArg5ugsfAk7W4wY=
github.com/danielpaulus/go-ios v1.0.144/go.mod h1:ZkUcaC59yNba47j/+ULKsCi3dYPFwY9r39PxdmVmLHE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
github.com/elazarl/goproxy v0.0.0-20240726154733-8b0c20506380 h1:1NyRx2f4W4WBRyg0Kys0ZbaNmDDzZ2R/C7DTi+bbsJ0=
github.com/elazarl/goproxy v0.0.0-20240726154733-8b0c20506380/go.mod h1:thX175TtLTzLj3p7N/Q9IiKZ7NF+p72cvL91emV0hzo=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -96,6 +94,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/getsentry/sentry-go v0.13.0 h1:20dgTiUSfxRB/EhMPtxcL9ZEbM1ZdR+W/7f7NWD+xWo=
@@ -117,6 +117,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
@@ -139,6 +141,8 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@@ -174,6 +178,8 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -195,13 +201,19 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grandcat/zeroconf v1.0.0 h1:uHhahLBKqwWBV6WZUDAT71044vwOTL+McW0mBJvo6kE=
github.com/grandcat/zeroconf v1.0.0/go.mod h1:lTKmG1zh86XyCoUeIHSA4FJMBwCJiQmGfcP2PdzytEs=
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-plugin v1.4.10 h1:xUbmA4jC6Dq163/fWcp8P3JuHilrHHMLNRxzGQJ9hNk=
@@ -213,6 +225,7 @@ github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbg
github.com/httprunner/funplugin v0.5.5 h1:VU1a6kj1AsJ/ucIhhI5NLHXOP4xnW2JGgk50vBV3Zis=
github.com/httprunner/funplugin v0.5.5/go.mod h1:YZzBBSOSdLZEpHZz0P2E5SOQ+o1+Fbn30oWS4RGHBz0=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
@@ -277,6 +290,9 @@ github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWV
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
@@ -295,8 +311,14 @@ github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=
github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
@@ -331,6 +353,10 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/quic-go/quic-go v0.40.1-0.20231203135336-87ef8ec48d55 h1:I4N3ZRnkZPbDN935Tg8QDf8fRpHp3bZ0U0/L42jBgNE=
github.com/quic-go/quic-go v0.40.1-0.20231203135336-87ef8ec48d55/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
@@ -349,6 +375,10 @@ github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMT
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8=
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E=
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
@@ -357,6 +387,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@@ -370,6 +401,8 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tadglines/go-pkgs v0.0.0-20210623144937-b983b20f54f9 h1:aeN+ghOV0b2VCmKKO3gqnDQ8mLbpABZgRR2FVYx4ouI=
github.com/tadglines/go-pkgs v0.0.0-20210623144937-b983b20f54f9/go.mod h1:roo6cZ/uqpwKMuvPG0YmzI5+AmUiMWfjCBZpGXqbTxE=
github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw=
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
@@ -385,11 +418,15 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 h1:CCriYyAfq1Br1aIYettdHZTy8mBTIPo7We18TuO/bak=
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
@@ -400,8 +437,9 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -412,6 +450,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY=
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -433,6 +473,8 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -447,6 +489,7 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -463,11 +506,12 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -487,6 +531,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -500,6 +546,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -532,12 +579,13 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -548,11 +596,13 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -573,6 +623,7 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@@ -594,10 +645,14 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
@@ -702,6 +757,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gvisor.dev/gvisor v0.0.0-20240405191320-0878b34101b5 h1:DOUDfNS+CFMM46k18FRF5k/0yz5NhZYMiUQxf4xglIU=
gvisor.dev/gvisor v0.0.0-20240405191320-0878b34101b5/go.mod h1:NQHVAzMwvZ+Qe3ElSiHmq9RUm1MdNHpUZ52fiEqvn+0=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -716,3 +773,5 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
software.sslmate.com/src/go-pkcs12 v0.2.0 h1:nlFkj7bTysH6VkC4fGphtjXRbezREPgrHuJG20hBGPE=
software.sslmate.com/src/go-pkcs12 v0.2.0/go.mod h1:23rNcYsMabIc1otwLpTkCCPwUq6kQsTyowttG/as0kQ=

View File

@@ -9,7 +9,7 @@ import (
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v4/hrp/internal/sdk"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice"
"github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
)
type Application struct {
@@ -37,21 +37,20 @@ var listAppsCmd = &cobra.Command{
return err
}
var applicationType gidevice.ApplicationType
device.GetDeviceInfo()
var applicationType uixt.ApplicationType
switch appType {
case "user":
applicationType = gidevice.ApplicationTypeUser
applicationType = uixt.ApplicationTypeUser
case "system":
applicationType = gidevice.ApplicationTypeSystem
applicationType = uixt.ApplicationTypeSystem
case "internal":
applicationType = gidevice.ApplicationTypeInternal
applicationType = uixt.ApplicationTypeInternal
case "all":
applicationType = gidevice.ApplicationTypeAny
applicationType = uixt.ApplicationTypeAny
}
result, err := device.InstallationProxyBrowse(
gidevice.WithApplicationType(applicationType),
gidevice.WithReturnAttributes("CFBundleVersion", "CFBundleDisplayName", "CFBundleIdentifier"))
result, err := device.ListApps(applicationType)
if err != nil {
return fmt.Errorf("get app list failed %v", err)
}

View File

@@ -7,52 +7,20 @@ import (
"strings"
"time"
"github.com/pkg/errors"
"github.com/danielpaulus/go-ios/ios"
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v4/hrp/internal/sdk"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice"
"github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
)
type Device struct {
d gidevice.Device
UDID string `json:"UDID"`
Status string `json:"status"`
ConnectionType string `json:"connectionType"`
ConnectionSpeed int `json:"connectionSpeed"`
DeviceDetail *DeviceDetail `json:"deviceDetail,omitempty"`
}
type DeviceDetail struct {
DeviceName string `json:"deviceName,omitempty"`
DeviceClass string `json:"deviceClass,omitempty"`
ProductVersion string `json:"productVersion,omitempty"`
ProductType string `json:"productType,omitempty"`
ProductName string `json:"productName,omitempty"`
PasswordProtected bool `json:"passwordProtected,omitempty"`
ModelNumber string `json:"modelNumber,omitempty"`
SerialNumber string `json:"serialNumber,omitempty"`
SIMStatus string `json:"simStatus,omitempty"`
PhoneNumber string `json:"phoneNumber,omitempty"`
CPUArchitecture string `json:"cpuArchitecture,omitempty"`
ProtocolVersion string `json:"protocolVersion,omitempty"`
RegionInfo string `json:"regionInfo,omitempty"`
TimeZone string `json:"timeZone,omitempty"`
UniqueDeviceID string `json:"uniqueDeviceID,omitempty"`
WiFiAddress string `json:"wifiAddress,omitempty"`
BuildVersion string `json:"buildVersion,omitempty"`
}
func (device *Device) GetDetail() (*DeviceDetail, error) {
value, err := device.d.GetValue("", "")
if err != nil {
return nil, errors.Wrap(err, "get device detail failed")
}
detailByte, _ := json.Marshal(value)
detail := &DeviceDetail{}
json.Unmarshal(detailByte, detail)
return detail, nil
d ios.DeviceEntry
UDID string `json:"UDID"`
Status string `json:"status"`
ConnectionType string `json:"connectionType"`
ConnectionSpeed int `json:"connectionSpeed"`
DeviceDetail *uixt.DeviceDetail `json:"deviceDetail,omitempty"`
}
func (device *Device) GetStatus() string {
@@ -89,7 +57,7 @@ var listDevicesCmd = &cobra.Command{
}
for _, d := range devices {
deviceProperties := d.Properties()
deviceProperties := d.Properties
device := &Device{
d: d,
UDID: deviceProperties.SerialNumber,
@@ -98,27 +66,16 @@ var listDevicesCmd = &cobra.Command{
}
device.Status = device.GetStatus()
if isDetail {
device.DeviceDetail, err = device.GetDetail()
if err != nil {
return err
}
fmt.Println(device.ToFormat())
} else {
fmt.Println(device.UDID, device.ConnectionType, device.Status)
}
fmt.Println(device.UDID, device.ConnectionType, device.Status)
}
return nil
},
}
var (
udid string
isDetail bool
)
var udid string
func init() {
listDevicesCmd.Flags().StringVarP(&udid, "udid", "u", "", "filter by device's udid")
listDevicesCmd.Flags().BoolVarP(&isDetail, "detail", "d", false, "print device's detail")
iosRootCmd.AddCommand(listDevicesCmd)
}

View File

@@ -1,11 +1,8 @@
package ios
import (
"fmt"
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice"
"github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
)
@@ -14,15 +11,12 @@ var iosRootCmd = &cobra.Command{
Short: "simple utils for ios device management",
}
func getDevice(udid string) (gidevice.Device, error) {
devices, err := uixt.GetIOSDevices(udid)
func getDevice(udid string) (*uixt.IOSDevice, error) {
device, err := uixt.NewIOSDevice(uixt.WithUDID(udid))
if err != nil {
return nil, err
}
if len(devices) > 1 {
return nil, fmt.Errorf("found multiple attached devices, please specify ios udid")
}
return devices[0], nil
return device, nil
}
func Init(rootCmd *cobra.Command) {

View File

@@ -34,13 +34,8 @@ var installCmd = &cobra.Command{
fmt.Println(err)
return err
}
driverExt, err := device.NewDriver()
if err != nil {
fmt.Println(err)
return err
}
err = driverExt.Install(args[0])
err = device.Install(args[0])
if err != nil {
fmt.Println(err)
return err
@@ -51,7 +46,7 @@ var installCmd = &cobra.Command{
}
func init() {
installCmd.Flags().StringVarP(&udid, "serial", "s", "", "filter by device's serial")
installCmd.Flags().StringVarP(&udid, "udid", "u", "", "filter by device's serial")
iosRootCmd.AddCommand(installCmd)
}

View File

@@ -1,9 +1,7 @@
package ios
import (
"encoding/base64"
"fmt"
"path/filepath"
"strings"
"time"
@@ -33,22 +31,18 @@ var mountCmd = &cobra.Command{
return err
}
value, err := device.GetValue("", "ProductVersion")
images, errImage := device.ListImage()
if err != nil {
return fmt.Errorf("get device ProductVersion failed: %v", err)
return fmt.Errorf("list device images failed: %v", err)
}
log.Info().Str("version", value.(string)).Msg("get device version")
imageSignatures, errImage := device.Images()
if listDeveloperDiskImage {
for i, imgSign := range imageSignatures {
fmt.Printf("[%d] %s\n", i+1, base64.StdEncoding.EncodeToString(imgSign))
for i, imgSign := range images {
fmt.Printf("[%d] %s\n", i+1, imgSign)
}
return nil
}
if errImage == nil && len(imageSignatures) > 0 {
if errImage == nil && len(images) > 0 {
log.Info().Msg("ios developer image is already mounted")
return nil
}
@@ -59,27 +53,8 @@ var mountCmd = &cobra.Command{
return fmt.Errorf("developer disk image directory not exist: %s", developerDiskImageDir)
}
ver := strings.Split(value.(string), ".")
if len(ver) < 2 {
return fmt.Errorf("got invalid device ProductVersion: %v", value)
}
version := ver[0] + "." + ver[1]
var dmgPath, signaturePath string
if builtin.IsFilePathExists(filepath.Join(developerDiskImageDir, "DeveloperDiskImage.dmg")) {
dmgPath = filepath.Join(developerDiskImageDir, "DeveloperDiskImage.dmg")
signaturePath = filepath.Join(developerDiskImageDir, "DeveloperDiskImage.dmg.signature")
} else if builtin.IsFilePathExists(filepath.Join(developerDiskImageDir, version, "DeveloperDiskImage.dmg")) {
dmgPath = filepath.Join(developerDiskImageDir, version, "DeveloperDiskImage.dmg")
signaturePath = filepath.Join(developerDiskImageDir, version, "DeveloperDiskImage.dmg.signature")
} else {
log.Error().Str("dir", developerDiskImageDir).Msgf(
"developer disk image %s not found in directory", version)
return fmt.Errorf("developer disk image %s not found", version)
}
if err = device.MountDeveloperDiskImage(dmgPath, signaturePath); err != nil {
return fmt.Errorf("mount developer disk image %s failed: %s", version, err)
if err = device.MountImage(developerDiskImageDir); err != nil {
return fmt.Errorf("mount developer disk image failed: %s", err)
}
log.Info().Msg("mount developer disk image successfully")

View File

@@ -1,92 +0,0 @@
package ios
import (
"os"
"os/signal"
"strings"
"syscall"
"time"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v4/hrp/internal/builtin"
"github.com/httprunner/httprunner/v4/hrp/internal/config"
"github.com/httprunner/httprunner/v4/hrp/internal/sdk"
"github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
)
var pcapCmd = &cobra.Command{
Use: "pcap",
Short: "capture ios network packets",
RunE: func(cmd *cobra.Command, args []string) (err error) {
startTime := time.Now()
defer func() {
sdk.SendGA4Event("hrp_ios_pcap", map[string]interface{}{
"args": strings.Join(args, "-"),
"success": err == nil,
"engagement_time_msec": time.Since(startTime).Milliseconds(),
})
}()
pcapOptions := []uixt.IOSPcapOption{}
if pid > 0 {
pcapOptions = append(pcapOptions, uixt.WithIOSPcapPID(pid))
}
if procName != "" {
pcapOptions = append(pcapOptions, uixt.WithIOSPcapProcName(procName))
}
if bundleID != "" {
pcapOptions = append(pcapOptions, uixt.WithIOSPcapBundleID(bundleID))
}
if len(pcapOptions) == 0 {
pcapOptions = append(pcapOptions, uixt.WithIOSPcapAll(true))
}
device, err := uixt.NewIOSDevice(
uixt.WithUDID(udid),
uixt.WithIOSPcapOptions(pcapOptions...),
)
if err != nil {
log.Fatal().Err(err).Msg("failed to init ios device")
}
err = builtin.EnsureFolderExists(config.ResultsPath)
if err != nil {
return err
}
if err = device.StartPcap(); err != nil {
return err
}
defer device.StopPcap()
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGTERM, syscall.SIGINT)
timer := time.NewTimer(time.Duration(timeDuration) * time.Second)
for {
select {
case <-timer.C:
return nil
case <-c:
log.Warn().Msg("received signal, stop pcap")
return nil
}
}
},
}
var (
timeDuration int
pid int
procName string
)
func init() {
pcapCmd.Flags().StringVarP(&udid, "udid", "u", "", "specify device by udid")
pcapCmd.Flags().IntVarP(&pid, "pid", "p", 0, "specify process ID")
pcapCmd.Flags().StringVarP(&procName, "procName", "n", "", "specify process name")
pcapCmd.Flags().StringVarP(&bundleID, "bundleID", "b", "", "specify bundle ID")
pcapCmd.Flags().IntVarP(&timeDuration, "duration", "t", 10, "specify time duraion in seconds")
iosRootCmd.AddCommand(pcapCmd)
}

View File

@@ -1,98 +0,0 @@
package ios
import (
"os"
"os/signal"
"strings"
"syscall"
"time"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v4/hrp/internal/builtin"
"github.com/httprunner/httprunner/v4/hrp/internal/config"
"github.com/httprunner/httprunner/v4/hrp/internal/sdk"
"github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
)
var perfCmd = &cobra.Command{
Use: "perf",
Short: "capture ios performance data (cpu,mem,disk,net,fps,etc.)",
RunE: func(cmd *cobra.Command, args []string) (err error) {
startTime := time.Now()
defer func() {
sdk.SendGA4Event("hrp_ios_perf", map[string]interface{}{
"args": strings.Join(args, "-"),
"success": err == nil,
"engagement_time_msec": time.Since(startTime).Milliseconds(),
})
}()
perfOptions := []uixt.IOSPerfOption{}
for _, p := range indicators {
switch p {
case "sys_cpu":
perfOptions = append(perfOptions, uixt.WithIOSPerfSystemCPU(true))
case "sys_mem":
perfOptions = append(perfOptions, uixt.WithIOSPerfSystemMem(true))
case "sys_net":
perfOptions = append(perfOptions, uixt.WithIOSPerfSystemNetwork(true))
case "sys_disk":
perfOptions = append(perfOptions, uixt.WithIOSPerfSystemDisk(true))
case "network":
perfOptions = append(perfOptions, uixt.WithIOSPerfNetwork(true))
case "fps":
perfOptions = append(perfOptions, uixt.WithIOSPerfFPS(true))
case "gpu":
perfOptions = append(perfOptions, uixt.WithIOSPerfGPU(true))
}
}
perfOptions = append(perfOptions, uixt.WithIOSPerfOutputInterval(interval*1000))
device, err := uixt.NewIOSDevice(
uixt.WithUDID(udid),
uixt.WithIOSPerfOptions(perfOptions...),
)
if err != nil {
log.Fatal().Err(err).Msg("failed to init ios device")
}
err = builtin.EnsureFolderExists(config.ResultsPath)
if err != nil {
return err
}
if err = device.StartPerf(); err != nil {
return err
}
defer device.StopPerf()
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGTERM, syscall.SIGINT)
timer := time.NewTimer(time.Duration(timeDuration) * time.Second)
for {
select {
case <-timer.C:
return nil
case <-c:
log.Warn().Msg("received signal, stop perf")
return nil
}
}
},
}
var (
interval int
indicators []string
)
func init() {
perfCmd.Flags().StringVarP(&udid, "udid", "u", "", "specify device by udid")
perfCmd.Flags().StringSliceVarP(&indicators, "indicators", "p", []string{"sys_cpu", "sys_mem"},
"specify performance monitor, e.g. sys_cpu,sys_mem,sys_net,sys_disk,fps,network,gpu")
perfCmd.Flags().IntVarP(&timeDuration, "duration", "t", 10, "specify time duraion in seconds")
perfCmd.Flags().IntVarP(&interval, "interval", "i", 3, "set interval in seconds")
iosRootCmd.AddCommand(perfCmd)
}

View File

@@ -5,7 +5,6 @@ import (
"strings"
"time"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v4/hrp/internal/sdk"
@@ -30,34 +29,12 @@ var psCmd = &cobra.Command{
return err
}
apps, err := device.AppList()
runningProcesses, err := device.ListProcess(!isAll)
if err != nil {
return errors.Wrap(err, "get ios apps failed")
}
maxNameLen := 0
mapper := make(map[string]interface{})
for _, app := range apps {
mapper[app.ExecutableName] = app.CFBundleIdentifier
if len(app.ExecutableName) > maxNameLen {
maxNameLen = len(app.ExecutableName)
}
}
runningProcesses, err := device.AppRunningProcesses()
if err != nil {
return errors.Wrap(err, "get running processes failed")
return err
}
for _, p := range runningProcesses {
if !isAll && !p.IsApplication {
continue
}
bundleID, ok := mapper[p.Name]
if !ok {
bundleID = ""
}
fmt.Printf("%4d %-"+fmt.Sprintf("%d", maxNameLen)+"s %20s %s\n",
fmt.Printf("%4d %-"+fmt.Sprintf("%d", len(runningProcesses))+"s %20s %s\n",
p.Pid, p.Name, time.Since(p.StartDate).String(), bundleID)
}
return nil

View File

@@ -12,7 +12,7 @@ import (
var rebootCmd = &cobra.Command{
Use: "reboot",
Short: "reboot or shutdown ios device",
Short: "reboot ios device",
PersistentPreRun: func(cmd *cobra.Command, args []string) {},
RunE: func(cmd *cobra.Command, args []string) (err error) {
startTime := time.Now()
@@ -29,23 +29,16 @@ var rebootCmd = &cobra.Command{
return err
}
if isShutdown {
err = device.Shutdown()
} else {
err = device.Reboot()
}
err = device.Reboot()
if err != nil {
return err
}
fmt.Printf("reboot %s success\n", device.Properties().UDID)
fmt.Printf("reboot %s success\n", device.UDID)
return nil
},
}
var isShutdown bool
func init() {
rebootCmd.Flags().StringVarP(&udid, "udid", "u", "", "specify device by udid")
rebootCmd.Flags().BoolVarP(&isShutdown, "shutdown", "s", false, "shutdown ios device")
iosRootCmd.AddCommand(rebootCmd)
}

59
hrp/cmd/ios/uninstall.go Normal file
View File

@@ -0,0 +1,59 @@
package ios
import (
"fmt"
"strings"
"time"
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v4/hrp/internal/sdk"
"github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
)
var uninstallCmd = &cobra.Command{
Use: "uninstall [flags] PACKAGE",
Short: "uninstall Package atomically",
Args: cobra.MinimumNArgs(0),
RunE: func(cmd *cobra.Command, args []string) (err error) {
startTime := time.Now()
defer func() {
sdk.SendGA4Event("hrp_adb_devices", map[string]interface{}{
"args": strings.Join(args, "-"),
"success": err == nil,
"engagement_time_msec": time.Since(startTime).Milliseconds(),
})
}()
if len(bundleId) == 0 {
return fmt.Errorf("bundleId is empty")
}
_, err = getDevice(udid)
if err != nil {
return err
}
device, err := uixt.NewIOSDevice(uixt.WithUDID(udid))
if err != nil {
fmt.Println(err)
return err
}
err = device.Uninstall(bundleId)
if err != nil {
fmt.Println(err)
return err
}
fmt.Println("success")
return nil
},
}
var bundleId string
func init() {
uninstallCmd.Flags().StringVarP(&udid, "udid", "u", "", "filter by device's serial")
uninstallCmd.Flags().StringVarP(&bundleId, "bundleId", "b", "", "bundleId to uninstall")
iosRootCmd.AddCommand(uninstallCmd)
}

View File

@@ -2,10 +2,7 @@ package ios
import (
"fmt"
"os"
"os/signal"
"strings"
"syscall"
"time"
"github.com/pkg/errors"
@@ -37,33 +34,24 @@ var xctestCmd = &cobra.Command{
}
log.Info().Str("bundleID", bundleID).Msg("run xctest")
out, cancel, err := device.XCTest(bundleID)
err = device.RunXCTest(bundleID, testRunnerBundleID, xctestConfig)
if err != nil {
return errors.Wrap(err, "run xctest failed")
}
done := make(chan os.Signal, 1)
signal.Notify(done, syscall.SIGTERM, syscall.SIGINT)
// print xctest running logs
go func() {
for s := range out {
fmt.Print(s)
}
done <- os.Interrupt
}()
<-done
cancel()
return nil
},
}
var bundleID string
var (
bundleID string
testRunnerBundleID string
xctestConfig string
)
func init() {
xctestCmd.Flags().StringVarP(&udid, "udid", "u", "", "filter by device's udid")
xctestCmd.Flags().StringVarP(&bundleID, "bundleID", "b", "", "specify ios bundleID")
xctestCmd.Flags().StringVarP(&testRunnerBundleID, "testRunnerBundleID", "t", "", "specify ios testRunnerBundleID")
xctestCmd.Flags().StringVarP(&xctestConfig, "xctestConfig", "x", "", "specify ios xctestConfig")
iosRootCmd.AddCommand(xctestCmd)
}

View File

@@ -5,6 +5,7 @@ import (
"bytes"
"context"
"crypto/hmac"
"crypto/md5"
"crypto/sha256"
"encoding/binary"
"encoding/csv"
@@ -24,6 +25,7 @@ import (
"strings"
"time"
"github.com/BurntSushi/locker"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
"gopkg.in/yaml.v3"
@@ -492,6 +494,69 @@ func DownloadFile(filePath string, fileUrl string) error {
return nil
}
func fileExists(filepath string) bool {
_, err := os.Stat(filepath)
if os.IsNotExist(err) {
return false // 文件不存在
}
return err == nil // 文件存在,且没有其他错误
}
func DownloadFileByUrl(fileUrl string) (filePath string, err error) {
// 使用 UUID 生成唯一文件名
cwd, err := os.Getwd()
if err != nil {
return "", err
}
hash := md5.Sum([]byte(fileUrl))
fileName := fmt.Sprintf("%x", hash)
filePath = filepath.Join(cwd, fileName)
locker.Lock(filePath)
defer locker.Unlock(filePath)
if fileExists(filePath) {
return filePath, nil
}
fmt.Printf("Downloading file to %s from URL %s\n", filePath, fileUrl)
// Create an HTTP client with default settings.
client := &http.Client{}
// Build the HTTP GET request.
req, err := http.NewRequest("GET", fileUrl, nil)
if err != nil {
return "", err
}
// Perform the request.
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
// Check the HTTP status code.
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("failed to download file: %s", resp.Status)
}
// Create the output file.
outFile, err := os.Create(fileName)
if err != nil {
return "", err
}
defer outFile.Close()
// Copy the response body to the file.
_, err = io.Copy(outFile, resp.Body)
if err != nil {
return "", err
}
fmt.Printf("File downloaded successfully: %s\n", fileName)
return filePath, nil
}
func RunCommand(cmdName string, args ...string) error {
cmd := exec.Command(cmdName, args...)
log.Info().Str("command", cmd.String()).Msg("exec command")

View File

@@ -1 +1 @@
v5.0.0+2411211916
v5.0.0+2411231506

View File

@@ -1,446 +0,0 @@
# gidevice
This module is initially forked from [electricbubble/gidevice@v0.6.2].
#### Devices
```go
package main
import (
"log"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice"
)
func main() {
usbmux, err := gidevice.NewUsbmux()
if err != nil {
log.Fatalln(err)
}
devices, err := usbmux.Devices()
if err != nil {
log.Fatal(err)
}
for _, dev := range devices {
log.Println(dev.Properties().SerialNumber, dev.Properties().ProductID, dev.Properties().DeviceID)
}
}
```
### GetValue
```go
package main
import (
"encoding/json"
"fmt"
"log"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice"
)
type DeviceDetail struct {
DeviceName string `json:"DeviceName,omitempty"`
DeviceColor string `json:"DeviceColor,omitempty"`
DeviceClass string `json:"DeviceClass,omitempty"`
ProductVersion string `json:"ProductVersion,omitempty"`
ProductType string `json:"ProductType,omitempty"`
ProductName string `json:"ProductName,omitempty"`
ModelNumber string `json:"ModelNumber,omitempty"`
SerialNumber string `json:"SerialNumber,omitempty"`
SIMStatus string `json:"SIMStatus,omitempty"`
PhoneNumber string `json:"PhoneNumber,omitempty"`
CPUArchitecture string `json:"CPUArchitecture,omitempty"`
ProtocolVersion string `json:"ProtocolVersion,omitempty"`
RegionInfo string `json:"RegionInfo,omitempty"`
TelephonyCapability bool `json:"TelephonyCapability,omitempty"`
TimeZone string `json:"TimeZone,omitempty"`
UniqueDeviceID string `json:"UniqueDeviceID,omitempty"`
WiFiAddress string `json:"WiFiAddress,omitempty"`
WirelessBoardSerialNumber string `json:"WirelessBoardSerialNumber,omitempty"`
BluetoothAddress string `json:"BluetoothAddress,omitempty"`
BuildVersion string `json:"BuildVersion,omitempty"`
}
func main() {
usbmux, err := gidevice.NewUsbmux()
if err != nil {
log.Fatal(err)
}
devices, err := usbmux.Devices()
if err != nil {
log.Fatal(err)
}
if len(devices) == 0 {
log.Fatal("No Device")
}
d := devices[0]
detail, err1 := d.GetValue("", "")
if err1 != nil {
fmt.Errorf("get %s device detail fail : %w", d.Properties().SerialNumber, err1)
}
data, _ := json.Marshal(detail)
d1 := &DeviceDetail{}
json.Unmarshal(data, d1)
fmt.Println(d1)
}
```
#### DeveloperDiskImage
```go
package main
import (
"encoding/base64"
"log"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice"
)
func main() {
usbmux, err := gidevice.NewUsbmux()
if err != nil {
log.Fatal(err)
}
devices, err := usbmux.Devices()
if err != nil {
log.Fatal(err)
}
if len(devices) == 0 {
log.Fatal("No Device")
}
d := devices[0]
imageSignatures, err := d.Images()
if err != nil {
log.Fatalln(err)
}
for i, imgSign := range imageSignatures {
log.Printf("[%d] %s\n", i+1, base64.StdEncoding.EncodeToString(imgSign))
}
dmgPath := "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/14.4/DeveloperDiskImage.dmg"
signaturePath := "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/14.4/DeveloperDiskImage.dmg.signature"
err = d.MountDeveloperDiskImage(dmgPath, signaturePath)
if err != nil {
log.Fatalln(err)
}
}
```
#### App
```go
package main
import (
"log"
"path/filepath"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice"
)
func main() {
usbmux, err := gidevice.NewUsbmux()
if err != nil {
log.Fatalln(err)
}
devices, err := usbmux.Devices()
if err != nil {
log.Fatalln(err)
}
if len(devices) == 0 {
log.Fatalln("No Device")
}
d := devices[0]
bundleID := "com.apple.Preferences"
pid, err := d.AppLaunch(bundleID)
if err != nil {
log.Fatalln(err)
}
err = d.AppKill(pid)
if err != nil {
log.Fatalln(err)
}
runningProcesses, err := d.AppRunningProcesses()
if err != nil {
log.Fatalln(err)
}
for _, process := range runningProcesses {
if process.IsApplication {
log.Printf("%4d\t%-24s\t%-36s\t%s\n", process.Pid, process.Name, filepath.Base(process.RealAppName), process.StartDate)
}
}
}
```
#### Screenshot
```go
package main
import (
"image"
"image/jpeg"
"image/png"
"log"
"os"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice"
)
func main() {
usbmux, err := gidevice.NewUsbmux()
if err != nil {
log.Fatalln(err)
}
devices, err := usbmux.Devices()
if err != nil {
log.Fatalln(err)
}
if len(devices) == 0 {
log.Fatalln("No Device")
}
d := devices[0]
raw, err := d.Screenshot()
if err != nil {
log.Fatalln(err)
}
img, format, err := image.Decode(raw)
if err != nil {
log.Fatalln(err)
}
userHomeDir, _ := os.UserHomeDir()
file, err := os.Create(userHomeDir + "/Desktop/s1." + format)
if err != nil {
log.Fatalln(err)
}
defer func() { _ = file.Close() }()
switch format {
case "png":
err = png.Encode(file, img)
case "jpeg":
err = jpeg.Encode(file, img, nil)
}
if err != nil {
log.Fatalln(err)
}
log.Println(file.Name())
}
```
#### SimulateLocation
```go
package main
import (
"log"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice"
)
func main() {
usbmux, err := gidevice.NewUsbmux()
if err != nil {
log.Fatalln(err)
}
devices, err := usbmux.Devices()
if err != nil {
log.Fatalln(err)
}
if len(devices) == 0 {
log.Fatalln("No Device")
}
d := devices[0]
// https://api.map.baidu.com/lbsapi/getpoint/index.html
if err = d.SimulateLocationUpdate(116.024067, 40.362639, gidevice.CoordinateSystemBD09); err != nil {
log.Fatalln(err)
}
// https://developer.amap.com/tools/picker
// https://lbs.qq.com/tool/getpoint/index.html
// if err = d.SimulateLocationUpdate(120.116979, 30.252876, gidevice.CoordinateSystemGCJ02); err != nil {
// log.Fatalln(err)
// }
// if err = d.SimulateLocationUpdate(121.499763, 31.239580,gidevice.CoordinateSystemWGS84); err != nil {
// if err = d.SimulateLocationUpdate(121.499763, 31.239580); err != nil {
// log.Fatalln(err)
// }
// err = d.SimulateLocationRecover()
// if err != nil {
// log.Fatalln(err)
// }
}
```
#### XCTest
```go
package main
import (
"fmt"
"log"
"os"
"os/signal"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice"
)
func main() {
usbmux, err := gidevice.NewUsbmux()
if err != nil {
log.Fatal(err)
}
devices, err := usbmux.Devices()
if err != nil {
log.Fatal(err)
}
if len(devices) == 0 {
log.Fatal("No Device")
}
d := devices[0]
out, cancel, err := d.XCTest("com.leixipaopao.WebDriverAgentRunner.xctrunner")
if err != nil {
log.Fatal(err)
}
done := make(chan os.Signal, 1)
signal.Notify(done, os.Interrupt)
go func() {
for s := range out {
fmt.Print(s)
}
}()
<-done
cancel()
fmt.Println()
log.Println("DONE")
}
```
#### Connect and Forward
```go
package main
import (
"fmt"
"io"
"log"
"net"
"os"
"os/signal"
"time"
"syscall"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice"
)
func main() {
usbmux, err := gidevice.NewUsbmux()
if err != nil {
log.Fatal(err)
}
devices, err := usbmux.Devices()
if err != nil {
log.Fatal(err)
}
if len(devices) == 0 {
log.Fatal("No Device")
}
d := devices[0]
localPort, remotePort := 8100, 8100
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", localPort))
go func(listener net.Listener) {
for {
var accept net.Conn
if accept, err = listener.Accept(); err != nil {
log.Println("accept:", err)
}
fmt.Println("accept", accept.RemoteAddr())
rInnerConn, err := d.NewConnect(remotePort)
if err != nil {
log.Println(err)
os.Exit(0)
}
rConn := rInnerConn.RawConn()
_ = rConn.SetDeadline(time.Time{})
go func(lConn net.Conn) {
go func(lConn, rConn net.Conn) {
if _, err := io.Copy(lConn, rConn); err != nil {
//do sth
}
}(lConn, rConn)
go func(lConn, rConn net.Conn) {
if _, err := io.Copy(rConn, lConn); err != nil {
//do sth
}
}(lConn, rConn)
}(accept)
}
}(listener)
done := make(chan os.Signal, syscall.SIGTERM)
signal.Notify(done, os.Interrupt, os.Kill)
<-done
}
```
[electricbubble/gidevice@v0.6.2]: https://github.com/electricbubble/gidevice/tree/v0.6.2

View File

@@ -1,552 +0,0 @@
package gidevice
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"path"
"strconv"
"time"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
)
var ErrAfcStatNotExist = errors.New("afc stat: no such file or directory")
var _ Afc = (*afc)(nil)
func newAfc(client *libimobiledevice.AfcClient) *afc {
return &afc{client: client}
}
type afc struct {
client *libimobiledevice.AfcClient
}
func (c *afc) DiskInfo() (info *AfcDiskInfo, err error) {
if err = c.client.Send(libimobiledevice.AfcOperationGetDeviceInfo, nil, nil); err != nil {
return nil, fmt.Errorf("afc send 'DiskInfo': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return nil, fmt.Errorf("afc receive 'DiskInfo': %w", err)
}
m := respMsg.Map()
info = &AfcDiskInfo{
Model: m["Model"],
}
if info.TotalBytes, err = strconv.ParseUint(m["FSTotalBytes"], 10, 64); err != nil {
return nil, fmt.Errorf("afc 'DiskInfo': %w", err)
}
if info.FreeBytes, err = strconv.ParseUint(m["FSFreeBytes"], 10, 64); err != nil {
return nil, fmt.Errorf("afc 'DiskInfo': %w", err)
}
if info.BlockSize, err = strconv.ParseUint(m["FSBlockSize"], 10, 64); err != nil {
return nil, fmt.Errorf("afc 'DiskInfo': %w", err)
}
return
}
func (c *afc) ReadDir(dirname string) (names []string, err error) {
if err = c.client.Send(libimobiledevice.AfcOperationReadDir, toCString(dirname), nil); err != nil {
return nil, fmt.Errorf("afc send 'ReadDir': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return nil, fmt.Errorf("afc receive 'ReadDir': %w", err)
}
if err = respMsg.Err(); err != nil {
return nil, fmt.Errorf("afc 'ReadDir': %w", err)
}
names = respMsg.Strings()
return
}
func (c *afc) Stat(filename string) (info *AfcFileInfo, err error) {
if err = c.client.Send(libimobiledevice.AfcOperationGetFileInfo, toCString(filename), nil); err != nil {
return nil, fmt.Errorf("afc send 'Stat': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return nil, fmt.Errorf("afc receive 'Stat': %w", err)
}
m := respMsg.Map()
if len(m) == 0 {
return nil, ErrAfcStatNotExist
}
info = &AfcFileInfo{
source: m,
name: path.Base(filename),
ifmt: m["st_ifmt"],
}
if info.creationTime, err = strconv.ParseUint(m["st_birthtime"], 10, 64); err != nil {
return nil, fmt.Errorf("afc 'Stat': %w", err)
}
if info.blocks, err = strconv.ParseUint(m["st_blocks"], 10, 64); err != nil {
return nil, fmt.Errorf("afc 'Stat': %w", err)
}
if info.modTime, err = strconv.ParseUint(m["st_mtime"], 10, 64); err != nil {
return nil, fmt.Errorf("afc 'Stat': %w", err)
}
if info.nlink, err = strconv.ParseUint(m["st_nlink"], 10, 64); err != nil {
return nil, fmt.Errorf("afc 'Stat': %w", err)
}
if info.size, err = strconv.ParseUint(m["st_size"], 10, 64); err != nil {
return nil, fmt.Errorf("afc 'Stat': %w", err)
}
return
}
func (c *afc) Open(filename string, mode AfcFileMode) (file *AfcFile, err error) {
buf := new(bytes.Buffer)
if err = binary.Write(buf, binary.LittleEndian, uint64(mode)); err != nil {
return nil, fmt.Errorf("afc send 'Open': %w", err)
}
buf.Write(toCString(filename))
if err = c.client.Send(libimobiledevice.AfcOperationFileOpen, buf.Bytes(), nil); err != nil {
return nil, fmt.Errorf("afc send 'Open': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return nil, fmt.Errorf("afc receive 'Open': %w", err)
}
if err = respMsg.Err(); err != nil {
return nil, fmt.Errorf("afc 'Open': %w", err)
}
if respMsg.Operation != libimobiledevice.AfcOperationFileOpenResult {
return nil, fmt.Errorf("afc operation mistake 'Open': '%d'", respMsg.Operation)
}
file = &AfcFile{
client: c.client,
fd: respMsg.Uint64(),
}
return
}
func (c *afc) Remove(filePath string) (err error) {
if err = c.client.Send(libimobiledevice.AfcOperationRemovePath, toCString(filePath), nil); err != nil {
return fmt.Errorf("afc send 'Remove': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return fmt.Errorf("afc receive 'Remove': %w", err)
}
if err = respMsg.Err(); err != nil {
return fmt.Errorf("afc 'Remove': %w", err)
}
return
}
func (c *afc) Rename(oldPath string, newPath string) (err error) {
if err = c.client.Send(libimobiledevice.AfcOperationRenamePath, toCString(oldPath, newPath), nil); err != nil {
return fmt.Errorf("afc send 'Rename': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return fmt.Errorf("afc receive 'Rename': %w", err)
}
if err = respMsg.Err(); err != nil {
return fmt.Errorf("afc 'Rename': %w", err)
}
return
}
func (c *afc) Mkdir(path string) (err error) {
if err = c.client.Send(libimobiledevice.AfcOperationMakeDir, toCString(path), nil); err != nil {
return fmt.Errorf("afc send 'Mkdir': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return fmt.Errorf("afc receive 'Mkdir': %w", err)
}
if err = respMsg.Err(); err != nil {
return fmt.Errorf("afc 'Mkdir': %w", err)
}
return
}
func (c *afc) Link(oldName string, newName string, linkType AfcLinkType) (err error) {
buf := new(bytes.Buffer)
_ = binary.Write(buf, binary.LittleEndian, uint64(linkType))
buf.Write(toCString(oldName, newName))
if err = c.client.Send(libimobiledevice.AfcOperationMakeLink, buf.Bytes(), nil); err != nil {
return fmt.Errorf("afc send 'Link': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return fmt.Errorf("afc receive 'Link': %w", err)
}
if err = respMsg.Err(); err != nil {
return fmt.Errorf("afc 'Link': %w", err)
}
return
}
func (c *afc) Truncate(filePath string, size int64) (err error) {
buf := new(bytes.Buffer)
_ = binary.Write(buf, binary.LittleEndian, uint64(size))
buf.Write(toCString(filePath))
if err = c.client.Send(libimobiledevice.AfcOperationTruncateFile, buf.Bytes(), nil); err != nil {
return fmt.Errorf("afc send 'Truncate': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return fmt.Errorf("afc receive 'Truncate': %w", err)
}
if err = respMsg.Err(); err != nil {
return fmt.Errorf("afc 'Truncate': %w", err)
}
return
}
func (c *afc) SetFileModTime(filePath string, modTime time.Time) (err error) {
buf := new(bytes.Buffer)
_ = binary.Write(buf, binary.LittleEndian, uint64(modTime.Unix()))
buf.Write(toCString(filePath))
if err = c.client.Send(libimobiledevice.AfcOperationSetFileModTime, buf.Bytes(), nil); err != nil {
return fmt.Errorf("afc send 'SetFileModTime': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return fmt.Errorf("afc receive 'SetFileModTime': %w", err)
}
if err = respMsg.Err(); err != nil {
return fmt.Errorf("afc 'SetFileModTime': %w", err)
}
return
}
func (c *afc) Hash(filePath string) ([]byte, error) {
var err error
if err = c.client.Send(libimobiledevice.AfcOperationGetFileHash, toCString(filePath), nil); err != nil {
return nil, fmt.Errorf("afc send 'Hash': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return nil, fmt.Errorf("afc receive 'Hash': %w", err)
}
if err = respMsg.Err(); err != nil {
return nil, fmt.Errorf("afc 'Hash': %w", err)
}
return respMsg.Payload, nil
}
func (c *afc) HashWithRange(filePath string, start, end uint64) ([]byte, error) {
buf := new(bytes.Buffer)
_ = binary.Write(buf, binary.LittleEndian, start)
_ = binary.Write(buf, binary.LittleEndian, end)
buf.Write(toCString(filePath))
var err error
if err = c.client.Send(libimobiledevice.AfcOperationGetFileHashRange, buf.Bytes(), nil); err != nil {
return nil, fmt.Errorf("afc send 'HashWithRange': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return nil, fmt.Errorf("afc receive 'HashWithRange': %w", err)
}
if err = respMsg.Err(); err != nil {
return nil, fmt.Errorf("afc 'HashWithRange': %w", err)
}
return respMsg.Payload, nil
}
// RemoveAll since iOS6+
func (c *afc) RemoveAll(path string) (err error) {
if err = c.client.Send(libimobiledevice.AfcOperationRemovePathAndContents, toCString(path), nil); err != nil {
return fmt.Errorf("afc send 'RemoveAll': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = c.client.Receive(); err != nil {
return fmt.Errorf("afc receive 'RemoveAll': %w", err)
}
if err = respMsg.Err(); err != nil {
return fmt.Errorf("afc 'RemoveAll': %w", err)
}
return
}
func (c *afc) WriteFile(filename string, data []byte, perm AfcFileMode) (err error) {
var file *AfcFile
if file, err = c.Open(filename, perm); err != nil {
return err
}
defer func() {
err = file.Close()
}()
if _, err = file.Write(data); err != nil {
return err
}
return
}
func toCString(s ...string) []byte {
buf := new(bytes.Buffer)
for _, v := range s {
buf.WriteString(v)
buf.WriteByte(0)
}
return buf.Bytes()
}
type AfcDiskInfo struct {
Model string
TotalBytes uint64
FreeBytes uint64
BlockSize uint64
}
type AfcFileInfo struct {
name string
creationTime uint64
blocks uint64
ifmt string
modTime uint64
nlink uint64
size uint64
source map[string]string
}
func (f *AfcFileInfo) Name() string {
return f.name
}
func (f *AfcFileInfo) Size() int64 {
return int64(f.size)
}
// func (f *AfcFileInfo) Mode() os.FileMode {
// return os.ModeType
// }
func (f *AfcFileInfo) ModTime() time.Time {
return time.Unix(0, int64(f.modTime))
}
func (f *AfcFileInfo) IsDir() bool {
return f.ifmt == "S_IFDIR"
}
// func (f *AfcFileInfo) Sys() interface{} {
// return f.source
// }
func (f *AfcFileInfo) CreationTime() time.Time {
return time.Unix(0, int64(f.creationTime))
}
// func (f *AfcFileInfo) Blocks() uint64 {
// return f.blocks
// }
// func (f *AfcFileInfo) Format() string {
// return f.ifmt
// }
// func (f *AfcFileInfo) Link() uint64 {
// return f.nlink
// }
// func (f *AfcFileInfo) PhysicalSize(info *AfcDiskInfo) int64 {
// return int64(f.blocks * (info.BlockSize / 8))
// }
type AfcFileMode uint32
const (
AfcFileModeRdOnly AfcFileMode = 0x00000001
AfcFileModeRw AfcFileMode = 0x00000002
AfcFileModeWrOnly AfcFileMode = 0x00000003
AfcFileModeWr AfcFileMode = 0x00000004
AfcFileModeAppend AfcFileMode = 0x00000005
AfcFileModeRdAppend AfcFileMode = 0x00000006
)
type AfcLockType int
const (
AfcLockTypeSharedLock AfcLockType = 1 | 4
AfcLockTypeExclusiveLock AfcLockType = 2 | 4
AfcLockTypeUnlock AfcLockType = 8 | 4
)
type AfcFile struct {
client *libimobiledevice.AfcClient
fd uint64
reader *bytes.Reader
}
func (f *AfcFile) op(o ...uint64) []byte {
buf := new(bytes.Buffer)
_ = binary.Write(buf, binary.LittleEndian, f.fd)
if len(o) == 0 {
return buf.Bytes()
}
for _, v := range o {
_ = binary.Write(buf, binary.LittleEndian, v)
}
return buf.Bytes()
}
func (f *AfcFile) Lock(lockType AfcLockType) (err error) {
if err = f.client.Send(libimobiledevice.AfcOperationFileRefLock, f.op(uint64(lockType)), nil); err != nil {
return fmt.Errorf("afc file send 'Lock': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = f.client.Receive(); err != nil {
return fmt.Errorf("afc file receive 'Lock': %w", err)
}
if err = respMsg.Err(); err != nil {
return fmt.Errorf("afc file 'Lock': %w", err)
}
return
}
func (f *AfcFile) Unlock() (err error) {
return f.Lock(AfcLockTypeUnlock)
}
func (f *AfcFile) Read(b []byte) (n int, err error) {
if err = f.client.Send(libimobiledevice.AfcOperationFileRead, f.op(uint64(len(b))), nil); err != nil {
return -1, fmt.Errorf("afc file send 'Read': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = f.client.Receive(); err != nil {
return -1, fmt.Errorf("afc file receive 'Read': %w", err)
}
if err = respMsg.Err(); err != nil {
return -1, fmt.Errorf("afc file 'Read': %w", err)
}
if respMsg.Payload == nil {
return 0, io.EOF
}
if f.reader == nil {
f.reader = bytes.NewReader(respMsg.Payload)
} else {
f.reader.Reset(respMsg.Payload)
}
return f.reader.Read(b)
}
func (f *AfcFile) Write(b []byte) (n int, err error) {
if err = f.client.Send(libimobiledevice.AfcOperationFileWrite, f.op(), b); err != nil {
return -1, fmt.Errorf("afc file send 'Write': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = f.client.Receive(); err != nil {
return -1, fmt.Errorf("afc file receive 'Write': %w", err)
}
if err = respMsg.Err(); err != nil {
return -1, fmt.Errorf("afc file 'Write': %w", err)
}
n = len(b)
return
}
func (f *AfcFile) Tell() (n uint64, err error) {
if err = f.client.Send(libimobiledevice.AfcOperationFileTell, f.op(), nil); err != nil {
return 0, fmt.Errorf("afc file 'Tell': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = f.client.Receive(); err != nil {
return 0, fmt.Errorf("afc file receive 'Tell': %w", err)
}
if err = respMsg.Err(); err != nil {
return 0, fmt.Errorf("afc file 'Tell': %w", err)
}
if respMsg.Operation != libimobiledevice.AfcOperationFileTellResult {
return 0, fmt.Errorf("afc operation mistake 'Tell': '%d'", respMsg.Operation)
}
n = respMsg.Uint64()
return
}
func (f *AfcFile) Seek(offset int64, whence int) (ret int64, err error) {
if err = f.client.Send(libimobiledevice.AfcOperationFileSeek, f.op(uint64(whence), uint64(offset)), nil); err != nil {
return -1, fmt.Errorf("afc file 'Seek': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = f.client.Receive(); err != nil {
return -1, fmt.Errorf("afc file receive 'Seek': %w", err)
}
if err = respMsg.Err(); err != nil {
return -1, fmt.Errorf("afc file 'Seek': %w", err)
}
var tell uint64
if tell, err = f.Tell(); err != nil {
return -1, err
}
ret = int64(tell)
return
}
func (f *AfcFile) Truncate(size int64) (err error) {
if err = f.client.Send(libimobiledevice.AfcOperationFileSetSize, f.op(uint64(size)), nil); err != nil {
return fmt.Errorf("afc file 'Truncate': %w", err)
}
var respMsg *libimobiledevice.AfcMessage
if respMsg, err = f.client.Receive(); err != nil {
return fmt.Errorf("afc file receive 'Truncate': %w", err)
}
if err = respMsg.Err(); err != nil {
return fmt.Errorf("afc file 'Truncate': %w", err)
}
return
}
func (f *AfcFile) Close() (err error) {
if err = f.client.Send(libimobiledevice.AfcOperationFileClose, f.op(), nil); err != nil {
return fmt.Errorf("afc file 'Close': %w", err)
}
if _, err = f.client.Receive(); err != nil {
return fmt.Errorf("afc file receive 'Close': %w", err)
}
return
}
type AfcLinkType int
const (
AfcLinkTypeHardLink AfcLinkType = 1
AfcLinkTypeSymLink AfcLinkType = 2
)

View File

@@ -1,102 +0,0 @@
//go:build localtest
package gidevice
import (
"fmt"
"io"
"log"
"os"
"testing"
)
var afcSrv Afc
func setupAfcSrv(t *testing.T) {
setupLockdownSrv(t)
var err error
if lockdownSrv, err = dev.lockdownService(); err != nil {
t.Fatal(err)
}
if afcSrv, err = lockdownSrv.AfcService(); err != nil {
t.Fatal(err)
}
}
func Test_afc_DiskInfo(t *testing.T) {
setupAfcSrv(t)
info, err := afcSrv.DiskInfo()
if err != nil {
t.Fatal(err)
}
log.Printf("%10s: %s\n", "Model", info.Model)
log.Printf("%10s: %d\n", "BlockSize", info.BlockSize/8)
log.Printf("%10s: %s\n", "FreeSpace", byteCountDecimal(int64(info.FreeBytes)))
log.Printf("%10s: %s\n", "UsedSpace", byteCountDecimal(int64(info.TotalBytes-info.FreeBytes)))
log.Printf("%10s: %s\n", "TotalSpace", byteCountDecimal(int64(info.TotalBytes)))
}
func byteCountDecimal(b int64) string {
const unit = 1000
if b < unit {
return fmt.Sprintf("%dB", b)
}
div, exp := int64(unit), 0
for n := b / unit; n >= unit; n /= unit {
div *= unit
exp++
}
return fmt.Sprintf("%.1f%cB", float64(b)/float64(div), "kMGTPE"[exp])
}
func Test_afc_ReadDir(t *testing.T) {
setupAfcSrv(t)
names, err := afcSrv.ReadDir("Downloads")
if err != nil {
t.Fatal(err)
}
for _, name := range names {
t.Log(name)
}
}
func Test_afc_Stat(t *testing.T) {
setupAfcSrv(t)
fileInfo, err := afcSrv.Stat("Downloads/downloads.28.sqlitedb")
if err != nil {
t.Fatal(err)
}
t.Log(fileInfo.Name())
t.Log(fileInfo.IsDir())
t.Log(fileInfo.CreationTime())
t.Log(fileInfo.ModTime())
t.Log(fileInfo.Size())
t.Log(byteCountDecimal(fileInfo.Size()))
}
func Test_afc_Open(t *testing.T) {
setupAfcSrv(t)
afcFile, err := afcSrv.Open("DCIM/105APPLE/IMG_5977.JPEG", AfcFileModeRdOnly)
if err != nil {
t.Fatal(err)
}
defer func() {
_ = afcFile.Close()
}()
userHomeDir, _ := os.UserHomeDir()
file, err := os.Create(userHomeDir + "/Desktop/tmp.jpeg")
if err != nil {
t.Fatal(err)
}
if _, err = io.Copy(file, afcFile); err != nil {
t.Fatal(err)
}
}

View File

@@ -1,169 +0,0 @@
package gidevice
import (
"fmt"
"io"
"os"
"path"
"path/filepath"
"strings"
"howett.net/plist"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
)
var _ CrashReportMover = (*crashReportMover)(nil)
func newCrashReportMover(client *libimobiledevice.CrashReportMoverClient) *crashReportMover {
return &crashReportMover{
client: client,
}
}
type crashReportMover struct {
client *libimobiledevice.CrashReportMoverClient
afc Afc
}
func (c *crashReportMover) readPing() (err error) {
var data []byte
if data, err = c.client.InnerConn().Read(4); err != nil {
return err
}
if string(data) != "ping" {
return fmt.Errorf("crashReportMover ping: %v", data)
}
return
}
func (c *crashReportMover) Move(hostDir string, opts ...CrashReportMoverOption) (err error) {
opt := defaultCrashReportMoverOption()
for _, fn := range opts {
fn(opt)
}
toExtract := make([]string, 0, 64)
fn := func(cwd string, info *AfcFileInfo) {
if info.IsDir() {
return
}
if cwd == "." {
cwd = ""
}
devFilename := path.Join(cwd, info.Name())
hostElem := strings.Split(devFilename, "/")
hostFilename := filepath.Join(hostDir, filepath.Join(hostElem...))
hostFilename = strings.TrimSuffix(hostFilename, ".synced")
if opt.extract && strings.HasSuffix(hostFilename, ".plist") {
toExtract = append(toExtract, hostFilename)
}
var afcFile *AfcFile
if afcFile, err = c.afc.Open(devFilename, AfcFileModeRdOnly); err != nil {
debugLog(fmt.Sprintf("crashReportMover open %s: %s", devFilename, err))
return
}
defer func() {
if err = afcFile.Close(); err != nil {
debugLog(fmt.Sprintf("crashReportMover device file close: %s", err))
}
}()
if err = os.MkdirAll(filepath.Dir(hostFilename), 0o755); err != nil {
debugLog(fmt.Sprintf("crashReportMover mkdir %s: %s", filepath.Dir(hostFilename), err))
return
}
var hostFile *os.File
if hostFile, err = os.Create(hostFilename); err != nil {
debugLog(fmt.Sprintf("crashReportMover create %s: %s", hostFilename, err))
return
}
defer func() {
if err = hostFile.Close(); err != nil {
debugLog(fmt.Sprintf("crashReportMover host file close: %s", err))
}
}()
if _, err = io.Copy(hostFile, afcFile); err != nil {
debugLog(fmt.Sprintf("crashReportMover copy %s", err))
return
}
opt.whenDone(devFilename)
if opt.keep {
return
}
if err = c.afc.Remove(devFilename); err != nil {
debugLog(fmt.Sprintf("crashReportMover remove %s: %s", devFilename, err))
return
}
}
if err = c.walkDir(".", fn); err != nil {
return err
}
if !opt.extract {
return nil
}
for _, name := range toExtract {
data, err := os.ReadFile(name)
if err != nil {
debugLog(fmt.Sprintf("crashReportMover extract read %s: %s", name, err))
continue
}
m := make(map[string]interface{})
if _, err = plist.Unmarshal(data, &m); err != nil {
debugLog(fmt.Sprintf("crashReportMover extract plist %s: %s", name, err))
continue
}
desc, ok := m["description"]
if !ok {
continue
}
hostExtCrash := strings.TrimSuffix(name, ".plist") + ".crash"
if err = os.WriteFile(hostExtCrash, []byte(fmt.Sprintf("%v", desc)), 0o755); err != nil {
debugLog(fmt.Sprintf("crashReportMover extract save %s: %s", name, err))
continue
}
}
return
}
func (c *crashReportMover) walkDir(dirname string, fn func(path string, info *AfcFileInfo)) (err error) {
var names []string
if names, err = c.afc.ReadDir(dirname); err != nil {
return err
}
cwd := dirname
for _, n := range names {
if n == "." || n == ".." {
continue
}
var info *AfcFileInfo
if info, err = c.afc.Stat(path.Join(cwd, n)); err != nil {
return err
}
if info.IsDir() {
if err = c.walkDir(path.Join(cwd, info.name), fn); err != nil {
return err
}
}
fn(cwd, info)
}
return
}

View File

@@ -1,43 +0,0 @@
//go:build localtest
package gidevice
import (
"fmt"
"os"
"testing"
)
var crashReportMoverSrv CrashReportMover
func setupCrashReportMoverSrv(t *testing.T) {
setupLockdownSrv(t)
var err error
if lockdownSrv, err = dev.lockdownService(); err != nil {
t.Fatal(err)
}
if crashReportMoverSrv, err = lockdownSrv.CrashReportMoverService(); err != nil {
t.Fatal(err)
}
}
func Test_crashReportMover_Move(t *testing.T) {
setupCrashReportMoverSrv(t)
SetDebug(true)
userHomeDir, _ := os.UserHomeDir()
// err := crashReportMoverSrv.Move(userHomeDir + "/Documents/temp/2021-04/out_gidevice")
// err := crashReportMoverSrv.Move(userHomeDir+"/Documents/temp/2021-04/out_gidevice",
err := crashReportMoverSrv.Move(userHomeDir+"/Documents/temp/2021-04/out_gidevice_extract",
WithKeepCrashReport(true),
WithExtractRawCrashReport(true),
WithWhenMoveIsDone(func(filename string) {
fmt.Println("Copy:", filename)
}),
)
if err != nil {
t.Fatal(err)
}
}

View File

@@ -1,959 +0,0 @@
package gidevice
import (
"bytes"
"context"
"errors"
"fmt"
"os"
"path"
"strings"
"time"
"github.com/rs/zerolog/log"
uuid "github.com/satori/go.uuid"
"howett.net/plist"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/ipa"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/nskeyedarchiver"
)
const LockdownPort = 62078
var _ Device = (*device)(nil)
func newDevice(client *libimobiledevice.UsbmuxClient, properties DeviceProperties) *device {
return &device{
umClient: client,
properties: &properties,
}
}
type device struct {
umClient *libimobiledevice.UsbmuxClient
lockdownClient *libimobiledevice.LockdownClient
properties *DeviceProperties
lockdown *lockdown
imageMounter ImageMounter
screenshot Screenshot
simulateLocation SimulateLocation
installationProxy InstallationProxy
instruments Instruments
afc Afc
houseArrest HouseArrest
syslogRelay SyslogRelay
diagnosticsRelay DiagnosticsRelay
springBoard SpringBoard
crashReportMover CrashReportMover
pcapd Pcapd
perfd []Perfd
}
func (d *device) Properties() DeviceProperties {
return *d.properties
}
func (d *device) NewConnect(port int, timeout ...time.Duration) (InnerConn, error) {
newClient, err := libimobiledevice.NewUsbmuxClient(timeout...)
if err != nil {
return nil, err
}
var pkt libimobiledevice.Packet
if pkt, err = newClient.NewPlistPacket(
newClient.NewConnectRequest(d.properties.DeviceID, port),
); err != nil {
newClient.Close()
return nil, err
}
if err = newClient.SendPacket(pkt); err != nil {
newClient.Close()
return nil, err
}
if _, err = newClient.ReceivePacket(); err != nil {
newClient.Close()
return nil, err
}
return newClient.InnerConn(), err
}
func (d *device) ReadPairRecord() (pairRecord *PairRecord, err error) {
var pkt libimobiledevice.Packet
if pkt, err = d.umClient.NewPlistPacket(
d.umClient.NewReadPairRecordRequest(d.properties.SerialNumber),
); err != nil {
return nil, err
}
if err = d.umClient.SendPacket(pkt); err != nil {
return nil, err
}
var respPkt libimobiledevice.Packet
if respPkt, err = d.umClient.ReceivePacket(); err != nil {
return nil, err
}
reply := struct {
Data []byte `plist:"PairRecordData"`
}{}
if err = respPkt.Unmarshal(&reply); err != nil {
return nil, err
}
var record PairRecord
if _, err = plist.Unmarshal(reply.Data, &record); err != nil {
return nil, err
}
pairRecord = &record
return
}
func (d *device) SavePairRecord(pairRecord *PairRecord) (err error) {
var data []byte
if data, err = plist.Marshal(pairRecord, plist.XMLFormat); err != nil {
return err
}
var pkt libimobiledevice.Packet
if pkt, err = d.umClient.NewPlistPacket(
d.umClient.NewSavePairRecordRequest(d.properties.SerialNumber, d.properties.DeviceID, data),
); err != nil {
return err
}
if err = d.umClient.SendPacket(pkt); err != nil {
return err
}
if _, err = d.umClient.ReceivePacket(); err != nil {
return err
}
return
}
func (d *device) DeletePairRecord() (err error) {
var pkt libimobiledevice.Packet
if pkt, err = d.umClient.NewPlistPacket(
d.umClient.NewDeletePairRecordRequest(d.properties.SerialNumber),
); err != nil {
return err
}
if err = d.umClient.SendPacket(pkt); err != nil {
return err
}
if _, err = d.umClient.ReceivePacket(); err != nil {
return err
}
return
}
func (d *device) lockdownService() (lockdown Lockdown, err error) {
// if d.lockdown != nil {
// return d.lockdown, nil
// }
var innerConn InnerConn
if innerConn, err = d.NewConnect(LockdownPort, 0); err != nil {
return nil, err
}
d.lockdownClient = libimobiledevice.NewLockdownClient(innerConn)
d.lockdown = newLockdown(d)
_, err = d.lockdown._getProductVersion()
lockdown = d.lockdown
return
}
func (d *device) QueryType() (LockdownType, error) {
if _, err := d.lockdownService(); err != nil {
return LockdownType{}, err
}
return d.lockdown.QueryType()
}
func (d *device) GetValue(domain, key string) (v interface{}, err error) {
if _, err = d.lockdownService(); err != nil {
return nil, err
}
if d.lockdown.pairRecord == nil {
if err = d.lockdown.handshake(); err != nil {
return nil, err
}
}
if err = d.lockdown.startSession(d.lockdown.pairRecord); err != nil {
return nil, err
}
if v, err = d.lockdown.GetValue(domain, key); err != nil {
return nil, err
}
err = d.lockdown.stopSession()
return
}
func (d *device) Pair() (pairRecord *PairRecord, err error) {
if _, err = d.lockdownService(); err != nil {
return nil, err
}
return d.lockdown.Pair()
}
func (d *device) imageMounterService() (imageMounter ImageMounter, err error) {
if d.imageMounter != nil {
return d.imageMounter, nil
}
if _, err = d.lockdownService(); err != nil {
return nil, err
}
if d.imageMounter, err = d.lockdown.ImageMounterService(); err != nil {
return nil, err
}
imageMounter = d.imageMounter
return
}
func (d *device) Images(imgType ...string) (imageSignatures [][]byte, err error) {
if _, err = d.imageMounterService(); err != nil {
return nil, err
}
if len(imgType) == 0 {
imgType = []string{"Developer"}
}
return d.imageMounter.Images(imgType[0])
}
func (d *device) MountDeveloperDiskImage(dmgPath string, signaturePath string) (err error) {
if _, err = d.imageMounterService(); err != nil {
return err
}
devImgPath := "/private/var/mobile/Media/PublicStaging/staging.dimage"
return d.imageMounter.UploadImageAndMount("Developer", devImgPath, dmgPath, signaturePath)
}
func (d *device) screenshotService() (screenshot Screenshot, err error) {
if d.screenshot != nil {
return d.screenshot, nil
}
if _, err = d.lockdownService(); err != nil {
return nil, err
}
if d.screenshot, err = d.lockdown.ScreenshotService(); err != nil {
return nil, err
}
screenshot = d.screenshot
return
}
func (d *device) Screenshot() (raw *bytes.Buffer, err error) {
if _, err = d.screenshotService(); err != nil {
return nil, err
}
return d.screenshot.Take()
}
func (d *device) simulateLocationService() (simulateLocation SimulateLocation, err error) {
if d.simulateLocation != nil {
return d.simulateLocation, nil
}
if _, err = d.lockdownService(); err != nil {
return nil, err
}
if d.simulateLocation, err = d.lockdown.SimulateLocationService(); err != nil {
return nil, err
}
simulateLocation = d.simulateLocation
return
}
func (d *device) SimulateLocationUpdate(longitude float64, latitude float64, coordinateSystem ...CoordinateSystem) (err error) {
if _, err = d.simulateLocationService(); err != nil {
return err
}
return d.simulateLocation.Update(longitude, latitude, coordinateSystem...)
}
func (d *device) SimulateLocationRecover() (err error) {
if _, err = d.simulateLocationService(); err != nil {
return err
}
return d.simulateLocation.Recover()
}
func (d *device) installationProxyService() (installationProxy InstallationProxy, err error) {
if d.installationProxy != nil {
return d.installationProxy, nil
}
if _, err = d.lockdownService(); err != nil {
return nil, err
}
if d.installationProxy, err = d.lockdown.InstallationProxyService(); err != nil {
return nil, err
}
installationProxy = d.installationProxy
return
}
func (d *device) InstallationProxyBrowse(opts ...InstallationProxyOption) (currentList []interface{}, err error) {
if _, err = d.installationProxyService(); err != nil {
return nil, err
}
return d.installationProxy.Browse(opts...)
}
func (d *device) InstallationProxyLookup(opts ...InstallationProxyOption) (lookupResult interface{}, err error) {
if _, err = d.installationProxyService(); err != nil {
return nil, err
}
return d.installationProxy.Lookup(opts...)
}
func (d *device) newInstrumentsService() (instruments Instruments, err error) {
// NOTICE: each instruments service should have individual connection, otherwise it will be blocked
if _, err = d.lockdownService(); err != nil {
return
}
return d.lockdown.InstrumentsService()
}
func (d *device) instrumentsService() (instruments Instruments, err error) {
if d.instruments != nil {
return d.instruments, nil
}
if d.instruments, err = d.newInstrumentsService(); err != nil {
return nil, err
}
instruments = d.instruments
return
}
func (d *device) AppLaunch(bundleID string, opts ...AppLaunchOption) (pid int, err error) {
if _, err = d.instrumentsService(); err != nil {
return 0, err
}
return d.instruments.AppLaunch(bundleID, opts...)
}
func (d *device) AppKill(pid int) (err error) {
if _, err = d.instrumentsService(); err != nil {
return err
}
return d.instruments.AppKill(pid)
}
func (d *device) AppRunningProcesses() (processes []Process, err error) {
if _, err = d.instrumentsService(); err != nil {
return nil, err
}
return d.instruments.AppRunningProcesses()
}
func (d *device) AppList(opts ...AppListOption) (apps []Application, err error) {
if _, err = d.instrumentsService(); err != nil {
return nil, err
}
return d.instruments.AppList(opts...)
}
func (d *device) DeviceInfo() (devInfo *DeviceInfo, err error) {
if _, err = d.instrumentsService(); err != nil {
return nil, err
}
return d.instruments.DeviceInfo()
}
func (d *device) testmanagerdService() (testmanagerd Testmanagerd, err error) {
if _, err = d.lockdownService(); err != nil {
return nil, err
}
if testmanagerd, err = d.lockdown.TestmanagerdService(); err != nil {
return nil, err
}
return
}
func (d *device) AfcService() (afc Afc, err error) {
if d.afc != nil {
return d.afc, nil
}
if _, err = d.lockdownService(); err != nil {
return nil, err
}
if d.afc, err = d.lockdown.AfcService(); err != nil {
return nil, err
}
afc = d.afc
return
}
func (d *device) AppInstall(ipaPath string) (err error) {
if _, err = d.AfcService(); err != nil {
return err
}
stagingPath := "PublicStaging"
if _, err = d.afc.Stat(stagingPath); err != nil {
if err != ErrAfcStatNotExist {
return err
}
if err = d.afc.Mkdir(stagingPath); err != nil {
return fmt.Errorf("app install: %w", err)
}
}
var info map[string]interface{}
if info, err = ipa.Info(ipaPath); err != nil {
return err
}
bundleID, ok := info["CFBundleIdentifier"]
if !ok {
return errors.New("can't find 'CFBundleIdentifier'")
}
installationPath := path.Join(stagingPath, fmt.Sprintf("%s.ipa", bundleID))
var data []byte
if data, err = os.ReadFile(ipaPath); err != nil {
return err
}
if err = d.afc.WriteFile(installationPath, data, AfcFileModeWr); err != nil {
return err
}
if _, err = d.installationProxyService(); err != nil {
return err
}
return d.installationProxy.Install(fmt.Sprintf("%s", bundleID), installationPath)
}
func (d *device) AppUninstall(bundleID string) (err error) {
if _, err = d.installationProxyService(); err != nil {
return err
}
return d.installationProxy.Uninstall(bundleID)
}
func (d *device) HouseArrestService() (houseArrest HouseArrest, err error) {
if d.houseArrest != nil {
return d.houseArrest, nil
}
if _, err = d.lockdownService(); err != nil {
return nil, err
}
if d.houseArrest, err = d.lockdown.HouseArrestService(); err != nil {
return nil, err
}
houseArrest = d.houseArrest
return
}
func (d *device) syslogRelayService() (syslogRelay SyslogRelay, err error) {
if d.syslogRelay != nil {
return d.syslogRelay, nil
}
if _, err = d.lockdownService(); err != nil {
return nil, err
}
if d.syslogRelay, err = d.lockdown.SyslogRelayService(); err != nil {
return nil, err
}
syslogRelay = d.syslogRelay
return
}
func (d *device) Syslog() (lines <-chan string, err error) {
if _, err = d.syslogRelayService(); err != nil {
return nil, err
}
return d.syslogRelay.Lines(), nil
}
func (d *device) SyslogStop() {
if d.syslogRelay == nil {
return
}
d.syslogRelay.Stop()
}
func (d *device) Reboot() (err error) {
if _, err = d.lockdownService(); err != nil {
return
}
if d.diagnosticsRelay, err = d.lockdown.DiagnosticsRelayService(); err != nil {
return
}
if err = d.diagnosticsRelay.Reboot(); err != nil {
return
}
return
}
func (d *device) Shutdown() (err error) {
if _, err = d.lockdownService(); err != nil {
return
}
if d.diagnosticsRelay, err = d.lockdown.DiagnosticsRelayService(); err != nil {
return
}
if err = d.diagnosticsRelay.Shutdown(); err != nil {
return
}
return
}
func (d *device) springBoardService() (springBoard SpringBoard, err error) {
if d.springBoard != nil {
return d.springBoard, nil
}
if _, err = d.lockdownService(); err != nil {
return nil, err
}
if d.springBoard, err = d.lockdown.SpringBoardService(); err != nil {
return nil, err
}
springBoard = d.springBoard
return
}
func (d *device) GetIconPNGData(bundleId string) (raw *bytes.Buffer, err error) {
if _, err = d.lockdownService(); err != nil {
return
}
if d.springBoard, err = d.lockdown.SpringBoardService(); err != nil {
return
}
if raw, err = d.springBoard.GetIconPNGData(bundleId); err != nil {
return
}
return
}
func (d *device) GetInterfaceOrientation() (orientation libimobiledevice.OrientationState, err error) {
if _, err = d.springBoardService(); err != nil {
return
}
if orientation, err = d.springBoard.GetInterfaceOrientation(); err != nil {
return
}
return
}
func (d *device) PcapStart(opts ...PcapOption) (lines <-chan []byte, err error) {
pcapOptions := &PcapOptions{}
for _, fn := range opts {
fn(pcapOptions)
}
log.Info().Interface("options", pcapOptions).Msg("pcap start")
// wait until get pid for bundle id
if pcapOptions.BundleID != "" {
instruments, err := d.newInstrumentsService()
if err != nil {
fmt.Printf("get pid by bundle id failed: %v\n", err)
os.Exit(1)
}
for {
pid, err := instruments.getPidByBundleID(pcapOptions.BundleID)
if err != nil {
time.Sleep(1 * time.Second)
continue
}
pcapOptions.Pid = pid
break
}
}
if d.pcapd == nil {
if _, err = d.lockdownService(); err != nil {
return nil, err
}
if d.pcapd, err = d.lockdown.PcapdService(
pcapOptions.Pid, pcapOptions.ProcName); err != nil {
return nil, err
}
}
return d.pcapd.Packet(), nil
}
func (d *device) PcapStop() {
if d.pcapd == nil {
return
}
d.pcapd.Stop()
}
func (d *device) PerfStart(opts ...PerfOption) (data <-chan []byte, err error) {
perfOptions := defaulPerfOption()
for _, fn := range opts {
fn(perfOptions)
}
// wait until get pid for bundle id
if perfOptions.BundleID != "" {
instruments, err := d.newInstrumentsService()
if err != nil {
fmt.Printf("get pid by bundle id failed: %v\n", err)
os.Exit(1)
}
for {
pid, err := instruments.getPidByBundleID(perfOptions.BundleID)
if err != nil {
time.Sleep(1 * time.Second)
continue
}
perfOptions.Pid = pid
break
}
}
// processAttributes must contain pid, or it can't get process info, reason unknown
if !containString(perfOptions.ProcessAttributes, "pid") {
perfOptions.ProcessAttributes = append(perfOptions.ProcessAttributes, "pid")
}
outCh := make(chan []byte, 100)
if perfOptions.SysCPU || perfOptions.SysMem || perfOptions.SysDisk ||
perfOptions.SysNetwork {
perfd, err := d.newPerfdSysmontap(perfOptions)
if err != nil {
return nil, err
}
data, err := perfd.Start()
if err != nil {
return nil, err
}
go func() {
for {
outCh <- (<-data)
}
}()
d.perfd = append(d.perfd, perfd)
}
if perfOptions.Network {
perfd, err := d.newPerfdNetworking(perfOptions)
if err != nil {
return nil, err
}
data, err := perfd.Start()
if err != nil {
return nil, err
}
go func() {
for {
outCh <- (<-data)
}
}()
d.perfd = append(d.perfd, perfd)
}
if perfOptions.FPS || perfOptions.gpu {
perfd, err := d.newPerfdGraphicsOpengl(perfOptions)
if err != nil {
return nil, err
}
data, err := perfd.Start()
if err != nil {
return nil, err
}
go func() {
for {
outCh <- (<-data)
}
}()
d.perfd = append(d.perfd, perfd)
}
return outCh, nil
}
func (d *device) PerfStop() {
if d.perfd == nil {
return
}
for _, p := range d.perfd {
p.Stop()
}
}
func (d *device) crashReportMoverService() (crashReportMover CrashReportMover, err error) {
if d.crashReportMover != nil {
return d.crashReportMover, nil
}
if _, err = d.lockdownService(); err != nil {
return nil, err
}
if d.crashReportMover, err = d.lockdown.CrashReportMoverService(); err != nil {
return nil, err
}
crashReportMover = d.crashReportMover
return
}
func (d *device) MoveCrashReport(hostDir string, opts ...CrashReportMoverOption) (err error) {
if _, err = d.crashReportMoverService(); err != nil {
return err
}
return d.crashReportMover.Move(hostDir, opts...)
}
func (d *device) XCTest(bundleID string, opts ...XCTestOption) (out <-chan string, cancel context.CancelFunc, err error) {
xcTestOpt := defaultXCTestOption()
for _, fn := range opts {
fn(xcTestOpt)
}
ctx, cancelFunc := context.WithCancel(context.TODO())
_out := make(chan string)
xcodeVersion := uint64(30)
var tmSrv1 Testmanagerd
if tmSrv1, err = d.testmanagerdService(); err != nil {
return _out, cancelFunc, err
}
var xcTestManager1 XCTestManagerDaemon
if xcTestManager1, err = tmSrv1.newXCTestManagerDaemon(); err != nil {
return _out, cancelFunc, err
}
var version []int
if version, err = d.lockdown._getProductVersion(); err != nil {
return _out, cancelFunc, err
}
if DeviceVersion(version...) >= DeviceVersion(11, 0, 0) {
if err = xcTestManager1.initiateControlSession(xcodeVersion); err != nil {
return _out, cancelFunc, err
}
}
var tmSrv2 Testmanagerd
if tmSrv2, err = d.testmanagerdService(); err != nil {
return _out, cancelFunc, err
}
var xcTestManager2 XCTestManagerDaemon
if xcTestManager2, err = tmSrv2.newXCTestManagerDaemon(); err != nil {
return _out, cancelFunc, err
}
xcTestManager2.registerCallback("_XCT_logDebugMessage:", func(m libimobiledevice.DTXMessageResult) {
// more information ( each operation )
// fmt.Println("###### xcTestManager2 ### -->", m)
if strings.Contains(fmt.Sprintf("%s", m), "Received test runner ready reply with error: (null)") {
// fmt.Println("###### xcTestManager2 ### -->", fmt.Sprintf("%v", m.Aux[0]))
time.Sleep(time.Second)
if err = xcTestManager2.startExecutingTestPlan(xcodeVersion); err != nil {
debugLog(fmt.Sprintf("startExecutingTestPlan %d: %s", xcodeVersion, err))
return
}
}
})
xcTestManager2.registerCallback("_Golang-iDevice_Unregistered", func(m libimobiledevice.DTXMessageResult) {
// more information
// _XCT_testRunnerReadyWithCapabilities:
// _XCT_didBeginExecutingTestPlan
// _XCT_didBeginInitializingForUITesting
// _XCT_testSuite:didStartAt:
// _XCT_testCase:method:willStartActivity:
// _XCT_testCase:method:didFinishActivity:
// _XCT_testCaseDidStartForTestClass:method:
// fmt.Println("###### xcTestManager2 ### _Unregistered -->", m)
})
sessionId := uuid.NewV4()
if err = xcTestManager2.initiateSession(xcodeVersion, nskeyedarchiver.NewNSUUID(sessionId.Bytes())); err != nil {
return _out, cancelFunc, err
}
if _, err = d.installationProxyService(); err != nil {
return _out, cancelFunc, err
}
var vResult interface{}
if vResult, err = d.installationProxy.Lookup(WithBundleIDs(bundleID)); err != nil {
return _out, cancelFunc, err
}
lookupResult := vResult.(map[string]interface{})
lookupResult = lookupResult[bundleID].(map[string]interface{})
appContainer := lookupResult["Container"].(string)
appPath := lookupResult["Path"].(string)
var pathXCTestCfg string
if pathXCTestCfg, err = d._uploadXCTestConfiguration(bundleID, sessionId, lookupResult); err != nil {
return _out, cancelFunc, err
}
if _, err = d.instrumentsService(); err != nil {
return _out, cancelFunc, err
}
if err = d.instruments.appProcess(bundleID); err != nil {
return _out, cancelFunc, err
}
pathXCTestConfiguration := appContainer + pathXCTestCfg
appEnv := map[string]interface{}{
"CA_ASSERT_MAIN_THREAD_TRANSACTIONS": "0",
"CA_DEBUG_TRANSACTIONS": "0",
"DYLD_FRAMEWORK_PATH": appPath + "/Frameworks:",
"DYLD_LIBRARY_PATH": appPath + "/Frameworks",
"NSUnbufferedIO": "YES",
"SQLITE_ENABLE_THREAD_ASSERTIONS": "1",
"WDA_PRODUCT_BUNDLE_IDENTIFIER": "",
"XCTestConfigurationFilePath": pathXCTestConfiguration, // Running tests with active test configuration:
// "XCTestBundlePath": fmt.Sprintf("%s/PlugIns/%s.xctest", appPath, name), // !!! ERROR
// "XCTestSessionIdentifier": sessionId.String(), // !!! ERROR
// "XCTestSessionIdentifier": "",
"XCODE_DBG_XPC_EXCLUSIONS": "com.apple.dt.xctestSymbolicator",
"MJPEG_SERVER_PORT": "",
"USE_PORT": "",
"LLVM_PROFILE_FILE": appContainer + "/tmp/%p.profraw",
}
if DeviceVersion(version...) >= DeviceVersion(11, 0, 0) {
appEnv["DYLD_INSERT_LIBRARIES"] = "/Developer/usr/lib/libMainThreadChecker.dylib"
appEnv["OS_ACTIVITY_DT_MODE"] = "YES"
}
appArgs := []interface{}{
"-NSTreatUnknownArgumentsAsOpen", "NO",
"-ApplePersistenceIgnoreState", "YES",
}
appOpt := map[string]interface{}{
"StartSuspendedKey": uint64(0),
}
if DeviceVersion(version...) >= DeviceVersion(12, 0, 0) {
appOpt["ActivateSuspended"] = uint64(1)
}
if len(xcTestOpt.appEnv) != 0 {
for k, v := range xcTestOpt.appEnv {
appEnv[k] = v
}
}
if len(xcTestOpt.appOpt) != 0 {
for k, v := range xcTestOpt.appEnv {
appOpt[k] = v
}
}
d.instruments.registerCallback("outputReceived:fromProcess:atTime:", func(m libimobiledevice.DTXMessageResult) {
// fmt.Println("###### instruments ### -->", m.Aux[0])
_out <- fmt.Sprintf("%s", m.Aux[0])
})
var pid int
if pid, err = d.instruments.AppLaunch(bundleID,
WithAppPath(appPath),
WithEnvironment(appEnv),
WithArguments(appArgs),
WithOptions(appOpt),
WithKillExisting(true),
); err != nil {
return _out, cancelFunc, err
}
// see https://github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/issues/31
// if err = d.instruments.startObserving(pid); err != nil {
// return _out, cancelFunc, err
// }
if DeviceVersion(version...) >= DeviceVersion(12, 0, 0) {
err = xcTestManager1.authorizeTestSession(pid)
} else if DeviceVersion(version...) <= DeviceVersion(9, 0, 0) {
err = xcTestManager1.initiateControlSessionForTestProcessID(pid)
} else {
err = xcTestManager1.initiateControlSessionForTestProcessIDProtocolVersion(pid, xcodeVersion)
}
if err != nil {
return _out, cancelFunc, err
}
go func() {
d.instruments.registerCallback("_Golang-iDevice_Over", func(_ libimobiledevice.DTXMessageResult) {
cancelFunc()
})
<-ctx.Done()
tmSrv1.close()
tmSrv2.close()
xcTestManager1.close()
xcTestManager2.close()
if _err := d.AppKill(pid); _err != nil {
debugLog(fmt.Sprintf("xctest kill: %d", pid))
}
// time.Sleep(time.Second)
close(_out)
}()
return _out, cancelFunc, err
}
func (d *device) _uploadXCTestConfiguration(bundleID string, sessionId uuid.UUID, lookupResult map[string]interface{}) (pathXCTestCfg string, err error) {
if _, err = d.HouseArrestService(); err != nil {
return "", err
}
var appAfc Afc
if appAfc, err = d.houseArrest.Container(bundleID); err != nil {
return "", err
}
appTmpFilenames, err := appAfc.ReadDir("/tmp")
if err != nil {
return "", err
}
for _, tName := range appTmpFilenames {
if strings.HasSuffix(tName, ".xctestconfiguration") {
if _err := appAfc.Remove(fmt.Sprintf("/tmp/%s", tName)); _err != nil {
debugLog(fmt.Sprintf("remove /tmp/%s: %s", tName, err))
continue
}
}
}
nameExec := lookupResult["CFBundleExecutable"].(string)
name := nameExec[:len(nameExec)-len("-Runner")]
appPath := lookupResult["Path"].(string)
pathXCTestCfg = fmt.Sprintf("/tmp/%s-%s.xctestconfiguration", name, strings.ToUpper(sessionId.String()))
var content []byte
if content, err = nskeyedarchiver.Marshal(
nskeyedarchiver.NewXCTestConfiguration(
nskeyedarchiver.NewNSUUID(sessionId.Bytes()),
nskeyedarchiver.NewNSURL(fmt.Sprintf("%s/PlugIns/%s.xctest", appPath, name)),
bundleID,
appPath,
),
); err != nil {
return "", err
}
if err = appAfc.WriteFile(pathXCTestCfg, content, AfcFileModeWr); err != nil {
return "", err
}
return
}

View File

@@ -1,176 +0,0 @@
//go:build localtest
package gidevice
import (
"fmt"
"os"
"os/signal"
"testing"
"time"
)
var dev Device
func setupDevice(t *testing.T) {
setupUsbmux(t)
devices, err := um.Devices()
if err != nil {
t.Fatal(err)
}
if len(devices) == 0 {
t.Fatal("No Device")
}
dev = devices[0]
}
func Test_device_ReadPairRecord(t *testing.T) {
setupDevice(t)
pairRecord, err := dev.ReadPairRecord()
if err != nil {
t.Fatal(err)
}
t.Log(pairRecord.HostID, pairRecord.SystemBUID, pairRecord.WiFiMACAddress)
}
func Test_device_NewConnect(t *testing.T) {
setupDevice(t)
if _, err := dev.NewConnect(LockdownPort); err != nil {
t.Fatal(err)
}
}
func Test_device_DeletePairRecord(t *testing.T) {
setupDevice(t)
if err := dev.DeletePairRecord(); err != nil {
t.Fatal(err)
}
}
func Test_device_SavePairRecord(t *testing.T) {
setupLockdownSrv(t)
pairRecord, err := lockdownSrv.Pair()
if err != nil {
t.Fatal(err)
}
err = dev.SavePairRecord(pairRecord)
if err != nil {
t.Fatal(err)
}
}
func Test_device_XCTest(t *testing.T) {
setupLockdownSrv(t)
bundleID = "com.leixipaopao.WebDriverAgentRunner.xctrunner"
out, cancel, err := dev.XCTest(bundleID)
// out, cancel, err := dev.XCTest(bundleID, WithXCTestEnv(map[string]interface{}{"USE_PORT": 8222, "MJPEG_SERVER_PORT": 8333}))
if err != nil {
t.Fatal(err)
}
done := make(chan os.Signal, 1)
signal.Notify(done, os.Interrupt)
go func() {
for s := range out {
fmt.Print(s)
}
done <- os.Interrupt
}()
for {
select {
case <-done:
cancel()
fmt.Println()
t.Log("DONE")
return
}
}
}
func Test_device_AppInstall(t *testing.T) {
setupLockdownSrv(t)
ipaPath := "/private/tmp/derivedDataPath/Build/Products/Release-iphoneos/WebDriverAgentRunner-Runner.ipa"
err := dev.AppInstall(ipaPath)
if err != nil {
t.Fatal(err)
}
}
func Test_device_AppUninstall(t *testing.T) {
setupLockdownSrv(t)
bundleID = "com.leixipaopao.WebDriverAgentRunner.xctrunner"
err := dev.AppUninstall(bundleID)
if err != nil {
t.Fatal(err)
}
}
func Test_device_Syslog(t *testing.T) {
setupLockdownSrv(t)
dev.SyslogStop()
lines, err := dev.Syslog()
if err != nil {
t.Fatal(err)
}
done := make(chan os.Signal, 1)
go func() {
for line := range lines {
fmt.Println(line)
}
done <- os.Interrupt
t.Log("DONE!!!")
}()
signal.Notify(done, os.Interrupt, os.Kill)
// <-done
time.Sleep(3 * time.Second)
dev.SyslogStop()
time.Sleep(200 * time.Millisecond)
}
func Test_device_Reboot(t *testing.T) {
setupDevice(t)
dev.Reboot()
}
func Test_device_Shutdown(t *testing.T) {
setupDevice(t)
dev.Shutdown()
}
func Test_device_InstallationProxyBrowse(t *testing.T) {
setupDevice(t)
list, err := dev.InstallationProxyBrowse(
WithApplicationType(ApplicationTypeUser),
WithReturnAttributes("CFBundleDisplayName", "CFBundleIdentifier", "SequenceNumber", "SequenceNumber"),
)
// list, err := dev.InstallationProxyBrowse()
if err != nil {
t.Fatal(err)
}
t.Log(len(list))
for _, l := range list {
t.Logf("%#v", l)
}
}

View File

@@ -1,39 +0,0 @@
package gidevice
import "github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
func newDiagnosticsRelay(client *libimobiledevice.DiagnosticsRelayClient) *diagnostics {
return &diagnostics{
client: client,
}
}
type diagnostics struct {
client *libimobiledevice.DiagnosticsRelayClient
}
func (d *diagnostics) Reboot() (err error) {
var pkt libimobiledevice.Packet
if pkt, err = d.client.NewXmlPacket(
d.client.NewBasicRequest("Restart"),
); err != nil {
return
}
if err = d.client.SendPacket(pkt); err != nil {
return err
}
return
}
func (d *diagnostics) Shutdown() (err error) {
var pkt libimobiledevice.Packet
if pkt, err = d.client.NewXmlPacket(
d.client.NewBasicRequest("Shutdown"),
); err != nil {
return
}
if err = d.client.SendPacket(pkt); err != nil {
return err
}
return
}

View File

@@ -1,57 +0,0 @@
package gidevice
import "github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
var _ HouseArrest = (*houseArrest)(nil)
func newHouseArrest(client *libimobiledevice.HouseArrestClient) *houseArrest {
return &houseArrest{
client: client,
}
}
type houseArrest struct {
client *libimobiledevice.HouseArrestClient
}
func (h *houseArrest) Documents(bundleID string) (afc Afc, err error) {
var pkt libimobiledevice.Packet
if pkt, err = h.client.NewXmlPacket(
h.client.NewDocumentsRequest(bundleID),
); err != nil {
return nil, err
}
if err = h.client.SendPacket(pkt); err != nil {
return nil, err
}
if _, err = h.client.ReceivePacket(); err != nil {
return nil, err
}
afcClient := libimobiledevice.NewAfcClient(h.client.InnerConn())
afc = newAfc(afcClient)
return
}
func (h *houseArrest) Container(bundleID string) (afc Afc, err error) {
var pkt libimobiledevice.Packet
if pkt, err = h.client.NewXmlPacket(
h.client.NewContainerRequest(bundleID),
); err != nil {
return nil, err
}
if err = h.client.SendPacket(pkt); err != nil {
return nil, err
}
if _, err = h.client.ReceivePacket(); err != nil {
return nil, err
}
afcClient := libimobiledevice.NewAfcClient(h.client.InnerConn())
afc = newAfc(afcClient)
return
}

View File

@@ -1,60 +0,0 @@
//go:build localtest
package gidevice
import (
"testing"
)
var houseArrestSrv HouseArrest
func setupHouseArrestSrv(t *testing.T) {
setupLockdownSrv(t)
var err error
if lockdownSrv, err = dev.lockdownService(); err != nil {
t.Fatal(err)
}
if houseArrestSrv, err = lockdownSrv.HouseArrestService(); err != nil {
t.Fatal(err)
}
}
func Test_houseArrest_Documents(t *testing.T) {
setupHouseArrestSrv(t)
bundleID = "com.apple.iMovie"
appAfc, err := houseArrestSrv.Documents(bundleID)
if err != nil {
t.Fatal(err)
}
names, err := appAfc.ReadDir("Documents")
if err != nil {
t.Fatal(err)
}
for _, name := range names {
t.Log(name)
}
}
func Test_houseArrest_Container(t *testing.T) {
setupHouseArrestSrv(t)
bundleID = "com.apple.iMovie"
appAfc, err := houseArrestSrv.Documents(bundleID)
if err != nil {
t.Fatal(err)
}
names, err := appAfc.ReadDir("Documents")
if err != nil {
t.Fatal(err)
}
for _, name := range names {
t.Log(name)
}
}

View File

@@ -1,481 +0,0 @@
package gidevice
import (
"bytes"
"context"
"fmt"
"time"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/nskeyedarchiver"
)
type Usbmux interface {
Devices() ([]Device, error)
ReadBUID() (string, error)
Listen(chan Device) (context.CancelFunc, error)
}
type Device interface {
Properties() DeviceProperties
NewConnect(port int, timeout ...time.Duration) (InnerConn, error)
ReadPairRecord() (pairRecord *PairRecord, err error)
SavePairRecord(pairRecord *PairRecord) (err error)
DeletePairRecord() (err error)
lockdownService() (lockdown Lockdown, err error)
QueryType() (LockdownType, error)
GetValue(domain, key string) (v interface{}, err error)
Pair() (pairRecord *PairRecord, err error)
imageMounterService() (imageMounter ImageMounter, err error)
Images(imgType ...string) (imageSignatures [][]byte, err error)
MountDeveloperDiskImage(dmgPath string, signaturePath string) (err error)
screenshotService() (lockdown Screenshot, err error)
Screenshot() (raw *bytes.Buffer, err error)
simulateLocationService() (simulateLocation SimulateLocation, err error)
SimulateLocationUpdate(longitude float64, latitude float64, coordinateSystem ...CoordinateSystem) (err error)
SimulateLocationRecover() (err error)
installationProxyService() (installationProxy InstallationProxy, err error)
InstallationProxyBrowse(opts ...InstallationProxyOption) (currentList []interface{}, err error)
InstallationProxyLookup(opts ...InstallationProxyOption) (lookupResult interface{}, err error)
instrumentsService() (instruments Instruments, err error)
AppLaunch(bundleID string, opts ...AppLaunchOption) (pid int, err error)
AppKill(pid int) (err error)
AppRunningProcesses() (processes []Process, err error)
AppList(opts ...AppListOption) (apps []Application, err error)
DeviceInfo() (devInfo *DeviceInfo, err error)
AfcService() (afc Afc, err error)
AppInstall(ipaPath string) (err error)
AppUninstall(bundleID string) (err error)
HouseArrestService() (houseArrest HouseArrest, err error)
syslogRelayService() (syslogRelay SyslogRelay, err error)
Syslog() (lines <-chan string, err error)
SyslogStop()
PcapStart(opts ...PcapOption) (packet <-chan []byte, err error)
PcapStop()
Reboot() error
Shutdown() error
crashReportMoverService() (crashReportMover CrashReportMover, err error)
MoveCrashReport(hostDir string, opts ...CrashReportMoverOption) (err error)
XCTest(bundleID string, opts ...XCTestOption) (out <-chan string, cancel context.CancelFunc, err error)
springBoardService() (springBoard SpringBoard, err error)
GetIconPNGData(bundleId string) (raw *bytes.Buffer, err error)
GetInterfaceOrientation() (orientation OrientationState, err error)
PerfStart(opts ...PerfOption) (data <-chan []byte, err error)
PerfStop()
}
type DeviceProperties = libimobiledevice.DeviceProperties
type OrientationState = libimobiledevice.OrientationState
type Lockdown interface {
QueryType() (LockdownType, error)
GetValue(domain, key string) (v interface{}, err error)
SetValue(domain, key string, value interface{}) (err error)
Pair() (pairRecord *PairRecord, err error)
EnterRecovery() (err error)
handshake() (err error)
startSession(pairRecord *PairRecord) (err error)
stopSession() (err error)
startService(service string, escrowBag []byte) (dynamicPort int, enableSSL bool, err error)
ImageMounterService() (imageMounter ImageMounter, err error)
ScreenshotService() (screenshot Screenshot, err error)
SimulateLocationService() (simulateLocation SimulateLocation, err error)
InstallationProxyService() (installationProxy InstallationProxy, err error)
InstrumentsService() (instruments Instruments, err error)
TestmanagerdService() (testmanagerd Testmanagerd, err error)
AfcService() (afc Afc, err error)
HouseArrestService() (houseArrest HouseArrest, err error)
SyslogRelayService() (syslogRelay SyslogRelay, err error)
DiagnosticsRelayService() (diagnostics DiagnosticsRelay, err error)
CrashReportMoverService() (crashReportMover CrashReportMover, err error)
SpringBoardService() (springBoard SpringBoard, err error)
}
type ImageMounter interface {
Images(imgType string) (imageSignatures [][]byte, err error)
UploadImage(imgType, dmgPath string, signatureData []byte) (err error)
Mount(imgType, devImgPath string, signatureData []byte) (err error)
UploadImageAndMount(imgType, devImgPath, dmgPath, signaturePath string) (err error)
}
type Screenshot interface {
exchange() (err error)
Take() (raw *bytes.Buffer, err error)
}
type SimulateLocation interface {
Update(longitude float64, latitude float64, coordinateSystem ...CoordinateSystem) (err error)
// Recover try to revert back
Recover() (err error)
}
type InstallationProxy interface {
Browse(opts ...InstallationProxyOption) (currentList []interface{}, err error)
Lookup(opts ...InstallationProxyOption) (lookupResult interface{}, err error)
Install(bundleID, packagePath string) (err error)
Uninstall(bundleID string) (err error)
}
type Instruments interface {
AppLaunch(bundleID string, opts ...AppLaunchOption) (pid int, err error)
AppKill(pid int) (err error)
AppRunningProcesses() (processes []Process, err error)
AppList(opts ...AppListOption) (apps []Application, err error)
DeviceInfo() (devInfo *DeviceInfo, err error)
getPidByBundleID(bundleID string) (pid int, err error)
appProcess(bundleID string) (err error)
startObserving(pid int) (err error)
notifyOfPublishedCapabilities() (err error)
requestChannel(channel string) (id uint32, err error)
call(channel, selector string, auxiliaries ...interface{}) (result *libimobiledevice.DTXMessageResult, err error)
// sysMonSetConfig(cfg ...interface{}) (err error)
// SysMonStart(cfg ...interface{}) (_ interface{}, err error)
registerCallback(obj string, cb func(m libimobiledevice.DTXMessageResult))
}
type Testmanagerd interface {
notifyOfPublishedCapabilities() (err error)
requestChannel(channel string) (id uint32, err error)
newXCTestManagerDaemon() (xcTestManager XCTestManagerDaemon, err error)
invoke(selector string, args *libimobiledevice.AuxBuffer, channel uint32, expectsReply bool) (*libimobiledevice.DTXMessageResult, error)
registerCallback(obj string, cb func(m libimobiledevice.DTXMessageResult))
close()
}
type Afc interface {
DiskInfo() (diskInfo *AfcDiskInfo, err error)
ReadDir(dirname string) (names []string, err error)
Stat(filename string) (info *AfcFileInfo, err error)
Open(filename string, mode AfcFileMode) (file *AfcFile, err error)
Remove(filePath string) (err error)
Rename(oldPath string, newPath string) (err error)
Mkdir(path string) (err error)
Link(oldName string, newName string, linkType AfcLinkType) (err error)
Truncate(filePath string, size int64) (err error)
SetFileModTime(filePath string, modTime time.Time) (err error)
// Hash sha1 algorithm
Hash(filePath string) ([]byte, error)
// HashWithRange sha1 algorithm with file range
HashWithRange(filePath string, start, end uint64) ([]byte, error)
RemoveAll(path string) (err error)
WriteFile(filename string, data []byte, perm AfcFileMode) (err error)
}
type HouseArrest interface {
Documents(bundleID string) (afc Afc, err error)
Container(bundleID string) (afc Afc, err error)
}
type XCTestManagerDaemon interface {
// initiateControlSession iOS 11+
initiateControlSession(XcodeVersion uint64) (err error)
startExecutingTestPlan(XcodeVersion uint64) (err error)
initiateSession(XcodeVersion uint64, nsUUID *nskeyedarchiver.NSUUID) (err error)
// authorizeTestSession iOS 12+
authorizeTestSession(pid int) (err error)
// initiateControlSessionForTestProcessID <= iOS 9
initiateControlSessionForTestProcessID(pid int) (err error)
// initiateControlSessionForTestProcessIDProtocolVersion iOS > 9 && iOS < 12
initiateControlSessionForTestProcessIDProtocolVersion(pid int, XcodeVersion uint64) (err error)
registerCallback(obj string, cb func(m libimobiledevice.DTXMessageResult))
close()
}
type SyslogRelay interface {
Lines() <-chan string
Stop()
}
type Pcapd interface {
Packet() <-chan []byte
Stop()
}
type Perfd interface {
Start() (data <-chan []byte, err error)
Stop()
}
type DiagnosticsRelay interface {
Reboot() error
Shutdown() error
}
type CrashReportMover interface {
Move(hostDir string, opts ...CrashReportMoverOption) (err error)
walkDir(dirname string, fn func(path string, info *AfcFileInfo)) (err error)
}
type SpringBoard interface {
GetIconPNGData(bundleId string) (raw *bytes.Buffer, err error)
GetInterfaceOrientation() (orientation OrientationState, err error)
}
type InnerConn = libimobiledevice.InnerConn
type LockdownType = libimobiledevice.LockdownType
type PairRecord = libimobiledevice.PairRecord
type CoordinateSystem = libimobiledevice.CoordinateSystem
const (
CoordinateSystemWGS84 = libimobiledevice.CoordinateSystemWGS84
CoordinateSystemBD09 = libimobiledevice.CoordinateSystemBD09
CoordinateSystemGCJ02 = libimobiledevice.CoordinateSystemGCJ02
)
type ApplicationType = libimobiledevice.ApplicationType
const (
ApplicationTypeSystem = libimobiledevice.ApplicationTypeSystem
ApplicationTypeUser = libimobiledevice.ApplicationTypeUser
ApplicationTypeInternal = libimobiledevice.ApplicationTypeInternal
ApplicationTypeAny = libimobiledevice.ApplicationTypeAny
)
type installationProxyOption = libimobiledevice.InstallationProxyOption
type InstallationProxyOption func(*installationProxyOption)
func WithApplicationType(appType ApplicationType) InstallationProxyOption {
return func(opt *installationProxyOption) {
opt.ApplicationType = appType
}
}
func WithReturnAttributes(attrs ...string) InstallationProxyOption {
return func(opt *installationProxyOption) {
if len(opt.ReturnAttributes) == 0 {
opt.ReturnAttributes = attrs
} else {
opt.ReturnAttributes = append(opt.ReturnAttributes, attrs...)
}
opt.ReturnAttributes = _removeDuplicate(opt.ReturnAttributes)
}
}
func WithBundleIDs(BundleIDs ...string) InstallationProxyOption {
return func(opt *installationProxyOption) {
if len(opt.BundleIDs) == 0 {
opt.BundleIDs = BundleIDs
} else {
opt.BundleIDs = append(opt.BundleIDs, BundleIDs...)
}
opt.BundleIDs = _removeDuplicate(opt.BundleIDs)
}
}
func WithMetaData(b bool) InstallationProxyOption {
return func(opt *installationProxyOption) {
opt.MetaData = b
}
}
type appLaunchOption struct {
appPath string
environment map[string]interface{}
arguments []interface{}
options map[string]interface{}
}
type AppLaunchOption func(option *appLaunchOption)
func WithAppPath(appPath string) AppLaunchOption {
return func(opt *appLaunchOption) {
opt.appPath = appPath
}
}
func WithEnvironment(environment map[string]interface{}) AppLaunchOption {
return func(opt *appLaunchOption) {
opt.environment = environment
}
}
func WithArguments(arguments []interface{}) AppLaunchOption {
return func(opt *appLaunchOption) {
opt.arguments = arguments
}
}
func WithOptions(options map[string]interface{}) AppLaunchOption {
return func(opt *appLaunchOption) {
for k, v := range options {
opt.options[k] = v
}
}
}
func WithKillExisting(b bool) AppLaunchOption {
return func(opt *appLaunchOption) {
v := uint64(0)
if b {
v = uint64(1)
}
opt.options["KillExisting"] = v
}
}
type appListOption struct {
appsMatching map[string]interface{}
updateToken string
}
type AppListOption func(option *appListOption)
func WithAppsMatching(appsMatching map[string]interface{}) AppListOption {
return func(opt *appListOption) {
opt.appsMatching = appsMatching
}
}
func WithUpdateToken(updateToken string) AppListOption {
return func(opt *appListOption) {
opt.updateToken = updateToken
}
}
type Process struct {
IsApplication bool `json:"isApplication"`
Name string `json:"name"`
Pid int `json:"pid"`
RealAppName string `json:"realAppName"`
StartDate time.Time `json:"startDate"`
}
type crashReportMoverOption struct {
whenDone func(filename string)
keep bool
extract bool
}
func defaultCrashReportMoverOption() *crashReportMoverOption {
return &crashReportMoverOption{
whenDone: func(filename string) {},
keep: false,
}
}
type CrashReportMoverOption func(opt *crashReportMoverOption)
func WithKeepCrashReport(b bool) CrashReportMoverOption {
return func(opt *crashReportMoverOption) {
opt.keep = b
}
}
func WithExtractRawCrashReport(b bool) CrashReportMoverOption {
return func(opt *crashReportMoverOption) {
opt.extract = b
}
}
func WithWhenMoveIsDone(whenDone func(filename string)) CrashReportMoverOption {
return func(opt *crashReportMoverOption) {
opt.whenDone = whenDone
}
}
type xcTestOption struct {
appEnv map[string]interface{}
appArgs []interface{}
appOpt map[string]interface{}
}
func defaultXCTestOption() *xcTestOption {
return &xcTestOption{
appEnv: make(map[string]interface{}),
appArgs: make([]interface{}, 0, 2),
appOpt: make(map[string]interface{}),
}
}
type XCTestOption func(opt *xcTestOption)
func WithXCTestEnv(env map[string]interface{}) XCTestOption {
return func(opt *xcTestOption) {
opt.appEnv = env
}
}
// func WithXCTestArgs(args []interface{}) XCTestOption {
// return func(opt *xcTestOption) {
// opt.appArgs = args
// }
// }
func WithXCTestOpt(appOpt map[string]interface{}) XCTestOption {
return func(opt *xcTestOption) {
opt.appOpt = appOpt
}
}
func _removeDuplicate(strSlice []string) []string {
existed := make(map[string]bool, len(strSlice))
noRepeat := make([]string, 0, len(strSlice))
for _, str := range strSlice {
if _, ok := existed[str]; ok {
continue
}
existed[str] = true
noRepeat = append(noRepeat, str)
}
return noRepeat
}
func DeviceVersion(version ...int) int {
if len(version) < 3 {
tmp := make([]int, 3)
copy(tmp, version)
version = tmp
}
maj, min, patch := version[0], version[1], version[2]
return ((maj & 0xFF) << 16) | ((min & 0xFF) << 8) | (patch & 0xFF)
}
var debugFlag = false
// SetDebug sets debug mode
func SetDebug(debug bool, libDebug ...bool) {
debugFlag = debug
if len(libDebug) >= 1 {
libimobiledevice.SetDebug(libDebug[0])
}
}
func debugLog(msg string) {
if !debugFlag {
return
}
fmt.Printf("[go-iDevice-debug] %s\n", msg)
}

View File

@@ -1,142 +0,0 @@
package gidevice
import (
"fmt"
"os"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
)
var _ ImageMounter = (*imageMounter)(nil)
func newImageMounter(client *libimobiledevice.ImageMounterClient) *imageMounter {
return &imageMounter{
client: client,
}
}
type imageMounter struct {
client *libimobiledevice.ImageMounterClient
}
func (m *imageMounter) Images(imgType string) (imageSignatures [][]byte, err error) {
var pkt libimobiledevice.Packet
if pkt, err = m.client.NewXmlPacket(
m.client.NewBasicRequest(libimobiledevice.CommandTypeLookupImage, imgType),
); err != nil {
return nil, err
}
if err = m.client.SendPacket(pkt); err != nil {
return nil, err
}
var respPkt libimobiledevice.Packet
if respPkt, err = m.client.ReceivePacket(); err != nil {
return nil, err
}
var reply libimobiledevice.ImageMounterLookupImageResponse
if err = respPkt.Unmarshal(&reply); err != nil {
return nil, err
}
imageSignatures = reply.ImageSignature
return
}
func (m *imageMounter) UploadImage(imgType, dmgPath string, signatureData []byte) (err error) {
var dmgFileInfo os.FileInfo
if dmgFileInfo, err = os.Stat(dmgPath); err != nil {
return err
}
var pkt libimobiledevice.Packet
if pkt, err = m.client.NewXmlPacket(
m.client.NewReceiveBytesRequest(imgType, uint32(dmgFileInfo.Size()), signatureData),
); err != nil {
return err
}
if err = m.client.SendPacket(pkt); err != nil {
return err
}
var respPkt libimobiledevice.Packet
if respPkt, err = m.client.ReceivePacket(); err != nil {
return err
}
var reply libimobiledevice.ImageMounterBasicResponse
if err = respPkt.Unmarshal(&reply); err != nil {
return err
}
if reply.Status != "ReceiveBytesAck" {
return fmt.Errorf("image mounter 'ReceiveBytes' status: %s", reply.Status)
}
var dmgData []byte
if dmgData, err = os.ReadFile(dmgPath); err != nil {
return err
}
if err = m.client.SendDmg(dmgData); err != nil {
return err
}
if respPkt, err = m.client.ReceivePacket(); err != nil {
return err
}
if err = respPkt.Unmarshal(&reply); err != nil {
return err
}
if reply.Status != "Complete" {
return fmt.Errorf("image mounter 'SendDmg' status: %s", reply.Status)
}
return
}
func (m *imageMounter) Mount(imgType, devImgPath string, signatureData []byte) (err error) {
var pkt libimobiledevice.Packet
if pkt, err = m.client.NewXmlPacket(
m.client.NewMountImageRequest(imgType, devImgPath, signatureData),
); err != nil {
return err
}
if err = m.client.SendPacket(pkt); err != nil {
return err
}
var respPkt libimobiledevice.Packet
if respPkt, err = m.client.ReceivePacket(); err != nil {
return err
}
var reply libimobiledevice.ImageMounterBasicResponse
if err = respPkt.Unmarshal(&reply); err != nil {
return err
}
if reply.Status != "Complete" {
return fmt.Errorf("image mounter 'MountImage' status: %s", reply.Status)
}
return
}
func (m *imageMounter) UploadImageAndMount(imgType, devImgPath, dmgPath, signaturePath string) (err error) {
var signatureData []byte
if signatureData, err = os.ReadFile(signaturePath); err != nil {
return err
}
if err = m.UploadImage(imgType, dmgPath, signatureData); err != nil {
return err
}
if err = m.Mount(imgType, devImgPath, signatureData); err != nil {
return err
}
return
}

View File

@@ -1,51 +0,0 @@
//go:build localtest
package gidevice
import (
"encoding/base64"
"testing"
)
var imageMounterSrv ImageMounter
func setupImageMounterSrv(t *testing.T) {
setupLockdownSrv(t)
var err error
if lockdownSrv, err = dev.lockdownService(); err != nil {
t.Fatal(err)
}
// Once
// dev.Images()
if imageMounterSrv, err = lockdownSrv.ImageMounterService(); err != nil {
t.Fatal(err)
}
}
func Test_imageMounter_Images(t *testing.T) {
setupImageMounterSrv(t)
// imageSignatures, err := dev.Images()
imageSignatures, err := imageMounterSrv.Images("Developer")
if err != nil {
t.Fatal(err)
}
for i, imgSign := range imageSignatures {
t.Logf("%2d, %s", i+1, base64.StdEncoding.EncodeToString(imgSign))
}
}
func Test_imageMounter_UploadImageAndMount(t *testing.T) {
setupImageMounterSrv(t)
devImgPath := "/private/var/mobile/Media/PublicStaging/staging.dimage"
dmgPath := "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/14.4/DeveloperDiskImage.dmg"
signaturePath := "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/14.4/DeveloperDiskImage.dmg.signature"
if err := imageMounterSrv.UploadImageAndMount("Developer", devImgPath, dmgPath, signaturePath); err != nil {
t.Fatal(err)
}
}

View File

@@ -1,167 +0,0 @@
package gidevice
import (
"fmt"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
)
var _ InstallationProxy = (*installationProxy)(nil)
func newInstallationProxy(client *libimobiledevice.InstallationProxyClient) *installationProxy {
return &installationProxy{
client: client,
}
}
type installationProxy struct {
client *libimobiledevice.InstallationProxyClient
}
func (p *installationProxy) Browse(opts ...InstallationProxyOption) (currentList []interface{}, err error) {
opt := new(installationProxyOption)
if len(opts) == 0 {
opt = nil
} else {
for _, optFunc := range opts {
optFunc(opt)
}
}
var pkt libimobiledevice.Packet
if pkt, err = p.client.NewXmlPacket(
p.client.NewBasicRequest(libimobiledevice.CommandTypeBrowse, opt),
); err != nil {
return nil, err
}
if err = p.client.SendPacket(pkt); err != nil {
return nil, err
}
var respPkt libimobiledevice.Packet
if respPkt, err = p.client.ReceivePacket(); err != nil {
return nil, err
}
var reply libimobiledevice.InstallationProxyBrowseResponse
if err = respPkt.Unmarshal(&reply); err != nil {
return nil, err
}
for reply.Status != "Complete" {
if respPkt, err = p.client.ReceivePacket(); err != nil {
return nil, err
}
if err = respPkt.Unmarshal(&reply); err != nil {
return nil, err
}
}
currentList = reply.CurrentList
return
}
func (p *installationProxy) Lookup(opts ...InstallationProxyOption) (lookupResult interface{}, err error) {
opt := new(installationProxyOption)
if len(opts) == 0 {
opt = nil
} else {
for _, optFunc := range opts {
optFunc(opt)
}
}
var pkt libimobiledevice.Packet
if pkt, err = p.client.NewXmlPacket(
p.client.NewBasicRequest(libimobiledevice.CommandTypeLookup, opt),
); err != nil {
return nil, err
}
if err = p.client.SendPacket(pkt); err != nil {
return nil, err
}
var respPkt libimobiledevice.Packet
if respPkt, err = p.client.ReceivePacket(); err != nil {
return nil, err
}
var reply libimobiledevice.InstallationProxyLookupResponse
if err = respPkt.Unmarshal(&reply); err != nil {
return nil, err
}
if reply.Status != "Complete" {
return nil, fmt.Errorf("installation proxy 'Lookup' status: %s", reply.Status)
}
lookupResult = reply.LookupResult
return
}
func (p *installationProxy) Install(bundleID, packagePath string) (err error) {
var pkt libimobiledevice.Packet
if pkt, err = p.client.NewXmlPacket(
p.client.NewInstallRequest(bundleID, packagePath),
); err != nil {
return err
}
if err = p.client.SendPacket(pkt); err != nil {
return err
}
var reply libimobiledevice.InstallationProxyInstallResponse
for len(reply.Error) == 0 {
var respPkt libimobiledevice.Packet
if respPkt, err = p.client.ReceivePacket(); err != nil {
return err
}
if err = respPkt.Unmarshal(&reply); err != nil {
return err
}
if reply.Status == "Complete" {
break
}
}
if len(reply.Error) != 0 {
return fmt.Errorf("installation proxy 'Install' status: %s (err: %s, desc: %s)", reply.Status, reply.Error, reply.ErrorDescription)
}
return
}
func (p *installationProxy) Uninstall(bundleID string) (err error) {
var pkt libimobiledevice.Packet
if pkt, err = p.client.NewXmlPacket(
p.client.NewUninstallRequest(bundleID),
); err != nil {
return err
}
if err = p.client.SendPacket(pkt); err != nil {
return err
}
var reply libimobiledevice.InstallationProxyInstallResponse
for len(reply.Error) == 0 {
var respPkt libimobiledevice.Packet
if respPkt, err = p.client.ReceivePacket(); err != nil {
return err
}
if err = respPkt.Unmarshal(&reply); err != nil {
return err
}
if reply.Status == "Complete" {
break
}
}
if len(reply.Error) != 0 {
return fmt.Errorf("installation proxy 'Uninstall' status: %s (err: %s, desc: %s)", reply.Status, reply.Error, reply.ErrorDescription)
}
return
}

View File

@@ -1,73 +0,0 @@
//go:build localtest
package gidevice
import (
"testing"
)
var installationProxySrv InstallationProxy
func setupInstallationProxySrv(t *testing.T) {
setupLockdownSrv(t)
var err error
if lockdownSrv, err = dev.lockdownService(); err != nil {
t.Fatal(err)
}
if installationProxySrv, err = lockdownSrv.InstallationProxyService(); err != nil {
t.Fatal(err)
}
}
func Test_installationProxy_Browse(t *testing.T) {
setupInstallationProxySrv(t)
// currentList, err := installationProxySrv.Browse(WithMetaData(true))
// currentList, err := installationProxySrv.Browse(WithReturnAttributes("CFBundleIdentifier", "SequenceNumber", "SequenceNumber"))
// currentList, err := installationProxySrv.Browse(WithApplicationType(ApplicationTypeSystem))
// currentList, err := installationProxySrv.Browse(WithApplicationType(ApplicationTypeSystem), WithReturnAttributes("ApplicationType", "ApplicationType"))
// currentList, err := dev.InstallationProxyBrowse()
currentList, err := installationProxySrv.Browse()
// currentList, err := installationProxySrv.Browse(WithBundleIDs("com.apple.MusicUIService"), WithBundleIDs("com.apple.Home.HomeControlService"))
if err != nil {
t.Fatal(err)
}
t.Log(len(currentList))
for _, cl := range currentList {
app, ok := cl.(map[string]interface{})
if ok {
t.Log(app)
} else {
t.Log(cl)
}
}
}
func Test_installationProxy_Lookup(t *testing.T) {
setupInstallationProxySrv(t)
// lookupResult, err := installationProxySrv.Lookup()
// lookupResult, err := dev.InstallationProxyLookup(
lookupResult, err := installationProxySrv.Lookup(
// WithApplicationType(ApplicationTypeUser),
// WithApplicationType(ApplicationTypeSystem),
// WithReturnAttributes("CFBundleDevelopmentRegion"),
// WithReturnAttributes("CFBundleDisplayName", "CFBundleIdentifier"),
// WithBundleIDs("com.apple.mobilephone"),
WithBundleIDs("com.leixipaopao.WebDriverAgentRunner.xctrunner"),
)
if err != nil {
t.Fatal(err)
}
ret := lookupResult.(map[string]interface{})
t.Log(len(ret))
for k, v := range ret {
t.Log(k, "-->", v)
}
}

View File

@@ -1,348 +0,0 @@
package gidevice
import (
"encoding/json"
"fmt"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
)
// instruments services
const (
instrumentsServiceDeviceInfo = "com.apple.instruments.server.services.deviceinfo"
instrumentsServiceProcessControl = "com.apple.instruments.server.services.processcontrol"
instrumentsServiceDeviceApplictionListing = "com.apple.instruments.server.services.device.applictionListing"
instrumentsServiceGraphicsOpengl = "com.apple.instruments.server.services.graphics.opengl" // 获取 GPU/FPS
instrumentsServiceSysmontap = "com.apple.instruments.server.services.sysmontap" // 获取 CPU/Mem/Disk/Network 性能数据
instrumentsServiceNetworking = "com.apple.instruments.server.services.networking" // 获取所有网络详情数据
instrumentsServiceMobileNotifications = "com.apple.instruments.server.services.mobilenotifications" // 监控应用状态
)
const (
instrumentsServiceXcodeNetworkStatistics = "com.apple.xcode.debug-gauge-data-providers.NetworkStatistics" // 获取单进程网络数据
instrumentsServiceXcodeEnergyStatistics = "com.apple.xcode.debug-gauge-data-providers.Energy" // 获取功耗数据
)
var _ Instruments = (*instruments)(nil)
func newInstruments(client *libimobiledevice.InstrumentsClient) *instruments {
return &instruments{
client: client,
}
}
type instruments struct {
client *libimobiledevice.InstrumentsClient
}
func (i *instruments) notifyOfPublishedCapabilities() (err error) {
_, err = i.client.NotifyOfPublishedCapabilities()
return
}
func (i *instruments) requestChannel(channel string) (id uint32, err error) {
return i.client.RequestChannel(channel)
}
func (i *instruments) call(channel, selector string, auxiliaries ...interface{}) (
result *libimobiledevice.DTXMessageResult, err error) {
chanID, err := i.requestChannel(channel)
if err != nil {
return nil, err
}
args := libimobiledevice.NewAuxBuffer()
for _, aux := range auxiliaries {
if err = args.AppendObject(aux); err != nil {
return nil, err
}
}
return i.client.Invoke(selector, args, chanID, true)
}
func (i *instruments) getPidByBundleID(bundleID string) (pid int, err error) {
apps, err := i.AppList()
if err != nil {
fmt.Printf("get app list error: %v\n", err)
return 0, err
}
mapper := make(map[string]interface{})
for _, app := range apps {
mapper[app.ExecutableName] = app.CFBundleIdentifier
}
processes, err := i.AppRunningProcesses()
if err != nil {
fmt.Printf("get running app processes error: %v\n", err)
return 0, err
}
for _, proc := range processes {
b, ok := mapper[proc.Name]
if ok && bundleID == b {
fmt.Printf("get pid %d by bundleId %s\n", proc.Pid, bundleID)
return proc.Pid, nil
}
}
fmt.Printf("can't find pid by bundleID: %s\n", bundleID)
return 0, fmt.Errorf("can't find pid by bundleID: %s", bundleID)
}
func (i *instruments) AppLaunch(bundleID string, opts ...AppLaunchOption) (pid int, err error) {
opt := new(appLaunchOption)
opt.appPath = ""
opt.options = map[string]interface{}{
"StartSuspendedKey": uint64(0),
"KillExisting": uint64(0),
}
if len(opts) != 0 {
for _, optFunc := range opts {
optFunc(opt)
}
}
var id uint32
if id, err = i.requestChannel(instrumentsServiceProcessControl); err != nil {
return 0, err
}
args := libimobiledevice.NewAuxBuffer()
if err = args.AppendObject(opt.appPath); err != nil {
return 0, err
}
if err = args.AppendObject(bundleID); err != nil {
return 0, err
}
if err = args.AppendObject(opt.environment); err != nil {
return 0, err
}
if err = args.AppendObject(opt.arguments); err != nil {
return 0, err
}
if err = args.AppendObject(opt.options); err != nil {
return 0, err
}
var result *libimobiledevice.DTXMessageResult
selector := "launchSuspendedProcessWithDevicePath:bundleIdentifier:environment:arguments:options:"
if result, err = i.client.Invoke(selector, args, id, true); err != nil {
return 0, err
}
if nsErr, ok := result.Obj.(libimobiledevice.NSError); ok {
return 0, fmt.Errorf("%s", nsErr.NSUserInfo.(map[string]interface{})["NSLocalizedDescription"])
}
return int(result.Obj.(uint64)), nil
}
func (i *instruments) appProcess(bundleID string) (err error) {
var id uint32
if id, err = i.requestChannel(instrumentsServiceProcessControl); err != nil {
return err
}
args := libimobiledevice.NewAuxBuffer()
if err = args.AppendObject(bundleID); err != nil {
return err
}
selector := "processIdentifierForBundleIdentifier:"
if _, err = i.client.Invoke(selector, args, id, true); err != nil {
return err
}
return
}
func (i *instruments) startObserving(pid int) (err error) {
var id uint32
if id, err = i.requestChannel(instrumentsServiceProcessControl); err != nil {
return err
}
args := libimobiledevice.NewAuxBuffer()
if err = args.AppendObject(pid); err != nil {
return err
}
var result *libimobiledevice.DTXMessageResult
selector := "startObservingPid:"
if result, err = i.client.Invoke(selector, args, id, true); err != nil {
return err
}
if nsErr, ok := result.Obj.(libimobiledevice.NSError); ok {
return fmt.Errorf("%s", nsErr.NSUserInfo.(map[string]interface{})["NSLocalizedDescription"])
}
return
}
func (i *instruments) AppKill(pid int) (err error) {
var id uint32
if id, err = i.requestChannel(instrumentsServiceProcessControl); err != nil {
return err
}
args := libimobiledevice.NewAuxBuffer()
if err = args.AppendObject(pid); err != nil {
return err
}
selector := "killPid:"
if _, err = i.client.Invoke(selector, args, id, false); err != nil {
return err
}
return
}
func (i *instruments) AppRunningProcesses() (processes []Process, err error) {
var id uint32
if id, err = i.requestChannel(instrumentsServiceDeviceInfo); err != nil {
return nil, err
}
selector := "runningProcesses"
var result *libimobiledevice.DTXMessageResult
if result, err = i.client.Invoke(selector, libimobiledevice.NewAuxBuffer(), id, true); err != nil {
return nil, err
}
objs := result.Obj.([]interface{})
processes = make([]Process, 0, len(objs))
for _, v := range objs {
m := v.(map[string]interface{})
var data []byte
if data, err = json.Marshal(m); err != nil {
debugLog(fmt.Sprintf("process marshal: %v\n%v\n", err, m))
err = nil
continue
}
var tp Process
if err = json.Unmarshal(data, &tp); err != nil {
debugLog(fmt.Sprintf("process unmarshal: %v\n%v\n", err, m))
err = nil
continue
}
processes = append(processes, tp)
}
return
}
func (i *instruments) AppList(opts ...AppListOption) (apps []Application, err error) {
opt := new(appListOption)
opt.updateToken = ""
opt.appsMatching = make(map[string]interface{})
if len(opts) != 0 {
for _, optFunc := range opts {
optFunc(opt)
}
}
var id uint32
if id, err = i.requestChannel(instrumentsServiceDeviceApplictionListing); err != nil {
return nil, err
}
args := libimobiledevice.NewAuxBuffer()
if err = args.AppendObject(opt.appsMatching); err != nil {
return nil, err
}
if err = args.AppendObject(opt.updateToken); err != nil {
return nil, err
}
selector := "installedApplicationsMatching:registerUpdateToken:"
var result *libimobiledevice.DTXMessageResult
if result, err = i.client.Invoke(selector, args, id, true); err != nil {
return nil, err
}
objs := result.Obj.([]interface{})
for _, v := range objs {
m := v.(map[string]interface{})
var data []byte
if data, err = json.Marshal(m); err != nil {
debugLog(fmt.Sprintf("application marshal: %v\n%v\n", err, m))
err = nil
continue
}
var app Application
if err = json.Unmarshal(data, &app); err != nil {
debugLog(fmt.Sprintf("application unmarshal: %v\n%v\n", err, m))
err = nil
continue
}
apps = append(apps, app)
}
return
}
func (i *instruments) DeviceInfo() (devInfo *DeviceInfo, err error) {
var id uint32
if id, err = i.requestChannel(instrumentsServiceDeviceInfo); err != nil {
return nil, err
}
selector := "systemInformation"
var result *libimobiledevice.DTXMessageResult
if result, err = i.client.Invoke(selector, libimobiledevice.NewAuxBuffer(), id, true); err != nil {
return nil, err
}
data, err := json.Marshal(result.Obj)
if err != nil {
return nil, err
}
devInfo = new(DeviceInfo)
err = json.Unmarshal(data, devInfo)
return
}
func (i *instruments) registerCallback(obj string, cb func(m libimobiledevice.DTXMessageResult)) {
i.client.RegisterCallback(obj, cb)
}
type Application struct {
AppExtensionUUIDs []string `json:"AppExtensionUUIDs,omitempty"`
BundlePath string `json:"BundlePath"`
CFBundleIdentifier string `json:"CFBundleIdentifier"`
ContainerBundleIdentifier string `json:"ContainerBundleIdentifier,omitempty"`
ContainerBundlePath string `json:"ContainerBundlePath,omitempty"`
DisplayName string `json:"DisplayName"`
ExecutableName string `json:"ExecutableName,omitempty"`
Placeholder bool `json:"Placeholder,omitempty"`
PluginIdentifier string `json:"PluginIdentifier,omitempty"`
PluginUUID string `json:"PluginUUID,omitempty"`
Restricted int `json:"Restricted"`
Type string `json:"Type"`
Version string `json:"Version"`
}
type DeviceInfo struct {
Description string `json:"_deviceDescription"`
DisplayName string `json:"_deviceDisplayName"`
Identifier string `json:"_deviceIdentifier"`
Version string `json:"_deviceVersion"`
ProductType string `json:"_productType"`
ProductVersion string `json:"_productVersion"`
XRDeviceClassName string `json:"_xrdeviceClassName"`
}

View File

@@ -1,99 +0,0 @@
//go:build localtest
package gidevice
import (
"testing"
)
var (
instrumentsSrv Instruments
bundleID = "com.apple.Preferences"
)
func setupInstrumentsSrv(t *testing.T) {
setupLockdownSrv(t)
var err error
if lockdownSrv, err = dev.lockdownService(); err != nil {
t.Fatal(err)
}
if instrumentsSrv, err = lockdownSrv.InstrumentsService(); err != nil {
t.Fatal(err)
}
}
func Test_instruments_AppLaunch(t *testing.T) {
setupInstrumentsSrv(t)
// bundleID = "com.leixipaopao.WebDriverAgentRunner.xctrunner"
// pid, err := dev.AppLaunch(bundleID)
pid, err := instrumentsSrv.AppLaunch(bundleID)
// pid, err := instrumentsSrv.AppLaunch(bundleID, WithKillExisting(true))
// pid, err := instrumentsSrv.AppLaunch(bundleID, WithKillExisting(true), WithArguments([]interface{}{"-AppleLanguages", "(Russian)"}))
if err != nil {
t.Fatal(err)
}
t.Log(pid)
}
func Test_instruments_AppKill(t *testing.T) {
setupInstrumentsSrv(t)
pid, err := instrumentsSrv.AppLaunch(bundleID)
if err != nil {
t.Fatal(err)
}
t.Log(pid)
// if err = dev.AppKill(pid); err != nil {
if err = instrumentsSrv.AppKill(pid); err != nil {
t.Fatal(err)
}
}
func Test_instruments_AppRunningProcesses(t *testing.T) {
setupInstrumentsSrv(t)
// processes, err := dev.AppRunningProcesses()
processes, err := instrumentsSrv.AppRunningProcesses()
if err != nil {
t.Fatal(err)
}
for _, p := range processes {
t.Log(p.IsApplication, "\t", p.Pid, "\t", p.Name, "\t", p.RealAppName, "\t", p.StartDate)
}
}
func Test_instruments_AppList(t *testing.T) {
setupInstrumentsSrv(t)
// apps, err := dev.AppList()
apps, err := instrumentsSrv.AppList()
if err != nil {
t.Fatal(err)
}
for _, app := range apps {
t.Logf("%v\t%v\t%v\t%v\t%v\n", app.Type, app.DisplayName, app.ExecutableName, app.AppExtensionUUIDs, app.BundlePath)
}
}
func Test_instruments_DeviceInfo(t *testing.T) {
setupInstrumentsSrv(t)
devInfo, err := instrumentsSrv.DeviceInfo()
if err != nil {
t.Fatal(err)
}
t.Log(devInfo.Description)
t.Log(devInfo.DisplayName)
t.Log(devInfo.Identifier)
t.Log(devInfo.Version)
t.Log(devInfo.ProductType)
t.Log(devInfo.ProductVersion)
t.Log(devInfo.XRDeviceClassName)
}

View File

@@ -1,684 +0,0 @@
package gidevice
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"math/big"
"strconv"
"strings"
"time"
uuid "github.com/satori/go.uuid"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
)
var _ Lockdown = (*lockdown)(nil)
func newLockdown(dev *device) *lockdown {
return &lockdown{
umClient: dev.umClient,
client: dev.lockdownClient,
dev: dev,
}
}
type lockdown struct {
umClient *libimobiledevice.UsbmuxClient
client *libimobiledevice.LockdownClient
sessionID string
dev *device
iOSVersion []int
pairRecord *PairRecord
}
func (c *lockdown) QueryType() (LockdownType, error) {
pkt, err := c.client.NewXmlPacket(
c.client.NewBasicRequest(libimobiledevice.RequestTypeQueryType),
)
if err != nil {
return LockdownType{}, err
}
if err = c.client.SendPacket(pkt); err != nil {
return LockdownType{}, err
}
var respPkt libimobiledevice.Packet
if respPkt, err = c.client.ReceivePacket(); err != nil {
return LockdownType{}, err
}
var reply libimobiledevice.LockdownTypeResponse
if err = respPkt.Unmarshal(&reply); err != nil {
return LockdownType{}, err
}
return LockdownType{Type: reply.Type}, nil
}
func (c *lockdown) GetValue(domain, key string) (v interface{}, err error) {
pkt, err := c.client.NewXmlPacket(
c.client.NewGetValueRequest(domain, key),
)
if err != nil {
return nil, err
}
if err = c.client.SendPacket(pkt); err != nil {
return nil, err
}
var respPkt libimobiledevice.Packet
if respPkt, err = c.client.ReceivePacket(); err != nil {
return nil, err
}
var reply libimobiledevice.LockdownValueResponse
if err = respPkt.Unmarshal(&reply); err != nil {
return nil, err
}
v = reply.Value
return
}
func (c *lockdown) SetValue(domain, key string, value interface{}) (err error) {
var pkt libimobiledevice.Packet
if pkt, err = c.client.NewXmlPacket(
c.client.NewSetValueRequest(domain, key, value),
); err != nil {
return err
}
if err = c.client.SendPacket(pkt); err != nil {
return err
}
var respPkt libimobiledevice.Packet
if respPkt, err = c.client.ReceivePacket(); err != nil {
return err
}
var reply libimobiledevice.LockdownValueResponse
if err = respPkt.Unmarshal(&reply); err != nil {
return err
}
if !reply.Value.(bool) {
return errors.New("lockdown SetValue: Failed")
}
return
}
func (c *lockdown) EnterRecovery() (err error) {
var pkt libimobiledevice.Packet
if pkt, err = c.client.NewXmlPacket(
c.client.NewEnterRecoveryRequest(),
); err != nil {
return err
}
if err = c.client.SendPacket(pkt); err != nil {
return err
}
if _, err = c.client.ReceivePacket(); err != nil {
return err
}
return
}
func (c *lockdown) handshake() (err error) {
var lockdownType LockdownType
if lockdownType, err = c.QueryType(); err != nil {
return err
}
if lockdownType.Type != "com.apple.mobile.lockdown" {
return fmt.Errorf("lockdown handshake 'QueryType': %s", lockdownType.Type)
}
// if (device->version < DEVICE_VERSION(7,0,0))
// for older devices, we need to validate pairing to receive trusted host status
if c.pairRecord, err = c.dev.ReadPairRecord(); err == nil {
return nil
}
if !strings.Contains(err.Error(), libimobiledevice.ReplyCodeBadDevice.String()) {
return err
}
if c.pairRecord, err = c.Pair(); err != nil {
return err
}
err = c.dev.SavePairRecord(c.pairRecord)
return
}
func (c *lockdown) Pair() (pairRecord *PairRecord, err error) {
var buid string
if buid, err = newUsbmux(c.umClient).ReadBUID(); err != nil {
return nil, err
}
var devPublicKeyPem []byte
var devWiFiAddr string
if lockdownValue, err := c.GetValue("", "DevicePublicKey"); err != nil {
return nil, err
} else {
devPublicKeyPem = lockdownValue.([]byte)
}
if lockdownValue, err := c.GetValue("", "WiFiAddress"); err != nil {
return nil, err
} else {
devWiFiAddr = lockdownValue.(string)
}
if pairRecord, err = generatePairRecord(devPublicKeyPem); err != nil {
return nil, err
}
pairRecord.SystemBUID = buid
pairRecord.HostID = strings.ToUpper(uuid.NewV4().String())
hostPrivateKey := pairRecord.HostPrivateKey
pairRecord.HostPrivateKey = nil
rootPrivateKey := pairRecord.RootPrivateKey
pairRecord.RootPrivateKey = nil
var pkt libimobiledevice.Packet
if pkt, err = c.client.NewXmlPacket(
c.client.NewPairRequest(pairRecord),
); err != nil {
return nil, err
}
if err = c.client.SendPacket(pkt); err != nil {
return nil, err
}
var respPkt libimobiledevice.Packet
if respPkt, err = c.client.ReceivePacket(); err != nil {
return nil, err
}
var reply libimobiledevice.LockdownPairResponse
if err = respPkt.Unmarshal(&reply); err != nil {
return nil, err
}
pairRecord.EscrowBag = reply.EscrowBag
pairRecord.WiFiMACAddress = devWiFiAddr
pairRecord.HostPrivateKey = hostPrivateKey
pairRecord.RootPrivateKey = rootPrivateKey
return
}
func (c *lockdown) startSession(pairRecord *PairRecord) (err error) {
// if we have a running session, stop current one first
if c.sessionID != "" {
if err = c.stopSession(); err != nil {
return err
}
}
var pkt libimobiledevice.Packet
if pkt, err = c.client.NewXmlPacket(
c.client.NewStartSessionRequest(pairRecord.SystemBUID, pairRecord.HostID),
); err != nil {
return err
}
if err = c.client.SendPacket(pkt); err != nil {
return err
}
var respPkt libimobiledevice.Packet
if respPkt, err = c.client.ReceivePacket(); err != nil {
return fmt.Errorf("lockdown start session: %w", err)
}
var reply libimobiledevice.LockdownStartSessionResponse
if err = respPkt.Unmarshal(&reply); err != nil {
return err
}
if reply.EnableSessionSSL {
if err = c.client.EnableSSL(c.iOSVersion, pairRecord); err != nil {
return err
}
}
c.sessionID = reply.SessionID
return
}
func (c *lockdown) stopSession() (err error) {
if c.sessionID == "" {
return nil
}
var pkt libimobiledevice.Packet
if pkt, err = c.client.NewXmlPacket(
c.client.NewStopSessionRequest(c.sessionID),
); err != nil {
return err
}
if err = c.client.SendPacket(pkt); err != nil {
return err
}
var respPkt libimobiledevice.Packet
if respPkt, err = c.client.ReceivePacket(); err != nil {
return fmt.Errorf("lockdown stop session: %w", err)
}
var reply libimobiledevice.LockdownBasicResponse
if err = respPkt.Unmarshal(&reply); err != nil {
return err
}
c.sessionID = ""
return
}
func (c *lockdown) startService(service string, escrowBag []byte) (dynamicPort int, enableSSL bool, err error) {
req := c.client.NewStartServiceRequest(service)
if escrowBag != nil {
req.EscrowBag = escrowBag
}
var pkt libimobiledevice.Packet
if pkt, err = c.client.NewXmlPacket(req); err != nil {
return 0, false, err
}
if err = c.client.SendPacket(pkt); err != nil {
return 0, false, err
}
respPkt, err := c.client.ReceivePacket()
if err != nil {
return 0, false, err
}
var reply libimobiledevice.LockdownStartServiceResponse
if err = respPkt.Unmarshal(&reply); err != nil {
return 0, false, err
}
if reply.Error != "" {
return 0, false, fmt.Errorf("lockdown start service: %s", reply.Error)
}
dynamicPort = reply.Port
enableSSL = reply.EnableServiceSSL
return
}
func (c *lockdown) ImageMounterService() (imageMounter ImageMounter, err error) {
var innerConn InnerConn
if innerConn, err = c._startService(libimobiledevice.ImageMounterServiceName, nil); err != nil {
return nil, err
}
imageMounterClient := libimobiledevice.NewImageMounterClient(innerConn)
imageMounter = newImageMounter(imageMounterClient)
return
}
func (c *lockdown) ScreenshotService() (screenshot Screenshot, err error) {
var innerConn InnerConn
if innerConn, err = c._startService(libimobiledevice.ScreenshotServiceName, nil); err != nil {
return nil, err
}
screenshotClient := libimobiledevice.NewScreenshotClient(innerConn)
screenshot = newScreenshot(screenshotClient)
return
}
func (c *lockdown) SimulateLocationService() (simulateLocation SimulateLocation, err error) {
var innerConn InnerConn
if innerConn, err = c._startService(libimobiledevice.SimulateLocationServiceName, nil); err != nil {
return nil, err
}
simulateLocationClient := libimobiledevice.NewSimulateLocationClient(innerConn)
simulateLocation = newSimulateLocation(simulateLocationClient)
return
}
func (c *lockdown) InstallationProxyService() (installationProxy InstallationProxy, err error) {
var innerConn InnerConn
if innerConn, err = c._startService(libimobiledevice.InstallationProxyServiceName, nil); err != nil {
return nil, err
}
installationProxyClient := libimobiledevice.NewInstallationProxyClient(innerConn)
installationProxy = newInstallationProxy(installationProxyClient)
return
}
func (c *lockdown) InstrumentsService() (instruments Instruments, err error) {
service := libimobiledevice.InstrumentsServiceName
if DeviceVersion(c.iOSVersion...) >= DeviceVersion(14, 0, 0) {
service = libimobiledevice.InstrumentsSecureProxyServiceName
}
var innerConn InnerConn
if innerConn, err = c._startService(service, nil); err != nil {
return nil, err
}
instrumentsClient := libimobiledevice.NewInstrumentsClient(innerConn)
instruments = newInstruments(instrumentsClient)
if service == libimobiledevice.InstrumentsServiceName {
_ = innerConn.DismissSSL()
}
if err = instruments.notifyOfPublishedCapabilities(); err != nil {
return nil, err
}
return
}
func (c *lockdown) TestmanagerdService() (testmanagerd Testmanagerd, err error) {
service := libimobiledevice.TestmanagerdServiceName
if DeviceVersion(c.iOSVersion...) >= DeviceVersion(14, 0, 0) {
service = libimobiledevice.TestmanagerdSecureServiceName
}
var innerConn InnerConn
if innerConn, err = c._startService(service, nil); err != nil {
return nil, err
}
testmanagerdClient := libimobiledevice.NewTestmanagerdClient(innerConn)
testmanagerd = newTestmanagerd(testmanagerdClient, c.iOSVersion)
if service == libimobiledevice.TestmanagerdServiceName {
_ = innerConn.DismissSSL()
}
if err = testmanagerd.notifyOfPublishedCapabilities(); err != nil {
return nil, err
}
return
}
func (c *lockdown) AfcService() (afc Afc, err error) {
var innerConn InnerConn
if innerConn, err = c._startService(libimobiledevice.AfcServiceName, nil); err != nil {
return nil, err
}
afcClient := libimobiledevice.NewAfcClient(innerConn)
afc = newAfc(afcClient)
return
}
func (c *lockdown) HouseArrestService() (houseArrest HouseArrest, err error) {
var innerConn InnerConn
if innerConn, err = c._startService(libimobiledevice.HouseArrestServiceName, nil); err != nil {
return nil, err
}
houseArrestClient := libimobiledevice.NewHouseArrestClient(innerConn)
houseArrest = newHouseArrest(houseArrestClient)
return
}
func (c *lockdown) SyslogRelayService() (syslogRelay SyslogRelay, err error) {
var innerConn InnerConn
if innerConn, err = c._startService(libimobiledevice.SyslogRelayServiceName, nil); err != nil {
return nil, err
}
syslogRelayClient := libimobiledevice.NewSyslogRelayClient(innerConn)
syslogRelay = newSyslogRelay(syslogRelayClient)
return
}
func (c *lockdown) PcapdService(targetPID int, targetProcName string) (pcapd Pcapd, err error) {
var innerConn InnerConn
if innerConn, err = c._startService(libimobiledevice.PcapdServiceName, nil); err != nil {
return nil, err
}
pcapdClient := libimobiledevice.NewPcapdClient(innerConn, targetPID, targetProcName)
return newPcapdClient(pcapdClient), nil
}
func (c *lockdown) DiagnosticsRelayService() (diagnostics DiagnosticsRelay, err error) {
var innerConn InnerConn
if innerConn, err = c._startService(libimobiledevice.DiagnosticsRelayServiceName, nil); err != nil {
return nil, err
}
diagnosticsRelayClient := libimobiledevice.NewDiagnosticsRelayClient(innerConn)
diagnostics = newDiagnosticsRelay(diagnosticsRelayClient)
return
}
func (c *lockdown) SpringBoardService() (springboard SpringBoard, err error) {
var innerConn InnerConn
if innerConn, err = c._startService(libimobiledevice.SpringBoardServiceName, nil); err != nil {
return nil, err
}
springBoardServiceClient := libimobiledevice.NewSpringBoardClient(innerConn)
springboard = newSpringBoard(springBoardServiceClient)
return
}
func (c *lockdown) CrashReportMoverService() (crashReportMover CrashReportMover, err error) {
var innerConn InnerConn
if innerConn, err = c._startService(libimobiledevice.CrashReportMoverServiceName, nil); err != nil {
return nil, err
}
mover := newCrashReportMover(libimobiledevice.NewCrashReportMoverClient(innerConn))
if err = mover.readPing(); err != nil {
return nil, err
}
if innerConn, err = c._startService(libimobiledevice.CrashReportCopyMobileServiceName, nil); err != nil {
return nil, err
}
mover.afc = newAfc(libimobiledevice.NewAfcClient(innerConn))
crashReportMover = mover
return
}
func (c *lockdown) _startService(serviceName string, escrowBag []byte) (innerConn InnerConn, err error) {
if err = c.handshake(); err != nil {
return nil, err
}
if err = c.startSession(c.pairRecord); err != nil {
return nil, err
}
dynamicPort, enableSSL, err := c.startService(serviceName, escrowBag)
if err != nil {
return nil, err
}
if err = c.stopSession(); err != nil {
return nil, err
}
if innerConn, err = c.dev.NewConnect(dynamicPort, 0); err != nil {
return nil, err
}
// clean deadline
innerConn.Timeout(0)
if enableSSL {
if err = innerConn.Handshake(c.iOSVersion, c.pairRecord); err != nil {
return nil, err
}
}
return
}
func (c *lockdown) _getProductVersion() (version []int, err error) {
if c.iOSVersion != nil {
return c.iOSVersion, nil
}
var devProductVersion []string
if lockdownValue, err := c.GetValue("", "ProductVersion"); err != nil {
return nil, err
} else {
devProductVersion = strings.Split(lockdownValue.(string), ".")
}
version = make([]int, len(devProductVersion))
for i, v := range devProductVersion {
version[i], _ = strconv.Atoi(v)
}
// if len(version) == 2 {
// version = append(version, 0)
// }
c.iOSVersion = version
return
}
func generatePairRecord(devPublicKeyPem []byte) (pairRecord *PairRecord, err error) {
block, _ := pem.Decode(devPublicKeyPem)
var deviceKey *rsa.PublicKey
if deviceKey, err = x509.ParsePKCS1PublicKey(block.Bytes); err != nil {
return nil, err
}
var rootKey, hostKey *rsa.PrivateKey
if rootKey, err = rsa.GenerateKey(rand.Reader, 2048); err != nil {
return nil, err
}
if hostKey, err = rsa.GenerateKey(rand.Reader, 2048); err != nil {
return nil, err
}
serialNumber := big.NewInt(0)
notBefore := time.Now()
notAfter := notBefore.Add(time.Hour * (24 * 365) * 10)
rootTemplate := x509.Certificate{
IsCA: true,
SerialNumber: serialNumber,
Version: 2,
SignatureAlgorithm: x509.SHA1WithRSA,
PublicKeyAlgorithm: x509.RSA,
NotBefore: notBefore,
NotAfter: notAfter,
BasicConstraintsValid: true,
}
var caCert, cert []byte
if caCert, err = x509.CreateCertificate(rand.Reader, &rootTemplate, &rootTemplate, rootKey.Public(), rootKey); err != nil {
return nil, err
}
hostTemplate := x509.Certificate{
SerialNumber: serialNumber,
Version: 2,
SignatureAlgorithm: x509.SHA1WithRSA,
PublicKeyAlgorithm: x509.RSA,
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
BasicConstraintsValid: true,
}
if cert, err = x509.CreateCertificate(rand.Reader, &hostTemplate, &rootTemplate, hostKey.Public(), rootKey); err != nil {
return nil, err
}
h := sha1.New()
if _, err = h.Write(rootKey.N.Bytes()); err != nil {
return nil, err
}
subjectKeyId := h.Sum(nil)
deviceTemplate := x509.Certificate{
SerialNumber: serialNumber,
Version: 2,
SignatureAlgorithm: x509.SHA1WithRSA,
PublicKeyAlgorithm: x509.RSA,
NotBefore: notBefore,
NotAfter: notAfter,
BasicConstraintsValid: true,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
SubjectKeyId: subjectKeyId,
}
var deviceCert []byte
if deviceCert, err = x509.CreateCertificate(rand.Reader, &deviceTemplate, &rootTemplate, deviceKey, rootKey); err != nil {
return nil, err
}
var deviceCertPEM []byte
if deviceCertPEM, err = encodePemCertificate(deviceCert); err != nil {
return nil, err
}
var caPEM, caPrivatePEM []byte
if caPEM, caPrivatePEM, err = encodePairPemFormat(caCert, rootKey); err != nil {
return nil, err
}
var certPEM, certPrivatePEM []byte
if certPEM, certPrivatePEM, err = encodePairPemFormat(cert, hostKey); err != nil {
return nil, err
}
pairRecord = new(PairRecord)
pairRecord.DeviceCertificate = deviceCertPEM
pairRecord.HostCertificate = certPEM
pairRecord.HostPrivateKey = certPrivatePEM
pairRecord.RootCertificate = caPEM
pairRecord.RootPrivateKey = caPrivatePEM
return
}
func encodePairPemFormat(cert []byte, key *rsa.PrivateKey) ([]byte, []byte, error) {
p, err := encodePemCertificate(cert)
if err != nil {
return nil, nil, err
}
buf := new(bytes.Buffer)
if err := pem.Encode(buf, &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key),
}); err != nil {
return nil, nil, err
}
privy := buf.Bytes()
return p, privy, nil
}
func encodePemCertificate(cert []byte) ([]byte, error) {
buf := new(bytes.Buffer)
if err := pem.Encode(buf, &pem.Block{
Type: "CERTIFICATE",
Bytes: cert,
}); err != nil {
return nil, err
}
return buf.Bytes(), nil
}

View File

@@ -1,108 +0,0 @@
//go:build localtest
package gidevice
import (
"fmt"
"os"
"os/signal"
"path"
"testing"
"time"
)
var lockdownSrv Lockdown
func setupLockdownSrv(t *testing.T) {
setupDevice(t)
var err error
if lockdownSrv, err = dev.lockdownService(); err != nil {
t.Fatal(err)
}
}
func Test_lockdown_QueryType(t *testing.T) {
setupLockdownSrv(t)
lockdownType, err := lockdownSrv.QueryType()
if err != nil {
t.Fatal(err)
}
t.Log(lockdownType.Type)
}
func Test_lockdown_GetValue(t *testing.T) {
setupLockdownSrv(t)
// v, err := dev.GetValue("com.apple.mobile.iTunes", "")
// v, err := dev.GetValue("com.apple.mobile.internal", "")
v, err := dev.GetValue("com.apple.mobile.battery", "")
// v, err := lockdownSrv.GetValue("", "ProductVersion")
// v, err := lockdownSrv.GetValue("", "DeviceName")
// v, err := lockdownSrv.GetValue("com.apple.mobile.iTunes", "")
// v, err := lockdownSrv.GetValue("com.apple.mobile.battery", "")
// v, err := lockdownSrv.GetValue("com.apple.disk_usage", "")
if err != nil {
t.Fatal(err)
}
t.Log(v)
}
func Test_lockdown_SyslogRelayService(t *testing.T) {
setupLockdownSrv(t)
syslogRelaySrv, err := lockdownSrv.SyslogRelayService()
if err != nil {
t.Fatal(err)
}
syslogRelaySrv.Stop()
lines := syslogRelaySrv.Lines()
done := make(chan os.Signal, 1)
go func() {
for line := range lines {
fmt.Println(line)
}
done <- os.Interrupt
fmt.Println("DONE!!!")
}()
signal.Notify(done, os.Interrupt, os.Kill)
<-done
syslogRelaySrv.Stop()
time.Sleep(time.Second)
}
func Test_lockdown_CrashReportMoverService(t *testing.T) {
setupLockdownSrv(t)
crashReportMoverSrv, err := lockdownSrv.CrashReportMoverService()
if err != nil {
t.Fatal(err)
}
filenames := make([]string, 0, 36)
fn := func(cwd string, info *AfcFileInfo) {
if cwd == "." {
cwd = ""
}
filenames = append(filenames, path.Join(cwd, info.Name()))
// fmt.Println(path.Join(cwd, name))
}
err = crashReportMoverSrv.walkDir(".", fn)
if err != nil {
t.Fatal(err)
}
for _, n := range filenames {
fmt.Println(n)
}
t.Log(len(filenames))
}

View File

@@ -1,97 +0,0 @@
package gidevice
import (
"log"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
)
type PcapOptions struct {
All bool // capture all packets
Pid int // capture packets from specific PID
ProcName string // capture packets from specific process name
BundleID string // convert to PID first, then capture packets from the PID
}
type PcapOption func(*PcapOptions)
func WithPcapAll(all bool) PcapOption {
return func(opt *PcapOptions) {
opt.All = all
}
}
func WithPcapProcName(procName string) PcapOption {
return func(opt *PcapOptions) {
opt.ProcName = procName
}
}
func WithPcapPID(pid int) PcapOption {
return func(opt *PcapOptions) {
opt.Pid = pid
}
}
func WithPcapBundleID(bundleID string) PcapOption {
return func(opt *PcapOptions) {
opt.BundleID = bundleID
}
}
type pcapdClient struct {
stop chan struct{}
c *libimobiledevice.PcapdClient
}
func newPcapdClient(c *libimobiledevice.PcapdClient) *pcapdClient {
return &pcapdClient{
stop: make(chan struct{}),
c: c,
}
}
func (c *pcapdClient) Packet() <-chan []byte {
packetCh := make(chan []byte, 10)
go func() {
for {
select {
case <-c.stop:
return
default:
pkt, err := c.c.ReceivePacket()
if err != nil {
close(packetCh)
return
}
var payload []byte
_ = pkt.Unmarshal(&payload)
raw, err := c.c.GetPacket(payload)
if err != nil {
close(packetCh)
return
}
if raw == nil {
// filtered packet
continue
}
res, err := c.c.CreatePacket(raw)
if err != nil {
log.Println("failed to create packet")
return
}
packetCh <- res
}
}
}()
return packetCh
}
func (c *pcapdClient) Stop() {
select {
case <-c.stop:
default:
close(c.stop)
}
c.c.Close()
}

View File

@@ -1,66 +0,0 @@
//go:build localtest
package gidevice
import (
"fmt"
"testing"
"time"
)
func TestPcapWithPID(t *testing.T) {
setupLockdownSrv(t)
data, err := dev.PcapStart(WithPcapPID(1234))
if err != nil {
t.Fatal(err)
}
timer := time.NewTimer(time.Duration(time.Second * 10))
for {
select {
case <-timer.C:
dev.PcapStop()
return
case d := <-data:
fmt.Println(string(d))
}
}
}
func TestPcapWithProcName(t *testing.T) {
setupLockdownSrv(t)
data, err := dev.PcapStart(WithPcapProcName("Awe"))
if err != nil {
t.Fatal(err)
}
timer := time.NewTimer(time.Duration(time.Second * 10))
for {
select {
case <-timer.C:
dev.PcapStop()
return
case d := <-data:
fmt.Println(string(d))
}
}
}
func TestPcapWithBundleID(t *testing.T) {
setupLockdownSrv(t)
data, err := dev.PcapStart(WithPcapBundleID("com.ss.iphone.ugc.Aweme"))
if err != nil {
t.Fatal(err)
}
timer := time.NewTimer(time.Duration(time.Second * 10))
for {
select {
case <-timer.C:
dev.PcapStop()
return
case d := <-data:
fmt.Println(string(d))
}
}
}

View File

@@ -1,893 +0,0 @@
package gidevice
import (
"context"
"encoding/binary"
"encoding/json"
"fmt"
"log"
"net"
"strconv"
"time"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
)
type PerfOptions struct {
// system
SysCPU bool `json:"sys_cpu,omitempty" yaml:"sys_cpu,omitempty"`
SysMem bool `json:"sys_mem,omitempty" yaml:"sys_mem,omitempty"`
SysDisk bool `json:"sys_disk,omitempty" yaml:"sys_disk,omitempty"`
SysNetwork bool `json:"sys_network,omitempty" yaml:"sys_network,omitempty"`
gpu bool
FPS bool `json:"fps,omitempty" yaml:"fps,omitempty"`
Network bool `json:"network,omitempty" yaml:"network,omitempty"`
// process
BundleID string `json:"bundle_id,omitempty" yaml:"bundle_id,omitempty"`
Pid int `json:"pid,omitempty" yaml:"pid,omitempty"`
// config
OutputInterval int `json:"output_interval,omitempty" yaml:"output_interval,omitempty"` // ms
SystemAttributes []string `json:"system_attributes,omitempty" yaml:"system_attributes,omitempty"`
ProcessAttributes []string `json:"process_attributes,omitempty" yaml:"process_attributes,omitempty"`
}
func defaulPerfOption() *PerfOptions {
return &PerfOptions{
SysCPU: false,
SysMem: false,
SysDisk: false,
SysNetwork: false,
gpu: false,
FPS: false,
Network: false,
OutputInterval: 1000, // default 1000ms
SystemAttributes: []string{
// disk
"diskBytesRead",
"diskBytesWritten",
"diskReadOps",
"diskWriteOps",
// memory
"vmCompressorPageCount",
"vmExtPageCount",
"vmFreeCount",
"vmIntPageCount",
"vmPurgeableCount",
"vmWireCount",
"vmUsedCount",
"__vmSwapUsage",
// network
"netBytesIn",
"netBytesOut",
"netPacketsIn",
"netPacketsOut",
},
ProcessAttributes: []string{
"pid",
"cpuUsage",
},
}
}
type PerfOption func(*PerfOptions)
func WithPerfSystemCPU(b bool) PerfOption {
return func(opt *PerfOptions) {
opt.SysCPU = b
}
}
func WithPerfSystemMem(b bool) PerfOption {
return func(opt *PerfOptions) {
opt.SysMem = b
}
}
func WithPerfSystemDisk(b bool) PerfOption {
return func(opt *PerfOptions) {
opt.SysDisk = b
}
}
func WithPerfSystemNetwork(b bool) PerfOption {
return func(opt *PerfOptions) {
opt.SysNetwork = b
}
}
func WithPerfBundleID(bundleID string) PerfOption {
return func(opt *PerfOptions) {
opt.BundleID = bundleID
}
}
func WithPerfPID(pid int) PerfOption {
return func(opt *PerfOptions) {
opt.Pid = pid
}
}
func WithPerfGPU(b bool) PerfOption {
return func(opt *PerfOptions) {
opt.gpu = b
}
}
func WithPerfFPS(b bool) PerfOption {
return func(opt *PerfOptions) {
opt.FPS = b
}
}
func WithPerfNetwork(b bool) PerfOption {
return func(opt *PerfOptions) {
opt.Network = b
}
}
func WithPerfOutputInterval(intervalMilliseconds int) PerfOption {
return func(opt *PerfOptions) {
opt.OutputInterval = intervalMilliseconds
}
}
func WithPerfProcessAttributes(attrs ...string) PerfOption {
return func(opt *PerfOptions) {
opt.ProcessAttributes = attrs
}
}
func WithPerfSystemAttributes(attrs ...string) PerfOption {
return func(opt *PerfOptions) {
opt.SystemAttributes = attrs
}
}
type perfdClient struct {
options *PerfOptions
i Instruments
stop chan struct{} // used to stop perf client
cancel context.CancelFunc // used to cancel all iterators
}
func (d *device) newPerfdSysmontap(options *PerfOptions) (*perfdSysmontap, error) {
instruments, err := d.newInstrumentsService()
if err != nil {
return nil, err
}
return &perfdSysmontap{
perfdClient: perfdClient{
i: instruments,
options: options,
stop: make(chan struct{}),
},
chanSysCPU: make(chan []byte, 10),
chanSysMem: make(chan []byte, 10),
chanSysDisk: make(chan []byte, 10),
chanSysNetwork: make(chan []byte, 10),
chanProcess: make(chan []byte, 10),
}, nil
}
type perfdSysmontap struct {
perfdClient
chanSysCPU chan []byte // system cpu channel
chanSysMem chan []byte // system mem channel
chanSysDisk chan []byte // system disk channel
chanSysNetwork chan []byte // system network channel
chanProcess chan []byte // process channel
}
func (c *perfdSysmontap) Start() (data <-chan []byte, err error) {
// set config
interval := time.Millisecond * time.Duration(c.options.OutputInterval)
log.Printf("set sysmontap sample interval: %dms\n", c.options.OutputInterval)
config := map[string]interface{}{
"bm": 0,
"cpuUsage": true,
"sampleInterval": interval, // time.Duration
"ur": c.options.OutputInterval, // 输出频率
"procAttrs": c.options.ProcessAttributes, // process performance
"sysAttrs": c.options.SystemAttributes, // system performance
}
if _, err = c.i.call(
instrumentsServiceSysmontap,
"setConfig:",
config,
); err != nil {
return nil, err
}
// start
if _, err = c.i.call(
instrumentsServiceSysmontap,
"start",
); err != nil {
return nil, err
}
// register listener
ctx, cancel := context.WithCancel(context.TODO())
c.i.registerCallback("", func(m libimobiledevice.DTXMessageResult) {
select {
case <-ctx.Done():
c.i.call(instrumentsServiceSysmontap, "stop")
return
default:
dataArray, ok := m.Obj.([]interface{})
if !ok || len(dataArray) != 2 {
return
}
if c.options.Pid != 0 {
c.parseProcessData(dataArray)
} else {
c.parseSystemData(dataArray)
}
}
})
c.cancel = cancel
outCh := make(chan []byte, 100)
go func() {
for {
select {
case <-c.stop:
c.cancel()
return
case cpuBytes, ok := <-c.chanSysCPU:
if ok {
outCh <- cpuBytes
}
case memBytes, ok := <-c.chanSysMem:
if ok {
outCh <- memBytes
}
case diskBytes, ok := <-c.chanSysDisk:
if ok {
outCh <- diskBytes
}
case networkBytes, ok := <-c.chanSysNetwork:
if ok {
outCh <- networkBytes
}
case processBytes, ok := <-c.chanProcess:
if ok {
outCh <- processBytes
}
}
}
}()
return outCh, nil
}
func (c *perfdSysmontap) Stop() {
close(c.stop)
}
func (c *perfdSysmontap) parseProcessData(dataArray []interface{}) {
// dataArray example:
// [
// map[
// CPUCount:2
// EnabledCPUs:2
// PerCPUUsage:[
// map[CPU_NiceLoad:0 CPU_SystemLoad:-1 CPU_TotalLoad:3.6363636363636402 CPU_UserLoad:-1]
// map[CPU_NiceLoad:0 CPU_SystemLoad:-1 CPU_TotalLoad:2.7272727272727195 CPU_UserLoad:-1]
// ]
// System:[36408520704 6897049600 3031160 773697 15596 61940 1297 26942 588 17020 127346 1835008 119718056 107009899 174046 103548]
// SystemCPUUsage:map[CPU_NiceLoad:0 CPU_SystemLoad:-1 CPU_TotalLoad:6.36363636363636 CPU_UserLoad:-1]
// StartMachAbsTime:5896602132889
// EndMachAbsTime:5896628486761
// Type:41
// ]
// map[
// Processes:map[
// 0:[1.3582834340402803 0]
// 124:[0.011456702068519481 124]
// 136:[0.05468332721703649 136]
// ]
// StartMachAbsTime:5896602295095
// EndMachAbsTime:5896628780514
// Type:5
// ]
// ]
processData := make(map[string]interface{})
processData["type"] = "process"
processData["timestamp"] = time.Now().Unix()
processData["pid"] = c.options.Pid
defer func() {
processBytes, _ := json.Marshal(processData)
c.chanProcess <- processBytes
}()
systemInfo := dataArray[0].(map[string]interface{})
processInfo := dataArray[1].(map[string]interface{})
if _, ok := systemInfo["System"]; !ok {
systemInfo, processInfo = processInfo, systemInfo
}
var targetProcessValue []interface{}
processList := processInfo["Processes"].(map[string]interface{})
for pid, v := range processList {
if pid != strconv.Itoa(c.options.Pid) {
continue
}
targetProcessValue = v.([]interface{})
}
if targetProcessValue == nil {
processData["msg"] = fmt.Sprintf("process %d not found", c.options.Pid)
return
}
processAttributesMap := make(map[string]interface{})
for idx, value := range c.options.ProcessAttributes {
processAttributesMap[value] = targetProcessValue[idx]
}
processData["proc_perf"] = processAttributesMap
systemAttributesValue := systemInfo["System"].([]interface{})
systemAttributesMap := make(map[string]int64)
for idx, value := range c.options.SystemAttributes {
systemAttributesMap[value] = convert2Int64(systemAttributesValue[idx])
}
processData["sys_perf"] = systemAttributesMap
}
func (c *perfdSysmontap) parseSystemData(dataArray []interface{}) {
timestamp := time.Now().Unix()
var systemInfo map[string]interface{}
data1 := dataArray[0].(map[string]interface{})
data2 := dataArray[1].(map[string]interface{})
if _, ok := data1["SystemCPUUsage"]; ok {
systemInfo = data1
} else {
systemInfo = data2
}
// systemInfo example:
// map[
// CPUCount:2
// EnabledCPUs:2
// PerCPUUsage:[
// map[CPU_NiceLoad:0 CPU_SystemLoad:-1 CPU_TotalLoad:3.9215686274509807 CPU_UserLoad:-1]
// map[CPU_NiceLoad:0 CPU_SystemLoad:-1 CPU_TotalLoad:11.650485436893206 CPU_UserLoad:-1]]
// ]
// System:[704211 35486281728 6303789056 3001119 1001 11033 52668 1740 40022 2114 17310 126903 1835008 160323 107909856 95067 95808179]
// SystemCPUUsage:map[
// CPU_NiceLoad:0
// CPU_SystemLoad:-1
// CPU_TotalLoad:15.572054064344186
// CPU_UserLoad:-1
// ]
// StartMachAbsTime:5339240248449
// EndMachAbsTime:5339264441260
// Type:41
// ]
if c.options.SysCPU {
sysCPUUsage := systemInfo["SystemCPUUsage"].(map[string]interface{})
sysCPUInfo := SystemCPUData{
PerfDataBase: PerfDataBase{
Type: "sys_cpu",
TimeStamp: timestamp,
},
NiceLoad: sysCPUUsage["CPU_NiceLoad"].(float64),
SystemLoad: sysCPUUsage["CPU_SystemLoad"].(float64),
TotalLoad: sysCPUUsage["CPU_TotalLoad"].(float64),
UserLoad: sysCPUUsage["CPU_UserLoad"].(float64),
}
cpuBytes, _ := json.Marshal(sysCPUInfo)
c.chanSysCPU <- cpuBytes
}
systemAttributesValue := systemInfo["System"].([]interface{})
systemAttributesMap := make(map[string]int64)
for idx, value := range c.options.SystemAttributes {
systemAttributesMap[value] = convert2Int64(systemAttributesValue[idx])
}
if c.options.SysMem {
kernelPageSize := int64(1) // why 16384 ?
appMemory := (systemAttributesMap["vmIntPageCount"] - systemAttributesMap["vmPurgeableCount"]) * kernelPageSize
cachedFiles := (systemAttributesMap["vmExtPageCount"] - systemAttributesMap["vmPurgeableCount"]) * kernelPageSize
compressed := systemAttributesMap["vmCompressorPageCount"] * kernelPageSize
usedMemory := (systemAttributesMap["vmUsedCount"] - systemAttributesMap["vmExtPageCount"]) * kernelPageSize
wiredMemory := systemAttributesMap["vmWireCount"] * kernelPageSize
swapUsed := systemAttributesMap["__vmSwapUsage"]
freeMemory := systemAttributesMap["vmFreeCount"] * kernelPageSize
sysMemInfo := SystemMemData{
PerfDataBase: PerfDataBase{
Type: "sys_mem",
TimeStamp: timestamp,
},
AppMemory: appMemory,
UsedMemory: usedMemory,
WiredMemory: wiredMemory,
FreeMemory: freeMemory,
CachedFiles: cachedFiles,
Compressed: compressed,
SwapUsed: swapUsed,
}
memBytes, _ := json.Marshal(sysMemInfo)
c.chanSysMem <- memBytes
}
if c.options.SysDisk {
diskBytesRead := systemAttributesMap["diskBytesRead"]
diskBytesWritten := systemAttributesMap["diskBytesWritten"]
diskReadOps := systemAttributesMap["diskReadOps"]
diskWriteOps := systemAttributesMap["diskWriteOps"]
sysDiskInfo := SystemDiskData{
PerfDataBase: PerfDataBase{
Type: "sys_disk",
TimeStamp: timestamp,
},
DataRead: diskBytesRead,
DataWritten: diskBytesWritten,
ReadOps: diskReadOps,
WriteOps: diskWriteOps,
}
diskBytes, _ := json.Marshal(sysDiskInfo)
c.chanSysDisk <- diskBytes
}
if c.options.SysNetwork {
netBytesIn := systemAttributesMap["netBytesIn"]
netBytesOut := systemAttributesMap["netBytesOut"]
netPacketsIn := systemAttributesMap["netPacketsIn"]
netPacketsOut := systemAttributesMap["netPacketsOut"]
sysNetworkInfo := SystemNetworkData{
PerfDataBase: PerfDataBase{
Type: "sys_network",
TimeStamp: timestamp,
},
BytesIn: netBytesIn,
BytesOut: netBytesOut,
PacketsIn: netPacketsIn,
PacketsOut: netPacketsOut,
}
networkBytes, _ := json.Marshal(sysNetworkInfo)
c.chanSysNetwork <- networkBytes
}
}
type SystemCPUData struct {
PerfDataBase // system cpu
NiceLoad float64 `json:"nice_load"`
SystemLoad float64 `json:"system_load"`
TotalLoad float64 `json:"total_load"`
UserLoad float64 `json:"user_load"`
}
type SystemMemData struct {
PerfDataBase // mem
AppMemory int64 `json:"app_memory"`
FreeMemory int64 `json:"free_memory"`
UsedMemory int64 `json:"used_memory"`
WiredMemory int64 `json:"wired_memory"`
CachedFiles int64 `json:"cached_files"`
Compressed int64 `json:"compressed"`
SwapUsed int64 `json:"swap_used"`
}
type SystemDiskData struct {
PerfDataBase // disk
DataRead int64 `json:"data_read"`
DataWritten int64 `json:"data_written"`
ReadOps int64 `json:"reads_in"`
WriteOps int64 `json:"writes_out"`
}
type SystemNetworkData struct {
PerfDataBase // network
BytesIn int64 `json:"bytes_in"`
BytesOut int64 `json:"bytes_out"`
PacketsIn int64 `json:"packets_in"`
PacketsOut int64 `json:"packets_out"`
}
func (d *device) newPerfdNetworking(options *PerfOptions) (*perfdNetworking, error) {
instruments, err := d.newInstrumentsService()
if err != nil {
return nil, err
}
return &perfdNetworking{
perfdClient: perfdClient{
i: instruments,
options: options,
stop: make(chan struct{}),
},
chanNetwork: make(chan []byte, 10),
}, nil
}
type perfdNetworking struct {
perfdClient
chanNetwork chan []byte // network channel
}
func (c *perfdNetworking) Start() (data <-chan []byte, err error) {
if _, err = c.i.call(
instrumentsServiceNetworking,
"replayLastRecordedSession",
); err != nil {
return nil, err
}
if _, err = c.i.call(
instrumentsServiceNetworking,
"startMonitoring",
); err != nil {
return nil, err
}
ctx, cancel := context.WithCancel(context.TODO())
c.i.registerCallback("", func(m libimobiledevice.DTXMessageResult) {
select {
case <-ctx.Done():
c.i.call(instrumentsServiceNetworking, "stopMonitoring")
return
default:
c.parseNetworking(m.Obj)
}
})
c.cancel = cancel
outCh := make(chan []byte, 100)
go func() {
for {
select {
case <-c.stop:
c.cancel()
return
case networkBytes, ok := <-c.chanNetwork:
if ok {
outCh <- networkBytes
}
}
}
}()
return outCh, nil
}
func (c *perfdNetworking) Stop() {
close(c.stop)
}
func (c *perfdNetworking) parseNetworking(data interface{}) {
raw, ok := data.([]interface{})
if !ok || len(raw) != 2 {
fmt.Printf("invalid networking data: %v\n", data)
return
}
var netBytes []byte
msgType := raw[0].(uint64)
msgValue := raw[1].([]interface{})
if msgType == 0 {
// interface-detection
// ['InterfaceIndex', "Name"]
// e.g. [0, [14, 'en0']]
netData := NetworkDataInterfaceDetection{
PerfDataBase: PerfDataBase{
Type: "network-interface-detection",
TimeStamp: time.Now().Unix(),
},
InterfaceIndex: convert2Int64(msgValue[0]),
Name: msgValue[1].(string),
}
netBytes, _ = json.Marshal(netData)
} else if msgType == 1 {
// connection-detected
// ['LocalAddress', 'RemoteAddress', 'InterfaceIndex', 'Pid',
// 'RecvBufferSize', 'RecvBufferUsed', 'SerialNumber', 'Kind']
// e.g. [1 [[16 2 211 158 192 168 100 101 0 0 0 0 0 0 0 0]
// [16 2 0 53 183 221 253 100 0 0 0 0 0 0 0 0]
// 14 -2 786896 0 133 2]]
localAddr, err := parseSocketAddr(msgValue[0].([]byte))
if err != nil {
fmt.Printf("parse local socket address err: %v\n", err)
}
remoteAddr, err := parseSocketAddr(msgValue[1].([]byte))
if err != nil {
fmt.Printf("parse remote socket address err: %v\n", err)
}
netData := NetworkDataConnectionDetected{
PerfDataBase: PerfDataBase{
Type: "network-connection-detected",
TimeStamp: time.Now().Unix(),
},
LocalAddress: localAddr,
RemoteAddress: remoteAddr,
InterfaceIndex: convert2Int64(msgValue[2]),
Pid: convert2Int64(msgValue[3]),
RecvBufferSize: convert2Int64(msgValue[4]),
RecvBufferUsed: convert2Int64(msgValue[5]),
SerialNumber: convert2Int64(msgValue[6]),
Kind: convert2Int64(msgValue[7]),
}
netBytes, _ = json.Marshal(netData)
} else if msgType == 2 {
// connection-update
// ['RxPackets', 'RxBytes', 'TxPackets', 'TxBytes',
// 'RxDups', 'RxOOO', 'TxRetx', 'MinRTT', 'AvgRTT', 'ConnectionSerial']
// e.g. [2, [21, 1708, 22, 14119, 309, 0, 5830, 0.076125, 0.076125, 54, -1]]
netData := NetworkDataConnectionUpdate{
PerfDataBase: PerfDataBase{
Type: "network-connection-update",
TimeStamp: time.Now().Unix(),
},
RxBytes: convert2Int64(msgValue[0]),
RxPackets: convert2Int64(msgValue[1]),
TxBytes: convert2Int64(msgValue[2]),
TxPackets: convert2Int64(msgValue[3]),
}
if value, ok := msgValue[4].(uint64); ok {
netData.RxDups = int64(value)
}
if value, ok := msgValue[5].(uint64); ok {
netData.RxOOO = int64(value)
}
if value, ok := msgValue[6].(uint64); ok {
netData.TxRetx = int64(value)
}
if value, ok := msgValue[7].(uint64); ok {
netData.MinRTT = int64(value)
}
if value, ok := msgValue[8].(uint64); ok {
netData.AvgRTT = int64(value)
}
if value, ok := msgValue[9].(uint64); ok {
netData.ConnectionSerial = int64(value)
}
netBytes, _ = json.Marshal(netData)
}
c.chanNetwork <- netBytes
}
func parseSocketAddr(data []byte) (string, error) {
len := data[0] // length of address
_ = data[1] // family
port := binary.BigEndian.Uint16(data[2:4]) // port
// network, data[4:4+len]
if len == 0x10 {
// IPv4, 4 bytes
ip := net.IP(data[4:8])
return fmt.Sprintf("%s:%d", ip, port), nil
} else if len == 0x1c {
// IPv6, 16 bytes
ip := net.IP(data[4:20])
return fmt.Sprintf("%s:%d", ip, port), nil
}
return "", fmt.Errorf("invalid socket address: %v", data)
}
type PerfDataBase struct {
Type string `json:"type"`
TimeStamp int64 `json:"timestamp"`
Msg string `json:"msg,omitempty"` // message for invalid data
}
// network-interface-detection
type NetworkDataInterfaceDetection struct {
PerfDataBase
InterfaceIndex int64 `json:"interface_index"` // 0
Name string `json:"name"` // 1
}
// network-connection-detected
type NetworkDataConnectionDetected struct {
PerfDataBase
LocalAddress string `json:"local_address"` // 0
RemoteAddress string `json:"remote_address"` // 1
InterfaceIndex int64 `json:"interface_index"` // 2
Pid int64 `json:"pid"` // 3
RecvBufferSize int64 `json:"recv_buffer_size"` // 4
RecvBufferUsed int64 `json:"recv_buffer_used"` // 5
SerialNumber int64 `json:"serial_number"` // 6
Kind int64 `json:"kind"` // 7
}
// network-connection-update
type NetworkDataConnectionUpdate struct {
PerfDataBase
RxBytes int64 `json:"rx_bytes"` // 0
RxPackets int64 `json:"rx_packets"` // 1
TxBytes int64 `json:"tx_bytes"` // 2
TxPackets int64 `json:"tx_packets"` // 3
RxDups int64 `json:"rx_dups,omitempty"` // 4
RxOOO int64 `json:"rx_000,omitempty"` // 5
TxRetx int64 `json:"tx_retx,omitempty"` // 6
MinRTT int64 `json:"min_rtt,omitempty"` // 7
AvgRTT int64 `json:"avg_rtt,omitempty"` // 8
ConnectionSerial int64 `json:"connection_serial"` // 9
}
func (d *device) newPerfdGraphicsOpengl(options *PerfOptions) (*perfdGraphicsOpengl, error) {
instruments, err := d.newInstrumentsService()
if err != nil {
return nil, err
}
return &perfdGraphicsOpengl{
perfdClient: perfdClient{
i: instruments,
options: options,
stop: make(chan struct{}),
},
chanGPU: make(chan []byte, 10),
chanFPS: make(chan []byte, 10),
}, nil
}
type perfdGraphicsOpengl struct {
perfdClient
chanGPU chan []byte // gpu channel
chanFPS chan []byte // fps channel
}
func (c *perfdGraphicsOpengl) Start() (data <-chan []byte, err error) {
if _, err = c.i.call(
instrumentsServiceGraphicsOpengl,
"setSamplingRate:",
float64(c.options.OutputInterval)/100, // FIXME: unable to set sampling rate, always 1.0
); err != nil {
return nil, err
}
if _, err = c.i.call(
instrumentsServiceGraphicsOpengl,
"startSamplingAtTimeInterval:",
0,
); err != nil {
return nil, err
}
ctx, cancel := context.WithCancel(context.TODO())
c.i.registerCallback("", func(m libimobiledevice.DTXMessageResult) {
select {
case <-ctx.Done():
c.i.call(instrumentsServiceGraphicsOpengl, "stopSampling")
return
default:
c.parseData(m.Obj)
}
})
c.cancel = cancel
outCh := make(chan []byte, 100)
go func() {
for {
select {
case <-c.stop:
c.cancel()
return
case gpuBytes, ok := <-c.chanGPU:
if ok {
outCh <- gpuBytes
}
case fpsBytes, ok := <-c.chanFPS:
if ok {
outCh <- fpsBytes
}
}
}
}()
return outCh, nil
}
func (c *perfdGraphicsOpengl) Stop() {
close(c.stop)
}
func (c *perfdGraphicsOpengl) parseData(data interface{}) {
// data example:
// map[
// Alloc system memory:50167808
// Allocated PB Size:1179648
// CoreAnimationFramesPerSecond:0 // fps from GPU
// Device Utilization %:0 // device
// IOGLBundleName:Built-In
// In use system memory:10633216
// Renderer Utilization %:0 // renderer
// SplitSceneCount:0
// TiledSceneBytes:0
// Tiler Utilization %:0 // tiler
// XRVideoCardRunTimeStamp:1010679
// recoveryCount:0
// ]
gpuInfo := GPUData{
PerfDataBase: PerfDataBase{
Type: "gpu",
TimeStamp: time.Now().Unix(),
},
}
fpsInfo := FPSData{
PerfDataBase: PerfDataBase{
Type: "fps",
TimeStamp: time.Now().Unix(),
},
}
defer func() {
if c.options.gpu {
gpuBytes, _ := json.Marshal(gpuInfo)
c.chanGPU <- gpuBytes
}
if c.options.FPS {
fpsBytes, _ := json.Marshal(fpsInfo)
c.chanFPS <- fpsBytes
}
}()
raw, ok := data.(map[string]interface{})
if !ok {
gpuInfo.Msg = fmt.Sprintf("invalid graphics.opengl data: %v", data)
return
}
// gpu
gpuInfo.DeviceUtilization = convert2Int64(raw["Device Utilization %"])
gpuInfo.TilerUtilization = convert2Int64(raw["Tiler Utilization %"])
gpuInfo.RendererUtilization = convert2Int64(raw["Renderer Utilization %"])
// fps
fpsInfo.FPS = int(convert2Int64(raw["CoreAnimationFramesPerSecond"]))
}
type GPUData struct {
PerfDataBase // gpu
TilerUtilization int64 `json:"tiler_utilization"` // 处理顶点的 GPU 时间占比
DeviceUtilization int64 `json:"device_utilization"` // 设备利用率
RendererUtilization int64 `json:"renderer_utilization"` // 渲染器利用率
}
type FPSData struct {
PerfDataBase // fps
FPS int `json:"fps"`
}
func convert2Int64(num interface{}) int64 {
switch value := num.(type) {
case int64:
return value
case uint64:
return int64(value)
case uint32:
return int64(value)
case uint16:
return int64(value)
case uint8:
return int64(value)
case uint:
return int64(value)
}
fmt.Printf("convert2Int64 failed: %v, %T\n", num, num)
return -1
}
func containString(ss []string, s string) bool {
for _, v := range ss {
if s == v {
return true
}
}
return false
}

View File

@@ -1,161 +0,0 @@
//go:build localtest
package gidevice
import (
"fmt"
"testing"
"time"
)
func TestPerfSystemMonitor(t *testing.T) {
setupLockdownSrv(t)
data, err := dev.PerfStart(
WithPerfSystemCPU(true),
WithPerfSystemMem(true),
WithPerfSystemDisk(true),
WithPerfSystemNetwork(true),
WithPerfOutputInterval(1000),
)
if err != nil {
t.Fatal(err)
}
timer := time.NewTimer(time.Duration(time.Second * 10))
for {
select {
case <-timer.C:
dev.PerfStop()
return
case d := <-data:
fmt.Println(string(d))
}
}
}
func TestPerfProcessMonitor(t *testing.T) {
setupLockdownSrv(t)
data, err := dev.PerfStart(
WithPerfProcessAttributes("cpuUsage", "memAnon"),
WithPerfOutputInterval(1000),
WithPerfPID(100),
WithPerfBundleID("com.apple.mobilesafari"), // higher priority than pid
)
if err != nil {
t.Fatal(err)
}
timer := time.NewTimer(time.Duration(time.Second * 10))
for {
select {
case <-timer.C:
dev.PerfStop()
return
case d := <-data:
fmt.Println(string(d))
}
}
}
func TestPerfGPU(t *testing.T) {
setupLockdownSrv(t)
data, err := dev.PerfStart(
WithPerfSystemCPU(false),
WithPerfSystemMem(false),
WithPerfGPU(true),
)
if err != nil {
t.Fatal(err)
}
timer := time.NewTimer(time.Duration(time.Second * 10))
for {
select {
case <-timer.C:
dev.PerfStop()
return
case d := <-data:
fmt.Println(string(d))
}
}
}
func TestPerfFPS(t *testing.T) {
setupLockdownSrv(t)
data, err := dev.PerfStart(
WithPerfSystemCPU(false),
WithPerfSystemMem(false),
WithPerfFPS(true),
)
if err != nil {
t.Fatal(err)
}
timer := time.NewTimer(time.Duration(time.Second * 10))
for {
select {
case <-timer.C:
dev.PerfStop()
return
case d := <-data:
fmt.Println(string(d))
}
}
}
func TestPerfNetwork(t *testing.T) {
setupLockdownSrv(t)
data, err := dev.PerfStart(
WithPerfSystemCPU(false),
WithPerfSystemMem(false),
WithPerfNetwork(true),
)
if err != nil {
t.Fatal(err)
}
timer := time.NewTimer(time.Duration(time.Second * 10))
for {
select {
case <-timer.C:
dev.PerfStop()
return
case d := <-data:
fmt.Println(string(d))
}
}
}
func TestPerfAll(t *testing.T) {
setupLockdownSrv(t)
data, err := dev.PerfStart(
WithPerfSystemCPU(true),
WithPerfSystemMem(true),
WithPerfSystemDisk(true),
WithPerfSystemNetwork(true),
WithPerfNetwork(true),
WithPerfFPS(true),
WithPerfGPU(true),
WithPerfBundleID("com.apple.mobilesafari"),
)
if err != nil {
t.Fatal(err)
}
timer := time.NewTimer(time.Duration(time.Second * 10))
for {
select {
case <-timer.C:
dev.PerfStop()
return
case d := <-data:
fmt.Println(string(d))
}
}
}

View File

@@ -1,53 +0,0 @@
package ipa
import (
"archive/zip"
"fmt"
"io"
"path"
"howett.net/plist"
)
func Info(ipaPath string) (info map[string]interface{}, err error) {
var reader *zip.ReadCloser
if reader, err = zip.OpenReader(ipaPath); err != nil {
return nil, err
}
defer func() {
err = reader.Close()
}()
for _, file := range reader.File {
matched, _err := path.Match("Payload/*.app/Info.plist", file.Name)
if _err != nil {
err = _err
continue
}
if !matched {
continue
}
var rd io.ReadCloser
if rd, _err = file.Open(); _err != nil {
return nil, _err
}
data, _err := io.ReadAll(rd)
if _err != nil {
return nil, _err
}
info = make(map[string]interface{})
_, _err = plist.Unmarshal(data, &info)
if _err != nil {
return nil, _err
}
}
if err != nil && len(info) == 0 {
return nil, fmt.Errorf("find Info.plist: %w", err)
}
return
}

View File

@@ -1,23 +0,0 @@
//go:build localtest
package ipa
import (
"testing"
)
func TestInfo(t *testing.T) {
name := "/Users/hero/Documents/Workspace/GitHub/taobao-iphone-device/tests/testdata/WebDriverAgentRunner.ipa"
name = "/private/tmp/derivedDataPath/Build/Products/Release-iphoneos/WebDriverAgentRunner-Runner.ipa"
info, err := Info(name)
if err != nil {
t.Fatal(err)
}
for k, v := range info {
t.Logf("%-50s\t%v", k, v)
}
t.Log(info["CFBundleIdentifier"])
}

View File

@@ -1,105 +0,0 @@
package libimobiledevice
import (
"bytes"
"encoding/hex"
"fmt"
)
const AfcServiceName = "com.apple.afc"
func NewAfcClient(innerConn InnerConn) *AfcClient {
return &AfcClient{
innerConn: innerConn,
}
}
type AfcClient struct {
innerConn InnerConn
packetNum uint64
}
func (c *AfcClient) newPacket(operation uint64, data, payload []byte) Packet {
c.packetNum++
pkt := &afcPacket{
operation: operation,
packetNum: c.packetNum,
entireLen: 40,
thisLen: 40,
}
if data != nil {
n := uint64(len(data))
pkt.entireLen += n
pkt.thisLen += n
}
if payload != nil {
pkt.entireLen += uint64(len(payload))
}
return pkt
}
func (c *AfcClient) Send(operation uint64, data, payload []byte) (err error) {
pkt := c.newPacket(operation, data, payload)
var raw []byte
if raw, err = pkt.Pack(); err != nil {
return fmt.Errorf("send packet (afc): %w", err)
}
buf := new(bytes.Buffer)
buf.Write(raw)
if data != nil {
debugLog(fmt.Sprintf("--> %s ...afc data...\n", pkt))
buf.Write(data)
} else {
debugLog(fmt.Sprintf("--> %s\n", pkt))
}
if err = c.innerConn.Write(buf.Bytes()); err != nil {
return fmt.Errorf("send packet (afc): %w", err)
}
if payload != nil {
if err = c.innerConn.Write(payload); err != nil {
return fmt.Errorf("send packet (afc): %w", err)
}
}
return
}
func (c *AfcClient) Receive() (respMsg *AfcMessage, err error) {
var bufHeader []byte
if bufHeader, err = c.innerConn.Read(40); err != nil {
return nil, fmt.Errorf("receive packet (afc): %w", err)
}
buffer := new(bytes.Buffer)
buffer.Write(bufHeader)
var respPkt *afcPacket
if respPkt, err = new(afcPacket).unpack(buffer); err != nil {
return nil, fmt.Errorf("receive packet (afc): %w", err)
}
respMsg = new(AfcMessage)
respMsg.Operation = respPkt.operation
buffer.Reset()
if respPkt.entireLen > 40 {
length := int(respPkt.entireLen - 40)
var bufDataAndPayload []byte
if bufDataAndPayload, err = c.innerConn.Read(length); err != nil {
return nil, fmt.Errorf("receive packet (afc): %w", err)
}
buffer.Write(bufDataAndPayload)
}
bufData := make([]byte, respPkt.thisLen-40)
if _, err = buffer.Read(bufData); err != nil {
return nil, fmt.Errorf("receive packet (afc buffer): %w", err)
}
respMsg.Data = bufData
respMsg.Payload = buffer.Bytes()
debugLog(fmt.Sprintf("<-- %s\n%s\n%s", respPkt, hex.Dump(respMsg.Data), hex.Dump(respMsg.Payload)))
return
}

View File

@@ -1,187 +0,0 @@
package libimobiledevice
import (
"bytes"
"encoding/binary"
"errors"
)
type AfcMessage struct {
Operation uint64
Data []byte
Payload []byte
}
func (m *AfcMessage) Map() map[string]string {
ret := make(map[string]string)
ss := m.Strings()
if ss != nil {
for i := 0; i < len(ss); i += 2 {
ret[ss[i]] = ss[i+1]
}
}
return ret
}
func (m *AfcMessage) Strings() []string {
if m.Operation == AfcOperationData {
bs := bytes.Split(m.Payload, []byte{0})
ss := make([]string, len(bs)-1)
for i := 0; i < len(ss); i++ {
ss[i] = string(bs[i])
}
return ss
}
return nil
}
func (m *AfcMessage) Uint64() uint64 {
return binary.LittleEndian.Uint64(m.Data)
}
func (m *AfcMessage) Err() error {
if m.Operation == AfcOperationStatus {
status := m.Uint64()
if status != AfcErrSuccess {
return toError(status)
}
}
return nil
}
func toError(status uint64) error {
switch status {
case AfcErrUnknownError:
return errors.New("UnknownError")
case AfcErrOperationHeaderInvalid:
return errors.New("OperationHeaderInvalid")
case AfcErrNoResources:
return errors.New("NoResources")
case AfcErrReadError:
return errors.New("ReadError")
case AfcErrWriteError:
return errors.New("WriteError")
case AfcErrUnknownPacketType:
return errors.New("UnknownPacketType")
case AfcErrInvalidArgument:
return errors.New("InvalidArgument")
case AfcErrObjectNotFound:
return errors.New("ObjectNotFound")
case AfcErrObjectIsDir:
return errors.New("ObjectIsDir")
case AfcErrPermDenied:
return errors.New("PermDenied")
case AfcErrServiceNotConnected:
return errors.New("ServiceNotConnected")
case AfcErrOperationTimeout:
return errors.New("OperationTimeout")
case AfcErrTooMuchData:
return errors.New("TooMuchData")
case AfcErrEndOfData:
return errors.New("EndOfData")
case AfcErrOperationNotSupported:
return errors.New("OperationNotSupported")
case AfcErrObjectExists:
return errors.New("ObjectExists")
case AfcErrObjectBusy:
return errors.New("ObjectBusy")
case AfcErrNoSpaceLeft:
return errors.New("NoSpaceLeft")
case AfcErrOperationWouldBlock:
return errors.New("OperationWouldBlock")
case AfcErrIoError:
return errors.New("IoError")
case AfcErrOperationInterrupted:
return errors.New("OperationInterrupted")
case AfcErrOperationInProgress:
return errors.New("OperationInProgress")
case AfcErrInternalError:
return errors.New("InternalError")
case AfcErrMuxError:
return errors.New("MuxError")
case AfcErrNoMemory:
return errors.New("NoMemory")
case AfcErrNotEnoughData:
return errors.New("NotEnoughData")
case AfcErrDirNotEmpty:
return errors.New("DirNotEmpty")
}
return nil
}
const (
AfcOperationInvalid = 0x00000000 /* Invalid */
AfcOperationStatus = 0x00000001 /* Status */
AfcOperationData = 0x00000002 /* Data */
AfcOperationReadDir = 0x00000003 /* ReadDir */
AfcOperationReadFile = 0x00000004 /* ReadFile */
AfcOperationWriteFile = 0x00000005 /* WriteFile */
AfcOperationWritePart = 0x00000006 /* WritePart */
AfcOperationTruncateFile = 0x00000007 /* TruncateFile */
AfcOperationRemovePath = 0x00000008 /* RemovePath */
AfcOperationMakeDir = 0x00000009 /* MakeDir */
AfcOperationGetFileInfo = 0x0000000A /* GetFileInfo */
AfcOperationGetDeviceInfo = 0x0000000B /* GetDeviceInfo */
AfcOperationWriteFileAtomic = 0x0000000C /* WriteFileAtomic (tmp file+rename) */
AfcOperationFileOpen = 0x0000000D /* FileRefOpen */
AfcOperationFileOpenResult = 0x0000000E /* FileRefOpenResult */
AfcOperationFileRead = 0x0000000F /* FileRefRead */
AfcOperationFileWrite = 0x00000010 /* FileRefWrite */
AfcOperationFileSeek = 0x00000011 /* FileRefSeek */
AfcOperationFileTell = 0x00000012 /* FileRefTell */
AfcOperationFileTellResult = 0x00000013 /* FileRefTellResult */
AfcOperationFileClose = 0x00000014 /* FileRefClose */
AfcOperationFileSetSize = 0x00000015 /* FileRefSetFileSize (ftruncate) */
AfcOperationGetConnectionInfo = 0x00000016 /* GetConnectionInfo */
AfcOperationSetConnectionOptions = 0x00000017 /* SetConnectionOptions */
AfcOperationRenamePath = 0x00000018 /* RenamePath */
AfcOperationSetFSBlockSize = 0x00000019 /* SetFSBlockSize (0x800000) */
AfcOperationSetSocketBlockSize = 0x0000001A /* SetSocketBlockSize (0x800000) */
AfcOperationFileRefLock = 0x0000001B /* FileRefLock */
AfcOperationMakeLink = 0x0000001C /* MakeLink */
AfcOperationGetFileHash = 0x0000001D /* GetFileHash */
AfcOperationSetFileModTime = 0x0000001E /* SetModTime */
AfcOperationGetFileHashRange = 0x0000001F /* GetFileHashWithRange */
/* iOS 6+ */
AfcOperationFileSetImmutableHint = 0x00000020 /* FileRefSetImmutableHint */
AfcOperationGetSizeOfPathContents = 0x00000021 /* GetSizeOfPathContents */
AfcOperationRemovePathAndContents = 0x00000022 /* RemovePathAndContents */
AfcOperationDirectoryEnumeratorRefOpen = 0x00000023 /* DirectoryEnumeratorRefOpen */
AfcOperationDirectoryEnumeratorRefOpenResult = 0x00000024 /* DirectoryEnumeratorRefOpenResult */
AfcOperationDirectoryEnumeratorRefRead = 0x00000025 /* DirectoryEnumeratorRefRead */
AfcOperationDirectoryEnumeratorRefClose = 0x00000026 /* DirectoryEnumeratorRefClose */
/* iOS 7+ */
AfcOperationFileRefReadWithOffset = 0x00000027 /* FileRefReadWithOffset */
AfcOperationFileRefWriteWithOffset = 0x00000028 /* FileRefWriteWithOffset */
)
const (
AfcErrSuccess = 0
AfcErrUnknownError = 1
AfcErrOperationHeaderInvalid = 2
AfcErrNoResources = 3
AfcErrReadError = 4
AfcErrWriteError = 5
AfcErrUnknownPacketType = 6
AfcErrInvalidArgument = 7
AfcErrObjectNotFound = 8
AfcErrObjectIsDir = 9
AfcErrPermDenied = 10
AfcErrServiceNotConnected = 11
AfcErrOperationTimeout = 12
AfcErrTooMuchData = 13
AfcErrEndOfData = 14
AfcErrOperationNotSupported = 15
AfcErrObjectExists = 16
AfcErrObjectBusy = 17
AfcErrNoSpaceLeft = 18
AfcErrOperationWouldBlock = 19
AfcErrIoError = 20
AfcErrOperationInterrupted = 21
AfcErrOperationInProgress = 22
AfcErrInternalError = 23
AfcErrMuxError = 30
AfcErrNoMemory = 31
AfcErrNotEnoughData = 32
AfcErrDirNotEmpty = 33
)

View File

@@ -1,138 +0,0 @@
package libimobiledevice
import (
"bytes"
"encoding/binary"
"errors"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/nskeyedarchiver"
)
type AuxBuffer struct {
buf *bytes.Buffer
}
func NewAuxBuffer() *AuxBuffer {
return &AuxBuffer{
buf: new(bytes.Buffer),
}
}
func (m *AuxBuffer) AppendObject(obj interface{}) error {
marshal, err := nskeyedarchiver.Marshal(obj)
if err != nil {
return err
}
m.AppendUInt32(10)
m.AppendUInt32(2)
m.AppendUInt32(uint32(len(marshal)))
m.buf.Write(marshal)
return nil
}
func (m *AuxBuffer) AppendInt64(v int64) {
m.AppendUInt32(10)
m.AppendUInt32(4)
m.AppendUInt64(uint64(v))
}
func (m *AuxBuffer) AppendInt32(v int32) {
m.AppendUInt32(10)
m.AppendUInt32(3)
m.AppendUInt32(uint32(v))
}
func (m *AuxBuffer) AppendUInt32(v uint32) {
_ = binary.Write(m.buf, binary.LittleEndian, v)
}
func (m *AuxBuffer) AppendUInt64(v uint64) {
_ = binary.Write(m.buf, binary.LittleEndian, v)
}
func (m *AuxBuffer) AppendBytes(b []byte) {
m.buf.Write(b)
}
func (m *AuxBuffer) Len() int {
return m.buf.Len()
}
func (m *AuxBuffer) Bytes() []byte {
dup := m.buf.Bytes()
b := make([]byte, 16)
binary.LittleEndian.PutUint64(b, 0x01f0)
binary.LittleEndian.PutUint64(b[8:], uint64(m.Len()))
return append(b, dup...)
}
func UnmarshalAuxBuffer(b []byte) ([]interface{}, error) {
reader := bytes.NewReader(b)
var magic, pkgLen uint64
if err := binary.Read(reader, binary.LittleEndian, &magic); err != nil {
return nil, err
}
if err := binary.Read(reader, binary.LittleEndian, &pkgLen); err != nil {
return nil, err
}
// if magic != 0x1df0 {
// TODO magic
// return nil, errors.New("magic not equal 0x1df0")
// }
if pkgLen > uint64(len(b)-16) {
return nil, errors.New("package length not enough")
}
var ret []interface{}
for reader.Len() > 0 {
var flag, typ uint32
if err := binary.Read(reader, binary.LittleEndian, &flag); err != nil {
return nil, err
}
if err := binary.Read(reader, binary.LittleEndian, &typ); err != nil {
return nil, err
}
switch typ {
case 2:
var l uint32
if err := binary.Read(reader, binary.LittleEndian, &l); err != nil {
return nil, err
}
plistBuf := make([]byte, l)
if _, err := reader.Read(plistBuf); err != nil {
return nil, err
}
archiver := NewNSKeyedArchiver()
d, err := archiver.Unmarshal(plistBuf)
if err != nil {
return nil, err
}
ret = append(ret, d)
case 3, 5:
var i int32
if err := binary.Read(reader, binary.LittleEndian, &i); err != nil {
return nil, err
}
ret = append(ret, i)
case 4, 6:
var i int64
if err := binary.Read(reader, binary.LittleEndian, &i); err != nil {
return nil, err
}
ret = append(ret, i)
case 10:
// TODO Dictionary key
// fmt.Println("Dictionary key!")
continue
default:
// fmt.Printf("unknown type %d\n", typ)
break
}
}
return ret, nil
}

View File

@@ -1,405 +0,0 @@
package libimobiledevice
import (
"bytes"
"context"
"encoding/hex"
"fmt"
"io"
"strings"
"sync"
"time"
"unsafe"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/nskeyedarchiver"
)
const (
_unregistered = "_Golang-iDevice_Unregistered"
_over = "_Golang-iDevice_Over"
)
func newDtxMessageClient(innerConn InnerConn) *dtxMessageClient {
c := &dtxMessageClient{
innerConn: innerConn,
msgID: 0,
publishedChannels: make(map[string]int32),
openedChannels: make(map[string]uint32),
toReply: make(chan *dtxMessageHeaderPacket),
mu: sync.Mutex{},
resultMap: make(map[interface{}]*DTXMessageResult),
callbackMap: make(map[string]func(m DTXMessageResult)),
}
c.RegisterCallback(_unregistered, func(m DTXMessageResult) {})
c.RegisterCallback(_over, func(m DTXMessageResult) {})
c.ctx, c.cancelFunc = context.WithCancel(context.Background())
c.startReceive()
c.startWaitingForReply()
return c
}
type dtxMessageClient struct {
innerConn InnerConn
msgID uint32
publishedChannels map[string]int32
openedChannels map[string]uint32
toReply chan *dtxMessageHeaderPacket
mu sync.Mutex
resultMap map[interface{}]*DTXMessageResult
callbackMap map[string]func(m DTXMessageResult)
ctx context.Context
cancelFunc context.CancelFunc
}
func (c *dtxMessageClient) SendDTXMessage(selector string, aux []byte, channelCode uint32, expectsReply bool) (msgID uint32, err error) {
payload := new(dtxMessagePayloadPacket)
header := &dtxMessageHeaderPacket{
ExpectsReply: 1,
}
flag := 0x1000
if !expectsReply {
flag = 0
header.ExpectsReply = 0
}
var sel []byte
if sel, err = nskeyedarchiver.Marshal(selector); err != nil {
return 0, err
}
if aux == nil {
aux = make([]byte, 0)
}
payload.Flags = uint32(0x2 | flag)
payload.AuxiliaryLength = uint32(len(aux))
payload.TotalLength = uint64(len(aux)) + uint64(len(sel))
header.Magic = 0x1F3D5B79
header.CB = uint32(unsafe.Sizeof(*header))
header.FragmentId = 0
header.FragmentCount = 1
header.Length = uint32(unsafe.Sizeof(*payload)) + uint32(payload.TotalLength)
c.msgID++
header.Identifier = c.msgID
header.ConversationIndex = 0
header.ChannelCode = channelCode
msgPkt := new(dtxMessagePacket)
msgPkt.Header = header
msgPkt.Payload = payload
msgPkt.Aux = aux
msgPkt.Sel = sel
raw, err := msgPkt.Pack()
if err != nil {
return 0, err
}
debugLog(fmt.Sprintf("--> %s\n", msgPkt))
msgID = header.Identifier
err = c.innerConn.Write(raw)
return
}
func (c *dtxMessageClient) ReceiveDTXMessage() (result *DTXMessageResult, err error) {
bufPayload := new(bytes.Buffer)
var header *dtxMessageHeaderPacket = nil
var needToReply *dtxMessageHeaderPacket = nil
for {
header = new(dtxMessageHeaderPacket)
lenHeader := int(unsafe.Sizeof(*header))
var bufHeader []byte
if bufHeader, err = c.innerConn.Read(lenHeader); err != nil {
return nil, fmt.Errorf("receive: length of DTXMessageHeader: %w", err)
}
if header, err = header.unpack(bytes.NewBuffer(bufHeader)); err != nil {
return nil, fmt.Errorf("receive: DTXMessageHeader unpack: %w", err)
}
if header.ExpectsReply == 1 {
needToReply = header
}
if header.Magic != 0x1F3D5B79 {
return nil, fmt.Errorf("receive: bad magic %x", header.Magic)
}
if header.ConversationIndex == 1 {
if header.Identifier != c.msgID {
return nil, fmt.Errorf("receive: except identifier %d new identifier %d", c.msgID, header.Identifier)
}
} else if header.ConversationIndex == 0 {
if header.Identifier > c.msgID {
c.msgID = header.Identifier
}
} else {
return nil, fmt.Errorf("receive: invalid conversationIndex %d", header.ConversationIndex)
}
if header.FragmentId == 0 && header.FragmentCount > 1 {
continue
}
var data []byte
if data, err = c.innerConn.Read(int(header.Length)); err != nil {
return nil, fmt.Errorf("receive: length of DTXMessageHeader: %w", err)
}
bufPayload.Write(data)
if header.FragmentId == header.FragmentCount-1 {
break
}
}
rawPayload := bufPayload.Bytes()
payload := new(dtxMessagePayloadPacket)
if payload, err = payload.unpack(bufPayload); err != nil {
return nil, fmt.Errorf("receive: unpack DTXMessagePayload: %w", err)
}
compress := (payload.Flags & 0xff000) >> 12
if compress != 0 {
return nil, fmt.Errorf("receive: message is compressed type %d", compress)
}
payloadSize := uint32(unsafe.Sizeof(*payload))
objOffset := uint64(payloadSize + payload.AuxiliaryLength)
var aux, obj []byte
// see https://github.com/electricbubble/gidevice/issues/28
if r, l := payloadSize+payload.AuxiliaryLength, len(rawPayload); int(r) <= l {
aux = rawPayload[payloadSize:r]
} else {
debugLog(fmt.Sprintf("<-- DTXMessage %s\n%s\n"+
"[aux] bounds out of range [:%d] with capacity %d",
header.String(), payload.String(),
r, l,
))
}
if r, l := objOffset+(payload.TotalLength-uint64(payload.AuxiliaryLength)), len(rawPayload); int(r) <= l {
obj = rawPayload[objOffset:r]
} else {
debugLog(fmt.Sprintf("<-- DTXMessage %s\n%s\n"+
"[obj] bounds out of range [:%d] with capacity %d",
header.String(), payload.String(),
r, l,
))
}
debugLog(fmt.Sprintf(
"<-- DTXMessage %s\n%s\n"+
"%s\n%s\n",
header.String(), payload.String(),
hex.Dump(aux), hex.Dump(obj),
))
result = new(DTXMessageResult)
if len(aux) > 0 {
if aux, err := UnmarshalAuxBuffer(aux); err != nil {
return nil, fmt.Errorf("receive: unpack AUX: %w", err)
} else {
result.Aux = aux
}
}
if len(obj) > 0 {
if obj, err := NewNSKeyedArchiver().Unmarshal(obj); err != nil {
return nil, fmt.Errorf("receive: unpack NSKeyedArchiver: %w", err)
} else {
result.Obj = obj
}
}
sObj, ok := result.Obj.(string)
if fn, do := c.callbackMap[sObj]; do {
fn(*result)
} else {
c.callbackMap[_unregistered](*result)
}
if needToReply != nil {
go func() { c.toReply <- needToReply }()
} else {
var sk interface{} = header.Identifier
if ok && sObj == "_notifyOfPublishedCapabilities:" {
sk = "_notifyOfPublishedCapabilities:"
}
c.mu.Lock()
c.resultMap[sk] = result
c.mu.Unlock()
}
return
}
func (c *dtxMessageClient) Connection() (publishedChannels map[string]int32, err error) {
args := NewAuxBuffer()
if err = args.AppendObject(map[string]interface{}{
"com.apple.private.DTXBlockCompression": uint64(2),
"com.apple.private.DTXConnection": uint64(1),
}); err != nil {
return nil, fmt.Errorf("connection DTXMessage: %w", err)
}
selector := "_notifyOfPublishedCapabilities:"
if _, err = c.SendDTXMessage(selector, args.Bytes(), 0, false); err != nil {
return nil, fmt.Errorf("connection send: %w", err)
}
var result *DTXMessageResult
if result, err = c.GetResult(selector); err != nil {
return nil, fmt.Errorf("connection receive: %w", err)
}
if result.Obj.(string) != "_notifyOfPublishedCapabilities:" {
return nil, fmt.Errorf("connection: response mismatch: %s", result.Obj)
}
aux := result.Aux[0].(map[string]interface{})
for k, v := range aux {
c.publishedChannels[k] = int32(v.(uint64))
}
return c.publishedChannels, nil
}
func (c *dtxMessageClient) MakeChannel(channel string) (id uint32, err error) {
var ok bool
if id, ok = c.openedChannels[channel]; ok {
return id, nil
}
id = uint32(len(c.openedChannels) + 1)
args := NewAuxBuffer()
args.AppendInt32(int32(id))
if err = args.AppendObject(channel); err != nil {
return 0, fmt.Errorf("make channel DTXMessage: %w", err)
}
selector := "_requestChannelWithCode:identifier:"
var msgID uint32
if msgID, err = c.SendDTXMessage(selector, args.Bytes(), 0, true); err != nil {
return 0, fmt.Errorf("make channel send: %w", err)
}
if _, err = c.GetResult(msgID); err != nil {
return 0, fmt.Errorf("make channel receive: %w", err)
}
c.openedChannels[channel] = id
return
}
func (c *dtxMessageClient) RegisterCallback(obj string, cb func(m DTXMessageResult)) {
c.callbackMap[obj] = cb
}
func (c *dtxMessageClient) GetResult(key interface{}) (*DTXMessageResult, error) {
startTime := time.Now()
for {
time.Sleep(100 * time.Millisecond)
c.mu.Lock()
if v, ok := c.resultMap[key]; ok {
delete(c.resultMap, key)
c.mu.Unlock()
return v, nil
} else {
c.mu.Unlock()
}
if elapsed := time.Since(startTime); elapsed > 30*time.Second {
return nil, fmt.Errorf("dtx: get result: timeout after %v", elapsed)
}
}
}
func (c *dtxMessageClient) Close() {
c.cancelFunc()
c.innerConn.Close()
}
func (c *dtxMessageClient) startReceive() {
go func() {
for {
select {
case <-c.ctx.Done():
return
default:
if _, err := c.ReceiveDTXMessage(); err != nil {
debugLog(fmt.Sprintf("dtx: receive: %s", err))
if strings.Contains(err.Error(), io.EOF.Error()) {
c.cancelFunc()
c.callbackMap[_over](DTXMessageResult{})
break
}
}
}
}
}()
}
func (c *dtxMessageClient) startWaitingForReply() {
go func() {
for {
select {
case <-c.ctx.Done():
return
case reqHeader := <-c.toReply:
replyPayload := new(dtxMessagePayloadPacket)
replyPayload.Flags = 0
replyPayload.AuxiliaryLength = 0
replyPayload.TotalLength = 0
replyHeader := new(dtxMessageHeaderPacket)
replyHeader.Magic = 0x1F3D5B79
replyHeader.CB = uint32(unsafe.Sizeof(*replyHeader))
replyHeader.FragmentId = 0
replyHeader.FragmentCount = 1
replyHeader.Length = uint32(unsafe.Sizeof(*replyPayload)) + uint32(replyPayload.TotalLength)
replyHeader.Identifier = reqHeader.Identifier
replyHeader.ConversationIndex = reqHeader.ConversationIndex + 1
replyHeader.ChannelCode = reqHeader.ChannelCode
replyHeader.ExpectsReply = 0
replyPkt := new(dtxMessagePacket)
replyPkt.Header = replyHeader
replyPkt.Payload = replyPayload
replyPkt.Aux = nil
replyPkt.Sel = nil
raw, err := replyPkt.Pack()
if err != nil {
debugLog(fmt.Sprintf("pack: reply DTXMessage: %s", err))
continue
}
if err = c.innerConn.Write(raw); err != nil {
debugLog(fmt.Sprintf("send: reply DTXMessage: %s", err))
continue
}
}
}
}()
}
type DTXMessageResult struct {
Obj interface{}
Aux []interface{}
}

View File

@@ -1,81 +0,0 @@
package libimobiledevice
import (
"bytes"
"encoding/binary"
"fmt"
"howett.net/plist"
)
func newServicePacketClient(innerConn InnerConn) *servicePacketClient {
return &servicePacketClient{
innerConn: innerConn,
}
}
type servicePacketClient struct {
innerConn InnerConn
}
func (c *servicePacketClient) NewXmlPacket(req interface{}) (Packet, error) {
return c.newPacket(req, plist.XMLFormat)
}
func (c *servicePacketClient) NewBinaryPacket(req interface{}) (Packet, error) {
return c.newPacket(req, plist.BinaryFormat)
}
func (c *servicePacketClient) newPacket(req interface{}, format int) (Packet, error) {
pkt := new(servicePacket)
if buf, err := plist.Marshal(req, format); err != nil {
return nil, fmt.Errorf("plist packet marshal: %w", err)
} else {
pkt.body = buf
}
pkt.length = uint32(len(pkt.body))
return pkt, nil
}
func (c *servicePacketClient) SendPacket(pkt Packet) (err error) {
var raw []byte
if raw, err = pkt.Pack(); err != nil {
return fmt.Errorf("send packet: %w", err)
}
debugLog(fmt.Sprintf("--> %s\n", pkt))
return c.innerConn.Write(raw)
}
func (c *servicePacketClient) ReceivePacket() (respPkt Packet, err error) {
var bufLen []byte
if bufLen, err = c.innerConn.Read(4); err != nil {
return nil, fmt.Errorf("receive packet: %w", err)
}
lenPkg := binary.BigEndian.Uint32(bufLen)
buffer := bytes.NewBuffer([]byte{})
buffer.Write(bufLen)
var buf []byte
if buf, err = c.innerConn.Read(int(lenPkg)); err != nil {
return nil, fmt.Errorf("receive packet: %w", err)
}
buffer.Write(buf)
if respPkt, err = new(servicePacket).Unpack(buffer); err != nil {
return nil, fmt.Errorf("receive packet: %w", err)
}
debugLog(fmt.Sprintf("<-- %s\n", respPkt))
var reply LockdownBasicResponse
if err = respPkt.Unmarshal(&reply); err != nil {
return nil, fmt.Errorf("receive packet: %w", err)
}
if reply.Error != "" {
return nil, fmt.Errorf("receive packet: %s", reply.Error)
}
return
}

View File

@@ -1,20 +0,0 @@
package libimobiledevice
const (
CrashReportMoverServiceName = "com.apple.crashreportmover"
CrashReportCopyMobileServiceName = "com.apple.crashreportcopymobile"
)
func NewCrashReportMoverClient(innerConn InnerConn) *CrashReportMoverClient {
return &CrashReportMoverClient{
newServicePacketClient(innerConn),
}
}
type CrashReportMoverClient struct {
client *servicePacketClient
}
func (c *CrashReportMoverClient) InnerConn() InnerConn {
return c.client.innerConn
}

View File

@@ -1,39 +0,0 @@
package libimobiledevice
const (
DiagnosticsRelayServiceName = "com.apple.mobile.diagnostics_relay"
)
type DiagnosticsRelayBasicRequest struct {
Request string `plist:"Request"`
Label string `plist:"Label"`
}
func NewDiagnosticsRelayClient(innerConn InnerConn) *DiagnosticsRelayClient {
return &DiagnosticsRelayClient{
newServicePacketClient(innerConn),
}
}
type DiagnosticsRelayClient struct {
client *servicePacketClient
}
func (c *DiagnosticsRelayClient) InnerConn() InnerConn {
return c.client.innerConn
}
func (c *DiagnosticsRelayClient) NewBasicRequest(relayType string) *DiagnosticsRelayBasicRequest {
return &DiagnosticsRelayBasicRequest{
Request: relayType,
Label: BundleID,
}
}
func (c *DiagnosticsRelayClient) NewXmlPacket(req interface{}) (Packet, error) {
return c.client.NewXmlPacket(req)
}
func (c *DiagnosticsRelayClient) SendPacket(pkt Packet) (err error) {
return c.client.SendPacket(pkt)
}

View File

@@ -1,56 +0,0 @@
package libimobiledevice
const HouseArrestServiceName = "com.apple.mobile.house_arrest"
const (
CommandTypeVendDocuments CommandType = "VendDocuments"
CommandTypeVendContainer CommandType = "VendContainer"
)
func NewHouseArrestClient(innerConn InnerConn) *HouseArrestClient {
return &HouseArrestClient{
newServicePacketClient(innerConn),
}
}
type HouseArrestClient struct {
client *servicePacketClient
}
func (c *HouseArrestClient) NewBasicRequest(cmdType CommandType, bundleID string) *HouseArrestBasicRequest {
return &HouseArrestBasicRequest{
Command: cmdType,
Identifier: bundleID,
}
}
func (c *HouseArrestClient) NewDocumentsRequest(bundleID string) *HouseArrestBasicRequest {
return c.NewBasicRequest(CommandTypeVendDocuments, bundleID)
}
func (c *HouseArrestClient) NewContainerRequest(bundleID string) *HouseArrestBasicRequest {
return c.NewBasicRequest(CommandTypeVendContainer, bundleID)
}
func (c *HouseArrestClient) NewXmlPacket(req interface{}) (Packet, error) {
return c.client.NewXmlPacket(req)
}
func (c *HouseArrestClient) SendPacket(pkt Packet) (err error) {
return c.client.SendPacket(pkt)
}
func (c *HouseArrestClient) ReceivePacket() (respPkt Packet, err error) {
return c.client.ReceivePacket()
}
func (c *HouseArrestClient) InnerConn() InnerConn {
return c.client.innerConn
}
type (
HouseArrestBasicRequest struct {
Command CommandType `plist:"Command"`
Identifier string `plist:"Identifier"`
}
)

View File

@@ -1,107 +0,0 @@
package libimobiledevice
import (
"errors"
"fmt"
"io"
"strings"
)
const ImageMounterServiceName = "com.apple.mobile.mobile_image_mounter"
var ErrDeviceLocked = errors.New("device locked")
type CommandType string
const (
CommandTypeLookupImage CommandType = "LookupImage"
CommandTypeReceiveBytes CommandType = "ReceiveBytes"
CommandTypeMountImage CommandType = "MountImage"
)
func NewImageMounterClient(innerConn InnerConn) *ImageMounterClient {
return &ImageMounterClient{
client: newServicePacketClient(innerConn),
}
}
type ImageMounterClient struct {
client *servicePacketClient
}
func (c *ImageMounterClient) NewBasicRequest(cmdType CommandType, imgType string) *ImageMounterBasicRequest {
return &ImageMounterBasicRequest{
Command: cmdType,
ImageType: imgType,
}
}
func (c *ImageMounterClient) NewReceiveBytesRequest(imgType string, imgSize uint32, imgSignature []byte) *ImageMounterReceiveBytesRequest {
return &ImageMounterReceiveBytesRequest{
ImageMounterBasicRequest: *c.NewBasicRequest(CommandTypeReceiveBytes, imgType),
ImageSize: imgSize,
ImageSignature: imgSignature,
}
}
func (c *ImageMounterClient) NewMountImageRequest(imgType, imgPath string, imgSignature []byte) *ImageMounterMountImageRequest {
return &ImageMounterMountImageRequest{
ImageMounterBasicRequest: *c.NewBasicRequest(CommandTypeMountImage, imgType),
ImagePath: imgPath,
ImageSignature: imgSignature,
}
}
func (c *ImageMounterClient) NewXmlPacket(req interface{}) (Packet, error) {
return c.client.NewXmlPacket(req)
}
func (c *ImageMounterClient) SendPacket(pkt Packet) (err error) {
return c.client.SendPacket(pkt)
}
func (c *ImageMounterClient) ReceivePacket() (respPkt Packet, err error) {
respPkt, err = c.client.ReceivePacket()
if err != nil {
if strings.Contains(err.Error(), io.EOF.Error()) {
return nil, ErrDeviceLocked
}
}
return
}
func (c *ImageMounterClient) SendDmg(data []byte) (err error) {
debugLog(fmt.Sprintf("--> ...DmgData...\n"))
return c.client.innerConn.Write(data)
}
type (
ImageMounterBasicRequest struct {
Command CommandType `plist:"Command"`
ImageType string `plist:"ImageType"`
}
ImageMounterReceiveBytesRequest struct {
ImageMounterBasicRequest
ImageSignature []byte `plist:"ImageSignature"`
ImageSize uint32 `plist:"ImageSize"`
}
ImageMounterMountImageRequest struct {
ImageMounterBasicRequest
ImagePath string `plist:"ImagePath"`
ImageSignature []byte `plist:"ImageSignature"`
}
)
type (
ImageMounterBasicResponse struct {
LockdownBasicResponse
Status string `plist:"Status"`
}
ImageMounterLookupImageResponse struct {
ImageMounterBasicResponse
ImageSignature [][]byte `plist:"ImageSignature"`
}
)

View File

@@ -1,119 +0,0 @@
package libimobiledevice
const InstallationProxyServiceName = "com.apple.mobile.installation_proxy"
const (
CommandTypeBrowse CommandType = "Browse"
CommandTypeLookup CommandType = "Lookup"
CommandTypeInstall CommandType = "Install"
CommandTypeUninstall CommandType = "Uninstall"
)
type ApplicationType string
const (
ApplicationTypeSystem ApplicationType = "System"
ApplicationTypeUser ApplicationType = "User"
ApplicationTypeInternal ApplicationType = "internal"
ApplicationTypeAny ApplicationType = "Any"
)
func NewInstallationProxyClient(innerConn InnerConn) *InstallationProxyClient {
return &InstallationProxyClient{
client: newServicePacketClient(innerConn),
}
}
type InstallationProxyClient struct {
client *servicePacketClient
}
func (c *InstallationProxyClient) NewBasicRequest(cmdType CommandType, opt *InstallationProxyOption) *InstallationProxyBasicRequest {
req := &InstallationProxyBasicRequest{Command: cmdType}
if opt != nil {
req.ClientOptions = opt
}
return req
}
func (c *InstallationProxyClient) NewInstallRequest(bundleID, packagePath string) *InstallationProxyInstallRequest {
opt := &InstallationProxyOption{
BundleID: bundleID,
}
req := &InstallationProxyInstallRequest{
Command: CommandTypeInstall,
ClientOptions: opt,
PackagePath: packagePath,
}
return req
}
func (c *InstallationProxyClient) NewUninstallRequest(bundleID string) *InstallationProxyUninstallRequest {
req := &InstallationProxyUninstallRequest{
Command: CommandTypeUninstall,
BundleID: bundleID,
}
return req
}
func (c *InstallationProxyClient) NewXmlPacket(req interface{}) (Packet, error) {
return c.client.NewXmlPacket(req)
}
func (c *InstallationProxyClient) SendPacket(pkt Packet) (err error) {
return c.client.SendPacket(pkt)
}
func (c *InstallationProxyClient) ReceivePacket() (respPkt Packet, err error) {
return c.client.ReceivePacket()
}
type InstallationProxyOption struct {
ApplicationType ApplicationType `plist:"ApplicationType,omitempty"`
ReturnAttributes []string `plist:"ReturnAttributes,omitempty"`
MetaData bool `plist:"com.apple.mobile_installation.metadata,omitempty"`
BundleIDs []string `plist:"BundleIDs,omitempty"` // for Lookup
BundleID string `plist:"CFBundleIdentifier,omitempty"` // for Install
}
type (
InstallationProxyBasicRequest struct {
Command CommandType `plist:"Command"`
ClientOptions *InstallationProxyOption `plist:"ClientOptions,omitempty"`
}
InstallationProxyInstallRequest struct {
Command CommandType `plist:"Command"`
ClientOptions *InstallationProxyOption `plist:"ClientOptions"`
PackagePath string `plist:"PackagePath"`
}
InstallationProxyUninstallRequest struct {
Command CommandType `plist:"Command"`
BundleID string `plist:"ApplicationIdentifier"`
}
)
type (
InstallationProxyBasicResponse struct {
Status string `plist:"Status"`
}
InstallationProxyLookupResponse struct {
InstallationProxyBasicResponse
LookupResult interface{} `plist:"LookupResult"`
}
InstallationProxyBrowseResponse struct {
InstallationProxyBasicResponse
CurrentAmount int `plist:"CurrentAmount"`
CurrentIndex int `plist:"CurrentIndex"`
CurrentList []interface{} `plist:"CurrentList"`
}
InstallationProxyInstallResponse struct {
InstallationProxyBasicResponse
Error string `plist:"Error"`
ErrorDescription string `plist:"ErrorDescription"`
}
)

View File

@@ -1,41 +0,0 @@
package libimobiledevice
const (
InstrumentsServiceName = "com.apple.instruments.remoteserver"
InstrumentsSecureProxyServiceName = "com.apple.instruments.remoteserver.DVTSecureSocketProxy"
)
func NewInstrumentsClient(innerConn InnerConn) *InstrumentsClient {
return &InstrumentsClient{
client: newDtxMessageClient(innerConn),
}
}
type InstrumentsClient struct {
client *dtxMessageClient
}
func (c *InstrumentsClient) NotifyOfPublishedCapabilities() (publishedChannels map[string]int32, err error) {
return c.client.Connection()
}
func (c *InstrumentsClient) RequestChannel(channel string) (id uint32, err error) {
return c.client.MakeChannel(channel)
}
func (c *InstrumentsClient) Invoke(selector string, args *AuxBuffer, channelCode uint32, expectsReply bool) (result *DTXMessageResult, err error) {
var msgID uint32
if msgID, err = c.client.SendDTXMessage(selector, args.Bytes(), channelCode, expectsReply); err != nil {
return nil, err
}
if expectsReply {
if result, err = c.client.GetResult(msgID); err != nil {
return nil, err
}
}
return
}
func (c *InstrumentsClient) RegisterCallback(obj string, cb func(m DTXMessageResult)) {
c.client.RegisterCallback(obj, cb)
}

View File

@@ -1,260 +0,0 @@
package libimobiledevice
import (
"reflect"
"strconv"
"time"
"howett.net/plist"
)
const nsNull = "$null"
func newKeyedArchiver() *KeyedArchiver {
return &KeyedArchiver{
Archiver: "NSKeyedArchiver",
Version: 100000,
}
}
type KeyedArchiver struct {
Archiver string `plist:"$archiver"`
Objects []interface{} `plist:"$objects"`
Top ArchiverRoot `plist:"$top"`
Version int `plist:"$version"`
}
func (ka *KeyedArchiver) UID() plist.UID {
return plist.UID(len(ka.Objects))
}
type ArchiverRoot struct {
Root plist.UID `plist:"root"`
}
type ArchiverClasses struct {
Classes []string `plist:"$classes"`
ClassName string `plist:"$classname"`
}
var (
NSMutableDictionaryClass = &ArchiverClasses{
Classes: []string{"NSMutableDictionary", "NSDictionary", "NSObject"},
ClassName: "NSMutableDictionary",
}
NSDictionaryClass = &ArchiverClasses{
Classes: []string{"NSDictionary", "NSObject"},
ClassName: "NSDictionary",
}
NSMutableArrayClass = &ArchiverClasses{
Classes: []string{"NSMutableArray", "NSArray", "NSObject"},
ClassName: "NSMutableArray",
}
NSArrayClass = &ArchiverClasses{
Classes: []string{"NSArray", "NSObject"},
ClassName: "NSArray",
}
NSMutableDataClass = &ArchiverClasses{
Classes: []string{"NSMutableArray", "NSArray", "NSObject"},
ClassName: "NSMutableArray",
}
NSDataClass = &ArchiverClasses{
Classes: []string{"NSData", "NSObject"},
ClassName: "NSData",
}
NSDateClass = &ArchiverClasses{
Classes: []string{"NSDate", "NSObject"},
ClassName: "NSDate",
}
NSErrorClass = &ArchiverClasses{
Classes: []string{"NSError", "NSObject"},
ClassName: "NSError",
}
)
type NSObject struct {
Class plist.UID `plist:"$class"`
}
type NSArray struct {
NSObject
Values []plist.UID `plist:"NS.objects"`
}
type NSDictionary struct {
NSArray
Keys []plist.UID `plist:"NS.keys"`
}
type NSData struct {
NSObject
Data []byte `plist:"NS.data"`
}
type NSError struct {
NSCode int
NSDomain string
NSUserInfo interface{}
}
type NSKeyedArchiver struct {
objRefVal []interface{}
objRef map[interface{}]plist.UID
}
func NewNSKeyedArchiver() *NSKeyedArchiver {
return &NSKeyedArchiver{
objRef: make(map[interface{}]plist.UID),
}
}
func (ka *NSKeyedArchiver) id(v interface{}) plist.UID {
var ref plist.UID
if id, ok := ka.objRef[v]; !ok {
ref = plist.UID(len(ka.objRef))
ka.objRefVal = append(ka.objRefVal, v)
ka.objRef[v] = ref
} else {
ref = id
}
return ref
}
func (ka *NSKeyedArchiver) flushToStruct(root *KeyedArchiver) {
for i := 0; i < len(ka.objRefVal); i++ {
val := ka.objRefVal[i]
vt := reflect.ValueOf(val)
if vt.Kind() == reflect.Ptr {
val = vt.Elem().Interface()
}
root.Objects = append(root.Objects, val)
}
}
func (ka *NSKeyedArchiver) clear() {
ka.objRef = make(map[interface{}]plist.UID)
ka.objRefVal = []interface{}{}
}
type XCTestConfiguration struct {
Contents map[string]interface{}
}
func (ka *NSKeyedArchiver) Marshal(obj interface{}) ([]byte, error) {
val := reflect.ValueOf(obj)
typ := val.Type()
root := newKeyedArchiver()
var tmpTop plist.UID
ka.id(nsNull)
switch typ.Kind() {
case reflect.Map:
m := &NSDictionary{}
m.Class = ka.id(NSDictionaryClass)
keys := val.MapKeys()
for _, v := range keys {
m.Keys = append(m.Keys, ka.id(v.Interface()))
m.Values = append(m.Values, ka.id(val.MapIndex(v).Interface()))
}
tmpTop = ka.id(m)
case reflect.Slice, reflect.Array:
if typ.Elem().Kind() == reflect.Uint8 {
d := &NSData{}
d.Class = ka.id(NSDataClass)
var w []byte
for i := 0; i < val.Len(); i++ {
w = append(w, uint8(val.Index(i).Uint()))
}
d.Data = w
}
a := &NSArray{}
a.Class = ka.id(NSArrayClass)
for i := 0; i < val.Len(); i++ {
a.Values = append(a.Values, ka.id(val.Index(i).Interface()))
}
tmpTop = ka.id(a)
case reflect.String:
tmpTop = ka.id(obj)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
tmpTop = ka.id(obj)
}
root.Top.Root = tmpTop
ka.flushToStruct(root)
ka.clear()
return plist.Marshal(root, plist.BinaryFormat)
}
func (ka *NSKeyedArchiver) convertValue(v interface{}) interface{} {
if m, ok := v.(map[string]interface{}); ok {
className := ka.objRefVal[m["$class"].(plist.UID)].(map[string]interface{})["$classname"]
switch className {
case NSMutableDictionaryClass.Classes[0], NSDictionaryClass.Classes[0]:
ret := make(map[string]interface{})
keys := m["NS.keys"].([]interface{})
values := m["NS.objects"].([]interface{})
for i := 0; i < len(keys); i++ {
var keyValue string
key := ka.objRefVal[keys[i].(plist.UID)]
switch key.(type) {
case uint64:
keyValue = strconv.Itoa(int(key.(uint64)))
break
default:
keyValue = key.(string)
}
val := ka.convertValue(ka.objRefVal[values[i].(plist.UID)])
ret[keyValue] = val
}
return ret
case NSMutableArrayClass.Classes[0], NSArrayClass.Classes[0]:
ret := make([]interface{}, 0)
values := m["NS.objects"].([]interface{})
for i := 0; i < len(values); i++ {
ret = append(ret, ka.convertValue(values[i]))
}
return ret
case NSMutableDataClass.Classes[0], NSDataClass.Classes[0]:
return m["NS.data"].([]byte)
case NSDateClass.Classes[0]:
return time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC).
Add(time.Duration(m["NS.time"].(float64)) * time.Second)
case NSErrorClass.Classes[0]:
err := &NSError{}
err.NSCode = int(m["NSCode"].(uint64))
err.NSDomain = ka.objRefVal[m["NSDomain"].(plist.UID)].(string)
err.NSUserInfo = ka.convertValue(ka.objRefVal[m["NSUserInfo"].(plist.UID)])
return *err
}
} else if uid, ok := v.(plist.UID); ok {
return ka.convertValue(ka.objRefVal[uid])
}
return v
}
func (ka *NSKeyedArchiver) Unmarshal(b []byte) (interface{}, error) {
archiver := new(KeyedArchiver)
if _, err := plist.Unmarshal(b, archiver); err != nil {
return nil, err
}
for _, v := range archiver.Objects {
ka.objRefVal = append(ka.objRefVal, v)
}
ret := ka.convertValue(ka.objRefVal[archiver.Top.Root])
ka.clear()
return ret, nil
}

View File

@@ -1,28 +0,0 @@
package libimobiledevice
import (
"bytes"
"log"
)
type Packet interface {
Pack() ([]byte, error)
Unpack(buffer *bytes.Buffer) (Packet, error)
Unmarshal(v interface{}) error
String() string
}
var debugFlag = false
// SetDebug sets debug mode
func SetDebug(debug bool) {
debugFlag = debug
}
func debugLog(msg string) {
if !debugFlag {
return
}
log.Printf("[%s-debug] %s\n", ProgramName, msg)
}

View File

@@ -1,183 +0,0 @@
package libimobiledevice
const ProtocolVersion = "2"
const LockdownPort = 62078
type RequestType string
const (
RequestTypeQueryType RequestType = "QueryType"
RequestTypeSetValue RequestType = "SetValue"
RequestTypeGetValue RequestType = "GetValue"
RequestTypePair RequestType = "Pair"
RequestTypeEnterRecovery RequestType = "EnterRecovery"
RequestTypeStartSession RequestType = "StartSession"
RequestTypeStopSession RequestType = "StopSession"
RequestTypeStartService RequestType = "StartService"
)
type LockdownType struct {
Type string `plist:"Type"`
}
func NewLockdownClient(innerConn InnerConn) *LockdownClient {
return &LockdownClient{
client: newServicePacketClient(innerConn),
}
}
type LockdownClient struct {
client *servicePacketClient
}
func (c *LockdownClient) NewBasicRequest(reqType RequestType) *LockdownBasicRequest {
return &LockdownBasicRequest{
Label: BundleID,
ProtocolVersion: ProtocolVersion,
Request: reqType,
}
}
func (c *LockdownClient) NewGetValueRequest(domain, key string) *LockdownValueRequest {
return &LockdownValueRequest{
LockdownBasicRequest: *c.NewBasicRequest(RequestTypeGetValue),
Domain: domain,
Key: key,
}
}
func (c *LockdownClient) NewSetValueRequest(domain, key string, value interface{}) *LockdownValueRequest {
return &LockdownValueRequest{
LockdownBasicRequest: *c.NewBasicRequest(RequestTypeSetValue),
Domain: domain,
Key: key,
Value: value,
}
}
func (c *LockdownClient) NewEnterRecoveryRequest() *LockdownBasicRequest {
return c.NewBasicRequest(RequestTypeEnterRecovery)
}
func (c *LockdownClient) NewPairRequest(pairRecord *PairRecord) *LockdownPairRequest {
return &LockdownPairRequest{
LockdownBasicRequest: *c.NewBasicRequest(RequestTypePair),
PairRecord: pairRecord,
PairingOptions: map[string]interface{}{
"ExtendedPairingErrors": true,
},
}
}
func (c *LockdownClient) NewStartSessionRequest(buid, hostID string) *LockdownStartSessionRequest {
return &LockdownStartSessionRequest{
LockdownBasicRequest: *c.NewBasicRequest(RequestTypeStartSession),
SystemBUID: buid,
HostID: hostID,
}
}
func (c *LockdownClient) NewStopSessionRequest(sessionID string) *LockdownStopSessionRequest {
return &LockdownStopSessionRequest{
LockdownBasicRequest: *c.NewBasicRequest(RequestTypeStopSession),
SessionID: sessionID,
}
}
func (c *LockdownClient) NewStartServiceRequest(service string) *LockdownStartServiceRequest {
return &LockdownStartServiceRequest{
LockdownBasicRequest: *c.NewBasicRequest(RequestTypeStartService),
Service: service,
}
}
func (c *LockdownClient) NewXmlPacket(req interface{}) (Packet, error) {
return c.client.NewXmlPacket(req)
}
func (c *LockdownClient) SendPacket(pkt Packet) (err error) {
return c.client.SendPacket(pkt)
}
func (c *LockdownClient) ReceivePacket() (respPkt Packet, err error) {
return c.client.ReceivePacket()
}
func (c *LockdownClient) EnableSSL(version []int, pairRecord *PairRecord) (err error) {
return c.client.innerConn.Handshake(version, pairRecord)
}
type (
LockdownBasicRequest struct {
Label string `plist:"Label"`
ProtocolVersion string `plist:"ProtocolVersion"`
Request RequestType `plist:"Request"`
}
LockdownValueRequest struct {
LockdownBasicRequest
Domain string `plist:"Domain,omitempty"`
Key string `plist:"Key,omitempty"`
Value interface{} `plist:"Value,omitempty"`
}
LockdownPairRequest struct {
LockdownBasicRequest
PairRecord *PairRecord `plist:"PairRecord"`
PairingOptions map[string]interface{} `plist:"PairingOptions"`
}
LockdownStartSessionRequest struct {
LockdownBasicRequest
SystemBUID string `plist:"SystemBUID"`
HostID string `plist:"HostID"`
}
LockdownStopSessionRequest struct {
LockdownBasicRequest
SessionID string `plist:"SessionID"`
}
LockdownStartServiceRequest struct {
LockdownBasicRequest
Service string `plist:"Service"`
EscrowBag []byte `plist:"EscrowBag,omitempty"`
}
)
type (
LockdownBasicResponse struct {
Request string `plist:"Request"`
Error string `plist:"Error"`
}
LockdownTypeResponse struct {
LockdownBasicResponse
Type string `plist:"Type"`
}
LockdownValueResponse struct {
LockdownBasicResponse
Key string `plist:"Key"`
Value interface{} `plist:"Value"`
}
LockdownPairResponse struct {
LockdownBasicResponse
EscrowBag []byte `plist:"EscrowBag"`
}
LockdownStartSessionResponse struct {
LockdownBasicResponse
EnableSessionSSL bool `plist:"EnableSessionSSL"`
SessionID string `plist:"SessionID"`
}
LockdownStartServiceResponse struct {
LockdownBasicResponse
EnableServiceSSL bool `plist:"EnableServiceSSL"`
Port int `plist:"Port"`
Service string `plist:"Service"`
}
)

View File

@@ -1,86 +0,0 @@
package libimobiledevice
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
)
var afcHeader = []byte{0x43, 0x46, 0x41, 0x36, 0x4C, 0x50, 0x41, 0x41}
var _ Packet = (*afcPacket)(nil)
type afcPacket struct {
entireLen uint64
thisLen uint64
packetNum uint64
operation uint64
}
func (p *afcPacket) Pack() ([]byte, error) {
buf := new(bytes.Buffer)
buf.Write(afcHeader)
b := make([]byte, 8)
binary.LittleEndian.PutUint64(b, p.entireLen)
buf.Write(b)
binary.LittleEndian.PutUint64(b, p.thisLen)
buf.Write(b)
binary.LittleEndian.PutUint64(b, p.packetNum)
buf.Write(b)
binary.LittleEndian.PutUint64(b, p.operation)
buf.Write(b)
return buf.Bytes(), nil
}
func (p *afcPacket) Unpack(buffer *bytes.Buffer) (Packet, error) {
return p.unpack(buffer)
}
func (p *afcPacket) unpack(buffer *bytes.Buffer) (*afcPacket, error) {
magic := make([]byte, 8)
if _, err := buffer.Read(magic); err != nil {
return nil, fmt.Errorf("afc packet unpack: %w", err)
}
if bytes.Compare(magic, afcHeader) != 0 {
return nil, errors.New("afc packet unpack: header not match")
}
respPkt := new(afcPacket)
if err := binary.Read(buffer, binary.LittleEndian, &respPkt.entireLen); err != nil {
return nil, fmt.Errorf("afc packet unpack: %w", err)
}
if err := binary.Read(buffer, binary.LittleEndian, &respPkt.thisLen); err != nil {
return nil, fmt.Errorf("afc packet unpack: %w", err)
}
if err := binary.Read(buffer, binary.LittleEndian, &respPkt.packetNum); err != nil {
return nil, fmt.Errorf("afc packet unpack: %w", err)
}
if err := binary.Read(buffer, binary.LittleEndian, &respPkt.operation); err != nil {
return nil, fmt.Errorf("afc packet unpack: %w", err)
}
return respPkt, nil
}
func (p *afcPacket) Unmarshal(v interface{}) error {
// switch msg := v.(type) {
// case *AfcMessage:
// // msg.EntireLen = p.entireLen
// // msg.ThisLen = p.thisLen
// // msg.PacketNum = p.packetNum
// msg.Operation = p.operation
// default:
// return errors.New("the type of the method parameter must be '*AfcMessage'")
// }
// return nil
panic("never use (afcPacket)")
}
func (p *afcPacket) String() string {
return fmt.Sprintf(
"EntireLen: %d, ThisLen: %d, PacketNum: %d, Operation: %X\n",
p.entireLen, p.thisLen, p.packetNum, p.operation,
)
}

View File

@@ -1,203 +0,0 @@
package libimobiledevice
import (
"bytes"
"encoding/binary"
"encoding/hex"
"fmt"
)
var (
_ Packet = (*dtxMessagePayloadPacket)(nil)
_ Packet = (*dtxMessageHeaderPacket)(nil)
_ Packet = (*dtxMessagePacket)(nil)
)
type dtxMessagePayloadPacket struct {
Flags uint32
AuxiliaryLength uint32
TotalLength uint64
}
func (p *dtxMessagePayloadPacket) Pack() ([]byte, error) {
buf := new(bytes.Buffer)
b := make([]byte, 4)
binary.LittleEndian.PutUint32(b, p.Flags)
buf.Write(b)
binary.LittleEndian.PutUint32(b, p.AuxiliaryLength)
buf.Write(b)
b = make([]byte, 8)
binary.LittleEndian.PutUint64(b, p.TotalLength)
buf.Write(b)
return buf.Bytes(), nil
}
func (p *dtxMessagePayloadPacket) Unpack(buffer *bytes.Buffer) (pkt Packet, err error) {
return p.unpack(buffer)
}
func (p *dtxMessagePayloadPacket) unpack(buffer *bytes.Buffer) (pkt *dtxMessagePayloadPacket, err error) {
respPkt := new(dtxMessagePayloadPacket)
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.Flags); err != nil {
return nil, fmt.Errorf("packet (DTXMessagePayloadHeader) unpack: %w", err)
}
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.AuxiliaryLength); err != nil {
return nil, fmt.Errorf("packet (DTXMessagePayloadHeader) unpack: %w", err)
}
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.TotalLength); err != nil {
return nil, fmt.Errorf("packet (DTXMessagePayloadHeader) unpack: %w", err)
}
return respPkt, nil
}
func (p *dtxMessagePayloadPacket) Unmarshal(v interface{}) error {
panic("never use (dtxMessagePayloadHeader)")
}
func (p *dtxMessagePayloadPacket) String() string {
return fmt.Sprintf("DTXMessagePayloadHeader Flags: %d, AuxiliaryLength: %d, TotalLength: %d\n",
p.Flags, p.AuxiliaryLength, p.TotalLength,
)
}
type dtxMessageHeaderPacket struct {
Magic uint32
CB uint32
FragmentId uint16
FragmentCount uint16
Length uint32
Identifier uint32
ConversationIndex uint32
ChannelCode uint32
ExpectsReply uint32
}
func (p *dtxMessageHeaderPacket) Pack() ([]byte, error) {
buf := new(bytes.Buffer)
b := make([]byte, 4)
binary.LittleEndian.PutUint32(b, p.Magic)
buf.Write(b)
binary.LittleEndian.PutUint32(b, p.CB)
buf.Write(b)
b = make([]byte, 2)
binary.LittleEndian.PutUint16(b, p.FragmentId)
buf.Write(b)
binary.LittleEndian.PutUint16(b, p.FragmentCount)
buf.Write(b)
b = make([]byte, 4)
binary.LittleEndian.PutUint32(b, p.Length)
buf.Write(b)
binary.LittleEndian.PutUint32(b, p.Identifier)
buf.Write(b)
binary.LittleEndian.PutUint32(b, p.ConversationIndex)
buf.Write(b)
binary.LittleEndian.PutUint32(b, p.ChannelCode)
buf.Write(b)
binary.LittleEndian.PutUint32(b, p.ExpectsReply)
buf.Write(b)
return buf.Bytes(), nil
}
func (p *dtxMessageHeaderPacket) Unpack(buffer *bytes.Buffer) (pkt Packet, err error) {
return p.unpack(buffer)
}
func (p *dtxMessageHeaderPacket) unpack(buffer *bytes.Buffer) (pkt *dtxMessageHeaderPacket, err error) {
respPkt := new(dtxMessageHeaderPacket)
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.Magic); err != nil {
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
}
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.CB); err != nil {
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
}
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.FragmentId); err != nil {
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
}
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.FragmentCount); err != nil {
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
}
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.Length); err != nil {
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
}
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.Identifier); err != nil {
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
}
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.ConversationIndex); err != nil {
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
}
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.ChannelCode); err != nil {
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
}
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.ExpectsReply); err != nil {
return nil, fmt.Errorf("packet (DTXMessageHeader) unpack: %w", err)
}
return respPkt, nil
}
func (p *dtxMessageHeaderPacket) Unmarshal(v interface{}) error {
panic("never use (DTXMessageHeader)")
}
func (p *dtxMessageHeaderPacket) String() string {
return fmt.Sprintf("DTXMessageHeader Magic: %d, CB: %d, FragmentId: %d, FragmentCount: %d\n"+
"Length: %d, Identifier: %d, ConversationIndex: %d, ChannelCode: %d, ExpectsReply: %d\n",
p.Magic, p.CB, p.FragmentId, p.FragmentCount,
p.Length, p.Identifier, p.ConversationIndex, p.ChannelCode, p.ExpectsReply,
)
}
type dtxMessagePacket struct {
Header *dtxMessageHeaderPacket
Payload *dtxMessagePayloadPacket
Aux []byte
Sel []byte
}
func (p *dtxMessagePacket) Pack() ([]byte, error) {
buf := new(bytes.Buffer)
raw, err := p.Header.Pack()
if err != nil {
return nil, fmt.Errorf("packet (DTXMessagePacket) pack: %w", err)
}
buf.Write(raw)
if raw, err = p.Payload.Pack(); err != nil {
return nil, fmt.Errorf("packet (DTXMessagePacket) pack: %w", err)
}
buf.Write(raw)
if p.Aux != nil || len(p.Aux) != 0 {
buf.Write(p.Aux)
}
if p.Sel != nil || len(p.Sel) != 0 {
buf.Write(p.Sel)
}
return buf.Bytes(), nil
}
func (p *dtxMessagePacket) Unpack(buffer *bytes.Buffer) (Packet, error) {
panic("implement me")
}
func (p *dtxMessagePacket) Unmarshal(v interface{}) error {
panic("never use (DTXMessagePacket)")
}
func (p *dtxMessagePacket) String() string {
return fmt.Sprintf(
"DTXMessagePacket %s\n%s\n"+
"%s\n%s\n",
p.Header.String(), p.Payload.String(),
// p.Aux, p.Sel,
hex.Dump(p.Aux), hex.Dump(p.Sel),
)
}

View File

@@ -1,53 +0,0 @@
package libimobiledevice
import (
"bytes"
"encoding/binary"
"fmt"
"strconv"
)
var _ Packet = (*locationPacket)(nil)
type locationPacket struct {
lon float64
lat float64
}
func (l *locationPacket) Pack() ([]byte, error) {
buf := new(bytes.Buffer)
if err := binary.Write(buf, binary.BigEndian, uint32(0)); err != nil {
return nil, fmt.Errorf("packet (location) pack: %w", err)
}
latS := []byte(strconv.FormatFloat(l.lat, 'E', -1, 64))
if err := binary.Write(buf, binary.BigEndian, uint32(len(latS))); err != nil {
return nil, fmt.Errorf("packet (location) pack: %w", err)
}
if err := binary.Write(buf, binary.BigEndian, latS); err != nil {
return nil, fmt.Errorf("packet (location) pack: %w", err)
}
lonS := []byte(strconv.FormatFloat(l.lon, 'E', -1, 64))
if err := binary.Write(buf, binary.BigEndian, uint32(len(lonS))); err != nil {
return nil, fmt.Errorf("packet (location) pack: %w", err)
}
if err := binary.Write(buf, binary.BigEndian, lonS); err != nil {
return nil, fmt.Errorf("packet (location) pack: %w", err)
}
return buf.Bytes(), nil
}
func (l *locationPacket) Unpack(buffer *bytes.Buffer) (Packet, error) {
panic("never use (location)")
}
func (l *locationPacket) Unmarshal(v interface{}) error {
panic("never use (location)")
}
func (l *locationPacket) String() string {
return fmt.Sprintf("lon: %v, lat: %v\n", l.lon, l.lat)
}

View File

@@ -1,47 +0,0 @@
package libimobiledevice
import (
"bytes"
"encoding/binary"
"fmt"
"howett.net/plist"
)
var _ Packet = (*servicePacket)(nil)
type servicePacket struct {
length uint32
body []byte
}
func (p *servicePacket) Pack() ([]byte, error) {
buf := new(bytes.Buffer)
b := make([]byte, 4)
binary.BigEndian.PutUint32(b, p.length)
buf.Write(b)
buf.Write(p.body)
return buf.Bytes(), nil
}
func (p *servicePacket) Unpack(buffer *bytes.Buffer) (pkt Packet, err error) {
respPkt := new(servicePacket)
if err = binary.Read(buffer, binary.BigEndian, &respPkt.length); err != nil {
return nil, fmt.Errorf("packet (service) unpack: %w", err)
}
respPkt.body = buffer.Bytes()
return respPkt, nil
}
func (p *servicePacket) Unmarshal(v interface{}) (err error) {
_, err = plist.Unmarshal(p.body, v)
return
}
func (p *servicePacket) String() string {
return fmt.Sprintf("Length: %d\n%s", p.length, p.body)
}

View File

@@ -1,112 +0,0 @@
package libimobiledevice
import (
"bytes"
"encoding/binary"
"fmt"
"howett.net/plist"
)
var _ Packet = (*packet)(nil)
type packet struct {
length uint32
version ProtoVersion
msgType ProtoMessageType
tag uint32
body []byte
}
func (p *packet) Pack() ([]byte, error) {
buf := new(bytes.Buffer)
b := make([]byte, 4)
binary.LittleEndian.PutUint32(b, p.length)
buf.Write(b)
binary.LittleEndian.PutUint32(b, uint32(p.version))
buf.Write(b)
binary.LittleEndian.PutUint32(b, uint32(p.msgType))
buf.Write(b)
binary.LittleEndian.PutUint32(b, p.tag)
buf.Write(b)
buf.Write(p.body)
return buf.Bytes(), nil
}
func (p *packet) Unpack(buffer *bytes.Buffer) (pkt Packet, err error) {
respPkt := new(packet)
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.length); err != nil {
return nil, fmt.Errorf("packet unpack: %w", err)
}
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.version); err != nil {
return nil, fmt.Errorf("packet unpack: %w", err)
}
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.msgType); err != nil {
return nil, fmt.Errorf("packet unpack: %w", err)
}
if err = binary.Read(buffer, binary.LittleEndian, &respPkt.tag); err != nil {
return nil, fmt.Errorf("packet unpack: %w", err)
}
respPkt.body = buffer.Bytes()
return respPkt, nil
}
func (p *packet) Unmarshal(v interface{}) (err error) {
_, err = plist.Unmarshal(p.body, v)
return
}
func (p *packet) String() string {
return fmt.Sprintf(
"Length: %d, Version: %d, Type: %d, Tag: %d\n%s",
p.length, p.version, p.msgType, p.tag, p.body,
)
}
type (
BasicRequest struct {
MessageType MessageType `plist:"MessageType"`
BundleID string `plist:"BundleID,omitempty"`
ProgramName string `plist:"ProgName,omitempty"`
ClientVersionString string `plist:"ClientVersionString"`
LibUSBMuxVersion uint `plist:"kLibUSBMuxVersion"`
}
ConnectRequest struct {
BasicRequest
DeviceID int `plist:"DeviceID"`
PortNumber int `plist:"PortNumber"`
}
ReadPairRecordRequest struct {
BasicRequest
PairRecordID string `plist:"PairRecordID"`
}
SavePairRecordRequest struct {
BasicRequest
PairRecordID string `plist:"PairRecordID"`
PairRecordData []byte `plist:"PairRecordData"`
DeviceID int `plist:"DeviceID"`
}
DeletePairRecordRequest struct {
BasicRequest
PairRecordID string `plist:"PairRecordID"`
}
)
type PairRecord struct {
DeviceCertificate []byte `plist:"DeviceCertificate"`
EscrowBag []byte `plist:"EscrowBag,omitempty"`
HostCertificate []byte `plist:"HostCertificate"`
HostPrivateKey []byte `plist:"HostPrivateKey,omitempty"`
HostID string `plist:"HostID"`
RootCertificate []byte `plist:"RootCertificate"`
RootPrivateKey []byte `plist:"RootPrivateKey,omitempty"`
SystemBUID string `plist:"SystemBUID"`
WiFiMACAddress string `plist:"WiFiMACAddress,omitempty"`
}

View File

@@ -1,152 +0,0 @@
package libimobiledevice
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"strings"
"time"
"github.com/lunixbochs/struc"
)
const PcapdServiceName = "com.apple.pcapd"
func filterPacket(pid int, procName string) func(*IOSPacketHeader) bool {
return func(iph *IOSPacketHeader) bool {
if pid > 0 {
return iph.Pid == int32(pid) ||
iph.Pid2 == int32(pid)
}
if procName != "" {
return strings.HasPrefix(iph.ProcName, procName) ||
strings.HasPrefix(iph.ProcName2, procName)
}
return true
}
}
func NewPcapdClient(innerConn InnerConn, targetPID int, targetProcName string) *PcapdClient {
return &PcapdClient{
filter: filterPacket(targetPID, targetProcName),
client: newServicePacketClient(innerConn),
}
}
type PcapdClient struct {
filter func(*IOSPacketHeader) bool
client *servicePacketClient
}
func (c *PcapdClient) ReceivePacket() (respPkt Packet, err error) {
var bufLen []byte
if bufLen, err = c.client.innerConn.Read(4); err != nil {
return nil, fmt.Errorf("lockdown(Pcapd) receive: %w", err)
}
lenPkg := binary.BigEndian.Uint32(bufLen)
buffer := bytes.NewBuffer([]byte{})
buffer.Write(bufLen)
var buf []byte
if buf, err = c.client.innerConn.Read(int(lenPkg)); err != nil {
return nil, fmt.Errorf("lockdown(Pcapd) receive: %w", err)
}
buffer.Write(buf)
if respPkt, err = new(servicePacket).Unpack(buffer); err != nil {
return nil, fmt.Errorf("lockdown(Pcapd) receive: %w", err)
}
debugLog(fmt.Sprintf("<-- %s\n", respPkt))
return
}
const (
PacketHeaderSize = uint32(95)
)
// ref: https://github.com/danielpaulus/go-ios/blob/fc943b9d236571f9775f5c593e3d49bb5bd67afd/ios/pcap/pcap.go#L27
type IOSPacketHeader struct {
HdrSize uint32 `struc:"uint32,big"`
Version uint8 `struc:"uint8,big"`
PacketSize uint32 `struc:"uint32,big"`
Type uint8 `struc:"uint8,big"`
Unit uint16 `struc:"uint16,big"`
IO uint8 `struc:"uint8,big"`
ProtocolFamily uint32 `struc:"uint32,big"`
FramePreLength uint32 `struc:"uint32,big"`
FramePstLength uint32 `struc:"uint32,big"`
IFName string `struc:"[16]byte"`
Pid int32 `struc:"int32,little"`
ProcName string `struc:"[17]byte"`
Unknown uint32 `struc:"uint32,little"`
Pid2 int32 `struc:"int32,little"`
ProcName2 string `struc:"[17]byte"`
Unknown2 [8]byte `struc:"[8]byte"`
}
func (c *PcapdClient) GetPacket(buf []byte) ([]byte, error) {
iph := IOSPacketHeader{}
preader := bytes.NewReader(buf)
_ = struc.Unpack(preader, &iph)
if c.filter != nil {
if !c.filter(&iph) {
return nil, nil
}
}
// support ios 15 beta4
if iph.HdrSize > PacketHeaderSize {
buf := make([]byte, iph.HdrSize-PacketHeaderSize)
_, err := io.ReadFull(preader, buf)
if err != nil {
return []byte{}, err
}
}
packet, err := io.ReadAll(preader)
if err != nil {
return packet, err
}
if iph.FramePreLength == 0 {
ext := []byte{
0xbe, 0xfe, 0xbe, 0xfe, 0xbe, 0xfe, 0xbe, 0xfe,
0xbe, 0xfe, 0xbe, 0xfe, 0x08, 0x00,
}
return append(ext, packet...), nil
}
return packet, nil
}
type PcaprecHdrS struct {
TsSec int `struc:"uint32,little"` /* timestamp seconds */
TsUsec int `struc:"uint32,little"` /* timestamp microseconds */
InclLen int `struc:"uint32,little"` /* number of octets of packet saved in file */
OrigLen int `struc:"uint32,little"` /* actual length of packet */
}
func (c *PcapdClient) CreatePacket(packet []byte) ([]byte, error) {
now := time.Now()
phs := &PcaprecHdrS{
int(now.Unix()),
int(now.UnixNano()/1e3 - now.Unix()*1e6),
len(packet),
len(packet),
}
var buf bytes.Buffer
err := struc.Pack(&buf, phs)
if err != nil {
return nil, err
}
buf.Write(packet)
return buf.Bytes(), nil
}
func (c *PcapdClient) Close() {
c.client.innerConn.Close()
}

View File

@@ -1,52 +0,0 @@
package libimobiledevice
import (
"bytes"
"encoding/binary"
"fmt"
)
const ScreenshotServiceName = "com.apple.mobile.screenshotr"
func NewScreenshotClient(innerConn InnerConn) *ScreenshotClient {
return &ScreenshotClient{
client: newServicePacketClient(innerConn),
}
}
type ScreenshotClient struct {
client *servicePacketClient
}
func (c *ScreenshotClient) NewBinaryPacket(req interface{}) (Packet, error) {
return c.client.NewBinaryPacket(req)
}
func (c *ScreenshotClient) SendPacket(pkt Packet) (err error) {
return c.client.SendPacket(pkt)
}
func (c *ScreenshotClient) ReceivePacket() (respPkt Packet, err error) {
var bufLen []byte
if bufLen, err = c.client.innerConn.Read(4); err != nil {
return nil, fmt.Errorf("lockdown(Screenshot) receive: %w", err)
}
lenPkg := binary.BigEndian.Uint32(bufLen)
buffer := bytes.NewBuffer([]byte{})
buffer.Write(bufLen)
var buf []byte
if buf, err = c.client.innerConn.Read(int(lenPkg)); err != nil {
return nil, fmt.Errorf("lockdown(Screenshot) receive: %w", err)
}
buffer.Write(buf)
if respPkt, err = new(servicePacket).Unpack(buffer); err != nil {
return nil, fmt.Errorf("lockdown(Screenshot) receive: %w", err)
}
debugLog(fmt.Sprintf("<-- %s\n", respPkt))
return
}

View File

@@ -1,128 +0,0 @@
package libimobiledevice
import (
"fmt"
"math"
"strings"
)
const SimulateLocationServiceName = "com.apple.dt.simulatelocation"
type CoordinateSystem string
const (
CoordinateSystemWGS84 CoordinateSystem = "WGS84"
CoordinateSystemBD09 CoordinateSystem = "BD09"
CoordinateSystemGCJ02 CoordinateSystem = "GCJ02"
)
func NewSimulateLocationClient(innerConn InnerConn) *SimulateLocationClient {
return &SimulateLocationClient{
client: newServicePacketClient(innerConn),
}
}
type SimulateLocationClient struct {
client *servicePacketClient
}
func (c *SimulateLocationClient) NewLocationPacket(lon, lat float64, coordinateSystem CoordinateSystem) Packet {
switch CoordinateSystem(strings.ToUpper(string(coordinateSystem))) {
case CoordinateSystemGCJ02:
lon, lat = gcj02ToWGS84(lon, lat)
case CoordinateSystemBD09:
lon, lat = bd09ToWGS84(lon, lat)
case CoordinateSystemWGS84:
_, _ = lon, lat
default:
_, _ = lon, lat
}
pkt := new(locationPacket)
pkt.lon = lon
pkt.lat = lat
return pkt
}
func (c *SimulateLocationClient) SendPacket(pkt Packet) (err error) {
return c.client.SendPacket(pkt)
}
// Recover try to revert back
func (c *SimulateLocationClient) Recover() error {
data := []byte{0x00, 0x00, 0x00, 0x01}
debugLog(fmt.Sprintf("--> %+v\n", data))
return c.client.innerConn.Write(data)
}
const (
xPi = math.Pi * 3000.0 / 180.0
offset = 0.00669342162296594323
axis = 6378245.0
)
func isOutOfChina(lon, lat float64) bool {
return !(lon > 73.66 && lon < 135.05 && lat > 3.86 && lat < 53.55)
}
func delta(lon, lat float64) (float64, float64) {
dLat := transformLat(lon-105.0, lat-35.0)
dLon := transformLng(lon-105.0, lat-35.0)
radLat := lat / 180.0 * math.Pi
magic := math.Sin(radLat)
magic = 1 - offset*magic*magic
sqrtMagic := math.Sqrt(magic)
dLat = (dLat * 180.0) / ((axis * (1 - offset)) / (magic * sqrtMagic) * math.Pi)
dLon = (dLon * 180.0) / (axis / sqrtMagic * math.Cos(radLat) * math.Pi)
mgLat := lat + dLat
mgLon := lon + dLon
return mgLon, mgLat
}
func transformLat(lon, lat float64) float64 {
ret := -100.0 + 2.0*lon + 3.0*lat + 0.2*lat*lat + 0.1*lon*lat + 0.2*math.Sqrt(math.Abs(lon))
ret += (20.0*math.Sin(6.0*lon*math.Pi) + 20.0*math.Sin(2.0*lon*math.Pi)) * 2.0 / 3.0
ret += (20.0*math.Sin(lat*math.Pi) + 40.0*math.Sin(lat/3.0*math.Pi)) * 2.0 / 3.0
ret += (160.0*math.Sin(lat/12.0*math.Pi) + 320*math.Sin(lat*math.Pi/30.0)) * 2.0 / 3.0
return ret
}
func transformLng(lon, lat float64) float64 {
ret := 300.0 + lon + 2.0*lat + 0.1*lon*lon + 0.1*lon*lat + 0.1*math.Sqrt(math.Abs(lon))
ret += (20.0*math.Sin(6.0*lon*math.Pi) + 20.0*math.Sin(2.0*lon*math.Pi)) * 2.0 / 3.0
ret += (20.0*math.Sin(lon*math.Pi) + 40.0*math.Sin(lon/3.0*math.Pi)) * 2.0 / 3.0
ret += (150.0*math.Sin(lon/12.0*math.Pi) + 300.0*math.Sin(lon/30.0*math.Pi)) * 2.0 / 3.0
return ret
}
func gcj02ToWGS84(lon, lat float64) (float64, float64) {
if isOutOfChina(lon, lat) {
return lon, lat
}
mgLon, mgLat := delta(lon, lat)
return lon*2 - mgLon, lat*2 - mgLat
}
func bd09ToGCJ02(lon, lat float64) (float64, float64) {
x := lon - 0.0065
y := lat - 0.006
z := math.Sqrt(x*x+y*y) - 0.00002*math.Sin(y*xPi)
theta := math.Atan2(y, x) - 0.000003*math.Cos(x*xPi)
gLon := z * math.Cos(theta)
gLat := z * math.Sin(theta)
return gLon, gLat
}
func bd09ToWGS84(lon, lat float64) (float64, float64) {
lon, lat = bd09ToGCJ02(lon, lat)
return gcj02ToWGS84(lon, lat)
}

View File

@@ -1,53 +0,0 @@
package libimobiledevice
type IconPNGDataResponse struct {
PNGData []byte `plist:"pngData"`
}
type InterfaceOrientationResponse struct {
Orientation OrientationState `plist:"interfaceOrientation"`
}
type OrientationState int64
const (
Unknown OrientationState = iota
Portrait
PortraitUpsideDown
LandscapeRight
LandscapeLeft
)
const (
SpringBoardServiceName = "com.apple.springboardservices"
)
func NewSpringBoardClient(innerConn InnerConn) *SpringBoardClient {
return &SpringBoardClient{
newServicePacketClient(innerConn),
}
}
type SpringBoardClient struct {
client *servicePacketClient
}
func (c *SpringBoardClient) InnerConn() InnerConn {
return c.client.innerConn
}
func (c *SpringBoardClient) NewXmlPacket(req interface{}) (Packet, error) {
return c.client.NewXmlPacket(req)
}
func (c *SpringBoardClient) SendPacket(pkt Packet) (err error) {
return c.client.SendPacket(pkt)
}
func (c *SpringBoardClient) ReceivePacket() (respPkt Packet, err error) {
return c.client.ReceivePacket()
}
func (c *SpringBoardClient) NewBinaryPacket(req interface{}) (Packet, error) {
return c.client.NewBinaryPacket(req)
}

View File

@@ -1,21 +0,0 @@
package libimobiledevice
const SyslogRelayServiceName = "com.apple.syslog_relay"
func NewSyslogRelayClient(innerConn InnerConn) *SyslogRelayClient {
return &SyslogRelayClient{
newServicePacketClient(innerConn),
}
}
type SyslogRelayClient struct {
client *servicePacketClient
}
func (c *SyslogRelayClient) InnerConn() InnerConn {
return c.client.innerConn
}
func (c *SyslogRelayClient) Close() {
c.client.innerConn.Close()
}

View File

@@ -1,45 +0,0 @@
package libimobiledevice
const (
TestmanagerdSecureServiceName = "com.apple.testmanagerd.lockdown.secure"
TestmanagerdServiceName = "com.apple.testmanagerd.lockdown"
)
func NewTestmanagerdClient(innerConn InnerConn) *TestmanagerdClient {
return &TestmanagerdClient{
client: newDtxMessageClient(innerConn),
}
}
type TestmanagerdClient struct {
client *dtxMessageClient
}
func (t *TestmanagerdClient) Connection() (publishedChannels map[string]int32, err error) {
return t.client.Connection()
}
func (t *TestmanagerdClient) MakeChannel(channel string) (id uint32, err error) {
return t.client.MakeChannel(channel)
}
func (t *TestmanagerdClient) Invoke(selector string, args *AuxBuffer, channelCode uint32, expectsReply bool) (result *DTXMessageResult, err error) {
var msgID uint32
if msgID, err = t.client.SendDTXMessage(selector, args.Bytes(), channelCode, expectsReply); err != nil {
return nil, err
}
if expectsReply {
if result, err = t.client.GetResult(msgID); err != nil {
return nil, err
}
}
return
}
func (t *TestmanagerdClient) RegisterCallback(obj string, cb func(m DTXMessageResult)) {
t.client.RegisterCallback(obj, cb)
}
func (t *TestmanagerdClient) Close() {
t.client.Close()
}

View File

@@ -1,414 +0,0 @@
package libimobiledevice
import (
"bytes"
"crypto/tls"
"encoding/binary"
"fmt"
"net"
"runtime"
"strconv"
"time"
"howett.net/plist"
)
var DefaultDeadlineTimeout = 30 * time.Second
const (
BundleID = "electricbubble.libimobiledevice"
ProgramName = "libimobiledevice"
ClientVersion = "libimobiledevice-beta"
LibUSBMuxVersion = 3
)
type ReplyCode uint64
const (
ReplyCodeOK ReplyCode = iota
ReplyCodeBadCommand
ReplyCodeBadDevice
ReplyCodeConnectionRefused
_ // ignore `4`
_ // ignore `5`
ReplyCodeBadVersion
)
func (rc ReplyCode) String() string {
switch rc {
case ReplyCodeOK:
return "ok"
case ReplyCodeBadCommand:
return "bad command"
case ReplyCodeBadDevice:
return "bad device"
case ReplyCodeConnectionRefused:
return "connection refused"
case ReplyCodeBadVersion:
return "bad version"
default:
return "unknown reply code: " + strconv.Itoa(int(rc))
}
}
type ProtoVersion uint32
// proto_version == 1
// construct message plist
// else `0`? res == `RESULT_BADVERSION`
// binary packet
const (
ProtoVersionBinary ProtoVersion = iota
ProtoVersionPlist
)
type ProtoMessageType uint32
const (
_ ProtoMessageType = iota
ProtoMessageTypeResult
ProtoMessageTypeConnect
ProtoMessageTypeListen
ProtoMessageTypeDeviceAdd
ProtoMessageTypeDeviceRemove
ProtoMessageTypeDevicePaired
_ // `7`
ProtoMessageTypePlist
)
type MessageType string
const (
MessageTypeResult MessageType = "Result"
MessageTypeConnect MessageType = "Connect"
MessageTypeListen MessageType = "Listen"
MessageTypeDeviceAdd MessageType = "Attached"
MessageTypeDeviceRemove MessageType = "Detached"
MessageTypeReadBUID MessageType = "ReadBUID"
MessageTypeReadPairRecord MessageType = "ReadPairRecord"
MessageTypeSavePairRecord MessageType = "SavePairRecord"
MessageTypeDeletePairRecord MessageType = "DeletePairRecord"
MessageTypeDeviceList MessageType = "ListDevices"
)
type BaseDevice struct {
MessageType MessageType `plist:"MessageType"`
DeviceID int `plist:"DeviceID"`
Properties DeviceProperties `plist:"Properties"`
}
type DeviceProperties struct {
DeviceID int `plist:"DeviceID"`
ConnectionType string `plist:"ConnectionType"`
ConnectionSpeed int `plist:"ConnectionSpeed"`
ProductID int `plist:"ProductID"`
LocationID int `plist:"LocationID"`
SerialNumber string `plist:"SerialNumber"`
UDID string `plist:"UDID"`
USBSerialNumber string `plist:"USBSerialNumber"`
EscapedFullServiceName string `plist:"EscapedFullServiceName"`
InterfaceIndex int `plist:"InterfaceIndex"`
NetworkAddress []byte `plist:"NetworkAddress"`
}
func NewUsbmuxClient(timeout ...time.Duration) (c *UsbmuxClient, err error) {
if len(timeout) == 0 {
timeout = []time.Duration{DefaultDeadlineTimeout}
}
c = &UsbmuxClient{version: ProtoVersionPlist}
var conn net.Conn
if conn, err = rawDial(timeout[0]); err != nil {
return nil, fmt.Errorf("usbmux connect: %w", err)
}
c.innerConn = newInnerConn(conn, timeout[0])
return
}
type UsbmuxClient struct {
innerConn InnerConn
version ProtoVersion
tag uint32
}
func (c *UsbmuxClient) NewBasicRequest(msgType MessageType) *BasicRequest {
return &BasicRequest{
MessageType: msgType,
BundleID: BundleID,
ProgramName: ProgramName,
ClientVersionString: ClientVersion,
LibUSBMuxVersion: LibUSBMuxVersion,
}
}
func (c *UsbmuxClient) NewConnectRequest(deviceID, port int) *ConnectRequest {
return &ConnectRequest{
BasicRequest: *c.NewBasicRequest(MessageTypeConnect),
DeviceID: deviceID,
PortNumber: ((port << 8) & 0xFF00) | (port >> 8),
}
}
func (c *UsbmuxClient) NewReadPairRecordRequest(udid string) *ReadPairRecordRequest {
return &ReadPairRecordRequest{
BasicRequest: *c.NewBasicRequest(MessageTypeReadPairRecord),
PairRecordID: udid,
}
}
func (c *UsbmuxClient) NewSavePairRecordRequest(udid string, deviceID int, data []byte) *SavePairRecordRequest {
return &SavePairRecordRequest{
BasicRequest: *c.NewBasicRequest(MessageTypeSavePairRecord),
PairRecordID: udid,
PairRecordData: data,
DeviceID: deviceID,
}
}
func (c *UsbmuxClient) NewDeletePairRecordRequest(udid string) *DeletePairRecordRequest {
return &DeletePairRecordRequest{
BasicRequest: *c.NewBasicRequest(MessageTypeDeletePairRecord),
PairRecordID: udid,
}
}
func (c *UsbmuxClient) NewPacket(protoMsgType ProtoMessageType) Packet {
return c.newPacket(protoMsgType)
}
func (c *UsbmuxClient) newPacket(protoMsgType ProtoMessageType) *packet {
c.tag++
pkt := &packet{
version: c.version,
msgType: protoMsgType,
tag: c.tag,
}
return pkt
}
func (c *UsbmuxClient) NewPlistPacket(req interface{}) (Packet, error) {
pkt := c.newPacket(ProtoMessageTypePlist)
if buf, err := plist.Marshal(req, plist.XMLFormat); err != nil {
return nil, fmt.Errorf("plist packet marshal: %w", err)
} else {
pkt.body = buf
}
pkt.length = uint32(len(pkt.body) + 4*4)
return pkt, nil
}
func (c *UsbmuxClient) SendPacket(pkt Packet) (err error) {
var raw []byte
if raw, err = pkt.Pack(); err != nil {
return fmt.Errorf("usbmux send: %w", err)
}
// debugLog(fmt.Sprintf("--> Length: %d, Version: %d, Type: %d, Tag: %d\n%s\n", pkt.Length(), pkt.Version(), pkt.Type(), pkt.Tag(), pkt.Body()))
debugLog(fmt.Sprintf("--> %s\n", pkt))
return c.innerConn.Write(raw)
}
func (c *UsbmuxClient) ReceivePacket() (respPkt Packet, err error) {
var bufLen []byte
if bufLen, err = c.innerConn.Read(4); err != nil {
return nil, fmt.Errorf("usbmux receive: %w", err)
}
lenPkg := binary.LittleEndian.Uint32(bufLen)
buffer := bytes.NewBuffer([]byte{})
buffer.Write(bufLen)
var buf []byte
if buf, err = c.innerConn.Read(int(lenPkg - 4)); err != nil {
return nil, fmt.Errorf("usbmux receive: %w", err)
}
buffer.Write(buf)
if respPkt, err = new(packet).Unpack(buffer); err != nil {
return nil, fmt.Errorf("usbmux receive: %w", err)
}
// debugLog(fmt.Sprintf("<-- Length: %d, Version: %d, Type: %d, Tag: %d\n%s\n", respPkt.Length(), respPkt.Version(), respPkt.Type(), respPkt.Tag(), respPkt.Body()))
debugLog(fmt.Sprintf("<-- %s\n", respPkt))
reply := struct {
MessageType string `plist:"MessageType"`
Number ReplyCode `plist:"Number"`
}{}
if err = respPkt.Unmarshal(&reply); err != nil {
return nil, fmt.Errorf("usbmux receive: %w", err)
}
if reply.Number != ReplyCodeOK {
return nil, fmt.Errorf("usbmux receive: %s", reply.Number.String())
}
return
}
func (c *UsbmuxClient) Close() {
c.innerConn.Close()
}
func (c *UsbmuxClient) RawConn() net.Conn {
return c.innerConn.RawConn()
}
func (c *UsbmuxClient) InnerConn() InnerConn {
return c.innerConn
}
func rawDial(timeout time.Duration) (net.Conn, error) {
dialer := net.Dialer{
Timeout: timeout,
}
var network, address string
switch runtime.GOOS {
case "darwin", "android", "linux":
network, address = "unix", "/var/run/usbmuxd"
case "windows":
network, address = "tcp", "127.0.0.1:27015"
default:
return nil, fmt.Errorf("raw dial: unsupported system: %s", runtime.GOOS)
}
return dialer.Dial(network, address)
}
type InnerConn interface {
Write(data []byte) (err error)
Read(length int) (data []byte, err error)
Handshake(version []int, pairRecord *PairRecord) (err error)
DismissSSL() (err error)
Close()
RawConn() net.Conn
Timeout(time.Duration)
}
func newInnerConn(conn net.Conn, timeout time.Duration) InnerConn {
return &safeConn{
conn: conn,
timeout: timeout,
}
}
type safeConn struct {
conn net.Conn
sslConn *tls.Conn
timeout time.Duration
}
func (c *safeConn) Write(data []byte) (err error) {
conn := c.RawConn()
if c.timeout <= 0 {
err = conn.SetWriteDeadline(time.Time{})
} else {
err = conn.SetWriteDeadline(time.Now().Add(c.timeout))
}
if err != nil {
return err
}
for totalSent := 0; totalSent < len(data); {
var sent int
if sent, err = conn.Write(data[totalSent:]); err != nil {
return err
}
if sent == 0 {
return err
}
totalSent += sent
}
return
}
func (c *safeConn) Read(length int) (data []byte, err error) {
conn := c.RawConn()
if c.timeout <= 0 {
err = conn.SetReadDeadline(time.Time{})
} else {
err = conn.SetReadDeadline(time.Now().Add(c.timeout))
}
if err != nil {
return nil, err
}
data = make([]byte, 0, length)
for len(data) < length {
buf := make([]byte, length-len(data))
_n, _err := 0, error(nil)
if _n, _err = conn.Read(buf); _err != nil && _n == 0 {
return nil, _err
}
data = append(data, buf[:_n]...)
}
return
}
func (c *safeConn) Handshake(version []int, pairRecord *PairRecord) (err error) {
minVersion := uint16(tls.VersionTLS11)
maxVersion := uint16(tls.VersionTLS11)
if version[0] > 10 {
minVersion = tls.VersionTLS11
maxVersion = tls.VersionTLS13
}
cert, err := tls.X509KeyPair(pairRecord.RootCertificate, pairRecord.RootPrivateKey)
if err != nil {
return err
}
config := &tls.Config{
Certificates: []tls.Certificate{cert},
InsecureSkipVerify: true,
MinVersion: minVersion,
MaxVersion: maxVersion,
}
c.sslConn = tls.Client(c.conn, config)
if err = c.sslConn.Handshake(); err != nil {
return err
}
return
}
func (c *safeConn) DismissSSL() (err error) {
if c.sslConn != nil {
// err = c.sslConn.CloseWrite()
// if err = c.sslConn.CloseWrite(); err != nil {
// return err
// }
c.sslConn = nil
}
return
}
func (c *safeConn) Close() {
if c.sslConn != nil {
if err := c.sslConn.Close(); err != nil {
debugLog(fmt.Sprintf("close: %s", err))
}
}
if c.conn != nil {
if err := c.conn.Close(); err != nil {
debugLog(fmt.Sprintf("close: %s", err))
}
}
}
// RawConn `sslConn` first
func (c *safeConn) RawConn() net.Conn {
if c.sslConn != nil {
return c.sslConn
}
return c.conn
}
func (c *safeConn) Timeout(duration time.Duration) {
c.timeout = duration
}

View File

@@ -1,37 +0,0 @@
package nskeyedarchiver
import "howett.net/plist"
type NSArray struct {
internal []interface{}
}
func NewNSArray(value []interface{}) *NSArray {
return &NSArray{
internal: value,
}
}
func (ns *NSArray) archive(objects []interface{}) []interface{} {
objs := make([]interface{}, 0, len(ns.internal))
info := map[string]interface{}{}
objects = append(objects, info)
for _, v := range ns.internal {
var uid plist.UID
objects, uid = archive(objects, v)
objs = append(objs, uid)
}
info["NS.objects"] = objs
info["$class"] = plist.UID(len(objects))
cls := map[string]interface{}{
"$classname": "NSArray",
"$classes": []interface{}{"NSArray", "NSObject"},
}
objects = append(objects, cls)
return objects
}

View File

@@ -1,20 +0,0 @@
//go:build localtest
package nskeyedarchiver
import (
"fmt"
"testing"
)
func TestNSArray_archive(t *testing.T) {
objs := make([]interface{}, 0, 1)
value := []interface{}{
"a", 1,
"b", "2",
"c", false,
}
array := NewNSArray(value)
objects := array.archive(objs)
fmt.Println(objects)
}

View File

@@ -1,44 +0,0 @@
package nskeyedarchiver
import (
"howett.net/plist"
)
type NSDictionary struct {
internal map[string]interface{}
}
func NewNSDictionary(value map[string]interface{}) *NSDictionary {
return &NSDictionary{
internal: value,
}
}
func (ns *NSDictionary) archive(objects []interface{}) []interface{} {
keys := make([]interface{}, 0, len(ns.internal))
objs := make([]interface{}, 0, len(ns.internal))
info := map[string]interface{}{}
objects = append(objects, info)
for k, v := range ns.internal {
uid := plist.UID(len(objects))
keys = append(keys, uid)
objects = append(objects, k)
objects, uid = archive(objects, v)
objs = append(objs, uid)
}
info["NS.keys"] = keys
info["NS.objects"] = objs
info["$class"] = plist.UID(len(objects))
cls := map[string]interface{}{
"$classname": "NSDictionary",
"$classes": []interface{}{"NSDictionary", "NSObject"},
}
objects = append(objects, cls)
return objects
}

View File

@@ -1,20 +0,0 @@
//go:build localtest
package nskeyedarchiver
import (
"fmt"
"testing"
)
func TestNSDictionary_archive(t *testing.T) {
objs := make([]interface{}, 0, 1)
value := map[string]interface{}{
"a": 1,
"b": "2",
"c": true,
}
dict := NewNSDictionary(value)
objects := dict.archive(objs)
fmt.Println(objects)
}

View File

@@ -1,80 +0,0 @@
package nskeyedarchiver
import (
"reflect"
"howett.net/plist"
)
func Marshal(obj interface{}) (raw []byte, err error) {
objects := []interface{}{"$null"}
objects, _ = archive(objects, obj)
archiver := map[string]interface{}{
"$version": 100000,
"$archiver": "NSKeyedArchiver",
"$top": map[string]interface{}{"root": plist.UID(1)},
"$objects": objects,
}
// if len(format) == 0 {
// format = []int{plist.BinaryFormat}
// }
// return plist.Marshal(archiver, format[0])
return plist.Marshal(archiver, plist.BinaryFormat)
}
func archive(_objects []interface{}, _value interface{}) (objects []interface{}, uid plist.UID) {
val := reflect.ValueOf(_value)
typ := val.Type()
switch typ.Kind() {
case reflect.String,
reflect.Bool,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Uintptr:
uid = plist.UID(len(_objects))
objects = append(_objects, _value)
return
case reflect.Map:
uid = plist.UID(len(_objects))
vv := make(map[string]interface{})
keys := val.MapKeys()
for _, k := range keys {
vv[k.String()] = val.MapIndex(k).Interface()
}
objects = NewNSDictionary(vv).archive(_objects)
return
case reflect.Slice, reflect.Array:
uid = plist.UID(len(_objects))
vv := make([]interface{}, val.Len())
for i := 0; i < val.Len(); i++ {
vv[i] = val.Index(i).Interface()
}
objects = NewNSArray(vv).archive(_objects)
return
case reflect.Struct, reflect.Ptr:
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
val = val.Elem()
}
switch typ.Name() {
case "NSUUID":
uid = plist.UID(len(_objects))
objects = NewNSUUID(val.Field(0).Bytes()).archive(_objects)
return
case "NSURL":
uid = plist.UID(len(_objects))
objects = NewNSURL(val.Field(0).String()).archive(_objects)
return
case "XCTestConfiguration":
uid = plist.UID(len(_objects))
objects = newXCTestConfiguration(_value).archive(_objects)
return
}
}
return
}
// TODO unarchive

View File

@@ -1,41 +0,0 @@
//go:build localtest
package nskeyedarchiver
import (
"fmt"
"testing"
uuid "github.com/satori/go.uuid"
)
func TestMarshal(t *testing.T) {
// value := map[string]interface{}{
// "a": 1,
// "b": "2",
// "c": true,
// }
// value := []interface{}{
// "a", 1,
// "b", "2",
// "c", false,
// }
// value := NewNSUUID(uuid.NewV4().Bytes())
// value := NewNSURL("/tmp")
value := NewXCTestConfiguration(NewNSUUID(uuid.NewV4().Bytes()), NewNSURL("/tmp"), "", "")
raw, err := Marshal(value)
if err != nil {
t.Fatal(err)
}
for _, v := range raw {
fmt.Printf("%x", v)
}
fmt.Println()
// fmt.Println(raw)
}

View File

@@ -1,27 +0,0 @@
package nskeyedarchiver
import (
"howett.net/plist"
)
type NSNull struct{}
func NewNSNull() *NSNull {
return &NSNull{}
}
func (ns *NSNull) archive(objects []interface{}) []interface{} {
info := map[string]interface{}{}
objects = append(objects, info)
info["$class"] = plist.UID(len(objects))
cls := map[string]interface{}{
"$classname": "NSNull",
"$classes": []interface{}{"NSNull", "NSObject"},
}
objects = append(objects, cls)
return objects
}

View File

@@ -1,38 +0,0 @@
package nskeyedarchiver
import (
"fmt"
"howett.net/plist"
)
type NSURL struct {
internal string
}
func NewNSURL(path string) *NSURL {
return &NSURL{
internal: path,
}
}
func (ns *NSURL) archive(objects []interface{}) []interface{} {
info := map[string]interface{}{}
objects = append(objects, info)
uid := plist.UID(0)
info["NS.base"] = uid
objects, uid = archive(objects, fmt.Sprintf("file://%s", ns.internal))
info["NS.relative"] = uid
info["$class"] = plist.UID(len(objects))
cls := map[string]interface{}{
"$classname": "NSURL",
"$classes": []interface{}{"NSURL", "NSObject"},
}
objects = append(objects, cls)
return objects
}

View File

@@ -1,15 +0,0 @@
//go:build localtest
package nskeyedarchiver
import (
"fmt"
"testing"
)
func TestNSURL_archive(t *testing.T) {
objs := make([]interface{}, 0, 1)
nsurl := NewNSURL("/tmp")
objects := nsurl.archive(objs)
fmt.Println(objects)
}

View File

@@ -1,51 +0,0 @@
package nskeyedarchiver
import (
"encoding/hex"
"howett.net/plist"
)
type NSUUID struct {
internal []byte
}
func NewNSUUID(uuid []byte) *NSUUID {
return &NSUUID{
internal: uuid,
}
}
func (ns *NSUUID) archive(objects []interface{}) []interface{} {
info := map[string]interface{}{
"NS.uuidbytes": ns.internal,
}
objects = append(objects, info)
info["$class"] = plist.UID(len(objects))
cls := map[string]interface{}{
"$classname": "NSUUID",
"$classes": []interface{}{"NSUUID", "NSObject"},
}
objects = append(objects, cls)
return objects
}
func (ns *NSUUID) String() string {
buf := make([]byte, 36)
hex.Encode(buf[0:8], ns.internal[0:4])
buf[8] = '-'
hex.Encode(buf[9:13], ns.internal[4:6])
buf[13] = '-'
hex.Encode(buf[14:18], ns.internal[6:8])
buf[18] = '-'
hex.Encode(buf[19:23], ns.internal[8:10])
buf[23] = '-'
hex.Encode(buf[24:], ns.internal[10:])
return string(buf)
}

View File

@@ -1,17 +0,0 @@
//go:build localtest
package nskeyedarchiver
import (
"fmt"
"testing"
uuid "github.com/satori/go.uuid"
)
func TestNSUUID_archive(t *testing.T) {
objs := make([]interface{}, 0, 1)
nsuuid := NewNSUUID(uuid.NewV4().Bytes())
objects := nsuuid.archive(objs)
fmt.Println(objects)
}

View File

@@ -1,10 +0,0 @@
package nskeyedarchiver
type XCTCapabilities struct {
internal map[string]interface{}
}
func (caps *XCTCapabilities) archive(objects []interface{}) []interface{} {
// TODO caps
return nil
}

View File

@@ -1,89 +0,0 @@
package nskeyedarchiver
import (
"reflect"
"howett.net/plist"
)
type XCTestConfiguration struct {
internal map[string]interface{}
}
func newXCTestConfiguration(cfg interface{}) *XCTestConfiguration {
return cfg.(*XCTestConfiguration)
}
func NewXCTestConfiguration(nsuuid *NSUUID, nsurl *NSURL, targetBundleID, targetAppPath string) *XCTestConfiguration {
contents := map[string]interface{}{
"aggregateStatisticsBeforeCrash": map[string]interface{}{
"XCSuiteRecordsKey": map[string]interface{}{},
},
"automationFrameworkPath": "/Developer/Library/PrivateFrameworks/XCTAutomationSupport.framework",
"baselineFileRelativePath": nil,
"baselineFileURL": nil,
"defaultTestExecutionTimeAllowance": nil,
"disablePerformanceMetrics": false,
"emitOSLogs": false,
"formatVersion": 2,
"gatherLocalizableStringsData": false,
"initializeForUITesting": true,
"maximumTestExecutionTimeAllowance": nil,
"productModuleName": "WebDriverAgentRunner", // set to other value is also OK
"randomExecutionOrderingSeed": nil,
"reportActivities": true,
"reportResultsToIDE": true,
"systemAttachmentLifetime": 2,
"targetApplicationArguments": []interface{}{}, // maybe useless
"targetApplicationEnvironment": nil,
"targetApplicationPath": targetAppPath,
"testApplicationDependencies": map[string]interface{}{},
"testApplicationUserOverrides": nil,
"testBundleRelativePath": nil,
"testExecutionOrdering": 0,
"testTimeoutsEnabled": false,
"testsDrivenByIDE": false,
"testsMustRunOnMainThread": true,
"testsToRun": nil,
"testsToSkip": nil,
"treatMissingBaselinesAsFailures": false,
"userAttachmentLifetime": 1,
"testBundleURL": nsurl,
"sessionIdentifier": nsuuid,
"targetApplicationBundleID": targetBundleID,
// "targetApplicationBundleID": "",
}
return &XCTestConfiguration{internal: contents}
}
func (cfg *XCTestConfiguration) archive(objects []interface{}) []interface{} {
info := map[string]interface{}{}
objects = append(objects, info)
info["$class"] = plist.UID(len(objects))
cls := map[string]interface{}{
"$classname": "XCTestConfiguration",
"$classes": []interface{}{"XCTestConfiguration", "NSObject"},
}
objects = append(objects, cls)
for k, v := range cfg.internal {
val := reflect.ValueOf(v)
if !val.IsValid() {
info[k] = plist.UID(0)
continue
}
typ := val.Type()
if k != "formatVersion" && (typ.Kind() == reflect.Bool || typ.Kind() == reflect.Uintptr || typ.Kind() == reflect.Int) {
info[k] = v
} else {
var uid plist.UID
objects, uid = archive(objects, v)
info[k] = uid
}
}
return objects
}

View File

@@ -1,17 +0,0 @@
//go:build localtest
package nskeyedarchiver
import (
"fmt"
"testing"
uuid "github.com/satori/go.uuid"
)
func TestXCTestConfiguration_archive(t *testing.T) {
objs := make([]interface{}, 0, 1)
xcTestConfiguration := NewXCTestConfiguration(NewNSUUID(uuid.NewV4().Bytes()), NewNSURL("/tmp"), "", "")
objects := xcTestConfiguration.archive(objs)
fmt.Println(objects)
}

View File

@@ -1,119 +0,0 @@
package gidevice
import (
"bytes"
"errors"
"fmt"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
)
var _ Screenshot = (*screenshot)(nil)
func newScreenshot(client *libimobiledevice.ScreenshotClient) *screenshot {
return &screenshot{
client: client,
exchanged: false,
}
}
type screenshot struct {
client *libimobiledevice.ScreenshotClient
exchanged bool
}
func (s *screenshot) Take() (raw *bytes.Buffer, err error) {
if err = s.exchange(); err != nil {
return nil, err
}
// link service
req := []interface{}{
"DLMessageProcessMessage",
map[string]interface{}{
"MessageType": "ScreenShotRequest",
},
}
var pkt libimobiledevice.Packet
if pkt, err = s.client.NewBinaryPacket(req); err != nil {
return nil, err
}
if err = s.client.SendPacket(pkt); err != nil {
return nil, err
}
var respPkt libimobiledevice.Packet
if respPkt, err = s.client.ReceivePacket(); err != nil {
return nil, err
}
var resp []interface{}
if err = respPkt.Unmarshal(&resp); err != nil {
return nil, err
}
if resp[0].(string) != "DLMessageProcessMessage" {
return nil, fmt.Errorf("message device not ready %s %s", resp[3], resp[4])
}
raw = new(bytes.Buffer)
screen := resp[1].(map[string]interface{})
var data []byte
ok := false
if data, ok = screen["ScreenShotData"].([]byte); !ok {
return nil, errors.New("`ScreenShotData` not ready")
}
if _, err = raw.Write(data); err != nil {
return nil, err
}
return
}
func (s *screenshot) exchange() (err error) {
if s.exchanged {
return
}
var respPkt libimobiledevice.Packet
if respPkt, err = s.client.ReceivePacket(); err != nil {
return err
}
var resp []interface{}
if err = respPkt.Unmarshal(&resp); err != nil {
return err
}
req := []interface{}{
"DLMessageVersionExchange",
"DLVersionsOk",
resp[1],
}
var pkt libimobiledevice.Packet
if pkt, err = s.client.NewBinaryPacket(req); err != nil {
return err
}
if err = s.client.SendPacket(pkt); err != nil {
return err
}
if respPkt, err = s.client.ReceivePacket(); err != nil {
return err
}
if err = respPkt.Unmarshal(&resp); err != nil {
return err
}
if resp[3].(string) != "DLMessageDeviceReady" {
return fmt.Errorf("message device not ready %s", resp[3])
}
s.exchanged = true
return
}

View File

@@ -1,58 +0,0 @@
//go:build localtest
package gidevice
import (
"image"
"image/jpeg"
"image/png"
"os"
"testing"
)
var screenshotSrv Screenshot
func setupScreenshotSrv(t *testing.T) {
setupLockdownSrv(t)
var err error
if lockdownSrv, err = dev.lockdownService(); err != nil {
t.Fatal(err)
}
if screenshotSrv, err = lockdownSrv.ScreenshotService(); err != nil {
t.Fatal(err)
}
}
func Test_screenshot_Take(t *testing.T) {
setupScreenshotSrv(t)
// raw, err := dev.Screenshot()
raw, err := screenshotSrv.Take()
if err != nil {
t.Fatal(err)
}
_ = raw
img, format, err := image.Decode(raw)
if err != nil {
t.Fatal(err)
}
userHomeDir, _ := os.UserHomeDir()
file, err := os.Create(userHomeDir + "/Desktop/s1." + format)
if err != nil {
t.Fatal(err)
}
defer func() { _ = file.Close() }()
switch format {
case "png":
err = png.Encode(file, img)
case "jpeg":
err = jpeg.Encode(file, img, nil)
}
if err != nil {
t.Fatal(err)
}
t.Log(file.Name())
}

View File

@@ -1,27 +0,0 @@
package gidevice
import "github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
var _ SimulateLocation = (*simulateLocation)(nil)
func newSimulateLocation(client *libimobiledevice.SimulateLocationClient) *simulateLocation {
return &simulateLocation{
client: client,
}
}
type simulateLocation struct {
client *libimobiledevice.SimulateLocationClient
}
func (s *simulateLocation) Update(longitude float64, latitude float64, coordinateSystem ...CoordinateSystem) (err error) {
if len(coordinateSystem) == 0 {
coordinateSystem = []CoordinateSystem{CoordinateSystemWGS84}
}
pkt := s.client.NewLocationPacket(longitude, latitude, coordinateSystem[0])
return s.client.SendPacket(pkt)
}
func (s *simulateLocation) Recover() (err error) {
return s.client.Recover()
}

View File

@@ -1,50 +0,0 @@
//go:build localtest
package gidevice
import "testing"
var simulateLocationSrv SimulateLocation
func setupSimulateLocationSrv(t *testing.T) {
setupLockdownSrv(t)
var err error
if lockdownSrv, err = dev.lockdownService(); err != nil {
t.Fatal(err)
}
if simulateLocationSrv, err = lockdownSrv.SimulateLocationService(); err != nil {
t.Fatal(err)
}
}
func Test_simulateLocation_Update(t *testing.T) {
setupSimulateLocationSrv(t)
// https://api.map.baidu.com/lbsapi/getpoint/index.html
// if err := dev.SimulateLocationUpdate(116.024067, 40.362639, CoordinateSystemBD09); err != nil {
if err := simulateLocationSrv.Update(116.024067, 40.362639, CoordinateSystemBD09); err != nil {
t.Fatal(err)
}
// https://developer.amap.com/tools/picker
// https://lbs.qq.com/tool/getpoint/index.html
// if err := simulateLocationSrv.Update(120.116979,30.252876, CoordinateSystemGCJ02); err != nil {
// t.Fatal(err)
// }
if err := simulateLocationSrv.Update(121.499763, 31.239580); err != nil {
// if err := simulateLocationSrv.Update(121.499763, 31.239580, CoordinateSystemWGS84); err != nil {
t.Fatal(err)
}
}
func Test_simulateLocation_Recover(t *testing.T) {
setupSimulateLocationSrv(t)
// if err := dev.SimulateLocationRecover(); err != nil {
if err := simulateLocationSrv.Recover(); err != nil {
t.Fatal(err)
}
}

View File

@@ -1,68 +0,0 @@
package gidevice
import (
"bytes"
"fmt"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
)
func newSpringBoard(client *libimobiledevice.SpringBoardClient) *springboard {
return &springboard{
client: client,
}
}
type springboard struct {
client *libimobiledevice.SpringBoardClient
}
func (s springboard) GetIconPNGData(bundleId string) (raw *bytes.Buffer, err error) {
var pkt libimobiledevice.Packet
req := map[string]interface{}{
"command": "getIconPNGData",
"bundleId": bundleId,
}
if pkt, err = s.client.NewBinaryPacket(req); err != nil {
return
}
if err = s.client.SendPacket(pkt); err != nil {
return nil, err
}
var respPkt libimobiledevice.Packet
if respPkt, err = s.client.ReceivePacket(); err != nil {
return nil, err
}
var reply libimobiledevice.IconPNGDataResponse
raw = new(bytes.Buffer)
if err = respPkt.Unmarshal(&reply); err != nil {
return nil, fmt.Errorf("receive packet: %w", err)
}
if _, err = raw.Write(reply.PNGData); err != nil {
return nil, err
}
return
}
func (s springboard) GetInterfaceOrientation() (orientation libimobiledevice.OrientationState, err error) {
var pkt libimobiledevice.Packet
req := map[string]interface{}{
"command": "getInterfaceOrientation",
}
if pkt, err = s.client.NewBinaryPacket(req); err != nil {
return
}
if err = s.client.SendPacket(pkt); err != nil {
return 0, err
}
var respPkt libimobiledevice.Packet
if respPkt, err = s.client.ReceivePacket(); err != nil {
return 0, err
}
var reply libimobiledevice.InterfaceOrientationResponse
if err = respPkt.Unmarshal(&reply); err != nil {
return 0, fmt.Errorf("receive packet: %w", err)
}
orientation = reply.Orientation
return
}

View File

@@ -1,55 +0,0 @@
//go:build localtest
package gidevice
import (
"fmt"
"image"
"image/jpeg"
"image/png"
"os"
"testing"
)
var springBoardSrv SpringBoard
func setupSpringBoardSrv(t *testing.T) {
setupLockdownSrv(t)
var err error
if lockdownSrv, err = dev.lockdownService(); err != nil {
t.Fatal(err)
}
if springBoardSrv, err = lockdownSrv.SpringBoardService(); err != nil {
t.Fatal(err)
}
}
func Test_springBoard_GetIcon(t *testing.T) {
setupSpringBoardSrv(t)
raw, _ := springBoardSrv.GetIconPNGData("com.ss.iphone.ugc.Aweme")
img, format, err := image.Decode(raw)
if err != nil {
t.Fatal(err)
}
file, err := os.Create("./abc." + format)
if err != nil {
t.Fatal(err)
}
defer func() { _ = file.Close() }()
switch format {
case "png":
err = png.Encode(file, img)
case "jpeg":
err = jpeg.Encode(file, img, nil)
}
if err != nil {
t.Fatal(err)
}
}
func Test_springBoard_GetOrient(t *testing.T) {
setupSpringBoardSrv(t)
fmt.Println(springBoardSrv.GetInterfaceOrientation())
}

View File

@@ -1,85 +0,0 @@
package gidevice
import (
"bufio"
"fmt"
"io"
"strings"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
)
var _ SyslogRelay = (*syslogRelay)(nil)
func newSyslogRelay(client *libimobiledevice.SyslogRelayClient) *syslogRelay {
r := &syslogRelay{
client: client,
stop: make(chan bool),
isReading: false,
}
r.reader = bufio.NewReader(r.client.InnerConn().RawConn())
return r
}
type syslogRelay struct {
client *libimobiledevice.SyslogRelayClient
reader *bufio.Reader
stop chan bool
isReading bool
}
func (r *syslogRelay) Lines() <-chan string {
out := make(chan string)
r.isReading = true
go func() {
defer func() {
close(out)
r.isReading = false
}()
for {
select {
case <-r.stop:
return
default:
bs, err := r.readLine()
if err != nil {
if strings.Contains(err.Error(), io.EOF.Error()) {
return
}
debugLog(fmt.Sprintf("syslog: %s", err))
}
if len(bs) > 1 && bs[0] == 0 {
bs = bs[1:]
}
out <- string(bs)
}
}
}()
return out
}
func (r *syslogRelay) Stop() {
if r.isReading {
r.stop <- true
}
}
func (r *syslogRelay) readLine() ([]byte, error) {
var line []byte
for {
l, more, err := r.reader.ReadLine()
if err != nil {
return nil, err
}
if line == nil && !more {
return l, nil
}
line = append(line, l...)
if !more {
break
}
}
return line, nil
}

View File

@@ -1,50 +0,0 @@
package gidevice
import (
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
)
var _ Testmanagerd = (*testmanagerd)(nil)
func newTestmanagerd(client *libimobiledevice.TestmanagerdClient, iOSVersion []int) *testmanagerd {
return &testmanagerd{
client: client,
iOSVersion: iOSVersion,
}
}
type testmanagerd struct {
client *libimobiledevice.TestmanagerdClient
iOSVersion []int
}
func (t *testmanagerd) notifyOfPublishedCapabilities() (err error) {
_, err = t.client.Connection()
return
}
func (t *testmanagerd) requestChannel(channel string) (id uint32, err error) {
return t.client.MakeChannel(channel)
}
func (t *testmanagerd) newXCTestManagerDaemon() (xcTestManager XCTestManagerDaemon, err error) {
var channelCode uint32
if channelCode, err = t.requestChannel("dtxproxy:XCTestManager_IDEInterface:XCTestManager_DaemonConnectionInterface"); err != nil {
return nil, err
}
xcTestManager = newXcTestManagerDaemon(t, channelCode)
return
}
func (t *testmanagerd) invoke(selector string, args *libimobiledevice.AuxBuffer, channelCode uint32, expectsReply bool) (
result *libimobiledevice.DTXMessageResult, err error) {
return t.client.Invoke(selector, args, channelCode, expectsReply)
}
func (t *testmanagerd) registerCallback(obj string, cb func(m libimobiledevice.DTXMessageResult)) {
t.client.RegisterCallback(obj, cb)
}
func (t *testmanagerd) close() {
t.client.Close()
}

View File

@@ -1,153 +0,0 @@
package gidevice
import (
"context"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
)
var _ Usbmux = (*usbmux)(nil)
func NewUsbmux() (Usbmux, error) {
umClient, err := libimobiledevice.NewUsbmuxClient()
if err != nil {
return nil, err
}
return &usbmux{client: umClient}, nil
}
func newUsbmux(client *libimobiledevice.UsbmuxClient) *usbmux {
return &usbmux{client: client}
}
type usbmux struct {
client *libimobiledevice.UsbmuxClient
}
func (um *usbmux) Devices() (devices []Device, err error) {
var pkt libimobiledevice.Packet
if pkt, err = um.client.NewPlistPacket(
um.client.NewBasicRequest(libimobiledevice.MessageTypeDeviceList),
); err != nil {
return nil, err
}
if err = um.client.SendPacket(pkt); err != nil {
return nil, err
}
var respPkt libimobiledevice.Packet
if respPkt, err = um.client.ReceivePacket(); err != nil {
return nil, err
}
reply := struct {
DeviceList []libimobiledevice.BaseDevice `plist:"DeviceList"`
}{}
if err = respPkt.Unmarshal(&reply); err != nil {
return nil, err
}
devices = make([]Device, len(reply.DeviceList))
for i := range reply.DeviceList {
dev := reply.DeviceList[i]
devices[i] = newDevice(um.client, dev.Properties)
}
return
}
func (um *usbmux) ReadBUID() (buid string, err error) {
var pktReadBUID libimobiledevice.Packet
if pktReadBUID, err = um.client.NewPlistPacket(
um.client.NewBasicRequest(libimobiledevice.MessageTypeReadBUID),
); err != nil {
return "", err
}
if err = um.client.SendPacket(pktReadBUID); err != nil {
return "", err
}
respPkt, err := um.client.ReceivePacket()
if err != nil {
return "", err
}
reply := struct {
BUID string `plist:"BUID"`
}{}
if err = respPkt.Unmarshal(&reply); err != nil {
return "", err
}
buid = reply.BUID
return
}
func (um *usbmux) Listen(devNotifier chan Device) (context.CancelFunc, error) {
baseDevNotifier := make(chan libimobiledevice.BaseDevice)
ctx, cancelFunc, err := um.listen(baseDevNotifier)
go func(ctx context.Context) {
defer close(devNotifier)
for {
select {
case <-ctx.Done():
return
case baseDev := <-baseDevNotifier:
if baseDev.MessageType != libimobiledevice.MessageTypeDeviceAdd {
baseDev.Properties.DeviceID = baseDev.DeviceID
}
client, err := libimobiledevice.NewUsbmuxClient()
if err != nil {
continue
}
devNotifier <- newDevice(client, baseDev.Properties)
}
}
}(ctx)
return cancelFunc, err
}
func (um *usbmux) listen(devNotifier chan libimobiledevice.BaseDevice) (ctx context.Context, cancelFunc context.CancelFunc, err error) {
var pkt libimobiledevice.Packet
if pkt, err = um.client.NewPlistPacket(
um.client.NewBasicRequest(libimobiledevice.MessageTypeListen),
); err != nil {
return nil, nil, err
}
if err = um.client.SendPacket(pkt); err != nil {
return nil, nil, err
}
ctx, cancelFunc = context.WithCancel(context.Background())
go func(ctx context.Context) {
defer close(devNotifier)
for {
select {
case <-ctx.Done():
return
default:
var respPkt libimobiledevice.Packet
if respPkt, err = um.client.ReceivePacket(); err != nil {
break
}
var replyDevice libimobiledevice.BaseDevice
if err = respPkt.Unmarshal(&replyDevice); err != nil {
break
}
if replyDevice.MessageType == libimobiledevice.MessageTypeResult {
break
}
devNotifier <- replyDevice
}
}
}(ctx)
return ctx, cancelFunc, nil
}

View File

@@ -1,70 +0,0 @@
//go:build localtest
package gidevice
import (
"testing"
"time"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
)
var um Usbmux
func setupUsbmux(t *testing.T) {
var err error
um, err = NewUsbmux()
if err != nil {
t.Fatal(err)
}
}
func Test_usbmux_Devices(t *testing.T) {
setupUsbmux(t)
devices, err := um.Devices()
if err != nil {
t.Fatal(err)
}
for _, dev := range devices {
t.Log(dev.Properties().SerialNumber, dev.Properties().ProductID, dev.Properties().DeviceID)
}
}
func Test_usbmux_ReadBUID(t *testing.T) {
setupUsbmux(t)
buid, err := um.ReadBUID()
if err != nil {
t.Fatal(err)
}
t.Log(buid)
}
func Test_usbmux_Listen(t *testing.T) {
setupUsbmux(t)
devNotifier := make(chan Device)
cancelFunc, err := um.Listen(devNotifier)
if err != nil {
t.Fatal(err)
}
go func() {
time.Sleep(20 * time.Second)
cancelFunc()
}()
for dev := range devNotifier {
if dev.Properties().ConnectionType != "" {
t.Log(dev.Properties().SerialNumber, dev.Properties().ProductID, dev.Properties().DeviceID)
} else {
t.Log(libimobiledevice.MessageTypeDeviceRemove, dev.Properties().DeviceID)
}
}
time.Sleep(5 * time.Second)
t.Log("Done")
}

View File

@@ -1,153 +0,0 @@
package gidevice
import (
"fmt"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/libimobiledevice"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice/pkg/nskeyedarchiver"
)
var _ XCTestManagerDaemon = (*xcTestManagerDaemon)(nil)
func newXcTestManagerDaemon(testmanagerd Testmanagerd, channelCode uint32) *xcTestManagerDaemon {
return &xcTestManagerDaemon{
testmanagerd: testmanagerd,
channelCode: channelCode,
}
}
type xcTestManagerDaemon struct {
testmanagerd Testmanagerd
channelCode uint32
}
func (d *xcTestManagerDaemon) initiateControlSession(XcodeVersion uint64) (err error) {
args := libimobiledevice.NewAuxBuffer()
if err = args.AppendObject(XcodeVersion); err != nil {
return err
}
selector := "_IDE_initiateControlSessionWithProtocolVersion:"
var ret *libimobiledevice.DTXMessageResult
if ret, err = d.testmanagerd.invoke(selector, args, d.channelCode, true); err != nil {
return err
}
if nsErr, ok := ret.Obj.(libimobiledevice.NSError); ok {
return fmt.Errorf("%s", nsErr.NSUserInfo.(map[string]interface{})["NSLocalizedDescription"])
}
return
}
func (d *xcTestManagerDaemon) startExecutingTestPlan(XcodeVersion uint64) (err error) {
args := libimobiledevice.NewAuxBuffer()
if err = args.AppendObject(XcodeVersion); err != nil {
return err
}
selector := "_IDE_startExecutingTestPlanWithProtocolVersion:"
if _, err = d.testmanagerd.invoke(selector, args, 0xFFFFFFFF, false); err != nil {
return err
}
return
}
func (d *xcTestManagerDaemon) initiateSession(XcodeVersion uint64, nsUUID *nskeyedarchiver.NSUUID) (err error) {
args := libimobiledevice.NewAuxBuffer()
if err = args.AppendObject(nsUUID); err != nil {
return err
}
if err = args.AppendObject(nsUUID.String() + "-Go-iDevice"); err != nil {
return err
}
if err = args.AppendObject("/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild"); err != nil {
return err
}
if err = args.AppendObject(XcodeVersion); err != nil {
return err
}
selector := "_IDE_initiateSessionWithIdentifier:forClient:atPath:protocolVersion:"
var ret *libimobiledevice.DTXMessageResult
if ret, err = d.testmanagerd.invoke(selector, args, d.channelCode, true); err != nil {
return err
}
if nsErr, ok := ret.Obj.(libimobiledevice.NSError); ok {
return fmt.Errorf("%s", nsErr.NSUserInfo.(map[string]interface{})["NSLocalizedDescription"])
}
return
}
func (d *xcTestManagerDaemon) authorizeTestSession(pid int) (err error) {
args := libimobiledevice.NewAuxBuffer()
if err = args.AppendObject(pid); err != nil {
return err
}
selector := "_IDE_authorizeTestSessionWithProcessID:"
var ret *libimobiledevice.DTXMessageResult
if ret, err = d.testmanagerd.invoke(selector, args, d.channelCode, true); err != nil {
return err
}
if nsErr, ok := ret.Obj.(libimobiledevice.NSError); ok {
return fmt.Errorf("%s", nsErr.NSUserInfo.(map[string]interface{})["NSLocalizedDescription"])
}
return
}
func (d *xcTestManagerDaemon) initiateControlSessionForTestProcessID(pid int) (err error) {
args := libimobiledevice.NewAuxBuffer()
if err = args.AppendObject(pid); err != nil {
return err
}
selector := "_IDE_initiateControlSessionForTestProcessID:"
var ret *libimobiledevice.DTXMessageResult
if ret, err = d.testmanagerd.invoke(selector, args, d.channelCode, true); err != nil {
return err
}
if nsErr, ok := ret.Obj.(libimobiledevice.NSError); ok {
return fmt.Errorf("%s", nsErr.NSUserInfo.(map[string]interface{})["NSLocalizedDescription"])
}
return
}
func (d *xcTestManagerDaemon) initiateControlSessionForTestProcessIDProtocolVersion(pid int, XcodeVersion uint64) (err error) {
args := libimobiledevice.NewAuxBuffer()
if err = args.AppendObject(pid); err != nil {
return err
}
if err = args.AppendObject(XcodeVersion); err != nil {
return err
}
selector := "_IDE_initiateControlSessionForTestProcessID:protocolVersion:"
var ret *libimobiledevice.DTXMessageResult
if ret, err = d.testmanagerd.invoke(selector, args, d.channelCode, true); err != nil {
return err
}
if nsErr, ok := ret.Obj.(libimobiledevice.NSError); ok {
return fmt.Errorf("%s", nsErr.NSUserInfo.(map[string]interface{})["NSLocalizedDescription"])
}
return
}
func (d *xcTestManagerDaemon) registerCallback(obj string, cb func(m libimobiledevice.DTXMessageResult)) {
d.testmanagerd.registerCallback(obj, cb)
}
func (d *xcTestManagerDaemon) close() {
d.testmanagerd.close()
}

View File

@@ -54,6 +54,7 @@ type LoginRequest struct {
PackageName string `json:"packageName"`
PhoneNumber string `json:"phoneNumber"`
Captcha string `json:"captcha"`
Password string `json:"password"`
}
type LogoutRequest struct {

View File

@@ -105,7 +105,7 @@ func loginHandler(c *gin.Context) {
return
}
err = dExt.Driver.LoginNoneUI(loginReq.PackageName, loginReq.PhoneNumber, loginReq.Captcha)
info, err := dExt.Driver.LoginNoneUI(loginReq.PackageName, loginReq.PhoneNumber, loginReq.Captcha, loginReq.Password)
if err != nil {
log.Err(err).Msg(fmt.Sprintf("[%s]: failed to login", c.HandlerName()))
c.JSON(http.StatusInternalServerError,
@@ -117,7 +117,7 @@ func loginHandler(c *gin.Context) {
c.Abort()
return
}
c.JSON(http.StatusOK, HttpResponse{Code: 0, Message: "success"})
c.JSON(http.StatusOK, HttpResponse{Code: 0, Message: "success", Result: info})
}
func logoutHandler(c *gin.Context) {

View File

@@ -15,24 +15,25 @@ import (
type ActionMethod string
const (
ACTION_LOG ActionMethod = "log"
ACTION_AppInstall ActionMethod = "install"
ACTION_AppUninstall ActionMethod = "uninstall"
ACTION_AppClear ActionMethod = "app_clear"
ACTION_AppStart ActionMethod = "app_start"
ACTION_AppLaunch ActionMethod = "app_launch" // 启动 app 并堵塞等待 app 首屏加载完成
ACTION_AppTerminate ActionMethod = "app_terminate"
ACTION_AppStop ActionMethod = "app_stop"
ACTION_ScreenShot ActionMethod = "screenshot"
ACTION_Sleep ActionMethod = "sleep"
ACTION_SleepMS ActionMethod = "sleep_ms"
ACTION_SleepRandom ActionMethod = "sleep_random"
ACTION_StartCamera ActionMethod = "camera_start" // alias for app_launch camera
ACTION_StopCamera ActionMethod = "camera_stop" // alias for app_terminate camera
ACTION_SetClipboard ActionMethod = "set_clipboard"
ACTION_GetClipboard ActionMethod = "get_clipboard"
ACTION_SetIme ActionMethod = "set_ime"
ACTION_GetSource ActionMethod = "get_source"
ACTION_LOG ActionMethod = "log"
ACTION_AppInstall ActionMethod = "install"
ACTION_AppUninstall ActionMethod = "uninstall"
ACTION_AppClear ActionMethod = "app_clear"
ACTION_AppStart ActionMethod = "app_start"
ACTION_AppLaunch ActionMethod = "app_launch" // 启动 app 并堵塞等待 app 首屏加载完成
ACTION_AppTerminate ActionMethod = "app_terminate"
ACTION_AppStop ActionMethod = "app_stop"
ACTION_ScreenShot ActionMethod = "screenshot"
ACTION_Sleep ActionMethod = "sleep"
ACTION_SleepMS ActionMethod = "sleep_ms"
ACTION_SleepRandom ActionMethod = "sleep_random"
ACTION_StartCamera ActionMethod = "camera_start" // alias for app_launch camera
ACTION_StopCamera ActionMethod = "camera_stop" // alias for app_terminate camera
ACTION_SetClipboard ActionMethod = "set_clipboard"
ACTION_GetClipboard ActionMethod = "get_clipboard"
ACTION_SetIme ActionMethod = "set_ime"
ACTION_GetSource ActionMethod = "get_source"
ACTION_GetForegroundApp ActionMethod = "get_foreground_app"
// UI handling
ACTION_Home ActionMethod = "home"
@@ -46,6 +47,7 @@ const (
ACTION_Swipe ActionMethod = "swipe"
ACTION_Input ActionMethod = "input"
ACTION_Back ActionMethod = "back"
ACTION_KeyCode ActionMethod = "keycode"
// custom actions
ACTION_SwipeToTapApp ActionMethod = "swipe_to_tap_app" // swipe left & right to find app and tap
@@ -109,7 +111,8 @@ type ActionOptions struct {
MaxRetryTimes int `json:"max_retry_times,omitempty" yaml:"max_retry_times,omitempty"` // max retry times
IgnoreNotFoundError bool `json:"ignore_NotFoundError,omitempty" yaml:"ignore_NotFoundError,omitempty"` // ignore error if target element not found
Interval float64 `json:"interval,omitempty" yaml:"interval,omitempty"` // interval between retries in seconds
PressDuration float64 `json:"duration,omitempty" yaml:"duration,omitempty"` // used to set duration of ios swipe action
Duration float64 `json:"duration,omitempty" yaml:"duration,omitempty"` // used to set duration of ios swipe action
PressDuration float64 `json:"press_duration,omitempty" yaml:"press_duration,omitempty"` // used to set duration of ios swipe action
Steps int `json:"steps,omitempty" yaml:"steps,omitempty"` // used to set steps of android swipe action
Direction interface{} `json:"direction,omitempty" yaml:"direction,omitempty"` // used by swipe to tap text or app
Timeout int `json:"timeout,omitempty" yaml:"timeout,omitempty"` // TODO: wait timeout in seconds for mobile action
@@ -159,6 +162,9 @@ func (o *ActionOptions) Options() []ActionOption {
if o.Interval != 0 {
options = append(options, WithInterval(o.Interval))
}
if o.Duration != 0 {
options = append(options, WithDuration(o.Duration))
}
if o.PressDuration != 0 {
options = append(options, WithPressDuration(o.PressDuration))
}
@@ -309,8 +315,8 @@ func (o *ActionOptions) updateData(data map[string]interface{}) {
data["steps"] = 12 // default steps
}
if o.PressDuration > 0 {
data["duration"] = o.PressDuration
if o.Duration > 0 {
data["duration"] = o.Duration
}
if _, ok := data["duration"]; !ok {
data["duration"] = 0 // default duration
@@ -380,9 +386,15 @@ func WithInterval(sec float64) ActionOption {
}
}
func WithPressDuration(duration float64) ActionOption {
func WithDuration(duration float64) ActionOption {
return func(o *ActionOptions) {
o.PressDuration = duration
o.Duration = duration
}
}
func WithPressDuration(pressDuration float64) ActionOption {
return func(o *ActionOptions) {
o.PressDuration = pressDuration
}
}

View File

@@ -6,12 +6,15 @@ import (
"encoding/json"
"encoding/xml"
"fmt"
"io"
"io/fs"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
"syscall"
"time"
"github.com/httprunner/funplugin/myexec"
@@ -245,6 +248,34 @@ func (ad *adbDriver) Unlock() (err error) {
return ad.PressKeyCodes(KCMenu, KMEmpty)
}
func (ad *adbDriver) Backspace(count int, options ...ActionOption) (err error) {
if count == 0 {
return nil
}
if count == 1 {
return ad.PressKeyCode(67)
}
keyArray := make([]KeyCode, count)
for i := range keyArray {
keyArray[i] = KeyCode(67)
}
return ad.combinationKey(keyArray)
}
func (ad *adbDriver) combinationKey(keyCodes []KeyCode) (err error) {
if len(keyCodes) == 1 {
return ad.PressKeyCode(keyCodes[0])
}
strKeyCodes := make([]string, len(keyCodes))
for i, keycode := range keyCodes {
strKeyCodes[i] = fmt.Sprintf("%d", keycode)
}
_, err = ad.adbClient.RunShellCommand(
"input", append([]string{"keycombination"}, strKeyCodes...)...)
return
}
func (ad *adbDriver) PressKeyCode(keyCode KeyCode) (err error) {
return ad.PressKeyCodes(keyCode, KMEmpty)
}
@@ -309,15 +340,11 @@ func (ad *adbDriver) TapFloat(x, y float64, options ...ActionOption) (err error)
return nil
}
func (ad *adbDriver) DoubleTap(x, y int, options ...ActionOption) error {
return ad.DoubleTapFloat(float64(x), float64(y), options...)
}
func (ad *adbDriver) DoubleTapFloat(x, y float64, options ...ActionOption) (err error) {
func (ad *adbDriver) DoubleTap(x, y float64, options ...ActionOption) error {
// adb shell input tap x y
xStr := fmt.Sprintf("%.1f", x)
yStr := fmt.Sprintf("%.1f", y)
_, err = ad.adbClient.RunShellCommand(
_, err := ad.adbClient.RunShellCommand(
"input", "tap", xStr, yStr)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("tap <%s, %s> failed", xStr, yStr))
@@ -331,22 +358,64 @@ func (ad *adbDriver) DoubleTapFloat(x, y float64, options ...ActionOption) (err
return nil
}
func (ad *adbDriver) TouchAndHold(x, y int, second ...float64) (err error) {
return ad.TouchAndHoldFloat(float64(x), float64(y), second...)
func (ad *adbDriver) TouchAndHold(x, y float64, options ...ActionOption) (err error) {
actionOptions := NewActionOptions(options...)
if len(actionOptions.Offset) == 2 {
x += float64(actionOptions.Offset[0])
y += float64(actionOptions.Offset[1])
}
x += actionOptions.getRandomOffset()
y += actionOptions.getRandomOffset()
duration := 1000.0
if actionOptions.Duration > 0 {
duration = actionOptions.Duration * 1000
}
// adb shell input swipe fromX fromY toX toY
_, err = ad.adbClient.RunShellCommand(
"input", "swipe",
fmt.Sprintf("%.1f", x), fmt.Sprintf("%.1f", y),
fmt.Sprintf("%.1f", x), fmt.Sprintf("%.1f", y),
fmt.Sprintf("%d", int(duration)),
)
if err != nil {
return errors.Wrap(err, "long press failed")
}
return nil
}
func (ad *adbDriver) TouchAndHoldFloat(x, y float64, second ...float64) (err error) {
err = errDriverNotImplemented
return
}
func (ad *adbDriver) Drag(fromX, fromY, toX, toY float64, options ...ActionOption) (err error) {
actionOptions := NewActionOptions(options...)
func (ad *adbDriver) Drag(fromX, fromY, toX, toY int, options ...ActionOption) error {
return ad.DragFloat(float64(fromX), float64(fromY), float64(toX), float64(toY), options...)
}
func (ad *adbDriver) DragFloat(fromX, fromY, toX, toY float64, options ...ActionOption) (err error) {
err = errDriverNotImplemented
return
if len(actionOptions.Offset) == 4 {
fromX += float64(actionOptions.Offset[0])
fromY += float64(actionOptions.Offset[1])
toX += float64(actionOptions.Offset[2])
toY += float64(actionOptions.Offset[3])
}
fromX += actionOptions.getRandomOffset()
fromY += actionOptions.getRandomOffset()
toX += actionOptions.getRandomOffset()
toY += actionOptions.getRandomOffset()
duration := 200.0
if actionOptions.Duration > 0 {
duration = actionOptions.Duration * 1000
}
command := "swipe"
if actionOptions.PressDuration > 0 {
command = "draganddrop"
}
// adb shell input swipe fromX fromY toX toY
_, err = ad.adbClient.RunShellCommand(
"input", command,
fmt.Sprintf("%.1f", fromX), fmt.Sprintf("%.1f", fromY),
fmt.Sprintf("%.1f", toX), fmt.Sprintf("%.1f", toY),
fmt.Sprintf("%d", int(duration)),
)
if err != nil {
return errors.Wrap(err, "drag failed")
}
return nil
}
func (ad *adbDriver) Swipe(fromX, fromY, toX, toY int, options ...ActionOption) error {
@@ -557,8 +626,8 @@ func (ad *adbDriver) Source(srcOpt ...SourceOption) (source string, err error) {
return
}
func (ad *adbDriver) LoginNoneUI(packageName, phoneNumber string, captcha string) error {
return errDriverNotImplemented
func (ad *adbDriver) LoginNoneUI(packageName, phoneNumber string, captcha, password string) (info AppLoginInfo, err error) {
return info, errDriverNotImplemented
}
func (ad *adbDriver) LogoutNoneUI(packageName string) error {
@@ -750,6 +819,10 @@ func (ad *adbDriver) GetSession() *DriverSession {
return &ad.Driver.session
}
func (ad *adbDriver) GetDriverResults() []*DriverResult {
return nil
}
func (ad *adbDriver) GetForegroundApp() (app AppInfo, err error) {
packageInfo, err := ad.adbClient.RunShellCommand(
"CLASSPATH=/data/local/tmp/evalite", "app_process", "/",
@@ -916,3 +989,63 @@ var androidActivities = map[string]map[string][]string{
},
// TODO: SPH, XHS
}
func (ad *adbDriver) RecordScreen(folderPath string, duration time.Duration) (videoPath string, err error) {
// 获取当前时间戳
timestamp := time.Now().Format("20060102_150405") + fmt.Sprintf("_%03d", time.Now().UnixNano()/1e6%1000)
// 创建文件名
fileName := fmt.Sprintf("%s/%s.mp4", folderPath, timestamp)
err = os.MkdirAll(folderPath, os.ModePerm)
if err != nil {
log.Error().Err(err).Msg("Error creating directory")
}
// 创建一个文件
file, err := os.Create(fileName)
if err != nil {
log.Error().Err(err)
return "", err
}
defer func() {
_ = file.Close()
}()
// scrcpy -s 7d21bb91 --record=file.mp4 -N
cmd := exec.Command(
"scrcpy",
"-s", ad.adbClient.Serial(),
fmt.Sprintf("--record=%s", fileName),
"-N",
)
cmd.Stdout = io.Discard
cmd.Stderr = io.Discard
// 启动命令
if err := cmd.Start(); err != nil {
log.Error().Err(err)
return "", err
}
timer := time.After(duration)
done := make(chan error)
go func() {
// 等待 ffmpeg 命令执行完毕
done <- cmd.Wait()
}()
select {
case <-timer:
// 超时,停止 scrcpy 进程
if err := cmd.Process.Signal(syscall.SIGINT); err != nil {
log.Error().Err(err)
}
case err := <-done:
// ffmpeg 正常结束
if err != nil {
log.Error().Err(err)
return "", err
}
}
return filepath.Abs(fileName)
}
func (ad *adbDriver) TearDown() {
}

Some files were not shown because too many files have changed in this diff Show More