This repository has been archived by the owner on Mar 28, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 25
/
boomer.go
177 lines (156 loc) · 4.99 KB
/
boomer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
package hrp
import (
"sync"
"time"
"github.com/jinzhu/copier"
"github.com/rs/zerolog/log"
"github.com/httprunner/funplugin"
"github.com/httprunner/hrp/internal/boomer"
"github.com/httprunner/hrp/internal/ga"
)
func NewBoomer(spawnCount int, spawnRate float64) *HRPBoomer {
b := &HRPBoomer{
Boomer: boomer.NewStandaloneBoomer(spawnCount, spawnRate),
pluginsMutex: new(sync.RWMutex),
}
return b
}
type HRPBoomer struct {
*boomer.Boomer
plugins []funplugin.IPlugin // each task has its own plugin process
pluginsMutex *sync.RWMutex // avoid data race
}
// Run starts to run load test for one or multiple testcases.
func (b *HRPBoomer) Run(testcases ...ITestCase) {
event := ga.EventTracking{
Category: "RunLoadTests",
Action: "hrp boom",
}
// report start event
go ga.SendEvent(event)
// report execution timing event
defer ga.SendEvent(event.StartTiming("execution"))
var taskSlice []*boomer.Task
for _, iTestCase := range testcases {
testcase, err := iTestCase.ToTestCase()
if err != nil {
panic(err)
}
cfg := testcase.Config
err = initParameterIterator(cfg, "boomer")
if err != nil {
panic(err)
}
rendezvousList := initRendezvous(testcase, int64(b.GetSpawnCount()))
task := b.convertBoomerTask(testcase, rendezvousList)
taskSlice = append(taskSlice, task)
waitRendezvous(rendezvousList)
}
b.Boomer.Run(taskSlice...)
}
func (b *HRPBoomer) Quit() {
b.pluginsMutex.Lock()
plugins := b.plugins
b.pluginsMutex.Unlock()
for _, plugin := range plugins {
plugin.Quit()
}
b.Boomer.Quit()
}
func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rendezvous) *boomer.Task {
hrpRunner := NewRunner(nil)
// set client transport for high concurrency load testing
hrpRunner.SetClientTransport(b.GetSpawnCount(), b.GetDisableKeepAlive(), b.GetDisableCompression())
config := testcase.Config
// each testcase has its own plugin process
plugin, _ := initPlugin(config.Path, false)
if plugin != nil {
b.pluginsMutex.Lock()
b.plugins = append(b.plugins, plugin)
b.pluginsMutex.Unlock()
}
// broadcast to all rendezvous at once when spawn done
go func() {
<-b.GetSpawnDoneChan()
for _, rendezvous := range rendezvousList {
rendezvous.setSpawnDone()
}
}()
return &boomer.Task{
Name: config.Name,
Weight: config.Weight,
Fn: func() {
runner := hrpRunner.newCaseRunner(testcase)
runner.parser.plugin = plugin
testcaseSuccess := true // flag whole testcase result
var transactionSuccess = true // flag current transaction result
cfg := testcase.Config
caseConfig := &TConfig{}
// copy config to avoid data racing
if err := copier.Copy(caseConfig, cfg); err != nil {
log.Error().Err(err).Msg("copy config data failed")
return
}
// iterate through all parameter iterators and update case variables
for _, it := range caseConfig.ParametersSetting.Iterators {
if it.HasNext() {
caseConfig.Variables = mergeVariables(it.Next(), caseConfig.Variables)
}
}
if err := runner.parseConfig(caseConfig); err != nil {
log.Error().Err(err).Msg("parse config failed")
return
}
startTime := time.Now()
for index, step := range testcase.TestSteps {
stepData, err := runner.runStep(index, caseConfig)
if err != nil {
// step failed
var elapsed int64
if stepData != nil {
elapsed = stepData.Elapsed
}
b.RecordFailure(step.Type(), step.Name(), elapsed, err.Error())
// update flag
testcaseSuccess = false
transactionSuccess = false
if runner.hrpRunner.failfast {
log.Error().Msg("abort running due to failfast setting")
break
}
log.Warn().Err(err).Msg("run step failed, continue next step")
continue
}
// step success
if stepData.StepType == stepTypeTransaction {
// transaction
// FIXME: support nested transactions
if step.ToStruct().Transaction.Type == transactionEnd { // only record when transaction ends
b.RecordTransaction(stepData.Name, transactionSuccess, stepData.Elapsed, 0)
transactionSuccess = true // reset flag for next transaction
}
} else if stepData.StepType == stepTypeRendezvous {
// rendezvous
// TODO: implement rendezvous in boomer
} else if stepData.StepType == stepTypeThinkTime {
// think time
// no record required
} else {
// request or testcase step
b.RecordSuccess(step.Type(), step.Name(), stepData.Elapsed, stepData.ContentSize)
}
}
endTime := time.Now()
// report duration for transaction without end
for name, transaction := range runner.transactions {
if len(transaction) == 1 {
// if transaction end time not exists, use testcase end time instead
duration := endTime.Sub(transaction[transactionStart])
b.RecordTransaction(name, transactionSuccess, duration.Milliseconds(), 0)
}
}
// report testcase as a whole Action transaction, inspired by LoadRunner
b.RecordTransaction("Action", testcaseSuccess, endTime.Sub(startTime).Milliseconds(), 0)
},
}
}