mirror of
https://github.com/httprunner/httprunner.git
synced 2026-06-27 18:41:33 +08:00
refactor: move boomer to hrp/pkg/boomer
This commit is contained in:
@@ -88,7 +88,6 @@ Usage:
|
||||
|
||||
Available Commands:
|
||||
adb simple utils for android device management
|
||||
boom run load test with boomer
|
||||
build build plugin for testing
|
||||
completion Generate the autocompletion script for the specified shell
|
||||
convert convert multiple source format to HttpRunner JSON/YAML/gotest/pytest cases
|
||||
@@ -96,6 +95,7 @@ Available Commands:
|
||||
ios simple utils for ios device management
|
||||
pytest run API test with pytest
|
||||
run run API test with go engine
|
||||
server start hrp server
|
||||
startproject create a scaffold project
|
||||
wiki visit https://httprunner.com
|
||||
|
||||
|
||||
11
README.md
11
README.md
@@ -10,8 +10,6 @@
|
||||
|
||||
`HttpRunner` 是一个开源的 API 测试工具,支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议,涵盖接口测试、性能测试、数字体验监测等测试类型。简单易用,功能强大,具有丰富的插件化机制和高度的可扩展能力。
|
||||
|
||||
> HttpRunner [用户调研问卷][survey] 持续收集中,我们将基于用户反馈动态调整产品特性和需求优先级。
|
||||
|
||||

