feat: 支持shoots协议,新增UIAgent驱动接口

This commit is contained in:
余泓铮
2024-07-16 20:30:39 +08:00
parent 9cf1809ac5
commit e2a7c29acf
19 changed files with 1149 additions and 76 deletions

37
go.mod
View File

@@ -24,13 +24,13 @@ require (
github.com/satori/go.uuid v1.2.0 github.com/satori/go.uuid v1.2.0
github.com/shirou/gopsutil v3.21.11+incompatible github.com/shirou/gopsutil v3.21.11+incompatible
github.com/spf13/cobra v1.5.0 github.com/spf13/cobra v1.5.0
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.9.0
gocv.io/x/gocv v0.32.1 gocv.io/x/gocv v0.32.1
golang.org/x/net v0.20.0 golang.org/x/net v0.26.0
golang.org/x/oauth2 v0.8.0 golang.org/x/oauth2 v0.8.0
golang.org/x/text v0.14.0 golang.org/x/text v0.16.0
google.golang.org/grpc v1.57.0 google.golang.org/grpc v1.57.0
google.golang.org/protobuf v1.31.0 google.golang.org/protobuf v1.34.2
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
howett.net/plist v1.0.0 howett.net/plist v1.0.0
) )
@@ -39,32 +39,47 @@ require (
cloud.google.com/go/compute v1.20.1 // indirect cloud.google.com/go/compute v1.20.1 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/bytedance/sonic v1.11.9 // indirect
github.com/bytedance/sonic/loader v0.1.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // 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
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.4 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-gonic/gin v1.10.0 // indirect
github.com/go-errors/errors v1.4.2 // indirect github.com/go-errors/errors v1.4.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/swag v0.22.3 // indirect github.com/go-openapi/swag v0.22.3 // indirect
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.22.0 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/golang/protobuf v1.5.3 // indirect github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-cmp v0.6.0 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect
github.com/hashicorp/go-plugin v1.4.10 // indirect github.com/hashicorp/go-plugin v1.4.10 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect github.com/hashicorp/yamux v0.1.1 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/incu6us/goimports-reviser/v2 v2.5.3 // indirect github.com/incu6us/goimports-reviser/v2 v2.5.3 // indirect
github.com/josharian/intern v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
github.com/kr/pretty v0.3.1 // indirect github.com/kr/pretty v0.3.1 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // 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/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/oklog/run v1.1.0 // indirect github.com/oklog/run v1.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/common v0.37.0 // indirect
@@ -75,11 +90,15 @@ require (
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/tklauser/go-sysconf v0.3.10 // indirect github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/tklauser/numcpus v0.5.0 // 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 github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/mod v0.14.0 // indirect golang.org/x/arch v0.8.0 // indirect
golang.org/x/sync v0.6.0 // indirect golang.org/x/crypto v0.24.0 // indirect
golang.org/x/sys v0.16.0 // indirect golang.org/x/mod v0.17.0 // indirect
golang.org/x/tools v0.17.0 // indirect golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/appengine v1.6.7 // indirect google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect

67
go.sum
View File

@@ -48,6 +48,10 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bytedance/sonic v1.11.9 h1:LFHENlIY/SLzDWverzdOvgMztTxcfcF+cqNsz9pK5zg=
github.com/bytedance/sonic v1.11.9/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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 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.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -57,6 +61,10 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
@@ -74,8 +82,14 @@ 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.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I=
github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s=
github.com/getsentry/sentry-go v0.13.0 h1:20dgTiUSfxRB/EhMPtxcL9ZEbM1ZdR+W/7f7NWD+xWo= github.com/getsentry/sentry-go v0.13.0 h1:20dgTiUSfxRB/EhMPtxcL9ZEbM1ZdR+W/7f7NWD+xWo=
github.com/getsentry/sentry-go v0.13.0/go.mod h1:EOsfu5ZdvKPfeHYV6pTVQnsjfp30+XA7//UooKNumH0= github.com/getsentry/sentry-go v0.13.0/go.mod h1:EOsfu5ZdvKPfeHYV6pTVQnsjfp30+XA7//UooKNumH0=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
@@ -102,7 +116,15 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -163,6 +185,8 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaU
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 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/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= 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/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
@@ -203,6 +227,10 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
@@ -213,6 +241,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc=
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -231,6 +261,8 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
@@ -255,6 +287,8 @@ github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DV
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= 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/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
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/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -314,19 +348,31 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 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/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 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/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw= 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/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
github.com/tklauser/numcpus v0.5.0 h1:ooe7gN0fg6myJ0EKoTAf5hebTZrH52px3New/D9iJ+A= github.com/tklauser/numcpus v0.5.0 h1:ooe7gN0fg6myJ0EKoTAf5hebTZrH52px3New/D9iJ+A=
github.com/tklauser/numcpus v0.5.0/go.mod h1:OGzpTxpcIMNGYQdit2BYL1pvk/dSOaJWjKoflh+RQjo= github.com/tklauser/numcpus v0.5.0/go.mod h1:OGzpTxpcIMNGYQdit2BYL1pvk/dSOaJWjKoflh+RQjo=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -339,12 +385,17 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
gocv.io/x/gocv v0.32.1 h1:BC9hHs5+47nVgySUFVKntc6RsF3SULFzqk6OV9xz+C0= gocv.io/x/gocv v0.32.1 h1:BC9hHs5+47nVgySUFVKntc6RsF3SULFzqk6OV9xz+C0=
gocv.io/x/gocv v0.32.1/go.mod h1:oc6FvfYqfBp99p+yOEzs9tbYF9gOrAQSeL/dyIPefJU= gocv.io/x/gocv v0.32.1/go.mod h1:oc6FvfYqfBp99p+yOEzs9tbYF9gOrAQSeL/dyIPefJU=
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=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
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-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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -377,6 +428,7 @@ 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.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
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-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-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -412,6 +464,8 @@ golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
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-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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -432,6 +486,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/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.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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-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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -477,11 +533,14 @@ 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-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-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 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= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -495,6 +554,8 @@ golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.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-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-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.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -540,6 +601,8 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
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-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-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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -627,6 +690,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -658,6 +723,8 @@ howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
mvdan.cc/gofumpt v0.6.0 h1:G3QvahNDmpD+Aek/bNOLrFR2XC6ZAdo62dZu65gmwGo= mvdan.cc/gofumpt v0.6.0 h1:G3QvahNDmpD+Aek/bNOLrFR2XC6ZAdo62dZu65gmwGo=
mvdan.cc/gofumpt v0.6.0/go.mod h1:4L0wf+kgIPZtcCWXynNS2e6bhmj73umwnuXSZarixzA= mvdan.cc/gofumpt v0.6.0/go.mod h1:4L0wf+kgIPZtcCWXynNS2e6bhmj73umwnuXSZarixzA=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 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/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

25
hrp/cmd/server.go Normal file
View File

@@ -0,0 +1,25 @@
package cmd
import (
"github.com/spf13/cobra"
"github.com/httprunner/httprunner/v4/hrp/pkg/server"
)
// serverCmd represents the server command
var serverCmd = &cobra.Command{
Use: "server start",
Short: "start hrp server",
Long: `start hrp server. exec automation by http`,
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return server.NewServer(port)
},
}
var port int
func init() {
rootCmd.AddCommand(serverCmd)
serverCmd.Flags().IntVarP(&port, "port", "p", 8082, "Port to run the server on")
}

View File

@@ -1 +1 @@
v4.5.8 v4.6.0

View File

@@ -292,6 +292,33 @@ func (d *Device) ReverseForwardKill(remoteInterface interface{}) error {
return err return err
} }
func (d *Device) RunShootsCommand(command []byte, processName string) (res string, err error) {
var tp transport
if tp, err = d.createDeviceTransport(); err != nil {
return "", err
}
defer func() { _ = tp.Close() }()
if err = tp.SendWithCheck(fmt.Sprintf("localabstract:%s", processName)); err != nil {
return "", err
}
if err = tp.SendBytes(command); err != nil {
return "", err
}
lenBuf, err := tp.ReadBytesN(4)
if err != nil {
return "", err
}
length := binary.LittleEndian.Uint32(lenBuf)
result, err := tp.ReadBytesN(int(length) - 4)
if err != nil {
return "", err
}
return string(result), nil
}
func (d *Device) ReverseForwardKillAll() error { func (d *Device) ReverseForwardKillAll() error {
_, err := d.executeCommand("reverse:killforward-all") _, err := d.executeCommand("reverse:killforward-all")
return err return err

View File

@@ -0,0 +1,18 @@
package server
// 常见的错误代码和消息
const (
InternalServerErrorCode = 100001
InternalServerErrorMsg = "Invalid Server Error"
InvalidParamErrorCode = 100002
InvalidParamErrorMsg = "Invalid %s Param"
CodeNotFound = 1004
MsgNotFound = "Resource not found"
)
const (
DeviceNotFoundCode = 110001
DeviceNotFoundMsg = "Device %s Not Found"
)

44
hrp/pkg/server/model.go Normal file
View File

@@ -0,0 +1,44 @@
package server
type HttpResponse struct {
Result interface{} `json:"result,omitempty"`
ErrorCode int `json:"errorCode"`
ErrorMsg string `json:"errorMsg"`
}
type TapRequest struct {
X float64 `json:"x"`
Y float64 `json:"y"`
Duration float64 `json:"duration"`
}
type DragRequest struct {
FromX float64 `json:"from_x"`
FromY float64 `json:"from_y"`
ToX float64 `json:"to_x"`
ToY float64 `json:"to_y"`
Duration float64 `json:"duration"`
}
type InputRequest struct {
Text string `json:"text"`
Frequency int `json:"frequency"` // only iOS
}
type KeycodeRequest struct {
Keycode int `json:"keycode"`
}
type AppLaunchRequest struct {
PackageName string `json:"packageName"`
}
type AppTerminalRequest struct {
PackageName string `json:"packageName"`
}
type LoginRequest struct {
PackageName string `json:"packageName"`
PhoneNumber string `json:"phoneNumber"`
Captcha string `json:"captcha"`
}

514
hrp/pkg/server/server.go Normal file
View File

@@ -0,0 +1,514 @@
package server
import (
"encoding/base64"
"fmt"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v4/hrp/pkg/gadb"
"github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
)
func NewServer(port int) error {
router := gin.Default()
router.GET("/ping", pingHandler)
router.GET("/api/v1/:platform/devices", listDeviceHandler)
router.POST("/api/v1/:platform/:serial/ui/tap", parseDeviceInfo(), tapHandler)
router.POST("/api/v1/:platform/:serial/ui/drag", parseDeviceInfo(), dragHandler)
router.POST("/api/v1/:platform/:serial/ui/input", parseDeviceInfo(), inputHandler)
router.POST("/api/v1/:platform/:serial/key/unlock", parseDeviceInfo(), unlockHandler)
router.POST("/api/v1/:platform/:serial/key/home", parseDeviceInfo(), homeHandler)
router.POST("/api/v1/:platform/:serial/key", parseDeviceInfo(), keycodeHandler)
router.GET("/api/v1/:platform/:serial/app/foreground", parseDeviceInfo(), foregroundAppHandler)
router.POST("/api/v1/:platform/:serial/app/launch", parseDeviceInfo(), launchAppHandler)
router.POST("/api/v1/:platform/:serial/app/terminal", parseDeviceInfo(), terminalAppHandler)
router.GET("/api/v1/:platform/:serial/screenshot", parseDeviceInfo(), screenshotHandler)
router.GET("/api/v1/:platform/:serial/shoots/source", parseDeviceInfo(), sourceHandler)
router.GET("/api/v1/:platform/:serial/adb/source", parseDeviceInfo(), adbSourceHandler)
router.POST("/api/v1/:platform/:serial/shoots/login", parseDeviceInfo(), loginHandler)
err := router.Run(fmt.Sprintf("127.0.0.1:%d", port))
if err != nil {
log.Err(err).Msg("failed to start http server")
return err
}
return nil
}
func pingHandler(c *gin.Context) {
c.JSON(http.StatusOK, HttpResponse{Result: true})
return
}
func listDeviceHandler(c *gin.Context) {
platform := c.Param("platform")
switch strings.ToLower(platform) {
case "android":
{
client, err := gadb.NewClient()
if err != nil {
log.Err(err).Msg("failed to init adb client")
c.JSON(http.StatusInternalServerError, HttpResponse{Result: false, ErrorCode: InternalServerErrorCode, ErrorMsg: InternalServerErrorMsg})
c.Abort()
return
}
devices, err := client.DeviceList()
if err != nil && strings.Contains(err.Error(), "no android device found") {
c.JSON(http.StatusOK, HttpResponse{Result: nil})
return
} else if err != nil {
log.Err(err).Msg("failed to list devices")
c.JSON(http.StatusInternalServerError, HttpResponse{Result: false, ErrorCode: InternalServerErrorCode, ErrorMsg: InternalServerErrorMsg})
c.Abort()
return
}
var deviceList []interface{}
for _, device := range devices {
brand, err := device.Brand()
if err != nil {
log.Err(err).Msg("failed to get device brand")
c.JSON(http.StatusInternalServerError, HttpResponse{Result: false, ErrorCode: InternalServerErrorCode, ErrorMsg: InternalServerErrorMsg})
c.Abort()
return
}
model, err := device.Model()
if err != nil {
log.Err(err).Msg("failed to get device model")
c.JSON(http.StatusInternalServerError, HttpResponse{Result: false, ErrorCode: InternalServerErrorCode, ErrorMsg: InternalServerErrorMsg})
c.Abort()
return
}
deviceInfo := map[string]interface{}{
"serial": device.Serial(),
"brand": brand,
"model": model,
"platform": "android",
}
deviceList = append(deviceList, deviceInfo)
}
c.JSON(http.StatusOK, HttpResponse{Result: deviceList})
return
}
default:
{
c.JSON(http.StatusBadRequest, HttpResponse{
ErrorCode: InvalidParamErrorCode,
ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "platform"),
})
c.Abort()
return
}
}
}
func tapHandler(c *gin.Context) {
var tapReq TapRequest
if err := c.ShouldBindJSON(&tapReq); err != nil {
log.Err(err).Msg(fmt.Sprintf("[%s]: Invalid Request", c.HandlerName()))
c.JSON(http.StatusBadRequest, HttpResponse{Result: false, ErrorCode: InvalidParamErrorCode, ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "request")})
c.Abort()
return
}
driverObj, exists := c.Get("driver")
if !exists {
log.Error().Msg(fmt.Sprintf("[%s]: Driver Not exsit", c.HandlerName()))
c.JSON(http.StatusBadRequest, HttpResponse{Result: false, ErrorCode: InvalidParamErrorCode, ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "driver")})
c.Abort()
return
}
dExt := driverObj.(*uixt.DriverExt)
if tapReq.X < 1 && tapReq.Y < 1 {
err := dExt.TapXY(tapReq.X, tapReq.Y, uixt.WithPressDuration(tapReq.Duration))
if err != nil {
log.Err(err).Msg(fmt.Sprintf("[%s]: failed to tap %f, %f", c.HandlerName(), tapReq.X, tapReq.Y))
c.JSON(http.StatusInternalServerError, HttpResponse{Result: false, ErrorCode: InternalServerErrorCode, ErrorMsg: InternalServerErrorMsg})
c.Abort()
return
}
} else {
err := dExt.TapAbsXY(tapReq.X, tapReq.Y, uixt.WithPressDuration(tapReq.Duration))
if err != nil {
log.Err(err).Msg(fmt.Sprintf("[%s]: failed to tap %f, %f", c.HandlerName(), tapReq.X, tapReq.Y))
c.JSON(http.StatusInternalServerError, HttpResponse{Result: false, ErrorCode: InternalServerErrorCode, ErrorMsg: InternalServerErrorMsg})
c.Abort()
return
}
}
c.JSON(http.StatusOK, HttpResponse{Result: true})
return
}
func dragHandler(c *gin.Context) {
var dragReq DragRequest
if err := c.ShouldBindJSON(&dragReq); err != nil {
log.Err(err).Msg(fmt.Sprintf("[%s]: Invalid Request", c.HandlerName()))
c.JSON(http.StatusBadRequest, HttpResponse{Result: false, ErrorCode: InvalidParamErrorCode, ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "request")})
c.Abort()
return
}
driverObj, exists := c.Get("driver")
if !exists {
log.Error().Msg(fmt.Sprintf("[%s]: Driver Not exsit", c.HandlerName()))
c.JSON(http.StatusBadRequest, HttpResponse{Result: false, ErrorCode: InvalidParamErrorCode, ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "driver")})
c.Abort()
return
}
dExt := driverObj.(*uixt.DriverExt)
if dragReq.FromX < 1 && dragReq.FromY < 1 && dragReq.ToX < 1 && dragReq.ToY < 1 {
err := dExt.SwipeRelative(dragReq.FromX, dragReq.FromY, dragReq.ToX, dragReq.ToY, uixt.WithPressDuration(dragReq.Duration))
if err != nil {
log.Err(err).Msg(fmt.Sprintf("[%s]: failed to drag from %f, %f to %f, %f", c.HandlerName(), dragReq.FromX, dragReq.FromY, dragReq.ToX, dragReq.ToY))
c.JSON(http.StatusInternalServerError, HttpResponse{Result: false, ErrorCode: InternalServerErrorCode, ErrorMsg: InternalServerErrorMsg})
c.Abort()
return
}
} else {
err := dExt.Driver.SwipeFloat(dragReq.FromX, dragReq.FromY, dragReq.ToX, dragReq.ToY, uixt.WithPressDuration(dragReq.Duration))
if err != nil {
log.Err(err).Msg(fmt.Sprintf("[%s]: failed to drag from %f, %f to %f, %f", c.HandlerName(), dragReq.FromX, dragReq.FromY, dragReq.ToX, dragReq.ToY))
c.JSON(http.StatusInternalServerError, HttpResponse{Result: false, ErrorCode: InternalServerErrorCode, ErrorMsg: InternalServerErrorMsg})
c.Abort()
return
}
}
c.JSON(http.StatusOK, HttpResponse{Result: true})
return
}
func inputHandler(c *gin.Context) {
var inputReq InputRequest
if err := c.ShouldBindJSON(&inputReq); err != nil {
log.Err(err).Msg(fmt.Sprintf("[%s]: Invalid Request", c.HandlerName()))
c.JSON(http.StatusBadRequest, HttpResponse{Result: false, ErrorCode: InvalidParamErrorCode, ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "request")})
c.Abort()
return
}
driverObj, exists := c.Get("driver")
if !exists {
log.Error().Msg(fmt.Sprintf("[%s]: Driver Not exsit", c.HandlerName()))
c.JSON(http.StatusBadRequest, HttpResponse{Result: false, ErrorCode: InvalidParamErrorCode, ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "driver")})
c.Abort()
return
}
dExt := driverObj.(*uixt.DriverExt)
err := dExt.Driver.SendKeys(inputReq.Text, uixt.WithFrequency(inputReq.Frequency))
if err != nil {
log.Err(err).Msg(fmt.Sprintf("[%s]: failed to input text %s", c.HandlerName(), inputReq.Text))
c.JSON(http.StatusInternalServerError, HttpResponse{Result: false, ErrorCode: InternalServerErrorCode, ErrorMsg: InternalServerErrorMsg})
c.Abort()
return
}
c.JSON(http.StatusOK, HttpResponse{Result: true})
return
}
func unlockHandler(c *gin.Context) {
driverObj, exists := c.Get("driver")
if !exists {
log.Error().Msg(fmt.Sprintf("[%s]: Driver Not exsit", c.HandlerName()))
c.JSON(http.StatusBadRequest, HttpResponse{Result: false, ErrorCode: InvalidParamErrorCode, ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "driver")})
c.Abort()
return
}
dExt := driverObj.(*uixt.DriverExt)
err := dExt.Driver.Unlock()
if err != nil {
log.Err(err).Msg(fmt.Sprintf("[%s]: failed to unlick screen", c.HandlerName()))
c.JSON(http.StatusInternalServerError, HttpResponse{Result: false, ErrorCode: InternalServerErrorCode, ErrorMsg: InternalServerErrorMsg})
c.Abort()
return
}
c.JSON(http.StatusOK, HttpResponse{Result: true})
return
}
func homeHandler(c *gin.Context) {
driverObj, exists := c.Get("driver")
if !exists {
log.Error().Msg(fmt.Sprintf("[%s]: Driver Not exsit", c.HandlerName()))
c.JSON(http.StatusBadRequest, HttpResponse{Result: false, ErrorCode: InvalidParamErrorCode, ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "driver")})
c.Abort()
return
}
dExt := driverObj.(*uixt.DriverExt)
err := dExt.Driver.Homescreen()
if err != nil {
log.Err(err).Msg(fmt.Sprintf("[%s]: failed to enter homescreen", c.HandlerName()))
c.JSON(http.StatusInternalServerError, HttpResponse{Result: false, ErrorCode: InternalServerErrorCode, ErrorMsg: InternalServerErrorMsg})
c.Abort()
return
}
c.JSON(http.StatusOK, HttpResponse{Result: true})
return
}
func keycodeHandler(c *gin.Context) {
var keycodeReq KeycodeRequest
if err := c.ShouldBindJSON(&keycodeReq); err != nil {
log.Err(err).Msg(fmt.Sprintf("[%s]: Invalid Request", c.HandlerName()))
c.JSON(http.StatusBadRequest, HttpResponse{Result: false, ErrorCode: InvalidParamErrorCode, ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "request")})
c.Abort()
return
}
driverObj, exists := c.Get("driver")
if !exists {
log.Error().Msg(fmt.Sprintf("[%s]: Driver Not exsit", c.HandlerName()))
c.JSON(http.StatusBadRequest, HttpResponse{Result: false, ErrorCode: InvalidParamErrorCode, ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "driver")})
c.Abort()
return
}
dExt := driverObj.(*uixt.DriverExt)
err := dExt.Driver.PressKeyCode(uixt.KeyCode(keycodeReq.Keycode))
if err != nil {
log.Err(err).Msg(fmt.Sprintf("[%s]: failed to input keycode %d", c.HandlerName(), keycodeReq.Keycode))
c.JSON(http.StatusInternalServerError, HttpResponse{Result: false, ErrorCode: InternalServerErrorCode, ErrorMsg: InternalServerErrorMsg})
c.Abort()
return
}
c.JSON(http.StatusOK, HttpResponse{Result: true})
return
}
func foregroundAppHandler(c *gin.Context) {
driverObj, exists := c.Get("driver")
if !exists {
log.Error().Msg(fmt.Sprintf("[%s]: Driver Not exsit", c.HandlerName()))
c.JSON(http.StatusBadRequest, HttpResponse{Result: false, ErrorCode: InvalidParamErrorCode, ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "driver")})
c.Abort()
return
}
dExt := driverObj.(*uixt.DriverExt)
appInfo, err := dExt.Driver.GetForegroundApp()
if err != nil {
log.Err(err).Msg(fmt.Sprintf("[%s]: failed to unlick screen", c.HandlerName()))
c.JSON(http.StatusInternalServerError, HttpResponse{Result: false, ErrorCode: InternalServerErrorCode, ErrorMsg: InternalServerErrorMsg})
c.Abort()
return
}
c.JSON(http.StatusOK, HttpResponse{Result: appInfo})
return
}
func launchAppHandler(c *gin.Context) {
var appLaunchReq AppLaunchRequest
if err := c.ShouldBindJSON(&appLaunchReq); err != nil {
log.Err(err).Msg(fmt.Sprintf("[%s]: Invalid Request", c.HandlerName()))
c.JSON(http.StatusBadRequest, HttpResponse{Result: "", ErrorCode: InvalidParamErrorCode, ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "request")})
c.Abort()
return
}
driverObj, exists := c.Get("driver")
if !exists {
log.Error().Msg(fmt.Sprintf("[%s]: Driver Not exsit", c.HandlerName()))
c.JSON(http.StatusBadRequest, HttpResponse{Result: "", ErrorCode: InvalidParamErrorCode, ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "driver")})
c.Abort()
return
}
dExt := driverObj.(*uixt.DriverExt)
err := dExt.Driver.AppLaunch(appLaunchReq.PackageName)
if err != nil {
log.Err(err).Msg(fmt.Sprintf("[%s]: failed to launch app %s", c.HandlerName(), appLaunchReq.PackageName))
c.JSON(http.StatusInternalServerError, HttpResponse{Result: "", ErrorCode: InternalServerErrorCode, ErrorMsg: InternalServerErrorMsg})
c.Abort()
return
}
c.JSON(http.StatusOK, HttpResponse{Result: true})
return
}
func terminalAppHandler(c *gin.Context) {
var appTerminalReq AppTerminalRequest
if err := c.ShouldBindJSON(&appTerminalReq); err != nil {
log.Err(err).Msg(fmt.Sprintf("[%s]: Invalid Request", c.HandlerName()))
c.JSON(http.StatusBadRequest, HttpResponse{Result: "", ErrorCode: InvalidParamErrorCode, ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "request")})
c.Abort()
return
}
driverObj, exists := c.Get("driver")
if !exists {
log.Error().Msg(fmt.Sprintf("[%s]: Driver Not exsit", c.HandlerName()))
c.JSON(http.StatusBadRequest, HttpResponse{Result: "", ErrorCode: InvalidParamErrorCode, ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "driver")})
c.Abort()
return
}
dExt := driverObj.(*uixt.DriverExt)
success, err := dExt.Driver.AppTerminate(appTerminalReq.PackageName)
if !success {
log.Err(err).Msg(fmt.Sprintf("[%s]: failed to launch app %s", c.HandlerName(), appTerminalReq.PackageName))
c.JSON(http.StatusInternalServerError, HttpResponse{Result: "", ErrorCode: InternalServerErrorCode, ErrorMsg: InternalServerErrorMsg})
c.Abort()
return
}
c.JSON(http.StatusOK, HttpResponse{Result: true})
return
}
func screenshotHandler(c *gin.Context) {
driverObj, exists := c.Get("driver")
if !exists {
log.Error().Msg(fmt.Sprintf("[%s]: Driver Not exsit", c.HandlerName()))
c.JSON(http.StatusBadRequest, HttpResponse{Result: "", ErrorCode: InvalidParamErrorCode, ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "driver")})
c.Abort()
return
}
dExt := driverObj.(*uixt.DriverExt)
raw, err := dExt.Driver.Screenshot()
if err != nil {
log.Err(err).Msg(fmt.Sprintf("[%s]: failed to get screenshot", c.HandlerName()))
c.JSON(http.StatusInternalServerError, HttpResponse{Result: "", ErrorCode: InternalServerErrorCode, ErrorMsg: InternalServerErrorMsg})
c.Abort()
return
}
c.JSON(http.StatusOK, HttpResponse{Result: base64.StdEncoding.EncodeToString(raw.Bytes())})
return
}
func sourceHandler(c *gin.Context) {
driverObj, exists := c.Get("driver")
if !exists {
log.Error().Msg(fmt.Sprintf("[%s]: Driver Not exsit", c.HandlerName()))
c.JSON(http.StatusBadRequest, HttpResponse{Result: "", ErrorCode: InvalidParamErrorCode, ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "driver")})
c.Abort()
return
}
dExt := driverObj.(*uixt.DriverExt)
app, err := dExt.Driver.GetForegroundApp()
if err != nil {
log.Err(err).Msg(fmt.Sprintf("[%s]: failed to get foreground app", c.HandlerName()))
c.JSON(http.StatusInternalServerError, HttpResponse{Result: "", ErrorCode: InternalServerErrorCode, ErrorMsg: InternalServerErrorMsg})
c.Abort()
return
}
source, err := dExt.Driver.Source(uixt.NewSourceOption().WithProcessName(app.PackageName))
if err != nil {
log.Err(err).Msg(fmt.Sprintf("[%s]: failed to get source %s", c.HandlerName(), app.PackageName))
c.JSON(http.StatusInternalServerError, HttpResponse{Result: "", ErrorCode: InternalServerErrorCode, ErrorMsg: InternalServerErrorMsg})
c.Abort()
return
}
c.JSON(http.StatusOK, HttpResponse{Result: source})
return
}
func adbSourceHandler(c *gin.Context) {
deviceObj, exists := c.Get("device")
if !exists {
log.Error().Msg(fmt.Sprintf("[%s]: Driver Not exsit", c.HandlerName()))
c.JSON(http.StatusBadRequest, HttpResponse{Result: "", ErrorCode: InvalidParamErrorCode, ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "device")})
c.Abort()
return
}
device := deviceObj.(*uixt.AndroidDevice)
driver, err := device.NewAdbDriver()
if err != nil {
log.Err(err).Msg(fmt.Sprintf("[%s]: failed to new adb driver", c.HandlerName()))
c.JSON(http.StatusInternalServerError, HttpResponse{Result: "", ErrorCode: InternalServerErrorCode, ErrorMsg: InternalServerErrorMsg})
c.Abort()
return
}
source, err := driver.Source()
if err != nil {
log.Err(err).Msg(fmt.Sprintf("[%s]: failed to get adb source", c.HandlerName()))
c.JSON(http.StatusInternalServerError, HttpResponse{Result: "", ErrorCode: InternalServerErrorCode, ErrorMsg: InternalServerErrorMsg})
c.Abort()
return
}
c.JSON(http.StatusOK, HttpResponse{Result: source})
return
}
func loginHandler(c *gin.Context) {
var loginReq LoginRequest
if err := c.ShouldBindJSON(&loginReq); err != nil {
log.Err(err).Msg(fmt.Sprintf("[%s]: Invalid Request", c.HandlerName()))
c.JSON(http.StatusBadRequest, HttpResponse{Result: "", ErrorCode: InvalidParamErrorCode, ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "request")})
c.Abort()
return
}
driverObj, exists := c.Get("driver")
if !exists {
log.Error().Msg(fmt.Sprintf("[%s]: Driver Not exsit", c.HandlerName()))
c.JSON(http.StatusBadRequest, HttpResponse{Result: "", ErrorCode: InvalidParamErrorCode, ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "driver")})
c.Abort()
return
}
dExt := driverObj.(*uixt.DriverExt)
err := dExt.Driver.LoginNoneUI(loginReq.PackageName, loginReq.PhoneNumber, loginReq.Captcha)
if err != nil {
log.Err(err).Msg(fmt.Sprintf("[%s]: failed to get foreground app", c.HandlerName()))
c.JSON(http.StatusInternalServerError, HttpResponse{Result: "", ErrorCode: InternalServerErrorCode, ErrorMsg: InternalServerErrorMsg})
c.Abort()
return
}
c.JSON(http.StatusOK, HttpResponse{Result: true})
return
}
func parseDeviceInfo() gin.HandlerFunc {
return func(c *gin.Context) {
platform := c.Param("platform")
switch strings.ToLower(platform) {
case "android":
serial := c.Param("serial")
if serial == "" {
log.Error().Str("platform", platform).Msg(fmt.Sprintf("[%s]: serial is empty", c.HandlerName()))
c.JSON(http.StatusBadRequest, HttpResponse{
ErrorCode: InvalidParamErrorCode,
ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "serial"),
})
c.Abort()
return
}
device, err := uixt.NewAndroidDevice(uixt.WithSerialNumber(serial), uixt.WithShoots(true))
if err != nil {
log.Error().Err(err).Str("platform", platform).Str("serial", serial).Msg(fmt.Sprintf("[%s]: Device Not Found", c.HandlerName()))
c.JSON(http.StatusBadRequest, HttpResponse{
ErrorCode: DeviceNotFoundCode,
ErrorMsg: fmt.Sprintf(DeviceNotFoundMsg, serial),
})
c.Abort()
return
}
c.Set("device", device)
driver, err := device.NewDriver(uixt.WithDriverImageService(false), uixt.WithDriverResultFolder(false))
if err != nil {
log.Error().Err(err).Str("platform", platform).Str("serial", serial).Msg(fmt.Sprintf("[%s]: Failed New Driver", c.HandlerName()))
c.JSON(http.StatusInternalServerError, HttpResponse{
ErrorCode: InternalServerErrorCode,
ErrorMsg: err.Error(),
})
c.Abort()
return
}
c.Set("driver", driver)
default:
c.JSON(http.StatusBadRequest, HttpResponse{
ErrorCode: InvalidParamErrorCode,
ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "platform"),
})
c.Abort()
return
}
c.Next()
}
}

