Skip to content

Commit

Permalink
Copy xk6-execution code back in
Browse files Browse the repository at this point in the history
works on #1320
  • Loading branch information
mstoykov committed Aug 31, 2021
1 parent 3994f1a commit 3722108
Show file tree
Hide file tree
Showing 4 changed files with 693 additions and 0 deletions.
2 changes: 2 additions & 0 deletions js/initcontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import (
"go.k6.io/k6/js/modules/k6/crypto/x509"
"go.k6.io/k6/js/modules/k6/data"
"go.k6.io/k6/js/modules/k6/encoding"
"go.k6.io/k6/js/modules/k6/execution"
"go.k6.io/k6/js/modules/k6/grpc"
"go.k6.io/k6/js/modules/k6/html"
"go.k6.io/k6/js/modules/k6/http"
Expand Down Expand Up @@ -322,6 +323,7 @@ func getInternalJSModules() map[string]interface{} {
"k6/crypto/x509": x509.New(),
"k6/data": data.New(),
"k6/encoding": encoding.New(),
"k6/execution": execution.New(),
"k6/net/grpc": grpc.New(),
"k6/html": html.New(),
"k6/http": http.New(),
Expand Down
205 changes: 205 additions & 0 deletions js/modules/k6/execution/execution.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
/*
*
* 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 (
"errors"
"time"

"github.com/dop251/goja"

"go.k6.io/k6/js/common"
"go.k6.io/k6/js/modules"
"go.k6.io/k6/lib"
)

type (
// RootModule is the global module instance that will create module
// instances for each VU.
RootModule struct{}

// ModuleInstance represents an instance of the execution module.
ModuleInstance struct {
modules.InstanceCore
obj *goja.Object
}
)

var (
_ modules.IsModuleV2 = &RootModule{}
_ modules.Instance = &ModuleInstance{}
)

// New returns a pointer to a new RootModule instance.
func New() *RootModule {
return &RootModule{}
}

// NewModuleInstance implements the modules.IsModuleV2 interface to return
// a new instance for each VU.
func (*RootModule) NewModuleInstance(m modules.InstanceCore) modules.Instance {
mi := &ModuleInstance{InstanceCore: m}
rt := m.GetRuntime()
o := rt.NewObject()
defProp := func(name string, newInfo func() (*goja.Object, error)) {
err := o.DefineAccessorProperty(name, rt.ToValue(func() goja.Value {
obj, err := newInfo()
if err != nil {
common.Throw(rt, err)
}
return obj
}), nil, goja.FLAG_FALSE, goja.FLAG_TRUE)
if err != nil {
common.Throw(rt, err)
}
}
defProp("scenario", mi.newScenarioInfo)
defProp("test", mi.newTestInfo)
defProp("vu", mi.newVUInfo)

mi.obj = o

return mi
}

// GetExports returns the exports of the execution module.
func (mi *ModuleInstance) GetExports() modules.Exports {
return modules.Exports{Default: mi.obj}
}

// newScenarioInfo returns a goja.Object with property accessors to retrieve
// information about the scenario the current VU is running in.
func (mi *ModuleInstance) newScenarioInfo() (*goja.Object, error) {
ctx := mi.GetContext()
vuState := lib.GetState(ctx)
ss := lib.GetScenarioState(ctx)
if ss == nil || vuState == nil {
return nil, errors.New("getting scenario information in the init context is not supported")
}

rt := common.GetRuntime(ctx)
if rt == nil {
return nil, errors.New("goja runtime is nil in context")
}

si := map[string]func() interface{}{
"name": func() interface{} {
ctx := mi.GetContext()
ss := lib.GetScenarioState(ctx)
return ss.Name
},
"executor": func() interface{} {
ctx := mi.GetContext()
ss := lib.GetScenarioState(ctx)
return ss.Executor
},
"startTime": func() interface{} { return float64(ss.StartTime.UnixNano()) / 1e9 },
"progress": func() interface{} {
p, _ := ss.ProgressFn()
return p
},
"iteration": func() interface{} {
return vuState.GetScenarioLocalVUIter()
},
"iterationGlobal": func() interface{} {
if vuState.GetScenarioGlobalVUIter != nil {
return vuState.GetScenarioGlobalVUIter()
}
return goja.Null()
},
}

return newInfoObj(rt, si)
}

// newTestInfo returns a goja.Object with property accessors to retrieve
// information about the overall test run (local instance).
func (mi *ModuleInstance) newTestInfo() (*goja.Object, error) {
ctx := mi.GetContext()
es := lib.GetExecutionState(ctx)
if es == nil {
return nil, errors.New("getting test information in the init context is not supported")
}

rt := common.GetRuntime(ctx)
if rt == nil {
return nil, errors.New("goja runtime is nil in context")
}

ti := map[string]func() interface{}{
"duration": func() interface{} {
return float64(es.GetCurrentTestRunDuration()) / float64(time.Millisecond)
},
"iterationsCompleted": func() interface{} {
return es.GetFullIterationCount()
},
"iterationsInterrupted": func() interface{} {
return es.GetPartialIterationCount()
},
"vusActive": func() interface{} {
return es.GetCurrentlyActiveVUsCount()
},
"vusMax": func() interface{} {
return es.GetInitializedVUsCount()
},
}

return newInfoObj(rt, ti)
}

// newVUInfo returns a goja.Object with property accessors to retrieve
// information about the currently executing VU.
func (mi *ModuleInstance) newVUInfo() (*goja.Object, error) {
ctx := mi.GetContext()
vuState := lib.GetState(ctx)
if vuState == nil {
return nil, errors.New("getting VU information in the init context is not supported")
}

rt := common.GetRuntime(ctx)
if rt == nil {
return nil, errors.New("goja runtime is nil in context")
}

vi := map[string]func() interface{}{
"id": func() interface{} { return vuState.VUID },
"idGlobal": func() interface{} { return vuState.VUIDGlobal },
"iteration": func() interface{} { return vuState.Iteration },
"iterationScenario": func() interface{} {
return vuState.GetScenarioVUIter()
},
}

return newInfoObj(rt, vi)
}

func newInfoObj(rt *goja.Runtime, props map[string]func() interface{}) (*goja.Object, error) {
o := rt.NewObject()

for p, get := range props {
err := o.DefineAccessorProperty(p, rt.ToValue(get), nil, goja.FLAG_FALSE, goja.FLAG_TRUE)
if err != nil {
return nil, err
}
}

return o, nil
}
Loading

0 comments on commit 3722108

Please sign in to comment.