Skip to content

Commit 7aba396

Browse files
Implementation of a ring buffer complete.
1 parent 003cf93 commit 7aba396

File tree

1 file changed

+290
-0
lines changed

1 file changed

+290
-0
lines changed

queue/ring_test.go

Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
/*
2+
Copyright 2014 Workiva, LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package queue
18+
19+
import (
20+
"sync"
21+
"sync/atomic"
22+
"testing"
23+
24+
"github.com/stretchr/testify/assert"
25+
)
26+
27+
func TestRingInsert(t *testing.T) {
28+
rb := NewRingBuffer(5)
29+
assert.Equal(t, uint64(8), rb.Cap())
30+
31+
err := rb.Put(5)
32+
if !assert.Nil(t, err) {
33+
return
34+
}
35+
36+
result, err := rb.Get()
37+
if !assert.Nil(t, err) {
38+
return
39+
}
40+
41+
assert.Equal(t, 5, result)
42+
}
43+
44+
func TestRingMultipleInserts(t *testing.T) {
45+
rb := NewRingBuffer(5)
46+
47+
err := rb.Put(1)
48+
if !assert.Nil(t, err) {
49+
return
50+
}
51+
52+
err = rb.Put(2)
53+
if !assert.Nil(t, err) {
54+
return
55+
}
56+
57+
result, err := rb.Get()
58+
if !assert.Nil(t, err) {
59+
return
60+
}
61+
62+
assert.Equal(t, 1, result)
63+
64+
result, err = rb.Get()
65+
if assert.Nil(t, err) {
66+
return
67+
}
68+
69+
assert.Equal(t, 2, result)
70+
}
71+
72+
func TestIntertwinedGetAndPut(t *testing.T) {
73+
rb := NewRingBuffer(5)
74+
err := rb.Put(1)
75+
if !assert.Nil(t, err) {
76+
return
77+
}
78+
79+
result, err := rb.Get()
80+
if !assert.Nil(t, err) {
81+
return
82+
}
83+
84+
assert.Equal(t, 1, result)
85+
86+
err = rb.Put(2)
87+
if !assert.Nil(t, err) {
88+
return
89+
}
90+
91+
result, err = rb.Get()
92+
if !assert.Nil(t, err) {
93+
return
94+
}
95+
96+
assert.Equal(t, 2, result)
97+
}
98+
99+
func TestPutToFull(t *testing.T) {
100+
rb := NewRingBuffer(3)
101+
102+
for i := 0; i < 4; i++ {
103+
err := rb.Put(i)
104+
if !assert.Nil(t, err) {
105+
return
106+
}
107+
}
108+
109+
var wg sync.WaitGroup
110+
wg.Add(2)
111+
112+
go func() {
113+
err := rb.Put(4)
114+
assert.Nil(t, err)
115+
wg.Done()
116+
}()
117+
118+
go func() {
119+
defer wg.Done()
120+
result, err := rb.Get()
121+
if !assert.Nil(t, err) {
122+
return
123+
}
124+
125+
assert.Equal(t, 0, result)
126+
}()
127+
128+
wg.Wait()
129+
}
130+
131+
func TestRingGetEmpty(t *testing.T) {
132+
rb := NewRingBuffer(3)
133+
134+
var wg sync.WaitGroup
135+
wg.Add(1)
136+
137+
// want to kick off this consumer to ensure it blocks
138+
go func() {
139+
wg.Done()
140+
result, err := rb.Get()
141+
assert.Nil(t, err)
142+
assert.Equal(t, 0, result)
143+
wg.Done()
144+
}()
145+
146+
wg.Wait()
147+
wg.Add(2)
148+
149+
go func() {
150+
defer wg.Done()
151+
err := rb.Put(0)
152+
assert.Nil(t, err)
153+
}()
154+
155+
wg.Wait()
156+
}
157+
158+
func TestRingLen(t *testing.T) {
159+
rb := NewRingBuffer(4)
160+
assert.Equal(t, uint64(0), rb.Len())
161+
162+
rb.Put(1)
163+
assert.Equal(t, uint64(1), rb.Len())
164+
165+
rb.Get()
166+
assert.Equal(t, uint64(0), rb.Len())
167+
168+
for i := 0; i < 4; i++ {
169+
rb.Put(1)
170+
}
171+
assert.Equal(t, uint64(4), rb.Len())
172+
173+
rb.Get()
174+
assert.Equal(t, uint64(3), rb.Len())
175+
}
176+
177+
func TestDisposeOnGet(t *testing.T) {
178+
numThreads := 8
179+
var wg sync.WaitGroup
180+
wg.Add(numThreads)
181+
rb := NewRingBuffer(4)
182+
var spunUp sync.WaitGroup
183+
spunUp.Add(numThreads)
184+
185+
for i := 0; i < numThreads; i++ {
186+
go func() {
187+
spunUp.Done()
188+
defer wg.Done()
189+
_, err := rb.Get()
190+
assert.NotNil(t, err)
191+
}()
192+
}
193+
194+
spunUp.Wait()
195+
196+
rb.Dispose()
197+
198+
wg.Wait()
199+
200+
assert.True(t, rb.IsDisposed())
201+
}
202+
203+
func TestDisposeOnPut(t *testing.T) {
204+
numThreads := 8
205+
var wg sync.WaitGroup
206+
wg.Add(numThreads)
207+
rb := NewRingBuffer(4)
208+
var spunUp sync.WaitGroup
209+
spunUp.Add(numThreads)
210+
211+
// fill up the queue
212+
for i := 0; i < 4; i++ {
213+
rb.Put(i)
214+
}
215+
216+
// it's now full
217+
for i := 0; i < numThreads; i++ {
218+
go func(i int) {
219+
spunUp.Done()
220+
defer wg.Done()
221+
err := rb.Put(i)
222+
assert.NotNil(t, err)
223+
}(i)
224+
}
225+
226+
spunUp.Wait()
227+
228+
rb.Dispose()
229+
230+
wg.Wait()
231+
232+
assert.True(t, rb.IsDisposed())
233+
}
234+
235+
func BenchmarkRBLifeCycle(b *testing.B) {
236+
rb := NewRingBuffer(64)
237+
238+
counter := uint64(0)
239+
var wg sync.WaitGroup
240+
wg.Add(1)
241+
242+
go func() {
243+
defer wg.Done()
244+
for {
245+
_, err := rb.Get()
246+
assert.Nil(b, err)
247+
248+
if atomic.AddUint64(&counter, 1) == uint64(b.N) {
249+
return
250+
}
251+
}
252+
}()
253+
254+
b.ResetTimer()
255+
256+
for i := 0; i < b.N; i++ {
257+
rb.Put(i)
258+
}
259+
260+
wg.Wait()
261+
}
262+
263+
func BenchmarkRBPut(b *testing.B) {
264+
rbs := make([]*RingBuffer, 0, b.N)
265+
266+
for i := 0; i < b.N; i++ {
267+
rbs = append(rbs, NewRingBuffer(2))
268+
}
269+
270+
b.ResetTimer()
271+
272+
for i := 0; i < b.N; i++ {
273+
rbs[i].Put(i)
274+
}
275+
}
276+
277+
func BenchmarkRBGet(b *testing.B) {
278+
rbs := make([]*RingBuffer, 0, b.N)
279+
280+
for i := 0; i < b.N; i++ {
281+
rbs = append(rbs, NewRingBuffer(2))
282+
rbs[i].Put(i)
283+
}
284+
285+
b.ResetTimer()
286+
287+
for i := 0; i < b.N; i++ {
288+
rbs[i].Get()
289+
}
290+
}

0 commit comments

Comments
 (0)