// Ensure that the mock's Ticker can be stopped. func TestMock_Ticker_Stop(t *testing.T) { var n int32 clock := clock.NewMock() // Create a channel to increment every second. ticker := clock.Ticker(1 * time.Second) go func() { for { <-ticker.C atomic.AddInt32(&n, 1) } }() gosched() // Move clock forward. clock.Add(5 * time.Second) if atomic.LoadInt32(&n) != 5 { t.Fatalf("expected 5, got: %d", n) } ticker.Stop() // Move clock forward again. clock.Add(5 * time.Second) if atomic.LoadInt32(&n) != 5 { t.Fatalf("still expected 5, got: %d", n) } }
// Ensure that multiple tickers can be used together. func TestMock_Ticker_Multi(t *testing.T) { var n int32 clock := clock.NewMock() go func() { a := clock.Ticker(1 * time.Microsecond) b := clock.Ticker(3 * time.Microsecond) for { select { case <-a.C: atomic.AddInt32(&n, 1) case <-b.C: atomic.AddInt32(&n, 100) } } }() gosched() // Move clock forward. clock.Add(10 * time.Microsecond) gosched() if atomic.LoadInt32(&n) != 310 { t.Fatalf("unexpected: %d", n) } }
func TestBreakerEvents(t *testing.T) { c := clock.NewMock() cb := NewBreaker() cb.Clock = c events := cb.Subscribe() cb.Trip() if e := <-events; e != BreakerTripped { t.Fatalf("expected to receive a trip event, got %d", e) } c.Add(cb.nextBackOff + 1) cb.Ready() if e := <-events; e != BreakerReady { t.Fatalf("expected to receive a breaker ready event, got %d", e) } cb.Reset() if e := <-events; e != BreakerReset { t.Fatalf("expected to receive a reset event, got %d", e) } cb.Fail() if e := <-events; e != BreakerFail { t.Fatalf("expected to receive a fail event, got %d", e) } }
func ExampleMock_Ticker() { // Create a new mock clock. clock := clock.NewMock() count := 0 // Increment count every mock second. go func() { ticker := clock.Ticker(1 * time.Second) for { <-ticker.C count++ } }() runtime.Gosched() // Move the clock forward 10 seconds and print the new value. clock.Add(10 * time.Second) fmt.Printf("Count is %d after 10 seconds\n", count) // Move the clock forward 5 more seconds and print the new value. clock.Add(5 * time.Second) fmt.Printf("Count is %d after 15 seconds\n", count) // Output: // Count is 10 after 10 seconds // Count is 15 after 15 seconds }
func ExampleMock_After() { // Create a new mock clock. clock := clock.NewMock() count := 0 // Create a channel to execute after 10 mock seconds. go func() { <-clock.After(10 * time.Second) count = 100 }() runtime.Gosched() // Print the starting value. fmt.Printf("%s: %d\n", clock.Now().UTC(), count) // Move the clock forward 5 seconds and print the value again. clock.Add(5 * time.Second) fmt.Printf("%s: %d\n", clock.Now().UTC(), count) // Move the clock forward 5 seconds to the tick time and check the value. clock.Add(5 * time.Second) fmt.Printf("%s: %d\n", clock.Now().UTC(), count) // Output: // 1970-01-01 00:00:00 +0000 UTC: 0 // 1970-01-01 00:00:05 +0000 UTC: 0 // 1970-01-01 00:00:10 +0000 UTC: 100 }
func TestThresholdBreakerResets(t *testing.T) { called := 0 success := false circuit := func() error { if called == 0 { called++ return fmt.Errorf("error") } success = true return nil } c := clock.NewMock() cb := NewThresholdBreaker(1) cb.Clock = c err := cb.Call(circuit, 0) if err == nil { t.Fatal("Expected cb to return an error") } c.Add(cb.nextBackOff + 1) for i := 0; i < 4; i++ { err = cb.Call(circuit, 0) if err != nil { t.Fatal("Expected cb to be successful") } if !success { t.Fatal("Expected cb to have been reset") } } }
// The following example shows how to create mock hashers for testing the rate // limiter in your code: func ExamplePerSecondHasher() { // Create a mock clock. mock := clock.NewMock() // Create a new per second hasher with the mock clock. hasher := PerMinuteHasher{ Clock: mock, } // Generate two consecutive hashes. On most systems, the following should // generate two identical hashes. hashOne := hasher.Hash("127.0.0.1") hashTwo := hasher.Hash("127.0.0.1") // Now we push the clock forward by a minute (time travel). mock.Add(time.Minute) // The third hash should be different now. hashThree := hasher.Hash("127.0.0.1") fmt.Println(hashOne == hashTwo) fmt.Println(hashOne == hashThree) // Output: true // false }
func Test_PerMinute_Hash(t *testing.T) { mock := clock.NewMock() hasher := PerMinuteHasher{ Clock: mock, } resultOne := hasher.Hash("127.0.0.1") mock.Add(time.Minute) resultTwo := hasher.Hash("127.0.0.1") assert.NotEqual(t, resultOne, resultTwo) resultThree := hasher.Hash("127.0.0.1") resultFour := hasher.Hash("127.0.0.1") resultFive := hasher.Hash("127.0.0.2") assert.Equal(t, resultThree, resultFour) assert.NotEqual(t, resultFour, resultFive) // Test that it can create a new clock hasher = PerMinuteHasher{} hasher.Hash("127.0.0.1") }
// Ensure that the mock's Tick channel sends at the correct time. func TestMock_Tick(t *testing.T) { var n int32 clock := clock.NewMock() // Create a channel to increment every 10 seconds. go func() { tick := clock.Tick(10 * time.Second) for { <-tick atomic.AddInt32(&n, 1) } }() gosched() // Move clock forward to just before the first tick. clock.Add(9 * time.Second) if atomic.LoadInt32(&n) != 0 { t.Fatalf("expected 0, got %d", n) } // Move clock forward to the start of the first tick. clock.Add(1 * time.Second) if atomic.LoadInt32(&n) != 1 { t.Fatalf("expected 1, got %d", n) } // Move clock forward over several ticks. clock.Add(30 * time.Second) if atomic.LoadInt32(&n) != 4 { t.Fatalf("expected 4, got %d", n) } }
// Ensure that the mock's current time can be changed. func TestMock_Now(t *testing.T) { clock := clock.NewMock() if now := clock.Now(); !now.Equal(time.Unix(0, 0)) { t.Fatalf("expected epoch, got: ", now) } // Add 10 seconds and check the time. clock.Add(10 * time.Second) if now := clock.Now(); !now.Equal(time.Unix(10, 0)) { t.Fatalf("expected epoch, got: ", now) } }
func NewHarness(t testing.TB) *Harness { te := Harness{ T: t, Clock: clock.NewMock(), } te.Env = &Env{ Out: &te.Out, Err: &te.Err, Clock: te.Clock, ParseAPIClient: &ParseAPIClient{APIClient: &parse.Client{}}, } return &te }
// Ensure that the mock's AfterFunc doesn't execute if stopped. func TestMock_AfterFunc_Stop(t *testing.T) { // Execute function after duration. clock := clock.NewMock() timer := clock.AfterFunc(10*time.Second, func() { t.Fatal("unexpected function execution") }) gosched() // Stop timer & move clock forward. timer.Stop() clock.Add(10 * time.Second) gosched() }
func newHarness(t testing.TB) *Harness { te := Harness{ T: t, Clock: clock.NewMock(), } te.env = &env{ Out: &te.Out, Err: &te.Err, Clock: te.Clock, Client: &Client{client: &parse.Client{}}, } return &te }
func TestHTTPStopTimeoutMissed(t *testing.T) { t.Parallel() klock := clock.NewMock() const count = 10000 hello := []byte("hello") finOkHandler := make(chan struct{}) unblockOkHandler := make(chan struct{}) okHandler := func(w http.ResponseWriter, r *http.Request) { defer close(finOkHandler) w.Header().Set("Content-Length", fmt.Sprint(len(hello)*count)) w.WriteHeader(200) for i := 0; i < count/2; i++ { w.Write(hello) } <-unblockOkHandler for i := 0; i < count/2; i++ { w.Write(hello) } } listener, err := net.Listen("tcp", "127.0.0.1:0") ensure.Nil(t, err) server := &http.Server{Handler: http.HandlerFunc(okHandler)} transport := &http.Transport{} client := &http.Client{Transport: transport} down := &httpdown.HTTP{ StopTimeout: time.Minute, Clock: klock, } s := down.Serve(server, listener) res, err := client.Get(fmt.Sprintf("http://%s/", listener.Addr().String())) ensure.Nil(t, err) finStop := make(chan struct{}) go func() { defer close(finStop) ensure.Nil(t, s.Stop()) }() klock.Wait(clock.Calls{After: 1}) // wait for Stop to call After klock.Add(down.StopTimeout) _, err = ioutil.ReadAll(res.Body) ensure.Err(t, err, regexp.MustCompile("^unexpected EOF$")) ensure.Nil(t, res.Body.Close()) close(unblockOkHandler) <-finOkHandler <-finStop }
func TestStatsTicker(t *testing.T) { t.Parallel() klock := clock.NewMock() expected := []string{"alive", "idle", "out", "waiting"} statsDone := make(chan string, 4) hc := &stats.HookClient{ BumpAvgHook: func(key string, val float64) { if contains(expected, key) { statsDone <- key } }, } var cm resourceMaker p := Pool{ New: cm.New, Stats: hc, Max: 4, MinIdle: 2, IdleTimeout: time.Second, ClosePoolSize: 2, Clock: klock, } // acquire and release some resources to make them idle var resources []io.Closer for i := p.Max; i > 0; i-- { r, err := p.Acquire() ensure.Nil(t, err) resources = append(resources, r) } for _, r := range resources { p.Release(r) } // tick IdleTimeout to make them eligible klock.Add(p.IdleTimeout) // tick Minute to trigger stats klock.Add(time.Minute) // stats should soon show idle closed ensure.SameElements( t, []string{<-statsDone, <-statsDone, <-statsDone, <-statsDone}, expected, ) }
func TestTrippableBreakerManualBreak(t *testing.T) { c := clock.NewMock() cb := NewBreaker() cb.Clock = c cb.Break() c.Add(cb.nextBackOff + 1) if cb.Ready() { t.Fatal("expected breaker to still be tripped") } cb.Reset() cb.Trip() c.Add(cb.nextBackOff + 1) if !cb.Ready() { t.Fatal("expected breaker to be ready") } }
func TestRateBreakerResets(t *testing.T) { serviceError := fmt.Errorf("service error") called := 0 success := false circuit := func() error { if called < 4 { called++ return serviceError } success = true return nil } c := clock.NewMock() cb := NewRateBreaker(0.5, 4) cb.Clock = c var err error for i := 0; i < 4; i++ { err = cb.Call(circuit, 0) if err == nil { t.Fatal("Expected cb to return an error (closed breaker, service failure)") } else if err != serviceError { t.Fatal("Expected cb to return error from service (closed breaker, service failure)") } } err = cb.Call(circuit, 0) if err == nil { t.Fatal("Expected cb to return an error (open breaker)") } else if err != ErrBreakerOpen { t.Fatal("Expected cb to return open open breaker error (open breaker)") } c.Add(cb.nextBackOff + 1) err = cb.Call(circuit, 0) if err != nil { t.Fatal("Expected cb to be successful") } if !success { t.Fatal("Expected cb to have been reset") } }
// Ensure that the mock's Ticker channel sends at the correct time. func TestMock_Ticker(t *testing.T) { var n int32 clock := clock.NewMock() // Create a channel to increment every microsecond. go func() { ticker := clock.Ticker(1 * time.Microsecond) for { <-ticker.C atomic.AddInt32(&n, 1) } }() gosched() // Move clock forward. clock.Add(10 * time.Microsecond) if atomic.LoadInt32(&n) != 10 { t.Fatalf("unexpected: %d", n) } }
func TestTimeoutBreaker(t *testing.T) { c := clock.NewMock() called := int32(0) circuit := func() error { atomic.AddInt32(&called, 1) c.Add(time.Millisecond) return nil } cb := NewThresholdBreaker(1) cb.Clock = c err := cb.Call(circuit, time.Millisecond) if err == nil { t.Fatal("expected timeout breaker to return an error") } cb.Call(circuit, time.Millisecond) if !cb.Tripped() { t.Fatal("expected timeout breaker to be open") } }
// Ensure that the mock's AfterFunc executes at the correct time. func TestMock_AfterFunc(t *testing.T) { var ok int32 clock := clock.NewMock() // Execute function after duration. clock.AfterFunc(10*time.Second, func() { atomic.StoreInt32(&ok, 1) }) // Move clock forward to just before the time. clock.Add(9 * time.Second) if atomic.LoadInt32(&ok) == 1 { t.Fatal("too early") } // Move clock forward to the after channel's time. clock.Add(1 * time.Second) if atomic.LoadInt32(&ok) == 0 { t.Fatal("too late") } }
func TestContiniousSendWithTimeoutOnly(t *testing.T) { t.Parallel() var fireTotal, addedTotal uint64 c := &testClient{ BatchTimeout: 5 * time.Millisecond, Fire: func(actual []string, notifier Notifier) { defer notifier.Done() atomic.AddUint64(&fireTotal, uint64(len(actual))) }, PendingWorkCapacity: 100, } klock := clock.NewMock() c.muster.klock = klock errCall(t, c.Start) finished := make(chan struct{}) go func() { defer close(finished) for { select { case <-finished: return case c.muster.Work <- "42": atomic.AddUint64(&addedTotal, 1) default: } } }() for i := 0; i < 3; i++ { klock.Add(c.BatchTimeout) } finished <- struct{}{} <-finished errCall(t, c.Stop) if fireTotal != addedTotal { t.Fatalf("fireTotal=%d VS addedTotal=%d", fireTotal, addedTotal) } }
func ExampleMock_AfterFunc() { // Create a new mock clock. clock := clock.NewMock() count := 0 // Execute a function after 10 mock seconds. clock.AfterFunc(10*time.Second, func() { count = 100 }) runtime.Gosched() // Print the starting value. fmt.Printf("%s: %d\n", clock.Now().UTC(), count) // Move the clock forward 10 seconds and print the new value. clock.Add(10 * time.Second) fmt.Printf("%s: %d\n", clock.Now().UTC(), count) // Output: // 1970-01-01 00:00:00 +0000 UTC: 0 // 1970-01-01 00:00:10 +0000 UTC: 100 }
// TestPartialSecondBackoff ensures that the breaker event less than nextBackoff value // time after tripping the breaker isn't allowed. func TestPartialSecondBackoff(t *testing.T) { c := clock.NewMock() cb := NewBreaker() cb.Clock = c // Set the time to 0.5 seconds after the epoch, then trip the breaker. c.Add(500 * time.Millisecond) cb.Trip() // Move forward 100 milliseconds in time and ensure that the backoff time // is set to a larger number than the clock advanced. c.Add(100 * time.Millisecond) cb.nextBackOff = 500 * time.Millisecond if cb.Ready() { t.Fatalf("expected breaker not to be ready after less time than nextBackoff had passed") } c.Add(401 * time.Millisecond) if !cb.Ready() { t.Fatalf("expected breaker to be ready after more than nextBackoff time had passed") } }
// Ensure that the mock's After channel sends at the correct time. func TestMock_After(t *testing.T) { var ok int32 clock := clock.NewMock() // Create a channel to execute after 10 mock seconds. ch := clock.After(10 * time.Second) go func(ch <-chan time.Time) { <-ch atomic.StoreInt32(&ok, 1) }(ch) // Move clock forward to just before the time. clock.Add(9 * time.Second) if atomic.LoadInt32(&ok) == 1 { t.Fatal("too early") } // Move clock forward to the after channel's time. clock.Add(1 * time.Second) if atomic.LoadInt32(&ok) == 0 { t.Fatal("too late") } }
// Ensure that the mock can sleep for the correct time. func TestMock_Sleep(t *testing.T) { var ok int32 clock := clock.NewMock() // Create a channel to execute after 10 mock seconds. go func() { clock.Sleep(10 * time.Second) atomic.StoreInt32(&ok, 1) }() gosched() // Move clock forward to just before the sleep duration. clock.Add(9 * time.Second) if atomic.LoadInt32(&ok) == 1 { t.Fatal("too early") } // Move clock forward to the after the sleep duration. clock.Add(1 * time.Second) if atomic.LoadInt32(&ok) == 0 { t.Fatal("too late") } }
func TestAddRemoveListener(t *testing.T) { c := clock.NewMock() cb := NewBreaker() cb.Clock = c events := make(chan ListenerEvent, 100) cb.AddListener(events) cb.Trip() if e := <-events; e.Event != BreakerTripped { t.Fatalf("expected to receive a trip event, got %v", e) } c.Add(cb.nextBackOff + 1) cb.Ready() if e := <-events; e.Event != BreakerReady { t.Fatalf("expected to receive a breaker ready event, got %v", e) } cb.Reset() if e := <-events; e.Event != BreakerReset { t.Fatalf("expected to receive a reset event, got %v", e) } cb.Fail() if e := <-events; e.Event != BreakerFail { t.Fatalf("expected to receive a fail event, got %v", e) } cb.RemoveListener(events) cb.Reset() select { case e := <-events: t.Fatalf("after removing listener, should not receive reset event; got %v", e) default: // Expected. } }
func TestWindowSlides(t *testing.T) { c := clock.NewMock() w := newWindow(time.Millisecond*10, 2) w.clock = c w.lastAccess = c.Now() w.Fail() c.Add(time.Millisecond * 6) w.Fail() counts := 0 w.buckets.Do(func(x interface{}) { b := x.(*bucket) if b.failure > 0 { counts++ } }) if counts != 2 { t.Fatalf("expected 2 buckets to have failures, got %d", counts) } c.Add(time.Millisecond * 15) w.Success() counts = 0 w.buckets.Do(func(x interface{}) { b := x.(*bucket) if b.failure > 0 { counts++ } }) if counts != 0 { t.Fatalf("expected 0 buckets to have failures, got %d", counts) } }
func TestTimeoutBreaker(t *testing.T) { wait := make(chan struct{}) c := clock.NewMock() called := int32(0) circuit := func() error { wait <- struct{}{} atomic.AddInt32(&called, 1) <-wait return nil } cb := NewThresholdBreaker(1) cb.Clock = c errc := make(chan error) go func() { errc <- cb.Call(circuit, time.Millisecond) }() <-wait c.Add(time.Millisecond * 3) wait <- struct{}{} err := <-errc if err == nil { t.Fatal("expected timeout breaker to return an error") } go cb.Call(circuit, time.Millisecond) <-wait c.Add(time.Millisecond * 3) wait <- struct{}{} if !cb.Tripped() { t.Fatal("expected timeout breaker to be open") } }
func TestTrippableBreakerState(t *testing.T) { c := clock.NewMock() cb := NewBreaker() cb.Clock = c if !cb.Ready() { t.Fatal("expected breaker to be ready") } cb.Trip() if cb.Ready() { t.Fatal("expected breaker to not be ready") } c.Add(cb.nextBackOff + 1) if !cb.Ready() { t.Fatal("expected breaker to be ready after reset timeout") } cb.Fail() c.Add(cb.nextBackOff + 1) if !cb.Ready() { t.Fatal("expected breaker to be ready after reset timeout, post failure") } }
func TestHTTPKillTimeoutMissed(t *testing.T) { t.Parallel() klock := clock.NewMock() statsDone := make(chan struct{}, 1) hc := &stats.HookClient{ BumpSumHook: func(key string, val float64) { if key == "kill.timeout" && val == 1 { statsDone <- struct{}{} } }, } const count = 10000 hello := []byte("hello") finOkHandler := make(chan struct{}) unblockOkHandler := make(chan struct{}) okHandler := func(w http.ResponseWriter, r *http.Request) { defer close(finOkHandler) w.Header().Set("Content-Length", fmt.Sprint(len(hello)*count)) w.WriteHeader(200) for i := 0; i < count/2; i++ { w.Write(hello) } <-unblockOkHandler for i := 0; i < count/2; i++ { w.Write(hello) } } listener, err := net.Listen("tcp", "127.0.0.1:0") ensure.Nil(t, err) unblockConnClose := make(chan chan struct{}, 1) listener = &closeErrConnListener{ Listener: listener, unblockClose: unblockConnClose, } server := &http.Server{Handler: http.HandlerFunc(okHandler)} transport := &http.Transport{} client := &http.Client{Transport: transport} down := &httpdown.HTTP{ StopTimeout: time.Minute, KillTimeout: time.Minute, Stats: hc, Clock: klock, } s := down.Serve(server, listener) res, err := client.Get(fmt.Sprintf("http://%s/", listener.Addr().String())) ensure.Nil(t, err) // Start the Stop process. finStop := make(chan struct{}) go func() { defer close(finStop) ensure.Nil(t, s.Stop()) }() klock.Wait(clock.Calls{After: 1}) // wait for Stop to call After klock.Add(down.StopTimeout) // trigger stop timeout klock.Wait(clock.Calls{After: 2}) // wait for Kill to call After klock.Add(down.KillTimeout) // trigger kill timeout // We hit both the StopTimeout & the KillTimeout. <-finStop // Then we unblock the Close, so we get an unexpected EOF since we close // before we finish writing the response. connCloseDone := make(chan struct{}) unblockConnClose <- connCloseDone <-connCloseDone close(unblockConnClose) // Then we unblock the handler which tries to write the rest of the data. close(unblockOkHandler) _, err = ioutil.ReadAll(res.Body) ensure.Err(t, err, regexp.MustCompile("^unexpected EOF$")) ensure.Nil(t, res.Body.Close()) <-finOkHandler <-statsDone }