|
||||
|
||||
[版本发布日志] | [English]
|
||||
@@ -81,7 +79,6 @@ Usage:
|
||||
|
||||
Available Commands:
|
||||
adb simple utils for android device management
|
||||
boom run load test with boomer
|
||||
build build plugin for testing
|
||||
completion Generate the autocompletion script for the specified shell
|
||||
convert convert multiple source format to HttpRunner JSON/YAML/gotest/pytest cases
|
||||
@@ -89,6 +86,7 @@ Available Commands:
|
||||
ios simple utils for ios device management
|
||||
pytest run API test with pytest
|
||||
run run API test with go engine
|
||||
server start hrp server
|
||||
startproject create a scaffold project
|
||||
wiki visit https://httprunner.com
|
||||
|
||||
@@ -109,13 +107,6 @@ Use "hrp [command] --help" for more information about a command.
|
||||
<a href="https://httprunner.com/docs/cases/umcare"><img src="https://httprunner.com/image/logo/umcare.png" title="通用环球医疗 - 使用 HttpRunner 实践接口自动化测试" width="100"></a>
|
||||
<a href="https://httprunner.com/docs/cases/mihoyo"><img src="https://httprunner.com/image/logo/miHoYo.png" title="米哈游 - 基于 HttpRunner 搭建接口自动化测试体系" width="100"></a>
|
||||
|
||||
|
||||
## 赞助商
|
||||
|
||||
[<img src="https://testing-studio.com/img/icon.png" alt="霍格沃兹测试开发学社" width="500">](https://qrcode.testing-studio.com/f?from=HttpRunner&url=https://testing-studio.com/)
|
||||
|
||||
> 霍格沃兹测试开发学社是中国软件测试开发高端教育品牌,产品由国内顶尖软件测试开发技术专家携手打造,为企业与个人提供专业的技能培训与咨询、测试工具与测试平台、测试外包与测试众包服务。领域涵盖 App/Web 自动化测试、接口自动化测试、性能测试、安全测试、持续交付/DevOps、测试左移、测试右移、精准测试、测试平台开发、测试管理等方向。-> [**联系我们**](http://qrcode.testing-studio.com/f?from=HttpRunner&url=https://ceshiren.com/t/topic/23745)
|
||||
|
||||
## Subscribe
|
||||
|
||||
关注 HttpRunner 的微信公众号,第一时间获得最新资讯。
|
||||
|
||||
@@ -1 +1 @@
|
||||
v5.0.0+2501071515
|
||||
v5.0.0+2502052038
|
||||
|
||||
@@ -180,7 +180,7 @@ func (iter *ParametersIterator) Next() map[string]interface{} {
|
||||
return selectedParameters
|
||||
}
|
||||
|
||||
func (iter *ParametersIterator) outParameters() map[string]interface{} {
|
||||
func (iter *ParametersIterator) Data() map[string]interface{} {
|
||||
res := map[string]interface{}{}
|
||||
for key, params := range iter.data {
|
||||
res[key] = params
|
||||
|
||||
@@ -25,7 +25,7 @@ func newParser() *Parser {
|
||||
}
|
||||
|
||||
type Parser struct {
|
||||
plugin funplugin.IPlugin // plugin is used to call functions
|
||||
Plugin funplugin.IPlugin // plugin is used to call functions
|
||||
}
|
||||
|
||||
func buildURL(baseURL, stepURL string, queryParams url.Values) (fullUrl *url.URL) {
|
||||
@@ -279,13 +279,13 @@ func (p *Parser) ParseString(raw string, variablesMapping map[string]interface{}
|
||||
// only support return at most one result value
|
||||
func (p *Parser) callFunc(funcName string, arguments ...interface{}) (interface{}, error) {
|
||||
// call with plugin function
|
||||
if p.plugin != nil {
|
||||
if p.plugin.Has(funcName) {
|
||||
return p.plugin.Call(funcName, arguments...)
|
||||
if p.Plugin != nil {
|
||||
if p.Plugin.Has(funcName) {
|
||||
return p.Plugin.Call(funcName, arguments...)
|
||||
}
|
||||
commonName := fungo.ConvertCommonName(funcName)
|
||||
if p.plugin.Has(commonName) {
|
||||
return p.plugin.Call(commonName, arguments...)
|
||||
if p.Plugin.Has(commonName) {
|
||||
return p.Plugin.Call(commonName, arguments...)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package hrp
|
||||
package hrpboomer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/rs/zerolog/log"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp"
|
||||
"github.com/httprunner/httprunner/v4/hrp/code"
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/builtin"
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/json"
|
||||
@@ -19,13 +20,15 @@ import (
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/boomer"
|
||||
)
|
||||
|
||||
var pluginMap sync.Map // used for reusing plugin instance
|
||||
|
||||
func NewStandaloneBoomer(spawnCount int64, spawnRate float64) *HRPBoomer {
|
||||
b := &HRPBoomer{
|
||||
Boomer: boomer.NewStandaloneBoomer(spawnCount, spawnRate),
|
||||
pluginsMutex: new(sync.RWMutex),
|
||||
}
|
||||
|
||||
b.hrpRunner = NewRunner(nil)
|
||||
b.hrpRunner = hrp.NewRunner(nil)
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -34,7 +37,7 @@ func NewMasterBoomer(masterBindHost string, masterBindPort int) *HRPBoomer {
|
||||
Boomer: boomer.NewMasterBoomer(masterBindHost, masterBindPort),
|
||||
pluginsMutex: new(sync.RWMutex),
|
||||
}
|
||||
b.hrpRunner = NewRunner(nil)
|
||||
b.hrpRunner = hrp.NewRunner(nil)
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -44,7 +47,7 @@ func NewWorkerBoomer(masterHost string, masterPort int) *HRPBoomer {
|
||||
pluginsMutex: new(sync.RWMutex),
|
||||
}
|
||||
|
||||
b.hrpRunner = NewRunner(nil)
|
||||
b.hrpRunner = hrp.NewRunner(nil)
|
||||
// set client transport for high concurrency load testing
|
||||
b.hrpRunner.SetClientTransport(b.GetSpawnCount(), b.GetDisableKeepAlive(), b.GetDisableCompression())
|
||||
return b
|
||||
@@ -52,7 +55,7 @@ func NewWorkerBoomer(masterHost string, masterPort int) *HRPBoomer {
|
||||
|
||||
type HRPBoomer struct {
|
||||
*boomer.Boomer
|
||||
hrpRunner *HRPRunner
|
||||
hrpRunner *hrp.HRPRunner
|
||||
plugins []funplugin.IPlugin // each task has its own plugin process
|
||||
pluginsMutex *sync.RWMutex // avoid data race
|
||||
}
|
||||
@@ -91,7 +94,7 @@ func (b *HRPBoomer) SetPython3Venv(venv string) *HRPBoomer {
|
||||
}
|
||||
|
||||
// Run starts to run load test for one or multiple testcases.
|
||||
func (b *HRPBoomer) Run(testcases ...ITestCase) {
|
||||
func (b *HRPBoomer) Run(testcases ...hrp.ITestCase) {
|
||||
startTime := time.Now()
|
||||
defer func() {
|
||||
// report boom event
|
||||
@@ -113,25 +116,25 @@ func (b *HRPBoomer) Run(testcases ...ITestCase) {
|
||||
b.Boomer.Run(taskSlice...)
|
||||
}
|
||||
|
||||
func (b *HRPBoomer) ConvertTestCasesToBoomerTasks(testcases ...ITestCase) (taskSlice []*boomer.Task) {
|
||||
func (b *HRPBoomer) ConvertTestCasesToBoomerTasks(testcases ...hrp.ITestCase) (taskSlice []*boomer.Task) {
|
||||
// load all testcases
|
||||
testCases, err := LoadTestCases(testcases...)
|
||||
testCases, err := hrp.LoadTestCases(testcases...)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to load testcases")
|
||||
os.Exit(code.GetErrorCode(err))
|
||||
}
|
||||
|
||||
for _, testcase := range testCases {
|
||||
rendezvousList := initRendezvous(testcase, int64(b.GetSpawnCount()))
|
||||
rendezvousList := hrp.InitRendezvous(testcase, int64(b.GetSpawnCount()))
|
||||
task := b.convertBoomerTask(testcase, rendezvousList)
|
||||
taskSlice = append(taskSlice, task)
|
||||
waitRendezvous(rendezvousList, b)
|
||||
hrp.WaitRendezvous(rendezvousList, b)
|
||||
}
|
||||
return taskSlice
|
||||
}
|
||||
|
||||
func (b *HRPBoomer) ParseTestCases(testCases []*TestCase) []*TestCase {
|
||||
var parsedTestCases []*TestCase
|
||||
func (b *HRPBoomer) ParseTestCases(testCases []*hrp.TestCase) []*hrp.TestCase {
|
||||
var parsedTestCases []*hrp.TestCase
|
||||
for _, tc := range testCases {
|
||||
caseRunner, err := b.hrpRunner.NewCaseRunner(*tc)
|
||||
if err != nil {
|
||||
@@ -139,8 +142,8 @@ func (b *HRPBoomer) ParseTestCases(testCases []*TestCase) []*TestCase {
|
||||
os.Exit(code.GetErrorCode(err))
|
||||
}
|
||||
caseConfig := caseRunner.TestCase.Config.Get()
|
||||
caseConfig.Parameters = caseRunner.parametersIterator.outParameters()
|
||||
parsedTestCases = append(parsedTestCases, &TestCase{
|
||||
caseConfig.Parameters = caseRunner.GetParametersIterator().Data()
|
||||
parsedTestCases = append(parsedTestCases, &hrp.TestCase{
|
||||
Config: caseConfig,
|
||||
TestSteps: caseRunner.TestSteps,
|
||||
})
|
||||
@@ -148,9 +151,9 @@ func (b *HRPBoomer) ParseTestCases(testCases []*TestCase) []*TestCase {
|
||||
return parsedTestCases
|
||||
}
|
||||
|
||||
func (b *HRPBoomer) TestCasesToBytes(testcases ...ITestCase) []byte {
|
||||
func (b *HRPBoomer) TestCasesToBytes(testcases ...hrp.ITestCase) []byte {
|
||||
// load all testcases
|
||||
testCases, err := LoadTestCases(testcases...)
|
||||
testCases, err := hrp.LoadTestCases(testcases...)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to load testcases")
|
||||
os.Exit(code.GetErrorCode(err))
|
||||
@@ -163,8 +166,8 @@ func (b *HRPBoomer) TestCasesToBytes(testcases ...ITestCase) []byte {
|
||||
return testCasesBytes
|
||||
}
|
||||
|
||||
func (b *HRPBoomer) BytesToTCases(testCasesBytes []byte) []*TestCase {
|
||||
var testcase []*TestCase
|
||||
func (b *HRPBoomer) BytesToTCases(testCasesBytes []byte) []*hrp.TestCase {
|
||||
var testcase []*hrp.TestCase
|
||||
err := json.Unmarshal(testCasesBytes, &testcase)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to unmarshal testcases")
|
||||
@@ -176,7 +179,7 @@ func (b *HRPBoomer) Quit() {
|
||||
b.Boomer.Quit()
|
||||
}
|
||||
|
||||
func (b *HRPBoomer) parseTCases(testCases []*TestCase) (testcases []ITestCase) {
|
||||
func (b *HRPBoomer) parseTCases(testCases []*hrp.TestCase) (testcases []hrp.ITestCase) {
|
||||
for _, tc := range testCases {
|
||||
// create temp dir to save testcase
|
||||
tempDir, err := os.MkdirTemp("", "hrp_testcases")
|
||||
@@ -289,9 +292,9 @@ func (b *HRPBoomer) PollTestCases(ctx context.Context) {
|
||||
for {
|
||||
select {
|
||||
case <-b.Boomer.ParseTestCasesChan():
|
||||
var tcs []ITestCase
|
||||
var tcs []hrp.ITestCase
|
||||
for _, tc := range b.GetTestCasesPath() {
|
||||
tcp := TestCasePath(tc)
|
||||
tcp := hrp.TestCasePath(tc)
|
||||
tcs = append(tcs, &tcp)
|
||||
}
|
||||
b.TestCaseBytesChan() <- b.TestCasesToBytes(tcs...)
|
||||
@@ -304,7 +307,7 @@ func (b *HRPBoomer) PollTestCases(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rendezvous) *boomer.Task {
|
||||
func (b *HRPBoomer) convertBoomerTask(testcase *hrp.TestCase, rendezvousList []*hrp.Rendezvous) *boomer.Task {
|
||||
// init case runner for testcase
|
||||
// this runner is shared by multiple session runners
|
||||
caseRunner, err := b.hrpRunner.NewCaseRunner(*testcase)
|
||||
@@ -312,9 +315,10 @@ func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rend
|
||||
log.Error().Err(err).Msg("failed to create runner")
|
||||
os.Exit(code.GetErrorCode(err))
|
||||
}
|
||||
if caseRunner.parser.plugin != nil {
|
||||
plugin := caseRunner.GetParser().Plugin
|
||||
if plugin != nil {
|
||||
b.pluginsMutex.Lock()
|
||||
b.plugins = append(b.plugins, caseRunner.parser.plugin)
|
||||
b.plugins = append(b.plugins, plugin)
|
||||
b.pluginsMutex.Unlock()
|
||||
}
|
||||
|
||||
@@ -322,12 +326,12 @@ func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rend
|
||||
go func() {
|
||||
<-b.GetSpawnDoneChan()
|
||||
for _, rendezvous := range rendezvousList {
|
||||
rendezvous.setSpawnDone()
|
||||
rendezvous.SetSpawnDone()
|
||||
}
|
||||
}()
|
||||
|
||||
// set paramters mode for load testing
|
||||
parametersIterator := caseRunner.parametersIterator
|
||||
parametersIterator := caseRunner.GetParametersIterator()
|
||||
parametersIterator.SetUnlimitedMode()
|
||||
|
||||
// reset start time only once
|
||||
@@ -348,18 +352,18 @@ func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rend
|
||||
|
||||
mutex.Lock()
|
||||
if parametersIterator.HasNext() {
|
||||
sessionRunner.initWithParameters(parametersIterator.Next())
|
||||
sessionRunner.InitWithParameters(parametersIterator.Next())
|
||||
}
|
||||
mutex.Unlock()
|
||||
|
||||
defer func() {
|
||||
sessionRunner.releaseResources()
|
||||
sessionRunner.ReleaseResources()
|
||||
}()
|
||||
|
||||
startTime := time.Now()
|
||||
for _, step := range testcase.TestSteps {
|
||||
// parse step struct
|
||||
err = sessionRunner.parseStepStruct(step)
|
||||
err = sessionRunner.ParseStep(step)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("parse step struct failed")
|
||||
}
|
||||
@@ -372,9 +376,9 @@ func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rend
|
||||
// update step result name with parsed step name
|
||||
stepResult.Name = step.Name()
|
||||
// record requests result of the step if step type is testcase
|
||||
if stepResult.StepType == stepTypeTestCase && stepResult.Data != nil {
|
||||
if stepResult.StepType == hrp.StepTypeTestCase && stepResult.Data != nil {
|
||||
// record requests of testcase step
|
||||
for _, result := range stepResult.Data.([]*StepResult) {
|
||||
for _, result := range stepResult.Data.([]*hrp.StepResult) {
|
||||
if result.Success {
|
||||
b.RecordSuccess(string(result.StepType), result.Name, result.Elapsed, result.ContentSize)
|
||||
} else {
|
||||
@@ -396,26 +400,22 @@ func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rend
|
||||
testcaseSuccess = false
|
||||
transactionSuccess = false
|
||||
|
||||
if b.hrpRunner.failfast {
|
||||
log.Error().Err(err).Msg("abort running due to failfast setting")
|
||||
break
|
||||
}
|
||||
log.Warn().Err(err).Msg("run step failed, continue next step")
|
||||
continue
|
||||
}
|
||||
|
||||
// record step success
|
||||
if stepResult.StepType == stepTypeTransaction {
|
||||
if stepResult.StepType == hrp.StepTypeTransaction {
|
||||
// transaction
|
||||
// FIXME: support nested transactions
|
||||
stepTransaction := step.(*StepTransaction)
|
||||
if stepTransaction.Transaction.Type == transactionEnd { // only record when transaction ends
|
||||
stepTransaction := step.(*hrp.StepTransaction)
|
||||
if stepTransaction.Transaction.Type == hrp.TransactionEnd { // only record when transaction ends
|
||||
b.RecordTransaction(stepTransaction.Name(), transactionSuccess, stepResult.Elapsed, 0)
|
||||
transactionSuccess = true // reset flag for next transaction
|
||||
}
|
||||
} else if stepResult.StepType == stepTypeRendezvous {
|
||||
} else if stepResult.StepType == hrp.StepTypeRendezvous {
|
||||
// rendezvous
|
||||
} else if stepResult.StepType == stepTypeThinkTime {
|
||||
} else if stepResult.StepType == hrp.StepTypeThinkTime {
|
||||
// think time
|
||||
// no record required
|
||||
} else {
|
||||
@@ -423,17 +423,17 @@ func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rend
|
||||
b.RecordSuccess(string(step.Type()), stepResult.Name, stepResult.Elapsed, stepResult.ContentSize)
|
||||
// update extracted variables
|
||||
for k, v := range stepResult.ExportVars {
|
||||
sessionRunner.sessionVariables[k] = v
|
||||
sessionRunner.GetSessionVariables()[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
endTime := time.Now()
|
||||
|
||||
// report duration for transaction without end
|
||||
for name, transaction := range sessionRunner.transactions {
|
||||
for name, transaction := range sessionRunner.GetTransactions() {
|
||||
if len(transaction) == 1 {
|
||||
// if transaction end time not exists, use testcase end time instead
|
||||
duration := endTime.Sub(transaction[transactionStart])
|
||||
duration := endTime.Sub(transaction[hrp.TransactionStart])
|
||||
b.RecordTransaction(name, transactionSuccess, duration.Milliseconds(), 0)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package hrp
|
||||
package hrpboomer
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -1,34 +1,32 @@
|
||||
package hrp
|
||||
package hrpboomer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp"
|
||||
)
|
||||
|
||||
func TestBoomerStandaloneRun(t *testing.T) {
|
||||
buildHashicorpGoPlugin()
|
||||
defer removeHashicorpGoPlugin()
|
||||
|
||||
testcase1 := &TestCase{
|
||||
Config: NewConfig("TestCase1").SetBaseURL("https://postman-echo.com"),
|
||||
TestSteps: []IStep{
|
||||
NewStep("headers").
|
||||
testcase1 := &hrp.TestCase{
|
||||
Config: hrp.NewConfig("TestCase1").SetBaseURL("https://postman-echo.com"),
|
||||
TestSteps: []hrp.IStep{
|
||||
hrp.NewStep("headers").
|
||||
GET("/headers").
|
||||
Validate().
|
||||
AssertEqual("status_code", 200, "check status code").
|
||||
AssertEqual("headers.\"Content-Type\"", "application/json", "check http response Content-Type"),
|
||||
NewStep("user-agent").
|
||||
hrp.NewStep("user-agent").
|
||||
GET("/user-agent").
|
||||
Validate().
|
||||
AssertEqual("status_code", 200, "check status code").
|
||||
AssertEqual("headers.\"Content-Type\"", "application/json", "check http response Content-Type"),
|
||||
NewStep("TestCase3").CallRefCase(&TestCase{Config: NewConfig("TestCase3")}),
|
||||
hrp.NewStep("TestCase3").CallRefCase(&hrp.TestCase{Config: hrp.NewConfig("TestCase3")}),
|
||||
},
|
||||
}
|
||||
testcase2 := TestCasePath(demoTestCaseWithPluginJSONPath)
|
||||
|
||||
b := NewStandaloneBoomer(2, 1)
|
||||
go b.Run(testcase1, &testcase2)
|
||||
go b.Run(testcase1)
|
||||
time.Sleep(5 * time.Second)
|
||||
b.Quit()
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package cmd
|
||||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/httprunner/httprunner/v4/hrp"
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/sdk"
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/boomer"
|
||||
hrpboomer "github.com/httprunner/httprunner/v4/hrp/pkg/boomer/hrp"
|
||||
)
|
||||
|
||||
// boomCmd represents the boom command
|
||||
@@ -25,11 +26,9 @@ var boomCmd = &cobra.Command{
|
||||
Args: cobra.MinimumNArgs(0),
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
boomer.SetUlimit(10240) // ulimit -n 10240
|
||||
if !strings.EqualFold(logLevel, "DEBUG") {
|
||||
// disable info logs for load testing
|
||||
log.Info().Msg("Set global log level to WARN for load testing")
|
||||
zerolog.SetGlobalLevel(zerolog.WarnLevel)
|
||||
}
|
||||
// disable info logs for load testing
|
||||
log.Info().Msg("Set global log level to WARN for load testing")
|
||||
zerolog.SetGlobalLevel(zerolog.WarnLevel)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
startTime := time.Now()
|
||||
@@ -57,13 +56,13 @@ var boomCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
// init boomer
|
||||
var hrpBoomer *hrp.HRPBoomer
|
||||
var hrpBoomer *hrpboomer.HRPBoomer
|
||||
if boomArgs.master {
|
||||
hrpBoomer = hrp.NewMasterBoomer(boomArgs.masterBindHost, boomArgs.masterBindPort)
|
||||
hrpBoomer = hrpboomer.NewMasterBoomer(boomArgs.masterBindHost, boomArgs.masterBindPort)
|
||||
} else if boomArgs.worker {
|
||||
hrpBoomer = hrp.NewWorkerBoomer(boomArgs.masterHost, boomArgs.masterPort)
|
||||
hrpBoomer = hrpboomer.NewWorkerBoomer(boomArgs.masterHost, boomArgs.masterPort)
|
||||
} else {
|
||||
hrpBoomer = hrp.NewStandaloneBoomer(boomArgs.SpawnCount, boomArgs.SpawnRate)
|
||||
hrpBoomer = hrpboomer.NewStandaloneBoomer(boomArgs.SpawnCount, boomArgs.SpawnRate)
|
||||
}
|
||||
hrpBoomer.SetProfile(&boomArgs.Profile)
|
||||
ctx := hrpBoomer.EnableGracefulQuit(context.Background())
|
||||
@@ -93,9 +92,6 @@ var boomCmd = &cobra.Command{
|
||||
go hrpBoomer.PollTasks(ctx)
|
||||
hrpBoomer.RunWorker()
|
||||
case "standalone":
|
||||
if venv != "" {
|
||||
hrpBoomer.SetPython3Venv(venv)
|
||||
}
|
||||
hrpBoomer.InitBoomer()
|
||||
hrpBoomer.Run(paths...)
|
||||
}
|
||||
@@ -122,8 +118,6 @@ type BoomArgs struct {
|
||||
var boomArgs BoomArgs
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(boomCmd)
|
||||
|
||||
boomCmd.Flags().Int64Var(&boomArgs.MaxRPS, "max-rps", 0, "Max RPS that boomer can generate, disabled by default.")
|
||||
boomCmd.Flags().StringVar(&boomArgs.RequestIncreaseRate, "request-increase-rate", "-1", "Request increase rate, disabled by default.")
|
||||
boomCmd.Flags().Int64Var(&boomArgs.SpawnCount, "spawn-count", 1, "The number of users to spawn for load testing")
|
||||
@@ -151,22 +145,3 @@ func init() {
|
||||
boomCmd.Flags().IntVar(&boomArgs.expectWorkers, "expect-workers", 1, "How many workers master should expect to connect before starting the test (only when --autostart is used)")
|
||||
boomCmd.Flags().IntVar(&boomArgs.expectWorkersMaxWait, "expect-workers-max-wait", 120, "How many workers master should expect to connect before starting the test (only when --autostart is used")
|
||||
}
|
||||
|
||||
func makeHRPBoomer() (*hrp.HRPBoomer, error) {
|
||||
// if set profile, the priority is higher than the other commands
|
||||
if boomArgs.profile != "" {
|
||||
err := hrp.LoadFileObject(boomArgs.profile, &boomArgs)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to load profile")
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
hrpBoomer := hrp.NewStandaloneBoomer(boomArgs.SpawnCount, boomArgs.SpawnRate)
|
||||
if venv != "" {
|
||||
hrpBoomer.SetPython3Venv(venv)
|
||||
}
|
||||
hrpBoomer.SetProfile(&boomArgs.Profile)
|
||||
hrpBoomer.EnableGracefulQuit(context.Background())
|
||||
hrpBoomer.InitBoomer()
|
||||
return hrpBoomer, nil
|
||||
}
|
||||
22
hrp/pkg/boomer/hrp/cli/main.go
Normal file
22
hrp/pkg/boomer/hrp/cli/main.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
// report panic to sentry
|
||||
sentry.CurrentHub().Recover(err)
|
||||
sentry.Flush(time.Second * 5)
|
||||
|
||||
// print panic trace
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
boomCmd.Execute()
|
||||
}
|
||||
@@ -291,7 +291,7 @@ func (r *HRPRunner) NewCaseRunner(testcase TestCase) (*CaseRunner, error) {
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "init plugin failed")
|
||||
}
|
||||
caseRunner.parser.plugin = plugin
|
||||
caseRunner.parser.Plugin = plugin
|
||||
|
||||
// load plugin info to testcase config
|
||||
pluginPath := plugin.Path()
|
||||
@@ -339,6 +339,14 @@ type CaseRunner struct {
|
||||
uixtDrivers map[string]*uixt.DriverExt
|
||||
}
|
||||
|
||||
func (r *CaseRunner) GetParametersIterator() *ParametersIterator {
|
||||
return r.parametersIterator
|
||||
}
|
||||
|
||||
func (r *CaseRunner) GetParser() *Parser {
|
||||
return r.parser
|
||||
}
|
||||
|
||||
// parseConfig parses testcase config, stores to parsedConfig.
|
||||
func (r *CaseRunner) parseConfig() (parsedConfig *TConfig, err error) {
|
||||
cfg := r.TestCase.Config.Get()
|
||||
@@ -541,7 +549,7 @@ func (r *CaseRunner) NewSession() *SessionRunner {
|
||||
sessionVariables: make(map[string]interface{}),
|
||||
summary: NewCaseSummary(),
|
||||
|
||||
transactions: make(map[string]map[transactionType]time.Time),
|
||||
transactions: make(map[string]map[TransactionType]time.Time),
|
||||
ws: newWSSession(),
|
||||
}
|
||||
return sessionRunner
|
||||
@@ -557,7 +565,7 @@ type SessionRunner struct {
|
||||
|
||||
// transactions stores transaction timing info.
|
||||
// key is transaction name, value is map of transaction type and time, e.g. start time and end time.
|
||||
transactions map[string]map[transactionType]time.Time
|
||||
transactions map[string]map[TransactionType]time.Time
|
||||
|
||||
// websocket session
|
||||
ws *wsSession
|
||||
@@ -573,11 +581,11 @@ func (r *SessionRunner) Start(givenVars map[string]interface{}) (summary *TestCa
|
||||
log.Info().Str("testcase", config.Name).Msg("run testcase start")
|
||||
|
||||
// update config variables with given variables
|
||||
r.initWithParameters(givenVars)
|
||||
r.InitWithParameters(givenVars)
|
||||
|
||||
defer func() {
|
||||
// release session resources
|
||||
r.releaseResources()
|
||||
r.ReleaseResources()
|
||||
|
||||
summary = r.summary
|
||||
summary.Name = config.Name
|
||||
@@ -645,7 +653,7 @@ func (r *SessionRunner) Start(givenVars map[string]interface{}) (summary *TestCa
|
||||
|
||||
func (r *SessionRunner) RunStep(step IStep) (stepResult *StepResult, err error) {
|
||||
// parse step struct
|
||||
if err = r.parseStepStruct(step); err != nil {
|
||||
if err = r.ParseStep(step); err != nil {
|
||||
log.Error().Err(err).Msg("parse step struct failed")
|
||||
if r.caseRunner.hrpRunner.failfast {
|
||||
return nil, errors.Wrap(err, "parse step struct failed")
|
||||
@@ -714,7 +722,7 @@ func (r *SessionRunner) GetSummary() *TestCaseSummary {
|
||||
return r.summary
|
||||
}
|
||||
|
||||
func (r *SessionRunner) parseStepStruct(step IStep) error {
|
||||
func (r *SessionRunner) ParseStep(step IStep) error {
|
||||
caseConfig := r.caseRunner.TestCase.Config.Get()
|
||||
stepConfig := step.Config()
|
||||
|
||||
@@ -770,9 +778,9 @@ func (r *SessionRunner) parseStepStruct(step IStep) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// initWithParameters updates session variables with given parameters.
|
||||
// InitWithParameters updates session variables with given parameters.
|
||||
// this is used for data driven
|
||||
func (r *SessionRunner) initWithParameters(parameters map[string]interface{}) {
|
||||
func (r *SessionRunner) InitWithParameters(parameters map[string]interface{}) {
|
||||
if len(parameters) == 0 {
|
||||
return
|
||||
}
|
||||
@@ -782,3 +790,11 @@ func (r *SessionRunner) initWithParameters(parameters map[string]interface{}) {
|
||||
r.sessionVariables[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SessionRunner) GetSessionVariables() map[string]interface{} {
|
||||
return r.sessionVariables
|
||||
}
|
||||
|
||||
func (r *SessionRunner) GetTransactions() map[string]map[TransactionType]time.Time {
|
||||
return r.transactions
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ func TestSessionRunner(t *testing.T) {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
err := sessionRunner.parseStepStruct(step)
|
||||
err := sessionRunner.ParseStep(step)
|
||||
if err != nil {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
24
hrp/step.go
24
hrp/step.go
@@ -5,18 +5,18 @@ import "github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
|
||||
type StepType string
|
||||
|
||||
const (
|
||||
stepTypeRequest StepType = "request"
|
||||
stepTypeAPI StepType = "api"
|
||||
stepTypeTestCase StepType = "testcase"
|
||||
stepTypeTransaction StepType = "transaction"
|
||||
stepTypeRendezvous StepType = "rendezvous"
|
||||
stepTypeThinkTime StepType = "thinktime"
|
||||
stepTypeWebSocket StepType = "websocket"
|
||||
stepTypeAndroid StepType = "android"
|
||||
stepTypeHarmony StepType = "harmony"
|
||||
stepTypeIOS StepType = "ios"
|
||||
stepTypeShell StepType = "shell"
|
||||
stepTypeFunction StepType = "function"
|
||||
StepTypeRequest StepType = "request"
|
||||
StepTypeAPI StepType = "api"
|
||||
StepTypeTestCase StepType = "testcase"
|
||||
StepTypeTransaction StepType = "transaction"
|
||||
StepTypeRendezvous StepType = "rendezvous"
|
||||
StepTypeThinkTime StepType = "thinktime"
|
||||
StepTypeWebSocket StepType = "websocket"
|
||||
StepTypeAndroid StepType = "android"
|
||||
StepTypeHarmony StepType = "harmony"
|
||||
StepTypeIOS StepType = "ios"
|
||||
StepTypeShell StepType = "shell"
|
||||
StepTypeFunction StepType = "function"
|
||||
|
||||
stepTypeSuffixExtraction StepType = "_extraction"
|
||||
stepTypeSuffixValidation StepType = "_validation"
|
||||
|
||||
@@ -90,7 +90,7 @@ func (s *StepAPIWithOptionalArgs) Name() string {
|
||||
}
|
||||
|
||||
func (s *StepAPIWithOptionalArgs) Type() StepType {
|
||||
return stepTypeAPI
|
||||
return StepTypeAPI
|
||||
}
|
||||
|
||||
func (s *StepAPIWithOptionalArgs) Config() *StepConfig {
|
||||
@@ -99,7 +99,7 @@ func (s *StepAPIWithOptionalArgs) Config() *StepConfig {
|
||||
|
||||
func (s *StepAPIWithOptionalArgs) Run(r *SessionRunner) (stepResult *StepResult, err error) {
|
||||
defer func() {
|
||||
stepResult.StepType = stepTypeAPI
|
||||
stepResult.StepType = StepTypeAPI
|
||||
}()
|
||||
// extend request with referenced API
|
||||
api, _ := s.API.(*API)
|
||||
|
||||
@@ -21,7 +21,7 @@ func (s *StepFunction) Name() string {
|
||||
}
|
||||
|
||||
func (s *StepFunction) Type() StepType {
|
||||
return stepTypeFunction
|
||||
return StepTypeFunction
|
||||
}
|
||||
|
||||
func (s *StepFunction) Config() *StepConfig {
|
||||
@@ -46,13 +46,13 @@ func runStepFunction(r *SessionRunner, step IStep) (stepResult *StepResult, err
|
||||
|
||||
log.Info().
|
||||
Str("name", step.Name()).
|
||||
Str("type", string(stepTypeFunction)).
|
||||
Str("type", string(StepTypeFunction)).
|
||||
Msg("run function")
|
||||
|
||||
start := time.Now()
|
||||
stepResult = &StepResult{
|
||||
Name: step.Name(),
|
||||
StepType: stepTypeFunction,
|
||||
StepType: StepTypeFunction,
|
||||
Success: false,
|
||||
ContentSize: 0,
|
||||
StartTime: start.Unix(),
|
||||
|
||||
@@ -39,15 +39,15 @@ func (s *StepMobile) obj() *MobileUI {
|
||||
|
||||
if s.IOS != nil {
|
||||
s.cache = s.IOS
|
||||
s.cache.OSType = string(stepTypeIOS)
|
||||
s.cache.OSType = string(StepTypeIOS)
|
||||
return s.cache
|
||||
} else if s.Harmony != nil {
|
||||
s.cache = s.Harmony
|
||||
s.cache.OSType = string(stepTypeHarmony)
|
||||
s.cache.OSType = string(StepTypeHarmony)
|
||||
return s.cache
|
||||
} else if s.Android != nil {
|
||||
s.cache = s.Android
|
||||
s.cache.OSType = string(stepTypeAndroid)
|
||||
s.cache.OSType = string(StepTypeAndroid)
|
||||
return s.cache
|
||||
} else if s.Mobile != nil {
|
||||
s.cache = s.Mobile
|
||||
|
||||
@@ -22,7 +22,7 @@ func (s *StepRendezvous) Name() string {
|
||||
}
|
||||
|
||||
func (s *StepRendezvous) Type() StepType {
|
||||
return stepTypeRendezvous
|
||||
return StepTypeRendezvous
|
||||
}
|
||||
|
||||
func (s *StepRendezvous) Config() *StepConfig {
|
||||
@@ -43,7 +43,7 @@ func (s *StepRendezvous) Run(r *SessionRunner) (*StepResult, error) {
|
||||
|
||||
stepResult := &StepResult{
|
||||
Name: rendezvous.Name,
|
||||
StepType: stepTypeRendezvous,
|
||||
StepType: StepTypeRendezvous,
|
||||
Success: true,
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ func (s *StepRendezvous) Run(r *SessionRunner) (*StepResult, error) {
|
||||
|
||||
func isPreRendezvousAllReleased(rendezvous *Rendezvous, testCase *TestCase) bool {
|
||||
for _, step := range testCase.TestSteps {
|
||||
if step.Type() != stepTypeRendezvous {
|
||||
if step.Type() != StepTypeRendezvous {
|
||||
continue
|
||||
}
|
||||
preRendezvous := step.(*StepRendezvous).Rendezvous
|
||||
@@ -149,7 +149,7 @@ func (r *Rendezvous) isSpawnDone() bool {
|
||||
return atomic.LoadUint32(&r.spawnDoneFlag) == 1
|
||||
}
|
||||
|
||||
func (r *Rendezvous) setSpawnDone() {
|
||||
func (r *Rendezvous) SetSpawnDone() {
|
||||
atomic.StoreUint32(&r.spawnDoneFlag, 1)
|
||||
}
|
||||
|
||||
@@ -161,7 +161,11 @@ func (r *Rendezvous) setReleased() {
|
||||
atomic.StoreUint32(&r.releasedFlag, 1)
|
||||
}
|
||||
|
||||
func initRendezvous(testcase *TestCase, total int64) []*Rendezvous {
|
||||
func (r *Rendezvous) updateRendezvousNumber(number int64) {
|
||||
atomic.StoreInt64(&r.Number, int64(float32(number)*r.Percent))
|
||||
}
|
||||
|
||||
func InitRendezvous(testcase *TestCase, total int64) []*Rendezvous {
|
||||
var rendezvousList []*Rendezvous
|
||||
for _, s := range testcase.TestSteps {
|
||||
step := s.(*StepRendezvous)
|
||||
@@ -195,11 +199,7 @@ func initRendezvous(testcase *TestCase, total int64) []*Rendezvous {
|
||||
return rendezvousList
|
||||
}
|
||||
|
||||
func (r *Rendezvous) updateRendezvousNumber(number int64) {
|
||||
atomic.StoreInt64(&r.Number, int64(float32(number)*r.Percent))
|
||||
}
|
||||
|
||||
func waitRendezvous(rendezvousList []*Rendezvous, b *HRPBoomer) {
|
||||
func WaitRendezvous(rendezvousList []*Rendezvous, b IBoomer) {
|
||||
if rendezvousList != nil {
|
||||
lastRendezvous := rendezvousList[len(rendezvousList)-1]
|
||||
for _, rendezvous := range rendezvousList {
|
||||
@@ -208,7 +208,7 @@ func waitRendezvous(rendezvousList []*Rendezvous, b *HRPBoomer) {
|
||||
}
|
||||
}
|
||||
|
||||
func waitSingleRendezvous(rendezvous *Rendezvous, rendezvousList []*Rendezvous, lastRendezvous *Rendezvous, b *HRPBoomer) {
|
||||
func waitSingleRendezvous(rendezvous *Rendezvous, rendezvousList []*Rendezvous, lastRendezvous *Rendezvous, b IBoomer) {
|
||||
for {
|
||||
// cycle start: block current checking until current rendezvous activated
|
||||
<-rendezvous.activateChan
|
||||
@@ -260,3 +260,7 @@ func waitSingleRendezvous(rendezvous *Rendezvous, rendezvousList []*Rendezvous,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type IBoomer interface {
|
||||
GetSpawnCount() int
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ func TestRunCaseWithRendezvous(t *testing.T) {
|
||||
{number: 100, percent: 1, timeout: 5000},
|
||||
}
|
||||
|
||||
rendezvousList := initRendezvous(rendezvousBoundaryTestcase, 100)
|
||||
rendezvousList := InitRendezvous(rendezvousBoundaryTestcase, 100)
|
||||
|
||||
for i, r := range rendezvousList {
|
||||
if r.Number != expectedRendezvousParams[i].number {
|
||||
|
||||
@@ -282,7 +282,7 @@ func runStepRequest(r *SessionRunner, step IStep) (stepResult *StepResult, err e
|
||||
start := time.Now()
|
||||
stepResult = &StepResult{
|
||||
Name: stepRequest.StepName,
|
||||
StepType: stepTypeRequest,
|
||||
StepType: StepTypeRequest,
|
||||
Success: false,
|
||||
ContentSize: 0,
|
||||
StartTime: start.Unix(),
|
||||
@@ -707,7 +707,7 @@ func (s *StepRequest) StartTransaction(name string) *StepTransaction {
|
||||
StepConfig: s.StepConfig,
|
||||
Transaction: &Transaction{
|
||||
Name: name,
|
||||
Type: transactionStart,
|
||||
Type: TransactionStart,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -718,7 +718,7 @@ func (s *StepRequest) EndTransaction(name string) *StepTransaction {
|
||||
StepConfig: s.StepConfig,
|
||||
Transaction: &Transaction{
|
||||
Name: name,
|
||||
Type: transactionEnd,
|
||||
Type: TransactionEnd,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ func (s *StepShell) Name() string {
|
||||
}
|
||||
|
||||
func (s *StepShell) Type() StepType {
|
||||
return stepTypeShell
|
||||
return StepTypeShell
|
||||
}
|
||||
|
||||
func (s *StepShell) Config() *StepConfig {
|
||||
@@ -59,7 +59,7 @@ func (s *StepShellValidation) Name() string {
|
||||
}
|
||||
|
||||
func (s *StepShellValidation) Type() StepType {
|
||||
return stepTypeShell + stepTypeSuffixValidation
|
||||
return StepTypeShell + stepTypeSuffixValidation
|
||||
}
|
||||
|
||||
func (s *StepShellValidation) Config() *StepConfig {
|
||||
@@ -91,14 +91,14 @@ func runStepShell(r *SessionRunner, step IStep) (stepResult *StepResult, err err
|
||||
|
||||
log.Info().
|
||||
Str("name", step.Name()).
|
||||
Str("type", string(stepTypeShell)).
|
||||
Str("type", string(StepTypeShell)).
|
||||
Str("content", shell.String).
|
||||
Msg("run shell string")
|
||||
|
||||
start := time.Now()
|
||||
stepResult = &StepResult{
|
||||
Name: step.Name(),
|
||||
StepType: stepTypeShell,
|
||||
StepType: StepTypeShell,
|
||||
Success: false,
|
||||
ContentSize: 0,
|
||||
StartTime: start.Unix(),
|
||||
|
||||
@@ -38,7 +38,7 @@ func (s *StepTestCaseWithOptionalArgs) Name() string {
|
||||
}
|
||||
|
||||
func (s *StepTestCaseWithOptionalArgs) Type() StepType {
|
||||
return stepTypeTestCase
|
||||
return StepTypeTestCase
|
||||
}
|
||||
|
||||
func (s *StepTestCaseWithOptionalArgs) Config() *StepConfig {
|
||||
@@ -49,7 +49,7 @@ func (s *StepTestCaseWithOptionalArgs) Run(r *SessionRunner) (stepResult *StepRe
|
||||
start := time.Now()
|
||||
stepResult = &StepResult{
|
||||
Name: s.StepName,
|
||||
StepType: stepTypeTestCase,
|
||||
StepType: StepTypeTestCase,
|
||||
Success: false,
|
||||
StartTime: start.Unix(),
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ func (s *StepThinkTime) Name() string {
|
||||
}
|
||||
|
||||
func (s *StepThinkTime) Type() StepType {
|
||||
return stepTypeThinkTime
|
||||
return StepTypeThinkTime
|
||||
}
|
||||
|
||||
func (s *StepThinkTime) Config() *StepConfig {
|
||||
@@ -36,7 +36,7 @@ func (s *StepThinkTime) Run(r *SessionRunner) (*StepResult, error) {
|
||||
|
||||
stepResult := &StepResult{
|
||||
Name: s.StepName,
|
||||
StepType: stepTypeThinkTime,
|
||||
StepType: StepTypeThinkTime,
|
||||
Success: true,
|
||||
}
|
||||
|
||||
|
||||
@@ -9,14 +9,14 @@ import (
|
||||
|
||||
type Transaction struct {
|
||||
Name string `json:"name" yaml:"name"`
|
||||
Type transactionType `json:"type" yaml:"type"`
|
||||
Type TransactionType `json:"type" yaml:"type"`
|
||||
}
|
||||
|
||||
type transactionType string
|
||||
type TransactionType string
|
||||
|
||||
const (
|
||||
transactionStart transactionType = "start"
|
||||
transactionEnd transactionType = "end"
|
||||
TransactionStart TransactionType = "start"
|
||||
TransactionEnd TransactionType = "end"
|
||||
)
|
||||
|
||||
// StepTransaction implements IStep interface.
|
||||
@@ -33,7 +33,7 @@ func (s *StepTransaction) Name() string {
|
||||
}
|
||||
|
||||
func (s *StepTransaction) Type() StepType {
|
||||
return stepTypeTransaction
|
||||
return StepTypeTransaction
|
||||
}
|
||||
|
||||
func (s *StepTransaction) Config() *StepConfig {
|
||||
@@ -49,7 +49,7 @@ func (s *StepTransaction) Run(r *SessionRunner) (*StepResult, error) {
|
||||
|
||||
stepResult := &StepResult{
|
||||
Name: transaction.Name,
|
||||
StepType: stepTypeTransaction,
|
||||
StepType: StepTypeTransaction,
|
||||
Success: true,
|
||||
Elapsed: 0,
|
||||
ContentSize: 0, // TODO: record transaction total response length
|
||||
@@ -57,25 +57,25 @@ func (s *StepTransaction) Run(r *SessionRunner) (*StepResult, error) {
|
||||
|
||||
// create transaction if not exists
|
||||
if _, ok := r.transactions[transaction.Name]; !ok {
|
||||
r.transactions[transaction.Name] = make(map[transactionType]time.Time)
|
||||
r.transactions[transaction.Name] = make(map[TransactionType]time.Time)
|
||||
}
|
||||
|
||||
// record transaction start time, override if already exists
|
||||
if transaction.Type == transactionStart {
|
||||
r.transactions[transaction.Name][transactionStart] = time.Now()
|
||||
if transaction.Type == TransactionStart {
|
||||
r.transactions[transaction.Name][TransactionStart] = time.Now()
|
||||
}
|
||||
// record transaction end time, override if already exists
|
||||
if transaction.Type == transactionEnd {
|
||||
r.transactions[transaction.Name][transactionEnd] = time.Now()
|
||||
if transaction.Type == TransactionEnd {
|
||||
r.transactions[transaction.Name][TransactionEnd] = time.Now()
|
||||
|
||||
// if transaction start time not exists, use testcase start time instead
|
||||
if _, ok := r.transactions[transaction.Name][transactionStart]; !ok {
|
||||
r.transactions[transaction.Name][transactionStart] = r.summary.Time.StartAt
|
||||
if _, ok := r.transactions[transaction.Name][TransactionStart]; !ok {
|
||||
r.transactions[transaction.Name][TransactionStart] = r.summary.Time.StartAt
|
||||
}
|
||||
|
||||
// calculate transaction duration
|
||||
duration := r.transactions[transaction.Name][transactionEnd].Sub(
|
||||
r.transactions[transaction.Name][transactionStart])
|
||||
duration := r.transactions[transaction.Name][TransactionEnd].Sub(
|
||||
r.transactions[transaction.Name][TransactionStart])
|
||||
stepResult.Elapsed = duration.Milliseconds()
|
||||
log.Info().Str("name", transaction.Name).Dur("elapsed", duration).Msg("transaction")
|
||||
}
|
||||
|
||||
@@ -276,7 +276,7 @@ func runStepWebSocket(r *SessionRunner, step IStep) (stepResult *StepResult, err
|
||||
start := time.Now()
|
||||
stepResult = &StepResult{
|
||||
Name: step.Name(),
|
||||
StepType: stepTypeWebSocket,
|
||||
StepType: StepTypeWebSocket,
|
||||
Success: false,
|
||||
ContentSize: 0,
|
||||
StartTime: start.Unix(),
|
||||
@@ -703,7 +703,7 @@ func getContentSize(resp interface{}) int64 {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SessionRunner) releaseResources() {
|
||||
func (r *SessionRunner) ReleaseResources() {
|
||||
// close websocket connections
|
||||
for _, wsConn := range r.ws.wsConnMap {
|
||||
if wsConn != nil {
|
||||
|
||||
@@ -193,7 +193,7 @@ type TestCaseSummary struct {
|
||||
// AddStepResult updates summary of StepResult.
|
||||
func (s *TestCaseSummary) AddStepResult(stepResult *StepResult) {
|
||||
switch stepResult.StepType {
|
||||
case stepTypeTestCase:
|
||||
case StepTypeTestCase:
|
||||
// record requests of testcase step
|
||||
records, ok := stepResult.Data.([]*StepResult)
|
||||
if !ok {
|
||||
|
||||
@@ -17,7 +17,7 @@ func TestGenHTMLReport(t *testing.T) {
|
||||
caseSummary2 := NewCaseSummary()
|
||||
stepResult2 := &StepResult{
|
||||
Name: "Test",
|
||||
StepType: stepTypeRequest,
|
||||
StepType: StepTypeRequest,
|
||||
Success: false,
|
||||
ContentSize: 0,
|
||||
Attachments: "err",
|
||||
@@ -40,7 +40,7 @@ func TestTestCaseSummary_AddStepResult(t *testing.T) {
|
||||
caseSummary := NewCaseSummary()
|
||||
stepResult1 := &StepResult{
|
||||
Name: "Test1",
|
||||
StepType: stepTypeRequest,
|
||||
StepType: StepTypeRequest,
|
||||
Success: true,
|
||||
ContentSize: 0,
|
||||
Attachments: "err",
|
||||
@@ -48,7 +48,7 @@ func TestTestCaseSummary_AddStepResult(t *testing.T) {
|
||||
caseSummary.AddStepResult(stepResult1)
|
||||
stepResult2 := &StepResult{
|
||||
Name: "Test2",
|
||||
StepType: stepTypeTestCase,
|
||||
StepType: StepTypeTestCase,
|
||||
Success: false,
|
||||
ContentSize: 0,
|
||||
Attachments: "err",
|
||||
|
||||
Reference in New Issue
Block a user