From 1bb2079c872f117c942f2db0da42ad0771abb251 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Wed, 3 May 2023 23:00:17 +0800 Subject: [PATCH] feat: catch interrupt signal --- examples/uitest/demo_android_feed_swipe.json | 15 ++++++---- hrp/internal/code/code.go | 2 ++ hrp/pkg/uixt/ext.go | 5 ++++ hrp/pkg/uixt/video_crawler.go | 14 ++++++++-- hrp/runner.go | 16 +++++++++++ hrp/step_mobile_ui.go | 29 +++++++++++++------- 6 files changed, 63 insertions(+), 18 deletions(-) diff --git a/examples/uitest/demo_android_feed_swipe.json b/examples/uitest/demo_android_feed_swipe.json index d74f45ae..c50ba576 100644 --- a/examples/uitest/demo_android_feed_swipe.json +++ b/examples/uitest/demo_android_feed_swipe.json @@ -45,7 +45,9 @@ { "method": "tap_ocr", "params": "ζˆ‘ηŸ₯道了", - "ignore_NotFoundError": true + "options": { + "ignore_NotFoundError": true + } } ] } @@ -56,7 +58,8 @@ "actions": [ { "method": "swipe", - "params": "up" + "params": "up", + "options": {} }, { "method": "sleep_random", @@ -75,7 +78,8 @@ "actions": [ { "method": "swipe", - "params": "up" + "params": "up", + "options": {} }, { "method": "sleep_random", @@ -94,7 +98,8 @@ "actions": [ { "method": "swipe", - "params": "up" + "params": "up", + "options": {} }, { "method": "sleep_random", @@ -131,4 +136,4 @@ ] } ] -} +} \ No newline at end of file diff --git a/hrp/internal/code/code.go b/hrp/internal/code/code.go index 301850b7..87c830ea 100644 --- a/hrp/internal/code/code.go +++ b/hrp/internal/code/code.go @@ -44,6 +44,7 @@ var ( InitPluginFailed = errors.New("init plugin failed") // 31 BuildGoPluginFailed = errors.New("build go plugin failed") // 32 BuildPyPluginFailed = errors.New("build py plugin failed") // 33 + InterruptError = errors.New("interrupt error") // 38 TimeoutError = errors.New("timeout error") // 39 ) @@ -112,6 +113,7 @@ var errorsMap = map[error]int{ InitPluginFailed: 31, BuildGoPluginFailed: 32, BuildPyPluginFailed: 33, + InterruptError: 38, TimeoutError: 39, // ios related diff --git a/hrp/pkg/uixt/ext.go b/hrp/pkg/uixt/ext.go index daf059c9..cb25faa0 100644 --- a/hrp/pkg/uixt/ext.go +++ b/hrp/pkg/uixt/ext.go @@ -12,8 +12,10 @@ import ( "mime/multipart" "net/http" "os" + "os/signal" "path/filepath" "strings" + "syscall" "time" "github.com/pkg/errors" @@ -62,6 +64,7 @@ type DriverExt struct { frame *bytes.Buffer doneMjpegStream chan bool OCRService IOCRService // used to get texts from image + interruptSignal chan os.Signal // cache step data cacheStepData cacheStepData @@ -75,7 +78,9 @@ func NewDriverExt(device Device, driver WebDriver) (dExt *DriverExt, err error) ScreenShots: make([]string, 0), OcrResults: make(map[string]string), }, + interruptSignal: make(chan os.Signal, 1), } + signal.Notify(dExt.interruptSignal, syscall.SIGTERM, syscall.SIGINT) dExt.doneMjpegStream = make(chan bool, 1) // get device window size diff --git a/hrp/pkg/uixt/video_crawler.go b/hrp/pkg/uixt/video_crawler.go index 4608d9f1..169f208a 100644 --- a/hrp/pkg/uixt/video_crawler.go +++ b/hrp/pkg/uixt/video_crawler.go @@ -148,7 +148,11 @@ func (l *LiveCrawler) Run(driver *DriverExt, enterPoint PointF) error { for !l.currentStat.isLiveTargetAchieved() { select { case <-l.currentStat.timer.C: - return errors.Wrap(code.TimeoutError, "timeout in live crawler") + log.Warn().Msg("timeout in live crawler") + return errors.Wrap(code.TimeoutError, "live crawler timeout") + case <-l.driver.interruptSignal: + log.Warn().Msg("interrupted in live crawler") + return errors.Wrap(code.InterruptError, "live crawler interrupted") default: // check if live room if err := l.driver.assertActivity(l.configs.AppPackageName, "live"); err != nil { @@ -246,7 +250,11 @@ func (dExt *DriverExt) VideoCrawler(configs *VideoCrawlerConfigs) (err error) { for { select { case <-currVideoStat.timer.C: - return errors.Wrap(code.TimeoutError, "timeout in feed crawler") + log.Warn().Msg("timeout in feed crawler") + return errors.Wrap(code.TimeoutError, "feed crawler timeout") + case <-dExt.interruptSignal: + log.Warn().Msg("interrupted in feed crawler") + return errors.Wrap(code.InterruptError, "feed crawler interrupted") default: // check if feed page if err := dExt.assertActivity(configs.AppPackageName, "feed"); err != nil { @@ -271,7 +279,7 @@ func (dExt *DriverExt) VideoCrawler(configs *VideoCrawlerConfigs) (err error) { log.Info().Msg("live video found") if !liveCrawler.currentStat.isLiveTargetAchieved() { if err := liveCrawler.Run(dExt, enterPoint); err != nil { - if errors.Is(err, code.TimeoutError) { + if errors.Is(err, code.TimeoutError) || errors.Is(err, code.InterruptError) { return err } log.Error().Err(err).Msg("run live crawler failed, continue") diff --git a/hrp/runner.go b/hrp/runner.go index 0ac6fe16..818d695c 100644 --- a/hrp/runner.go +++ b/hrp/runner.go @@ -8,8 +8,11 @@ import ( "net/http" "net/http/cookiejar" "net/url" + "os" + "os/signal" "path/filepath" "strings" + "syscall" "testing" "time" @@ -39,6 +42,8 @@ func NewRunner(t *testing.T) *HRPRunner { t = &testing.T{} } jar, _ := cookiejar.New(nil) + interruptSignal := make(chan os.Signal, 1) + signal.Notify(interruptSignal, syscall.SIGTERM, syscall.SIGINT) return &HRPRunner{ t: t, failfast: true, // default to failfast @@ -62,6 +67,7 @@ func NewRunner(t *testing.T) *HRPRunner { TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }, caseTimeoutTimer: time.NewTimer(time.Hour * 2), // default case timeout to 2 hour + interruptSignal: interruptSignal, } } @@ -79,6 +85,7 @@ type HRPRunner struct { wsDialer *websocket.Dialer uiClients map[string]*uixt.DriverExt // UI automation clients for iOS and Android, key is udid/serial caseTimeoutTimer *time.Timer // case timeout timer + interruptSignal chan os.Signal // interrupt signal channel } // SetClientTransport configures transport of http client for high concurrency load testing @@ -520,7 +527,11 @@ func (r *SessionRunner) Start(givenVars map[string]interface{}) error { for _, step := range r.caseRunner.testCase.TestSteps { select { case <-r.caseRunner.hrpRunner.caseTimeoutTimer.C: + log.Warn().Msg("timeout in session runner") return errors.Wrap(code.TimeoutError, "session runner timeout") + case <-r.caseRunner.hrpRunner.interruptSignal: + log.Warn().Msg("interrupted in session runner") + return errors.Wrap(code.InterruptError, "session runner interrupted") default: // TODO: parse step struct // parse step name @@ -579,6 +590,11 @@ func (r *SessionRunner) Start(givenVars map[string]interface{}) error { Bool("success", false). Msg("run step end") + // interrupted or timeout, abort running + if errors.Is(err, code.InterruptError) || errors.Is(err, code.TimeoutError) { + return err + } + // check if failfast if r.caseRunner.hrpRunner.failfast { return errors.Wrap(err, "abort running due to failfast setting") diff --git a/hrp/step_mobile_ui.go b/hrp/step_mobile_ui.go index 3e0944e2..850330a0 100644 --- a/hrp/step_mobile_ui.go +++ b/hrp/step_mobile_ui.go @@ -629,18 +629,27 @@ func runStepMobileUI(s *SessionRunner, step *TStep) (stepResult *StepResult, err // run actions for _, action := range actions { - if action.Params, err = s.caseRunner.parser.Parse(action.Params, stepVariables); err != nil { - if !code.IsErrorPredefined(err) { - err = errors.Wrap(code.ParseError, - fmt.Sprintf("parse action params failed: %v", err)) + select { + case <-s.caseRunner.hrpRunner.caseTimeoutTimer.C: + log.Warn().Msg("timeout in mobile UI runner") + return stepResult, errors.Wrap(code.TimeoutError, "mobile UI runner timeout") + case <-s.caseRunner.hrpRunner.interruptSignal: + log.Warn().Msg("interrupted in mobile UI runner") + return stepResult, errors.Wrap(code.InterruptError, "mobile UI runner interrupted") + default: + if action.Params, err = s.caseRunner.parser.Parse(action.Params, stepVariables); err != nil { + if !code.IsErrorPredefined(err) { + err = errors.Wrap(code.ParseError, + fmt.Sprintf("parse action params failed: %v", err)) + } + return stepResult, err } - return stepResult, err - } - if err := uiDriver.DoAction(action); err != nil { - if !code.IsErrorPredefined(err) { - err = errors.Wrap(code.MobileUIDriverError, err.Error()) + if err := uiDriver.DoAction(action); err != nil { + if !code.IsErrorPredefined(err) { + err = errors.Wrap(code.MobileUIDriverError, err.Error()) + } + return stepResult, err } - return stepResult, err } }