diff --git a/examples/uitest/demo_feed_random_slide.json b/examples/uitest/demo_feed_random_slide.json new file mode 100644 index 00000000..86c62947 --- /dev/null +++ b/examples/uitest/demo_feed_random_slide.json @@ -0,0 +1,84 @@ +{ + "config": { + "name": "点播_抖音_滑动场景_随机间隔_android", + "variables": { + "device": "${ENV(SerialNumber)}" + }, + "android": [ + { + "serial": "$device" + } + ] + }, + "teststeps": [ + { + "name": "启动抖音", + "android": { + "actions": [ + { + "method": "app_terminate", + "params": "com.ss.android.ugc.aweme" + }, + { + "method": "app_launch", + "params": "com.ss.android.ugc.aweme" + }, + { + "method": "sleep", + "params": 10 + } + ] + } + }, + { + "name": "处理青少年弹窗", + "android": { + "actions": [ + { + "method": "tap_ocr", + "params": "我知道了", + "ignore_NotFoundError": true + } + ] + } + }, + { + "name": "滑动 Feed 35 次,随机间隔 0-20s", + "android": { + "actions": [ + { + "method": "swipe", + "params": "up" + }, + { + "method": "sleep_random", + "params": [ + 0, + 20 + ] + } + ] + }, + "loops": 35 + }, + { + "name": "滑动 Feed 15 次,随机间隔 15-50s", + "android": { + "actions": [ + { + "method": "swipe", + "params": "up" + }, + { + "method": "sleep_random", + "params": [ + 15, + 50 + ] + } + ] + }, + "loops": 15 + } + ] +} diff --git a/examples/uitest/demo_feed_random_slide_test.go b/examples/uitest/demo_feed_random_slide_test.go new file mode 100644 index 00000000..cc7c5d95 --- /dev/null +++ b/examples/uitest/demo_feed_random_slide_test.go @@ -0,0 +1,50 @@ +//go:build localtest + +package uitest + +import ( + "testing" + + "github.com/httprunner/httprunner/v4/hrp" + "github.com/httprunner/httprunner/v4/hrp/pkg/uixt" +) + +func TestAndroidDouyinFeedTest(t *testing.T) { + testCase := &hrp.TestCase{ + Config: hrp.NewConfig("点播_抖音_滑动场景_随机间隔_android"). + WithVariables(map[string]interface{}{ + "device": "${ENV(SerialNumber)}", + }). + SetAndroid(uixt.WithSerialNumber("$device")), + TestSteps: []hrp.IStep{ + hrp.NewStep("启动抖音"). + Android(). + AppTerminate("com.ss.android.ugc.aweme"). + AppLaunch("com.ss.android.ugc.aweme"). + Sleep(10), + hrp.NewStep("处理青少年弹窗"). + Android(). + TapByOCR("我知道了", uixt.WithIgnoreNotFoundError(true)), + hrp.NewStep("滑动 Feed 35 次,随机间隔 0-20s"). + Loop(35). + Android(). + SwipeUp(). + SleepRandom(0, 20), + hrp.NewStep("滑动 Feed 15 次,随机间隔 15-50s"). + Loop(15). + Android(). + SwipeUp(). + SleepRandom(15, 50), + }, + } + + if err := testCase.Dump2JSON("demo_feed_random_slide.json"); err != nil { + t.Fatal(err) + } + + runner := hrp.NewRunner(t).SetSaveTests(true) + err := runner.Run(testCase) + if err != nil { + t.Fatal(err) + } +} diff --git a/hrp/internal/builtin/function.go b/hrp/internal/builtin/function.go index 00d01d97..baf94a19 100644 --- a/hrp/internal/builtin/function.go +++ b/hrp/internal/builtin/function.go @@ -24,6 +24,8 @@ var Functions = map[string]interface{}{ "get_timestamp": getTimestamp, // call without arguments "sleep": sleep, // call with one argument "gen_random_string": genRandomString, // call with one argument + "random_int": rand.Intn, // call with one argument + "random_range": random_range, // call with two arguments "max": math.Max, // call with two arguments "md5": MD5, // call with one argument "parameterize": loadFromCSV, @@ -49,6 +51,10 @@ func init() { rand.Seed(time.Now().UnixNano()) } +func random_range(a, b float64) float64 { + return a + rand.Float64()*(b-a) +} + func getTimestamp() int64 { return time.Now().UnixNano() / int64(time.Millisecond) } diff --git a/hrp/pkg/uixt/android_adb_driver.go b/hrp/pkg/uixt/android_adb_driver.go index 50eed4c7..394bf14e 100644 --- a/hrp/pkg/uixt/android_adb_driver.go +++ b/hrp/pkg/uixt/android_adb_driver.go @@ -321,6 +321,13 @@ func (ad *adbDriver) IsHealthy() (healthy bool, err error) { 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 { + return err + } + + // start logcat err = ad.logcat.CatchLogcat() if err != nil { err = errors.Wrap(code.AndroidCaptureLogError, diff --git a/hrp/pkg/uixt/android_device.go b/hrp/pkg/uixt/android_device.go index 0df9162d..d70afa54 100644 --- a/hrp/pkg/uixt/android_device.go +++ b/hrp/pkg/uixt/android_device.go @@ -311,12 +311,13 @@ func (l *AdbLogcat) CatchLogcat() (err error) { } // clear logcat - if err = myexec.RunCommand("adb", "-s", l.serial, "logcat", "-c"); err != nil { + if err = myexec.RunCommand("adb", "-s", l.serial, "logcat", "--clear"); err != nil { return } // start logcat - l.cmd = myexec.Command("adb", "-s", l.serial, "logcat", "-v", "time", "-s", "iesqaMonitor:V") + l.cmd = myexec.Command("adb", "-s", l.serial, + "logcat", "--format", "time", "-s", "iesqaMonitor:V") l.cmd.Stderr = l.logBuffer l.cmd.Stdout = l.logBuffer if err = l.cmd.Start(); err != nil { diff --git a/hrp/pkg/uixt/ext.go b/hrp/pkg/uixt/ext.go index d87e7b18..3a75c156 100644 --- a/hrp/pkg/uixt/ext.go +++ b/hrp/pkg/uixt/ext.go @@ -7,6 +7,7 @@ import ( "image" "image/jpeg" "image/png" + "math/rand" "mime" "mime/multipart" "net/http" @@ -32,6 +33,7 @@ const ( AppStop MobileMethod = "app_stop" CtlScreenShot MobileMethod = "screenshot" CtlSleep MobileMethod = "sleep" + CtlSleepRandom MobileMethod = "sleep_random" CtlStartCamera MobileMethod = "camera_start" // alias for app_launch camera CtlStopCamera MobileMethod = "camera_stop" // alias for app_terminate camera RecordStart MobileMethod = "record_start" @@ -315,6 +317,10 @@ func isPathExists(path string) bool { return true } +func init() { + rand.Seed(time.Now().UnixNano()) +} + func (dExt *DriverExt) FindUIRectInUIKit(search string, options ...DataOption) (x, y, width, height float64, err error) { // click on text, using OCR if !isPathExists(search) { @@ -602,6 +608,16 @@ func (dExt *DriverExt) DoAction(action MobileAction) error { return nil } return fmt.Errorf("invalid sleep params: %v(%T)", action.Params, action.Params) + case CtlSleepRandom: + if params, ok := action.Params.([]interface{}); ok && len(params) == 2 { + a := params[0].(float64) + b := params[1].(float64) + n := a + rand.Float64()*(b-a) + log.Info().Float64("duration", n).Msg("sleep random seconds") + time.Sleep(time.Duration(n*1000) * time.Millisecond) + return nil + } + return fmt.Errorf("invalid sleep random params: %v(%T)", action.Params, action.Params) case CtlScreenShot: // take snapshot log.Info().Msg("take snapshot for current screen") diff --git a/hrp/step_mobile_ui.go b/hrp/step_mobile_ui.go index 5d37b203..16dc290b 100644 --- a/hrp/step_mobile_ui.go +++ b/hrp/step_mobile_ui.go @@ -281,6 +281,14 @@ func (s *StepMobile) Sleep(n float64) *StepMobile { return &StepMobile{step: s.step} } +func (s *StepMobile) SleepRandom(a, b float64) *StepMobile { + s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{ + Method: uixt.CtlSleepRandom, + Params: []float64{a, b}, + }) + return &StepMobile{step: s.step} +} + func (s *StepMobile) ScreenShot() *StepMobile { s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{ Method: uixt.CtlScreenShot,