What happened
On Darwin (macOS), State.Stop() in pkg/internal/testing/process may return "unable to kill process ..." instead of the expected "timeout waiting for process ... to stop" error when StopTimeout expires.
This causes test failures in process_test.go where the test asserts the error contains "timeout":
Expect(processState.Stop()).To(MatchError(ContainSubstring("timeout")))
What you expected to happen
Stop() should always return a timeout error when StopTimeout expires, regardless of whether the subsequent SIGKILL succeeds or fails.
How to reproduce it
On macOS, run:
go test ./pkg/internal/testing/process/ -run "Stop.*cannot be stopped" -count=100
The test is flaky — it passes when SIGKILL succeeds, but fails when the process has already exited between the timeout and the SIGKILL attempt.
Root cause
In process.go, the Stop() method's timeout case:
case <-timedOut:
if err := signalProcess(ps.Cmd.Process, syscall.SIGKILL); err != nil {
return fmt.Errorf("unable to kill process %s: %w", ps.Path, err)
}
return fmt.Errorf("timeout waiting for process %s to stop", path.Base(ps.Path))
When the timeout fires:
SIGTERM was already sent, and the process is shutting down
SIGKILL is sent via syscall.Kill(-process.Pid, syscall.SIGKILL) (in signal_unix.go)
- On Darwin, if the process is already in its termination sequence,
syscall.Kill returns EPERM
- The
"unable to kill process" error is returned instead of the "timeout waiting for process to stop" error
The SIGKILL after timeout is a best-effort escalation — its failure should not override the timeout error that is the primary signal.
Environment
Suggested fix
The timeout error should always be returned when StopTimeout expires. The SIGKILL error can be included as supplementary information but should not replace the timeout error.
What happened
On Darwin (macOS),
State.Stop()inpkg/internal/testing/processmay return"unable to kill process ..."instead of the expected"timeout waiting for process ... to stop"error whenStopTimeoutexpires.This causes test failures in
process_test.gowhere the test asserts the error contains"timeout":What you expected to happen
Stop()should always return a timeout error whenStopTimeoutexpires, regardless of whether the subsequentSIGKILLsucceeds or fails.How to reproduce it
On macOS, run:
The test is flaky — it passes when
SIGKILLsucceeds, but fails when the process has already exited between the timeout and theSIGKILLattempt.Root cause
In
process.go, theStop()method's timeout case:When the timeout fires:
SIGTERMwas already sent, and the process is shutting downSIGKILLis sent viasyscall.Kill(-process.Pid, syscall.SIGKILL)(insignal_unix.go)syscall.KillreturnsEPERM"unable to kill process"error is returned instead of the"timeout waiting for process to stop"errorThe
SIGKILLafter timeout is a best-effort escalation — its failure should not override the timeout error that is the primary signal.Environment
Suggested fix
The timeout error should always be returned when
StopTimeoutexpires. TheSIGKILLerror can be included as supplementary information but should not replace the timeout error.