rate: use fake time for testing

Fixes golang/go#43055

Change-Id: I2c6836f2da06b8a25f95b6ad494503051efe56fd
Reviewed-on: https://go-review.googlesource.com/c/time/+/411314
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Ian Lance Taylor <iant@google.com>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/rate/rate.go b/rate/rate.go
index e77ade3..12e813c 100644
--- a/rate/rate.go
+++ b/rate/rate.go
@@ -223,6 +223,18 @@
 // canceled, or the expected wait time exceeds the Context's Deadline.
 // The burst limit is ignored if the rate limit is Inf.
 func (lim *Limiter) WaitN(ctx context.Context, n int) (err error) {
+	// The test code calls lim.wait with a fake timer generator.
+	// This is the real timer generator.
+	newTimer := func(d time.Duration) (<-chan time.Time, func() bool, func()) {
+		timer := time.NewTimer(d)
+		return timer.C, timer.Stop, func() {}
+	}
+
+	return lim.wait(ctx, n, time.Now(), newTimer)
+}
+
+// wait is the internal implementation of WaitN.
+func (lim *Limiter) wait(ctx context.Context, n int, now time.Time, newTimer func(d time.Duration) (<-chan time.Time, func() bool, func())) error {
 	lim.mu.Lock()
 	burst := lim.burst
 	limit := lim.limit
@@ -238,7 +250,6 @@
 	default:
 	}
 	// Determine wait limit
