From 4ee3b43d1f9f439ca036b544a532b42563ebd09c Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Wed, 3 May 2023 12:48:20 +0800 Subject: [PATCH] feat: set testcase and request timeout in seconds --- hrp/cmd/run.go | 5 +- hrp/config.go | 16 ++-- hrp/runner.go | 158 ++++++++++++++++++++++----------------- hrp/step_request_test.go | 6 +- 4 files changed, 106 insertions(+), 79 deletions(-) diff --git a/hrp/cmd/run.go b/hrp/cmd/run.go index 5a7b4f8e..f46f4efc 100644 --- a/hrp/cmd/run.go +++ b/hrp/cmd/run.go @@ -37,6 +37,7 @@ var ( proxyUrl string saveTests bool genHTMLReport bool + caseTimeout float32 ) func init() { @@ -48,12 +49,14 @@ func init() { runCmd.Flags().StringVarP(&proxyUrl, "proxy-url", "p", "", "set proxy url") runCmd.Flags().BoolVarP(&saveTests, "save-tests", "s", false, "save tests summary") runCmd.Flags().BoolVarP(&genHTMLReport, "gen-html-report", "g", false, "generate html report") + runCmd.Flags().Float32Var(&caseTimeout, "case-timeout", 3600, "set testcase timeout (seconds)") } func makeHRPRunner() *hrp.HRPRunner { runner := hrp.NewRunner(nil). SetFailfast(!continueOnFailure). - SetSaveTests(saveTests) + SetSaveTests(saveTests). + SetCaseTimeout(caseTimeout) if genHTMLReport { runner.GenHTMLReport() } diff --git a/hrp/config.go b/hrp/config.go index 9c03278f..6857aee1 100644 --- a/hrp/config.go +++ b/hrp/config.go @@ -2,7 +2,6 @@ package hrp import ( "reflect" - "time" "github.com/httprunner/httprunner/v4/hrp/internal/builtin" "github.com/httprunner/httprunner/v4/hrp/pkg/uixt" @@ -32,7 +31,8 @@ type TConfig struct { WebSocketSetting *WebSocketConfig `json:"websocket,omitempty" yaml:"websocket,omitempty"` IOS []*uixt.IOSDevice `json:"ios,omitempty" yaml:"ios,omitempty"` Android []*uixt.AndroidDevice `json:"android,omitempty" yaml:"android,omitempty"` - Timeout float64 `json:"timeout,omitempty" yaml:"timeout,omitempty"` // global timeout in seconds + RequestTimeout float32 `json:"request_timeout,omitempty" yaml:"request_timeout,omitempty"` // request timeout in seconds + CaseTimeout float32 `json:"case_timeout,omitempty" yaml:"case_timeout,omitempty"` // testcase timeout in seconds Export []string `json:"export,omitempty" yaml:"export,omitempty"` Weight int `json:"weight,omitempty" yaml:"weight,omitempty"` Path string `json:"path,omitempty" yaml:"path,omitempty"` // testcase file path @@ -75,9 +75,15 @@ func (c *TConfig) SetThinkTime(strategy thinkTimeStrategy, cfg interface{}, limi return c } -// SetTimeout sets testcase timeout in seconds. -func (c *TConfig) SetTimeout(timeout time.Duration) *TConfig { - c.Timeout = timeout.Seconds() +// SetRequestTimeout sets request timeout in seconds. +func (c *TConfig) SetRequestTimeout(seconds float32) *TConfig { + c.RequestTimeout = seconds + return c +} + +// SetCaseTimeout sets testcase timeout in seconds. +func (c *TConfig) SetCaseTimeout(seconds float32) *TConfig { + c.CaseTimeout = seconds return c } diff --git a/hrp/runner.go b/hrp/runner.go index 2c5e3c06..0ac6fe16 100644 --- a/hrp/runner.go +++ b/hrp/runner.go @@ -21,6 +21,7 @@ import ( "golang.org/x/net/http2" "github.com/httprunner/httprunner/v4/hrp/internal/builtin" + "github.com/httprunner/httprunner/v4/hrp/internal/code" "github.com/httprunner/httprunner/v4/hrp/internal/sdk" "github.com/httprunner/httprunner/v4/hrp/internal/version" "github.com/httprunner/httprunner/v4/hrp/pkg/uixt" @@ -60,22 +61,24 @@ func NewRunner(t *testing.T) *HRPRunner { wsDialer: &websocket.Dialer{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }, + caseTimeoutTimer: time.NewTimer(time.Hour * 2), // default case timeout to 2 hour } } type HRPRunner struct { - t *testing.T - failfast bool - httpStatOn bool - requestsLogOn bool - pluginLogOn bool - venv string - saveTests bool - genHTMLReport bool - httpClient *http.Client - http2Client *http.Client - wsDialer *websocket.Dialer - uiClients map[string]*uixt.DriverExt // UI automation clients for iOS and Android, key is udid/serial + t *testing.T + failfast bool + httpStatOn bool + requestsLogOn bool + pluginLogOn bool + venv string + saveTests bool + genHTMLReport bool + httpClient *http.Client + http2Client *http.Client + 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 } // SetClientTransport configures transport of http client for high concurrency load testing @@ -152,10 +155,17 @@ func (r *HRPRunner) SetProxyUrl(proxyUrl string) *HRPRunner { return r } -// SetTimeout configures global timeout in seconds. -func (r *HRPRunner) SetTimeout(timeout time.Duration) *HRPRunner { - log.Info().Float64("timeout(seconds)", timeout.Seconds()).Msg("[init] SetTimeout") - r.httpClient.Timeout = timeout +// SetRequestTimeout configures global request timeout in seconds. +func (r *HRPRunner) SetRequestTimeout(seconds float32) *HRPRunner { + log.Info().Float32("timeout_seconds", seconds).Msg("[init] SetRequestTimeout") + r.httpClient.Timeout = time.Duration(seconds*1000) * time.Millisecond + return r +} + +// SetCaseTimeout configures global testcase timeout in seconds. +func (r *HRPRunner) SetCaseTimeout(seconds float32) *HRPRunner { + log.Info().Float32("timeout_seconds", seconds).Msg("[init] SetCaseTimeout") + r.caseTimeoutTimer = time.NewTimer(time.Duration(seconds*1000) * time.Millisecond) return r } @@ -291,10 +301,13 @@ func (r *HRPRunner) NewCaseRunner(testcase *TestCase) (*CaseRunner, error) { return nil, errors.Wrap(err, "parse testcase config failed") } + // set request timeout in seconds + if testcase.Config.RequestTimeout != 0 { + r.SetRequestTimeout(testcase.Config.RequestTimeout) + } // set testcase timeout in seconds - if testcase.Config.Timeout != 0 { - timeout := time.Duration(testcase.Config.Timeout*1000) * time.Millisecond - r.SetTimeout(timeout) + if testcase.Config.CaseTimeout != 0 { + r.SetCaseTimeout(testcase.Config.CaseTimeout) } // load plugin info to testcase config @@ -505,66 +518,71 @@ func (r *SessionRunner) Start(givenVars map[string]interface{}) error { // run step in sequential order for _, step := range r.caseRunner.testCase.TestSteps { - // TODO: parse step struct - // parse step name - parsedName, err := r.caseRunner.parser.ParseString(step.Name(), r.sessionVariables) - if err != nil { - parsedName = step.Name() - } - stepName := convertString(parsedName) - log.Info().Str("step", stepName). - Str("type", string(step.Type())).Msg("run step start") + select { + case <-r.caseRunner.hrpRunner.caseTimeoutTimer.C: + return errors.Wrap(code.TimeoutError, "session runner timeout") + default: + // TODO: parse step struct + // parse step name + parsedName, err := r.caseRunner.parser.ParseString(step.Name(), r.sessionVariables) + if err != nil { + parsedName = step.Name() + } + stepName := convertString(parsedName) + log.Info().Str("step", stepName). + Str("type", string(step.Type())).Msg("run step start") - // run times of step - loopTimes := step.Struct().Loops - if loopTimes < 0 { - log.Warn().Int("loops", loopTimes).Msg("loop times should be positive, set to 1") - loopTimes = 1 - } else if loopTimes == 0 { - loopTimes = 1 - } else if loopTimes > 1 { - log.Info().Int("loops", loopTimes).Msg("run step with specified loop times") - } - - // run step with specified loop times - var stepResult *StepResult - for i := 1; i <= loopTimes; i++ { - var loopIndex string - if loopTimes > 1 { - log.Info().Int("index", i).Msg("start running step in loop") - loopIndex = fmt.Sprintf("_loop_%d", i) + // run times of step + loopTimes := step.Struct().Loops + if loopTimes < 0 { + log.Warn().Int("loops", loopTimes).Msg("loop times should be positive, set to 1") + loopTimes = 1 + } else if loopTimes == 0 { + loopTimes = 1 + } else if loopTimes > 1 { + log.Info().Int("loops", loopTimes).Msg("run step with specified loop times") } - // run step - stepResult, err = step.Run(r) - stepResult.Name = stepName + loopIndex + // run step with specified loop times + var stepResult *StepResult + for i := 1; i <= loopTimes; i++ { + var loopIndex string + if loopTimes > 1 { + log.Info().Int("index", i).Msg("start running step in loop") + loopIndex = fmt.Sprintf("_loop_%d", i) + } - r.updateSummary(stepResult) - } + // run step + stepResult, err = step.Run(r) + stepResult.Name = stepName + loopIndex - // update extracted variables - for k, v := range stepResult.ExportVars { - r.sessionVariables[k] = v - } + r.updateSummary(stepResult) + } - if err == nil { - log.Info().Str("step", stepResult.Name). + // update extracted variables + for k, v := range stepResult.ExportVars { + r.sessionVariables[k] = v + } + + if err == nil { + log.Info().Str("step", stepResult.Name). + Str("type", string(stepResult.StepType)). + Bool("success", true). + Interface("exportVars", stepResult.ExportVars). + Msg("run step end") + continue + } + + // failed + log.Error().Err(err).Str("step", stepResult.Name). Str("type", string(stepResult.StepType)). - Bool("success", true). - Interface("exportVars", stepResult.ExportVars). + Bool("success", false). Msg("run step end") - continue - } - // failed - log.Error().Err(err).Str("step", stepResult.Name). - Str("type", string(stepResult.StepType)). - Bool("success", false). - Msg("run step end") - - // check if failfast - if r.caseRunner.hrpRunner.failfast { - return errors.Wrap(err, "abort running due to failfast setting") + // check if failfast + if r.caseRunner.hrpRunner.failfast { + return errors.Wrap(err, "abort running due to failfast setting") + } } } diff --git a/hrp/step_request_test.go b/hrp/step_request_test.go index 7172bf66..7b476ba9 100644 --- a/hrp/step_request_test.go +++ b/hrp/step_request_test.go @@ -164,7 +164,7 @@ func TestRunCaseWithTimeout(t *testing.T) { // global timeout testcase1 := &TestCase{ Config: NewConfig("TestCase1"). - SetTimeout(10 * time.Second). // set global timeout to 10s + SetRequestTimeout(10). // set global timeout to 10s SetBaseURL("https://httpbin.org"), TestSteps: []IStep{ NewStep("step1"). @@ -180,7 +180,7 @@ func TestRunCaseWithTimeout(t *testing.T) { testcase2 := &TestCase{ Config: NewConfig("TestCase2"). - SetTimeout(10 * time.Second). // set global timeout to 10s + SetRequestTimeout(10). // set global timeout to 10s SetBaseURL("https://httpbin.org"), TestSteps: []IStep{ NewStep("step1"). @@ -197,7 +197,7 @@ func TestRunCaseWithTimeout(t *testing.T) { // step timeout testcase3 := &TestCase{ Config: NewConfig("TestCase3"). - SetTimeout(10 * time.Second). + SetRequestTimeout(10). SetBaseURL("https://httpbin.org"), TestSteps: []IStep{ NewStep("step2").