feat: 新增双端安装卸载 安卓清楚缓存

This commit is contained in:
余泓铮
2024-08-06 20:30:18 +08:00
parent 7c1b5ce57e
commit c8a42e951a
11 changed files with 236 additions and 47 deletions

View File

@@ -17,6 +17,7 @@ type ActionMethod string
const (
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"
@@ -64,6 +65,9 @@ const (
ACTION_VideoCrawler ActionMethod = "video_crawler"
ACTION_ClosePopups ActionMethod = "close_popups"
ACTION_EndToEndDelay ActionMethod = "live_e2e"
ACTION_InstallApp ActionMethod = "install_app"
ACTION_UninstallApp ActionMethod = "uninstall_app"
ACTION_DownloadApp ActionMethod = "download_app"
)
type MobileAction struct {
@@ -558,8 +562,23 @@ func (dExt *DriverExt) DoAction(action MobileAction) (err error) {
switch action.Method {
case ACTION_AppInstall:
// TODO
return errActionNotImplemented
if appUrl, ok := action.Params.(string); ok {
if err = dExt.InstallByUrl(appUrl, NewInstallOptions(WithRetryTime(action.MaxRetryTimes))); err != nil {
return errors.Wrap(err, "failed to install app")
}
}
case ACTION_AppUninstall:
if packageName, ok := action.Params.(string); ok {
if err = dExt.Uninstall(packageName); err != nil {
return errors.Wrap(err, "failed to uninstall app")
}
}
case ACTION_AppClear:
if packageName, ok := action.Params.(string); ok {
if err = dExt.Driver.Clear(packageName); err != nil {
return errors.Wrap(err, "failed to clear app")
}
}
case ACTION_AppLaunch:
if bundleId, ok := action.Params.(string); ok {
return dExt.Driver.AppLaunch(bundleId)

View File

@@ -11,6 +11,7 @@ import (
"fmt"
"io"
"net"
"os"
"os/exec"
"regexp"
"strconv"
@@ -328,7 +329,16 @@ func (dev *AndroidDevice) StopPcap() string {
return ""
}
func (dev *AndroidDevice) Install(app io.ReadSeeker, opts InstallOptions) error {
func (dev *AndroidDevice) Uninstall(packageName string) error {
return myexec.RunCommand("adb", "-s", dev.SerialNumber, "uninstall", packageName)
}
func (dev *AndroidDevice) Install(appPath string, opts *InstallOptions) error {
app, err := os.Open(appPath)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("install %s open file failed", appPath))
}
brand, err := dev.d.Brand()
if err != nil {
return err

View File

@@ -50,18 +50,6 @@ func WithThreshold(threshold float64) CVOption {
}
}
type InstallOptions struct {
Reinstall bool
GrantPermission bool
Downgrade bool
}
type InstallResult struct {
Result int `json:"result"`
ErrorCode int `json:"errorCode"`
ErrorMsg string `json:"errorMsg"`
}
type ScreenResult struct {
bufSource *bytes.Buffer // raw image buffer bytes
imagePath string // image file path
@@ -213,35 +201,59 @@ func newDriverExt(device Device, driver WebDriver, options ...DriverOption) (dEx
return dExt, nil
}
func (dExt *DriverExt) Install(filePath string, opts InstallOptions) error {
app, err := os.Open(filePath)
func (dExt *DriverExt) InstallByUrl(url string, opts *InstallOptions) error {
// 获取当前目录
cwd, err := os.Getwd()
if err != nil {
return errors.Wrap(err, fmt.Sprintf("install %s open file failed", filePath))
return err
}
stopChan := make(chan struct{})
go func() {
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
actions := []TapTextAction{
{Text: "^.*无视风险安装$", Options: []ActionOption{WithTapOffset(100, 0), WithRegex(true), WithIgnoreNotFoundError(true)}},
{Text: "^已了解此应用未经检测.*", Options: []ActionOption{WithTapOffset(-450, 0), WithRegex(true), WithIgnoreNotFoundError(true)}},
// 将文件保存到当前目录
appPath := filepath.Join(cwd, fmt.Sprint(time.Now().UnixNano())) // 替换为你想保存的文件名
err = builtin.DownloadFile(appPath, url)
if err != nil {
return err
}
err = dExt.Install(appPath, opts)
if err != nil {
return err
}
return nil
}
func (dExt *DriverExt) Uninstall(packageName string) error {
return dExt.Device.Uninstall(packageName)
}
func (dExt *DriverExt) Install(filePath string, opts *InstallOptions) error {
if _, ok := dExt.Device.(*AndroidDevice); ok {
stopChan := make(chan struct{})
go func() {
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
actions := []TapTextAction{
{Text: "^.*无视风险安装$", Options: []ActionOption{WithTapOffset(100, 0), WithRegex(true), WithIgnoreNotFoundError(true)}},
{Text: "^已了解此应用未经检测.*", Options: []ActionOption{WithTapOffset(-450, 0), WithRegex(true), WithIgnoreNotFoundError(true)}},
}
_ = dExt.Driver.TapByTexts(actions...)
_ = dExt.TapByOCR("^(.*无视风险安装|确定|继续|完成|点击继续安装|继续安装旧版本|替换|安装|授权本次安装|继续安装|重新安装)$", WithRegex(true), WithIgnoreNotFoundError(true))
case <-stopChan:
fmt.Println("Ticker stopped")
return
}
_ = dExt.Driver.TapByTexts(actions...)
_ = dExt.TapByOCR("^(.*无视风险安装|确定|继续|完成|点击继续安装|继续安装旧版本|替换|安装|授权本次安装|继续安装|重新安装)$", WithRegex(true), WithIgnoreNotFoundError(true))
case <-stopChan:
fmt.Println("Ticker stopped")
return
}
}
}()
defer func() {
close(stopChan)
}()
return dExt.Device.Install(app, opts)
}()
defer func() {
close(stopChan)
}()
}
return dExt.Device.Install(filePath, opts)
}
// takeScreenShot takes screenshot and saves image file to $CWD/screenshots/ folder

48
hrp/pkg/uixt/install.go Normal file
View File

@@ -0,0 +1,48 @@
package uixt
type InstallOptions struct {
Reinstall bool
GrantPermission bool
Downgrade bool
RetryTime int
}
type InstallOption func(o *InstallOptions)
func NewInstallOptions(options ...InstallOption) *InstallOptions {
installOptions := &InstallOptions{}
for _, option := range options {
option(installOptions)
}
return installOptions
}
func WithReinstall(reinstall bool) InstallOption {
return func(o *InstallOptions) {
o.Reinstall = reinstall
}
}
func WithGrantPermission(grantPermission bool) InstallOption {
return func(o *InstallOptions) {
o.GrantPermission = grantPermission
}
}
func WithDowngrade(downgrade bool) InstallOption {
return func(o *InstallOptions) {
o.Downgrade = downgrade
}
}
func WithRetryTime(retryTime int) InstallOption {
return func(o *InstallOptions) {
o.RetryTime = retryTime
}
}
type InstallResult struct {
Result int `json:"result"`
ErrorCode int `json:"errorCode"`
ErrorMsg string `json:"errorMsg"`
}

View File

@@ -2,7 +2,6 @@ package uixt
import (
"bytes"
"io"
"math"
"strings"
"time"
@@ -506,7 +505,9 @@ type Device interface {
StartPcap() error
StopPcap() string
Install(app io.ReadSeeker, opts InstallOptions) error
Uninstall(packageName string) error
Install(appPath string, opts *InstallOptions) error
}
type ForegroundApp struct {

View File

@@ -13,6 +13,7 @@ import (
"strconv"
"time"
"github.com/httprunner/funplugin/myexec"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
@@ -472,8 +473,18 @@ func (dev *IOSDevice) StopPcap() string {
return dev.pcapFile
}
func (dev *IOSDevice) Install(app io.ReadSeeker, opts InstallOptions) error {
return errors.New("install method not implemented")
func (dev *IOSDevice) Install(appPath string, opts *InstallOptions) (err error) {
for i := 0; i < opts.RetryTime; i++ {
err = myexec.RunCommand("ideviceinstaller", "-u", dev.UDID, "-i", appPath)
if err == nil {
return nil
}
}
return err
}
func (dev *IOSDevice) Uninstall(bundleId string) error {
return myexec.RunCommand("ideviceinstaller", "-u", dev.UDID, "-U", bundleId)
}
func (dev *IOSDevice) forward(localPort, remotePort int) error {

View File

@@ -26,7 +26,7 @@ func setup(t *testing.T) {
if err != nil {
t.Fatal(err)
}
iOSDriverExt, err = newDriverExt(device, driver, nil)
iOSDriverExt, err = newDriverExt(device, driver)
if err != nil {
t.Fatal(err)
}
@@ -37,6 +37,14 @@ func TestViaUSB(t *testing.T) {
t.Log(driver.Status())
}
func TestInstall(t *testing.T) {
setup(t)
err := iOSDriverExt.Install("/Users/bytedance/workcode/httprunner/hrp/pkg/uixt/1722942152176906000", NewInstallOptions(WithRetryTime(5)))
if err != nil {
t.Fatal(err)
}
}
func TestNewIOSDevice(t *testing.T) {
device, _ := NewIOSDevice()
if device != nil {