-	now := time.Now()
 	waitLimit := InfDuration
 	if deadline, ok := ctx.Deadline(); ok {
 		waitLimit = deadline.Sub(now)
@@ -253,10 +264,11 @@
 	if delay == 0 {
 		return nil
 	}
-	t := time.NewTimer(delay)
-	defer t.Stop()
+	ch, stop, advance := newTimer(delay)
+	defer stop()
+	advance() // only has an effect when testing
 	select {
-	case <-t.C:
+	case <-ch:
 		// We can proceed.
 		return nil
 	case <-ctx.Done():
diff --git a/rate/rate_test.go b/rate/rate_test.go
index 5d5fabf..7bdd74a 100644
--- a/rate/rate_test.go
+++ b/rate/rate_test.go
@@ -10,7 +10,6 @@
 import (
 	"context"
 	"math"
-	"runtime"
 	"sync"
 	"sync/atomic"
 	"testing"
@@ -142,6 +141,93 @@
 	}
 }
 
+// testTime is a fake time used for testing.
+type testTime struct {
+	mu     sync.Mutex
+	cur    time.Time   // current fake time
+	timers []testTimer // fake timers
+}
+
+// testTimer is a fake timer.
+type testTimer struct {
+	when time.Time
+	ch   chan<- time.Time
+}
+
+// now returns the current fake time.
+func (tt *testTime) now() time.Time {
+	tt.mu.Lock()
+	defer tt.mu.Unlock()
+	return tt.cur
+}
+
+// newTimer creates a fake timer. It returns the channel,
+// a function to stop the timer (which we don't care about),
+// and a function to advance to the next timer.
+func (tt *testTime) newTimer(dur time.Duration) (<-chan time.Time, func() bool, func()) {
+	tt.mu.Lock()
+	defer tt.mu.Unlock()
+	ch := make(chan time.Time, 1)
+	timer := testTimer{
+		when: tt.cur.Add(dur),
+		ch:   ch,
+	}
+	tt.timers = append(tt.timers, timer)
+	return ch, func() bool { return true }, tt.advanceToTimer
+}
+
+// since returns the fake time since the given time.
+func (tt *testTime) since(t time.Time) time.Duration {
+	tt.mu.Lock()
+	defer tt.mu.Unlock()
+	return tt.cur.Sub(t)
+}
+
+// advance advances the fake time.
+func (tt *testTime) advance(dur time.Duration) {
+	tt.mu.Lock()
+	defer tt.mu.Unlock()
+	tt.advanceUnlocked(dur)
+}
+
+// advanceUnlock advances the fake time, assuming it is already locked.
+func (tt *testTime) advanceUnlocked(dur time.Duration) {
+	tt.cur = tt.cur.Add(dur)
+	i := 0
+	for i < len(tt.timers) {
+		if tt.timers[i].when.After(tt.cur) {
+			i++
+		} else {
+			tt.timers[i].ch <- tt.cur
+			copy(tt.timers[i:], tt.timers[i+1:])
+			tt.timers = tt.timers[:len(tt.timers)-1]
+		}
+	}
+}
+
+// advanceToTimer advances the time to the next timer.
+func (tt *testTime) advanceToTimer() {
+	tt.mu.Lock()
+	defer tt.mu.Unlock()
+	if len(tt.timers) == 0 {
+		panic("no timer")
+	}
+	when := tt.timers[0].when
+	for _, timer := range tt.timers[1:] {
+		if timer.when.Before(when) {
+			when = timer.when
+		}
+	}
+	tt.advanceUnlocked(when.Sub(tt.cur))
+}
+
+// makeTestTime hooks the testTimer into the package.
+func makeTestTime(t *testing.T) *testTime {
+	return &testTime{
+		cur: time.Now(),
+	}
+}
+
 func TestSimultaneousRequests(t *testing.T) {
 	const (
 		limit       = 1
@@ -176,44 +262,31 @@
 }
 
 func TestLongRunningQPS(t *testing.T) {
-	if testing.Short() {
-		t.Skip("skipping in short mode")
-	}
-	if runtime.GOOS == "openbsd" {
-		t.Skip("low resolution time.Sleep invalidates test (golang.org/issue/14183)")
-		return
-	}
-
-	// The test runs for a few seconds executing many requests and then checks
-	// that overall number of requests is reasonable.
+	// The test runs for a few (fake) seconds executing many requests
+	// and then checks that overall number of requests is reasonable.
 	const (
 		limit = 100
 		burst = 100
 	)
-	var numOK = int32(0)
+	var (
+		numOK = int32(0)
+		tt    = makeTestTime(t)
+	)
 
 	lim := NewLimiter(limit, burst)
 
-	var wg sync.WaitGroup
-	f := func() {
-		if ok := lim.Allow(); ok {
-			atomic.AddInt32(&numOK, 1)
-		}
-		wg.Done()
-	}
-
-	start := time.Now()
+	start := tt.now()
 	end := start.Add(5 * time.Second)
-	for time.Now().Before(end) {
-		wg.Add(1)
-		go f()
+	for tt.now().Before(end) {
+		if ok := lim.AllowN(tt.now(), 1); ok {
+			numOK++
+		}
 
 		// This will still offer ~500 requests per second, but won't consume
 		// outrageous amount of CPU.
-		time.Sleep(2 * time.Millisecond)
+		tt.advance(2 * time.Millisecond)
 	}
-	wg.Wait()
-	elapsed := time.Since(start)
+	elapsed := tt.since(start)
 	ideal := burst + (limit * float64(elapsed) / float64(time.Second))
 
 	// We should never get more requests than allowed.
@@ -402,11 +475,11 @@
 	nilErr bool
 }
 
-func runWait(t *testing.T, lim *Limiter, w wait) {
+func runWait(t *testing.T, tt *testTime, lim *Limiter, w wait) {
 	t.Helper()
-	start := time.Now()
-	err := lim.WaitN(w.ctx, w.n)
-	delay := time.Since(start)
+	start := tt.now()
+	err := lim.wait(w.ctx, w.n, start, tt.newTimer)
+	delay := tt.since(start)
 
 	if (w.nilErr && err != nil) || (!w.nilErr && err == nil) || !waitDelayOk(w.delay, delay) {
 		errString := "<nil>"
@@ -451,46 +524,55 @@
 }
 
 func TestWaitSimple(t *testing.T) {
+	tt := makeTestTime(t)
+
 	lim := NewLimiter(10, 3)
 
 	ctx, cancel := context.WithCancel(context.Background())
 	cancel()
-	runWait(t, lim, wait{"already-cancelled", ctx, 1, 0, false})
+	runWait(t, tt, lim, wait{"already-cancelled", ctx, 1, 0, false})
 
-	runWait(t, lim, wait{"exceed-burst-error", context.Background(), 4, 0, false})
+	runWait(t, tt, lim, wait{"exceed-burst-error", context.Background(), 4, 0, false})
 
-	runWait(t, lim, wait{"act-now", context.Background(), 2, 0, true})
-	runWait(t, lim, wait{"act-later", context.Background(), 3, 2, true})
+	runWait(t, tt, lim, wait{"act-now", context.Background(), 2, 0, true})
+	runWait(t, tt, lim, wait{"act-later", context.Background(), 3, 2, true})
 }
 
 func TestWaitCancel(t *testing.T) {
+	tt := makeTestTime(t)
+
 	lim := NewLimiter(10, 3)
 
 	ctx, cancel := context.WithCancel(context.Background())
-	runWait(t, lim, wait{"act-now", ctx, 2, 0, true}) // after this lim.tokens = 1
+	runWait(t, tt, lim, wait{"act-now", ctx, 2, 0, true}) // after this lim.tokens = 1
+	ch, _, _ := tt.newTimer(d)
 	go func() {
-		time.Sleep(d)
+		<-ch
 		cancel()
 	}()
-	runWait(t, lim, wait{"will-cancel", ctx, 3, 1, false})
+	runWait(t, tt, lim, wait{"will-cancel", ctx, 3, 1, false})
 	// should get 3 tokens back, and have lim.tokens = 2
 	t.Logf("tokens:%v last:%v lastEvent:%v", lim.tokens, lim.last, lim.lastEvent)
-	runWait(t, lim, wait{"act-now-after-cancel", context.Background(), 2, 0, true})
+	runWait(t, tt, lim, wait{"act-now-after-cancel", context.Background(), 2, 0, true})
 }
 
 func TestWaitTimeout(t *testing.T) {
+	tt := makeTestTime(t)
+
 	lim := NewLimiter(10, 3)
 
 	ctx, cancel := context.WithTimeout(context.Background(), d)
 	defer cancel()
-	runWait(t, lim, wait{"act-now", ctx, 2, 0, true})
-	runWait(t, lim, wait{"w-timeout-err", ctx, 3, 0, false})
+	runWait(t, tt, lim, wait{"act-now", ctx, 2, 0, true})
+	runWait(t, tt, lim, wait{"w-timeout-err", ctx, 3, 0, false})
 }
 
 func TestWaitInf(t *testing.T) {
+	tt := makeTestTime(t)
+
 	lim := NewLimiter(Inf, 0)
 
-	runWait(t, lim, wait{"exceed-burst-no-error", context.Background(), 3, 0, true})
+	runWait(t, tt, lim, wait{"exceed-burst-no-error", context.Background(), 3, 0, true})
 }
 
 func BenchmarkAllowN(b *testing.B) {