Repository: kubernetes/kubernetes
File: test/utils/ktesting/stepcontext_test.go (Go)

Code before:
	})

	// This must use a real testing.T, otherwise Init doesn't initialize signal handling.
	tCtx := Init(t)
	tCtx = WithStep(tCtx, "step")
	removeReporter := tCtx.Value("GINKGO_SPEC_CONTEXT").(ginkgoReporter).AttachProgressReporter(func() string { return "hello world" })
	defer removeReporter()
	tCtx.Expect(tCtx.Value("some other key")).To(gomega.BeNil(), "value for unknown context value key")

	// Trigger report and wait for it.
	defaultProgressReporter.progressChannel <- os.Interrupt
	report := <-out.stream
	tCtx.Expect(report).To(gomega.Equal(`You requested a progress report.

step: hello world
`), "report")

	gomega.NewGomegaWithT(t).Expect(context.Cause(interruptCtx)).To(gomega.Succeed(), "not interrupted yet")
	defaultProgressReporter.signalChannel <- os.Interrupt
	message := <-out.stream
	tCtx.Expect(message).To(gomega.Equal(`

INFO: canceling test context: received interrupt signal

`))
	gomega.NewGomegaWithT(t).Expect(context.Cause(tCtx)).To(gomega.MatchError(gomega.Equal("received interrupt signal")), "interrupted")
}

// outputStream forwards exactly one Write call to a stream.
// A second Write call is an error and will panic.
type outputStream struct {
	stream chan string
}

var _ io.Writer = &outputStream{}

func newOutputStream() *outputStream {
	return &outputStream{
		stream: make(chan string),
	}
}

func (s *outputStream) Write(buf []byte) (int, error) {
	s.stream <- string(buf)
	return len(buf), nil
}


Review comment (bug):
I'm seeing `stress ./ktesting.test` blow up here with errors like this.

```
/tmp/go-stress-20260129T111101-1207466201
--- FAIL: TestProgressReport (0.00s)
    stepcontext_test.go:116:
        interrupted
        Expected an error, got nil
FAIL


ERROR: exit status 1
```

Plain `go test` with a high `-count` also reliably fails for me:
```
go test ./test/utils/ktesting -count 10000 -failfast -run TestProgressReport
```

Code after:
		interruptCtx, interrupted = context.WithCancelCause(context.Background())
	})

	// This must use a real testing.T, otherwise Init doesn't initialize signal handling.
	tCtx := Init(t)
	tCtx = WithStep(tCtx, "step")
	removeReporter := tCtx.Value("GINKGO_SPEC_CONTEXT").(ginkgoReporter).AttachProgressReporter(func() string { return "hello world" })
	defer removeReporter()
	tCtx.Expect(tCtx.Value("some other key")).To(gomega.BeNil(), "value for unknown context value key")

	// Trigger report and wait for it.
	defaultProgressReporter.progressChannel <- os.Interrupt
	report := <-out.stream
	tCtx.Expect(report).To(gomega.Equal(`You requested a progress report.

step: hello world
`), "report")

	gomega.NewGomegaWithT(t).Expect(context.Cause(interruptCtx)).To(gomega.Succeed(), "not interrupted yet")
	defaultProgressReporter.signalChannel <- os.Interrupt
	message := <-out.stream
	tCtx.Expect(message).To(gomega.Equal(`

INFO: canceling test context: received interrupt signal

`))
	gomega.NewGomegaWithT(t).Eventually(func() error { return context.Cause(tCtx) }).WithTimeout(30*time.Second).To(gomega.MatchError(gomega.Equal("received interrupt signal")), "interrupted")
}

// outputStream forwards exactly one Write call to a stream.
// A second Write call is an error and will panic.
type outputStream struct {
	stream chan string
}

var _ io.Writer = &outputStream{}

func newOutputStream() *outputStream {
	return &outputStream{
		stream: make(chan string),
	}
}

func (s *outputStream) Write(buf []byte) (int, error) {
	s.stream <- string(buf)
	return len(buf), nil
}