How to use initVU method of local Package

Best K6 code snippet using local.initVU

runner_test.go

Source:runner_test.go Github

copy

Full Screen

...70 `)71 assert.NoError(t, err)72 t.Run("NewVU", func(t *testing.T) {73 t.Parallel()74 initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))75 assert.NoError(t, err)76 vuc, ok := initVU.(*VU)77 assert.True(t, ok)78 assert.Equal(t, int64(0), vuc.Runtime.Get("counter").Export())79 ctx, cancel := context.WithCancel(context.Background())80 defer cancel()81 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})82 t.Run("RunOnce", func(t *testing.T) {83 err = vu.RunOnce()84 assert.NoError(t, err)85 assert.Equal(t, int64(1), vuc.Runtime.Get("counter").Export())86 })87 })88 })89 t.Run("Invalid", func(t *testing.T) {90 t.Parallel()91 _, err := getSimpleRunner(t, "/script.js", `blarg`)92 assert.EqualError(t, err, "ReferenceError: blarg is not defined\n\tat file:///script.js:1:1(0)\n")93 })94}95func TestRunnerGetDefaultGroup(t *testing.T) {96 t.Parallel()97 r1, err := getSimpleRunner(t, "/script.js", `exports.default = function() {};`)98 if assert.NoError(t, err) {99 assert.NotNil(t, r1.GetDefaultGroup())100 }101 registry := metrics.NewRegistry()102 builtinMetrics := metrics.RegisterBuiltinMetrics(registry)103 r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{}, builtinMetrics, registry)104 if assert.NoError(t, err) {105 assert.NotNil(t, r2.GetDefaultGroup())106 }107}108func TestRunnerOptions(t *testing.T) {109 t.Parallel()110 r1, err := getSimpleRunner(t, "/script.js", `exports.default = function() {};`)111 require.NoError(t, err)112 registry := metrics.NewRegistry()113 builtinMetrics := metrics.RegisterBuiltinMetrics(registry)114 r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{}, builtinMetrics, registry)115 require.NoError(t, err)116 testdata := map[string]*Runner{"Source": r1, "Archive": r2}117 for name, r := range testdata {118 name, r := name, r119 t.Run(name, func(t *testing.T) {120 t.Parallel()121 assert.Equal(t, r.Bundle.Options, r.GetOptions())122 assert.Equal(t, null.NewBool(false, false), r.Bundle.Options.Paused)123 r.SetOptions(lib.Options{Paused: null.BoolFrom(true)})124 assert.Equal(t, r.Bundle.Options, r.GetOptions())125 assert.Equal(t, null.NewBool(true, true), r.Bundle.Options.Paused)126 r.SetOptions(lib.Options{Paused: null.BoolFrom(false)})127 assert.Equal(t, r.Bundle.Options, r.GetOptions())128 assert.Equal(t, null.NewBool(false, true), r.Bundle.Options.Paused)129 })130 }131}132func TestOptionsSettingToScript(t *testing.T) {133 t.Parallel()134 optionVariants := []string{135 "",136 "var options = null;",137 "var options = undefined;",138 "var options = {};",139 "var options = {teardownTimeout: '1s'};",140 }141 for i, variant := range optionVariants {142 variant := variant143 t.Run(fmt.Sprintf("Variant#%d", i), func(t *testing.T) {144 t.Parallel()145 data := variant + `146 exports.default = function() {147 if (!options) {148 throw new Error("Expected options to be defined!");149 }150 if (options.teardownTimeout != __ENV.expectedTeardownTimeout) {151 throw new Error("expected teardownTimeout to be " + __ENV.expectedTeardownTimeout + " but it was " + options.teardownTimeout);152 }153 };`154 r, err := getSimpleRunner(t, "/script.js", data,155 lib.RuntimeOptions{Env: map[string]string{"expectedTeardownTimeout": "4s"}})156 require.NoError(t, err)157 newOptions := lib.Options{TeardownTimeout: types.NullDurationFrom(4 * time.Second)}158 r.SetOptions(newOptions)159 require.Equal(t, newOptions, r.GetOptions())160 samples := make(chan stats.SampleContainer, 100)161 initVU, err := r.NewVU(1, 1, samples)162 if assert.NoError(t, err) {163 ctx, cancel := context.WithCancel(context.Background())164 defer cancel()165 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})166 err := vu.RunOnce()167 assert.NoError(t, err)168 }169 })170 }171}172func TestOptionsPropagationToScript(t *testing.T) {173 t.Parallel()174 data := `175 var options = { setupTimeout: "1s", myOption: "test" };176 exports.options = options;177 exports.default = function() {178 if (options.external) {179 throw new Error("Unexpected property external!");180 }181 if (options.myOption != "test") {182 throw new Error("expected myOption to remain unchanged but it was '" + options.myOption + "'");183 }184 if (options.setupTimeout != __ENV.expectedSetupTimeout) {185 throw new Error("expected setupTimeout to be " + __ENV.expectedSetupTimeout + " but it was " + options.setupTimeout);186 }187 };`188 expScriptOptions := lib.Options{SetupTimeout: types.NullDurationFrom(1 * time.Second)}189 r1, err := getSimpleRunner(t, "/script.js", data,190 lib.RuntimeOptions{Env: map[string]string{"expectedSetupTimeout": "1s"}})191 require.NoError(t, err)192 require.Equal(t, expScriptOptions, r1.GetOptions())193 registry := metrics.NewRegistry()194 builtinMetrics := metrics.RegisterBuiltinMetrics(registry)195 r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{Env: map[string]string{"expectedSetupTimeout": "3s"}}, builtinMetrics, registry)196 require.NoError(t, err)197 require.Equal(t, expScriptOptions, r2.GetOptions())198 newOptions := lib.Options{SetupTimeout: types.NullDurationFrom(3 * time.Second)}199 require.NoError(t, r2.SetOptions(newOptions))200 require.Equal(t, newOptions, r2.GetOptions())201 testdata := map[string]*Runner{"Source": r1, "Archive": r2}202 for name, r := range testdata {203 r := r204 t.Run(name, func(t *testing.T) {205 t.Parallel()206 samples := make(chan stats.SampleContainer, 100)207 initVU, err := r.NewVU(1, 1, samples)208 if assert.NoError(t, err) {209 ctx, cancel := context.WithCancel(context.Background())210 defer cancel()211 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})212 err := vu.RunOnce()213 assert.NoError(t, err)214 }215 })216 }217}218func TestMetricName(t *testing.T) {219 t.Parallel()220 script := `221 var Counter = require("k6/metrics").Counter;222 var myCounter = new Counter("not ok name @");223 exports.default = function(data) {224 myCounter.add(1);225 }226 `227 _, err := getSimpleRunner(t, "/script.js", script)228 require.Error(t, err)229}230func TestSetupDataIsolation(t *testing.T) {231 t.Parallel()232 script := `233 var Counter = require("k6/metrics").Counter;234 exports.options = {235 scenarios: {236 shared_iters: {237 executor: "shared-iterations",238 vus: 5,239 iterations: 500,240 },241 },242 teardownTimeout: "5s",243 setupTimeout: "5s",244 };245 var myCounter = new Counter("mycounter");246 exports.setup = function() {247 return { v: 0 };248 }249 exports.default = function(data) {250 if (data.v !== __ITER) {251 throw new Error("default: wrong data for iter " + __ITER + ": " + JSON.stringify(data));252 }253 data.v += 1;254 myCounter.add(1);255 }256 exports.teardown = function(data) {257 if (data.v !== 0) {258 throw new Error("teardown: wrong data: " + data.v);259 }260 myCounter.add(1);261 }262 `263 runner, err := getSimpleRunner(t, "/script.js", script)264 require.NoError(t, err)265 options := runner.GetOptions()266 require.Empty(t, options.Validate())267 execScheduler, err := local.NewExecutionScheduler(runner, testutils.NewLogger(t))268 require.NoError(t, err)269 mockOutput := mockoutput.New()270 registry := metrics.NewRegistry()271 builtinMetrics := metrics.RegisterBuiltinMetrics(registry)272 engine, err := core.NewEngine(273 execScheduler, options, lib.RuntimeOptions{}, []output.Output{mockOutput}, testutils.NewLogger(t), builtinMetrics,274 )275 require.NoError(t, err)276 ctx, cancel := context.WithCancel(context.Background())277 run, wait, err := engine.Init(ctx, ctx)278 require.NoError(t, err)279 require.Empty(t, runner.defaultGroup.Groups)280 errC := make(chan error)281 go func() { errC <- run() }()282 select {283 case <-time.After(10 * time.Second):284 cancel()285 t.Fatal("Test timed out")286 case err := <-errC:287 cancel()288 require.NoError(t, err)289 wait()290 require.False(t, engine.IsTainted())291 }292 require.Contains(t, runner.defaultGroup.Groups, "setup")293 require.Contains(t, runner.defaultGroup.Groups, "teardown")294 var count int295 for _, s := range mockOutput.Samples {296 if s.Metric.Name == "mycounter" {297 count += int(s.Value)298 }299 }300 require.Equal(t, 501, count, "mycounter should be the number of iterations + 1 for the teardown")301}302func testSetupDataHelper(t *testing.T, data string) {303 t.Helper()304 expScriptOptions := lib.Options{305 SetupTimeout: types.NullDurationFrom(1 * time.Second),306 TeardownTimeout: types.NullDurationFrom(1 * time.Second),307 }308 r1, err := getSimpleRunner(t, "/script.js", data) // TODO fix this309 require.NoError(t, err)310 require.Equal(t, expScriptOptions, r1.GetOptions())311 testdata := map[string]*Runner{"Source": r1}312 for name, r := range testdata {313 r := r314 t.Run(name, func(t *testing.T) {315 t.Parallel()316 samples := make(chan stats.SampleContainer, 100)317 if !assert.NoError(t, r.Setup(context.Background(), samples)) {318 return319 }320 initVU, err := r.NewVU(1, 1, samples)321 if assert.NoError(t, err) {322 ctx, cancel := context.WithCancel(context.Background())323 defer cancel()324 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})325 err := vu.RunOnce()326 assert.NoError(t, err)327 }328 })329 }330}331func TestSetupDataReturnValue(t *testing.T) {332 t.Parallel()333 testSetupDataHelper(t, `334 exports.options = { setupTimeout: "1s", teardownTimeout: "1s" };335 exports.setup = function() {336 return 42;337 }338 exports.default = function(data) {339 if (data != 42) {340 throw new Error("default: wrong data: " + JSON.stringify(data))341 }342 };343 exports.teardown = function(data) {344 if (data != 42) {345 throw new Error("teardown: wrong data: " + JSON.stringify(data))346 }347 };`)348}349func TestSetupDataNoSetup(t *testing.T) {350 t.Parallel()351 testSetupDataHelper(t, `352 exports.options = { setupTimeout: "1s", teardownTimeout: "1s" };353 exports.default = function(data) {354 if (data !== undefined) {355 throw new Error("default: wrong data: " + JSON.stringify(data))356 }357 };358 exports.teardown = function(data) {359 if (data !== undefined) {360 console.log(data);361 throw new Error("teardown: wrong data: " + JSON.stringify(data))362 }363 };`)364}365func TestConsoleInInitContext(t *testing.T) {366 t.Parallel()367 r1, err := getSimpleRunner(t, "/script.js", `368 console.log("1");369 exports.default = function(data) {370 };371 `)372 require.NoError(t, err)373 testdata := map[string]*Runner{"Source": r1}374 for name, r := range testdata {375 r := r376 t.Run(name, func(t *testing.T) {377 t.Parallel()378 samples := make(chan stats.SampleContainer, 100)379 initVU, err := r.NewVU(1, 1, samples)380 if assert.NoError(t, err) {381 ctx, cancel := context.WithCancel(context.Background())382 defer cancel()383 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})384 err := vu.RunOnce()385 assert.NoError(t, err)386 }387 })388 }389}390func TestSetupDataNoReturn(t *testing.T) {391 t.Parallel()392 testSetupDataHelper(t, `393 exports.options = { setupTimeout: "1s", teardownTimeout: "1s" };394 exports.setup = function() { }395 exports.default = function(data) {396 if (data !== undefined) {397 throw new Error("default: wrong data: " + JSON.stringify(data))398 }399 };400 exports.teardown = function(data) {401 if (data !== undefined) {402 throw new Error("teardown: wrong data: " + JSON.stringify(data))403 }404 };`)405}406func TestRunnerIntegrationImports(t *testing.T) {407 t.Parallel()408 t.Run("Modules", func(t *testing.T) {409 t.Parallel()410 modules := []string{411 "k6",412 "k6/http",413 "k6/metrics",414 "k6/html",415 }416 rtOpts := lib.RuntimeOptions{CompatibilityMode: null.StringFrom("extended")}417 for _, mod := range modules {418 mod := mod419 t.Run(mod, func(t *testing.T) {420 t.Run("Source", func(t *testing.T) {421 _, err := getSimpleRunner(t, "/script.js", fmt.Sprintf(`import "%s"; exports.default = function() {}`, mod), rtOpts)422 assert.NoError(t, err)423 })424 })425 }426 })427 t.Run("Files", func(t *testing.T) {428 t.Parallel()429 testdata := map[string]struct{ filename, path string }{430 "Absolute": {"/path/script.js", "/path/to/lib.js"},431 "Relative": {"/path/script.js", "./to/lib.js"},432 "Adjacent": {"/path/to/script.js", "./lib.js"},433 "STDIN-Absolute": {"-", "/path/to/lib.js"},434 "STDIN-Relative": {"-", "./path/to/lib.js"},435 }436 for name, data := range testdata {437 name, data := name, data438 t.Run(name, func(t *testing.T) {439 t.Parallel()440 fs := afero.NewMemMapFs()441 require.NoError(t, fs.MkdirAll("/path/to", 0o755))442 require.NoError(t, afero.WriteFile(fs, "/path/to/lib.js", []byte(`exports.default = "hi!";`), 0o644))443 r1, err := getSimpleRunner(t, data.filename, fmt.Sprintf(`444 var hi = require("%s").default;445 exports.default = function() {446 if (hi != "hi!") { throw new Error("incorrect value"); }447 }`, data.path), fs)448 require.NoError(t, err)449 registry := metrics.NewRegistry()450 builtinMetrics := metrics.RegisterBuiltinMetrics(registry)451 r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{}, builtinMetrics, registry)452 require.NoError(t, err)453 testdata := map[string]*Runner{"Source": r1, "Archive": r2}454 for name, r := range testdata {455 r := r456 t.Run(name, func(t *testing.T) {457 initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))458 require.NoError(t, err)459 ctx, cancel := context.WithCancel(context.Background())460 defer cancel()461 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})462 err = vu.RunOnce()463 require.NoError(t, err)464 })465 }466 })467 }468 })469}470func TestVURunContext(t *testing.T) {471 t.Parallel()472 r1, err := getSimpleRunner(t, "/script.js", `473 exports.options = { vus: 10 };474 exports.default = function() { fn(); }475 `)476 require.NoError(t, err)477 r1.SetOptions(r1.GetOptions().Apply(lib.Options{Throw: null.BoolFrom(true)}))478 registry := metrics.NewRegistry()479 builtinMetrics := metrics.RegisterBuiltinMetrics(registry)480 r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{}, builtinMetrics, registry)481 require.NoError(t, err)482 testdata := map[string]*Runner{"Source": r1, "Archive": r2}483 for name, r := range testdata {484 r := r485 t.Run(name, func(t *testing.T) {486 t.Parallel()487 vu, err := r.newVU(1, 1, make(chan stats.SampleContainer, 100))488 require.NoError(t, err)489 fnCalled := false490 vu.Runtime.Set("fn", func() {491 fnCalled = true492 assert.Equal(t, vu.Runtime, common.GetRuntime(*vu.Context), "incorrect runtime in context")493 assert.Nil(t, common.GetInitEnv(*vu.Context)) // shouldn't get this in the vu context494 state := lib.GetState(*vu.Context)495 if assert.NotNil(t, state) {496 assert.Equal(t, null.IntFrom(10), state.Options.VUs)497 assert.Equal(t, null.BoolFrom(true), state.Options.Throw)498 assert.NotNil(t, state.Logger)499 assert.Equal(t, r.GetDefaultGroup(), state.Group)500 assert.Equal(t, vu.Transport, state.Transport)501 }502 })503 ctx, cancel := context.WithCancel(context.Background())504 defer cancel()505 activeVU := vu.Activate(&lib.VUActivationParams{RunContext: ctx})506 err = activeVU.RunOnce()507 assert.NoError(t, err)508 assert.True(t, fnCalled, "fn() not called")509 })510 }511}512func TestVURunInterrupt(t *testing.T) {513 t.Parallel()514 r1, err := getSimpleRunner(t, "/script.js", `515 exports.default = function() { while(true) {} }516 `)517 require.NoError(t, err)518 require.NoError(t, r1.SetOptions(lib.Options{Throw: null.BoolFrom(true)}))519 registry := metrics.NewRegistry()520 builtinMetrics := metrics.RegisterBuiltinMetrics(registry)521 r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{}, builtinMetrics, registry)522 require.NoError(t, err)523 testdata := map[string]*Runner{"Source": r1, "Archive": r2}524 for name, r := range testdata {525 name, r := name, r526 t.Run(name, func(t *testing.T) {527 t.Parallel()528 samples := make(chan stats.SampleContainer, 100)529 defer close(samples)530 go func() {531 for range samples {532 }533 }()534 vu, err := r.newVU(1, 1, samples)535 require.NoError(t, err)536 ctx, cancel := context.WithTimeout(context.Background(), 20*time.Millisecond)537 defer cancel()538 activeVU := vu.Activate(&lib.VUActivationParams{RunContext: ctx})539 err = activeVU.RunOnce()540 assert.Error(t, err)541 assert.Contains(t, err.Error(), "context canceled")542 })543 }544}545func TestVURunInterruptDoesntPanic(t *testing.T) {546 t.Parallel()547 r1, err := getSimpleRunner(t, "/script.js", `548 exports.default = function() { while(true) {} }549 `)550 require.NoError(t, err)551 require.NoError(t, r1.SetOptions(lib.Options{Throw: null.BoolFrom(true)}))552 registry := metrics.NewRegistry()553 builtinMetrics := metrics.RegisterBuiltinMetrics(registry)554 r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{}, builtinMetrics, registry)555 require.NoError(t, err)556 testdata := map[string]*Runner{"Source": r1, "Archive": r2}557 for name, r := range testdata {558 r := r559 t.Run(name, func(t *testing.T) {560 t.Parallel()561 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)562 defer cancel()563 samples := make(chan stats.SampleContainer, 100)564 defer close(samples)565 go func() {566 for range samples {567 }568 }()569 var wg sync.WaitGroup570 initVU, err := r.newVU(1, 1, samples)571 require.NoError(t, err)572 for i := 0; i < 1000; i++ {573 wg.Add(1)574 newCtx, newCancel := context.WithCancel(ctx)575 vu := initVU.Activate(&lib.VUActivationParams{576 RunContext: newCtx,577 DeactivateCallback: func(_ lib.InitializedVU) { wg.Done() },578 })579 ch := make(chan struct{})580 go func() {581 close(ch)582 vuErr := vu.RunOnce()583 assert.Error(t, vuErr)584 assert.Contains(t, vuErr.Error(), "context canceled")585 }()586 <-ch587 time.Sleep(time.Millisecond * 1) // NOTE: increase this in case of problems ;)588 newCancel()589 wg.Wait()590 }591 })592 }593}594func TestVUIntegrationGroups(t *testing.T) {595 t.Parallel()596 r1, err := getSimpleRunner(t, "/script.js", `597 var group = require("k6").group;598 exports.default = function() {599 fnOuter();600 group("my group", function() {601 fnInner();602 group("nested group", function() {603 fnNested();604 })605 });606 }607 `)608 require.NoError(t, err)609 registry := metrics.NewRegistry()610 builtinMetrics := metrics.RegisterBuiltinMetrics(registry)611 r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{}, builtinMetrics, registry)612 require.NoError(t, err)613 testdata := map[string]*Runner{"Source": r1, "Archive": r2}614 for name, r := range testdata {615 r := r616 t.Run(name, func(t *testing.T) {617 t.Parallel()618 vu, err := r.newVU(1, 1, make(chan stats.SampleContainer, 100))619 require.NoError(t, err)620 fnOuterCalled := false621 fnInnerCalled := false622 fnNestedCalled := false623 vu.Runtime.Set("fnOuter", func() {624 fnOuterCalled = true625 assert.Equal(t, r.GetDefaultGroup(), lib.GetState(*vu.Context).Group)626 })627 vu.Runtime.Set("fnInner", func() {628 fnInnerCalled = true629 g := lib.GetState(*vu.Context).Group630 assert.Equal(t, "my group", g.Name)631 assert.Equal(t, r.GetDefaultGroup(), g.Parent)632 })633 vu.Runtime.Set("fnNested", func() {634 fnNestedCalled = true635 g := lib.GetState(*vu.Context).Group636 assert.Equal(t, "nested group", g.Name)637 assert.Equal(t, "my group", g.Parent.Name)638 assert.Equal(t, r.GetDefaultGroup(), g.Parent.Parent)639 })640 ctx, cancel := context.WithCancel(context.Background())641 defer cancel()642 activeVU := vu.Activate(&lib.VUActivationParams{RunContext: ctx})643 err = activeVU.RunOnce()644 assert.NoError(t, err)645 assert.True(t, fnOuterCalled, "fnOuter() not called")646 assert.True(t, fnInnerCalled, "fnInner() not called")647 assert.True(t, fnNestedCalled, "fnNested() not called")648 })649 }650}651func TestVUIntegrationMetrics(t *testing.T) {652 t.Parallel()653 r1, err := getSimpleRunner(t, "/script.js", `654 var group = require("k6").group;655 var Trend = require("k6/metrics").Trend;656 var myMetric = new Trend("my_metric");657 exports.default = function() { myMetric.add(5); }658 `)659 require.NoError(t, err)660 registry := metrics.NewRegistry()661 builtinMetrics := metrics.RegisterBuiltinMetrics(registry)662 r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{}, builtinMetrics, registry)663 require.NoError(t, err)664 testdata := map[string]*Runner{"Source": r1, "Archive": r2}665 for name, r := range testdata {666 r := r667 t.Run(name, func(t *testing.T) {668 t.Parallel()669 samples := make(chan stats.SampleContainer, 100)670 defer close(samples)671 vu, err := r.newVU(1, 1, samples)672 require.NoError(t, err)673 ctx, cancel := context.WithCancel(context.Background())674 defer cancel()675 activeVU := vu.Activate(&lib.VUActivationParams{RunContext: ctx})676 err = activeVU.RunOnce()677 assert.NoError(t, err)678 sampleCount := 0679 for i, sampleC := range stats.GetBufferedSamples(samples) {680 for j, s := range sampleC.GetSamples() {681 sampleCount++682 switch i + j {683 case 0:684 assert.Equal(t, 5.0, s.Value)685 assert.Equal(t, "my_metric", s.Metric.Name)686 assert.Equal(t, stats.Trend, s.Metric.Type)687 case 1:688 assert.Equal(t, 0.0, s.Value)689 assert.Equal(t, builtinMetrics.DataSent, s.Metric, "`data_sent` sample is before `data_received` and `iteration_duration`")690 case 2:691 assert.Equal(t, 0.0, s.Value)692 assert.Equal(t, builtinMetrics.DataReceived, s.Metric, "`data_received` sample is after `data_received`")693 case 3:694 assert.Equal(t, builtinMetrics.IterationDuration, s.Metric, "`iteration-duration` sample is after `data_received`")695 case 4:696 assert.Equal(t, builtinMetrics.Iterations, s.Metric, "`iterations` sample is after `iteration_duration`")697 assert.Equal(t, float64(1), s.Value)698 }699 }700 }701 assert.Equal(t, sampleCount, 5)702 })703 }704}705func TestVUIntegrationInsecureRequests(t *testing.T) {706 t.Parallel()707 testdata := map[string]struct {708 opts lib.Options709 errMsg string710 }{711 "Null": {712 lib.Options{},713 "x509: certificate has expired or is not yet valid",714 },715 "False": {716 lib.Options{InsecureSkipTLSVerify: null.BoolFrom(false)},717 "x509: certificate has expired or is not yet valid",718 },719 "True": {720 lib.Options{InsecureSkipTLSVerify: null.BoolFrom(true)},721 "",722 },723 }724 for name, data := range testdata {725 data := data726 t.Run(name, func(t *testing.T) {727 t.Parallel()728 r1, err := getSimpleRunner(t, "/script.js", `729 var http = require("k6/http");;730 exports.default = function() { http.get("https://expired.badssl.com/"); }731 `)732 require.NoError(t, err)733 require.NoError(t, r1.SetOptions(lib.Options{Throw: null.BoolFrom(true)}.Apply(data.opts)))734 registry := metrics.NewRegistry()735 builtinMetrics := metrics.RegisterBuiltinMetrics(registry)736 r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{}, builtinMetrics, registry)737 require.NoError(t, err)738 runners := map[string]*Runner{"Source": r1, "Archive": r2}739 for name, r := range runners {740 r := r741 t.Run(name, func(t *testing.T) {742 t.Parallel()743 r.Logger, _ = logtest.NewNullLogger()744 initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))745 require.NoError(t, err)746 ctx, cancel := context.WithCancel(context.Background())747 defer cancel()748 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})749 err = vu.RunOnce()750 if data.errMsg != "" {751 require.Error(t, err)752 assert.Contains(t, err.Error(), data.errMsg)753 } else {754 assert.NoError(t, err)755 }756 })757 }758 })759 }760}761func TestVUIntegrationBlacklistOption(t *testing.T) {762 t.Parallel()763 r1, err := getSimpleRunner(t, "/script.js", `764 var http = require("k6/http");;765 exports.default = function() { http.get("http://10.1.2.3/"); }766 `)767 require.NoError(t, err)768 cidr, err := lib.ParseCIDR("10.0.0.0/8")769 require.NoError(t, err)770 require.NoError(t, r1.SetOptions(lib.Options{771 Throw: null.BoolFrom(true),772 BlacklistIPs: []*lib.IPNet{cidr},773 }))774 registry := metrics.NewRegistry()775 builtinMetrics := metrics.RegisterBuiltinMetrics(registry)776 r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{}, builtinMetrics, registry)777 require.NoError(t, err)778 runners := map[string]*Runner{"Source": r1, "Archive": r2}779 for name, r := range runners {780 r := r781 t.Run(name, func(t *testing.T) {782 t.Parallel()783 initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))784 require.NoError(t, err)785 ctx, cancel := context.WithCancel(context.Background())786 defer cancel()787 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})788 err = vu.RunOnce()789 require.Error(t, err)790 assert.Contains(t, err.Error(), "IP (10.1.2.3) is in a blacklisted range (10.0.0.0/8)")791 })792 }793}794func TestVUIntegrationBlacklistScript(t *testing.T) {795 t.Parallel()796 r1, err := getSimpleRunner(t, "/script.js", `797 var http = require("k6/http");;798 exports.options = {799 throw: true,800 blacklistIPs: ["10.0.0.0/8"],801 };802 exports.default = function() { http.get("http://10.1.2.3/"); }803 `)804 require.NoError(t, err)805 registry := metrics.NewRegistry()806 builtinMetrics := metrics.RegisterBuiltinMetrics(registry)807 r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{}, builtinMetrics, registry)808 require.NoError(t, err)809 runners := map[string]*Runner{"Source": r1, "Archive": r2}810 for name, r := range runners {811 r := r812 t.Run(name, func(t *testing.T) {813 t.Parallel()814 initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))815 require.NoError(t, err)816 ctx, cancel := context.WithCancel(context.Background())817 defer cancel()818 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})819 err = vu.RunOnce()820 require.Error(t, err)821 assert.Contains(t, err.Error(), "IP (10.1.2.3) is in a blacklisted range (10.0.0.0/8)")822 })823 }824}825func TestVUIntegrationBlockHostnamesOption(t *testing.T) {826 t.Parallel()827 r1, err := getSimpleRunner(t, "/script.js", `828 var http = require("k6/http");829 exports.default = function() { http.get("https://k6.io/"); }830 `)831 require.NoError(t, err)832 hostnames, err := types.NewNullHostnameTrie([]string{"*.io"})833 require.NoError(t, err)834 require.NoError(t, r1.SetOptions(lib.Options{835 Throw: null.BoolFrom(true),836 BlockedHostnames: hostnames,837 }))838 registry := metrics.NewRegistry()839 builtinMetrics := metrics.RegisterBuiltinMetrics(registry)840 r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{}, builtinMetrics, registry)841 require.NoError(t, err)842 runners := map[string]*Runner{"Source": r1, "Archive": r2}843 for name, r := range runners {844 r := r845 t.Run(name, func(t *testing.T) {846 t.Parallel()847 initVu, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))848 require.NoError(t, err)849 vu := initVu.Activate(&lib.VUActivationParams{RunContext: context.Background()})850 err = vu.RunOnce()851 require.Error(t, err)852 assert.Contains(t, err.Error(), "hostname (k6.io) is in a blocked pattern (*.io)")853 })854 }855}856func TestVUIntegrationBlockHostnamesScript(t *testing.T) {857 t.Parallel()858 r1, err := getSimpleRunner(t, "/script.js", `859 var http = require("k6/http");860 exports.options = {861 throw: true,862 blockHostnames: ["*.io"],863 };864 exports.default = function() { http.get("https://k6.io/"); }865 `)866 require.NoError(t, err)867 registry := metrics.NewRegistry()868 builtinMetrics := metrics.RegisterBuiltinMetrics(registry)869 r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{}, builtinMetrics, registry)870 require.NoError(t, err)871 runners := map[string]*Runner{"Source": r1, "Archive": r2}872 for name, r := range runners {873 r := r874 t.Run(name, func(t *testing.T) {875 t.Parallel()876 initVu, err := r.NewVU(0, 0, make(chan stats.SampleContainer, 100))877 require.NoError(t, err)878 vu := initVu.Activate(&lib.VUActivationParams{RunContext: context.Background()})879 err = vu.RunOnce()880 require.Error(t, err)881 assert.Contains(t, err.Error(), "hostname (k6.io) is in a blocked pattern (*.io)")882 })883 }884}885func TestVUIntegrationHosts(t *testing.T) {886 t.Parallel()887 tb := httpmultibin.NewHTTPMultiBin(t)888 r1, err := getSimpleRunner(t, "/script.js",889 tb.Replacer.Replace(`890 var k6 = require("k6");891 var check = k6.check;892 var fail = k6.fail;893 var http = require("k6/http");;894 exports.default = function() {895 var res = http.get("http://test.loadimpact.com:HTTPBIN_PORT/");896 check(res, {897 "is correct IP": function(r) { return r.remote_ip === "127.0.0.1" }898 }) || fail("failed to override dns");899 }900 `))901 require.NoError(t, err)902 r1.SetOptions(lib.Options{903 Throw: null.BoolFrom(true),904 Hosts: map[string]*lib.HostAddress{905 "test.loadimpact.com": {IP: net.ParseIP("127.0.0.1")},906 },907 })908 registry := metrics.NewRegistry()909 builtinMetrics := metrics.RegisterBuiltinMetrics(registry)910 r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{}, builtinMetrics, registry)911 require.NoError(t, err)912 runners := map[string]*Runner{"Source": r1, "Archive": r2}913 for name, r := range runners {914 r := r915 t.Run(name, func(t *testing.T) {916 t.Parallel()917 initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))918 require.NoError(t, err)919 ctx, cancel := context.WithCancel(context.Background())920 defer cancel()921 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})922 err = vu.RunOnce()923 require.NoError(t, err)924 })925 }926}927func TestVUIntegrationTLSConfig(t *testing.T) {928 t.Parallel()929 unsupportedVersionErrorMsg := "remote error: tls: handshake failure"930 for _, tag := range build.Default.ReleaseTags {931 if tag == "go1.12" {932 unsupportedVersionErrorMsg = "tls: no supported versions satisfy MinVersion and MaxVersion"933 break934 }935 }936 testdata := map[string]struct {937 opts lib.Options938 errMsg string939 }{940 "NullCipherSuites": {941 lib.Options{},942 "",943 },944 "SupportedCipherSuite": {945 lib.Options{TLSCipherSuites: &lib.TLSCipherSuites{tls.TLS_RSA_WITH_AES_128_GCM_SHA256}},946 "",947 },948 "UnsupportedCipherSuite": {949 lib.Options{TLSCipherSuites: &lib.TLSCipherSuites{tls.TLS_RSA_WITH_RC4_128_SHA}},950 "remote error: tls: handshake failure",951 },952 "NullVersion": {953 lib.Options{},954 "",955 },956 "SupportedVersion": {957 lib.Options{TLSVersion: &lib.TLSVersions{Min: tls.VersionTLS12, Max: tls.VersionTLS12}},958 "",959 },960 "UnsupportedVersion": {961 lib.Options{TLSVersion: &lib.TLSVersions{Min: tls.VersionSSL30, Max: tls.VersionSSL30}},962 unsupportedVersionErrorMsg,963 },964 }965 registry := metrics.NewRegistry()966 builtinMetrics := metrics.RegisterBuiltinMetrics(registry)967 for name, data := range testdata {968 data := data969 t.Run(name, func(t *testing.T) {970 t.Parallel()971 r1, err := getSimpleRunner(t, "/script.js", `972 var http = require("k6/http");;973 exports.default = function() { http.get("https://sha256.badssl.com/"); }974 `)975 require.NoError(t, err)976 require.NoError(t, r1.SetOptions(lib.Options{Throw: null.BoolFrom(true)}.Apply(data.opts)))977 r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{}, builtinMetrics, registry)978 require.NoError(t, err)979 runners := map[string]*Runner{"Source": r1, "Archive": r2}980 for name, r := range runners {981 r := r982 t.Run(name, func(t *testing.T) {983 t.Parallel()984 r.Logger, _ = logtest.NewNullLogger()985 initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))986 require.NoError(t, err)987 ctx, cancel := context.WithCancel(context.Background())988 defer cancel()989 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})990 err = vu.RunOnce()991 if data.errMsg != "" {992 require.Error(t, err)993 assert.Contains(t, err.Error(), data.errMsg)994 } else {995 assert.NoError(t, err)996 }997 })998 }999 })1000 }1001}1002func TestVUIntegrationOpenFunctionError(t *testing.T) {1003 t.Parallel()1004 r, err := getSimpleRunner(t, "/script.js", `1005 exports.default = function() { open("/tmp/foo") }1006 `)1007 assert.NoError(t, err)1008 initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))1009 assert.NoError(t, err)1010 ctx, cancel := context.WithCancel(context.Background())1011 defer cancel()1012 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})1013 err = vu.RunOnce()1014 assert.Error(t, err)1015 assert.Contains(t, err.Error(), "only available in the init stage")1016}1017func TestVUIntegrationOpenFunctionErrorWhenSneaky(t *testing.T) {1018 t.Parallel()1019 r, err := getSimpleRunner(t, "/script.js", `1020 var sneaky = open;1021 exports.default = function() { sneaky("/tmp/foo") }1022 `)1023 assert.NoError(t, err)1024 initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))1025 assert.NoError(t, err)1026 ctx, cancel := context.WithCancel(context.Background())1027 defer cancel()1028 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})1029 err = vu.RunOnce()1030 assert.Error(t, err)1031 assert.Contains(t, err.Error(), "only available in the init stage")1032}1033func TestVUIntegrationCookiesReset(t *testing.T) {1034 t.Parallel()1035 tb := httpmultibin.NewHTTPMultiBin(t)1036 r1, err := getSimpleRunner(t, "/script.js", tb.Replacer.Replace(`1037 var http = require("k6/http");;1038 exports.default = function() {1039 var url = "HTTPBIN_URL";1040 var preRes = http.get(url + "/cookies");1041 if (preRes.status != 200) { throw new Error("wrong status (pre): " + preRes.status); }1042 if (preRes.json().k1 || preRes.json().k2) {1043 throw new Error("cookies persisted: " + preRes.body);1044 }1045 var res = http.get(url + "/cookies/set?k2=v2&k1=v1");1046 if (res.status != 200) { throw new Error("wrong status: " + res.status) }1047 if (res.json().k1 != "v1" || res.json().k2 != "v2") {1048 throw new Error("wrong cookies: " + res.body);1049 }1050 }1051 `))1052 require.NoError(t, err)1053 r1.SetOptions(lib.Options{1054 Throw: null.BoolFrom(true),1055 MaxRedirects: null.IntFrom(10),1056 Hosts: tb.Dialer.Hosts,1057 })1058 registry := metrics.NewRegistry()1059 builtinMetrics := metrics.RegisterBuiltinMetrics(registry)1060 r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{}, builtinMetrics, registry)1061 require.NoError(t, err)1062 runners := map[string]*Runner{"Source": r1, "Archive": r2}1063 for name, r := range runners {1064 r := r1065 t.Run(name, func(t *testing.T) {1066 t.Parallel()1067 initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))1068 require.NoError(t, err)1069 ctx, cancel := context.WithCancel(context.Background())1070 defer cancel()1071 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})1072 for i := 0; i < 2; i++ {1073 err = vu.RunOnce()1074 assert.NoError(t, err)1075 }1076 })1077 }1078}1079func TestVUIntegrationCookiesNoReset(t *testing.T) {1080 t.Parallel()1081 tb := httpmultibin.NewHTTPMultiBin(t)1082 r1, err := getSimpleRunner(t, "/script.js", tb.Replacer.Replace(`1083 var http = require("k6/http");;1084 exports.default = function() {1085 var url = "HTTPBIN_URL";1086 if (__ITER == 0) {1087 var res = http.get(url + "/cookies/set?k2=v2&k1=v1");1088 if (res.status != 200) { throw new Error("wrong status: " + res.status) }1089 if (res.json().k1 != "v1" || res.json().k2 != "v2") {1090 throw new Error("wrong cookies: " + res.body);1091 }1092 }1093 if (__ITER == 1) {1094 var res = http.get(url + "/cookies");1095 if (res.status != 200) { throw new Error("wrong status (pre): " + res.status); }1096 if (res.json().k1 != "v1" || res.json().k2 != "v2") {1097 throw new Error("wrong cookies: " + res.body);1098 }1099 }1100 }1101 `))1102 require.NoError(t, err)1103 r1.SetOptions(lib.Options{1104 Throw: null.BoolFrom(true),1105 MaxRedirects: null.IntFrom(10),1106 Hosts: tb.Dialer.Hosts,1107 NoCookiesReset: null.BoolFrom(true),1108 })1109 registry := metrics.NewRegistry()1110 builtinMetrics := metrics.RegisterBuiltinMetrics(registry)1111 r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{}, builtinMetrics, registry)1112 require.NoError(t, err)1113 runners := map[string]*Runner{"Source": r1, "Archive": r2}1114 for name, r := range runners {1115 r := r1116 t.Run(name, func(t *testing.T) {1117 t.Parallel()1118 initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))1119 require.NoError(t, err)1120 ctx, cancel := context.WithCancel(context.Background())1121 defer cancel()1122 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})1123 err = vu.RunOnce()1124 assert.NoError(t, err)1125 err = vu.RunOnce()1126 assert.NoError(t, err)1127 })1128 }1129}1130func TestVUIntegrationVUID(t *testing.T) {1131 t.Parallel()1132 r1, err := getSimpleRunner(t, "/script.js", `1133 exports.default = function() {1134 if (__VU != 1234) { throw new Error("wrong __VU: " + __VU); }1135 }`,1136 )1137 require.NoError(t, err)1138 r1.SetOptions(lib.Options{Throw: null.BoolFrom(true)})1139 registry := metrics.NewRegistry()1140 builtinMetrics := metrics.RegisterBuiltinMetrics(registry)1141 r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{}, builtinMetrics, registry)1142 require.NoError(t, err)1143 runners := map[string]*Runner{"Source": r1, "Archive": r2}1144 for name, r := range runners {1145 r := r1146 t.Run(name, func(t *testing.T) {1147 t.Parallel()1148 initVU, err := r.NewVU(1234, 1234, make(chan stats.SampleContainer, 100))1149 require.NoError(t, err)1150 ctx, cancel := context.WithCancel(context.Background())1151 defer cancel()1152 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})1153 err = vu.RunOnce()1154 assert.NoError(t, err)1155 })1156 }1157}1158func TestVUIntegrationClientCerts(t *testing.T) {1159 t.Parallel()1160 clientCAPool := x509.NewCertPool()1161 assert.True(t, clientCAPool.AppendCertsFromPEM(1162 []byte("-----BEGIN CERTIFICATE-----\n"+1163 "MIIBYzCCAQqgAwIBAgIUMYw1pqZ1XhXdFG0S2ITXhfHBsWgwCgYIKoZIzj0EAwIw\n"+1164 "EDEOMAwGA1UEAxMFTXkgQ0EwHhcNMTcwODE1MTYxODAwWhcNMjIwODE0MTYxODAw\n"+1165 "WjAQMQ4wDAYDVQQDEwVNeSBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFWO\n"+1166 "fg4dgL8cdvjoSWDQFLBJxlbQFlZfOSyUR277a4g91BD07KWX+9ny+Q8WuUODog06\n"+1167 "xH1g8fc6zuaejllfzM6jQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD\n"+1168 "AQH/MB0GA1UdDgQWBBTeoSFylGCmyqj1X4sWez1r6hkhjDAKBggqhkjOPQQDAgNH\n"+1169 "ADBEAiAfuKi6u/BVXenCkgnU2sfXsYjel6rACuXEcx01yaaWuQIgXAtjrDisdlf4\n"+1170 "0ZdoIoYjNhDAXUtnyRBt+V6+rIklv/8=\n"+1171 "-----END CERTIFICATE-----"),1172 ))1173 serverCert, err := tls.X509KeyPair(1174 []byte("-----BEGIN CERTIFICATE-----\n"+1175 "MIIBxjCCAW2gAwIBAgIUICcYHG1bI28NZm676wHlMPxL+CEwCgYIKoZIzj0EAwIw\n"+1176 "EDEOMAwGA1UEAxMFTXkgQ0EwHhcNMTcwODE3MTQwNjAwWhcNMTgwODE3MTQwNjAw\n"+1177 "WjAZMRcwFQYDVQQDEw4xMjcuMC4wLjE6Njk2OTBZMBMGByqGSM49AgEGCCqGSM49\n"+1178 "AwEHA0IABCdD1IqowucJ5oUjGYCZZnXvgi7EMD4jD1osbOkzOFFnHSLRvdm6fcJu\n"+1179 "vPUcl4g8zUs466sC0AVUNpk21XbA/QajgZswgZgwDgYDVR0PAQH/BAQDAgWgMB0G\n"+1180 "A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1Ud\n"+1181 "DgQWBBTeAc8HY3sgGIV+fu/lY0OKr2Ho0jAfBgNVHSMEGDAWgBTeoSFylGCmyqj1\n"+1182 "X4sWez1r6hkhjDAZBgNVHREEEjAQgg4xMjcuMC4wLjE6Njk2OTAKBggqhkjOPQQD\n"+1183 "AgNHADBEAiAt3gC5FGQfSJXQ5DloXAOeJDFnKIL7d6xhftgPS5O08QIgRuAyysB8\n"+1184 "5JXHvvze5DMN/clHYptos9idVFc+weUZAUQ=\n"+1185 "-----END CERTIFICATE-----\n"+1186 "-----BEGIN CERTIFICATE-----\n"+1187 "MIIBYzCCAQqgAwIBAgIUMYw1pqZ1XhXdFG0S2ITXhfHBsWgwCgYIKoZIzj0EAwIw\n"+1188 "EDEOMAwGA1UEAxMFTXkgQ0EwHhcNMTcwODE1MTYxODAwWhcNMjIwODE0MTYxODAw\n"+1189 "WjAQMQ4wDAYDVQQDEwVNeSBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFWO\n"+1190 "fg4dgL8cdvjoSWDQFLBJxlbQFlZfOSyUR277a4g91BD07KWX+9ny+Q8WuUODog06\n"+1191 "xH1g8fc6zuaejllfzM6jQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD\n"+1192 "AQH/MB0GA1UdDgQWBBTeoSFylGCmyqj1X4sWez1r6hkhjDAKBggqhkjOPQQDAgNH\n"+1193 "ADBEAiAfuKi6u/BVXenCkgnU2sfXsYjel6rACuXEcx01yaaWuQIgXAtjrDisdlf4\n"+1194 "0ZdoIoYjNhDAXUtnyRBt+V6+rIklv/8=\n"+1195 "-----END CERTIFICATE-----"),1196 []byte("-----BEGIN EC PRIVATE KEY-----\n"+1197 "MHcCAQEEIKYptA4VtQ8UOKL+d1wkhl+51aPpvO+ppY62nLF9Z1w5oAoGCCqGSM49\n"+1198 "AwEHoUQDQgAEJ0PUiqjC5wnmhSMZgJlmde+CLsQwPiMPWixs6TM4UWcdItG92bp9\n"+1199 "wm689RyXiDzNSzjrqwLQBVQ2mTbVdsD9Bg==\n"+1200 "-----END EC PRIVATE KEY-----"),1201 )1202 require.NoError(t, err)1203 listener, err := tls.Listen("tcp", "127.0.0.1:0", &tls.Config{1204 Certificates: []tls.Certificate{serverCert},1205 ClientAuth: tls.RequireAndVerifyClientCert,1206 ClientCAs: clientCAPool,1207 })1208 require.NoError(t, err)1209 t.Cleanup(func() { _ = listener.Close() })1210 srv := &http.Server{1211 Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {1212 _, _ = fmt.Fprintf(w, "ok")1213 }),1214 ErrorLog: stdlog.New(ioutil.Discard, "", 0),1215 }1216 go func() { _ = srv.Serve(listener) }()1217 registry := metrics.NewRegistry()1218 builtinMetrics := metrics.RegisterBuiltinMetrics(registry)1219 t.Run("Unauthenticated", func(t *testing.T) {1220 t.Parallel()1221 r1, err := getSimpleRunner(t, "/script.js", fmt.Sprintf(`1222 var http = require("k6/http");;1223 exports.default = function() { http.get("https://%s")}1224 `, listener.Addr().String()))1225 require.NoError(t, err)1226 require.NoError(t, r1.SetOptions(lib.Options{1227 Throw: null.BoolFrom(true),1228 InsecureSkipTLSVerify: null.BoolFrom(true),1229 }))1230 r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{}, builtinMetrics, registry)1231 require.NoError(t, err)1232 runners := map[string]*Runner{"Source": r1, "Archive": r2}1233 for name, r := range runners {1234 r := r1235 t.Run(name, func(t *testing.T) {1236 t.Parallel()1237 r.Logger, _ = logtest.NewNullLogger()1238 initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))1239 if assert.NoError(t, err) {1240 ctx, cancel := context.WithCancel(context.Background())1241 defer cancel()1242 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})1243 err := vu.RunOnce()1244 require.Error(t, err)1245 assert.Contains(t, err.Error(), "remote error: tls: bad certificate")1246 }1247 })1248 }1249 })1250 t.Run("Authenticated", func(t *testing.T) {1251 t.Parallel()1252 r1, err := getSimpleRunner(t, "/script.js", fmt.Sprintf(`1253 var http = require("k6/http");;1254 exports.default = function() { http.get("https://%s")}1255 `, listener.Addr().String()))1256 require.NoError(t, err)1257 require.NoError(t, r1.SetOptions(lib.Options{1258 Throw: null.BoolFrom(true),1259 InsecureSkipTLSVerify: null.BoolFrom(true),1260 }))1261 require.NoError(t, r1.SetOptions(lib.Options{1262 TLSAuth: []*lib.TLSAuth{1263 {1264 TLSAuthFields: lib.TLSAuthFields{1265 Domains: []string{"127.0.0.1"},1266 Cert: "-----BEGIN CERTIFICATE-----\n" +1267 "MIIBoTCCAUigAwIBAgIUd6XedDxP+rGo+kq0APqHElGZzs4wCgYIKoZIzj0EAwIw\n" +1268 "EDEOMAwGA1UEAxMFTXkgQ0EwHhcNMTcwODE3MTUwNjAwWhcNMTgwODE3MTUwNjAw\n" +1269 "WjARMQ8wDQYDVQQDEwZjbGllbnQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATL\n" +1270 "mi/a1RVvk05FyrYmartbo/9cW+53DrQLW1twurII2q5ZfimdMX05A32uB3Ycoy/J\n" +1271 "x+w7Ifyd/YRw0zEc3NHQo38wfTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYI\n" +1272 "KwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFN2SR/TD\n" +1273 "yNW5DQWxZSkoXHQWsLY+MB8GA1UdIwQYMBaAFN6hIXKUYKbKqPVfixZ7PWvqGSGM\n" +1274 "MAoGCCqGSM49BAMCA0cAMEQCICtETmyOmupmg4w3tw59VYJyOBqRTxg6SK+rOQmq\n" +1275 "kE1VAiAUvsflDfmWBZ8EMPu46OhX6RX6MbvJ9NNvRco2G5ek1w==\n" +1276 "-----END CERTIFICATE-----",1277 Key: "-----BEGIN EC PRIVATE KEY-----\n" +1278 "MHcCAQEEIOrnhT05alCeQEX66HgnSHah/m5LazjJHLDawYRnhUtZoAoGCCqGSM49\n" +1279 "AwEHoUQDQgAEy5ov2tUVb5NORcq2Jmq7W6P/XFvudw60C1tbcLqyCNquWX4pnTF9\n" +1280 "OQN9rgd2HKMvycfsOyH8nf2EcNMxHNzR0A==\n" +1281 "-----END EC PRIVATE KEY-----",1282 },1283 },1284 },1285 }))1286 r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{}, builtinMetrics, registry)1287 require.NoError(t, err)1288 runners := map[string]*Runner{"Source": r1, "Archive": r2}1289 for name, r := range runners {1290 r := r1291 t.Run(name, func(t *testing.T) {1292 initVU, err := r.NewVU(1, 1, make(chan stats.SampleContainer, 100))1293 if assert.NoError(t, err) {1294 ctx, cancel := context.WithCancel(context.Background())1295 defer cancel()1296 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})1297 err := vu.RunOnce()1298 assert.NoError(t, err)1299 }1300 })1301 }1302 })1303}1304func TestHTTPRequestInInitContext(t *testing.T) {1305 t.Parallel()1306 tb := httpmultibin.NewHTTPMultiBin(t)1307 _, err := getSimpleRunner(t, "/script.js", tb.Replacer.Replace(`1308 var k6 = require("k6");1309 var check = k6.check;1310 var fail = k6.fail;1311 var http = require("k6/http");;1312 var res = http.get("HTTPBIN_URL/");1313 exports.default = function() {1314 console.log(test);1315 }1316 `))1317 if assert.Error(t, err) {1318 assert.Contains(1319 t,1320 err.Error(),1321 k6http.ErrHTTPForbiddenInInitContext.Error())1322 }1323}1324func TestInitContextForbidden(t *testing.T) {1325 t.Parallel()1326 table := [...][3]string{1327 {1328 "http.request",1329 `var http = require("k6/http");;1330 var res = http.get("HTTPBIN_URL");1331 exports.default = function() { console.log("p"); }`,1332 k6http.ErrHTTPForbiddenInInitContext.Error(),1333 },1334 {1335 "http.batch",1336 `var http = require("k6/http");;1337 var res = http.batch("HTTPBIN_URL/something", "HTTPBIN_URL/else");1338 exports.default = function() { console.log("p"); }`,1339 k6http.ErrBatchForbiddenInInitContext.Error(),1340 },1341 {1342 "http.cookieJar",1343 `var http = require("k6/http");;1344 var jar = http.cookieJar();1345 exports.default = function() { console.log("p"); }`,1346 k6http.ErrJarForbiddenInInitContext.Error(),1347 },1348 {1349 "check",1350 `var check = require("k6").check;1351 check("test", {'is test': function(test) { return test == "test"}})1352 exports.default = function() { console.log("p"); }`,1353 k6.ErrCheckInInitContext.Error(),1354 },1355 {1356 "group",1357 `var group = require("k6").group;1358 group("group1", function () { console.log("group1");})1359 exports.default = function() { console.log("p"); }`,1360 k6.ErrGroupInInitContext.Error(),1361 },1362 {1363 "ws",1364 `var ws = require("k6/ws");1365 var url = "ws://echo.websocket.org";1366 var params = { "tags": { "my_tag": "hello" } };1367 var response = ws.connect(url, params, function (socket) {1368 socket.on('open', function open() {1369 console.log('connected');1370 })1371 });1372 exports.default = function() { console.log("p"); }`,1373 ws.ErrWSInInitContext.Error(),1374 },1375 {1376 "metric",1377 `var Counter = require("k6/metrics").Counter;1378 var counter = Counter("myCounter");1379 counter.add(1);1380 exports.default = function() { console.log("p"); }`,1381 k6metrics.ErrMetricsAddInInitContext.Error(),1382 },1383 }1384 tb := httpmultibin.NewHTTPMultiBin(t)1385 for _, test := range table {1386 test := test1387 t.Run(test[0], func(t *testing.T) {1388 t.Parallel()1389 _, err := getSimpleRunner(t, "/script.js", tb.Replacer.Replace(test[1]))1390 if assert.Error(t, err) {1391 assert.Contains(1392 t,1393 err.Error(),1394 test[2])1395 }1396 })1397 }1398}1399func TestArchiveRunningIntegrity(t *testing.T) {1400 t.Parallel()1401 fs := afero.NewMemMapFs()1402 data := `1403 var fput = open("/home/somebody/test.json");1404 exports.options = { setupTimeout: "10s", teardownTimeout: "10s" };1405 exports.setup = function () {1406 return JSON.parse(fput);1407 }1408 exports.default = function(data) {1409 if (data != 42) {1410 throw new Error("incorrect answer " + data);1411 }1412 }1413 `1414 require.NoError(t, afero.WriteFile(fs, "/home/somebody/test.json", []byte(`42`), os.ModePerm))1415 require.NoError(t, afero.WriteFile(fs, "/script.js", []byte(data), os.ModePerm))1416 r1, err := getSimpleRunner(t, "/script.js", data, fs)1417 require.NoError(t, err)1418 buf := bytes.NewBuffer(nil)1419 require.NoError(t, r1.MakeArchive().Write(buf))1420 arc, err := lib.ReadArchive(buf)1421 require.NoError(t, err)1422 registry := metrics.NewRegistry()1423 builtinMetrics := metrics.RegisterBuiltinMetrics(registry)1424 r2, err := NewFromArchive(testutils.NewLogger(t), arc, lib.RuntimeOptions{}, builtinMetrics, registry)1425 require.NoError(t, err)1426 runners := map[string]*Runner{"Source": r1, "Archive": r2}1427 for name, r := range runners {1428 r := r1429 t.Run(name, func(t *testing.T) {1430 t.Parallel()1431 var err error1432 ch := make(chan stats.SampleContainer, 100)1433 err = r.Setup(context.Background(), ch)1434 require.NoError(t, err)1435 initVU, err := r.NewVU(1, 1, ch)1436 require.NoError(t, err)1437 ctx, cancel := context.WithCancel(context.Background())1438 defer cancel()1439 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})1440 err = vu.RunOnce()1441 require.NoError(t, err)1442 })1443 }1444}1445func TestArchiveNotPanicking(t *testing.T) {1446 t.Parallel()1447 fs := afero.NewMemMapFs()1448 require.NoError(t, afero.WriteFile(fs, "/non/existent", []byte(`42`), os.ModePerm))1449 r1, err := getSimpleRunner(t, "/script.js", `1450 var fput = open("/non/existent");1451 exports.default = function(data) {}1452 `, fs)1453 require.NoError(t, err)1454 arc := r1.MakeArchive()1455 arc.Filesystems = map[string]afero.Fs{"file": afero.NewMemMapFs()}1456 registry := metrics.NewRegistry()1457 builtinMetrics := metrics.RegisterBuiltinMetrics(registry)1458 r2, err := NewFromArchive(testutils.NewLogger(t), arc, lib.RuntimeOptions{}, builtinMetrics, registry)1459 // we do want this to error here as this is where we find out that a given file is not in the1460 // archive1461 require.Error(t, err)1462 require.Nil(t, r2)1463}1464func TestStuffNotPanicking(t *testing.T) {1465 t.Parallel()1466 tb := httpmultibin.NewHTTPMultiBin(t)1467 r, err := getSimpleRunner(t, "/script.js", tb.Replacer.Replace(`1468 var http = require("k6/http");1469 var ws = require("k6/ws");1470 var group = require("k6").group;1471 var parseHTML = require("k6/html").parseHTML;1472 exports.options = { iterations: 1, vus: 1 };1473 exports.default = function() {1474 var doc = parseHTML(http.get("HTTPBIN_URL/html").body);1475 var testCases = [1476 function() { return group()},1477 function() { return group("test")},1478 function() { return group("test", "wat")},1479 function() { return doc.find('p').each()},1480 function() { return doc.find('p').each("wat")},1481 function() { return doc.find('p').map()},1482 function() { return doc.find('p').map("wat")},1483 function() { return ws.connect("WSBIN_URL/ws-echo")},1484 ];1485 testCases.forEach(function(fn, idx) {1486 var hasException;1487 try {1488 fn();1489 hasException = false;1490 } catch (e) {1491 hasException = true;1492 }1493 if (hasException === false) {1494 throw new Error("Expected test case #" + idx + " to return an error");1495 } else if (hasException === undefined) {1496 throw new Error("Something strange happened with test case #" + idx);1497 }1498 });1499 }1500 `))1501 require.NoError(t, err)1502 ch := make(chan stats.SampleContainer, 1000)1503 initVU, err := r.NewVU(1, 1, ch)1504 require.NoError(t, err)1505 ctx, cancel := context.WithCancel(context.Background())1506 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})1507 errC := make(chan error)1508 go func() { errC <- vu.RunOnce() }()1509 select {1510 case <-time.After(15 * time.Second):1511 cancel()1512 t.Fatal("Test timed out")1513 case err := <-errC:1514 cancel()1515 require.NoError(t, err)1516 }1517}1518func TestPanicOnSimpleHTML(t *testing.T) {1519 t.Parallel()1520 r, err := getSimpleRunner(t, "/script.js", `1521 var parseHTML = require("k6/html").parseHTML;1522 exports.options = { iterations: 1, vus: 1 };1523 exports.default = function() {1524 var doc = parseHTML("<html>");1525 var o = doc.find(".something").slice(0, 4).toArray()1526 };1527 `)1528 require.NoError(t, err)1529 ch := make(chan stats.SampleContainer, 1000)1530 initVU, err := r.NewVU(1, 1, ch)1531 require.NoError(t, err)1532 ctx, cancel := context.WithCancel(context.Background())1533 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})1534 errC := make(chan error)1535 go func() { errC <- vu.RunOnce() }()1536 select {1537 case <-time.After(15 * time.Second):1538 cancel()1539 t.Fatal("Test timed out")1540 case err := <-errC:1541 cancel()1542 require.NoError(t, err)1543 }1544}1545func TestSystemTags(t *testing.T) {1546 t.Parallel()1547 tb := httpmultibin.NewHTTPMultiBin(t)1548 // Handle paths with custom logic1549 tb.Mux.HandleFunc("/wrong-redirect", func(w http.ResponseWriter, r *http.Request) {1550 w.Header().Add("Location", "%")1551 w.WriteHeader(http.StatusTemporaryRedirect)1552 })1553 httpURL, err := url.Parse(tb.ServerHTTP.URL)1554 require.NoError(t, err)1555 testedSystemTags := []struct{ tag, exec, expVal string }{1556 {"proto", "http_get", "HTTP/1.1"},1557 {"status", "http_get", "200"},1558 {"method", "http_get", "GET"},1559 {"url", "http_get", tb.ServerHTTP.URL},1560 {"url", "https_get", tb.ServerHTTPS.URL},1561 {"ip", "http_get", httpURL.Hostname()},1562 {"name", "http_get", tb.ServerHTTP.URL},1563 {"group", "http_get", ""},1564 {"vu", "http_get", "8"},1565 {"vu", "noop", "9"},1566 {"iter", "http_get", "0"},1567 {"iter", "noop", "0"},1568 {"tls_version", "https_get", "tls1.3"},1569 {"ocsp_status", "https_get", "unknown"},1570 {"error", "bad_url_get", `dial: connection refused`},1571 {"error_code", "bad_url_get", "1212"},1572 {"scenario", "http_get", "default"},1573 // TODO: add more tests1574 }1575 for num, tc := range testedSystemTags {1576 num, tc := num, tc1577 t.Run(fmt.Sprintf("TC %d with only %s", num, tc.tag), func(t *testing.T) {1578 t.Parallel()1579 samples := make(chan stats.SampleContainer, 100)1580 r, err := getSimpleRunner(t, "/script.js", tb.Replacer.Replace(`1581 var http = require("k6/http");1582 exports.http_get = function() {1583 http.get("HTTPBIN_IP_URL");1584 };1585 exports.https_get = function() {1586 http.get("HTTPSBIN_IP_URL");1587 };1588 exports.bad_url_get = function() {1589 http.get("http://127.0.0.1:1");1590 };1591 exports.noop = function() {};1592 `), lib.RuntimeOptions{CompatibilityMode: null.StringFrom("base")})1593 require.NoError(t, err)1594 require.NoError(t, r.SetOptions(r.GetOptions().Apply(lib.Options{1595 Throw: null.BoolFrom(false),1596 TLSVersion: &lib.TLSVersions{Max: tls.VersionTLS13},1597 SystemTags: stats.ToSystemTagSet([]string{tc.tag}),1598 InsecureSkipTLSVerify: null.BoolFrom(true),1599 })))1600 vu, err := r.NewVU(uint64(num), 0, samples)1601 require.NoError(t, err)1602 activeVU := vu.Activate(&lib.VUActivationParams{1603 RunContext: context.Background(),1604 Exec: tc.exec,1605 Scenario: "default",1606 })1607 require.NoError(t, activeVU.RunOnce())1608 bufSamples := stats.GetBufferedSamples(samples)1609 require.NotEmpty(t, bufSamples)1610 for _, sample := range bufSamples[0].GetSamples() {1611 assert.NotEmpty(t, sample.Tags)1612 for emittedTag, emittedVal := range sample.Tags.CloneTags() {1613 assert.Equal(t, tc.tag, emittedTag)1614 assert.Equal(t, tc.expVal, emittedVal)1615 }1616 }1617 })1618 }1619}1620func TestVUPanic(t *testing.T) {1621 t.Parallel()1622 r1, err := getSimpleRunner(t, "/script.js", `1623 var group = require("k6").group;1624 exports.default = function() {1625 group("panic here", function() {1626 if (__ITER == 0) {1627 panic("here we panic");1628 }1629 console.log("here we don't");1630 })1631 }`,1632 )1633 require.NoError(t, err)1634 registry := metrics.NewRegistry()1635 builtinMetrics := metrics.RegisterBuiltinMetrics(registry)1636 r2, err := NewFromArchive(testutils.NewLogger(t), r1.MakeArchive(), lib.RuntimeOptions{}, builtinMetrics, registry)1637 require.NoError(t, err)1638 runners := map[string]*Runner{"Source": r1, "Archive": r2}1639 for name, r := range runners {1640 r := r1641 t.Run(name, func(t *testing.T) {1642 t.Parallel()1643 initVU, err := r.NewVU(1, 1234, make(chan stats.SampleContainer, 100))1644 require.NoError(t, err)1645 logger := logrus.New()1646 logger.SetLevel(logrus.InfoLevel)1647 logger.Out = ioutil.Discard1648 hook := testutils.SimpleLogrusHook{1649 HookedLevels: []logrus.Level{logrus.InfoLevel, logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel},1650 }1651 logger.AddHook(&hook)1652 ctx, cancel := context.WithCancel(context.Background())1653 defer cancel()1654 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})1655 vu.(*ActiveVU).Runtime.Set("panic", func(str string) { panic(str) })1656 vu.(*ActiveVU).state.Logger = logger1657 vu.(*ActiveVU).Console.logger = logger.WithField("source", "console")1658 err = vu.RunOnce()1659 require.Error(t, err)1660 assert.Contains(t, err.Error(), "a panic occurred in VU code but was caught: here we panic")1661 entries := hook.Drain()1662 require.Len(t, entries, 1)1663 assert.Equal(t, logrus.ErrorLevel, entries[0].Level)1664 require.True(t, strings.HasPrefix(entries[0].Message, "panic: here we panic"))1665 // broken since goja@f3cfc97811c0b4d8337902c3e42fb2371ba1d524 see1666 // https://github.com/dop251/goja/issues/179#issuecomment-7835720201667 // require.True(t, strings.HasSuffix(entries[0].Message, "Goja stack:\nfile:///script.js:3:4(12)"))1668 err = vu.RunOnce()1669 assert.NoError(t, err)1670 entries = hook.Drain()1671 require.Len(t, entries, 1)1672 assert.Equal(t, logrus.InfoLevel, entries[0].Level)1673 require.Contains(t, entries[0].Message, "here we don't")1674 })1675 }1676}1677type multiFileTestCase struct {1678 fses map[string]afero.Fs1679 rtOpts lib.RuntimeOptions1680 cwd string1681 script string1682 expInitErr bool1683 expVUErr bool1684 samples chan stats.SampleContainer1685}1686func runMultiFileTestCase(t *testing.T, tc multiFileTestCase, tb *httpmultibin.HTTPMultiBin) {1687 t.Helper()1688 logger := testutils.NewLogger(t)1689 registry := metrics.NewRegistry()1690 builtinMetrics := metrics.RegisterBuiltinMetrics(registry)1691 runner, err := New(1692 logger,1693 &loader.SourceData{1694 URL: &url.URL{Path: tc.cwd + "/script.js", Scheme: "file"},1695 Data: []byte(tc.script),1696 },1697 tc.fses,1698 tc.rtOpts,1699 builtinMetrics,1700 registry,1701 )1702 if tc.expInitErr {1703 require.Error(t, err)1704 return1705 }1706 require.NoError(t, err)1707 options := runner.GetOptions()1708 require.Empty(t, options.Validate())1709 vu, err := runner.NewVU(1, 1, tc.samples)1710 require.NoError(t, err)1711 jsVU, ok := vu.(*VU)1712 require.True(t, ok)1713 jsVU.state.Dialer = tb.Dialer1714 jsVU.state.TLSConfig = tb.TLSClientConfig1715 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)1716 defer cancel()1717 activeVU := vu.Activate(&lib.VUActivationParams{RunContext: ctx})1718 err = activeVU.RunOnce()1719 if tc.expVUErr {1720 require.Error(t, err)1721 } else {1722 require.NoError(t, err)1723 }1724 arc := runner.MakeArchive()1725 runnerFromArc, err := NewFromArchive(logger, arc, tc.rtOpts, builtinMetrics, registry)1726 require.NoError(t, err)1727 vuFromArc, err := runnerFromArc.NewVU(2, 2, tc.samples)1728 require.NoError(t, err)1729 jsVUFromArc, ok := vuFromArc.(*VU)1730 require.True(t, ok)1731 jsVUFromArc.state.Dialer = tb.Dialer1732 jsVUFromArc.state.TLSConfig = tb.TLSClientConfig1733 activeVUFromArc := jsVUFromArc.Activate(&lib.VUActivationParams{RunContext: ctx})1734 err = activeVUFromArc.RunOnce()1735 if tc.expVUErr {1736 require.Error(t, err)1737 return1738 }1739 require.NoError(t, err)1740}1741func TestComplicatedFileImportsForGRPC(t *testing.T) {1742 t.Parallel()1743 tb := httpmultibin.NewHTTPMultiBin(t)1744 tb.GRPCStub.UnaryCallFunc = func(ctx context.Context, sreq *grpc_testing.SimpleRequest) (1745 *grpc_testing.SimpleResponse, error,1746 ) {1747 return &grpc_testing.SimpleResponse{1748 Username: "foo",1749 }, nil1750 }1751 fs := afero.NewMemMapFs()1752 protoFile, err := ioutil.ReadFile("../vendor/google.golang.org/grpc/test/grpc_testing/test.proto")1753 require.NoError(t, err)1754 require.NoError(t, afero.WriteFile(fs, "/path/to/service.proto", protoFile, 0644))1755 require.NoError(t, afero.WriteFile(fs, "/path/to/same-dir.proto", []byte(1756 `syntax = "proto3";package whatever;import "service.proto";`,1757 ), 0644))1758 require.NoError(t, afero.WriteFile(fs, "/path/subdir.proto", []byte(1759 `syntax = "proto3";package whatever;import "to/service.proto";`,1760 ), 0644))1761 require.NoError(t, afero.WriteFile(fs, "/path/to/abs.proto", []byte(1762 `syntax = "proto3";package whatever;import "/path/to/service.proto";`,1763 ), 0644))1764 grpcTestCase := func(expInitErr, expVUErr bool, cwd, loadCode string) multiFileTestCase {1765 script := tb.Replacer.Replace(fmt.Sprintf(`1766 var grpc = require('k6/net/grpc');1767 var client = new grpc.Client();1768 %s // load statements1769 exports.default = function() {1770 client.connect('GRPCBIN_ADDR', {timeout: '3s'});1771 var resp = client.invoke('grpc.testing.TestService/UnaryCall', {})1772 if (!resp.message || resp.error || resp.message.username !== 'foo') {1773 throw new Error('unexpected response message: ' + JSON.stringify(resp.message))1774 }1775 }1776 `, loadCode))1777 return multiFileTestCase{1778 fses: map[string]afero.Fs{"file": fs, "https": afero.NewMemMapFs()},1779 rtOpts: lib.RuntimeOptions{CompatibilityMode: null.NewString("base", true)},1780 samples: make(chan stats.SampleContainer, 100),1781 cwd: cwd, expInitErr: expInitErr, expVUErr: expVUErr, script: script,1782 }1783 }1784 testCases := []multiFileTestCase{1785 grpcTestCase(false, true, "/", `/* no grpc loads */`), // exp VU error with no proto files loaded1786 // Init errors when the protobuf file can't be loaded1787 grpcTestCase(true, false, "/", `client.load(null, 'service.proto');`),1788 grpcTestCase(true, false, "/", `client.load(null, '/wrong/path/to/service.proto');`),1789 grpcTestCase(true, false, "/", `client.load(['/', '/path/'], 'service.proto');`),1790 // Direct imports of service.proto1791 grpcTestCase(false, false, "/", `client.load(null, '/path/to/service.proto');`), // full path should be fine1792 grpcTestCase(false, false, "/path/to/", `client.load([], 'service.proto');`), // file name from same folder1793 grpcTestCase(false, false, "/", `client.load(['./path//to/'], 'service.proto');`),1794 grpcTestCase(false, false, "/path/", `client.load(['./to/'], 'service.proto');`),1795 grpcTestCase(false, false, "/whatever", `client.load(['/path/to/'], 'service.proto');`), // with import paths1796 grpcTestCase(false, false, "/path", `client.load(['/', '/path/to/'], 'service.proto');`), // with import paths1797 grpcTestCase(false, false, "/whatever", `client.load(['../path/to/'], 'service.proto');`),1798 // Import another file that imports "service.proto" directly1799 grpcTestCase(true, false, "/", `client.load([], '/path/to/same-dir.proto');`),1800 grpcTestCase(true, false, "/path/", `client.load([], 'to/same-dir.proto');`),1801 grpcTestCase(true, false, "/", `client.load(['/path/'], 'to/same-dir.proto');`),1802 grpcTestCase(false, false, "/path/to/", `client.load([], 'same-dir.proto');`),1803 grpcTestCase(false, false, "/", `client.load(['/path/to/'], 'same-dir.proto');`),1804 grpcTestCase(false, false, "/whatever", `client.load(['/other', '/path/to/'], 'same-dir.proto');`),1805 grpcTestCase(false, false, "/", `client.load(['./path//to/'], 'same-dir.proto');`),1806 grpcTestCase(false, false, "/path/", `client.load(['./to/'], 'same-dir.proto');`),1807 grpcTestCase(false, false, "/whatever", `client.load(['../path/to/'], 'same-dir.proto');`),1808 // Import another file that imports "to/service.proto" directly1809 grpcTestCase(true, false, "/", `client.load([], '/path/to/subdir.proto');`),1810 grpcTestCase(false, false, "/path/", `client.load([], 'subdir.proto');`),1811 grpcTestCase(false, false, "/", `client.load(['/path/'], 'subdir.proto');`),1812 grpcTestCase(false, false, "/", `client.load(['./path/'], 'subdir.proto');`),1813 grpcTestCase(false, false, "/whatever", `client.load(['/other', '/path/'], 'subdir.proto');`),1814 grpcTestCase(false, false, "/whatever", `client.load(['../other', '../path/'], 'subdir.proto');`),1815 // Import another file that imports "/path/to/service.proto" directly1816 grpcTestCase(true, false, "/", `client.load(['/path'], '/path/to/abs.proto');`),1817 grpcTestCase(false, false, "/", `client.load([], '/path/to/abs.proto');`),1818 grpcTestCase(false, false, "/whatever", `client.load(['/'], '/path/to/abs.proto');`),1819 }1820 for i, tc := range testCases {1821 i, tc := i, tc1822 t.Run(fmt.Sprintf("TestCase_%d", i), func(t *testing.T) {1823 t.Parallel()1824 t.Logf(1825 "CWD: %s, expInitErr: %t, expVUErr: %t, script injected with: `%s`",1826 tc.cwd, tc.expInitErr, tc.expVUErr, tc.script,1827 )1828 runMultiFileTestCase(t, tc, tb)1829 })1830 }1831}1832func TestMinIterationDurationIsCancellable(t *testing.T) {1833 t.Parallel()1834 r, err := getSimpleRunner(t, "/script.js", `1835 exports.options = { iterations: 1, vus: 1, minIterationDuration: '1m' };1836 exports.default = function() { /* do nothing */ };1837 `)1838 require.NoError(t, err)1839 ch := make(chan stats.SampleContainer, 1000)1840 initVU, err := r.NewVU(1, 1, ch)1841 require.NoError(t, err)1842 ctx, cancel := context.WithCancel(context.Background())1843 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})1844 errC := make(chan error)1845 go func() { errC <- vu.RunOnce() }()1846 time.Sleep(200 * time.Millisecond) // give it some time to actually start1847 cancel() // simulate the end of gracefulStop or a Ctrl+C event1848 select {1849 case <-time.After(3 * time.Second):1850 t.Fatal("Test timed out or minIterationDuration prevailed")1851 case err := <-errC:1852 require.NoError(t, err)1853 }1854}1855func TestExecutionInfo(t *testing.T) {1856 t.Parallel()1857 testCases := []struct {1858 name, script, expErr string1859 }{1860 {name: "vu_ok", script: `1861 var exec = require('k6/execution');1862 exports.default = function() {1863 if (exec.vu.idInInstance !== 1) throw new Error('unexpected VU ID: '+exec.vu.idInInstance);1864 if (exec.vu.idInTest !== 10) throw new Error('unexpected global VU ID: '+exec.vu.idInTest);1865 if (exec.vu.iterationInInstance !== 0) throw new Error('unexpected VU iteration: '+exec.vu.iterationInInstance);1866 if (exec.vu.iterationInScenario !== 0) throw new Error('unexpected scenario iteration: '+exec.vu.iterationInScenario);1867 }`},1868 {name: "vu_err", script: `1869 var exec = require('k6/execution');1870 exec.vu;1871 `, expErr: "getting VU information in the init context is not supported"},1872 {name: "scenario_ok", script: `1873 var exec = require('k6/execution');1874 var sleep = require('k6').sleep;1875 exports.default = function() {1876 var si = exec.scenario;1877 sleep(0.1);1878 if (si.name !== 'default') throw new Error('unexpected scenario name: '+si.name);1879 if (si.executor !== 'test-exec') throw new Error('unexpected executor: '+si.executor);1880 if (si.startTime > new Date().getTime()) throw new Error('unexpected startTime: '+si.startTime);1881 if (si.progress !== 0.1) throw new Error('unexpected progress: '+si.progress);1882 if (si.iterationInInstance !== 3) throw new Error('unexpected scenario local iteration: '+si.iterationInInstance);1883 if (si.iterationInTest !== 4) throw new Error('unexpected scenario local iteration: '+si.iterationInTest);1884 }`},1885 {name: "scenario_err", script: `1886 var exec = require('k6/execution');1887 exec.scenario;1888 `, expErr: "getting scenario information in the init context is not supported"},1889 {name: "test_ok", script: `1890 var exec = require('k6/execution');1891 exports.default = function() {1892 var ti = exec.instance;1893 if (ti.currentTestRunDuration !== 0) throw new Error('unexpected test duration: '+ti.currentTestRunDuration);1894 if (ti.vusActive !== 1) throw new Error('unexpected vusActive: '+ti.vusActive);1895 if (ti.vusInitialized !== 0) throw new Error('unexpected vusInitialized: '+ti.vusInitialized);1896 if (ti.iterationsCompleted !== 0) throw new Error('unexpected iterationsCompleted: '+ti.iterationsCompleted);1897 if (ti.iterationsInterrupted !== 0) throw new Error('unexpected iterationsInterrupted: '+ti.iterationsInterrupted);1898 }`},1899 {name: "test_err", script: `1900 var exec = require('k6/execution');1901 exec.instance;1902 `, expErr: "getting instance information in the init context is not supported"},1903 }1904 for _, tc := range testCases {1905 tc := tc1906 t.Run(tc.name, func(t *testing.T) {1907 t.Parallel()1908 r, err := getSimpleRunner(t, "/script.js", tc.script)1909 if tc.expErr != "" {1910 require.Error(t, err)1911 assert.Contains(t, err.Error(), tc.expErr)1912 return1913 }1914 require.NoError(t, err)1915 samples := make(chan stats.SampleContainer, 100)1916 initVU, err := r.NewVU(1, 10, samples)1917 require.NoError(t, err)1918 execScheduler, err := local.NewExecutionScheduler(r, testutils.NewLogger(t))1919 require.NoError(t, err)1920 ctx, cancel := context.WithCancel(context.Background())1921 defer cancel()1922 ctx = lib.WithExecutionState(ctx, execScheduler.GetState())1923 ctx = lib.WithScenarioState(ctx, &lib.ScenarioState{1924 Name: "default",1925 Executor: "test-exec",1926 StartTime: time.Now(),1927 ProgressFn: func() (float64, []string) {1928 return 0.1, nil1929 },1930 })1931 vu := initVU.Activate(&lib.VUActivationParams{1932 RunContext: ctx,1933 Exec: "default",1934 GetNextIterationCounters: func() (uint64, uint64) { return 3, 4 },1935 })1936 execState := execScheduler.GetState()1937 execState.ModCurrentlyActiveVUsCount(+1)1938 err = vu.RunOnce()1939 assert.NoError(t, err)1940 })1941 }1942}...

