Skip to content

Commit b2945db

Browse files
authored
Enhancement/569 change reuse user randomizer (#24)
* Make session id count also for reuse user iterations * Determinism not broken due to a failed iteration when using reuse user flag * Bugfix for determinsm and thinktime actions * Randomizer can now be overriden by other by users or future schedulers
1 parent 4b498c9 commit b2945db

File tree

15 files changed

+267
-115
lines changed

15 files changed

+267
-115
lines changed

docs/settingup.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -726,7 +726,7 @@ Each action executed by `randomaction` is followed by a customizable `thinktime`
726726
* `uniform`: Random think time with uniform distribution, defined by `mean` and `dev`.
727727
* `delay`: Delay (seconds), used with type `static`.
728728
* `mean`: Mean (seconds), used with type `uniform`.
729-
* `dev`: Deviation (seconds), used with type `uniform`.
729+
* `dev`: Deviation (seconds) from `mean` value, used with type `uniform`.
730730
* `iterations`: Number of random actions to perform.
731731

732732
### Random action defaults
@@ -1037,12 +1037,14 @@ Simulate user think time.
10371037
* `uniform`: Random think time with uniform distribution, defined by `mean` and `dev`.
10381038
* `delay`: Delay (seconds), used with type `static`.
10391039
* `mean`: Mean (seconds), used with type `uniform`.
1040-
* `dev`: Deviation (seconds), used with type `uniform`.
1040+
* `dev`: Deviation (seconds) from `mean` value, used with type `uniform`.
10411041

10421042
### Examples
10431043

10441044
#### ThinkTime uniform
10451045

1046+
This simulates a think time of 10 to 15 seconds.
1047+
10461048
```json
10471049
{
10481050
"label": "TimerDelay",
@@ -1057,6 +1059,8 @@ Simulate user think time.
10571059

10581060
#### ThinkTime constant
10591061

1062+
This simulates a think time of 5 seconds.
1063+
10601064
```json
10611065
{
10621066
"label": "TimerDelay",

generatedocs/data/actions/thinktime/examples.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
#### ThinkTime uniform
44

5+
This simulates a think time of 10 to 15 seconds.
6+
57
```json
68
{
79
"label": "TimerDelay",
@@ -16,6 +18,8 @@
1618

1719
#### ThinkTime constant
1820

21+
This simulates a think time of 5 seconds.
22+
1923
```json
2024
{
2125
"label": "TimerDelay",

generatedocs/data/params.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,7 @@
525525
"Mean (seconds), used with type `uniform`."
526526
],
527527
"thinktime.dev": [
528-
"Deviation (seconds), used with type `uniform`."
528+
"Deviation (seconds) from `mean` value, used with type `uniform`."
529529
],
530530
"unpublishsheet.mode": [
531531
"",

generatedocs/generated/documentation.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ var (
161161
},
162162
"thinktime": {
163163
Description: "## ThinkTime action\n\nSimulate user think time.\n\n**Note:** This action does not require an app context (that is, it does not have to be prepended with an `openapp` action).\n",
164-
Examples: "### Examples\n\n#### ThinkTime uniform\n\n```json\n{\n \"label\": \"TimerDelay\",\n \"action\": \"thinktime\",\n \"settings\": {\n \"type\": \"uniform\",\n \"mean\": 12.5,\n \"dev\": 2.5\n } \n} \n```\n\n#### ThinkTime constant\n\n```json\n{\n \"label\": \"TimerDelay\",\n \"action\": \"thinktime\",\n \"settings\": {\n \"type\": \"static\",\n \"delay\": 5\n }\n}\n```\n",
164+
Examples: "### Examples\n\n#### ThinkTime uniform\n\nThis simulates a think time of 10 to 15 seconds.\n\n```json\n{\n \"label\": \"TimerDelay\",\n \"action\": \"thinktime\",\n \"settings\": {\n \"type\": \"uniform\",\n \"mean\": 12.5,\n \"dev\": 2.5\n } \n} \n```\n\n#### ThinkTime constant\n\nThis simulates a think time of 5 seconds.\n\n```json\n{\n \"label\": \"TimerDelay\",\n \"action\": \"thinktime\",\n \"settings\": {\n \"type\": \"static\",\n \"delay\": 5\n }\n}\n```\n",
165165
},
166166
"unpublishsheet": {
167167
Description: "## UnpublishSheet action\n\nUnpublish sheets in the current app.\n",
@@ -318,7 +318,7 @@ var (
318318
"staticselect.type": { "Selection type","`hypercubecells`: Select in hypercube.","`listobjectvalues`: Select in listbox." },
319319
"staticselect.wrap": { "Wrap selection with Begin / End selection requests (`true` / `false`)." },
320320
"thinktime.delay": { "Delay (seconds), used with type `static`." },
321-
"thinktime.dev": { "Deviation (seconds), used with type `uniform`." },
321+
"thinktime.dev": { "Deviation (seconds) from `mean` value, used with type `uniform`." },
322322
"thinktime.mean": { "Mean (seconds), used with type `uniform`." },
323323
"thinktime.type": { "Type of think time","`static`: Static think time, defined by `delay`.","`uniform`: Random think time with uniform distribution, defined by `mean` and `dev`." },
324324
"unpublishsheet.mode": { "","`allsheets`: Unpublish all sheets in the app.","`sheetids`: Only unpublish the sheets specified by the `sheetIds` array." },

helpers/distribution.go

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@ package helpers
22

33
import (
44
"fmt"
5-
"strconv"
6-
"math/rand"
7-
85
"github.com/pkg/errors"
96
"github.com/qlik-oss/gopherciser/enummap"
10-
"github.com/qlik-oss/gopherciser/randomizer"
7+
"strconv"
8+
"time"
119
)
1210

1311
type (
@@ -34,7 +32,8 @@ const (
3432
UniformDistribution
3533
)
3634

37-
func (value DistributionType) GetEnumMap() *enummap.EnumMap{
35+
// GetEnumMap of DistributionType
36+
func (value DistributionType) GetEnumMap() *enummap.EnumMap {
3837
enumMap, _ := enummap.NewEnumMap(map[string]int{
3938
"static": int(StaticDistribution),
4039
"uniform": int(UniformDistribution),
@@ -62,6 +61,7 @@ func (value DistributionType) MarshalJSON() ([]byte, error) {
6261
return []byte(fmt.Sprintf(`"%s"`, str)), nil
6362
}
6463

64+
// Validate DistributionSettings
6565
func (settings DistributionSettings) Validate() error {
6666
switch settings.Type {
6767
case StaticDistribution:
@@ -89,21 +89,22 @@ func (settings DistributionSettings) Validate() error {
8989
return nil
9090
}
9191

92-
func (settings DistributionSettings) GetSample(rnd *randomizer.Randomizer) (float64, error) {
92+
// RandDuration returns a random duration
93+
func (settings DistributionSettings) RandDuration(rnd Randomizer) (time.Duration, error) {
9394
switch settings.Type {
9495
case StaticDistribution:
95-
return settings.Delay, nil
96+
return time.Duration(settings.Delay * float64(time.Second)), nil
9697
case UniformDistribution:
97-
min := settings.Mean - settings.Deviation
98-
max := settings.Mean + settings.Deviation
99-
delay := (rand.Float64() * (max-min)) + min
100-
return delay, nil
98+
min := time.Duration(float64(time.Second) * (settings.Mean - settings.Deviation))
99+
max := time.Duration(float64(time.Second) * (settings.Mean + settings.Deviation))
100+
return rnd.RandDuration(min, max)
101101
default:
102102
return 0, errors.Errorf("distribution type<%d> not yet supported", settings.Type)
103103
}
104104
}
105105

106-
func (settings DistributionSettings) GetMax(rnd *randomizer.Randomizer) (float64, error) {
106+
// GetMax value
107+
func (settings DistributionSettings) GetMax() (float64, error) {
107108
switch settings.Type {
108109
case StaticDistribution:
109110
return settings.Delay, nil
@@ -115,7 +116,8 @@ func (settings DistributionSettings) GetMax(rnd *randomizer.Randomizer) (float64
115116
}
116117
}
117118

118-
func (settings DistributionSettings) GetMin(rnd *randomizer.Randomizer) (float64, error) {
119+
// GetMin value
120+
func (settings DistributionSettings) GetMin() (float64, error) {
119121
switch settings.Type {
120122
case StaticDistribution:
121123
return settings.Delay, nil
@@ -127,6 +129,7 @@ func (settings DistributionSettings) GetMin(rnd *randomizer.Randomizer) (float64
127129
}
128130
}
129131

132+
// GetActionInfo get information for action details logging
130133
func (settings DistributionSettings) GetActionInfo() string {
131134
switch settings.Type {
132135
case StaticDistribution:

helpers/distribution_test.go

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,19 @@ package helpers
22

33
import (
44
"testing"
5+
"time"
56

67
"github.com/qlik-oss/gopherciser/randomizer"
78
)
89

10+
type Rnd struct {
11+
*randomizer.Randomizer
12+
}
13+
14+
func (rnd *Rnd) Reset(instance, session uint64, onlyinstanceSeed bool) {
15+
rnd.Randomizer = randomizer.NewSeededRandomizer(randomizer.GetPredictableSeed(int(instance), int(session)))
16+
}
17+
918
func TestDistributionUniform(t *testing.T) {
1019
t.Parallel()
1120

@@ -28,19 +37,20 @@ func TestDistributionUniform(t *testing.T) {
2837
t.Fatalf("Deviation: Expected<0.1> got<%f>", settings.Deviation)
2938
}
3039

31-
rndCompare := randomizer.NewSeededRandomizer(randomizer.GetPredictableSeed(1, 1))
40+
rndCompare := &Rnd{}
41+
rndCompare.Reset(1, 1, false)
3242

33-
sample, err := settings.GetSample(rndCompare)
43+
sample, err := settings.RandDuration(rndCompare)
3444
if err != nil {
3545
t.Fatal(err)
3646
}
3747

38-
if sample > 0.6 {
39-
t.Fatalf("Sample: Expected< < 0.6> got<%f>", sample)
48+
if sample > time.Duration(0.6*float64(time.Second)) {
49+
t.Fatalf("Sample: Expected< < 0.6s> got<%v>", sample)
4050
}
4151

42-
if sample < 0.4 {
43-
t.Fatalf("Sample: Expected< > 0.4> got<%f>", sample)
52+
if sample < time.Duration(0.4*float64(time.Second)) {
53+
t.Fatalf("Sample: Expected< > 0.4> got<%v>", sample)
4454
}
4555
}
4656

@@ -61,13 +71,13 @@ func TestThinkTimeStatic(t *testing.T) {
6171
t.Fatalf("Delay: Expected<0.1> got<%f>", settings.Delay)
6272
}
6373

64-
sample, err := settings.GetSample(nil)
74+
sample, err := settings.RandDuration(nil)
6575
if err != nil {
6676
t.Fatal(err)
6777
}
6878

69-
if sample != 0.1 {
70-
t.Fatalf("Sample: Expected<0.1> got<%f>", sample)
79+
if sample != time.Duration(0.1*float64(time.Second)) {
80+
t.Fatalf("Sample: Expected<0.1> got<%v>", sample)
7181
}
7282
}
7383

helpers/randomizer.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package helpers
2+
3+
import "time"
4+
5+
type Randomizer interface {
6+
Rand(max int) int
7+
RandWeightedInt(weights []int) (int, error)
8+
RandIntPos(ints []int) (int, int, error)
9+
RandDuration(minDuration, maxDuration time.Duration) (time.Duration, error)
10+
Reset(instance, session uint64, onlyinstanceSeed bool)
11+
}

scenario/select.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package scenario
33
import (
44
"context"
55
"fmt"
6+
"github.com/qlik-oss/gopherciser/helpers"
67
"math"
78
"sort"
89
"strconv"
@@ -16,7 +17,6 @@ import (
1617
"github.com/qlik-oss/gopherciser/enummap"
1718
"github.com/qlik-oss/gopherciser/globals/constant"
1819
"github.com/qlik-oss/gopherciser/logger"
19-
"github.com/qlik-oss/gopherciser/randomizer"
2020
"github.com/qlik-oss/gopherciser/senseobjdef"
2121
"github.com/qlik-oss/gopherciser/session"
2222
)
@@ -465,7 +465,7 @@ func getHyperCubeCardinal(objId string, dimension int, hypercube *enigmahandlers
465465
return dimInfo.Cardinal, nil
466466
}
467467

468-
func getSelectQty(min, max, possible int, rnd *randomizer.Randomizer) int {
468+
func getSelectQty(min, max, possible int, rnd helpers.Randomizer) int {
469469
localMin := min
470470
localMax := max
471471

@@ -488,7 +488,7 @@ func getSelectQty(min, max, possible int, rnd *randomizer.Randomizer) int {
488488
return rnd.Rand(localMax-localMin+1) + localMin
489489
}
490490

491-
func fillSelectPosFromAll(min, max, cardinal int, rnd *randomizer.Randomizer) ([]int, error) {
491+
func fillSelectPosFromAll(min, max, cardinal int, rnd helpers.Randomizer) ([]int, error) {
492492
if rnd == nil {
493493
return nil, errors.Errorf("Randomizer not provided")
494494
}
@@ -532,7 +532,7 @@ func fillSelectPosFromAll(min, max, cardinal int, rnd *randomizer.Randomizer) ([
532532
return selectPos.Array(), nil
533533
}
534534

535-
func fillSelectPosFromPossible(min, max int, possible []int, rnd *randomizer.Randomizer) ([]int, error) {
535+
func fillSelectPosFromPossible(min, max int, possible []int, rnd helpers.Randomizer) ([]int, error) {
536536
if rnd == nil {
537537
return nil, errors.Errorf("Randomizer not provided")
538538
}
@@ -581,7 +581,7 @@ func fillSelectPosFromPossible(min, max int, possible []int, rnd *randomizer.Ran
581581
return selectPos.Array(), nil
582582
}
583583

584-
func fillSelectBinsFromBins(min, max int, bins []string, rnd *randomizer.Randomizer) ([]string, error) {
584+
func fillSelectBinsFromBins(min, max int, bins []string, rnd helpers.Randomizer) ([]string, error) {
585585
if rnd == nil {
586586
return nil, errors.Errorf("Randomizer not provided")
587587
}

scenario/select_test.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ import (
88
"github.com/qlik-oss/gopherciser/randomizer"
99
)
1010

11+
type Rnd struct {
12+
*randomizer.Randomizer
13+
}
14+
15+
func (rnd *Rnd) Reset(instance, session uint64, onlyinstanceSeed bool) {
16+
rnd.Randomizer = randomizer.NewSeededRandomizer(randomizer.GetPredictableSeed(int(instance), int(session)))
17+
}
18+
1119
func TestSelectUnmarshal(t *testing.T) {
1220
t.Parallel()
1321

@@ -117,7 +125,8 @@ func TestValidate(t *testing.T) {
117125
func TestSelectQty(t *testing.T) {
118126
t.Parallel()
119127

120-
rnd := randomizer.NewSeededRandomizer(randomizer.GetPredictableSeedUInt64(1, 2))
128+
rnd := &Rnd{}
129+
rnd.Reset(1, 2, false)
121130

122131
v := getSelectQty(2, 5, 5, rnd)
123132
validateInt(t, "selectqty", v, 4)
@@ -178,7 +187,8 @@ func TestCutSlice(t *testing.T) {
178187
func TestFillPos(t *testing.T) {
179188
t.Parallel()
180189

181-
rnd := randomizer.NewSeededRandomizer(randomizer.GetPredictableSeedUInt64(1, 3245345))
190+
rnd := &Rnd{}
191+
rnd.Reset(1, 3245345, false)
182192

183193
selectPos, err := fillSelectPosFromAll(3, 6, 5, rnd)
184194
if err != nil {

scenario/thinktime.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ import (
1313
"github.com/qlik-oss/gopherciser/session"
1414
)
1515

16-
const nanosecond float64 = 0.000000001
17-
1816
type (
1917
// ThinkTimeSettings think time settings
2018
ThinkTimeSettings struct {
@@ -30,13 +28,12 @@ func (settings ThinkTimeSettings) Execute(sessionState *session.State, actionSta
3028
sessionState.LogEntry.Log(logger.WarningLevel, "Faking sent message in timer delay failed")
3129
}
3230

33-
seconds, err := settings.DistributionSettings.GetSample(sessionState.Randomizer())
34-
delay := time.Duration(int(seconds*1000000000)) * time.Nanosecond
31+
delay, err := settings.DistributionSettings.RandDuration(sessionState.Randomizer())
3532
if err != nil {
3633
actionState.AddErrors(errors.WithStack(err))
3734
return
3835
}
39-
if seconds < nanosecond {
36+
if delay < time.Nanosecond {
4037
actionState.AddErrors(errors.New("timer delay not set"))
4138
return
4239
}

0 commit comments

Comments
 (0)