mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-13 17:29:56 +08:00
Merge pull request #1243 from httprunner/fix-data-race-by-parse-config
fix: data racing by parsing config
This commit is contained in:
@@ -5,7 +5,6 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/httprunner/funplugin"
|
||||
@@ -98,27 +97,21 @@ func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rend
|
||||
Name: config.Name,
|
||||
Weight: config.Weight,
|
||||
Fn: func() {
|
||||
sessionTestCase := &TestCase{}
|
||||
// copy testcase to avoid data racing
|
||||
if err := copier.Copy(sessionTestCase, testcase); err != nil {
|
||||
log.Error().Err(err).Msg("copy testcase data failed")
|
||||
return
|
||||
}
|
||||
sessionRunner := hrpRunner.NewSessionRunner(sessionTestCase)
|
||||
sessionRunner := hrpRunner.NewSessionRunner(testcase)
|
||||
sessionRunner.parser.plugin = plugin
|
||||
|
||||
testcaseSuccess := true // flag whole testcase result
|
||||
var transactionSuccess = true // flag current transaction result
|
||||
|
||||
cfg := sessionTestCase.Config
|
||||
var parameterVariables map[string]interface{}
|
||||
// iterate through all parameter iterators and update case variables
|
||||
for _, it := range cfg.ParametersSetting.Iterators {
|
||||
for _, it := range testcase.Config.ParametersSetting.Iterators {
|
||||
if it.HasNext() {
|
||||
cfg.Variables = mergeVariables(it.Next(), cfg.Variables)
|
||||
parameterVariables = it.Next()
|
||||
}
|
||||
}
|
||||
|
||||
if err := sessionRunner.parseConfig(cfg); err != nil {
|
||||
if err := sessionRunner.parseConfig(parameterVariables); err != nil {
|
||||
log.Error().Err(err).Msg("parse config failed")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
_ "embed"
|
||||
"time"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
@@ -14,6 +15,7 @@ type SessionRunner struct {
|
||||
testCase *TestCase
|
||||
hrpRunner *HRPRunner
|
||||
parser *Parser
|
||||
parsedConfig *TConfig
|
||||
sessionVariables map[string]interface{}
|
||||
// transactions stores transaction timing info.
|
||||
// key is transaction name, value is map of transaction type and time, e.g. start time and end time.
|
||||
@@ -24,10 +26,10 @@ type SessionRunner struct {
|
||||
|
||||
func (r *SessionRunner) init() {
|
||||
log.Info().Msg("init session runner")
|
||||
r.parsedConfig = &TConfig{}
|
||||
r.sessionVariables = make(map[string]interface{})
|
||||
r.transactions = make(map[string]map[transactionType]time.Time)
|
||||
r.startTime = time.Now()
|
||||
r.summary.Name = r.testCase.Config.Name
|
||||
}
|
||||
|
||||
func (r *SessionRunner) GetParser() *Parser {
|
||||
@@ -35,7 +37,7 @@ func (r *SessionRunner) GetParser() *Parser {
|
||||
}
|
||||
|
||||
func (r *SessionRunner) GetConfig() *TConfig {
|
||||
return r.testCase.Config
|
||||
return r.parsedConfig
|
||||
}
|
||||
|
||||
func (r *SessionRunner) LogOn() bool {
|
||||
@@ -62,7 +64,7 @@ func (r *SessionRunner) Start() error {
|
||||
}()
|
||||
|
||||
// parse config
|
||||
if err := r.parseConfig(config); err != nil {
|
||||
if err := r.parseConfig(nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -105,56 +107,67 @@ func (r *SessionRunner) MergeStepVariables(vars map[string]interface{}) (map[str
|
||||
// step variables > session variables (extracted variables from previous steps)
|
||||
overrideVars := mergeVariables(vars, r.sessionVariables)
|
||||
// step variables > testcase config variables
|
||||
overrideVars = mergeVariables(overrideVars, r.testCase.Config.Variables)
|
||||
overrideVars = mergeVariables(overrideVars, r.parsedConfig.Variables)
|
||||
|
||||
// parse step variables
|
||||
parsedVariables, err := r.parser.ParseVariables(overrideVars)
|
||||
if err != nil {
|
||||
log.Error().Interface("variables", r.testCase.Config.Variables).
|
||||
log.Error().Interface("variables", r.parsedConfig.Variables).
|
||||
Err(err).Msg("parse step variables failed")
|
||||
return nil, err
|
||||
}
|
||||
return parsedVariables, nil
|
||||
}
|
||||
|
||||
func (r *SessionRunner) parseConfig(cfg *TConfig) error {
|
||||
// parseConfig parses testcase config with given variables, stores to parsedConfig.
|
||||
func (r *SessionRunner) parseConfig(variables map[string]interface{}) error {
|
||||
cfg := r.testCase.Config
|
||||
|
||||
// deep copy config to avoid data racing
|
||||
if err := copier.Copy(r.parsedConfig, cfg); err != nil {
|
||||
log.Error().Err(err).Msg("copy testcase config failed")
|
||||
return err
|
||||
}
|
||||
|
||||
// parse config variables
|
||||
parsedVariables, err := r.parser.ParseVariables(cfg.Variables)
|
||||
mergedVars := mergeVariables(variables, cfg.Variables)
|
||||
parsedVariables, err := r.parser.ParseVariables(mergedVars)
|
||||
if err != nil {
|
||||
log.Error().Interface("variables", cfg.Variables).Err(err).Msg("parse config variables failed")
|
||||
return err
|
||||
}
|
||||
cfg.Variables = parsedVariables
|
||||
r.parsedConfig.Variables = parsedVariables
|
||||
|
||||
// parse config name
|
||||
parsedName, err := r.parser.ParseString(cfg.Name, cfg.Variables)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg.Name = convertString(parsedName)
|
||||
r.parsedConfig.Name = convertString(parsedName)
|
||||
|
||||
// parse config base url
|
||||
parsedBaseURL, err := r.parser.ParseString(cfg.BaseURL, cfg.Variables)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg.BaseURL = convertString(parsedBaseURL)
|
||||
r.parsedConfig.BaseURL = convertString(parsedBaseURL)
|
||||
|
||||
// ensure correction of think time config
|
||||
cfg.ThinkTimeSetting.checkThinkTime()
|
||||
r.parsedConfig.ThinkTimeSetting.checkThinkTime()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SessionRunner) GetSummary() *TestCaseSummary {
|
||||
caseSummary := r.summary
|
||||
caseSummary.Name = r.parsedConfig.Name
|
||||
caseSummary.Time.StartAt = r.startTime
|
||||
caseSummary.Time.Duration = time.Since(r.startTime).Seconds()
|
||||
exportVars := make(map[string]interface{})
|
||||
for _, value := range r.testCase.Config.Export {
|
||||
for _, value := range r.parsedConfig.Export {
|
||||
exportVars[value] = r.sessionVariables[value]
|
||||
}
|
||||
caseSummary.InOut.ExportVars = exportVars
|
||||
caseSummary.InOut.ConfigVars = r.testCase.Config.Variables
|
||||
caseSummary.InOut.ConfigVars = r.parsedConfig.Variables
|
||||
return caseSummary
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ func TestRunRequestRun(t *testing.T) {
|
||||
}
|
||||
runner := NewRunner(t).SetRequestsLogOn()
|
||||
sessionRunner := runner.NewSessionRunner(testcase)
|
||||
sessionRunner.parseConfig(nil)
|
||||
if _, err := stepGET.Run(sessionRunner); err != nil {
|
||||
t.Fatalf("stepGET.Run() error: %v", err)
|
||||
}
|
||||
|
||||
@@ -55,18 +55,24 @@ func (s *StepTestCaseWithOptionalArgs) Run(r *SessionRunner) (*StepResult, error
|
||||
return stepResult, err
|
||||
}
|
||||
|
||||
// copy step to avoid data racing
|
||||
copiedStep := &TStep{}
|
||||
if err := copier.Copy(copiedStep, s.step); err != nil {
|
||||
log.Error().Err(err).Msg("copy step failed")
|
||||
stepTestCase := s.step.TestCase.(*TestCase)
|
||||
|
||||
// copy testcase to avoid data racing
|
||||
copiedTestCase := &TestCase{}
|
||||
if err := copier.Copy(copiedTestCase, stepTestCase); err != nil {
|
||||
log.Error().Err(err).Msg("copy step testcase failed")
|
||||
return stepResult, err
|
||||
}
|
||||
|
||||
copiedStep.Variables = stepVariables
|
||||
copiedTestCase := copiedStep.TestCase.(*TestCase)
|
||||
|
||||
// override testcase config
|
||||
extendWithTestCase(s.step, copiedTestCase)
|
||||
// override testcase name
|
||||
if s.step.Name != "" {
|
||||
copiedTestCase.Config.Name = s.step.Name
|
||||
}
|
||||
// merge & override variables
|
||||
copiedTestCase.Config.Variables = mergeVariables(stepVariables, copiedTestCase.Config.Variables)
|
||||
// merge & override extractors
|
||||
copiedTestCase.Config.Export = mergeSlices(s.step.Export, copiedTestCase.Config.Export)
|
||||
|
||||
sessionRunner := r.hrpRunner.NewSessionRunner(copiedTestCase)
|
||||
|
||||
@@ -84,11 +90,6 @@ func (s *StepTestCaseWithOptionalArgs) Run(r *SessionRunner) (*StepResult, error
|
||||
stepResult.ExportVars = summary.InOut.ExportVars
|
||||
stepResult.Success = true
|
||||
|
||||
// update extracted variables
|
||||
for k, v := range stepResult.ExportVars {
|
||||
r.sessionVariables[k] = v
|
||||
}
|
||||
|
||||
// merge testcase summary
|
||||
r.summary.Records = append(r.summary.Records, summary.Records...)
|
||||
r.summary.Stat.Total += summary.Stat.Total
|
||||
@@ -97,15 +98,3 @@ func (s *StepTestCaseWithOptionalArgs) Run(r *SessionRunner) (*StepResult, error
|
||||
|
||||
return stepResult, nil
|
||||
}
|
||||
|
||||
// extend referenced testcase with teststep, teststep config merge and override referenced testcase config
|
||||
func extendWithTestCase(testStep *TStep, overriddenTestCase *TestCase) {
|
||||
// override testcase name
|
||||
if testStep.Name != "" {
|
||||
overriddenTestCase.Config.Name = testStep.Name
|
||||
}
|
||||
// merge & override variables
|
||||
overriddenTestCase.Config.Variables = mergeVariables(testStep.Variables, overriddenTestCase.Config.Variables)
|
||||
// merge & override extractors
|
||||
overriddenTestCase.Config.Export = mergeSlices(testStep.Export, overriddenTestCase.Config.Export)
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ func (s *StepThinkTime) Run(r *SessionRunner) (*StepResult, error) {
|
||||
Success: true,
|
||||
}
|
||||
|
||||
cfg := r.testCase.Config.ThinkTimeSetting
|
||||
cfg := r.parsedConfig.ThinkTimeSetting
|
||||
if cfg == nil {
|
||||
cfg = &ThinkTimeConfig{thinkTimeDefault, nil, 0}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user