Full Screen

Full Screen

console_test.go

Source:console_test.go Github

copy

Full Screen

...124 r, err := getSimpleRunner(t, "/script.js", fmt.Sprintf(125 `exports.default = function() { console.log(%s); }`, tt.in))126 require.NoError(t, err)127 samples := make(chan metrics.SampleContainer, 100)128 initVU, err := r.newVU(1, 1, samples)129 assert.NoError(t, err)130 ctx, cancel := context.WithCancel(context.Background())131 defer cancel()132 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})133 logger := extractLogger(vu.(*ActiveVU).Console.logger)134 logger.Out = ioutil.Discard135 logger.Level = logrus.DebugLevel136 hook := logtest.NewLocal(logger)137 err = vu.RunOnce()138 assert.NoError(t, err)139 entry := hook.LastEntry()140 require.NotNil(t, entry, "nothing logged")141 assert.Equal(t, tt.expected, entry.Message)142 assert.Equal(t, logrus.Fields{"source": "console"}, entry.Data)143 })144 }145}146func TestConsoleLevels(t *testing.T) {147 t.Parallel()148 levels := map[string]logrus.Level{149 "log": logrus.InfoLevel,150 "debug": logrus.DebugLevel,151 "info": logrus.InfoLevel,152 "warn": logrus.WarnLevel,153 "error": logrus.ErrorLevel,154 }155 argsets := []struct {156 in string157 exp string158 }{159 {in: `"string"`, exp: "string"},160 {in: `{}`, exp: "{}"},161 {in: `{foo:"bar"}`, exp: `{"foo":"bar"}`},162 }163 for name, level := range levels {164 name, level := name, level165 t.Run(name, func(t *testing.T) {166 t.Parallel()167 for _, tt := range argsets {168 args, result := tt.in, tt.exp169 t.Run(args, func(t *testing.T) {170 t.Parallel()171 r, err := getSimpleRunner(t, "/script.js", fmt.Sprintf(172 `exports.default = function() { console.%s(%s); }`,173 name, args,174 ))175 assert.NoError(t, err)176 samples := make(chan metrics.SampleContainer, 100)177 initVU, err := r.newVU(1, 1, samples)178 assert.NoError(t, err)179 ctx, cancel := context.WithCancel(context.Background())180 defer cancel()181 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})182 logger := extractLogger(vu.(*ActiveVU).Console.logger)183 logger.Out = ioutil.Discard184 logger.Level = logrus.DebugLevel185 hook := logtest.NewLocal(logger)186 err = vu.RunOnce()187 assert.NoError(t, err)188 entry := hook.LastEntry()189 require.NotNil(t, entry, "nothing logged")190 assert.Equal(t, level, entry.Level)191 assert.Equal(t, result, entry.Message)192 assert.Equal(t, logrus.Fields{"source": "console"}, entry.Data)193 })194 }195 })196 }197}198func TestFileConsole(t *testing.T) {199 t.Parallel()200 var (201 levels = map[string]logrus.Level{202 "log": logrus.InfoLevel,203 "debug": logrus.DebugLevel,204 "info": logrus.InfoLevel,205 "warn": logrus.WarnLevel,206 "error": logrus.ErrorLevel,207 }208 argsets = map[string]struct {209 Message string210 Data logrus.Fields211 }{212 `"string"`: {Message: "string", Data: logrus.Fields{}},213 `"string","a","b"`: {Message: "string a b", Data: logrus.Fields{}},214 `"string",1,2`: {Message: "string 1 2", Data: logrus.Fields{}},215 `{}`: {Message: "{}", Data: logrus.Fields{}},216 }217 preExisting = map[string]bool{218 "log exists": false,219 "log doesn't exist": true,220 }221 preExistingText = "Prexisting file\n"222 )223 for name, level := range levels {224 t.Run(name, func(t *testing.T) {225 t.Parallel()226 for args, result := range argsets {227 t.Run(args, func(t *testing.T) {228 t.Parallel()229 // whether the file is existed before logging230 for msg, deleteFile := range preExisting {231 t.Run(msg, func(t *testing.T) {232 t.Parallel()233 f, err := ioutil.TempFile("", "")234 if err != nil {235 t.Fatalf("Couldn't create temporary file for testing: %s", err)236 }237 logFilename := f.Name()238 defer os.Remove(logFilename)239 // close it as we will want to reopen it and maybe remove it240 if deleteFile {241 f.Close()242 if err := os.Remove(logFilename); err != nil {243 t.Fatalf("Couldn't remove tempfile: %s", err)244 }245 } else {246 // TODO: handle case where the string was no written in full ?247 _, err = f.WriteString(preExistingText)248 _ = f.Close()249 if err != nil {250 t.Fatalf("Error while writing text to preexisting logfile: %s", err)251 }252 }253 r, err := getSimpleRunner(t, "/script",254 fmt.Sprintf(255 `exports.default = function() { console.%s(%s); }`,256 name, args,257 ))258 assert.NoError(t, err)259 err = r.SetOptions(lib.Options{260 ConsoleOutput: null.StringFrom(logFilename),261 })262 assert.NoError(t, err)263 samples := make(chan metrics.SampleContainer, 100)264 initVU, err := r.newVU(1, 1, samples)265 assert.NoError(t, err)266 ctx, cancel := context.WithCancel(context.Background())267 defer cancel()268 vu := initVU.Activate(&lib.VUActivationParams{RunContext: ctx})269 logger := extractLogger(vu.(*ActiveVU).Console.logger)270 logger.Level = logrus.DebugLevel271 hook := logtest.NewLocal(logger)272 err = vu.RunOnce()273 assert.NoError(t, err)274 // Test if the file was created.275 _, err = os.Stat(logFilename)276 assert.NoError(t, err)277 entry := hook.LastEntry()278 if assert.NotNil(t, entry, "nothing logged") {279 assert.Equal(t, level, entry.Level)280 assert.Equal(t, result.Message, entry.Message)281 data := result.Data282 if data == nil {...

Full Screen

Full Screen

initVU

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 r, err := lib.New(4 &lib.SourceData{5 Data: []byte(`6 import { sleep } from "k6";7 import { Counter } from "k6/metrics";8 import { initVU } from "./1.js";9 export let options = {10 { duration: "1s", target: 1 },11 };12 export default function () {13 sleep(1);14 };15 },16 testutils.NewLogger(t),17 lib.RuntimeOptions{},18 if err != nil {19 t.Fatal(err)20 }21 ctx, cancel := context.WithCancel(context.Background())22 defer cancel()23 runners := r.GetRunners()24 if len(runners) != 1 {25 t.Fatalf("Expected 1 runner, got %d", len(runners))26 }27 runners[0].Run(ctx)28 metrics := runners[0].GetMetrics()29 if exp, got := int64(1), metrics["iterations"]; exp != got {30 t.Errorf("Expected %d iterations, got %d", exp, got)31 }32 if exp, got := int64(1), metrics["vus"]; exp != got {33 t.Errorf("Expected %d VUs, got %d", exp, got)34 }35 if exp, got := int64(1), metrics["vus_max"]; exp != got {36 t.Errorf("Expected %d max VUs, got %d", exp, got)37 }38}

Full Screen

Full Screen

initVU

Using AI Code Generation

copy

Full Screen

1import (2type VU struct {3 initVU func(*js.VU) error4}5func (v *VU) RunOnce(ctx context.Context) ([]stats.Sample, error) {6}7func main() {8 l := loader.New()9 r, err := l.LoadSourceData("./1.js", "1.js")10 if err != nil {11 panic(err)12 }13 es := r.GetExec()14 if es == nil {15 panic("no exec")16 }17 v := &VU{18 }19 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)20 defer cancel()21 vu, err := js.NewVU(es, lib.RuntimeOptions{})22 if err != nil {23 panic(err)24 }25 defer vu.Close()26 if err := v.initVU(vu); err != nil {27 panic(err)28 }29 wg.Add(1)30 go func() {31 for {32 select {33 case <-ctx.Done():34 wg.Done()35 _, err := v.RunOnce(ctx)36 if err != nil {37 panic(err)38 }39 }40 }41 }()42 wg.Wait()43}44export let options = {45};46export default function () {47 console.log("Hello, world!");48}49go.k6.io/k6/js.(*VU).RunOnce(0xc0000b6c00, 0x1e2c680, 0xc0000b6c00, 0x0, 0x0)

Full Screen

Full Screen

initVU

Using AI Code Generation

copy

Full Screen

1import (2func init() {3 modules.Register("k6/x/foobar", new(FooBar))4}5type FooBar struct{}6func (fb *FooBar) initVU() {7 fmt.Println("initVU")8}9func (fb *FooBar) teardownVU() {10 fmt.Println("teardownVU")11}12func (fb *FooBar) add(a, b int) int {13}14import { check, sleep } from "k6";15import http from "k6/http";16import foobar from "k6/x/foobar";17export let options = {18};19export default function () {20 foobar.initVU();21 foobar.teardownVU();22 foobar.add(1, 2);23 sleep(1);24}25import { check, sleep } from "k6";26import http from "k6/http";27import foobar from "k6/x/foobar";28export let options = {29};30export default function () {31 foobar.initVU();32 foobar.teardownVU();33 foobar.add(1, 2);34 sleep(1);35}36import { check, sleep } from "k6";37import http from "k6/http";38import foobar from "k6/x/foobar";39export let options = {40};41export default function () {42 foobar.initVU();43 foobar.teardownVU();44 foobar.add(1, 2);45 sleep(1);46}47import { check, sleep } from "k6";48import http from "k6/http";49import foobar from "k6/x/foobar";50export let options = {51};52export default function () {53 foobar.initVU();54 foobar.teardownVU();55 foobar.add(1, 2);56 sleep(1);57}58import { check, sleep } from "k6

Full Screen

Full Screen

initVU

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 k6.InitVU(func(ctx *k6.VU) interface{} {4 return &struct {5 }{}6 })7 k6.Default(func(ctx *k6.VU) {8 fmt.Println(s.Text())9 })10 })11 k6.Default(func(ctx *k6.VU) {12 time.Sleep(3 * time.Second)13 })14}15import (16func main() {17 k6.InitVU(func(ctx *k6.VU) interface{} {18 return &struct {19 }{}20 })21 k6.Default(func(ctx *k6.VU) {22 fmt.Println(s.Text())23 })24 })25 k6.Default(func(ctx *k6.VU) {26 time.Sleep(3 * time.Second)27 })28}

Full Screen

Full Screen

initVU

Using AI Code Generation

copy

Full Screen

1public class 1 {2 public static void main(String[] args) {3 2 obj = new 2();4 obj.initVU();5 }6}7class 2 {8 void initVU() {9 System.out.println("Initializing VU...");10 }11}12public class 1 {13 public static void main(String[] args) {14 2 obj = new 2();15 obj.initVU();16 }17}18class 2 {19 void initVU() {20 class LocalClass {21 void initVU() {22 System.out.println("Initializing VU...");23 }24 }25 LocalClass obj = new LocalClass();26 obj.initVU();27 }28}29public class 1 {30 public static void main(String[] args) {31 2 obj = new 2();32 obj.initVU();33 }34}35class 2 {36 void initVU() {

Full Screen

Full Screen

initVU

Using AI Code Generation

copy

Full Screen

1import "fmt"2type T struct {3}4func (t *T) InitVU() {5 fmt.Println("InitVU")6}7func main() {8 t := T{2}9 t.InitVU()10}11import "fmt"12type T struct {13}14func (t *T) InitVU() {15 fmt.Println("InitVU")16}17func main() {18 t := T{2}19 t.InitVU()20}21import "fmt"22type T struct {23}24func (t *T) InitVU() {25 fmt.Println("InitVU")26}27func main() {28 t := T{2}29 t.InitVU()30}31import "fmt"32type T struct {33}34func (t *T) InitVU() {35 fmt.Println("InitVU")36}37func main() {38 t := T{2}39 t.InitVU()40}41import "fmt"42type T struct {43}44func (t *T) InitVU() {45 fmt.Println("InitVU")46}47func main() {48 t := T{2}49 t.InitVU()50}51import "fmt"52type T struct {53}54func (t *T) InitVU() {55 fmt.Println("InitVU")56}57func main() {58 t := T{2}59 t.InitVU()60}61import "fmt"62type T struct {63}64func (t *T) InitVU() {65 fmt.Println("InitVU")66}67func main() {68 t := T{2}69 t.InitVU()70}

Full Screen

Full Screen

initVU

Using AI Code Generation

copy

Full Screen

1import "fmt"2func main() {3 var v1 = new(Vehicle)4 var v2 = new(Vehicle)5 v1.initVU("Toyota", "Camry", 2013, 1200)6 v2.initVU("Nissan", "Altima", 2015, 2000)7 fmt.Println("Vehicle 1:", v1.make, v1.model, v1.year, v1.mileage)8 fmt.Println("Vehicle 2:", v2.make, v2.model, v2.year, v2.mileage)9}10type Vehicle struct {11}12func (v *Vehicle) initVU(make, model string, year, mileage int) {13}14import "fmt"15func main() {16 var v1 = new(Vehicle)17 var v2 = new(Vehicle)18 v1.initVU("Toyota", "Camry", 2013, 1200)19 v2.initVU("Nissan", "Altima", 2015, 2000)20 fmt.Println("Vehicle 1:", v1.make, v1.model, v1.year, v1.mileage)21 fmt.Println("Vehicle 2:", v2.make, v2.model, v2.year, v2.mileage)22}23type Vehicle struct {24}25func (v *Vehicle) initVU(make, model string, year, mileage int) {26}27import "fmt"28func main()

Full Screen

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful