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

@@ -42,8 +42,8 @@ var installCmd = &cobra.Command{
replace, _ := cmd.Flags().GetBool("replace")
downgrade, _ := cmd.Flags().GetBool("downgrade")
grant, _ := cmd.Flags().GetBool("grant")
option := uixt.InstallOptions{Reinstall: replace, GrantPermission: grant, Downgrade: downgrade}
err = driverExt.Install(args[0], option)
err = driverExt.Install(args[0], uixt.NewInstallOptions(uixt.WithReinstall(replace), uixt.WithDowngrade(downgrade), uixt.WithGrantPermission(grant)))
if err != nil {
fmt.Println(err)
return err

52
hrp/cmd/ios/install.go Normal file
View File

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

View File

@@ -8,9 +8,11 @@ import (
"encoding/csv"
builtinJSON "encoding/json"
"fmt"
"io"
"math"
"math/rand"
"net"
"net/http"
"os"
"path/filepath"
"reflect"
@@ -509,3 +511,29 @@ func GetCurrentDay() string {
formattedDate := now.Format("20060102")
return formattedDate
}
func DownloadFile(filePath string, url string) error {
log.Info().Str("filePath", filePath).Str("url", url).Msg("download file")
out, err := os.Create(filePath)
if err != nil {
return err
}
defer out.Close()
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("bad status: %s, download failed", resp.Status)
}
_, err = io.Copy(out, resp.Body)
if err != nil {
return err
}
return nil
}

View File

@@ -1 +1 @@
v4.6.2
v4.6.3

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 {