1package backoff
2
3import (
4	"fmt"
5	"math"
6	"testing"
7	"time"
8)
9
10// If given New with 0's and no jitter, ensure that certain invariants are met:
11//
12//   - the default max duration and interval should be used
13//   - noJitter should be true
14//   - the RNG should not be initialised
15//   - the first duration should be equal to the default interval
16func TestDefaults(t *testing.T) {
17	b := NewWithoutJitter(0, 0)
18
19	if b.maxDuration != DefaultMaxDuration {
20		t.Fatalf("expected new backoff to use the default max duration (%s), but have %s", DefaultMaxDuration, b.maxDuration)
21	}
22
23	if b.interval != DefaultInterval {
24		t.Fatalf("exepcted new backoff to use the default interval (%s), but have %s", DefaultInterval, b.interval)
25	}
26
27	if b.noJitter != true {
28		t.Fatal("backoff should have been initialised without jitter")
29	}
30
31	dur := b.Duration()
32	if dur != DefaultInterval {
33		t.Fatalf("expected first duration to be %s, have %s", DefaultInterval, dur)
34	}
35}
36
37// Given a zero-value initialised Backoff, it should be transparently
38// setup.
39func TestSetup(t *testing.T) {
40	b := new(Backoff)
41	dur := b.Duration()
42	if dur < 0 || dur > (5*time.Minute) {
43		t.Fatalf("want duration between 0 and 5 minutes, have %s", dur)
44	}
45}
46
47// Ensure that tries incremenets as expected.
48func TestTries(t *testing.T) {
49	b := NewWithoutJitter(5, 1)
50
51	for i := uint64(0); i < 3; i++ {
52		if b.n != i {
53			t.Fatalf("want tries=%d, have tries=%d", i, b.n)
54		}
55
56		pow := 1 << i
57		expected := time.Duration(pow)
58		dur := b.Duration()
59		if dur != expected {
60			t.Fatalf("want duration=%d, have duration=%d at i=%d", expected, dur, i)
61		}
62	}
63
64	for i := uint(3); i < 5; i++ {
65		dur := b.Duration()
66		if dur != 5 {
67			t.Fatalf("want duration=5, have %d at i=%d", dur, i)
68		}
69	}
70}
71
72// Ensure that a call to Reset will actually reset the Backoff.
73func TestReset(t *testing.T) {
74	const iter = 10
75	b := New(1000, 1)
76	for i := 0; i < iter; i++ {
77		_ = b.Duration()
78	}
79
80	if b.n != iter {
81		t.Fatalf("expected tries=%d, have tries=%d", iter, b.n)
82	}
83
84	b.Reset()
85	if b.n != 0 {
86		t.Fatalf("expected tries=0 after reset, have tries=%d", b.n)
87	}
88}
89
90const decay = 5 * time.Millisecond
91const max = 10 * time.Millisecond
92const interval = time.Millisecond
93
94func TestDecay(t *testing.T) {
95	const iter = 10
96
97	b := NewWithoutJitter(max, 1)
98	b.SetDecay(decay)
99
100	var backoff time.Duration
101	for i := 0; i < iter; i++ {
102		backoff = b.Duration()
103	}
104
105	if b.n != iter {
106		t.Fatalf("expected tries=%d, have tries=%d", iter, b.n)
107	}
108
109	// Don't decay below backoff
110	b.lastTry = time.Now().Add(-backoff + 1)
111	backoff = b.Duration()
112	if b.n != iter+1 {
113		t.Fatalf("expected tries=%d, have tries=%d", iter+1, b.n)
114	}
115
116	// Reset after backoff + decay
117	b.lastTry = time.Now().Add(-backoff - decay)
118	b.Duration()
119	if b.n != 1 {
120		t.Fatalf("expected tries=%d, have tries=%d", 1, b.n)
121	}
122}
123
124// Ensure that decay works even if the retry counter is saturated.
125func TestDecaySaturation(t *testing.T) {
126	b := NewWithoutJitter(1<<2, 1)
127	b.SetDecay(decay)
128
129	var duration time.Duration
130	for i := 0; i <= 2; i++ {
131		duration = b.Duration()
132	}
133
134	if duration != 1<<2 {
135		t.Fatalf("expected duration=%v, have duration=%v", 1<<2, duration)
136	}
137
138	b.lastTry = time.Now().Add(-duration - decay)
139	b.n = math.MaxUint64
140
141	duration = b.Duration()
142	if duration != 1 {
143		t.Errorf("expected duration=%v, have duration=%v", 1, duration)
144	}
145}
146
147func ExampleBackoff_SetDecay() {
148	b := NewWithoutJitter(max, interval)
149	b.SetDecay(decay)
150
151	// try 0
152	fmt.Println(b.Duration())
153
154	// try 1
155	fmt.Println(b.Duration())
156
157	// try 2
158	duration := b.Duration()
159	fmt.Println(duration)
160
161	// try 3, below decay
162	time.Sleep(duration)
163	duration = b.Duration()
164	fmt.Println(duration)
165
166	// try 4, resets
167	time.Sleep(duration + decay)
168	fmt.Println(b.Duration())
169
170	// Output: 1ms
171	// 2ms
172	// 4ms
173	// 8ms
174	// 1ms
175}
176