diff --git a/cmd/run.go b/cmd/run.go index cd70e6fab47..853613ee6d5 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -306,22 +306,17 @@ func (c *cmdRun) run(cmd *cobra.Command, args []string) (err error) { // Init has passed successfully, so unless disabled, make sure we send a // usage report after the context is done. if !conf.NoUsageReport.Bool { - backgroundProcesses.Add(2) - reportCtx, reportCancel := context.WithCancel(globalCtx) - reportDone := make(chan error) + backgroundProcesses.Add(1) go func() { + defer backgroundProcesses.Done() + reportCtx, reportCancel := context.WithTimeout(globalCtx, 3*time.Second) + defer reportCancel() logger.Debug("Sending usage report...") - reportDone <- reportUsage(reportCtx, execScheduler) - close(reportDone) - backgroundProcesses.Done() - }() - go func() { - select { - case <-reportDone: - case <-time.After(3 * time.Second): + if rerr := reportUsage(reportCtx, execScheduler); rerr != nil { + logger.WithError(rerr).Debug("Error sending usage report") + } else { + logger.Debug("Usage report sent successfully") } - reportCancel() - backgroundProcesses.Done() }() } diff --git a/cmd/tests/cmd_run_test.go b/cmd/tests/cmd_run_test.go index 4280e89be62..c1cd82342e4 100644 --- a/cmd/tests/cmd_run_test.go +++ b/cmd/tests/cmd_run_test.go @@ -57,6 +57,7 @@ func TestSimpleTestStdin(t *testing.T) { cmd.ExecuteWithGlobalState(ts.GlobalState) stdout := ts.Stdout.String() + assert.Contains(t, stdout, "output: -") assert.Contains(t, stdout, "default: 1 iterations for each of 1 VUs") assert.Contains(t, stdout, "1 complete and 0 interrupted iterations") assert.Empty(t, ts.Stderr.Bytes()) @@ -1745,3 +1746,37 @@ func TestBrowserPermissions(t *testing.T) { }) } } + +func TestUIRenderOutput(t *testing.T) { + t.Parallel() + + tests := []struct { + outputs []string + expRender string + }{ + {outputs: []string{}, expRender: "output: -\n"}, + {outputs: []string{"json"}, expRender: "output: json(stdout)\n\n"}, + {outputs: []string{"json", "csv"}, expRender: "output: json(stdout), csv (file.csv)\n\n"}, + } + + for _, tc := range tests { + tc := tc + + t.Run(tc.expRender, func(t *testing.T) { + t.Parallel() + + ts := NewGlobalTestState(t) + ts.CmdArgs = []string{"k6", "run"} + for _, o := range tc.outputs { + ts.CmdArgs = append(ts.CmdArgs, "-o") + ts.CmdArgs = append(ts.CmdArgs, o) + } + ts.CmdArgs = append(ts.CmdArgs, "-") + ts.Stdin = bytes.NewBufferString(`export default function() {};`) + cmd.ExecuteWithGlobalState(ts.GlobalState) + + stdout := ts.Stdout.String() + assert.Contains(t, stdout, tc.expRender) + }) + } +} diff --git a/cmd/ui.go b/cmd/ui.go index ba5385686d6..a2aaab725c1 100644 --- a/cmd/ui.go +++ b/cmd/ui.go @@ -19,6 +19,7 @@ import ( "go.k6.io/k6/cmd/state" "go.k6.io/k6/lib" "go.k6.io/k6/lib/consts" + "go.k6.io/k6/metrics/engine" "go.k6.io/k6/output" "go.k6.io/k6/ui/pb" ) @@ -109,11 +110,16 @@ func printExecutionDescription( switch { case outputOverride != "": outputDescriptions = []string{outputOverride} - case len(outputs) == 0: - outputDescriptions = []string{"-"} default: for _, out := range outputs { - outputDescriptions = append(outputDescriptions, out.Description()) + desc := out.Description() + if desc == engine.IngesterDescription { + if len(outputs) != 1 { + continue + } + desc = "-" + } + outputDescriptions = append(outputDescriptions, desc) } } diff --git a/js/modules/k6/execution/execution.go b/js/modules/k6/execution/execution.go index 768fbd23569..a85fa0e1e7f 100644 --- a/js/modules/k6/execution/execution.go +++ b/js/modules/k6/execution/execution.go @@ -203,6 +203,11 @@ func (mi *ModuleInstance) newVUInfo() (*goja.Object, error) { "idInTest": func() interface{} { return vuState.VUIDGlobal }, "iterationInInstance": func() interface{} { return vuState.Iteration }, "iterationInScenario": func() interface{} { + if vuState.GetScenarioVUIter == nil { + // hasn't been set yet, no iteration stats available + return 0 + } + return vuState.GetScenarioVUIter() }, } diff --git a/js/modules/k6/execution/execution_test.go b/js/modules/k6/execution/execution_test.go index 04b6ee25378..c5cba14f0d5 100644 --- a/js/modules/k6/execution/execution_test.go +++ b/js/modules/k6/execution/execution_test.go @@ -406,6 +406,34 @@ func TestScenarioNoAvailableInInitContext(t *testing.T) { } } +func TestVUDefaultDetails(t *testing.T) { + t.Parallel() + + rt := goja.New() + m, ok := New().NewModuleInstance( + &modulestest.VU{ + RuntimeField: rt, + CtxField: context.Background(), + StateField: &lib.State{ + Options: lib.Options{ + Paused: null.BoolFrom(true), + }, + }, + }, + ).(*ModuleInstance) + require.True(t, ok) + require.NoError(t, rt.Set("exec", m.Exports().Default)) + + props := []string{"idInInstance", "idInTest", "iterationInInstance", "iterationInScenario"} + + for _, code := range props { + prop := fmt.Sprintf("exec.vu.%s", code) + res, err := rt.RunString(prop) + require.NoError(t, err) + require.Equal(t, "0", res.String()) + } +} + func TestTagsDynamicObjectGet(t *testing.T) { t.Parallel() rt := goja.New() diff --git a/lib/consts/consts.go b/lib/consts/consts.go index 441690fc2cc..691d21a0a84 100644 --- a/lib/consts/consts.go +++ b/lib/consts/consts.go @@ -8,7 +8,7 @@ import ( ) // Version contains the current semantic version of k6. -const Version = "0.43.0" +const Version = "0.43.1" // VersionDetails can be set externally as part of the build process var VersionDetails = "" //nolint:gochecknoglobals diff --git a/metrics/engine/ingester.go b/metrics/engine/ingester.go index 83a514552a0..a8736af8c95 100644 --- a/metrics/engine/ingester.go +++ b/metrics/engine/ingester.go @@ -11,6 +11,11 @@ const collectRate = 50 * time.Millisecond var _ output.Output = &outputIngester{} +// IngesterDescription is a short description for ingester. +// This variable is used from a function in cmd/ui file for matching this output +// and print a special text. +const IngesterDescription = "Internal Metrics Ingester" + // outputIngester implements the output.Output interface and can be used to // "feed" the MetricsEngine data from a `k6 run` test run. type outputIngester struct { @@ -23,7 +28,7 @@ type outputIngester struct { // Description returns a human-readable description of the output. func (oi *outputIngester) Description() string { - return "engine" + return IngesterDescription } // Start the engine by initializing a new output.PeriodicFlusher diff --git a/release notes/v0.43.1.md b/release notes/v0.43.1.md new file mode 100644 index 00000000000..061d2108281 --- /dev/null +++ b/release notes/v0.43.1.md @@ -0,0 +1,4 @@ +k6 v0.43.1 is a patch release containing a few bugfixes: +- [#2926](https://github.com/grafana/k6/pull/2926) fixed a panic in `setup()` code when `vu.iterationInScenario` from `k6/execution` was used. +- [#2934](https://github.com/grafana/k6/pull/2934) fixed a wrongly printed internal output ID to the `stdout` UI. +- [#2938](https://github.com/grafana/k6/pull/2938) fixed a synchronization bug that caused k6 to get stuck after the end-of-test summary when sending the usage report took more than 3s. Thanks for [reporting this](https://github.com/grafana/k6/issues/2937), @ichasepucks! \ No newline at end of file