(t *testing.T)
| 101 | } |
| 102 | |
| 103 | func TestContextWatcherStress(t *testing.T) { |
| 104 | var cancelFuncCalls atomic.Int64 |
| 105 | var cleanupFuncCalls atomic.Int64 |
| 106 | |
| 107 | cw := ctxwatch.NewContextWatcher(&testHandler{ |
| 108 | handleCancel: func(context.Context) { |
| 109 | cancelFuncCalls.Add(1) |
| 110 | }, handleUnwatchAfterCancel: func() { |
| 111 | cleanupFuncCalls.Add(1) |
| 112 | }, |
| 113 | }) |
| 114 | |
| 115 | cycleCount := 100_000 |
| 116 | |
| 117 | for i := range cycleCount { |
| 118 | ctx, cancel := context.WithCancel(context.Background()) |
| 119 | defer cancel() // satisfy linter; cancel is idempotent |
| 120 | cw.Watch(ctx) |
| 121 | if i%2 == 0 { |
| 122 | cancel() |
| 123 | } |
| 124 | |
| 125 | // Without time.Sleep, cw.Unwatch will almost always run before the cancel func which means cancel will never happen. This gives us a better mix. |
| 126 | if i%333 == 0 { |
| 127 | // on Windows Sleep takes more time than expected so we try to get here less frequently to avoid |
| 128 | // the CI takes a long time |
| 129 | time.Sleep(time.Nanosecond) |
| 130 | } |
| 131 | |
| 132 | cw.Unwatch() |
| 133 | if i%2 == 1 { |
| 134 | cancel() |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | actualCancelFuncCalls := cancelFuncCalls.Load() |
| 139 | actualCleanupFuncCalls := cleanupFuncCalls.Load() |
| 140 | |
| 141 | if actualCancelFuncCalls == 0 { |
| 142 | t.Fatal("actualCancelFuncCalls == 0") |
| 143 | } |
| 144 | |
| 145 | maxCancelFuncCalls := int64(cycleCount) / 2 |
| 146 | if actualCancelFuncCalls > maxCancelFuncCalls { |
| 147 | t.Errorf("cancel func calls should be no more than %d but was %d", actualCancelFuncCalls, maxCancelFuncCalls) |
| 148 | } |
| 149 | |
| 150 | if actualCancelFuncCalls != actualCleanupFuncCalls { |
| 151 | t.Errorf("cancel func calls (%d) should be equal to cleanup func calls (%d) but was not", actualCancelFuncCalls, actualCleanupFuncCalls) |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | func BenchmarkContextWatcherUncancellable(b *testing.B) { |
| 156 | cw := ctxwatch.NewContextWatcher(&testHandler{handleCancel: func(context.Context) {}, handleUnwatchAfterCancel: func() {}}) |
nothing calls this directly
no test coverage detected