...
1 package ambex
2
3 import (
4 "fmt"
5 "sync"
6 "testing"
7 "time"
8
9 "github.com/stretchr/testify/assert"
10
11 "github.com/datawire/dlib/dlog"
12 )
13
14 type harness struct {
15 T *testing.T
16 C chan time.Time
17 version int
18 updates chan Update
19 pushed chan int
20 expectedVersion int
21
22 mutex sync.Mutex
23 usage int
24 clock time.Time
25 }
26
27 var drainTime = 10 * time.Minute
28
29 func newHarness(t *testing.T) *harness {
30 C := make(chan time.Time)
31 h := &harness{t, C, 0, make(chan Update), make(chan int, 10000), 1, sync.Mutex{}, 0, time.Now()}
32 go func() {
33 assert.NoError(t, updaterWithTicker(dlog.NewTestContext(t, false), h.updates, h.getUsage, drainTime, &time.Ticker{C: C}, h.time))
34 }()
35 return h
36 }
37
38 func (h *harness) advance(d time.Duration) time.Time {
39 h.mutex.Lock()
40 result := h.clock.Add(d)
41 h.clock = result
42 h.mutex.Unlock()
43 return result
44 }
45
46 func (h *harness) time() time.Time {
47 return h.advance(0)
48 }
49
50 func (h *harness) getUsage() int {
51 h.mutex.Lock()
52 defer h.mutex.Unlock()
53 return h.usage
54 }
55
56 func (h *harness) setUsage(u int) {
57 h.mutex.Lock()
58 defer h.mutex.Unlock()
59 h.usage = u
60 }
61
62
63 func (h *harness) tick(d time.Duration) {
64 h.C <- h.advance(d)
65 }
66
67
68 func (h *harness) update(d time.Duration) int {
69 h.version++
70 version := h.version
71 h.advance(d)
72 h.updates <- Update{fmt.Sprintf("%d", version), func() error {
73 h.pushed <- version
74 return nil
75 }}
76 return version
77 }
78
79
80
81 func (h *harness) expectUntil(version int) {
82 for {
83 select {
84 case v := <-h.pushed:
85 assert.Equal(h.T, h.expectedVersion, v)
86 if v < version {
87 h.expectedVersion++
88 continue
89 }
90
91 if v != version {
92 assert.Fail(h.T, fmt.Sprintf("expected version %d, got %d", version, v))
93 }
94 return
95 case <-time.After(1 * time.Second):
96 assert.Fail(h.T, fmt.Sprintf("expected version %d, but timed out", h.expectedVersion))
97 return
98 }
99 }
100
101 }
102
103
104 func (h *harness) expectExact(version int) {
105 select {
106 case v := <-h.pushed:
107 assert.Equal(h.T, version, v)
108 h.expectedVersion = version + 1
109 case <-time.After(1 * time.Second):
110 assert.Fail(h.T, fmt.Sprintf("expected version %d, but timed out", version))
111 return
112 }
113 }
114
115
116 func (h *harness) expectNone() {
117 select {
118 case v := <-h.pushed:
119 assert.Fail(h.T, fmt.Sprintf("expected no pushes, but go version %d", v))
120 case <-time.After(1 * time.Second):
121 return
122 }
123 }
124
125
126 func TestHappyPath(t *testing.T) {
127 h := newHarness(t)
128 var version int
129 for i := 0; i < 1000; i++ {
130 version = h.update(0)
131 }
132 h.expectUntil(version)
133 }
134
135
136 func TestConstrained(t *testing.T) {
137 h := newHarness(t)
138
139 h.setUsage(50)
140 for i := 0; i < 1000; i++ {
141 h.update(0)
142 }
143
144
145 h.expectUntil(120)
146
147 h.tick(drainTime)
148 h.expectExact(1000)
149
150 h.setUsage(60)
151 for i := 0; i < 1000; i++ {
152 h.update(0)
153 }
154
155 h.expectUntil(1059)
156
157 h.tick(drainTime)
158 h.expectExact(2000)
159
160 h.setUsage(70)
161 for i := 0; i < 1000; i++ {
162 h.update(0)
163 }
164
165 h.expectUntil(2029)
166
167 h.tick(drainTime)
168 h.expectExact(3000)
169
170 h.setUsage(80)
171 for i := 0; i < 1000; i++ {
172 h.update(0)
173 }
174
175 h.expectUntil(3014)
176
177 h.tick(drainTime)
178 h.expectExact(4000)
179
180 h.setUsage(90)
181 for i := 0; i < 1000; i++ {
182 h.update(0)
183 }
184
185 h.expectNone()
186
187 h.tick(drainTime)
188 h.expectExact(5000)
189
190
191 h.setUsage(25)
192 for i := 0; i < 1000; i++ {
193 h.update(0)
194 }
195 h.expectUntil(6000)
196 }
197
View as plain text