github.com/crowdsecurity/crowdsec@v1.6.1/pkg/time/rate/rate_test.go (about)

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build go1.7
     6  // +build go1.7
     7  
     8  package rate
     9  
    10  import (
    11  	"context"
    12  	"math"
    13  	"runtime"
    14  	"sync"
    15  	"sync/atomic"
    16  	"testing"
    17  	"time"
    18  )
    19  
    20  func TestLimit(t *testing.T) {
    21  	if Limit(10) == Inf {
    22  		t.Errorf("Limit(10) == Inf should be false")
    23  	}
    24  }
    25  
    26  func closeEnough(a, b Limit) bool {
    27  	return (math.Abs(float64(a)/float64(b)) - 1.0) < 1e-9
    28  }
    29  
    30  func TestEvery(t *testing.T) {
    31  	cases := []struct {
    32  		interval time.Duration
    33  		lim      Limit
    34  	}{
    35  		{0, Inf},
    36  		{-1, Inf},
    37  		{1 * time.Nanosecond, Limit(1e9)},
    38  		{1 * time.Microsecond, Limit(1e6)},
    39  		{1 * time.Millisecond, Limit(1e3)},
    40  		{10 * time.Millisecond, Limit(100)},
    41  		{100 * time.Millisecond, Limit(10)},
    42  		{1 * time.Second, Limit(1)},
    43  		{2 * time.Second, Limit(0.5)},
    44  		{time.Duration(2.5 * float64(time.Second)), Limit(0.4)},
    45  		{4 * time.Second, Limit(0.25)},
    46  		{10 * time.Second, Limit(0.1)},
    47  		{time.Duration(math.MaxInt64), Limit(1e9 / float64(math.MaxInt64))},
    48  	}
    49  	for _, tc := range cases {
    50  		lim := Every(tc.interval)
    51  		if !closeEnough(lim, tc.lim) {
    52  			t.Errorf("Every(%v) = %v want %v", tc.interval, lim, tc.lim)
    53  		}
    54  	}
    55  }
    56  
    57  const (
    58  	d = 100 * time.Millisecond
    59  )
    60  
    61  var (
    62  	t0 = time.Now()
    63  	t1 = t0.Add(time.Duration(1) * d)
    64  	t2 = t0.Add(time.Duration(2) * d)
    65  	t3 = t0.Add(time.Duration(3) * d)
    66  	t4 = t0.Add(time.Duration(4) * d)
    67  	t5 = t0.Add(time.Duration(5) * d)
    68  	t9 = t0.Add(time.Duration(9) * d)
    69  )
    70  
    71  type allow struct {
    72  	t  time.Time
    73  	n  int
    74  	ok bool
    75  }
    76  
    77  func run(t *testing.T, lim *Limiter, allows []allow) {
    78  	for i, allow := range allows {
    79  		ok := lim.AllowN(allow.t, allow.n)
    80  		if ok != allow.ok {
    81  			t.Errorf("step %d: lim.AllowN(%v, %v) = %v want %v",
    82  				i, allow.t, allow.n, ok, allow.ok)
    83  		}
    84  	}
    85  }
    86  
    87  func TestLimiterBurst1(t *testing.T) {
    88  	run(t, NewLimiter(10, 1), []allow{
    89  		{t0, 1, true},
    90  		{t0, 1, false},
    91  		{t0, 1, false},
    92  		{t1, 1, true},
    93  		{t1, 1, false},
    94  		{t1, 1, false},
    95  		{t2, 2, false}, // burst size is 1, so n=2 always fails
    96  		{t2, 1, true},
    97  		{t2, 1, false},
    98  	})
    99  }
   100  
   101  func TestLimiterBurst3(t *testing.T) {
   102  	run(t, NewLimiter(10, 3), []allow{
   103  		{t0, 2, true},
   104  		{t0, 2, false},
   105  		{t0, 1, true},
   106  		{t0, 1, false},
   107  		{t1, 4, false},
   108  		{t2, 1, true},
   109  		{t3, 1, true},
   110  		{t4, 1, true},
   111  		{t4, 1, true},
   112  		{t4, 1, false},
   113  		{t4, 1, false},
   114  		{t9, 3, true},
   115  		{t9, 0, true},
   116  	})
   117  }
   118  
   119  func TestLimiterJumpBackwards(t *testing.T) {
   120  	run(t, NewLimiter(10, 3), []allow{
   121  		{t1, 1, true}, // start at t1
   122  		{t0, 1, true}, // jump back to t0, two tokens remain
   123  		{t0, 1, true},
   124  		{t0, 1, false},
   125  		{t0, 1, false},
   126  		{t1, 1, true}, // got a token
   127  		{t1, 1, false},
   128  		{t1, 1, false},
   129  		{t2, 1, true}, // got another token
   130  		{t2, 1, false},
   131  		{t2, 1, false},
   132  	})
   133  }
   134  
   135  // Ensure that tokensFromDuration doesn't produce
   136  // rounding errors by truncating nanoseconds.
   137  // See golang.org/issues/34861.
   138  func TestLimiter_noTruncationErrors(t *testing.T) {
   139  	if !NewLimiter(0.7692307692307693, 1).Allow() {
   140  		t.Fatal("expected true")
   141  	}
   142  }
   143  
   144  func TestSimultaneousRequests(t *testing.T) {
   145  	const (
   146  		limit       = 1
   147  		burst       = 5
   148  		numRequests = 15
   149  	)
   150  	var (
   151  		wg    sync.WaitGroup
   152  		numOK = uint32(0)
   153  	)
   154  
   155  	// Very slow replenishing bucket.
   156  	lim := NewLimiter(limit, burst)
   157  
   158  	// Tries to take a token, atomically updates the counter and decreases the wait
   159  	// group counter.
   160  	f := func() {
   161  		defer wg.Done()
   162  		if ok := lim.Allow(); ok {
   163  			atomic.AddUint32(&numOK, 1)
   164  		}
   165  	}
   166  
   167  	wg.Add(numRequests)
   168  	for i := 0; i < numRequests; i++ {
   169  		go f()
   170  	}
   171  	wg.Wait()
   172  	if numOK != burst {
   173  		t.Errorf("numOK = %d, want %d", numOK, burst)
   174  	}
   175  }
   176  
   177  func TestLongRunningQPS(t *testing.T) {
   178  	if testing.Short() {
   179  		t.Skip("skipping in short mode")
   180  	}
   181  	if runtime.GOOS == "openbsd" {
   182  		t.Skip("low resolution time.Sleep invalidates test (golang.org/issue/14183)")
   183  		return
   184  	}
   185  	if runtime.GOOS == "windows" {
   186  		t.Skip("test is unreliable on windows")
   187  	}
   188  
   189  	// The test runs for a few seconds executing many requests and then checks
   190  	// that overall number of requests is reasonable.
   191  	const (
   192  		limit = 100
   193  		burst = 100
   194  	)
   195  	var numOK = int32(0)
   196  
   197  	lim := NewLimiter(limit, burst)
   198  
   199  	var wg sync.WaitGroup
   200  	f := func() {
   201  		if ok := lim.Allow(); ok {
   202  			atomic.AddInt32(&numOK, 1)
   203  		}
   204  		wg.Done()
   205  	}
   206  
   207  	start := time.Now()
   208  	end := start.Add(5 * time.Second)
   209  	for time.Now().Before(end) {
   210  		wg.Add(1)
   211  		go f()
   212  
   213  		// This will still offer ~500 requests per second, but won't consume
   214  		// outrageous amount of CPU.
   215  		time.Sleep(2 * time.Millisecond)
   216  	}
   217  	wg.Wait()
   218  	elapsed := time.Since(start)
   219  	ideal := burst + (limit * float64(elapsed) / float64(time.Second))
   220  
   221  	// We should never get more requests than allowed.
   222  	if want := int32(ideal + 1); numOK > want {
   223  		t.Errorf("numOK = %d, want %d (ideal %f)", numOK, want, ideal)
   224  	}
   225  	// We should get very close to the number of requests allowed.
   226  	if want := int32(0.999 * ideal); numOK < want {
   227  		t.Errorf("numOK = %d, want %d (ideal %f)", numOK, want, ideal)
   228  	}
   229  }
   230  
   231  type request struct {
   232  	t   time.Time
   233  	n   int
   234  	act time.Time
   235  	ok  bool
   236  }
   237  
   238  // dFromDuration converts a duration to a multiple of the global constant d
   239  func dFromDuration(dur time.Duration) int {
   240  	// Adding a millisecond to be swallowed by the integer division
   241  	// because we don't care about small inaccuracies
   242  	return int((dur + time.Millisecond) / d)
   243  }
   244  
   245  // dSince returns multiples of d since t0
   246  func dSince(t time.Time) int {
   247  	return dFromDuration(t.Sub(t0))
   248  }
   249  
   250  func runReserve(t *testing.T, lim *Limiter, req request) *Reservation {
   251  	return runReserveMax(t, lim, req, InfDuration)
   252  }
   253  
   254  func runReserveMax(t *testing.T, lim *Limiter, req request, maxReserve time.Duration) *Reservation {
   255  	r := lim.reserveN(req.t, req.n, maxReserve)
   256  	if r.ok && (dSince(r.timeToAct) != dSince(req.act)) || r.ok != req.ok {
   257  		t.Errorf("lim.reserveN(t%d, %v, %v) = (t%d, %v) want (t%d, %v)",
   258  			dSince(req.t), req.n, maxReserve, dSince(r.timeToAct), r.ok, dSince(req.act), req.ok)
   259  	}
   260  	return &r
   261  }
   262  
   263  func TestSimpleReserve(t *testing.T) {
   264  	lim := NewLimiter(10, 2)
   265  
   266  	runReserve(t, lim, request{t0, 2, t0, true})
   267  	runReserve(t, lim, request{t0, 2, t2, true})
   268  	runReserve(t, lim, request{t3, 2, t4, true})
   269  }
   270  
   271  func TestMix(t *testing.T) {
   272  	lim := NewLimiter(10, 2)
   273  
   274  	runReserve(t, lim, request{t0, 3, t1, false}) // should return false because n > Burst
   275  	runReserve(t, lim, request{t0, 2, t0, true})
   276  	run(t, lim, []allow{{t1, 2, false}}) // not enough tokens - don't allow
   277  	runReserve(t, lim, request{t1, 2, t2, true})
   278  	run(t, lim, []allow{{t1, 1, false}}) // negative tokens - don't allow
   279  	run(t, lim, []allow{{t3, 1, true}})
   280  }
   281  
   282  func TestCancelInvalid(t *testing.T) {
   283  	lim := NewLimiter(10, 2)
   284  
   285  	runReserve(t, lim, request{t0, 2, t0, true})
   286  	r := runReserve(t, lim, request{t0, 3, t3, false})
   287  	r.CancelAt(t0)                               // should have no effect
   288  	runReserve(t, lim, request{t0, 2, t2, true}) // did not get extra tokens
   289  }
   290  
   291  func TestCancelLast(t *testing.T) {
   292  	lim := NewLimiter(10, 2)
   293  
   294  	runReserve(t, lim, request{t0, 2, t0, true})
   295  	r := runReserve(t, lim, request{t0, 2, t2, true})
   296  	r.CancelAt(t1) // got 2 tokens back
   297  	runReserve(t, lim, request{t1, 2, t2, true})
   298  }
   299  
   300  func TestCancelTooLate(t *testing.T) {
   301  	lim := NewLimiter(10, 2)
   302  
   303  	runReserve(t, lim, request{t0, 2, t0, true})
   304  	r := runReserve(t, lim, request{t0, 2, t2, true})
   305  	r.CancelAt(t3) // too late to cancel - should have no effect
   306  	runReserve(t, lim, request{t3, 2, t4, true})
   307  }
   308  
   309  func TestCancel0Tokens(t *testing.T) {
   310  	lim := NewLimiter(10, 2)
   311  
   312  	runReserve(t, lim, request{t0, 2, t0, true})
   313  	r := runReserve(t, lim, request{t0, 1, t1, true})
   314  	runReserve(t, lim, request{t0, 1, t2, true})
   315  	r.CancelAt(t0) // got 0 tokens back
   316  	runReserve(t, lim, request{t0, 1, t3, true})
   317  }
   318  
   319  func TestCancel1Token(t *testing.T) {
   320  	lim := NewLimiter(10, 2)
   321  
   322  	runReserve(t, lim, request{t0, 2, t0, true})
   323  	r := runReserve(t, lim, request{t0, 2, t2, true})
   324  	runReserve(t, lim, request{t0, 1, t3, true})
   325  	r.CancelAt(t2) // got 1 token back
   326  	runReserve(t, lim, request{t2, 2, t4, true})
   327  }
   328  
   329  func TestCancelMulti(t *testing.T) {
   330  	lim := NewLimiter(10, 4)
   331  
   332  	runReserve(t, lim, request{t0, 4, t0, true})
   333  	rA := runReserve(t, lim, request{t0, 3, t3, true})
   334  	runReserve(t, lim, request{t0, 1, t4, true})
   335  	rC := runReserve(t, lim, request{t0, 1, t5, true})
   336  	rC.CancelAt(t1) // get 1 token back
   337  	rA.CancelAt(t1) // get 2 tokens back, as if C was never reserved
   338  	runReserve(t, lim, request{t1, 3, t5, true})
   339  }
   340  
   341  func TestReserveJumpBack(t *testing.T) {
   342  	lim := NewLimiter(10, 2)
   343  
   344  	runReserve(t, lim, request{t1, 2, t1, true}) // start at t1
   345  	runReserve(t, lim, request{t0, 1, t1, true}) // should violate Limit,Burst
   346  	runReserve(t, lim, request{t2, 2, t3, true})
   347  }
   348  
   349  func TestReserveJumpBackCancel(t *testing.T) {
   350  	lim := NewLimiter(10, 2)
   351  
   352  	runReserve(t, lim, request{t1, 2, t1, true}) // start at t1
   353  	r := runReserve(t, lim, request{t1, 2, t3, true})
   354  	runReserve(t, lim, request{t1, 1, t4, true})
   355  	r.CancelAt(t0)                               // cancel at t0, get 1 token back
   356  	runReserve(t, lim, request{t1, 2, t4, true}) // should violate Limit,Burst
   357  }
   358  
   359  func TestReserveSetLimit(t *testing.T) {
   360  	lim := NewLimiter(5, 2)
   361  
   362  	runReserve(t, lim, request{t0, 2, t0, true})
   363  	runReserve(t, lim, request{t0, 2, t4, true})
   364  	lim.SetLimitAt(t2, 10)
   365  	runReserve(t, lim, request{t2, 1, t4, true}) // violates Limit and Burst
   366  }
   367  
   368  func TestReserveSetBurst(t *testing.T) {
   369  	lim := NewLimiter(5, 2)
   370  
   371  	runReserve(t, lim, request{t0, 2, t0, true})
   372  	runReserve(t, lim, request{t0, 2, t4, true})
   373  	lim.SetBurstAt(t3, 4)
   374  	runReserve(t, lim, request{t0, 4, t9, true}) // violates Limit and Burst
   375  }
   376  
   377  func TestReserveSetLimitCancel(t *testing.T) {
   378  	lim := NewLimiter(5, 2)
   379  
   380  	runReserve(t, lim, request{t0, 2, t0, true})
   381  	r := runReserve(t, lim, request{t0, 2, t4, true})
   382  	lim.SetLimitAt(t2, 10)
   383  	r.CancelAt(t2) // 2 tokens back
   384  	runReserve(t, lim, request{t2, 2, t3, true})
   385  }
   386  
   387  func TestReserveMax(t *testing.T) {
   388  	lim := NewLimiter(10, 2)
   389  	maxT := d
   390  
   391  	runReserveMax(t, lim, request{t0, 2, t0, true}, maxT)
   392  	runReserveMax(t, lim, request{t0, 1, t1, true}, maxT)  // reserve for close future
   393  	runReserveMax(t, lim, request{t0, 1, t2, false}, maxT) // time to act too far in the future
   394  }
   395  
   396  type wait struct {
   397  	name   string
   398  	ctx    context.Context
   399  	n      int
   400  	delay  int // in multiples of d
   401  	nilErr bool
   402  }
   403  
   404  func runWait(t *testing.T, lim *Limiter, w wait) {
   405  	start := time.Now()
   406  	err := lim.WaitN(w.ctx, w.n)
   407  	delay := time.Since(start)
   408  	if (w.nilErr && err != nil) || (!w.nilErr && err == nil) || w.delay != dFromDuration(delay) {
   409  		errString := "<nil>"
   410  		if !w.nilErr {
   411  			errString = "<non-nil error>"
   412  		}
   413  		t.Errorf("lim.WaitN(%v, lim, %v) = %v with delay %v ; want %v with delay %v",
   414  			w.name, w.n, err, delay, errString, d*time.Duration(w.delay))
   415  	}
   416  }
   417  
   418  func TestWaitSimple(t *testing.T) {
   419  	lim := NewLimiter(10, 3)
   420  
   421  	ctx, cancel := context.WithCancel(context.Background())
   422  	cancel()
   423  	runWait(t, lim, wait{"already-cancelled", ctx, 1, 0, false})
   424  
   425  	runWait(t, lim, wait{"exceed-burst-error", context.Background(), 4, 0, false})
   426  
   427  	runWait(t, lim, wait{"act-now", context.Background(), 2, 0, true})
   428  	runWait(t, lim, wait{"act-later", context.Background(), 3, 2, true})
   429  }
   430  
   431  func TestWaitCancel(t *testing.T) {
   432  	lim := NewLimiter(10, 3)
   433  
   434  	ctx, cancel := context.WithCancel(context.Background())
   435  	runWait(t, lim, wait{"act-now", ctx, 2, 0, true}) // after this lim.tokens = 1
   436  	go func() {
   437  		time.Sleep(d)
   438  		cancel()
   439  	}()
   440  	runWait(t, lim, wait{"will-cancel", ctx, 3, 1, false})
   441  	// should get 3 tokens back, and have lim.tokens = 2
   442  	t.Logf("tokens:%v last:%v lastEvent:%v", lim.tokens, lim.last, lim.lastEvent)
   443  	runWait(t, lim, wait{"act-now-after-cancel", context.Background(), 2, 0, true})
   444  }
   445  
   446  func TestWaitTimeout(t *testing.T) {
   447  	lim := NewLimiter(10, 3)
   448  
   449  	ctx, cancel := context.WithTimeout(context.Background(), d)
   450  	defer cancel()
   451  	runWait(t, lim, wait{"act-now", ctx, 2, 0, true})
   452  	runWait(t, lim, wait{"w-timeout-err", ctx, 3, 0, false})
   453  }
   454  
   455  func TestWaitInf(t *testing.T) {
   456  	lim := NewLimiter(Inf, 0)
   457  
   458  	runWait(t, lim, wait{"exceed-burst-no-error", context.Background(), 3, 0, true})
   459  }
   460  
   461  func BenchmarkAllowN(b *testing.B) {
   462  	lim := NewLimiter(Every(1*time.Second), 1)
   463  	now := time.Now()
   464  	b.ReportAllocs()
   465  	b.ResetTimer()
   466  	b.RunParallel(func(pb *testing.PB) {
   467  		for pb.Next() {
   468  			lim.AllowN(now, 1)
   469  		}
   470  	})
   471  }
   472  
   473  func BenchmarkWaitNNoDelay(b *testing.B) {
   474  	lim := NewLimiter(Limit(b.N), b.N)
   475  	ctx := context.Background()
   476  	b.ReportAllocs()
   477  	b.ResetTimer()
   478  	for i := 0; i < b.N; i++ {
   479  		if err := lim.WaitN(ctx, 1); err != nil {
   480  			b.Errorf("failed limiter : %s", err)
   481  		}
   482  	}
   483  }