(t *testing.T)
| 185 | } |
| 186 | |
| 187 | func TestCloserStack_Timeout(t *testing.T) { |
| 188 | t.Parallel() |
| 189 | ctx := testutil.Context(t, testutil.WaitShort) |
| 190 | logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug) |
| 191 | mClock := quartz.NewMock(t) |
| 192 | trap := mClock.Trap().TickerFunc("closerStack") |
| 193 | defer trap.Close() |
| 194 | uut := newCloserStack(ctx, logger, mClock) |
| 195 | var ac [3]*asyncCloser |
| 196 | for i := range ac { |
| 197 | ac[i] = newAsyncCloser(ctx, t) |
| 198 | err := uut.push(fmt.Sprintf("async %d", i), ac[i]) |
| 199 | require.NoError(t, err) |
| 200 | } |
| 201 | defer func() { |
| 202 | for _, a := range ac { |
| 203 | a.unblock() |
| 204 | testutil.TryReceive(ctx, t, a.done) // ensure we don't race with context cancellation |
| 205 | } |
| 206 | }() |
| 207 | |
| 208 | closed := make(chan struct{}) |
| 209 | go func() { |
| 210 | defer close(closed) |
| 211 | uut.close(nil) |
| 212 | }() |
| 213 | trap.MustWait(ctx).MustRelease(ctx) |
| 214 | // top starts right away, but it hangs |
| 215 | testutil.TryReceive(ctx, t, ac[2].started) |
| 216 | // timer pops and we start the middle one |
| 217 | mClock.Advance(gracefulShutdownTimeout).MustWait(ctx) |
| 218 | testutil.TryReceive(ctx, t, ac[1].started) |
| 219 | |
| 220 | // middle one finishes |
| 221 | ac[1].unblock() |
| 222 | // bottom starts, but also hangs |
| 223 | testutil.TryReceive(ctx, t, ac[0].started) |
| 224 | |
| 225 | // timer has to pop twice to time out. |
| 226 | mClock.Advance(gracefulShutdownTimeout).MustWait(ctx) |
| 227 | mClock.Advance(gracefulShutdownTimeout).MustWait(ctx) |
| 228 | testutil.TryReceive(ctx, t, closed) |
| 229 | } |
| 230 | |
| 231 | func TestCloserStack_PushAfterClose_ConnClosed(t *testing.T) { |
| 232 | t.Parallel() |
nothing calls this directly
no test coverage detected