-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Scenario's stage number #2199
Scenario's stage number #2199
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
/* | ||
* | ||
* k6 - a next-generation load testing tool | ||
* Copyright (C) 2021 Load Impact | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU Affero General Public License as | ||
* published by the Free Software Foundation, either version 3 of the | ||
* License, or (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU Affero General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Affero General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
* | ||
*/ | ||
|
||
package execution | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
"time" | ||
|
||
"github.com/dop251/goja" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"go.k6.io/k6/js/common" | ||
"go.k6.io/k6/js/modulestest" | ||
"go.k6.io/k6/lib" | ||
) | ||
|
||
func TestScenarioStage(t *testing.T) { | ||
t.Parallel() | ||
|
||
rt := goja.New() | ||
ctx := common.WithRuntime(context.Background(), rt) | ||
ctx = lib.WithScenarioState(ctx, &lib.ScenarioState{ | ||
Stages: []lib.ScenarioStage{ | ||
{ | ||
Index: 0, | ||
Name: "ramp up", | ||
Duration: 10 * time.Second, | ||
}, | ||
{ | ||
Index: 1, | ||
Name: "ramp down", | ||
Duration: 10 * time.Second, | ||
}, | ||
}, | ||
StartTime: time.Now().Add(-11 * time.Second), | ||
}) | ||
m, ok := New().NewModuleInstance( | ||
&modulestest.InstanceCore{ | ||
Runtime: rt, | ||
InitEnv: &common.InitEnvironment{}, | ||
State: &lib.State{}, | ||
Ctx: ctx, | ||
}, | ||
).(*ModuleInstance) | ||
require.True(t, ok) | ||
require.NoError(t, rt.Set("exec", m.GetExports().Default)) | ||
|
||
num, err := rt.RunString(`exec.scenario.stage.number`) | ||
require.NoError(t, err) | ||
assert.Equal(t, int64(1), num.ToInteger()) | ||
|
||
stage, err := rt.RunString(`exec.scenario.stage.name`) | ||
require.NoError(t, err) | ||
assert.Equal(t, "ramp down", stage.String()) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -113,9 +113,45 @@ type ExecutorConfig interface { | |
// ScenarioState holds runtime scenario information returned by the k6/execution | ||
// JS module. | ||
type ScenarioState struct { | ||
Name, Executor string | ||
StartTime time.Time | ||
ProgressFn func() (float64, []string) | ||
Name string | ||
Executor string | ||
StartTime time.Time | ||
ProgressFn func() (float64, []string) | ||
Stages []ScenarioStage | ||
} | ||
|
||
// ScenarioStage represents a Scenario's Stage. | ||
// where Index tracks the original slice's position of the Stage. | ||
type ScenarioStage struct { | ||
Index uint | ||
Name string | ||
Duration time.Duration | ||
} | ||
|
||
// CurrentStage returns the detected Stage that is currently running | ||
// based on the StartTime of the Scenario. | ||
func (s *ScenarioState) CurrentStage() (*ScenarioStage, error) { | ||
if len(s.Stages) < 1 { | ||
// TODO: improve this error message | ||
return nil, fmt.Errorf("can't get the current Stage because any Stage has been defined") | ||
} | ||
Comment on lines
+134
to
+137
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is going to throw an exception on executors with no stages? I am not certain this is the best idea instead of returning |
||
|
||
// sum represents the stages passed | ||
sum := int64(0) | ||
elapsed := time.Since(s.StartTime) | ||
for _, stage := range s.Stages { | ||
sum += int64(stage.Duration) | ||
// when elapsed is smaller than sum | ||
// then the current stage has been found | ||
if int64(elapsed) < sum { | ||
return &stage, nil | ||
} | ||
} | ||
Comment on lines
+139
to
+149
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't really think that trying to calculate the exact stage on each call is a good idea. I really don't think that will be what most users will care about and if they do there is an easy way to do it (as you do here). But I would expect it will be really surprising to people accessing I do expect that later one will definitely get them to scratch their head quite a bit and try to figure out how did they write their code in such a way that a key they just added with |
||
|
||
// it happen when: | ||
// * the total duration is equal to the latest stage's upper limit | ||
// * the latest stage is taking more than the expected defined duration | ||
return &s.Stages[len(s.Stages)-1], nil | ||
} | ||
|
||
// InitVUFunc is just a shorthand so we don't have to type the function | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
package lib | ||
|
||
import ( | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestScenarioStateCurrentStage(t *testing.T) { | ||
t.Parallel() | ||
|
||
t.Run("Success", func(t *testing.T) { | ||
t.Parallel() | ||
// this just asserts that the fields are populated as expected | ||
s1 := ScenarioStage{ | ||
Index: 1, | ||
Name: "stage1", | ||
Duration: time.Second, | ||
} | ||
|
||
state := ScenarioState{ | ||
Stages: []ScenarioStage{ | ||
{ | ||
Index: 0, | ||
Duration: 2 * time.Second, | ||
}, | ||
s1, | ||
}, | ||
} | ||
stage, err := state.CurrentStage() | ||
require.NoError(t, err) | ||
assert.Equal(t, &s1, stage) | ||
}) | ||
|
||
t.Run("SuccessEdgeCases", func(t *testing.T) { | ||
t.Parallel() | ||
tests := []struct { | ||
name string | ||
stages []time.Duration | ||
elapsed time.Duration // it fakes the elapsed scenario time | ||
expIndex uint | ||
}{ | ||
{ | ||
name: "ZeroTime", | ||
stages: []time.Duration{5 * time.Second, 20 * time.Second}, | ||
elapsed: 0, | ||
expIndex: 0, | ||
}, | ||
{ | ||
name: "FirstStage", | ||
stages: []time.Duration{5 * time.Second, 20 * time.Second}, | ||
elapsed: 4 * time.Second, | ||
expIndex: 0, | ||
}, | ||
{ | ||
name: "MiddleStage", | ||
stages: []time.Duration{5 * time.Second, 20 * time.Second, 10 * time.Second}, | ||
elapsed: 10 * time.Second, | ||
expIndex: 1, | ||
}, | ||
{ | ||
name: "StageUpperLimit", | ||
stages: []time.Duration{5 * time.Second, 20 * time.Second, 10 * time.Second}, | ||
elapsed: 25 * time.Second, | ||
expIndex: 2, | ||
}, | ||
{ | ||
name: "OverLatestStage", | ||
stages: []time.Duration{5 * time.Second, 20 * time.Second}, | ||
elapsed: 30 * time.Second, | ||
expIndex: 1, | ||
}, | ||
} | ||
|
||
for _, tc := range tests { | ||
tc := tc | ||
t.Run(tc.name, func(t *testing.T) { | ||
t.Parallel() | ||
|
||
stages := func() []ScenarioStage { | ||
stages := make([]ScenarioStage, 0, len(tc.stages)) | ||
for i, duration := range tc.stages { | ||
stage := ScenarioStage{ | ||
Index: uint(i), | ||
Duration: duration, | ||
} | ||
if uint(i) == tc.expIndex { | ||
stage.Name = tc.name | ||
} | ||
stages = append(stages, stage) | ||
} | ||
return stages | ||
} | ||
|
||
state := ScenarioState{ | ||
Stages: stages(), | ||
StartTime: time.Now().Add(-tc.elapsed), | ||
} | ||
|
||
stage, err := state.CurrentStage() | ||
require.NoError(t, err) | ||
assert.Equal(t, tc.expIndex, stage.Index) | ||
assert.Equal(t, tc.name, stage.Name) | ||
}) | ||
} | ||
}) | ||
|
||
t.Run("ErrorOnEmpty", func(t *testing.T) { | ||
t.Parallel() | ||
state := ScenarioState{} | ||
stage, err := state.CurrentStage() | ||
require.NotNil(t, err) | ||
assert.Contains(t, err.Error(), "any Stage") | ||
assert.Nil(t, stage) | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: you can just make with the appropriate len(instead of cap) and do
stages[i] = ...
instead ofappend