From be95cecdfb294070a33a8db063458608829296dd Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Tue, 11 Feb 2025 22:05:11 +0800 Subject: [PATCH] change: TapByHierarchy --- internal/version/VERSION | 2 +- pkg/uixt/android_driver_adb.go | 33 ++---- pkg/uixt/driver.go | 177 --------------------------------- pkg/uixt/driver_action.go | 5 - pkg/uixt/driver_session.go | 91 ++++++++++++++++- pkg/uixt/driver_utils.go | 89 +++++++++++++++++ 6 files changed, 188 insertions(+), 209 deletions(-) diff --git a/internal/version/VERSION b/internal/version/VERSION index 3bd8feab..08d292f3 100644 --- a/internal/version/VERSION +++ b/internal/version/VERSION @@ -1 +1 @@ -v5.0.0+2502112149 +v5.0.0+2502112205 diff --git a/pkg/uixt/android_driver_adb.go b/pkg/uixt/android_driver_adb.go index 92ffcd6c..1c10a279 100644 --- a/pkg/uixt/android_driver_adb.go +++ b/pkg/uixt/android_driver_adb.go @@ -548,6 +548,14 @@ func (ad *ADBDriver) ScreenShot(opts ...option.ActionOption) (raw *bytes.Buffer, return raw, nil } +func (ad *ADBDriver) TapByHierarchy(text string, opts ...option.ActionOption) error { + sourceTree, err := ad.sourceTree() + if err != nil { + return err + } + return ad.tapByTextUsingHierarchy(sourceTree, text, opts...) +} + func (ad *ADBDriver) Source(srcOpt ...option.SourceOption) (source string, err error) { _, err = ad.runShellCommand("rm", "-rf", "/sdcard/window_dump.xml") if err != nil { @@ -566,7 +574,7 @@ func (ad *ADBDriver) Source(srcOpt ...option.SourceOption) (source string, err e } func (ad *ADBDriver) sourceTree(srcOpt ...option.SourceOption) (sourceTree *Hierarchy, err error) { - source, err := ad.Source() + source, err := ad.Source(srcOpt...) if err != nil { return } @@ -578,14 +586,6 @@ func (ad *ADBDriver) sourceTree(srcOpt ...option.SourceOption) (sourceTree *Hier return } -func (ad *ADBDriver) TapByText(text string, opts ...option.ActionOption) error { - sourceTree, err := ad.sourceTree() - if err != nil { - return err - } - return ad.tapByTextUsingHierarchy(sourceTree, text, opts...) -} - func (ad *ADBDriver) tapByTextUsingHierarchy(hierarchy *Hierarchy, text string, opts ...option.ActionOption) error { bounds := ad.searchNodes(hierarchy.Layout, text, opts...) actionOptions := option.NewActionOptions(opts...) @@ -606,21 +606,6 @@ func (ad *ADBDriver) tapByTextUsingHierarchy(hierarchy *Hierarchy, text string, return nil } -func (ad *ADBDriver) TapByTexts(actions ...TapTextAction) error { - sourceTree, err := ad.sourceTree() - if err != nil { - return err - } - - for _, action := range actions { - err := ad.tapByTextUsingHierarchy(sourceTree, action.Text, action.Options...) - if err != nil { - return err - } - } - return nil -} - func (ad *ADBDriver) searchNodes(nodes []Layout, text string, opts ...option.ActionOption) []Bounds { actionOptions := option.NewActionOptions(opts...) var results []Bounds diff --git a/pkg/uixt/driver.go b/pkg/uixt/driver.go index 577c1bea..59887ed5 100644 --- a/pkg/uixt/driver.go +++ b/pkg/uixt/driver.go @@ -2,18 +2,10 @@ package uixt import ( "bytes" - "encoding/base64" - builtinJSON "encoding/json" - "fmt" _ "image/gif" _ "image/png" - "regexp" "time" - "github.com/pkg/errors" - "github.com/rs/zerolog/log" - - "github.com/httprunner/httprunner/v5/internal/json" "github.com/httprunner/httprunner/v5/pkg/uixt/ai" "github.com/httprunner/httprunner/v5/pkg/uixt/option" "github.com/httprunner/httprunner/v5/pkg/uixt/types" @@ -109,172 +101,3 @@ type XTDriver struct { CVService ai.ICVService // OCR/CV LLMService ai.ILLMService // LLM } - -func (dExt *XTDriver) Setup() error { - // unlock device screen - err := dExt.Unlock() - if err != nil { - log.Error().Err(err).Msg("unlock device screen failed") - return err - } - - return nil -} - -func (dExt *XTDriver) assertOCR(text, assert string) error { - var opts []option.ActionOption - opts = append(opts, option.WithScreenShotFileName(fmt.Sprintf("assert_ocr_%s", text))) - - switch assert { - case AssertionEqual: - _, err := dExt.FindScreenText(text, opts...) - if err != nil { - return errors.Wrap(err, "assert ocr equal failed") - } - case AssertionNotEqual: - _, err := dExt.FindScreenText(text, opts...) - if err == nil { - return errors.New("assert ocr not equal failed") - } - case AssertionExists: - opts = append(opts, option.WithRegex(true)) - _, err := dExt.FindScreenText(text, opts...) - if err != nil { - return errors.Wrap(err, "assert ocr exists failed") - } - case AssertionNotExists: - opts = append(opts, option.WithRegex(true)) - _, err := dExt.FindScreenText(text, opts...) - if err == nil { - return errors.New("assert ocr not exists failed") - } - default: - return fmt.Errorf("unexpected assert method %s", assert) - } - return nil -} - -func (dExt *XTDriver) assertForegroundApp(appName, assert string) error { - app, err := dExt.ForegroundInfo() - if err != nil { - log.Warn().Err(err).Msg("get foreground app failed, skip app assertion") - return nil // Notice: ignore error when get foreground app failed - } - - switch assert { - case AssertionEqual: - if app.PackageName != appName { - return errors.Wrap(err, "assert foreground app equal failed") - } - case AssertionNotEqual: - if app.PackageName == appName { - return errors.New("assert foreground app not equal failed") - } - default: - return fmt.Errorf("unexpected assert method %s", assert) - } - return nil -} - -func (dExt *XTDriver) DoValidation(check, assert, expected string, message ...string) (err error) { - switch check { - case SelectorOCR: - err = dExt.assertOCR(expected, assert) - case SelectorForegroundApp: - err = dExt.assertForegroundApp(expected, assert) - } - - if err != nil { - if message == nil { - message = []string{""} - } - log.Error().Err(err).Str("assert", assert).Str("expect", expected). - Str("msg", message[0]).Msg("validate failed") - return err - } - - log.Info().Str("assert", assert).Str("expect", expected).Msg("validate success") - return nil -} - -type DriverRawResponse []byte - -func (r DriverRawResponse) CheckErr() (err error) { - reply := new(struct { - Value struct { - Err string `json:"error"` - Message string `json:"message"` - Traceback string `json:"traceback"` // wda - Stacktrace string `json:"stacktrace"` // uia - } - }) - if err = json.Unmarshal(r, reply); err != nil { - return err - } - if reply.Value.Err != "" { - errText := reply.Value.Message - re := regexp.MustCompile(`{.+?=(.+?)}`) - if re.MatchString(reply.Value.Message) { - subMatch := re.FindStringSubmatch(reply.Value.Message) - errText = subMatch[len(subMatch)-1] - } - return fmt.Errorf("%s: %s", reply.Value.Err, errText) - } - return -} - -func (r DriverRawResponse) ValueConvertToString() (s string, err error) { - reply := new(struct{ Value string }) - if err = json.Unmarshal(r, reply); err != nil { - return "", errors.Wrapf(err, "json.Unmarshal failed, rawResponse: %s", string(r)) - } - s = reply.Value - return -} - -func (r DriverRawResponse) ValueConvertToBool() (b bool, err error) { - reply := new(struct{ Value bool }) - if err = json.Unmarshal(r, reply); err != nil { - return false, err - } - b = reply.Value - return -} - -func (r DriverRawResponse) ValueConvertToSessionInfo() (sessionInfo Session, err error) { - reply := new(struct{ Value struct{ Session } }) - if err = json.Unmarshal(r, reply); err != nil { - return Session{}, err - } - sessionInfo = reply.Value.Session - return -} - -func (r DriverRawResponse) ValueConvertToJsonRawMessage() (raw builtinJSON.RawMessage, err error) { - reply := new(struct{ Value builtinJSON.RawMessage }) - if err = json.Unmarshal(r, reply); err != nil { - return nil, err - } - raw = reply.Value - return -} - -func (r DriverRawResponse) ValueConvertToJsonObject() (obj map[string]interface{}, err error) { - if err = json.Unmarshal(r, &obj); err != nil { - return nil, err - } - return -} - -func (r DriverRawResponse) ValueDecodeAsBase64() (raw *bytes.Buffer, err error) { - str, err := r.ValueConvertToString() - if err != nil { - return nil, errors.Wrap(err, "failed to convert value to string") - } - decodeString, err := base64.StdEncoding.DecodeString(str) - if err != nil { - return nil, errors.Wrap(err, "failed to decode base64 string") - } - raw = bytes.NewBuffer(decodeString) - return -} diff --git a/pkg/uixt/driver_action.go b/pkg/uixt/driver_action.go index d4d5956e..7d8ec8ec 100644 --- a/pkg/uixt/driver_action.go +++ b/pkg/uixt/driver_action.go @@ -91,11 +91,6 @@ func (ma MobileAction) GetOptions() []option.ActionOption { return actionOptionList } -type TapTextAction struct { - Text string - Options []option.ActionOption -} - func (dExt *XTDriver) ParseActionOptions(opts ...option.ActionOption) []option.ActionOption { actionOptions := option.NewActionOptions(opts...) diff --git a/pkg/uixt/driver_session.go b/pkg/uixt/driver_session.go index 79b64c6c..fc0f908e 100644 --- a/pkg/uixt/driver_session.go +++ b/pkg/uixt/driver_session.go @@ -3,19 +3,24 @@ package uixt import ( "bytes" "context" + "encoding/base64" + builtinJSON "encoding/json" "fmt" "io" "net" "net/http" "net/url" "path" + "regexp" "strings" "time" - "github.com/httprunner/httprunner/v5/internal/json" - "github.com/httprunner/httprunner/v5/pkg/uixt/types" + "github.com/pkg/errors" "github.com/rs/zerolog" "github.com/rs/zerolog/log" + + "github.com/httprunner/httprunner/v5/internal/json" + "github.com/httprunner/httprunner/v5/pkg/uixt/types" ) type Session struct { @@ -208,3 +213,85 @@ func NewHTTPClientWithConnection(conn net.Conn, timeout time.Duration) *http.Cli Timeout: timeout, } } + +type DriverRawResponse []byte + +func (r DriverRawResponse) CheckErr() (err error) { + reply := new(struct { + Value struct { + Err string `json:"error"` + Message string `json:"message"` + Traceback string `json:"traceback"` // wda + Stacktrace string `json:"stacktrace"` // uia + } + }) + if err = json.Unmarshal(r, reply); err != nil { + return err + } + if reply.Value.Err != "" { + errText := reply.Value.Message + re := regexp.MustCompile(`{.+?=(.+?)}`) + if re.MatchString(reply.Value.Message) { + subMatch := re.FindStringSubmatch(reply.Value.Message) + errText = subMatch[len(subMatch)-1] + } + return fmt.Errorf("%s: %s", reply.Value.Err, errText) + } + return +} + +func (r DriverRawResponse) ValueConvertToString() (s string, err error) { + reply := new(struct{ Value string }) + if err = json.Unmarshal(r, reply); err != nil { + return "", errors.Wrapf(err, "json.Unmarshal failed, rawResponse: %s", string(r)) + } + s = reply.Value + return +} + +func (r DriverRawResponse) ValueConvertToBool() (b bool, err error) { + reply := new(struct{ Value bool }) + if err = json.Unmarshal(r, reply); err != nil { + return false, err + } + b = reply.Value + return +} + +func (r DriverRawResponse) ValueConvertToSessionInfo() (sessionInfo Session, err error) { + reply := new(struct{ Value struct{ Session } }) + if err = json.Unmarshal(r, reply); err != nil { + return Session{}, err + } + sessionInfo = reply.Value.Session + return +} + +func (r DriverRawResponse) ValueConvertToJsonRawMessage() (raw builtinJSON.RawMessage, err error) { + reply := new(struct{ Value builtinJSON.RawMessage }) + if err = json.Unmarshal(r, reply); err != nil { + return nil, err + } + raw = reply.Value + return +} + +func (r DriverRawResponse) ValueConvertToJsonObject() (obj map[string]interface{}, err error) { + if err = json.Unmarshal(r, &obj); err != nil { + return nil, err + } + return +} + +func (r DriverRawResponse) ValueDecodeAsBase64() (raw *bytes.Buffer, err error) { + str, err := r.ValueConvertToString() + if err != nil { + return nil, errors.Wrap(err, "failed to convert value to string") + } + decodeString, err := base64.StdEncoding.DecodeString(str) + if err != nil { + return nil, errors.Wrap(err, "failed to decode base64 string") + } + raw = bytes.NewBuffer(decodeString) + return +} diff --git a/pkg/uixt/driver_utils.go b/pkg/uixt/driver_utils.go index 612988a4..fd2f98ba 100644 --- a/pkg/uixt/driver_utils.go +++ b/pkg/uixt/driver_utils.go @@ -4,8 +4,10 @@ import ( "fmt" "github.com/pkg/errors" + "github.com/rs/zerolog/log" "github.com/httprunner/httprunner/v5/code" + "github.com/httprunner/httprunner/v5/pkg/uixt/option" ) func convertToAbsolutePoint(driver IDriver, x, y float64) (absX, absY float64, err error) { @@ -56,3 +58,90 @@ func convertToAbsoluteCoordinates(driver IDriver, fromX, fromY, toX, toY float64 func assertRelative(p float64) bool { return p >= 0 && p <= 1 } + +func (dExt *XTDriver) Setup() error { + // unlock device screen + err := dExt.Unlock() + if err != nil { + log.Error().Err(err).Msg("unlock device screen failed") + return err + } + + return nil +} + +func (dExt *XTDriver) assertOCR(text, assert string) error { + var opts []option.ActionOption + opts = append(opts, option.WithScreenShotFileName(fmt.Sprintf("assert_ocr_%s", text))) + + switch assert { + case AssertionEqual: + _, err := dExt.FindScreenText(text, opts...) + if err != nil { + return errors.Wrap(err, "assert ocr equal failed") + } + case AssertionNotEqual: + _, err := dExt.FindScreenText(text, opts...) + if err == nil { + return errors.New("assert ocr not equal failed") + } + case AssertionExists: + opts = append(opts, option.WithRegex(true)) + _, err := dExt.FindScreenText(text, opts...) + if err != nil { + return errors.Wrap(err, "assert ocr exists failed") + } + case AssertionNotExists: + opts = append(opts, option.WithRegex(true)) + _, err := dExt.FindScreenText(text, opts...) + if err == nil { + return errors.New("assert ocr not exists failed") + } + default: + return fmt.Errorf("unexpected assert method %s", assert) + } + return nil +} + +func (dExt *XTDriver) assertForegroundApp(appName, assert string) error { + app, err := dExt.ForegroundInfo() + if err != nil { + log.Warn().Err(err).Msg("get foreground app failed, skip app assertion") + return nil // Notice: ignore error when get foreground app failed + } + + switch assert { + case AssertionEqual: + if app.PackageName != appName { + return errors.Wrap(err, "assert foreground app equal failed") + } + case AssertionNotEqual: + if app.PackageName == appName { + return errors.New("assert foreground app not equal failed") + } + default: + return fmt.Errorf("unexpected assert method %s", assert) + } + return nil +} + +func (dExt *XTDriver) DoValidation(check, assert, expected string, message ...string) (err error) { + switch check { + case SelectorOCR: + err = dExt.assertOCR(expected, assert) + case SelectorForegroundApp: + err = dExt.assertForegroundApp(expected, assert) + } + + if err != nil { + if message == nil { + message = []string{""} + } + log.Error().Err(err).Str("assert", assert).Str("expect", expected). + Str("msg", message[0]).Msg("validate failed") + return err + } + + log.Info().Str("assert", assert).Str("expect", expected).Msg("validate success") + return nil +}