nolint:tparallel // Subtests must be sequential, each starts its own reaper.
(t *testing.T)
| 126 | |
| 127 | //nolint:tparallel // Subtests must be sequential, each starts its own reaper. |
| 128 | func TestForkReapExitCodes(t *testing.T) { |
| 129 | t.Parallel() |
| 130 | if testutil.InCI() { |
| 131 | t.Skip("Detected CI, skipping reaper tests") |
| 132 | } |
| 133 | if !runSubprocess(t) { |
| 134 | return |
| 135 | } |
| 136 | |
| 137 | tests := []struct { |
| 138 | name string |
| 139 | command string |
| 140 | expectedCode int |
| 141 | }{ |
| 142 | {"exit 0", "exit 0", 0}, |
| 143 | {"exit 1", "exit 1", 1}, |
| 144 | {"exit 42", "exit 42", 42}, |
| 145 | {"exit 255", "exit 255", 255}, |
| 146 | {"SIGKILL", "kill -9 $$", 128 + 9}, |
| 147 | {"SIGTERM", "kill -15 $$", 128 + 15}, |
| 148 | } |
| 149 | |
| 150 | //nolint:paralleltest // Subtests must be sequential, each starts its own reaper. |
| 151 | for _, tt := range tests { |
| 152 | t.Run(tt.name, func(t *testing.T) { |
| 153 | var reapLock sync.RWMutex |
| 154 | opts := append([]reaper.Option{ |
| 155 | reaper.WithExecArgs("/bin/sh", "-c", tt.command), |
| 156 | reaper.WithReapLock(&reapLock), |
| 157 | }, withDone(t)...) |
| 158 | reapLock.RLock() |
| 159 | exitCode, err := reaper.ForkReap(opts...) |
| 160 | reapLock.RUnlock() |
| 161 | require.NoError(t, err) |
| 162 | require.Equal(t, tt.expectedCode, exitCode, "exit code mismatch for %q", tt.command) |
| 163 | }) |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | // TestReapInterrupt verifies that ForkReap forwards caught signals |
| 168 | // to the child process. The test sends SIGINT to its own process |
nothing calls this directly
no test coverage detected