1
16
17 package cache
18
19 import (
20 "fmt"
21 "reflect"
22 "runtime"
23 "testing"
24 "time"
25 )
26
27 func testFifoObjectKeyFunc(obj interface{}) (string, error) {
28 return obj.(testFifoObject).name, nil
29 }
30
31 type testFifoObject struct {
32 name string
33 val interface{}
34 }
35
36 func mkFifoObj(name string, val interface{}) testFifoObject {
37 return testFifoObject{name: name, val: val}
38 }
39
40 func TestFIFO_basic(t *testing.T) {
41 f := NewFIFO(testFifoObjectKeyFunc)
42 const amount = 500
43 go func() {
44 for i := 0; i < amount; i++ {
45 f.Add(mkFifoObj(string([]rune{'a', rune(i)}), i+1))
46 }
47 }()
48 go func() {
49 for u := uint64(0); u < amount; u++ {
50 f.Add(mkFifoObj(string([]rune{'b', rune(u)}), u+1))
51 }
52 }()
53
54 lastInt := int(0)
55 lastUint := uint64(0)
56 for i := 0; i < amount*2; i++ {
57 switch obj := Pop(f).(testFifoObject).val.(type) {
58 case int:
59 if obj <= lastInt {
60 t.Errorf("got %v (int) out of order, last was %v", obj, lastInt)
61 }
62 lastInt = obj
63 case uint64:
64 if obj <= lastUint {
65 t.Errorf("got %v (uint) out of order, last was %v", obj, lastUint)
66 } else {
67 lastUint = obj
68 }
69 default:
70 t.Fatalf("unexpected type %#v", obj)
71 }
72 }
73 }
74
75 func TestFIFO_requeueOnPop(t *testing.T) {
76 f := NewFIFO(testFifoObjectKeyFunc)
77
78 f.Add(mkFifoObj("foo", 10))
79 _, err := f.Pop(func(obj interface{}, isInInitialList bool) error {
80 if obj.(testFifoObject).name != "foo" {
81 t.Fatalf("unexpected object: %#v", obj)
82 }
83 return ErrRequeue{Err: nil}
84 })
85 if err != nil {
86 t.Fatalf("unexpected error: %v", err)
87 }
88 if _, ok, err := f.GetByKey("foo"); !ok || err != nil {
89 t.Fatalf("object should have been requeued: %t %v", ok, err)
90 }
91
92 _, err = f.Pop(func(obj interface{}, isInInitialList bool) error {
93 if obj.(testFifoObject).name != "foo" {
94 t.Fatalf("unexpected object: %#v", obj)
95 }
96 return ErrRequeue{Err: fmt.Errorf("test error")}
97 })
98 if err == nil || err.Error() != "test error" {
99 t.Fatalf("unexpected error: %v", err)
100 }
101 if _, ok, err := f.GetByKey("foo"); !ok || err != nil {
102 t.Fatalf("object should have been requeued: %t %v", ok, err)
103 }
104
105 _, err = f.Pop(func(obj interface{}, isInInitialList bool) error {
106 if obj.(testFifoObject).name != "foo" {
107 t.Fatalf("unexpected object: %#v", obj)
108 }
109 return nil
110 })
111 if err != nil {
112 t.Fatalf("unexpected error: %v", err)
113 }
114 if _, ok, err := f.GetByKey("foo"); ok || err != nil {
115 t.Fatalf("object should have been removed: %t %v", ok, err)
116 }
117 }
118
119 func TestFIFO_addUpdate(t *testing.T) {
120 f := NewFIFO(testFifoObjectKeyFunc)
121 f.Add(mkFifoObj("foo", 10))
122 f.Update(mkFifoObj("foo", 15))
123
124 if e, a := []interface{}{mkFifoObj("foo", 15)}, f.List(); !reflect.DeepEqual(e, a) {
125 t.Errorf("Expected %+v, got %+v", e, a)
126 }
127 if e, a := []string{"foo"}, f.ListKeys(); !reflect.DeepEqual(e, a) {
128 t.Errorf("Expected %+v, got %+v", e, a)
129 }
130
131 got := make(chan testFifoObject, 2)
132 go func() {
133 for {
134 got <- Pop(f).(testFifoObject)
135 }
136 }()
137
138 first := <-got
139 if e, a := 15, first.val; e != a {
140 t.Errorf("Didn't get updated value (%v), got %v", e, a)
141 }
142 select {
143 case unexpected := <-got:
144 t.Errorf("Got second value %v", unexpected.val)
145 case <-time.After(50 * time.Millisecond):
146 }
147 _, exists, _ := f.Get(mkFifoObj("foo", ""))
148 if exists {
149 t.Errorf("item did not get removed")
150 }
151 }
152
153 func TestFIFO_addReplace(t *testing.T) {
154 f := NewFIFO(testFifoObjectKeyFunc)
155 f.Add(mkFifoObj("foo", 10))
156 f.Replace([]interface{}{mkFifoObj("foo", 15)}, "15")
157 got := make(chan testFifoObject, 2)
158 go func() {
159 for {
160 got <- Pop(f).(testFifoObject)
161 }
162 }()
163
164 first := <-got
165 if e, a := 15, first.val; e != a {
166 t.Errorf("Didn't get updated value (%v), got %v", e, a)
167 }
168 select {
169 case unexpected := <-got:
170 t.Errorf("Got second value %v", unexpected.val)
171 case <-time.After(50 * time.Millisecond):
172 }
173 _, exists, _ := f.Get(mkFifoObj("foo", ""))
174 if exists {
175 t.Errorf("item did not get removed")
176 }
177 }
178
179 func TestFIFO_detectLineJumpers(t *testing.T) {
180 f := NewFIFO(testFifoObjectKeyFunc)
181
182 f.Add(mkFifoObj("foo", 10))
183 f.Add(mkFifoObj("bar", 1))
184 f.Add(mkFifoObj("foo", 11))
185 f.Add(mkFifoObj("foo", 13))
186 f.Add(mkFifoObj("zab", 30))
187
188 if e, a := 13, Pop(f).(testFifoObject).val; a != e {
189 t.Fatalf("expected %d, got %d", e, a)
190 }
191
192 f.Add(mkFifoObj("foo", 14))
193
194 if e, a := 1, Pop(f).(testFifoObject).val; a != e {
195 t.Fatalf("expected %d, got %d", e, a)
196 }
197
198 if e, a := 30, Pop(f).(testFifoObject).val; a != e {
199 t.Fatalf("expected %d, got %d", e, a)
200 }
201
202 if e, a := 14, Pop(f).(testFifoObject).val; a != e {
203 t.Fatalf("expected %d, got %d", e, a)
204 }
205 }
206
207 func TestFIFO_addIfNotPresent(t *testing.T) {
208 f := NewFIFO(testFifoObjectKeyFunc)
209
210 f.Add(mkFifoObj("a", 1))
211 f.Add(mkFifoObj("b", 2))
212 f.AddIfNotPresent(mkFifoObj("b", 3))
213 f.AddIfNotPresent(mkFifoObj("c", 4))
214
215 if e, a := 3, len(f.items); a != e {
216 t.Fatalf("expected queue length %d, got %d", e, a)
217 }
218
219 expectedValues := []int{1, 2, 4}
220 for _, expected := range expectedValues {
221 if actual := Pop(f).(testFifoObject).val; actual != expected {
222 t.Fatalf("expected value %d, got %d", expected, actual)
223 }
224 }
225 }
226
227 func TestFIFO_HasSynced(t *testing.T) {
228 tests := []struct {
229 actions []func(f *FIFO)
230 expectedSynced bool
231 }{
232 {
233 actions: []func(f *FIFO){},
234 expectedSynced: false,
235 },
236 {
237 actions: []func(f *FIFO){
238 func(f *FIFO) { f.Add(mkFifoObj("a", 1)) },
239 },
240 expectedSynced: true,
241 },
242 {
243 actions: []func(f *FIFO){
244 func(f *FIFO) { f.Replace([]interface{}{}, "0") },
245 },
246 expectedSynced: true,
247 },
248 {
249 actions: []func(f *FIFO){
250 func(f *FIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") },
251 },
252 expectedSynced: false,
253 },
254 {
255 actions: []func(f *FIFO){
256 func(f *FIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") },
257 func(f *FIFO) { Pop(f) },
258 },
259 expectedSynced: false,
260 },
261 {
262 actions: []func(f *FIFO){
263 func(f *FIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") },
264 func(f *FIFO) { Pop(f) },
265 func(f *FIFO) { Pop(f) },
266 },
267 expectedSynced: true,
268 },
269 }
270
271 for i, test := range tests {
272 f := NewFIFO(testFifoObjectKeyFunc)
273
274 for _, action := range test.actions {
275 action(f)
276 }
277 if e, a := test.expectedSynced, f.HasSynced(); a != e {
278 t.Errorf("test case %v failed, expected: %v , got %v", i, e, a)
279 }
280 }
281 }
282
283
284
285 func TestFIFO_PopShouldUnblockWhenClosed(t *testing.T) {
286 f := NewFIFO(testFifoObjectKeyFunc)
287
288 c := make(chan struct{})
289 const jobs = 10
290 for i := 0; i < jobs; i++ {
291 go func() {
292 f.Pop(func(obj interface{}, isInInitialList bool) error {
293 return nil
294 })
295 c <- struct{}{}
296 }()
297 }
298
299 runtime.Gosched()
300 f.Close()
301
302 for i := 0; i < jobs; i++ {
303 select {
304 case <-c:
305 case <-time.After(500 * time.Millisecond):
306 t.Fatalf("timed out waiting for Pop to return after Close")
307 }
308 }
309 }
310
View as plain text