View File

@@ -29,6 +29,7 @@ const (
ACTION_SetClipboard ActionMethod = "set_clipboard" ACTION_SetClipboard ActionMethod = "set_clipboard"
ACTION_GetClipboard ActionMethod = "get_clipboard" ACTION_GetClipboard ActionMethod = "get_clipboard"
ACTION_SetIme ActionMethod = "set_ime" ACTION_SetIme ActionMethod = "set_ime"
ACTION_GetSource ActionMethod = "get_source"
// UI validation // UI validation
// selectors // selectors
@@ -616,6 +617,15 @@ func (dExt *DriverExt) DoAction(action MobileAction) (err error) {
} }
return nil return nil
} }
case ACTION_GetSource:
if packageName, ok := action.Params.(string); ok {
source := NewSourceOption().WithProcessName(packageName)
_, err = dExt.Driver.Source(source)
if err != nil {
return errors.Wrap(err, "failed to set ime")
}
return nil
}
case ACTION_TapXY: case ACTION_TapXY:
if location, ok := action.Params.([]interface{}); ok { if location, ok := action.Params.([]interface{}); ok {
// relative x,y of window size: [0.5, 0.5] // relative x,y of window size: [0.5, 0.5]

View File

@@ -3,6 +3,7 @@ package uixt
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"encoding/json"
"encoding/xml" "encoding/xml"
"fmt" "fmt"
"io/fs" "io/fs"
@@ -218,10 +219,18 @@ func (ad *adbDriver) Orientation() (orientation Orientation, err error) {
} }
func (ad *adbDriver) Homescreen() (err error) { func (ad *adbDriver) Homescreen() (err error) {
return ad.PressKeyCode(KCHome, KMEmpty) return ad.PressKeyCodes(KCHome, KMEmpty)
} }
func (ad *adbDriver) PressKeyCode(keyCode KeyCode, metaState KeyMeta) (err error) { func (ad *adbDriver) Unlock() (err error) {
return ad.PressKeyCodes(KCMenu, KMEmpty)
}
func (ad *adbDriver) PressKeyCode(keyCode KeyCode) (err error) {
return ad.PressKeyCodes(keyCode, KMEmpty)
}
func (ad *adbDriver) PressKeyCodes(keyCode KeyCode, metaState KeyMeta) (err error) {
// adb shell input keyevent <keyCode> // adb shell input keyevent <keyCode>
_, err = ad.adbClient.RunShellCommand( _, err = ad.adbClient.RunShellCommand(
"input", "keyevent", fmt.Sprintf("%d", keyCode)) "input", "keyevent", fmt.Sprintf("%d", keyCode))
@@ -507,6 +516,10 @@ func (ad *adbDriver) Source(srcOpt ...SourceOption) (source string, err error) {
return return
} }
func (ad *adbDriver) LoginNoneUI(packageName, phoneNumber string, captcha string) error {
return errDriverNotImplemented
}
func (ad *adbDriver) sourceTree(srcOpt ...SourceOption) (sourceTree *Hierarchy, err error) { func (ad *adbDriver) sourceTree(srcOpt ...SourceOption) (sourceTree *Hierarchy, err error) {
source, err := ad.Source() source, err := ad.Source()
if err != nil { if err != nil {
@@ -693,51 +706,21 @@ func (ad *adbDriver) GetDriverResults() []*DriverResult {
} }
func (ad *adbDriver) GetForegroundApp() (app AppInfo, err error) { func (ad *adbDriver) GetForegroundApp() (app AppInfo, err error) {
// adb shell dumpsys activity activities packageInfo, err := ad.adbClient.RunShellCommand("CLASSPATH=/data/local/tmp/eval_tool", "app_process", "/", "com.bytedance.iesqa.eval_process.PackageService")
output, err := ad.adbClient.RunShellCommand("dumpsys", "activity", "activities")
if err != nil { if err != nil {
log.Error().Err(err).Msg("failed to dumpsys activities") return app, err
return AppInfo{}, errors.Wrap(err, "dumpsys activities failed")
} }
log.Info().Msg(packageInfo)
lines := strings.Split(string(output), "\n") err = json.Unmarshal([]byte(strings.TrimSpace(packageInfo)), &app)
for _, line := range lines { return
trimmedLine := strings.TrimSpace(line)
// grep mResumedActivity|ResumedActivity
if strings.HasPrefix(trimmedLine, "mResumedActivity:") || strings.HasPrefix(trimmedLine, "ResumedActivity:") {
// mResumedActivity: ActivityRecord{9656d74 u0 com.android.settings/.Settings t407}
// ResumedActivity: ActivityRecord{8265c25 u0 com.android.settings/.Settings t73}
strs := strings.Split(trimmedLine, " ")
for _, str := range strs {
if strings.Contains(str, "/") {
// com.android.settings/.Settings
s := strings.Split(str, "/")
app := AppInfo{
AppBaseInfo: AppBaseInfo{
PackageName: s[0],
Activity: s[1],
},
}
return app, nil
}
}
}
}
return AppInfo{}, errors.Wrap(code.MobileUIAssertForegroundAppError, "get foreground app failed")
} }
func (ad *adbDriver) GetFocusedPackage() (packageName string, err error) { func (ad *adbDriver) GetFocusedPackage() (packageName string, err error) {
res, err := ad.adbClient.RunShellCommand("dumpsys", "window", "windows", "|", "grep", "-E", "'mCurrentFocus|mFocusedApp'") res, err := ad.adbClient.RunShellCommand("dumpsys", "activity", "activities", "|", "grep", "-E", "'mResumedActivity'")
if err != nil { if err != nil {
return "", err return "", err
} }
match := regexp.MustCompile("mCurrentFocus.+\\s([^\\s/}]+)/[^\\s/}]+(\\.[^\\s/}]+)}").FindStringSubmatch(res) match := regexp.MustCompile(`mResumedActivity:.*? (\S+)/`).FindStringSubmatch(res)
if len(match) > 1 {
packageName = match[1]
return
}
match = regexp.MustCompile("mFocusedApp.+Record\\{.*\\s([^\\s/}]+)/([^\\s/}]+)(\\s[^\\s/}]+)*}").FindStringSubmatch(res)
if len(match) > 1 { if len(match) > 1 {
packageName = match[1] packageName = match[1]
return return
@@ -778,7 +761,7 @@ func (ad *adbDriver) SetIme(imeRegx string) error {
currentPackage, err := ad.GetFocusedPackage() currentPackage, err := ad.GetFocusedPackage()
log.Info().Str("beforeFocusedPackage", focusedPackage).Str("afterFocusedPackage", currentPackage).Msg("") log.Info().Str("beforeFocusedPackage", focusedPackage).Str("afterFocusedPackage", currentPackage).Msg("")
if err == nil && currentPackage != focusedPackage { if err == nil && currentPackage != focusedPackage {
_ = ad.PressKeyCode(KCBack, KMEmpty) _ = ad.PressKeyCodes(KCBack, KMEmpty)
} }
} }
} }

View File

@@ -4,9 +4,11 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"context" "context"
"embed"
"fmt" "fmt"
"os/exec" "os/exec"
"strings" "strings"
"time"
"github.com/httprunner/funplugin/myexec" "github.com/httprunner/funplugin/myexec"
"github.com/pkg/errors" "github.com/pkg/errors"
@@ -25,6 +27,9 @@ var (
UIA2ServerPort = 6790 UIA2ServerPort = 6790
) )
//go:embed eval_tool
var evalTool embed.FS
const forwardToPrefix = "forward-to-" const forwardToPrefix = "forward-to-"
type AndroidDeviceOption func(*AndroidDevice) type AndroidDeviceOption func(*AndroidDevice)
@@ -41,6 +46,12 @@ func WithUIA2(uia2On bool) AndroidDeviceOption {
} }
} }
func WithShoots(shootsOn bool) AndroidDeviceOption {
return func(device *AndroidDevice) {
device.SHOOTS = shootsOn
}
}
func WithUIA2IP(ip string) AndroidDeviceOption { func WithUIA2IP(ip string) AndroidDeviceOption {
return func(device *AndroidDevice) { return func(device *AndroidDevice) {
device.UIA2IP = ip device.UIA2IP = ip
@@ -109,7 +120,14 @@ func NewAndroidDevice(options ...AndroidDeviceOption) (device *AndroidDevice, er
device.d = dev device.d = dev
device.logcat = NewAdbLogcat(device.SerialNumber) device.logcat = NewAdbLogcat(device.SerialNumber)
evalToolRaw, err := evalTool.ReadFile("eval_tool")
if err != nil {
return nil, errors.Wrap(code.LoadFileError, err.Error())
}
err = dev.Push(bytes.NewReader(evalToolRaw), "/data/local/tmp/eval_tool", time.Now())
if err != nil {
return nil, errors.Wrap(code.AndroidShellExecError, err.Error())
}
log.Info().Str("serial", device.SerialNumber).Msg("init android device") log.Info().Str("serial", device.SerialNumber).Msg("init android device")
return device, nil return device, nil
} }
@@ -152,6 +170,7 @@ type AndroidDevice struct {
logcat *AdbLogcat logcat *AdbLogcat
SerialNumber string `json:"serial,omitempty" yaml:"serial,omitempty"` SerialNumber string `json:"serial,omitempty" yaml:"serial,omitempty"`
UIA2 bool `json:"uia2,omitempty" yaml:"uia2,omitempty"` // use uiautomator2 UIA2 bool `json:"uia2,omitempty" yaml:"uia2,omitempty"` // use uiautomator2
SHOOTS bool `json:"shoots,omitempty" yaml:"uia2,omitempty"` // use uiautomator2
UIA2IP string `json:"uia2_ip,omitempty" yaml:"uia2_ip,omitempty"` // uiautomator2 server ip UIA2IP string `json:"uia2_ip,omitempty" yaml:"uia2_ip,omitempty"` // uiautomator2 server ip
UIA2Port int `json:"uia2_port,omitempty" yaml:"uia2_port,omitempty"` // uiautomator2 server port UIA2Port int `json:"uia2_port,omitempty" yaml:"uia2_port,omitempty"` // uiautomator2 server port
LogOn bool `json:"log_on,omitempty" yaml:"log_on,omitempty"` LogOn bool `json:"log_on,omitempty" yaml:"log_on,omitempty"`
@@ -174,7 +193,7 @@ func (dev *AndroidDevice) LogEnabled() bool {
} }
func (dev *AndroidDevice) NewDriver(options ...DriverOption) (driverExt *DriverExt, err error) { func (dev *AndroidDevice) NewDriver(options ...DriverOption) (driverExt *DriverExt, err error) {
driverOptions := &DriverOptions{} driverOptions := NewDriverOptions()
for _, option := range options { for _, option := range options {
option(driverOptions) option(driverOptions)
} }
@@ -182,6 +201,8 @@ func (dev *AndroidDevice) NewDriver(options ...DriverOption) (driverExt *DriverE
var driver WebDriver var driver WebDriver
if dev.UIA2 || dev.LogOn { if dev.UIA2 || dev.LogOn {
driver, err = dev.NewUSBDriver(driverOptions.capabilities) driver, err = dev.NewUSBDriver(driverOptions.capabilities)
} else if dev.SHOOTS {
driver, err = dev.NewShootsDriver(driverOptions.capabilities)
} else { } else {
driver, err = dev.NewAdbDriver() driver, err = dev.NewAdbDriver()
} }
@@ -189,7 +210,7 @@ func (dev *AndroidDevice) NewDriver(options ...DriverOption) (driverExt *DriverE
return nil, errors.Wrap(err, "failed to init UIA driver") return nil, errors.Wrap(err, "failed to init UIA driver")
} }
driverExt, err = newDriverExt(dev, driver, driverOptions.plugin) driverExt, err = newDriverExt(dev, driver, options...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -226,6 +247,25 @@ func (dev *AndroidDevice) NewUSBDriver(capabilities Capabilities) (driver WebDri
return uiaDriver, nil return uiaDriver, nil
} }
func (dev *AndroidDevice) NewShootsDriver(capabilities Capabilities) (driver *ShootsAndroidDriver, err error) {
localPort, err := dev.d.Forward(ShootsSocketName)
if err != nil {
return nil, errors.Wrap(code.AndroidDeviceConnectionError,
fmt.Sprintf("forward port %d->%s failed: %v",
localPort, ShootsSocketName, err))
}
shootsDriver, err := newShootsAndroidDriver(fmt.Sprintf("127.0.0.1:%d", localPort))
if err != nil {
_ = dev.d.ForwardKill(localPort)
return nil, errors.Wrap(code.AndroidDeviceConnectionError, err.Error())
}
shootsDriver.adbClient = dev.d
shootsDriver.logcat = dev.logcat
return shootsDriver, nil
}
// NewHTTPDriver creates new remote HTTP client, this will also start a new session. // NewHTTPDriver creates new remote HTTP client, this will also start a new session.
func (dev *AndroidDevice) NewHTTPDriver(capabilities Capabilities) (driver WebDriver, err error) { func (dev *AndroidDevice) NewHTTPDriver(capabilities Capabilities) (driver WebDriver, err error) {
rawURL := fmt.Sprintf("http://%s:%d/wd/hub", dev.UIA2IP, dev.UIA2Port) rawURL := fmt.Sprintf("http://%s:%d/wd/hub", dev.UIA2IP, dev.UIA2Port)

View File

@@ -0,0 +1,235 @@
package uixt
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"net"
"time"
"github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v4/hrp/internal/json"
)
type ShootsAndroidDriver struct {
socket net.Conn
seq int
timeout time.Duration
adbDriver
}
const ShootsSocketName = "com.bytest.device"
// newShootsAndroidDriver
// 创建shoots Driver address为forward后的端口格式127.0.0.1:${port}
func newShootsAndroidDriver(address string, readTimeout ...time.Duration) (*ShootsAndroidDriver, error) {
timeout := 10 * time.Second
if len(readTimeout) > 0 {
timeout = readTimeout[0]
}
conn, err := net.Dial("tcp", address)
if err != nil {
log.Err(err).Msg(fmt.Sprintf("failed to connect %s", address))
return nil, err
}
return &ShootsAndroidDriver{
socket: conn,
timeout: timeout,
}, nil
}
func (sad *ShootsAndroidDriver) NewSession(capabilities Capabilities) (SessionInfo, error) {
return SessionInfo{}, errDriverNotImplemented
}
func (sad *ShootsAndroidDriver) sendCommand(packageName string, cmdType string, params map[string]interface{}, readTimeout ...time.Duration) (interface{}, error) {
sad.seq++
packet := map[string]interface{}{
"Seq": sad.seq,
"Cmd": cmdType,
"v": "",
}
for key, value := range params {
if key == "Cmd" || key == "Seq" {
return "", errors.New("params cannot be Cmd or Seq")
}
packet[key] = value
}
data, err := json.Marshal(packet)
if err != nil {
return nil, err
}
res, err := sad.adbClient.RunShootsCommand(append(data, '\n'), packageName)
if err != nil {
return nil, err
}
var resultMap map[string]interface{}
if err := json.Unmarshal([]byte(res), &resultMap); err != nil {
return nil, err
}
if resultMap["Error"] != nil {
return nil, fmt.Errorf("failed to call shoots command: %s", resultMap["Error"].(string))
}
return resultMap["Result"], nil
}
func (sad *ShootsAndroidDriver) send(data []byte, readTimeout ...time.Duration) (map[string]interface{}, error) {
timeout := sad.timeout
if len(readTimeout) > 0 {
timeout = readTimeout[0]
}
_ = sad.socket.SetReadDeadline(time.Now().Add(timeout))
err := _send(sad.socket, append(data, '\n'))
if err != nil {
sad.close()
return nil, err
}
raw, err := _readAll(sad.socket)
if err != nil {
return nil, err
}
var result map[string]interface{}
if err := json.Unmarshal(raw, &result); err != nil {
log.Printf("error when parse json response: %s\n", raw)
return nil, err
}
return result, nil
}
func _send(writer io.Writer, msg []byte) (err error) {
for totalSent := 0; totalSent < len(msg); {
var sent int
if sent, err = writer.Write(msg[totalSent:]); err != nil {
return err
}
if sent == 0 {
return errors.New("socket connection broken")
}
totalSent += sent
}
return
}
func _readN(reader io.Reader, size int) (raw []byte, err error) {
raw = make([]byte, 0, size)
for len(raw) < size {
buf := make([]byte, size-len(raw))
var n int
if n, err = io.ReadFull(reader, buf); err != nil {
return nil, err
}
if n == 0 {
return nil, errors.New("socket connection broken")
}
raw = append(raw, buf...)
}
return
}
func _readAll(reader io.Reader) (raw []byte, err error) {
buffer := new(bytes.Buffer)
for true {
lengthBuf := make([]byte, 4)
_, err := io.ReadFull(reader, lengthBuf)
if err != nil {
if err == io.EOF {
return buffer.Bytes(), nil
} else if errors.Is(err, io.ErrUnexpectedEOF) {
err = fmt.Errorf("reached unexpected EOF, read partial data: %s %v", string(buffer.Bytes()), err)
return nil, err
} else {
return nil, err
}
}
length := binary.BigEndian.Uint32(lengthBuf)
data, err := _readN(reader, int(length)-4)
if err != nil {
return nil, err
}
buffer.Write(data)
}
return buffer.Bytes(), nil
}
func (sad *ShootsAndroidDriver) DeleteSession() error {
return sad.close()
}
func (sad *ShootsAndroidDriver) close() error {
if sad.socket != nil {
return sad.socket.Close()
}
return nil
}
func (sad *ShootsAndroidDriver) Status() (DeviceStatus, error) {
app, err := sad.GetForegroundApp()
res, err := sad.sendCommand(app.PackageName, "Hello", nil)
if err != nil {
return DeviceStatus{}, err
}
log.Info().Msg(fmt.Sprintf("pint shoots result :%v", res))
return DeviceStatus{}, nil
}
func (sad *ShootsAndroidDriver) Source(srcOpt ...SourceOption) (source string, err error) {
app, err := sad.GetForegroundApp()
params := map[string]interface{}{
"ClassName": "com.bytedance.byteinsight.MockOperator",
"Method": "getLayout",
"RetType": "",
"Args": []string{},
}
res, err := sad.sendCommand(app.PackageName, "CallStaticMethod", params)
if err != nil {
return "", err
}
return res.(string), nil
}
func (sad *ShootsAndroidDriver) LoginNoneUI(packageName, phoneNumber string, captcha string) error {
_, err := sad.adbClient.RunShellCommand("am", "broadcast", "-a", fmt.Sprintf("%s.util.crony.action_login", packageName), "-e", "phone", phoneNumber, "-e", "code", captcha)
time.Sleep(5 * time.Second)
login, err := sad.isLogin(packageName)
if err != nil || !login {
log.Err(err).Msg("failed to login")
return fmt.Errorf("failed to login")
}
return err
}
func (sad *ShootsAndroidDriver) isLogin(packageName string) (login bool, err error) {
params := map[string]interface{}{
"ClassName": "com.ss.android.ugc.aweme.account.AccountProxyService",
"Method": "userService",
"RetType": "",
"Args": []string{},
"CacheObject": true,
}
id, err := sad.sendCommand(packageName, "CallStaticMethod", params)
if err != nil {
return false, err
}
params = map[string]interface{}{
"Method": "isLogin",
"RetType": "",
"Args": []string{},
"ObjectId": int(id.(float64)),
}
loginObj, err := sad.sendCommand(packageName, "CallMethod", params)
if err != nil {
return false, err
}
return loginObj.(bool), nil
}

View File

@@ -0,0 +1,40 @@
package uixt
import "testing"
var driver *ShootsAndroidDriver
func setupAndroid(t *testing.T) {
device, err := NewAndroidDevice()
checkErr(t, err)
device.SHOOTS = true
driver, err = device.NewShootsDriver(Capabilities{})
checkErr(t, err)
}
func TestHello(t *testing.T) {
setupAndroid(t)
status, err := driver.Status()
if err != nil {
t.Fatal(err)
}
t.Log(status)
}
func TestSource(t *testing.T) {
setupAndroid(t)
source, err := driver.Source()
if err != nil {
t.Fatal(err)
}
t.Log(source)
}
func TestLogin(t *testing.T) {
setupAndroid(t)
res, err := driver.isLogin("com.ss.android.ugc.aweme")
if err != nil {
t.Fatal(err)
}
t.Log(res)
}

View File

@@ -250,10 +250,14 @@ func (ud *uiaDriver) PressBack(options ...ActionOption) (err error) {
} }
func (ud *uiaDriver) Homescreen() (err error) { func (ud *uiaDriver) Homescreen() (err error) {
return ud.PressKeyCode(KCHome, KMEmpty) return ud.PressKeyCodes(KCHome, KMEmpty)
} }
func (ud *uiaDriver) PressKeyCode(keyCode KeyCode, metaState KeyMeta, flags ...KeyFlag) (err error) { func (ud *uiaDriver) PressKeyCode(keyCode KeyCode) (err error) {
return ud.PressKeyCodes(keyCode, KMEmpty)
}
func (ud *uiaDriver) PressKeyCodes(keyCode KeyCode, metaState KeyMeta, flags ...KeyFlag) (err error) {
// register(postHandler, new PressKeyCodeAsync("/wd/hub/session/:sessionId/appium/device/press_keycode")) // register(postHandler, new PressKeyCodeAsync("/wd/hub/session/:sessionId/appium/device/press_keycode"))
data := map[string]interface{}{ data := map[string]interface{}{
"keycode": keyCode, "keycode": keyCode,

BIN
hrp/pkg/uixt/eval_tool Normal file

Binary file not shown.

View File

@@ -155,11 +155,16 @@ type DriverExt struct {
plugin funplugin.IPlugin plugin funplugin.IPlugin
} }
func newDriverExt(device Device, driver WebDriver, plugin funplugin.IPlugin) (dExt *DriverExt, err error) { func newDriverExt(device Device, driver WebDriver, options ...DriverOption) (dExt *DriverExt, err error) {
driverOptions := &DriverOptions{}
for _, option := range options {
option(driverOptions)
}
dExt = &DriverExt{ dExt = &DriverExt{
Device: device, Device: device,
Driver: driver, Driver: driver,
plugin: plugin, plugin: driverOptions.plugin,
cacheStepData: cacheStepData{}, cacheStepData: cacheStepData{},
interruptSignal: make(chan os.Signal, 1), interruptSignal: make(chan os.Signal, 1),
} }
@@ -179,17 +184,19 @@ func newDriverExt(device Device, driver WebDriver, plugin funplugin.IPlugin) (dE
if err != nil { if err != nil {
return nil, errors.Wrap(err, "get screen resolution failed") return nil, errors.Wrap(err, "get screen resolution failed")
} }
if driverOptions.withImageService {
if dExt.ImageService, err = newVEDEMImageService(); err != nil { if dExt.ImageService, err = newVEDEMImageService(); err != nil {
return nil, err return nil, err
}
} }
if driverOptions.withResultFolder {
// create results directory // create results directory
if err = builtin.EnsureFolderExists(env.ResultsPath); err != nil { if err = builtin.EnsureFolderExists(env.ResultsPath); err != nil {
return nil, errors.Wrap(err, "create results directory failed") return nil, errors.Wrap(err, "create results directory failed")
} }
if err = builtin.EnsureFolderExists(env.ScreenShotsPath); err != nil { if err = builtin.EnsureFolderExists(env.ScreenShotsPath); err != nil {
return nil, errors.Wrap(err, "create screenshots directory failed") return nil, errors.Wrap(err, "create screenshots directory failed")
}
} }
return dExt, nil return dExt, nil
} }

View File

@@ -252,11 +252,7 @@ type Screen struct {
} }
type AppInfo struct { type AppInfo struct {
ProcessArguments struct { Name string `json:"name,omitempty"`
Env interface{} `json:"env"`
Args []interface{} `json:"args"`
} `json:"processArguments"`
Name string `json:"name"`
AppBaseInfo AppBaseInfo
} }
@@ -266,6 +262,10 @@ type AppBaseInfo struct {
ViewController string `json:"viewController,omitempty"` // ios view controller ViewController string `json:"viewController,omitempty"` // ios view controller
PackageName string `json:"packageName,omitempty"` // android package name PackageName string `json:"packageName,omitempty"` // android package name
Activity string `json:"activity,omitempty"` // android activity Activity string `json:"activity,omitempty"` // android activity
VersionName string `json:"versionName,omitempty"`
VersionCode int `json:"versionCode,omitempty"`
AppName string `json:"appName,omitempty"`
// AppIcon string `json:"appIcon,omitempty"`
} }
type AppState int type AppState int
@@ -376,6 +376,11 @@ func (opt SourceOption) WithFormatAsJson() SourceOption {
return opt return opt
} }
func (opt SourceOption) WithProcessName(processName string) SourceOption {
opt["processName"] = processName
return opt
}
// WithFormatAsXml Application elements tree in form of xml string // WithFormatAsXml Application elements tree in form of xml string
func (opt SourceOption) WithFormatAsXml() SourceOption { func (opt SourceOption) WithFormatAsXml() SourceOption {
opt["format"] = "xml" opt["format"] = "xml"
@@ -448,8 +453,17 @@ type Rect struct {
} }
type DriverOptions struct { type DriverOptions struct {
capabilities Capabilities capabilities Capabilities
plugin funplugin.IPlugin plugin funplugin.IPlugin
withImageService bool
withResultFolder bool
}
func NewDriverOptions() *DriverOptions {
return &DriverOptions{
withImageService: true,
withResultFolder: true,
}
} }
type DriverOption func(*DriverOptions) type DriverOption func(*DriverOptions)
@@ -460,6 +474,18 @@ func WithDriverCapabilities(capabilities Capabilities) DriverOption {
} }
} }
func WithDriverImageService(withImageService bool) DriverOption {
return func(options *DriverOptions) {
options.withImageService = withImageService
}
}
func WithDriverResultFolder(withResultFolder bool) DriverOption {
return func(options *DriverOptions) {
options.withResultFolder = withResultFolder
}
}
func WithDriverPlugin(plugin funplugin.IPlugin) DriverOption { func WithDriverPlugin(plugin funplugin.IPlugin) DriverOption {
return func(options *DriverOptions) { return func(options *DriverOptions) {
options.plugin = plugin options.plugin = plugin
@@ -526,6 +552,8 @@ type WebDriver interface {
// Homescreen Forces the device under test to switch to the home screen // Homescreen Forces the device under test to switch to the home screen
Homescreen() error Homescreen() error
Unlock() (err error)
// AppLaunch Launch an application with given bundle identifier in scope of current session. // AppLaunch Launch an application with given bundle identifier in scope of current session.
// !This method is only available since Xcode9 SDK // !This method is only available since Xcode9 SDK
AppLaunch(packageName string) error AppLaunch(packageName string) error
@@ -588,11 +616,15 @@ type WebDriver interface {
// PressBack Presses the back button // PressBack Presses the back button
PressBack(options ...ActionOption) error PressBack(options ...ActionOption) error
PressKeyCode(keyCode KeyCode) (err error)
Screenshot() (*bytes.Buffer, error) Screenshot() (*bytes.Buffer, error)
// Source Return application elements tree // Source Return application elements tree
Source(srcOpt ...SourceOption) (string, error) Source(srcOpt ...SourceOption) (string, error)
LoginNoneUI(packageName, phoneNumber string, captcha string) error
TapByText(text string, options ...ActionOption) error TapByText(text string, options ...ActionOption) error
TapByTexts(actions ...TapTextAction) error TapByTexts(actions ...TapTextAction) error

View File

@@ -308,7 +308,7 @@ func (dev *IOSDevice) LogEnabled() bool {
} }
func (dev *IOSDevice) NewDriver(options ...DriverOption) (driverExt *DriverExt, err error) { func (dev *IOSDevice) NewDriver(options ...DriverOption) (driverExt *DriverExt, err error) {
driverOptions := &DriverOptions{} driverOptions := NewDriverOptions()
for _, option := range options { for _, option := range options {
option(driverOptions) option(driverOptions)
} }
@@ -339,7 +339,7 @@ func (dev *IOSDevice) NewDriver(options ...DriverOption) (driverExt *DriverExt,
} }
} }
driverExt, err = newDriverExt(dev, driver, driverOptions.plugin) driverExt, err = newDriverExt(dev, driver, options...)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -595,6 +595,10 @@ func (wd *wdaDriver) SetIme(ime string) error {
return errDriverNotImplemented return errDriverNotImplemented
} }
func (wd *wdaDriver) PressKeyCode(keyCode KeyCode) (err error) {
return errDriverNotImplemented
}
func (wd *wdaDriver) SendKeys(text string, options ...ActionOption) (err error) { func (wd *wdaDriver) SendKeys(text string, options ...ActionOption) (err error) {
// [[FBRoute POST:@"/wda/keys"] respondWithTarget:self action:@selector(handleKeys:)] // [[FBRoute POST:@"/wda/keys"] respondWithTarget:self action:@selector(handleKeys:)]
actionOptions := NewActionOptions(options...) actionOptions := NewActionOptions(options...)
@@ -655,6 +659,10 @@ func (wd *wdaDriver) PressButton(devBtn DeviceButton) (err error) {
return return
} }
func (wd *wdaDriver) LoginNoneUI(packageName, phoneNumber string, captcha string) error {
return errDriverNotImplemented
}
func (wd *wdaDriver) StartCamera() (err error) { func (wd *wdaDriver) StartCamera() (err error) {
// start camera, alias for app_launch com.apple.camera // start camera, alias for app_launch com.apple.camera
return wd.AppLaunch("com.apple.camera") return wd.AppLaunch("com.apple.camera")