From f77b454cf28ec77b4f809c9e560cd4f33fa0e23b Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Fri, 14 Apr 2023 13:29:22 +0800 Subject: [PATCH 01/12] change: take screenshot after each step --- hrp/step_mobile_ui.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/hrp/step_mobile_ui.go b/hrp/step_mobile_ui.go index a193f0b8..56ee9e4f 100644 --- a/hrp/step_mobile_ui.go +++ b/hrp/step_mobile_ui.go @@ -563,6 +563,16 @@ func runStepMobileUI(s *SessionRunner, step *TStep) (stepResult *StepResult, err attachments["error"] = err.Error() } + // take screenshot after each step + screenshotPath, err := uiDriver.ScreenShot( + fmt.Sprintf("step_%d", time.Now().Unix())) + if err != nil { + log.Error().Err(err).Str("step", step.Name).Msg("take screenshot failed") + } else { + log.Info().Str("path", screenshotPath).Msg("take screenshot on step finished") + screenshots = append(screenshots, screenshotPath) + } + // save attachments screenshots = append(screenshots, uiDriver.ScreenShots...) attachments["screenshots"] = screenshots @@ -599,16 +609,6 @@ func runStepMobileUI(s *SessionRunner, step *TStep) (stepResult *StepResult, err } } - // take snapshot - screenshotPath, err := uiDriver.ScreenShot( - fmt.Sprintf("validate_%d", time.Now().Unix())) - if err != nil { - log.Warn().Err(err).Str("step", step.Name).Msg("take screenshot failed") - } else { - log.Info().Str("path", screenshotPath).Msg("take screenshot before validation") - screenshots = append(screenshots, screenshotPath) - } - // validate validateResults, err := validateUI(uiDriver, step.Validators) if err != nil { From c97b84dd39befc7a2ce193ecb9b239f2d2df9f06 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Fri, 14 Apr 2023 15:26:16 +0800 Subject: [PATCH 02/12] refactor: get attached android/ios devices --- hrp/cmd/adb/devices.go | 20 ++--------- hrp/cmd/ios/devices.go | 14 ++------ hrp/cmd/ios/init.go | 9 ++--- hrp/pkg/uixt/android_device.go | 61 +++++++++++++++++++++------------- hrp/pkg/uixt/android_test.go | 2 +- hrp/pkg/uixt/ios_device.go | 43 +++++++++++++----------- 6 files changed, 70 insertions(+), 79 deletions(-) diff --git a/hrp/cmd/adb/devices.go b/hrp/cmd/adb/devices.go index 28eb8bad..3a0de351 100644 --- a/hrp/cmd/adb/devices.go +++ b/hrp/cmd/adb/devices.go @@ -5,10 +5,8 @@ import ( "fmt" "os" - "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/httprunner/httprunner/v4/hrp/pkg/gadb" "github.com/httprunner/httprunner/v4/hrp/pkg/uixt" ) @@ -21,22 +19,10 @@ var listAndroidDevicesCmd = &cobra.Command{ Use: "devices", Short: "List all Android devices", RunE: func(cmd *cobra.Command, args []string) error { - devices, err := uixt.DeviceList() + deviceList, err := uixt.GetAndroidDevices(serial) if err != nil { - return errors.Wrap(err, "list android devices failed") - } - - var deviceList []*gadb.Device - // filter by serial - for _, d := range devices { - if serial != "" && serial != d.Serial() { - continue - } - deviceList = append(deviceList, d) - } - if serial != "" && len(deviceList) == 0 { - fmt.Printf("no android device found for serial: %s\n", serial) - os.Exit(1) + fmt.Println(err) + os.Exit(0) } for _, d := range deviceList { diff --git a/hrp/cmd/ios/devices.go b/hrp/cmd/ios/devices.go index c1f230d3..ad8fb55c 100644 --- a/hrp/cmd/ios/devices.go +++ b/hrp/cmd/ios/devices.go @@ -70,18 +70,10 @@ var listDevicesCmd = &cobra.Command{ Short: "List all iOS devices", PersistentPreRun: func(cmd *cobra.Command, args []string) {}, RunE: func(cmd *cobra.Command, args []string) error { - devices, err := uixt.IOSDevices(udid) + devices, err := uixt.GetIOSDevices(udid) if err != nil { - return err - } - if len(devices) == 0 { - if udid != "" { - fmt.Printf("no ios device found for udid: %s\n", udid) - os.Exit(1) - } else { - fmt.Println("no ios device found") - os.Exit(0) - } + fmt.Println(err) + os.Exit(0) } for _, d := range devices { diff --git a/hrp/cmd/ios/init.go b/hrp/cmd/ios/init.go index 209846fb..0a285f17 100644 --- a/hrp/cmd/ios/init.go +++ b/hrp/cmd/ios/init.go @@ -2,7 +2,6 @@ package ios import ( "fmt" - "os" "github.com/spf13/cobra" @@ -16,16 +15,12 @@ var iosRootCmd = &cobra.Command{ } func getDevice(udid string) (gidevice.Device, error) { - devices, err := uixt.IOSDevices(udid) + devices, err := uixt.GetIOSDevices(udid) if err != nil { return nil, err } - if len(devices) == 0 { - fmt.Println("no ios device found") - os.Exit(1) - } if len(devices) > 1 { - return nil, fmt.Errorf("multiple devices found, please specify udid") + return nil, fmt.Errorf("multiple devices found, please specify ios udid") } return devices[0], nil } diff --git a/hrp/pkg/uixt/android_device.go b/hrp/pkg/uixt/android_device.go index ad80f6e1..52063250 100644 --- a/hrp/pkg/uixt/android_device.go +++ b/hrp/pkg/uixt/android_device.go @@ -81,15 +81,6 @@ func GetAndroidDeviceOptions(dev *AndroidDevice) (deviceOptions []AndroidDeviceO // uiautomator2 server must be started before // adb shell am instrument -w io.appium.uiautomator2.server.test/androidx.test.runner.AndroidJUnitRunner func NewAndroidDevice(options ...AndroidDeviceOption) (device *AndroidDevice, err error) { - deviceList, err := DeviceList() - if err != nil { - return nil, errors.Wrap(code.AndroidDeviceConnectionError, - fmt.Sprintf("get attached devices failed: %v", err)) - } else if len(deviceList) == 0 { - return nil, errors.Wrap(code.AndroidDeviceConnectionError, - "not attached device found") - } - device = &AndroidDevice{ UIA2IP: UIA2ServerHost, UIA2Port: UIA2ServerPort, @@ -98,30 +89,52 @@ func NewAndroidDevice(options ...AndroidDeviceOption) (device *AndroidDevice, er option(device) } - serialNumber := device.SerialNumber - for _, dev := range deviceList { - // find device by serial number if specified - if serialNumber != "" && dev.Serial() != serialNumber { - continue - } - - device.SerialNumber = dev.Serial() - device.d = dev - device.logcat = NewAdbLogcat(device.SerialNumber) - return device, nil + deviceList, err := GetAndroidDevices(device.SerialNumber) + if err != nil { + return nil, errors.Wrap(code.AndroidDeviceConnectionError, err.Error()) } - return nil, errors.Wrap(code.AndroidDeviceConnectionError, - fmt.Sprintf("device %s not found", device.SerialNumber)) + dev := deviceList[0] + device.SerialNumber = dev.Serial() + device.d = dev + device.logcat = NewAdbLogcat(device.SerialNumber) + + log.Info().Str("serial", device.SerialNumber).Msg("select android device") + return device, nil } -func DeviceList() (devices []*gadb.Device, err error) { +func GetAndroidDevices(serial ...string) (devices []*gadb.Device, err error) { var adbClient gadb.Client if adbClient, err = gadb.NewClientWith(AdbServerHost, AdbServerPort); err != nil { return nil, errors.Wrap(code.AndroidDeviceConnectionError, err.Error()) } - return adbClient.DeviceList() + if devices, err = adbClient.DeviceList(); err != nil { + return nil, errors.Wrap(code.AndroidDeviceConnectionError, + fmt.Sprintf("list android devices failed: %v", err)) + } + + var deviceList []*gadb.Device + // filter by serial + for _, d := range devices { + for _, s := range serial { + if s != "" && s != d.Serial() { + continue + } + deviceList = append(deviceList, d) + } + } + + if len(deviceList) == 0 { + var err error + if serial == nil || (len(serial) == 1 && serial[0] == "") { + err = fmt.Errorf("no android device found") + } else { + err = fmt.Errorf("no android device found for serial %v", serial) + } + return nil, err + } + return deviceList, nil } type AndroidDevice struct { diff --git a/hrp/pkg/uixt/android_test.go b/hrp/pkg/uixt/android_test.go index 44d8d3ac..17d61f97 100644 --- a/hrp/pkg/uixt/android_test.go +++ b/hrp/pkg/uixt/android_test.go @@ -324,7 +324,7 @@ func Test_getFreePort(t *testing.T) { } func TestDeviceList(t *testing.T) { - devices, err := DeviceList() + devices, err := GetAndroidDevices() if err != nil { t.Fatal(err) } diff --git a/hrp/pkg/uixt/ios_device.go b/hrp/pkg/uixt/ios_device.go index c7fc3555..93828a8d 100644 --- a/hrp/pkg/uixt/ios_device.go +++ b/hrp/pkg/uixt/ios_device.go @@ -141,7 +141,7 @@ func WithIOSPcapOptions(options ...gidevice.PcapOption) IOSDeviceOption { } } -func IOSDevices(udid ...string) (devices []gidevice.Device, err error) { +func GetIOSDevices(udid ...string) (devices []gidevice.Device, err error) { var usbmux gidevice.Usbmux if usbmux, err = gidevice.NewUsbmux(); err != nil { return nil, errors.Wrap(code.IOSDeviceConnectionError, @@ -168,6 +168,15 @@ func IOSDevices(udid ...string) (devices []gidevice.Device, err error) { } } + if len(deviceList) == 0 { + var err error + if udid == nil || (len(udid) == 1 && udid[0] == "") { + err = fmt.Errorf("no ios device found") + } else { + err = fmt.Errorf("no ios device found for udid %v", udid) + } + return nil, err + } return deviceList, nil } @@ -223,31 +232,27 @@ func NewIOSDevice(options ...IOSDeviceOption) (device *IOSDevice, err error) { option(device) } - deviceList, err := IOSDevices(device.UDID) + deviceList, err := GetIOSDevices(device.UDID) if err != nil { - return nil, err + return nil, errors.Wrap(code.IOSDeviceConnectionError, err.Error()) } - for _, dev := range deviceList { - udid := dev.Properties().SerialNumber - device.UDID = udid - device.d = dev + dev := deviceList[0] + udid := dev.Properties().SerialNumber + device.UDID = udid + device.d = dev - // run xctest if XCTestBundleID is set - if device.XCTestBundleID != "" { - _, err = device.RunXCTest(device.XCTestBundleID) - if err != nil { - log.Error().Err(err).Str("udid", udid).Msg("failed to init XCTest") - continue - } + // run xctest if XCTestBundleID is set + if device.XCTestBundleID != "" { + _, err = device.RunXCTest(device.XCTestBundleID) + if err != nil { + log.Error().Err(err).Str("udid", udid).Msg("failed to init XCTest") + return } - - log.Info().Str("udid", device.UDID).Msg("select device") - return device, nil } - return nil, errors.Wrap(code.IOSDeviceConnectionError, - fmt.Sprintf("device %s not found", device.UDID)) + log.Info().Str("udid", device.UDID).Msg("select ios device") + return device, nil } type IOSDevice struct { From 2ceac61c4cb167af33b50bb5e36c604c86adb421 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Fri, 14 Apr 2023 16:21:02 +0800 Subject: [PATCH 03/12] feat: add adb screencap sub command --- hrp/cmd/adb/init.go | 20 +++++++++++++++++++- hrp/cmd/adb/screencap.go | 37 +++++++++++++++++++++++++++++++++++++ hrp/cmd/ios/init.go | 2 +- hrp/pkg/uixt/ocr_vedem.go | 2 +- 4 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 hrp/cmd/adb/screencap.go diff --git a/hrp/cmd/adb/init.go b/hrp/cmd/adb/init.go index 9025ef70..e2a86bce 100644 --- a/hrp/cmd/adb/init.go +++ b/hrp/cmd/adb/init.go @@ -1,6 +1,13 @@ package adb -import "github.com/spf13/cobra" +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/httprunner/httprunner/v4/hrp/pkg/gadb" + "github.com/httprunner/httprunner/v4/hrp/pkg/uixt" +) var androidRootCmd = &cobra.Command{ Use: "adb", @@ -8,6 +15,17 @@ var androidRootCmd = &cobra.Command{ PersistentPreRun: func(cmd *cobra.Command, args []string) {}, } +func getDevice(serial string) (*gadb.Device, error) { + devices, err := uixt.GetAndroidDevices(serial) + if err != nil { + return nil, err + } + if len(devices) > 1 { + return nil, fmt.Errorf("found multiple attached devices, please specify android serial") + } + return devices[0], nil +} + func Init(rootCmd *cobra.Command) { rootCmd.AddCommand(androidRootCmd) } diff --git a/hrp/cmd/adb/screencap.go b/hrp/cmd/adb/screencap.go new file mode 100644 index 00000000..d9166191 --- /dev/null +++ b/hrp/cmd/adb/screencap.go @@ -0,0 +1,37 @@ +package adb + +import ( + "fmt" + "io/ioutil" + "time" + + "github.com/spf13/cobra" +) + +var screencapAndroidDevicesCmd = &cobra.Command{ + Use: "screencap", + Short: "Start android screen capture", + RunE: func(cmd *cobra.Command, args []string) error { + device, err := getDevice(serial) + if err != nil { + return err + } + + res, err := device.ScreenCap() + if err != nil { + return err + } + + filepath := fmt.Sprintf("screencap_%d.png", time.Now().Unix()) + if err = ioutil.WriteFile(filepath, res, 0o644); err != nil { + return err + } + fmt.Println("screencap saved to", filepath) + return nil + }, +} + +func init() { + screencapAndroidDevicesCmd.Flags().StringVarP(&serial, "serial", "s", "", "filter by device's serial") + androidRootCmd.AddCommand(screencapAndroidDevicesCmd) +} diff --git a/hrp/cmd/ios/init.go b/hrp/cmd/ios/init.go index 0a285f17..8ec31071 100644 --- a/hrp/cmd/ios/init.go +++ b/hrp/cmd/ios/init.go @@ -20,7 +20,7 @@ func getDevice(udid string) (gidevice.Device, error) { return nil, err } if len(devices) > 1 { - return nil, fmt.Errorf("multiple devices found, please specify ios udid") + return nil, fmt.Errorf("found multiple attached devices, please specify ios udid") } return devices[0], nil } diff --git a/hrp/pkg/uixt/ocr_vedem.go b/hrp/pkg/uixt/ocr_vedem.go index 03d8a271..e93fd8f2 100644 --- a/hrp/pkg/uixt/ocr_vedem.go +++ b/hrp/pkg/uixt/ocr_vedem.go @@ -102,7 +102,7 @@ func (s *veDEMOCRService) getOCRResult(imageBuf *bytes.Buffer) ([]OCRResult, err logID = getLogID(resp.Header) } log.Error().Err(err). - Str("logID", logID). + Str("X-TT-LOGID", logID). Int("imageBufSize", size). Msgf("request OCR service failed, retry %d", i) time.Sleep(1 * time.Second) From dd7724b5868d34ad75df7468aaa0033c53115f11 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Fri, 14 Apr 2023 17:42:28 +0800 Subject: [PATCH 04/12] feat: add IsAppInForeground to check if the given package is in fore ground --- hrp/pkg/uixt/android_adb_driver.go | 49 +++++++++++++++++++++++++++--- hrp/pkg/uixt/android_test.go | 28 +++++++++++++++++ hrp/pkg/uixt/client.go | 2 ++ hrp/pkg/uixt/interface.go | 10 ++++-- hrp/pkg/uixt/ios_driver.go | 14 +++++++++ 5 files changed, 95 insertions(+), 8 deletions(-) diff --git a/hrp/pkg/uixt/android_adb_driver.go b/hrp/pkg/uixt/android_adb_driver.go index fbf89eb2..4449a268 100644 --- a/hrp/pkg/uixt/android_adb_driver.go +++ b/hrp/pkg/uixt/android_adb_driver.go @@ -153,11 +153,11 @@ func (ad *adbDriver) PressKeyCode(keyCode KeyCode, metaState KeyMeta) (err error return } -func (ad *adbDriver) AppLaunch(bundleId string) (err error) { +func (ad *adbDriver) AppLaunch(packageName string) (err error) { // 不指定 Activity 名称启动(启动主 Activity) // adb shell monkey -p -c android.intent.category.LAUNCHER 1 sOutput, err := ad.adbClient.RunShellCommand( - "monkey", "-p", bundleId, "-c", "android.intent.category.LAUNCHER", "1", + "monkey", "-p", packageName, "-c", "android.intent.category.LAUNCHER", "1", ) if err != nil { return err @@ -165,14 +165,22 @@ func (ad *adbDriver) AppLaunch(bundleId string) (err error) { if strings.Contains(sOutput, "monkey aborted") { return fmt.Errorf("app launch: %s", strings.TrimSpace(sOutput)) } + ad.lastLaunchedPackageName = packageName return nil } -func (ad *adbDriver) AppTerminate(bundleId string) (successful bool, err error) { +func (ad *adbDriver) AppTerminate(packageName string) (successful bool, err error) { // 强制停止应用,停止 相关的进程 // adb shell am force-stop - _, err = ad.adbClient.RunShellCommand("am", "force-stop", bundleId) - return err == nil, err + _, err = ad.adbClient.RunShellCommand("am", "force-stop", packageName) + if err != nil { + return false, err + } + + if ad.lastLaunchedPackageName == packageName { + ad.lastLaunchedPackageName = "" // reset last launched package name + } + return true, nil } func (ad *adbDriver) Tap(x, y int, options ...DataOption) error { @@ -347,3 +355,34 @@ func (ad *adbDriver) StopCaptureLog() (result interface{}, err error) { content := ad.logcat.logBuffer.String() return ConvertPoints(content), nil } + +func (ad *adbDriver) GetLastLaunchedApp() (packageName string) { + return ad.lastLaunchedPackageName +} + +func (ad *adbDriver) IsAppInForeground(packageName string) (bool, error) { + if packageName == "" { + return false, errors.New("package name is not given") + } + + // adb shell dumpsys activity activities | grep mResumedActivity + output, err := ad.adbClient.RunShellCommand("dumpsys", "activity", "activities") + if err != nil { + return false, err + } + + lines := strings.Split(string(output), "\n") + isInForeground := false + + for _, line := range lines { + trimmedLine := strings.TrimSpace(line) + if strings.HasPrefix(trimmedLine, "mResumedActivity:") { + if strings.Contains(trimmedLine, packageName) { + isInForeground = true + } + break + } + } + + return isInForeground, nil +} diff --git a/hrp/pkg/uixt/android_test.go b/hrp/pkg/uixt/android_test.go index 17d61f97..b2a89037 100644 --- a/hrp/pkg/uixt/android_test.go +++ b/hrp/pkg/uixt/android_test.go @@ -353,6 +353,34 @@ func TestDriver_AppLaunch(t *testing.T) { t.Log(ioutil.WriteFile("s1.png", raw.Bytes(), 0o600)) } +func TestDriver_IsAppInForeground(t *testing.T) { + device, _ := NewAndroidDevice() + driver, err := device.NewDriver(nil) + if err != nil { + t.Fatal(err) + } + + err = driver.Driver.AppLaunch("com.android.settings") + if err != nil { + t.Fatal(err) + } + + yes, err := driver.Driver.IsAppInForeground(driver.Driver.GetLastLaunchedApp()) + if err != nil || !yes { + t.Fatal(err) + } + + _, err = driver.Driver.AppTerminate("com.android.settings") + if err != nil { + t.Fatal(err) + } + + yes, err = driver.Driver.IsAppInForeground("com.android.settings") + if err != nil || yes { + t.Fatal(err) + } +} + func TestDriver_KeepAlive(t *testing.T) { device, _ := NewAndroidDevice() driver, err := device.NewDriver(nil) diff --git a/hrp/pkg/uixt/client.go b/hrp/pkg/uixt/client.go index 5ebd9309..95a8b277 100644 --- a/hrp/pkg/uixt/client.go +++ b/hrp/pkg/uixt/client.go @@ -20,6 +20,8 @@ type Driver struct { urlPrefix *url.URL sessionId string client *http.Client + // cache the last launched package name + lastLaunchedPackageName string } func (wd *Driver) concatURL(u *url.URL, elem ...string) string { diff --git a/hrp/pkg/uixt/interface.go b/hrp/pkg/uixt/interface.go index d9706a17..501e6862 100644 --- a/hrp/pkg/uixt/interface.go +++ b/hrp/pkg/uixt/interface.go @@ -627,10 +627,14 @@ type WebDriver interface { // AppLaunch Launch an application with given bundle identifier in scope of current session. // !This method is only available since Xcode9 SDK - AppLaunch(bundleId string) error - // AppTerminate Terminate an application with the given bundle id. + AppLaunch(packageName string) error + // AppTerminate Terminate an application with the given pacakge name. // Either `true` if the app has been successfully terminated or `false` if it was not running - AppTerminate(bundleId string) (bool, error) + AppTerminate(packageName string) (bool, error) + // GetLastLaunchedApp returns the package name of the last launched app + GetLastLaunchedApp() string + // IsAppInForeground returns true if the given package is in foreground + IsAppInForeground(packageName string) (bool, error) // StartCamera Starts a new camera for recording StartCamera() error diff --git a/hrp/pkg/uixt/ios_driver.go b/hrp/pkg/uixt/ios_driver.go index 53339d8c..33bbe0ed 100644 --- a/hrp/pkg/uixt/ios_driver.go +++ b/hrp/pkg/uixt/ios_driver.go @@ -308,6 +308,9 @@ func (wd *wdaDriver) AppLaunch(bundleId string) (err error) { data := make(map[string]interface{}) data["bundleId"] = bundleId _, err = wd.httpPOST(data, "/session", wd.sessionId, "/wda/apps/launch") + if err == nil { + wd.lastLaunchedPackageName = bundleId + } return } @@ -328,6 +331,9 @@ func (wd *wdaDriver) AppTerminate(bundleId string) (successful bool, err error) if successful, err = rawResp.valueConvertToBool(); err != nil { return false, err } + if wd.lastLaunchedPackageName == bundleId { + wd.lastLaunchedPackageName = "" // reset last launched package name + } return } @@ -348,6 +354,14 @@ func (wd *wdaDriver) AppDeactivate(second float64) (err error) { return } +func (wd *wdaDriver) GetLastLaunchedApp() (packageName string) { + return wd.lastLaunchedPackageName +} + +func (wd *wdaDriver) IsAppInForeground(packageName string) (bool, error) { + return false, errors.New("not implemented") +} + func (wd *wdaDriver) Tap(x, y int, options ...DataOption) error { return wd.TapFloat(float64(x), float64(y), options...) } From d277a87d807541824f45cbbcb99f37be4ff5a2cc Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Fri, 14 Apr 2023 18:03:13 +0800 Subject: [PATCH 05/12] change: print ocr service logID when success --- hrp/pkg/uixt/ocr_vedem.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/hrp/pkg/uixt/ocr_vedem.go b/hrp/pkg/uixt/ocr_vedem.go index e93fd8f2..67c80b40 100644 --- a/hrp/pkg/uixt/ocr_vedem.go +++ b/hrp/pkg/uixt/ocr_vedem.go @@ -93,14 +93,17 @@ func (s *veDEMOCRService) getOCRResult(imageBuf *bytes.Buffer) ([]OCRResult, err // retry 3 times for i := 1; i <= 3; i++ { resp, err = client.Do(req) - if err == nil { - break - } - var logID string if resp != nil { logID = getLogID(resp.Header) } + if err == nil { + log.Debug(). + Str("X-TT-LOGID", logID). + Int("imageBufSize", size). + Msg("request OCR service success") + break + } log.Error().Err(err). Str("X-TT-LOGID", logID). Int("imageBufSize", size). From 069a2997e026e7ca638c96cc712133f0ca90837e Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Fri, 14 Apr 2023 18:57:36 +0800 Subject: [PATCH 06/12] feat: check if app is in foreground when step failed --- hrp/internal/code/code.go | 5 +++-- hrp/step_mobile_ui.go | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/hrp/internal/code/code.go b/hrp/internal/code/code.go index 4687bc96..354ff302 100644 --- a/hrp/internal/code/code.go +++ b/hrp/internal/code/code.go @@ -67,8 +67,9 @@ var ( // UI automation related: [70, 80) var ( - MobileUIDriverError = errors.New("mobile UI driver error") // 70 - MobileUIValidationError = errors.New("mobile UI validation error") // 75 + MobileUIDriverError = errors.New("mobile UI driver error") // 70 + MobileUIValidationError = errors.New("mobile UI validation error") // 75 + MobileUIAppNotInForegroundError = errors.New("mobile UI app not in foreground error") // 76 ) // OCR related: [80, 90) diff --git a/hrp/step_mobile_ui.go b/hrp/step_mobile_ui.go index 56ee9e4f..99014a4c 100644 --- a/hrp/step_mobile_ui.go +++ b/hrp/step_mobile_ui.go @@ -561,6 +561,14 @@ func runStepMobileUI(s *SessionRunner, step *TStep) (stepResult *StepResult, err attachments := make(map[string]interface{}) if err != nil { attachments["error"] = err.Error() + + // check if app is in foreground + packageName := uiDriver.Driver.GetLastLaunchedApp() + yes, err2 := uiDriver.Driver.IsAppInForeground(packageName) + if packageName != "" && (!yes || err2 != nil) { + log.Error().Err(err2).Str("packageName", packageName).Msg("app is not in foreground") + err = errors.Wrap(code.MobileUIAppNotInForegroundError, err.Error()) + } } // take screenshot after each step From 3364ddb25a2ca2c5d83f1d5ebb32c7868e0c1613 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Fri, 14 Apr 2023 19:02:34 +0800 Subject: [PATCH 07/12] bump version to v4.3.3.2023041419 --- docs/CHANGELOG.md | 6 +++++- hrp/internal/version/VERSION | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 5cf607b5..d7b5f38d 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,14 +1,18 @@ # Release History -## v4.3.3 (2023-04-11) +## v4.3.3 (2023-04-14) **go version** - feat: add `sleep_random` to sleep random seconds, with weight for multiple time ranges - feat: input text with adb +- feat: add adb `screencap` sub command +- feat: add `IsAppInForeground` to check if the given package is in foreground +- feat: check if app is in foreground when step failed - fix: adb driver for TapFloat - fix: stop logcat only when enabled - fix: do not fail case when kill logcat error +- fix: take screenshot after each step ## v4.3.2 (2022-12-26) diff --git a/hrp/internal/version/VERSION b/hrp/internal/version/VERSION index c294f2b5..f36ebd00 100644 --- a/hrp/internal/version/VERSION +++ b/hrp/internal/version/VERSION @@ -1 +1 @@ -v4.3.3 \ No newline at end of file +v4.3.3.2023041419 \ No newline at end of file From 2d3a4985ed7480ec8c3ce6a852dec7086bf2fec8 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Fri, 14 Apr 2023 20:28:04 +0800 Subject: [PATCH 08/12] fix: adb logcat clear argument --- hrp/pkg/uixt/android_adb_driver.go | 2 +- hrp/pkg/uixt/android_device.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hrp/pkg/uixt/android_adb_driver.go b/hrp/pkg/uixt/android_adb_driver.go index 4449a268..f373f924 100644 --- a/hrp/pkg/uixt/android_adb_driver.go +++ b/hrp/pkg/uixt/android_adb_driver.go @@ -329,7 +329,7 @@ func (ad *adbDriver) StartCaptureLog(identifier ...string) (err error) { log.Info().Msg("start adb log recording") // clear logcat - if _, err = ad.adbClient.RunShellCommand("logcat", "--clear"); err != nil { + if _, err = ad.adbClient.RunShellCommand("logcat", "-c"); err != nil { return err } diff --git a/hrp/pkg/uixt/android_device.go b/hrp/pkg/uixt/android_device.go index 52063250..b511659d 100644 --- a/hrp/pkg/uixt/android_device.go +++ b/hrp/pkg/uixt/android_device.go @@ -328,7 +328,7 @@ func (l *AdbLogcat) CatchLogcat() (err error) { } // clear logcat - if err = myexec.RunCommand("adb", "-s", l.serial, "logcat", "--clear"); err != nil { + if err = myexec.RunCommand("adb", "-s", l.serial, "shell", "logcat", "-c"); err != nil { return } From 947653014bb54ac0704b352a841a1a7b3fbf6a10 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Fri, 14 Apr 2023 21:03:47 +0800 Subject: [PATCH 09/12] fix: ScreenCap call RunShellCommandWithBytes --- hrp/internal/version/VERSION | 2 +- hrp/pkg/gadb/device.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hrp/internal/version/VERSION b/hrp/internal/version/VERSION index f36ebd00..193b8191 100644 --- a/hrp/internal/version/VERSION +++ b/hrp/internal/version/VERSION @@ -1 +1 @@ -v4.3.3.2023041419 \ No newline at end of file +v4.3.3.2023041421 \ No newline at end of file diff --git a/hrp/pkg/gadb/device.go b/hrp/pkg/gadb/device.go index 97bb08db..87a496ea 100644 --- a/hrp/pkg/gadb/device.go +++ b/hrp/pkg/gadb/device.go @@ -572,5 +572,5 @@ func (d *Device) Uninstall(packageName string, keepData ...bool) (string, error) } func (d *Device) ScreenCap() ([]byte, error) { - return d.RunShellCommandV2WithBytes("screencap", "-p") + return d.RunShellCommandWithBytes("screencap", "-p") } From 8b1aa36a8a9cb45369ad9d7447b84759277df4a4 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Fri, 14 Apr 2023 21:37:35 +0800 Subject: [PATCH 10/12] fix: support gif image --- hrp/internal/version/VERSION | 2 +- hrp/pkg/uixt/ext.go | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/hrp/internal/version/VERSION b/hrp/internal/version/VERSION index 193b8191..45631758 100644 --- a/hrp/internal/version/VERSION +++ b/hrp/internal/version/VERSION @@ -1 +1 @@ -v4.3.3.2023041421 \ No newline at end of file +v4.3.3.2304142136 \ No newline at end of file diff --git a/hrp/pkg/uixt/ext.go b/hrp/pkg/uixt/ext.go index 5bfe9e09..f110e107 100644 --- a/hrp/pkg/uixt/ext.go +++ b/hrp/pkg/uixt/ext.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "image" + "image/gif" "image/jpeg" "image/png" "math/rand" @@ -284,6 +285,8 @@ func saveScreenShot(raw *bytes.Buffer, fileName string) (string, error) { err = png.Encode(file, img) case "jpeg": err = jpeg.Encode(file, img, nil) + case "gif": + err = gif.Encode(file, img, nil) default: return "", fmt.Errorf("unsupported image format: %s", format) } From c787b73e9916b079a67cd2b704b92d42d6aa4964 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Fri, 14 Apr 2023 21:50:24 +0800 Subject: [PATCH 11/12] fix: checkout ocr response status code --- hrp/internal/version/VERSION | 2 +- hrp/pkg/uixt/ocr_vedem.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hrp/internal/version/VERSION b/hrp/internal/version/VERSION index 45631758..45af2229 100644 --- a/hrp/internal/version/VERSION +++ b/hrp/internal/version/VERSION @@ -1 +1 @@ -v4.3.3.2304142136 \ No newline at end of file +v4.3.3.2304142149 \ No newline at end of file diff --git a/hrp/pkg/uixt/ocr_vedem.go b/hrp/pkg/uixt/ocr_vedem.go index 67c80b40..f68964b6 100644 --- a/hrp/pkg/uixt/ocr_vedem.go +++ b/hrp/pkg/uixt/ocr_vedem.go @@ -97,7 +97,7 @@ func (s *veDEMOCRService) getOCRResult(imageBuf *bytes.Buffer) ([]OCRResult, err if resp != nil { logID = getLogID(resp.Header) } - if err == nil { + if err == nil && resp.StatusCode == http.StatusOK { log.Debug(). Str("X-TT-LOGID", logID). Int("imageBufSize", size). From 524b277308afe85cda6026b542d47b82319b69d0 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Fri, 14 Apr 2023 23:50:40 +0800 Subject: [PATCH 12/12] fix: screencap compatibility for shell v1 and v2 --- docs/CHANGELOG.md | 1 + hrp/internal/version/VERSION | 2 +- hrp/pkg/gadb/device.go | 17 ++++++++++++++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index d7b5f38d..35632507 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -13,6 +13,7 @@ - fix: stop logcat only when enabled - fix: do not fail case when kill logcat error - fix: take screenshot after each step +- fix: screencap compatibility for shell v1 and v2 ## v4.3.2 (2022-12-26) diff --git a/hrp/internal/version/VERSION b/hrp/internal/version/VERSION index 45af2229..f907911d 100644 --- a/hrp/internal/version/VERSION +++ b/hrp/internal/version/VERSION @@ -1 +1 @@ -v4.3.3.2304142149 \ No newline at end of file +v4.3.3.2304142356 \ No newline at end of file diff --git a/hrp/pkg/gadb/device.go b/hrp/pkg/gadb/device.go index 87a496ea..181609b0 100644 --- a/hrp/pkg/gadb/device.go +++ b/hrp/pkg/gadb/device.go @@ -572,5 +572,20 @@ func (d *Device) Uninstall(packageName string, keepData ...bool) (string, error) } func (d *Device) ScreenCap() ([]byte, error) { - return d.RunShellCommandWithBytes("screencap", "-p") + if d.HasFeature(FeatShellV2) { + return d.RunShellCommandV2WithBytes("screencap", "-p") + } + + // for shell v1, screenshot buffer maybe truncated + // thus we firstly save it to local file and then pull it + tempPath := fmt.Sprintf("/data/local/tmp/screenshot_%d.png", + time.Now().Unix()) + _, err := d.RunShellCommandWithBytes("screencap", "-p", tempPath) + if err != nil { + return nil, err + } + + buffer := bytes.NewBuffer(nil) + err = d.Pull(tempPath, buffer) + return buffer.Bytes(), err }