mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-12 02:21:29 +08:00
refactor: relocate code
This commit is contained in:
@@ -4,20 +4,13 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"io"
|
||||
"math"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/code"
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/builtin"
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/env"
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/json"
|
||||
)
|
||||
|
||||
type IImageService interface {
|
||||
@@ -25,8 +18,23 @@ type IImageService interface {
|
||||
GetImage(imageBuf *bytes.Buffer, options ...ActionOption) (imageResult *ImageResult, err error)
|
||||
}
|
||||
|
||||
var client = &http.Client{
|
||||
Timeout: time.Second * 10,
|
||||
type ImageResult struct {
|
||||
URL string `json:"url,omitempty"` // image uploaded url
|
||||
OCRResult OCRResults `json:"ocrResult,omitempty"` // OCR texts
|
||||
// NoLive(非直播间)
|
||||
// Shop(电商)
|
||||
// LifeService(生活服务)
|
||||
// Show(秀场)
|
||||
// Game(游戏)
|
||||
// People(多人)
|
||||
// PK(PK)
|
||||
// Media(媒体)
|
||||
// Chat(语音)
|
||||
// Event(赛事)
|
||||
LiveType string `json:"liveType,omitempty"` // 直播间类型
|
||||
LivePopularity int64 `json:"livePopularity,omitempty"` // 直播间热度
|
||||
UIResult UIResultMap `json:"uiResult,omitempty"` // 图标检测
|
||||
ClosePopupsResult *ClosePopupsResult `json:"closeResult,omitempty"` // 弹窗按钮检测
|
||||
}
|
||||
|
||||
type OCRResult struct {
|
||||
@@ -58,31 +66,6 @@ func (o OCRResults) ToOCRTexts() (ocrTexts OCRTexts) {
|
||||
return
|
||||
}
|
||||
|
||||
type ImageResult struct {
|
||||
URL string `json:"url,omitempty"` // image uploaded url
|
||||
OCRResult OCRResults `json:"ocrResult,omitempty"` // OCR texts
|
||||
// NoLive(非直播间)
|
||||
// Shop(电商)
|
||||
// LifeService(生活服务)
|
||||
// Show(秀场)
|
||||
// Game(游戏)
|
||||
// People(多人)
|
||||
// PK(PK)
|
||||
// Media(媒体)
|
||||
// Chat(语音)
|
||||
// Event(赛事)
|
||||
LiveType string `json:"liveType,omitempty"` // 直播间类型
|
||||
LivePopularity int64 `json:"livePopularity,omitempty"` // 直播间热度
|
||||
UIResult UIResultMap `json:"uiResult,omitempty"` // 图标检测
|
||||
ClosePopupsResult *ClosePopupsResult `json:"closeResult,omitempty"` // 弹窗按钮检测
|
||||
}
|
||||
|
||||
type APIResponseImage struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Result ImageResult `json:"result"`
|
||||
}
|
||||
|
||||
type OCRText struct {
|
||||
Text string
|
||||
Rect image.Rectangle
|
||||
@@ -198,199 +181,23 @@ func (t OCRTexts) FindTexts(texts []string, options ...ActionOption) (results OC
|
||||
fmt.Sprintf("texts %s not found in %v", texts, t.texts()))
|
||||
}
|
||||
|
||||
func newVEDEMImageService() (*veDEMImageService, error) {
|
||||
if err := checkEnv(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &veDEMImageService{}, nil
|
||||
}
|
||||
type UIResultMap map[string]UIResults
|
||||
|
||||
// veDEMImageService implements IImageService interface
|
||||
// actions:
|
||||
//
|
||||
// ocr - get ocr texts
|
||||
// upload - get image uploaded url
|
||||
// liveType - get live type
|
||||
// popup - get popup windows
|
||||
// close - get close popup
|
||||
// ui - get ui position by type(s)
|
||||
type veDEMImageService struct{}
|
||||
|
||||
func (s *veDEMImageService) GetImage(imageBuf *bytes.Buffer, options ...ActionOption) (imageResult *ImageResult, err error) {
|
||||
actionOptions := NewActionOptions(options...)
|
||||
screenshotActions := actionOptions.screenshotActions()
|
||||
if len(screenshotActions) == 0 {
|
||||
// skip
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
bodyBuf := &bytes.Buffer{}
|
||||
bodyWriter := multipart.NewWriter(bodyBuf)
|
||||
for _, action := range screenshotActions {
|
||||
bodyWriter.WriteField("actions", action)
|
||||
}
|
||||
for _, uiType := range actionOptions.ScreenShotWithUITypes {
|
||||
bodyWriter.WriteField("uiTypes", uiType)
|
||||
}
|
||||
|
||||
// 使用高精度集群
|
||||
bodyWriter.WriteField("ocrCluster", "highPrecision")
|
||||
|
||||
if actionOptions.ScreenShotWithOCRCluster != "" {
|
||||
bodyWriter.WriteField("ocrCluster", actionOptions.ScreenShotWithOCRCluster)
|
||||
}
|
||||
|
||||
if actionOptions.Timeout > 0 {
|
||||
bodyWriter.WriteField("timeout", fmt.Sprintf("%v", actionOptions.Timeout))
|
||||
} else {
|
||||
bodyWriter.WriteField("timeout", fmt.Sprintf("%v", 10))
|
||||
}
|
||||
|
||||
formWriter, err := bodyWriter.CreateFormFile("image", "screenshot.png")
|
||||
if err != nil {
|
||||
err = errors.Wrap(code.CVRequestError,
|
||||
fmt.Sprintf("create form file error: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
size, err := formWriter.Write(imageBuf.Bytes())
|
||||
if err != nil {
|
||||
err = errors.Wrap(code.CVRequestError,
|
||||
fmt.Sprintf("write form error: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
err = bodyWriter.Close()
|
||||
if err != nil {
|
||||
err = errors.Wrap(code.CVRequestError,
|
||||
fmt.Sprintf("close body writer error: %v", err))
|
||||
return
|
||||
}
|
||||
var req *http.Request
|
||||
var resp *http.Response
|
||||
// retry 3 times
|
||||
for i := 1; i <= 3; i++ {
|
||||
copiedBodyBuf := &bytes.Buffer{}
|
||||
if _, err := copiedBodyBuf.Write(bodyBuf.Bytes()); err != nil {
|
||||
log.Error().Err(err).Msg("copy screenshot buffer failed")
|
||||
continue
|
||||
}
|
||||
|
||||
req, err = http.NewRequest("POST", env.VEDEM_IMAGE_URL, copiedBodyBuf)
|
||||
if err != nil {
|
||||
err = errors.Wrap(code.CVRequestError,
|
||||
fmt.Sprintf("construct request error: %v", err))
|
||||
// FilterUIResults filters ui icons, the former the uiTypes, the higher the priority
|
||||
func (u UIResultMap) FilterUIResults(uiTypes []string) (uiResults UIResults, err error) {
|
||||
var ok bool
|
||||
for _, uiType := range uiTypes {
|
||||
uiResults, ok = u[uiType]
|
||||
if ok && len(uiResults) != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// ppe env
|
||||
// req.Header.Add("x-tt-env", "ppe_vedem_algorithm")
|
||||
// req.Header.Add("x-use-ppe", "1")
|
||||
|
||||
signToken := "UNSIGNED-PAYLOAD"
|
||||
token := builtin.Sign("auth-v2", env.VEDEM_IMAGE_AK, env.VEDEM_IMAGE_SK, []byte(signToken))
|
||||
|
||||
req.Header.Add("Agw-Auth", token)
|
||||
req.Header.Add("Agw-Auth-Content", signToken)
|
||||
req.Header.Add("Content-Type", bodyWriter.FormDataContentType())
|
||||
|
||||
start := time.Now()
|
||||
resp, err = client.Do(req)
|
||||
elapsed := time.Since(start)
|
||||
if err != nil {
|
||||
log.Error().Err(err).
|
||||
Int("imageBufSize", size).
|
||||
Msgf("request veDEM OCR service error, retry %d", i)
|
||||
continue
|
||||
}
|
||||
|
||||
logID := getLogID(resp.Header)
|
||||
statusCode := resp.StatusCode
|
||||
if statusCode != http.StatusOK {
|
||||
log.Error().
|
||||
Str("X-TT-LOGID", logID).
|
||||
Int("imageBufSize", size).
|
||||
Int("statusCode", statusCode).
|
||||
Msgf("request veDEM OCR service failed, retry %d", i)
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debug().
|
||||
Str("X-TT-LOGID", logID).
|
||||
Int("image_bytes", size).
|
||||
Int64("elapsed(ms)", elapsed.Milliseconds()).
|
||||
Msg("request OCR service success")
|
||||
break
|
||||
}
|
||||
if resp == nil {
|
||||
err = code.CVServiceConnectionError
|
||||
return
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
results, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
err = errors.Wrap(code.CVResponseError,
|
||||
fmt.Sprintf("read response body error: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
err = errors.Wrap(code.CVResponseError,
|
||||
fmt.Sprintf("unexpected response status code: %d, results: %v",
|
||||
resp.StatusCode, string(results)))
|
||||
return
|
||||
}
|
||||
|
||||
var imageResponse APIResponseImage
|
||||
err = json.Unmarshal(results, &imageResponse)
|
||||
if err != nil {
|
||||
log.Error().Err(err).
|
||||
Str("response", string(results)).
|
||||
Msg("json unmarshal veDEM image response body failed")
|
||||
err = errors.Wrap(code.CVResponseError,
|
||||
"json unmarshal veDEM image response body error")
|
||||
return
|
||||
}
|
||||
|
||||
if imageResponse.Code != 0 {
|
||||
log.Error().
|
||||
Int("code", imageResponse.Code).
|
||||
Str("message", imageResponse.Message).
|
||||
Msg("request veDEM OCR service failed")
|
||||
}
|
||||
|
||||
imageResult = &imageResponse.Result
|
||||
log.Debug().Interface("imageResult", imageResult).Msg("get image data by veDEM")
|
||||
return imageResult, nil
|
||||
err = errors.Wrap(code.CVResultNotFoundError, fmt.Sprintf("UI types %v not detected", uiTypes))
|
||||
return
|
||||
}
|
||||
|
||||
func checkEnv() error {
|
||||
if env.VEDEM_IMAGE_URL == "" {
|
||||
return errors.Wrap(code.CVEnvMissedError, "VEDEM_IMAGE_URL missed")
|
||||
}
|
||||
log.Info().Str("VEDEM_IMAGE_URL", env.VEDEM_IMAGE_URL).Msg("get env")
|
||||
if env.VEDEM_IMAGE_AK == "" {
|
||||
return errors.Wrap(code.CVEnvMissedError, "VEDEM_IMAGE_AK missed")
|
||||
}
|
||||
if env.VEDEM_IMAGE_SK == "" {
|
||||
return errors.Wrap(code.CVEnvMissedError, "VEDEM_IMAGE_SK missed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLogID(header http.Header) string {
|
||||
if len(header) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
logID, ok := header["X-Tt-Logid"]
|
||||
if !ok || len(logID) == 0 {
|
||||
return ""
|
||||
}
|
||||
return logID[0]
|
||||
type UIResult struct {
|
||||
Box
|
||||
}
|
||||
|
||||
type Box struct {
|
||||
@@ -417,10 +224,6 @@ func (box Box) Center() PointF {
|
||||
}
|
||||
}
|
||||
|
||||
type UIResult struct {
|
||||
Box
|
||||
}
|
||||
|
||||
type UIResults []UIResult
|
||||
|
||||
func (u UIResults) FilterScope(scope AbsScope) (results UIResults) {
|
||||
@@ -473,17 +276,10 @@ func (u UIResults) GetUIResult(options ...ActionOption) (UIResult, error) {
|
||||
return uiResults[idx], nil
|
||||
}
|
||||
|
||||
type UIResultMap map[string]UIResults
|
||||
|
||||
// FilterUIResults filters ui icons, the former the uiTypes, the higher the priority
|
||||
func (u UIResultMap) FilterUIResults(uiTypes []string) (uiResults UIResults, err error) {
|
||||
var ok bool
|
||||
for _, uiType := range uiTypes {
|
||||
uiResults, ok = u[uiType]
|
||||
if ok && len(uiResults) != 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
err = errors.Wrap(code.CVResultNotFoundError, fmt.Sprintf("UI types %v not detected", uiTypes))
|
||||
return
|
||||
// ClosePopupsResult represents the result of recognized popup to close
|
||||
type ClosePopupsResult struct {
|
||||
Type string `json:"type"`
|
||||
PopupArea Box `json:"popupArea"`
|
||||
CloseArea Box `json:"closeArea"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
223
hrp/pkg/uixt/ai_vedem.go
Normal file
223
hrp/pkg/uixt/ai_vedem.go
Normal file
@@ -0,0 +1,223 @@
|
||||
package uixt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/code"
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/builtin"
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/env"
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/json"
|
||||
)
|
||||
|
||||
var client = &http.Client{
|
||||
Timeout: time.Second * 10,
|
||||
}
|
||||
|
||||
type APIResponseImage struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Result ImageResult `json:"result"`
|
||||
}
|
||||
|
||||
func newVEDEMImageService() (*veDEMImageService, error) {
|
||||
if err := checkEnv(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &veDEMImageService{}, nil
|
||||
}
|
||||
|
||||
// veDEMImageService implements IImageService interface
|
||||
// actions:
|
||||
//
|
||||
// ocr - get ocr texts
|
||||
// upload - get image uploaded url
|
||||
// liveType - get live type
|
||||
// popup - get popup windows
|
||||
// close - get close popup
|
||||
// ui - get ui position by type(s)
|
||||
type veDEMImageService struct{}
|
||||
|
||||
func (s *veDEMImageService) GetImage(imageBuf *bytes.Buffer, options ...ActionOption) (imageResult *ImageResult, err error) {
|
||||
actionOptions := NewActionOptions(options...)
|
||||
screenshotActions := actionOptions.screenshotActions()
|
||||
if len(screenshotActions) == 0 {
|
||||
// skip
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
bodyBuf := &bytes.Buffer{}
|
||||
bodyWriter := multipart.NewWriter(bodyBuf)
|
||||
for _, action := range screenshotActions {
|
||||
bodyWriter.WriteField("actions", action)
|
||||
}
|
||||
for _, uiType := range actionOptions.ScreenShotWithUITypes {
|
||||
bodyWriter.WriteField("uiTypes", uiType)
|
||||
}
|
||||
|
||||
// 使用高精度集群
|
||||
bodyWriter.WriteField("ocrCluster", "highPrecision")
|
||||
|
||||
if actionOptions.ScreenShotWithOCRCluster != "" {
|
||||
bodyWriter.WriteField("ocrCluster", actionOptions.ScreenShotWithOCRCluster)
|
||||
}
|
||||
|
||||
if actionOptions.Timeout > 0 {
|
||||
bodyWriter.WriteField("timeout", fmt.Sprintf("%v", actionOptions.Timeout))
|
||||
} else {
|
||||
bodyWriter.WriteField("timeout", fmt.Sprintf("%v", 10))
|
||||
}
|
||||
|
||||
formWriter, err := bodyWriter.CreateFormFile("image", "screenshot.png")
|
||||
if err != nil {
|
||||
err = errors.Wrap(code.CVRequestError,
|
||||
fmt.Sprintf("create form file error: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
size, err := formWriter.Write(imageBuf.Bytes())
|
||||
if err != nil {
|
||||
err = errors.Wrap(code.CVRequestError,
|
||||
fmt.Sprintf("write form error: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
err = bodyWriter.Close()
|
||||
if err != nil {
|
||||
err = errors.Wrap(code.CVRequestError,
|
||||
fmt.Sprintf("close body writer error: %v", err))
|
||||
return
|
||||
}
|
||||
var req *http.Request
|
||||
var resp *http.Response
|
||||
// retry 3 times
|
||||
for i := 1; i <= 3; i++ {
|
||||
copiedBodyBuf := &bytes.Buffer{}
|
||||
if _, err := copiedBodyBuf.Write(bodyBuf.Bytes()); err != nil {
|
||||
log.Error().Err(err).Msg("copy screenshot buffer failed")
|
||||
continue
|
||||
}
|
||||
|
||||
req, err = http.NewRequest("POST", env.VEDEM_IMAGE_URL, copiedBodyBuf)
|
||||
if err != nil {
|
||||
err = errors.Wrap(code.CVRequestError,
|
||||
fmt.Sprintf("construct request error: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// ppe env
|
||||
// req.Header.Add("x-tt-env", "ppe_vedem_algorithm")
|
||||
// req.Header.Add("x-use-ppe", "1")
|
||||
|
||||
signToken := "UNSIGNED-PAYLOAD"
|
||||
token := builtin.Sign("auth-v2", env.VEDEM_IMAGE_AK, env.VEDEM_IMAGE_SK, []byte(signToken))
|
||||
|
||||
req.Header.Add("Agw-Auth", token)
|
||||
req.Header.Add("Agw-Auth-Content", signToken)
|
||||
req.Header.Add("Content-Type", bodyWriter.FormDataContentType())
|
||||
|
||||
start := time.Now()
|
||||
resp, err = client.Do(req)
|
||||
elapsed := time.Since(start)
|
||||
if err != nil {
|
||||
log.Error().Err(err).
|
||||
Int("imageBufSize", size).
|
||||
Msgf("request veDEM OCR service error, retry %d", i)
|
||||
continue
|
||||
}
|
||||
|
||||
logID := getLogID(resp.Header)
|
||||
statusCode := resp.StatusCode
|
||||
if statusCode != http.StatusOK {
|
||||
log.Error().
|
||||
Str("X-TT-LOGID", logID).
|
||||
Int("imageBufSize", size).
|
||||
Int("statusCode", statusCode).
|
||||
Msgf("request veDEM OCR service failed, retry %d", i)
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debug().
|
||||
Str("X-TT-LOGID", logID).
|
||||
Int("image_bytes", size).
|
||||
Int64("elapsed(ms)", elapsed.Milliseconds()).
|
||||
Msg("request OCR service success")
|
||||
break
|
||||
}
|
||||
if resp == nil {
|
||||
err = code.CVServiceConnectionError
|
||||
return
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
results, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
err = errors.Wrap(code.CVResponseError,
|
||||
fmt.Sprintf("read response body error: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
err = errors.Wrap(code.CVResponseError,
|
||||
fmt.Sprintf("unexpected response status code: %d, results: %v",
|
||||
resp.StatusCode, string(results)))
|
||||
return
|
||||
}
|
||||
|
||||
var imageResponse APIResponseImage
|
||||
err = json.Unmarshal(results, &imageResponse)
|
||||
if err != nil {
|
||||
log.Error().Err(err).
|
||||
Str("response", string(results)).
|
||||
Msg("json unmarshal veDEM image response body failed")
|
||||
err = errors.Wrap(code.CVResponseError,
|
||||
"json unmarshal veDEM image response body error")
|
||||
return
|
||||
}
|
||||
|
||||
if imageResponse.Code != 0 {
|
||||
log.Error().
|
||||
Int("code", imageResponse.Code).
|
||||
Str("message", imageResponse.Message).
|
||||
Msg("request veDEM OCR service failed")
|
||||
}
|
||||
|
||||
imageResult = &imageResponse.Result
|
||||
log.Debug().Interface("imageResult", imageResult).Msg("get image data by veDEM")
|
||||
return imageResult, nil
|
||||
}
|
||||
|
||||
func checkEnv() error {
|
||||
if env.VEDEM_IMAGE_URL == "" {
|
||||
return errors.Wrap(code.CVEnvMissedError, "VEDEM_IMAGE_URL missed")
|
||||
}
|
||||
log.Info().Str("VEDEM_IMAGE_URL", env.VEDEM_IMAGE_URL).Msg("get env")
|
||||
if env.VEDEM_IMAGE_AK == "" {
|
||||
return errors.Wrap(code.CVEnvMissedError, "VEDEM_IMAGE_AK missed")
|
||||
}
|
||||
if env.VEDEM_IMAGE_SK == "" {
|
||||
return errors.Wrap(code.CVEnvMissedError, "VEDEM_IMAGE_SK missed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLogID(header http.Header) string {
|
||||
if len(header) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
logID, ok := header["X-Tt-Logid"]
|
||||
if !ok || len(logID) == 0 {
|
||||
return ""
|
||||
}
|
||||
return logID[0]
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -24,7 +23,7 @@ func checkOCR(buff *bytes.Buffer) error {
|
||||
}
|
||||
|
||||
func TestOCRWithScreenshot(t *testing.T) {
|
||||
setupAndroid(t)
|
||||
setupAndroidAdbDriver(t)
|
||||
|
||||
raw, err := driverExt.Driver.Screenshot()
|
||||
if err != nil {
|
||||
@@ -52,27 +51,6 @@ func TestOCRWithLocalFile(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func matchPopup(text string) bool {
|
||||
for _, popup := range popups {
|
||||
if regexp.MustCompile(popup[1]).MatchString(text) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func TestMatchRegex(t *testing.T) {
|
||||
testData := []string{
|
||||
"以后再说", "我知道了", "同意", "拒绝", "稍后",
|
||||
"始终允许", "继续使用", "仅在使用中允许",
|
||||
}
|
||||
for _, text := range testData {
|
||||
if !matchPopup(text) {
|
||||
t.Fatal(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTapUIWithScreenshot(t *testing.T) {
|
||||
serialNumber := os.Getenv("SERIAL_NUMBER")
|
||||
device, _ := NewAndroidDevice(WithSerialNumber(serialNumber))
|
||||
@@ -98,11 +76,3 @@ func TestDriverExtOCR(t *testing.T) {
|
||||
t.Logf("point.X: %v, point.Y: %v", point.X, point.Y)
|
||||
driverExt.Driver.TapFloat(point.X, point.Y-20)
|
||||
}
|
||||
|
||||
func TestClosePopup(t *testing.T) {
|
||||
setupAndroid(t)
|
||||
|
||||
if err := driverExt.ClosePopupsHandler(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,16 @@ var (
|
||||
driverExt *DriverExt
|
||||
)
|
||||
|
||||
func setupAndroid(t *testing.T) {
|
||||
func setupAndroidAdbDriver(t *testing.T) {
|
||||
device, err := NewAndroidDevice()
|
||||
checkErr(t, err)
|
||||
device.UIA2 = false
|
||||
device.LogOn = false
|
||||
driverExt, err = device.NewDriver()
|
||||
checkErr(t, err)
|
||||
}
|
||||
|
||||
func setupAndroidUIA2Driver(t *testing.T) {
|
||||
device, err := NewAndroidDevice()
|
||||
checkErr(t, err)
|
||||
device.UIA2 = true
|
||||
@@ -122,7 +131,7 @@ func TestDriver_DeviceSize(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDriver_Source(t *testing.T) {
|
||||
setupAndroid(t)
|
||||
setupAndroidUIA2Driver(t)
|
||||
|
||||
source, err := driverExt.Driver.Source()
|
||||
if err != nil {
|
||||
@@ -192,7 +201,7 @@ func TestDriver_DeviceInfo(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDriver_Tap(t *testing.T) {
|
||||
setupAndroid(t)
|
||||
setupAndroidUIA2Driver(t)
|
||||
driverExt.Driver.StartCaptureLog("")
|
||||
err := driverExt.TapXY(0.5, 0.5, WithIdentifier("test"), WithPressDuration(4))
|
||||
if err != nil {
|
||||
@@ -210,7 +219,7 @@ func TestDriver_Tap(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDriver_Swipe(t *testing.T) {
|
||||
setupAndroid(t)
|
||||
setupAndroidUIA2Driver(t)
|
||||
err := driverExt.Driver.Swipe(400, 1000, 400, 500, WithPressDuration(0.5))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -218,7 +227,7 @@ func TestDriver_Swipe(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDriver_Swipe_Relative(t *testing.T) {
|
||||
setupAndroid(t)
|
||||
setupAndroidUIA2Driver(t)
|
||||
err := driverExt.SwipeRelative(0.5, 0.7, 0.5, 0.5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -245,7 +254,7 @@ func TestDriver_Drag(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDriver_SendKeys(t *testing.T) {
|
||||
setupAndroid(t)
|
||||
setupAndroidUIA2Driver(t)
|
||||
|
||||
err := driverExt.Driver.SendKeys("辽宁省沈阳市新民市民族街36-4", WithIdentifier("test"))
|
||||
if err != nil {
|
||||
@@ -293,7 +302,7 @@ func TestDriver_SetRotation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDriver_GetOrientation(t *testing.T) {
|
||||
setupAndroid(t)
|
||||
setupAndroidUIA2Driver(t)
|
||||
_, _ = driverExt.Driver.AppTerminate("com.quark.browser")
|
||||
_ = driverExt.Driver.AppLaunch("com.quark.browser")
|
||||
time.Sleep(2 * time.Second)
|
||||
@@ -366,7 +375,7 @@ func TestDriver_AppLaunch(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDriver_IsAppInForeground(t *testing.T) {
|
||||
setupAndroid(t)
|
||||
setupAndroidUIA2Driver(t)
|
||||
|
||||
err := driverExt.Driver.AppLaunch("com.android.settings")
|
||||
checkErr(t, err)
|
||||
@@ -465,7 +474,7 @@ func TestDriver_ShellInputUnicode(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTapTexts(t *testing.T) {
|
||||
setupAndroid(t)
|
||||
setupAndroidUIA2Driver(t)
|
||||
actions := []TapTextAction{
|
||||
{Text: "^.*无视风险安装$", Options: []ActionOption{WithTapOffset(100, 0), WithRegex(true), WithIgnoreNotFoundError(true)}},
|
||||
{Text: "已了解此应用未经检测.*", Options: []ActionOption{WithTapOffset(-450, 0), WithRegex(true), WithIgnoreNotFoundError(true)}},
|
||||
|
||||
@@ -323,22 +323,6 @@ const (
|
||||
NotificationTypeDarwin NotificationType = "darwin"
|
||||
)
|
||||
|
||||
// EventPageID The event page identifier
|
||||
type EventPageID int
|
||||
|
||||
const EventPageIDConsumer EventPageID = 0x0C
|
||||
|
||||
// EventUsageID The event usage identifier (usages are defined per-page)
|
||||
type EventUsageID int
|
||||
|
||||
const (
|
||||
EventUsageIDCsmrVolumeUp EventUsageID = 0xE9
|
||||
EventUsageIDCsmrVolumeDown EventUsageID = 0xEA
|
||||
EventUsageIDCsmrHome EventUsageID = 0x40
|
||||
EventUsageIDCsmrPower EventUsageID = 0x30
|
||||
EventUsageIDCsmrSnapshot EventUsageID = 0x65 // Power + Home
|
||||
)
|
||||
|
||||
type Orientation string
|
||||
|
||||
const (
|
||||
|
||||
@@ -71,14 +71,6 @@ func (dExt *DriverExt) AutoPopupHandler() error {
|
||||
return dExt.handleTextPopup(screenResult.Texts)
|
||||
}
|
||||
|
||||
// ClosePopupsResult represents the result of recognized popup to close
|
||||
type ClosePopupsResult struct {
|
||||
Type string `json:"type"`
|
||||
PopupArea Box `json:"popupArea"`
|
||||
CloseArea Box `json:"closeArea"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
type PopupInfo struct {
|
||||
*ClosePopupsResult
|
||||
ClosePoints []PointF `json:"close_points,omitempty"` // CV 识别的所有关闭按钮(仅关闭按钮,可能存在多个)
|
||||
@@ -101,8 +93,14 @@ func (p *PopupInfo) ClosePoint() *PointF {
|
||||
return &closePoint
|
||||
}
|
||||
|
||||
func (dExt *DriverExt) CheckPopup() (*PopupInfo, error) {
|
||||
log.Info().Msg("check if popup exist")
|
||||
func (dExt *DriverExt) CheckPopup() (popup *PopupInfo, err error) {
|
||||
defer func() {
|
||||
if popup == nil {
|
||||
log.Info().Msg("check popup, no found")
|
||||
} else {
|
||||
log.Info().Interface("popup", popup).Msg("found popup")
|
||||
}
|
||||
}()
|
||||
screenResult, err := dExt.GetScreenResult(
|
||||
WithScreenShotUpload(true),
|
||||
WithScreenShotClosePopups(true), // get popup area and close area
|
||||
@@ -110,7 +108,7 @@ func (dExt *DriverExt) CheckPopup() (*PopupInfo, error) {
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get screen result failed for popup handler")
|
||||
}
|
||||
popup := screenResult.Popup
|
||||
popup = screenResult.Popup
|
||||
if popup == nil {
|
||||
return nil, errors.New("popup not found")
|
||||
}
|
||||
@@ -119,7 +117,6 @@ func (dExt *DriverExt) CheckPopup() (*PopupInfo, error) {
|
||||
// close point not found
|
||||
return nil, errors.New("popup close point not found")
|
||||
}
|
||||
log.Info().Interface("popup", popup).Msg("found popup")
|
||||
return popup, nil
|
||||
}
|
||||
|
||||
|
||||
47
hrp/pkg/uixt/popups_test.go
Normal file
47
hrp/pkg/uixt/popups_test.go
Normal file
@@ -0,0 +1,47 @@
|
||||
//go:build localtest
|
||||
|
||||
package uixt
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCheckPopup(t *testing.T) {
|
||||
setupAndroidAdbDriver(t)
|
||||
popup, err := driverExt.CheckPopup()
|
||||
if err != nil {
|
||||
t.Logf("error: %v", err)
|
||||
} else {
|
||||
t.Logf("popup: %+v", popup)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClosePopup(t *testing.T) {
|
||||
setupAndroidAdbDriver(t)
|
||||
|
||||
if err := driverExt.ClosePopupsHandler(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func matchPopup(text string) bool {
|
||||
for _, popup := range popups {
|
||||
if regexp.MustCompile(popup[1]).MatchString(text) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func TestMatchRegex(t *testing.T) {
|
||||
testData := []string{
|
||||
"以后再说", "我知道了", "同意", "拒绝", "稍后",
|
||||
"始终允许", "继续使用", "仅在使用中允许",
|
||||
}
|
||||
for _, text := range testData {
|
||||
if !matchPopup(text) {
|
||||
t.Fatal(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,6 +56,7 @@ func (dExt *DriverExt) GetScreenResult(options ...ActionOption) (screenResult *S
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
dExt.cacheStepData.screenShots = append(dExt.cacheStepData.screenShots, imagePath)
|
||||
|
||||
screenResult = &ScreenResult{
|
||||
bufSource: bufSource,
|
||||
@@ -171,7 +172,6 @@ func (dExt *DriverExt) GetScreenShot(fileName string) (raw *bytes.Buffer, path s
|
||||
log.Error().Err(err).Msg("save screenshot file failed")
|
||||
return nil, "", err
|
||||
}
|
||||
dExt.cacheStepData.screenShots = append(dExt.cacheStepData.screenShots, path)
|
||||
return compressed, path, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func TestAndroidSwipeAction(t *testing.T) {
|
||||
setupAndroid(t)
|
||||
setupAndroidAdbDriver(t)
|
||||
|
||||
swipeAction := driverExt.prepareSwipeAction(WithDirection("up"))
|
||||
err := swipeAction(driverExt)
|
||||
@@ -19,14 +19,14 @@ func TestAndroidSwipeAction(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAndroidSwipeToTapApp(t *testing.T) {
|
||||
setupAndroid(t)
|
||||
setupAndroidAdbDriver(t)
|
||||
|
||||
err := driverExt.swipeToTapApp("抖音")
|
||||
checkErr(t, err)
|
||||
}
|
||||
|
||||
func TestAndroidSwipeToTapTexts(t *testing.T) {
|
||||
setupAndroid(t)
|
||||
setupAndroidAdbDriver(t)
|
||||
|
||||
err := driverExt.Driver.AppLaunch("com.ss.android.ugc.aweme")
|
||||
checkErr(t, err)
|
||||
|
||||
Reference in New Issue
Block a user