package fibonacci import ( "backoff/utilities" "context" "fmt" "math/big" "testing" "time" ) func wouldOverflow(a, b int64) (product int64, overflows bool) { results := new(big.Int).Mul(big.NewInt(a), big.NewInt(b)) return results.Int64(), results.IsInt64() } func fibonacci(n int64) int64 { switch { case n <= 0: return 0 case n == 1: return 1 default: var a, b int64 = 0, 1 for i := int64(2); i <= n; i++ { a, b = b, a+b } return b } } func TestFibonacciResultsArray(t *testing.T) { for n, got := range TheFibonacciSequence { want := fibonacci(int64(n)) if want != got { t.Fatalf("error for input %d: wanted %d; got %d\n", n, want, got) } } } func TestFibonacciFunc(t *testing.T) { for n := range len(TheFibonacciSequence) { want := fibonacci(int64(n)) got := fibonacci(int64(n)) if want != got { t.Fatalf("error for input %d: wanted %d; got %d\n", n, want, got) } } } func TestFibonacciBackoffNextDoesNotOverflow(t *testing.T) { backoff := FibonacciBackoff{} for n, want := range TheFibonacciSequence { got := backoff.Next() if time.Duration(want) != got { t.Fatalf("error for %d: wanted %s; got %s\n", n, time.Duration(want), time.Duration(got)) } } backoff.Reset() if want, got := int64(0), backoff.Iteration; want != got { t.Fatalf("error: wanted %d; got %d after Reset()\n", want, got) } var previous time.Duration backoff.PauseMultiplier = time.Second for input, output := range TheFibonacciSequence { var want time.Duration switch _, overflows := utilities.ProductWouldOverflowInt64(output, backoff.PauseMultiplier.Nanoseconds()); overflows { case true: want = previous default: want = time.Duration(output) * backoff.PauseMultiplier } got := backoff.Next() if want != got { t.Fatalf("error for %d (iteration=%d); wanted %s; got %s\n", input, backoff.Iteration, want, got) } previous = got } } func TestFibonacciBackoffIterationLimit(t *testing.T) { backoff := FibonacciBackoff{MaxIteration: 5} for input, output := range TheFibonacciSequence { want := time.Duration(output) if input >= int(backoff.MaxIteration) { if backoff.Iteration > backoff.MaxIteration { t.Fatalf("error: backoff.Iteration %d > max %d (%+v)\n", backoff.Iteration, backoff.MaxIteration, backoff) } want = time.Duration(fibonacci(5)) } got := backoff.Next() if want != got { t.Fatalf("error for %d: wanted %s; got %s\n", input, want, got) } } } func TestFibonacciPauseLimit(t *testing.T) { backoff := FibonacciBackoff{MaxPause: time.Second} for input := range TheFibonacciSequence { if got := backoff.Next(); got > time.Second { t.Fatalf("error for %d: expected max %s; got %s\n", input, time.Second, got) } } // test with jitter backoff.Reset() backoff.Jitter = time.Millisecond * 100 backoff.PauseMultiplier = time.Second max := time.Second + backoff.Jitter min := time.Second - backoff.Jitter for input := range TheFibonacciSequence { if got := backoff.Next(); input > 0 && (got > max || got < min) { t.Fatalf("error for %d: expected value in range %s - %s; got %s\n", input, min, max, got) } } fmt.Printf("finished\n") } func TestFibonacciBackoffAfter(t *testing.T) { backoff := FibonacciBackoff{Iteration: 1, PauseMultiplier: time.Millisecond} for i := 1; i <= 5; i++ { want := time.Duration(fibonacci(int64(i))) * time.Millisecond min, max := want-time.Millisecond*10, want+time.Millisecond*10 got := func() (took time.Duration) { start := time.Now() defer func() { took = time.Since(start) }() <-backoff.After(context.Background()) return }() if got > max || got < min { t.Fatalf("error: wanted value in range %s-%s; got %s\n", min, max, got) } } }