go.mway.dev/chrono@v0.6.1-0.20240126030049-189c5aef20d2/clock/fake_clock_test.go (about) 1 // Copyright (c) 2023 Matt Way 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to 5 // deal in the Software without restriction, including without limitation the 6 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 // sell copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 // IN THE THE SOFTWARE. 20 21 package clock_test 22 23 import ( 24 "context" 25 "sync" 26 "testing" 27 "time" 28 29 "github.com/stretchr/testify/require" 30 "go.mway.dev/chrono" 31 "go.mway.dev/chrono/clock" 32 "go.uber.org/atomic" 33 ) 34 35 func TestFakeClock_Add(t *testing.T) { 36 clk := clock.NewFakeClock() 37 requireClockIs(t, 0, clk) 38 39 // Test moving forward. 40 for i := int64(1); i <= 1000; i++ { 41 prev := clk.Nanotime() 42 clk.Add(time.Duration(i)) 43 requireClockIs(t, prev+i, clk) 44 } 45 46 // Test moving backward. 47 for i := int64(1); i <= 1000; i++ { 48 prev := clk.Nanotime() 49 clk.Add(-time.Duration(i)) 50 requireClockIs(t, prev-i, clk) 51 } 52 53 // We should be back where we started. 54 requireClockIs(t, 0, clk) 55 } 56 57 func TestFakeClock_SetTime(t *testing.T) { 58 clk := clock.NewFakeClock() 59 60 for i := int64(1); i <= 1000; i++ { 61 clk.SetTime(time.Unix(0, i)) 62 requireClockIs(t, i, clk) 63 } 64 65 for i := int64(1000); i > 0; i-- { 66 clk.SetTime(time.Unix(0, i)) 67 requireClockIs(t, i, clk) 68 } 69 } 70 71 func TestFakeClock_SetNanotime(t *testing.T) { 72 clk := clock.NewFakeClock() 73 74 for i := int64(1); i <= 1000; i++ { 75 clk.SetNanotime(i) 76 requireClockIs(t, i, clk) 77 } 78 79 for i := int64(1000); i > 0; i-- { 80 clk.SetNanotime(i) 81 requireClockIs(t, i, clk) 82 } 83 } 84 85 func TestFakeClock_After(t *testing.T) { 86 var ( 87 clk = clock.NewFakeClock() 88 timerC = clk.After(time.Second) 89 ) 90 91 require.NotNil(t, timerC) 92 requireNoTick(t, timerC) 93 94 clk.Add(time.Second) 95 96 ts := requireTick(t, timerC) 97 requireTimeIs(t, int64(time.Second), ts) 98 99 // Start a new timer. This time, we want to advance the clock once to its 100 // expiration, and then once again immediately after, to ensure that the 101 // timer's channel only contains the first tick. 102 timerC = clk.After(time.Second) 103 clk.Add(time.Second) 104 clk.Add(time.Second) 105 106 ts = requireTick(t, timerC) 107 requireTimeIs(t, 2*int64(time.Second), ts) 108 } 109 110 func TestFakeClock_AfterFunc(t *testing.T) { 111 var ( 112 clk = clock.NewFakeClock() 113 calls = atomic.NewInt64(0) 114 fn = func() { calls.Inc() } 115 timer = clk.AfterFunc(time.Second, fn) 116 ) 117 118 for i := int64(0); i < 10; i++ { 119 timer.Reset(time.Second) 120 clk.Add(time.Second) 121 waitFor(t, time.Second, func() bool { 122 return calls.Load() == i+1 123 }) 124 } 125 } 126 127 func TestFakeClockSince(t *testing.T) { 128 var ( 129 clk = clock.NewFakeClock() 130 since int64 131 ) 132 133 for i := int64(1); i < 1000; i++ { 134 clk.SetNanotime(i) 135 requireClockIs(t, i, clk) 136 requireClockSince(t, i, since, clk) 137 } 138 } 139 140 func TestFakeClock_NewTimer(t *testing.T) { 141 var ( 142 clk = clock.NewFakeClock() 143 timer = clk.NewTimer(time.Second) 144 ) 145 146 for i := int64(0); i < 10; i++ { 147 requireNoTick(t, timer.C) 148 clk.Add(time.Second) 149 requireTick(t, timer.C) 150 require.False(t, timer.Reset(time.Second)) 151 } 152 153 // Cause the timer's tick channel to fill, and then tick again. 154 clk.Add(time.Second) 155 156 // Because the last tick wasn't consumed, the reported timestamp will be 157 // in the past. 158 want := clk.Nanotime() 159 timer.Reset(time.Second) 160 clk.Add(time.Second) 161 ts := requireTick(t, timer.C) 162 requireTimeIs(t, want, ts) 163 164 require.False(t, timer.Stop()) 165 requireNoTick(t, timer.C) 166 } 167 168 func TestFakeClock_Timer_Zeroes(t *testing.T) { 169 var ( 170 clk = clock.NewFakeClock() 171 timer *clock.Timer 172 ) 173 174 require.NotPanics(t, func() { 175 timer = clk.NewTimer(-1) 176 }) 177 178 require.NotPanics(t, func() { 179 timer = clk.NewTimer(0) 180 }) 181 182 requireNoTick(t, timer.C) 183 184 clk.Add(time.Second) 185 requireTick(t, timer.C) 186 187 // Ensure that resetting the timer will not panic if given a duration <= 0. 188 require.NotPanics(t, func() { 189 timer.Reset(-1) 190 }) 191 require.NotPanics(t, func() { 192 timer.Reset(0) 193 }) 194 195 // The timer will still report that it has been stopped, because the clock 196 // has not been changed. 197 require.True(t, timer.Stop()) 198 } 199 200 func TestFakeClock_NewTicker(t *testing.T) { 201 var ( 202 clk = clock.NewFakeClock() 203 ticker = clk.NewTicker(time.Second) 204 ) 205 206 for i := int64(0); i < 10; i++ { 207 requireNoTick(t, ticker.C) 208 clk.Add(time.Second) 209 requireTick(t, ticker.C) 210 211 if i%2 == 0 { 212 ticker.Reset(time.Second) 213 } 214 } 215 216 ticker.Stop() 217 requireNoTick(t, ticker.C) 218 219 require.Panics(t, func() { 220 clk.NewTicker(-1) 221 }) 222 223 require.Panics(t, func() { 224 clk.NewTicker(0) 225 }) 226 } 227 228 //nolint:gocyclo 229 func TestFakeClock_Ticker_Goroutine(t *testing.T) { 230 var ( 231 clk = clock.NewFakeClock() 232 ticks = make(chan struct{}, 10) 233 ready = make(chan struct{}) 234 wg sync.WaitGroup 235 ) 236 237 ctx, cancel := context.WithCancel(context.Background()) 238 defer cancel() 239 240 for i := 0; i < cap(ticks); i++ { 241 wg.Add(1) 242 go func() { 243 ticker := clk.NewTicker(time.Second) 244 defer ticker.Stop() 245 246 ready <- struct{}{} 247 248 for { 249 select { 250 case <-ticker.C: 251 case <-ctx.Done(): 252 return 253 } 254 255 select { 256 case ticks <- struct{}{}: 257 case <-ctx.Done(): 258 return 259 } 260 } 261 }() 262 } 263 264 maxWait := time.NewTicker(5 * time.Second) 265 defer maxWait.Stop() 266 267 for i := 0; i < cap(ticks); i++ { 268 select { 269 case <-ready: 270 case <-maxWait.C: 271 require.FailNow(t, "timed out waiting for ticker ticks") 272 } 273 } 274 275 for i := 0; i < cap(ticks); i++ { 276 clk.Add(time.Second) 277 278 select { 279 case <-ticks: 280 case <-maxWait.C: 281 require.FailNow(t, "timed out waiting for ticker ticks") 282 } 283 } 284 } 285 286 func TestFakeClock_Ticker_Zeroes(t *testing.T) { 287 var ( 288 clk = clock.NewFakeClock() 289 ticker = clk.NewTicker(time.Second) 290 ) 291 292 requireNoTick(t, ticker.C) 293 294 clk.Add(time.Second) 295 requireTick(t, ticker.C) 296 297 // Ensure that the ticker will panic if given a duration <= 0. 298 require.Panics(t, func() { 299 ticker.Reset(-1) 300 }) 301 require.Panics(t, func() { 302 ticker.Reset(0) 303 }) 304 } 305 306 func TestFakeClock_Tick(t *testing.T) { 307 var ( 308 clk = clock.NewFakeClock() 309 tickerC = clk.Tick(time.Second) 310 ) 311 312 for i := int64(0); i < 10; i++ { 313 requireNoTick(t, tickerC) 314 clk.Add(time.Second) 315 requireTick(t, tickerC) 316 } 317 318 require.Panics(t, func() { 319 clk.Tick(-1) 320 }) 321 322 require.Panics(t, func() { 323 clk.Tick(0) 324 }) 325 } 326 327 func TestFakeClock_Sleep(t *testing.T) { 328 var ( 329 clk = clock.NewFakeClock() 330 sleepdone = make(chan struct{}) 331 ) 332 go func() { 333 defer close(sleepdone) 334 clk.Sleep(time.Second) 335 }() 336 337 for i := 0; i < 10; i++ { 338 clk.Add(time.Second) 339 time.Sleep(time.Millisecond) 340 } 341 342 select { 343 case <-sleepdone: 344 case <-time.After(time.Second): 345 require.Fail(t, "sleep did not wake") 346 } 347 } 348 349 func TestFakeClock_InterleavedTimers(t *testing.T) { 350 var ( 351 clk = clock.NewFakeClock() 352 timer2 = clk.NewTimer(3 * time.Second) 353 timer3 = clk.NewTimer(1 * time.Second) 354 timer1 = clk.NewTimer(2 * time.Second) 355 ) 356 357 timer2.Reset(2 * time.Second) 358 timer3.Reset(3 * time.Second) 359 timer1.Reset(1 * time.Second) 360 361 // n.b. Handle channels in reverse order. We defined them above in order of 362 // descending duration, but since ticking is synchonous, evaluate them 363 // in sorted (ascending) order to ensure that the internals are doing 364 // what we expect them to. 365 for i := int64(0); i < 10; i++ { 366 requireNoTick(t, timer1.C) 367 requireNoTick(t, timer2.C) 368 requireNoTick(t, timer3.C) 369 370 clk.Add(time.Second) 371 requireTick(t, timer1.C) 372 requireNoTick(t, timer2.C) 373 requireNoTick(t, timer3.C) 374 375 clk.Add(time.Second) 376 requireNoTick(t, timer1.C) 377 requireTick(t, timer2.C) 378 requireNoTick(t, timer3.C) 379 380 clk.Add(time.Second) 381 requireNoTick(t, timer1.C) 382 requireNoTick(t, timer2.C) 383 requireTick(t, timer3.C) 384 385 require.False(t, timer1.Reset(1*time.Second)) 386 require.False(t, timer2.Reset(2*time.Second)) 387 require.False(t, timer3.Reset(3*time.Second)) 388 } 389 390 // Ensure that all timers covered by the time change fire. 391 clk.Add(3 * time.Second) 392 requireTick(t, timer1.C) 393 requireTick(t, timer2.C) 394 requireTick(t, timer3.C) 395 396 require.False(t, timer1.Stop()) 397 require.False(t, timer2.Stop()) 398 require.False(t, timer3.Stop()) 399 400 requireNoTick(t, timer1.C) 401 requireNoTick(t, timer2.C) 402 requireNoTick(t, timer3.C) 403 } 404 405 func TestFakeClock_ManyTimers(t *testing.T) { 406 var ( 407 clk = clock.NewFakeClock() 408 timers []*clock.Timer 409 ) 410 411 // for i := 0; i < 1<<10; i++ { 412 for i := 0; i < 5; i++ { 413 timers = append(timers, clk.NewTimer(time.Second)) 414 } 415 416 for i := int64(0); i < 10; i++ { 417 for j := 0; j < len(timers); j++ { 418 requireNoTick(t, timers[j].C) 419 } 420 421 clk.Add(time.Second) 422 423 for j := 0; j < len(timers); j++ { 424 requireTick(t, timers[j].C) 425 require.False(t, timers[j].Reset(time.Second)) 426 } 427 } 428 429 for j := 0; j < len(timers); j++ { 430 require.True(t, timers[j].Stop()) 431 requireNoTick(t, timers[j].C) 432 } 433 } 434 435 func TestFakeClock_Timer_DoubleStop(t *testing.T) { 436 cases := [][]time.Duration{ 437 {time.Second, 5 * time.Second}, 438 {5 * time.Second, time.Second}, 439 {time.Second, time.Second}, 440 {time.Second, 2 * time.Second, 3 * time.Second}, 441 {3 * time.Second, 2 * time.Second, time.Second}, 442 } 443 444 var ( 445 clk = clock.NewFakeClock() 446 timers []*clock.Timer 447 ) 448 449 for _, durations := range cases { 450 for _, dur := range durations { 451 timers = append(timers, clk.NewTimer(dur)) 452 } 453 454 for _, timer := range timers { 455 require.True(t, timer.Stop()) 456 require.False(t, timer.Stop()) 457 } 458 459 timers = nil 460 } 461 } 462 463 func TestFakeClock_Stopwatch(t *testing.T) { 464 var ( 465 clk = clock.NewFakeClock() 466 stopwatch = clk.NewStopwatch() 467 ) 468 469 require.Equal(t, 0*time.Second, stopwatch.Elapsed()) 470 471 clk.Add(time.Second) 472 require.Equal(t, time.Second, stopwatch.Elapsed()) 473 474 clk.Add(time.Second) 475 require.Equal(t, 2*time.Second, stopwatch.Elapsed()) 476 require.Equal(t, 2*time.Second, stopwatch.Reset()) 477 require.Equal(t, 0*time.Second, stopwatch.Elapsed()) 478 479 clk.Add(time.Second) 480 require.Equal(t, time.Second, stopwatch.Elapsed()) 481 } 482 483 func requireClockSince(t *testing.T, expect int64, since int64, clk *clock.FakeClock) { 484 require.EqualValues(t, expect, clk.Since(time.Unix(0, since))) 485 require.EqualValues(t, expect, clk.SinceNanotime(since)) 486 } 487 488 func requireClockIs(t *testing.T, expect int64, clk *clock.FakeClock) { 489 requireTimeIs(t, expect, clk.Now()) 490 requireNanotimeIs(t, expect, clk.Nanotime()) 491 } 492 493 func requireNanotimeIs(t *testing.T, expect int64, ns int64) { 494 require.Equal(t, expect, ns) 495 } 496 497 func requireTimeIs(t *testing.T, expect int64, ts time.Time) { 498 require.EqualValues(t, expect, ts.UnixNano()) 499 } 500 501 func requireTick(t *testing.T, ch <-chan time.Time) (ts time.Time) { 502 select { 503 case ts = <-ch: 504 case <-time.After(time.Second): 505 require.Fail(t, "timed out waiting for tick") 506 } 507 return 508 } 509 510 func requireNoTick(t *testing.T, ch <-chan time.Time) { 511 select { 512 case <-ch: 513 require.Fail(t, "unexpected tick") 514 default: 515 } 516 } 517 518 func waitFor(t *testing.T, d time.Duration, f func() bool) { 519 start := chrono.Nanotime() 520 for !f() { 521 if time.Duration(chrono.Nanotime()-start) >= d { 522 require.Fail(t, "timeout", "waited for %v", d) 523 } 524 time.Sleep(d >> 8) 525 } 526 }