feat: set testcase and request timeout in seconds

This commit is contained in:
lilong.129
2023-05-03 12:48:20 +08:00
parent d27729edc6
commit 4ee3b43d1f
4 changed files with 106 additions and 79 deletions

View File

@@ -37,6 +37,7 @@ var (
proxyUrl string proxyUrl string
saveTests bool saveTests bool
genHTMLReport bool genHTMLReport bool
caseTimeout float32
) )
func init() { func init() {
@@ -48,12 +49,14 @@ func init() {
runCmd.Flags().StringVarP(&proxyUrl, "proxy-url", "p", "", "set proxy url") runCmd.Flags().StringVarP(&proxyUrl, "proxy-url", "p", "", "set proxy url")
runCmd.Flags().BoolVarP(&saveTests, "save-tests", "s", false, "save tests summary") 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().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 { func makeHRPRunner() *hrp.HRPRunner {
runner := hrp.NewRunner(nil). runner := hrp.NewRunner(nil).
SetFailfast(!continueOnFailure). SetFailfast(!continueOnFailure).
SetSaveTests(saveTests) SetSaveTests(saveTests).
SetCaseTimeout(caseTimeout)
if genHTMLReport { if genHTMLReport {
runner.GenHTMLReport() runner.GenHTMLReport()
} }

View File

@@ -2,7 +2,6 @@ package hrp
import ( import (
"reflect" "reflect"
"time"
"github.com/httprunner/httprunner/v4/hrp/internal/builtin" "github.com/httprunner/httprunner/v4/hrp/internal/builtin"
"github.com/httprunner/httprunner/v4/hrp/pkg/uixt" "github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
@@ -32,7 +31,8 @@ type TConfig struct {
WebSocketSetting *WebSocketConfig `json:"websocket,omitempty" yaml:"websocket,omitempty"` WebSocketSetting *WebSocketConfig `json:"websocket,omitempty" yaml:"websocket,omitempty"`
IOS []*uixt.IOSDevice `json:"ios,omitempty" yaml:"ios,omitempty"` IOS []*uixt.IOSDevice `json:"ios,omitempty" yaml:"ios,omitempty"`
Android []*uixt.AndroidDevice `json:"android,omitempty" yaml:"android,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"` Export []string `json:"export,omitempty" yaml:"export,omitempty"`
Weight int `json:"weight,omitempty" yaml:"weight,omitempty"` Weight int `json:"weight,omitempty" yaml:"weight,omitempty"`
Path string `json:"path,omitempty" yaml:"path,omitempty"` // testcase file path 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 return c
} }
// SetTimeout sets testcase timeout in seconds. // SetRequestTimeout sets request timeout in seconds.
func (c *TConfig) SetTimeout(timeout time.Duration) *TConfig { func (c *TConfig) SetRequestTimeout(seconds float32) *TConfig {
c.Timeout = timeout.Seconds() c.RequestTimeout = seconds
return c
}
// SetCaseTimeout sets testcase timeout in seconds.
func (c *TConfig) SetCaseTimeout(seconds float32) *TConfig {
c.CaseTimeout = seconds
return c return c
} }

View File

@@ -21,6 +21,7 @@ import (
"golang.org/x/net/http2" "golang.org/x/net/http2"
"github.com/httprunner/httprunner/v4/hrp/internal/builtin" "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/sdk"
"github.com/httprunner/httprunner/v4/hrp/internal/version" "github.com/httprunner/httprunner/v4/hrp/internal/version"
"github.com/httprunner/httprunner/v4/hrp/pkg/uixt" "github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
@@ -60,22 +61,24 @@ func NewRunner(t *testing.T) *HRPRunner {
wsDialer: &websocket.Dialer{ wsDialer: &websocket.Dialer{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}, },
caseTimeoutTimer: time.NewTimer(time.Hour * 2), // default case timeout to 2 hour
} }
} }
type HRPRunner struct { type HRPRunner struct {
t *testing.T t *testing.T
failfast bool failfast bool
httpStatOn bool httpStatOn bool
requestsLogOn bool requestsLogOn bool
pluginLogOn bool pluginLogOn bool
venv string venv string
saveTests bool saveTests bool
genHTMLReport bool genHTMLReport bool
httpClient *http.Client httpClient *http.Client
http2Client *http.Client http2Client *http.Client
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
} }
// SetClientTransport configures transport of http client for high concurrency load testing // SetClientTransport configures transport of http client for high concurrency load testing
@@ -152,10 +155,17 @@ func (r *HRPRunner) SetProxyUrl(proxyUrl string) *HRPRunner {
return r return r
} }
// SetTimeout configures global timeout in seconds. // SetRequestTimeout configures global request timeout in seconds.
func (r *HRPRunner) SetTimeout(timeout time.Duration) *HRPRunner { func (r *HRPRunner) SetRequestTimeout(seconds float32) *HRPRunner {
log.Info().Float64("timeout(seconds)", timeout.Seconds()).Msg("[init] SetTimeout") log.Info().Float32("timeout_seconds", seconds).Msg("[init] SetRequestTimeout")
r.httpClient.Timeout = timeout 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 return r
} }
@@ -291,10 +301,13 @@ func (r *HRPRunner) NewCaseRunner(testcase *TestCase) (*CaseRunner, error) {
return nil, errors.Wrap(err, "parse testcase config failed") 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 // set testcase timeout in seconds
if testcase.Config.Timeout != 0 { if testcase.Config.CaseTimeout != 0 {
timeout := time.Duration(testcase.Config.Timeout*1000) * time.Millisecond r.SetCaseTimeout(testcase.Config.CaseTimeout)
r.SetTimeout(timeout)
} }
// load plugin info to testcase config // load plugin info to testcase config
@@ -505,66 +518,71 @@ func (r *SessionRunner) Start(givenVars map[string]interface{}) error {
// run step in sequential order // run step in sequential order
for _, step := range r.caseRunner.testCase.TestSteps { for _, step := range r.caseRunner.testCase.TestSteps {
// TODO: parse step struct select {
// parse step name case <-r.caseRunner.hrpRunner.caseTimeoutTimer.C:
parsedName, err := r.caseRunner.parser.ParseString(step.Name(), r.sessionVariables) return errors.Wrap(code.TimeoutError, "session runner timeout")
if err != nil { default:
parsedName = step.Name() // TODO: parse step struct
} // parse step name
stepName := convertString(parsedName) parsedName, err := r.caseRunner.parser.ParseString(step.Name(), r.sessionVariables)
log.Info().Str("step", stepName). if err != nil {
Str("type", string(step.Type())).Msg("run step start") parsedName = step.Name()
}
stepName := convertString(parsedName)
log.Info().Str("step", stepName).
Str("type", string(step.Type())).Msg("run step start")
// run times of step // run times of step
loopTimes := step.Struct().Loops loopTimes := step.Struct().Loops
if loopTimes < 0 { if loopTimes < 0 {
log.Warn().Int("loops", loopTimes).Msg("loop times should be positive, set to 1") log.Warn().Int("loops", loopTimes).Msg("loop times should be positive, set to 1")
loopTimes = 1 loopTimes = 1
} else if loopTimes == 0 { } else if loopTimes == 0 {
loopTimes = 1 loopTimes = 1
} else if loopTimes > 1 { } else if loopTimes > 1 {
log.Info().Int("loops", loopTimes).Msg("run step with specified loop times") 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 step // run step with specified loop times
stepResult, err = step.Run(r) var stepResult *StepResult
stepResult.Name = stepName + loopIndex 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 r.updateSummary(stepResult)
for k, v := range stepResult.ExportVars { }
r.sessionVariables[k] = v
}
if err == nil { // update extracted variables
log.Info().Str("step", stepResult.Name). 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)). Str("type", string(stepResult.StepType)).
Bool("success", true). Bool("success", false).
Interface("exportVars", stepResult.ExportVars).
Msg("run step end") Msg("run step end")
continue
}
// failed // check if failfast
log.Error().Err(err).Str("step", stepResult.Name). if r.caseRunner.hrpRunner.failfast {
Str("type", string(stepResult.StepType)). return errors.Wrap(err, "abort running due to failfast setting")
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")
} }
} }

View File

@@ -164,7 +164,7 @@ func TestRunCaseWithTimeout(t *testing.T) {
// global timeout // global timeout
testcase1 := &TestCase{ testcase1 := &TestCase{
Config: NewConfig("TestCase1"). Config: NewConfig("TestCase1").
SetTimeout(10 * time.Second). // set global timeout to 10s SetRequestTimeout(10). // set global timeout to 10s
SetBaseURL("https://httpbin.org"), SetBaseURL("https://httpbin.org"),
TestSteps: []IStep{ TestSteps: []IStep{
NewStep("step1"). NewStep("step1").
@@ -180,7 +180,7 @@ func TestRunCaseWithTimeout(t *testing.T) {
testcase2 := &TestCase{ testcase2 := &TestCase{
Config: NewConfig("TestCase2"). Config: NewConfig("TestCase2").
SetTimeout(10 * time.Second). // set global timeout to 10s SetRequestTimeout(10). // set global timeout to 10s
SetBaseURL("https://httpbin.org"), SetBaseURL("https://httpbin.org"),
TestSteps: []IStep{ TestSteps: []IStep{
NewStep("step1"). NewStep("step1").
@@ -197,7 +197,7 @@ func TestRunCaseWithTimeout(t *testing.T) {
// step timeout // step timeout
testcase3 := &TestCase{ testcase3 := &TestCase{
Config: NewConfig("TestCase3"). Config: NewConfig("TestCase3").
SetTimeout(10 * time.Second). SetRequestTimeout(10).
SetBaseURL("https://httpbin.org"), SetBaseURL("https://httpbin.org"),
TestSteps: []IStep{ TestSteps: []IStep{
NewStep("step2"). NewStep("step2").