mirror of
https://github.com/httprunner/httprunner.git
synced 2026-09-04 23:16:57 +08:00
feat: catch interrupt signal
This commit is contained in:
@@ -45,7 +45,9 @@
|
|||||||
{
|
{
|
||||||
"method": "tap_ocr",
|
"method": "tap_ocr",
|
||||||
"params": "我知道了",
|
"params": "我知道了",
|
||||||
"ignore_NotFoundError": true
|
"options": {
|
||||||
|
"ignore_NotFoundError": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -56,7 +58,8 @@
|
|||||||
"actions": [
|
"actions": [
|
||||||
{
|
{
|
||||||
"method": "swipe",
|
"method": "swipe",
|
||||||
"params": "up"
|
"params": "up",
|
||||||
|
"options": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"method": "sleep_random",
|
"method": "sleep_random",
|
||||||
@@ -75,7 +78,8 @@
|
|||||||
"actions": [
|
"actions": [
|
||||||
{
|
{
|
||||||
"method": "swipe",
|
"method": "swipe",
|
||||||
"params": "up"
|
"params": "up",
|
||||||
|
"options": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"method": "sleep_random",
|
"method": "sleep_random",
|
||||||
@@ -94,7 +98,8 @@
|
|||||||
"actions": [
|
"actions": [
|
||||||
{
|
{
|
||||||
"method": "swipe",
|
"method": "swipe",
|
||||||
"params": "up"
|
"params": "up",
|
||||||
|
"options": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"method": "sleep_random",
|
"method": "sleep_random",
|
||||||
@@ -131,4 +136,4 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -44,6 +44,7 @@ var (
|
|||||||
InitPluginFailed = errors.New("init plugin failed") // 31
|
InitPluginFailed = errors.New("init plugin failed") // 31
|
||||||
BuildGoPluginFailed = errors.New("build go plugin failed") // 32
|
BuildGoPluginFailed = errors.New("build go plugin failed") // 32
|
||||||
BuildPyPluginFailed = errors.New("build py plugin failed") // 33
|
BuildPyPluginFailed = errors.New("build py plugin failed") // 33
|
||||||
|
InterruptError = errors.New("interrupt error") // 38
|
||||||
TimeoutError = errors.New("timeout error") // 39
|
TimeoutError = errors.New("timeout error") // 39
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -112,6 +113,7 @@ var errorsMap = map[error]int{
|
|||||||
InitPluginFailed: 31,
|
InitPluginFailed: 31,
|
||||||
BuildGoPluginFailed: 32,
|
BuildGoPluginFailed: 32,
|
||||||
BuildPyPluginFailed: 33,
|
BuildPyPluginFailed: 33,
|
||||||
|
InterruptError: 38,
|
||||||
TimeoutError: 39,
|
TimeoutError: 39,
|
||||||
|
|
||||||
// ios related
|
// ios related
|
||||||
|
|||||||
@@ -12,8 +12,10 @@ import (
|
|||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@@ -62,6 +64,7 @@ type DriverExt struct {
|
|||||||
frame *bytes.Buffer
|
frame *bytes.Buffer
|
||||||
doneMjpegStream chan bool
|
doneMjpegStream chan bool
|
||||||
OCRService IOCRService // used to get texts from image
|
OCRService IOCRService // used to get texts from image
|
||||||
|
interruptSignal chan os.Signal
|
||||||
|
|
||||||
// cache step data
|
// cache step data
|
||||||
cacheStepData cacheStepData
|
cacheStepData cacheStepData
|
||||||
@@ -75,7 +78,9 @@ func NewDriverExt(device Device, driver WebDriver) (dExt *DriverExt, err error)
|
|||||||
ScreenShots: make([]string, 0),
|
ScreenShots: make([]string, 0),
|
||||||
OcrResults: make(map[string]string),
|
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)
|
dExt.doneMjpegStream = make(chan bool, 1)
|
||||||
|
|
||||||
// get device window size
|
// get device window size
|
||||||
|
|||||||
@@ -148,7 +148,11 @@ func (l *LiveCrawler) Run(driver *DriverExt, enterPoint PointF) error {
|
|||||||
for !l.currentStat.isLiveTargetAchieved() {
|
for !l.currentStat.isLiveTargetAchieved() {
|
||||||
select {
|
select {
|
||||||
case <-l.currentStat.timer.C:
|
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:
|
default:
|
||||||
// check if live room
|
// check if live room
|
||||||
if err := l.driver.assertActivity(l.configs.AppPackageName, "live"); err != nil {
|
if err := l.driver.assertActivity(l.configs.AppPackageName, "live"); err != nil {
|
||||||
@@ -246,7 +250,11 @@ func (dExt *DriverExt) VideoCrawler(configs *VideoCrawlerConfigs) (err error) {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-currVideoStat.timer.C:
|
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:
|
default:
|
||||||
// check if feed page
|
// check if feed page
|
||||||
if err := dExt.assertActivity(configs.AppPackageName, "feed"); err != nil {
|
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")
|
log.Info().Msg("live video found")
|
||||||
if !liveCrawler.currentStat.isLiveTargetAchieved() {
|
if !liveCrawler.currentStat.isLiveTargetAchieved() {
|
||||||
if err := liveCrawler.Run(dExt, enterPoint); err != nil {
|
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
|
return err
|
||||||
}
|
}
|
||||||
log.Error().Err(err).Msg("run live crawler failed, continue")
|
log.Error().Err(err).Msg("run live crawler failed, continue")
|
||||||
|
|||||||
@@ -8,8 +8,11 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/http/cookiejar"
|
"net/http/cookiejar"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -39,6 +42,8 @@ func NewRunner(t *testing.T) *HRPRunner {
|
|||||||
t = &testing.T{}
|
t = &testing.T{}
|
||||||
}
|
}
|
||||||
jar, _ := cookiejar.New(nil)
|
jar, _ := cookiejar.New(nil)
|
||||||
|
interruptSignal := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(interruptSignal, syscall.SIGTERM, syscall.SIGINT)
|
||||||
return &HRPRunner{
|
return &HRPRunner{
|
||||||
t: t,
|
t: t,
|
||||||
failfast: true, // default to failfast
|
failfast: true, // default to failfast
|
||||||
@@ -62,6 +67,7 @@ func NewRunner(t *testing.T) *HRPRunner {
|
|||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||||
},
|
},
|
||||||
caseTimeoutTimer: time.NewTimer(time.Hour * 2), // default case timeout to 2 hour
|
caseTimeoutTimer: time.NewTimer(time.Hour * 2), // default case timeout to 2 hour
|
||||||
|
interruptSignal: interruptSignal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,6 +85,7 @@ type HRPRunner struct {
|
|||||||
wsDialer *websocket.Dialer
|
wsDialer *websocket.Dialer
|
||||||
uiClients map[string]*uixt.DriverExt // UI automation clients for iOS and Android, key is udid/serial
|
uiClients map[string]*uixt.DriverExt // UI automation clients for iOS and Android, key is udid/serial
|
||||||
caseTimeoutTimer *time.Timer // case timeout timer
|
caseTimeoutTimer *time.Timer // case timeout timer
|
||||||
|
interruptSignal chan os.Signal // interrupt signal channel
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetClientTransport configures transport of http client for high concurrency load testing
|
// 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 {
|
for _, step := range r.caseRunner.testCase.TestSteps {
|
||||||
select {
|
select {
|
||||||
case <-r.caseRunner.hrpRunner.caseTimeoutTimer.C:
|
case <-r.caseRunner.hrpRunner.caseTimeoutTimer.C:
|
||||||
|
log.Warn().Msg("timeout in session runner")
|
||||||
return errors.Wrap(code.TimeoutError, "session runner timeout")
|
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:
|
default:
|
||||||
// TODO: parse step struct
|
// TODO: parse step struct
|
||||||
// parse step name
|
// parse step name
|
||||||
@@ -579,6 +590,11 @@ func (r *SessionRunner) Start(givenVars map[string]interface{}) error {
|
|||||||
Bool("success", false).
|
Bool("success", false).
|
||||||
Msg("run step end")
|
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
|
// check if failfast
|
||||||
if r.caseRunner.hrpRunner.failfast {
|
if r.caseRunner.hrpRunner.failfast {
|
||||||
return errors.Wrap(err, "abort running due to failfast setting")
|
return errors.Wrap(err, "abort running due to failfast setting")
|
||||||
|
|||||||
+19
-10
@@ -629,18 +629,27 @@ func runStepMobileUI(s *SessionRunner, step *TStep) (stepResult *StepResult, err
|
|||||||
|
|
||||||
// run actions
|
// run actions
|
||||||
for _, action := range actions {
|
for _, action := range actions {
|
||||||
if action.Params, err = s.caseRunner.parser.Parse(action.Params, stepVariables); err != nil {
|
select {
|
||||||
if !code.IsErrorPredefined(err) {
|
case <-s.caseRunner.hrpRunner.caseTimeoutTimer.C:
|
||||||
err = errors.Wrap(code.ParseError,
|
log.Warn().Msg("timeout in mobile UI runner")
|
||||||
fmt.Sprintf("parse action params failed: %v", err))
|
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) {
|
||||||
if err := uiDriver.DoAction(action); err != nil {
|
err = errors.Wrap(code.MobileUIDriverError, err.Error())
|
||||||
if !code.IsErrorPredefined(err) {
|
}
|
||||||
err = errors.Wrap(code.MobileUIDriverError, err.Error())
|
return stepResult, err
|
||||||
}
|
}
|
||||||
return stepResult, err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user