k8s.io/client-go@v0.22.2/tools/cache/delta_fifo_test.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors. 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 cache 18 19 import ( 20 "errors" 21 "fmt" 22 "reflect" 23 "runtime" 24 "testing" 25 "time" 26 ) 27 28 // helper function to reduce stuttering 29 func testPop(f *DeltaFIFO) testFifoObject { 30 return Pop(f).(Deltas).Newest().Object.(testFifoObject) 31 } 32 33 // literalListerGetter is a KeyListerGetter that is based on a 34 // function that returns a slice of objects to list and get. 35 // The function must list the same objects every time. 36 type literalListerGetter func() []testFifoObject 37 38 var _ KeyListerGetter = literalListerGetter(nil) 39 40 // ListKeys just calls kl. 41 func (kl literalListerGetter) ListKeys() []string { 42 result := []string{} 43 for _, fifoObj := range kl() { 44 result = append(result, fifoObj.name) 45 } 46 return result 47 } 48 49 // GetByKey returns the key if it exists in the list returned by kl. 50 func (kl literalListerGetter) GetByKey(key string) (interface{}, bool, error) { 51 for _, v := range kl() { 52 if v.name == key { 53 return v, true, nil 54 } 55 } 56 return nil, false, nil 57 } 58 59 func TestDeltaFIFO_basic(t *testing.T) { 60 f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{KeyFunction: testFifoObjectKeyFunc}) 61 const amount = 500 62 go func() { 63 for i := 0; i < amount; i++ { 64 f.Add(mkFifoObj(string([]rune{'a', rune(i)}), i+1)) 65 } 66 }() 67 go func() { 68 for u := uint64(0); u < amount; u++ { 69 f.Add(mkFifoObj(string([]rune{'b', rune(u)}), u+1)) 70 } 71 }() 72 73 lastInt := int(0) 74 lastUint := uint64(0) 75 for i := 0; i < amount*2; i++ { 76 switch obj := testPop(f).val.(type) { 77 case int: 78 if obj <= lastInt { 79 t.Errorf("got %v (int) out of order, last was %v", obj, lastInt) 80 } 81 lastInt = obj 82 case uint64: 83 if obj <= lastUint { 84 t.Errorf("got %v (uint) out of order, last was %v", obj, lastUint) 85 } else { 86 lastUint = obj 87 } 88 default: 89 t.Fatalf("unexpected type %#v", obj) 90 } 91 } 92 } 93 94 // TestDeltaFIFO_replaceWithDeleteDeltaIn tests that a `Sync` delta for an 95 // object `O` with ID `X` is added when .Replace is called and `O` is among the 96 // replacement objects even if the DeltaFIFO already stores in terminal position 97 // a delta of type `Delete` for ID `X`. Not adding the `Sync` delta causes 98 // SharedIndexInformers to miss `O`'s create notification, see https://github.com/kubernetes/kubernetes/issues/83810 99 // for more details. 100 func TestDeltaFIFO_replaceWithDeleteDeltaIn(t *testing.T) { 101 oldObj := mkFifoObj("foo", 1) 102 newObj := mkFifoObj("foo", 2) 103 104 f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{ 105 KeyFunction: testFifoObjectKeyFunc, 106 KnownObjects: literalListerGetter(func() []testFifoObject { 107 return []testFifoObject{oldObj} 108 }), 109 }) 110 111 f.Delete(oldObj) 112 f.Replace([]interface{}{newObj}, "") 113 114 actualDeltas := Pop(f) 115 expectedDeltas := Deltas{ 116 Delta{Type: Deleted, Object: oldObj}, 117 Delta{Type: Sync, Object: newObj}, 118 } 119 if !reflect.DeepEqual(expectedDeltas, actualDeltas) { 120 t.Errorf("expected %#v, got %#v", expectedDeltas, actualDeltas) 121 } 122 } 123 124 func TestDeltaFIFO_requeueOnPop(t *testing.T) { 125 f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{KeyFunction: testFifoObjectKeyFunc}) 126 127 f.Add(mkFifoObj("foo", 10)) 128 _, err := f.Pop(func(obj interface{}) error { 129 if obj.(Deltas)[0].Object.(testFifoObject).name != "foo" { 130 t.Fatalf("unexpected object: %#v", obj) 131 } 132 return ErrRequeue{Err: nil} 133 }) 134 if err != nil { 135 t.Fatalf("unexpected error: %v", err) 136 } 137 if _, ok, err := f.GetByKey("foo"); !ok || err != nil { 138 t.Fatalf("object should have been requeued: %t %v", ok, err) 139 } 140 141 _, err = f.Pop(func(obj interface{}) error { 142 if obj.(Deltas)[0].Object.(testFifoObject).name != "foo" { 143 t.Fatalf("unexpected object: %#v", obj) 144 } 145 return ErrRequeue{Err: fmt.Errorf("test error")} 146 }) 147 if err == nil || err.Error() != "test error" { 148 t.Fatalf("unexpected error: %v", err) 149 } 150 if _, ok, err := f.GetByKey("foo"); !ok || err != nil { 151 t.Fatalf("object should have been requeued: %t %v", ok, err) 152 } 153 154 _, err = f.Pop(func(obj interface{}) error { 155 if obj.(Deltas)[0].Object.(testFifoObject).name != "foo" { 156 t.Fatalf("unexpected object: %#v", obj) 157 } 158 return nil 159 }) 160 if err != nil { 161 t.Fatalf("unexpected error: %v", err) 162 } 163 if _, ok, err := f.GetByKey("foo"); ok || err != nil { 164 t.Fatalf("object should have been removed: %t %v", ok, err) 165 } 166 } 167 168 func TestDeltaFIFO_addUpdate(t *testing.T) { 169 f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{KeyFunction: testFifoObjectKeyFunc}) 170 f.Add(mkFifoObj("foo", 10)) 171 f.Update(mkFifoObj("foo", 12)) 172 f.Delete(mkFifoObj("foo", 15)) 173 174 if e, a := []interface{}{mkFifoObj("foo", 15)}, f.List(); !reflect.DeepEqual(e, a) { 175 t.Errorf("Expected %+v, got %+v", e, a) 176 } 177 if e, a := []string{"foo"}, f.ListKeys(); !reflect.DeepEqual(e, a) { 178 t.Errorf("Expected %+v, got %+v", e, a) 179 } 180 181 got := make(chan testFifoObject, 2) 182 go func() { 183 for { 184 obj := testPop(f) 185 t.Logf("got a thing %#v", obj) 186 t.Logf("D len: %v", len(f.queue)) 187 got <- obj 188 } 189 }() 190 191 first := <-got 192 if e, a := 15, first.val; e != a { 193 t.Errorf("Didn't get updated value (%v), got %v", e, a) 194 } 195 select { 196 case unexpected := <-got: 197 t.Errorf("Got second value %v", unexpected.val) 198 case <-time.After(50 * time.Millisecond): 199 } 200 _, exists, _ := f.Get(mkFifoObj("foo", "")) 201 if exists { 202 t.Errorf("item did not get removed") 203 } 204 } 205 206 func TestDeltaFIFO_enqueueingNoLister(t *testing.T) { 207 f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{KeyFunction: testFifoObjectKeyFunc}) 208 f.Add(mkFifoObj("foo", 10)) 209 f.Update(mkFifoObj("bar", 15)) 210 f.Add(mkFifoObj("qux", 17)) 211 f.Delete(mkFifoObj("qux", 18)) 212 213 // This delete does not enqueue anything because baz doesn't exist. 214 f.Delete(mkFifoObj("baz", 20)) 215 216 expectList := []int{10, 15, 18} 217 for _, expect := range expectList { 218 if e, a := expect, testPop(f).val; e != a { 219 t.Errorf("Didn't get updated value (%v), got %v", e, a) 220 } 221 } 222 if e, a := 0, len(f.items); e != a { 223 t.Errorf("queue unexpectedly not empty: %v != %v\n%#v", e, a, f.items) 224 } 225 } 226 227 func TestDeltaFIFO_enqueueingWithLister(t *testing.T) { 228 f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{ 229 KeyFunction: testFifoObjectKeyFunc, 230 KnownObjects: literalListerGetter(func() []testFifoObject { 231 return []testFifoObject{mkFifoObj("foo", 5), mkFifoObj("bar", 6), mkFifoObj("baz", 7)} 232 }), 233 }) 234 f.Add(mkFifoObj("foo", 10)) 235 f.Update(mkFifoObj("bar", 15)) 236 237 // This delete does enqueue the deletion, because "baz" is in the key lister. 238 f.Delete(mkFifoObj("baz", 20)) 239 240 expectList := []int{10, 15, 20} 241 for _, expect := range expectList { 242 if e, a := expect, testPop(f).val; e != a { 243 t.Errorf("Didn't get updated value (%v), got %v", e, a) 244 } 245 } 246 if e, a := 0, len(f.items); e != a { 247 t.Errorf("queue unexpectedly not empty: %v != %v", e, a) 248 } 249 } 250 251 func TestDeltaFIFO_addReplace(t *testing.T) { 252 f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{KeyFunction: testFifoObjectKeyFunc}) 253 f.Add(mkFifoObj("foo", 10)) 254 f.Replace([]interface{}{mkFifoObj("foo", 15)}, "0") 255 got := make(chan testFifoObject, 2) 256 go func() { 257 for { 258 got <- testPop(f) 259 } 260 }() 261 262 first := <-got 263 if e, a := 15, first.val; e != a { 264 t.Errorf("Didn't get updated value (%v), got %v", e, a) 265 } 266 select { 267 case unexpected := <-got: 268 t.Errorf("Got second value %v", unexpected.val) 269 case <-time.After(50 * time.Millisecond): 270 } 271 _, exists, _ := f.Get(mkFifoObj("foo", "")) 272 if exists { 273 t.Errorf("item did not get removed") 274 } 275 } 276 277 func TestDeltaFIFO_ResyncNonExisting(t *testing.T) { 278 f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{ 279 KeyFunction: testFifoObjectKeyFunc, 280 KnownObjects: literalListerGetter(func() []testFifoObject { 281 return []testFifoObject{mkFifoObj("foo", 5)} 282 }), 283 }) 284 f.Delete(mkFifoObj("foo", 10)) 285 f.Resync() 286 287 deltas := f.items["foo"] 288 if len(deltas) != 1 { 289 t.Fatalf("unexpected deltas length: %v", deltas) 290 } 291 if deltas[0].Type != Deleted { 292 t.Errorf("unexpected delta: %v", deltas[0]) 293 } 294 } 295 296 func TestDeltaFIFO_Resync(t *testing.T) { 297 f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{ 298 KeyFunction: testFifoObjectKeyFunc, 299 KnownObjects: literalListerGetter(func() []testFifoObject { 300 return []testFifoObject{mkFifoObj("foo", 5)} 301 }), 302 }) 303 f.Resync() 304 305 deltas := f.items["foo"] 306 if len(deltas) != 1 { 307 t.Fatalf("unexpected deltas length: %v", deltas) 308 } 309 if deltas[0].Type != Sync { 310 t.Errorf("unexpected delta: %v", deltas[0]) 311 } 312 } 313 314 func TestDeltaFIFO_DeleteExistingNonPropagated(t *testing.T) { 315 f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{ 316 KeyFunction: testFifoObjectKeyFunc, 317 KnownObjects: literalListerGetter(func() []testFifoObject { 318 return []testFifoObject{} 319 }), 320 }) 321 f.Add(mkFifoObj("foo", 5)) 322 f.Delete(mkFifoObj("foo", 6)) 323 324 deltas := f.items["foo"] 325 if len(deltas) != 2 { 326 t.Fatalf("unexpected deltas length: %v", deltas) 327 } 328 if deltas[len(deltas)-1].Type != Deleted { 329 t.Errorf("unexpected delta: %v", deltas[len(deltas)-1]) 330 } 331 } 332 333 func TestDeltaFIFO_ReplaceMakesDeletions(t *testing.T) { 334 // We test with only one pre-existing object because there is no 335 // promise about how their deletes are ordered. 336 337 // Try it with a pre-existing Delete 338 f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{ 339 KeyFunction: testFifoObjectKeyFunc, 340 KnownObjects: literalListerGetter(func() []testFifoObject { 341 return []testFifoObject{mkFifoObj("foo", 5), mkFifoObj("bar", 6), mkFifoObj("baz", 7)} 342 }), 343 }) 344 f.Delete(mkFifoObj("baz", 10)) 345 f.Replace([]interface{}{mkFifoObj("foo", 5)}, "0") 346 347 expectedList := []Deltas{ 348 {{Deleted, mkFifoObj("baz", 10)}}, 349 {{Sync, mkFifoObj("foo", 5)}}, 350 // Since "bar" didn't have a delete event and wasn't in the Replace list 351 // it should get a tombstone key with the right Obj. 352 {{Deleted, DeletedFinalStateUnknown{Key: "bar", Obj: mkFifoObj("bar", 6)}}}, 353 } 354 355 for _, expected := range expectedList { 356 cur := Pop(f).(Deltas) 357 if e, a := expected, cur; !reflect.DeepEqual(e, a) { 358 t.Errorf("Expected %#v, got %#v", e, a) 359 } 360 } 361 362 // Now try starting with an Add instead of a Delete 363 f = NewDeltaFIFOWithOptions(DeltaFIFOOptions{ 364 KeyFunction: testFifoObjectKeyFunc, 365 KnownObjects: literalListerGetter(func() []testFifoObject { 366 return []testFifoObject{mkFifoObj("foo", 5), mkFifoObj("bar", 6), mkFifoObj("baz", 7)} 367 }), 368 }) 369 f.Add(mkFifoObj("baz", 10)) 370 f.Replace([]interface{}{mkFifoObj("foo", 5)}, "0") 371 372 expectedList = []Deltas{ 373 {{Added, mkFifoObj("baz", 10)}, 374 {Deleted, DeletedFinalStateUnknown{Key: "baz", Obj: mkFifoObj("baz", 7)}}}, 375 {{Sync, mkFifoObj("foo", 5)}}, 376 // Since "bar" didn't have a delete event and wasn't in the Replace list 377 // it should get a tombstone key with the right Obj. 378 {{Deleted, DeletedFinalStateUnknown{Key: "bar", Obj: mkFifoObj("bar", 6)}}}, 379 } 380 381 for _, expected := range expectedList { 382 cur := Pop(f).(Deltas) 383 if e, a := expected, cur; !reflect.DeepEqual(e, a) { 384 t.Errorf("Expected %#v, got %#v", e, a) 385 } 386 } 387 388 // Now try starting without an explicit KeyListerGetter 389 f = NewDeltaFIFOWithOptions(DeltaFIFOOptions{KeyFunction: testFifoObjectKeyFunc}) 390 f.Add(mkFifoObj("baz", 10)) 391 f.Replace([]interface{}{mkFifoObj("foo", 5)}, "0") 392 393 expectedList = []Deltas{ 394 {{Added, mkFifoObj("baz", 10)}, 395 {Deleted, DeletedFinalStateUnknown{Key: "baz", Obj: mkFifoObj("baz", 10)}}}, 396 {{Sync, mkFifoObj("foo", 5)}}, 397 } 398 399 for _, expected := range expectedList { 400 cur := Pop(f).(Deltas) 401 if e, a := expected, cur; !reflect.DeepEqual(e, a) { 402 t.Errorf("Expected %#v, got %#v", e, a) 403 } 404 } 405 } 406 407 // TestDeltaFIFO_ReplaceMakesDeletionsReplaced is the same as the above test, but 408 // ensures that a Replaced DeltaType is emitted. 409 func TestDeltaFIFO_ReplaceMakesDeletionsReplaced(t *testing.T) { 410 f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{ 411 KeyFunction: testFifoObjectKeyFunc, 412 KnownObjects: literalListerGetter(func() []testFifoObject { 413 return []testFifoObject{mkFifoObj("foo", 5), mkFifoObj("bar", 6), mkFifoObj("baz", 7)} 414 }), 415 EmitDeltaTypeReplaced: true, 416 }) 417 418 f.Delete(mkFifoObj("baz", 10)) 419 f.Replace([]interface{}{mkFifoObj("foo", 6)}, "0") 420 421 expectedList := []Deltas{ 422 {{Deleted, mkFifoObj("baz", 10)}}, 423 {{Replaced, mkFifoObj("foo", 6)}}, 424 // Since "bar" didn't have a delete event and wasn't in the Replace list 425 // it should get a tombstone key with the right Obj. 426 {{Deleted, DeletedFinalStateUnknown{Key: "bar", Obj: mkFifoObj("bar", 6)}}}, 427 } 428 429 for _, expected := range expectedList { 430 cur := Pop(f).(Deltas) 431 if e, a := expected, cur; !reflect.DeepEqual(e, a) { 432 t.Errorf("Expected %#v, got %#v", e, a) 433 } 434 } 435 } 436 437 // TestDeltaFIFO_ReplaceDeltaType checks that passing EmitDeltaTypeReplaced 438 // means that Replaced is correctly emitted. 439 func TestDeltaFIFO_ReplaceDeltaType(t *testing.T) { 440 f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{ 441 KeyFunction: testFifoObjectKeyFunc, 442 KnownObjects: literalListerGetter(func() []testFifoObject { 443 return []testFifoObject{mkFifoObj("foo", 5)} 444 }), 445 EmitDeltaTypeReplaced: true, 446 }) 447 f.Replace([]interface{}{mkFifoObj("foo", 5)}, "0") 448 449 expectedList := []Deltas{ 450 {{Replaced, mkFifoObj("foo", 5)}}, 451 } 452 453 for _, expected := range expectedList { 454 cur := Pop(f).(Deltas) 455 if e, a := expected, cur; !reflect.DeepEqual(e, a) { 456 t.Errorf("Expected %#v, got %#v", e, a) 457 } 458 } 459 } 460 461 func TestDeltaFIFO_UpdateResyncRace(t *testing.T) { 462 f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{ 463 KeyFunction: testFifoObjectKeyFunc, 464 KnownObjects: literalListerGetter(func() []testFifoObject { 465 return []testFifoObject{mkFifoObj("foo", 5)} 466 }), 467 }) 468 f.Update(mkFifoObj("foo", 6)) 469 f.Resync() 470 471 expectedList := []Deltas{ 472 {{Updated, mkFifoObj("foo", 6)}}, 473 } 474 475 for _, expected := range expectedList { 476 cur := Pop(f).(Deltas) 477 if e, a := expected, cur; !reflect.DeepEqual(e, a) { 478 t.Errorf("Expected %#v, got %#v", e, a) 479 } 480 } 481 } 482 483 func TestDeltaFIFO_HasSyncedCorrectOnDeletion(t *testing.T) { 484 f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{ 485 KeyFunction: testFifoObjectKeyFunc, 486 KnownObjects: literalListerGetter(func() []testFifoObject { 487 return []testFifoObject{mkFifoObj("foo", 5), mkFifoObj("bar", 6), mkFifoObj("baz", 7)} 488 }), 489 }) 490 f.Replace([]interface{}{mkFifoObj("foo", 5)}, "0") 491 492 expectedList := []Deltas{ 493 {{Sync, mkFifoObj("foo", 5)}}, 494 // Since "bar" didn't have a delete event and wasn't in the Replace list 495 // it should get a tombstone key with the right Obj. 496 {{Deleted, DeletedFinalStateUnknown{Key: "bar", Obj: mkFifoObj("bar", 6)}}}, 497 {{Deleted, DeletedFinalStateUnknown{Key: "baz", Obj: mkFifoObj("baz", 7)}}}, 498 } 499 500 for _, expected := range expectedList { 501 if f.HasSynced() { 502 t.Errorf("Expected HasSynced to be false") 503 } 504 cur := Pop(f).(Deltas) 505 if e, a := expected, cur; !reflect.DeepEqual(e, a) { 506 t.Errorf("Expected %#v, got %#v", e, a) 507 } 508 } 509 if !f.HasSynced() { 510 t.Errorf("Expected HasSynced to be true") 511 } 512 } 513 514 func TestDeltaFIFO_detectLineJumpers(t *testing.T) { 515 f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{KeyFunction: testFifoObjectKeyFunc}) 516 517 f.Add(mkFifoObj("foo", 10)) 518 f.Add(mkFifoObj("bar", 1)) 519 f.Add(mkFifoObj("foo", 11)) 520 f.Add(mkFifoObj("foo", 13)) 521 f.Add(mkFifoObj("zab", 30)) 522 523 if e, a := 13, testPop(f).val; a != e { 524 t.Fatalf("expected %d, got %d", e, a) 525 } 526 527 f.Add(mkFifoObj("foo", 14)) // ensure foo doesn't jump back in line 528 529 if e, a := 1, testPop(f).val; a != e { 530 t.Fatalf("expected %d, got %d", e, a) 531 } 532 533 if e, a := 30, testPop(f).val; a != e { 534 t.Fatalf("expected %d, got %d", e, a) 535 } 536 537 if e, a := 14, testPop(f).val; a != e { 538 t.Fatalf("expected %d, got %d", e, a) 539 } 540 } 541 542 func TestDeltaFIFO_addIfNotPresent(t *testing.T) { 543 f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{KeyFunction: testFifoObjectKeyFunc}) 544 545 emptyDeltas := Deltas{} 546 if err := f.AddIfNotPresent(emptyDeltas); err == nil || !errors.Is(err, ErrZeroLengthDeltasObject) { 547 t.Errorf("Expected error '%v', got %v", ErrZeroLengthDeltasObject, err) 548 } 549 550 f.Add(mkFifoObj("b", 3)) 551 b3 := Pop(f) 552 f.Add(mkFifoObj("c", 4)) 553 c4 := Pop(f) 554 if e, a := 0, len(f.items); e != a { 555 t.Fatalf("Expected %v, got %v items in queue", e, a) 556 } 557 558 f.Add(mkFifoObj("a", 1)) 559 f.Add(mkFifoObj("b", 2)) 560 f.AddIfNotPresent(b3) 561 f.AddIfNotPresent(c4) 562 563 if e, a := 3, len(f.items); a != e { 564 t.Fatalf("expected queue length %d, got %d", e, a) 565 } 566 567 expectedValues := []int{1, 2, 4} 568 for _, expected := range expectedValues { 569 if actual := testPop(f).val; actual != expected { 570 t.Fatalf("expected value %d, got %d", expected, actual) 571 } 572 } 573 } 574 575 func TestDeltaFIFO_KeyOf(t *testing.T) { 576 f := DeltaFIFO{keyFunc: testFifoObjectKeyFunc} 577 578 table := []struct { 579 obj interface{} 580 key string 581 }{ 582 {obj: testFifoObject{name: "A"}, key: "A"}, 583 {obj: DeletedFinalStateUnknown{Key: "B", Obj: nil}, key: "B"}, 584 {obj: Deltas{{Object: testFifoObject{name: "C"}}}, key: "C"}, 585 {obj: Deltas{{Object: DeletedFinalStateUnknown{Key: "D", Obj: nil}}}, key: "D"}, 586 } 587 588 for _, item := range table { 589 got, err := f.KeyOf(item.obj) 590 if err != nil { 591 t.Errorf("Unexpected error for %q: %v", item.obj, err) 592 continue 593 } 594 if e, a := item.key, got; e != a { 595 t.Errorf("Expected %v, got %v", e, a) 596 } 597 } 598 } 599 600 func TestDeltaFIFO_HasSynced(t *testing.T) { 601 tests := []struct { 602 actions []func(f *DeltaFIFO) 603 expectedSynced bool 604 }{ 605 { 606 actions: []func(f *DeltaFIFO){}, 607 expectedSynced: false, 608 }, 609 { 610 actions: []func(f *DeltaFIFO){ 611 func(f *DeltaFIFO) { f.Add(mkFifoObj("a", 1)) }, 612 }, 613 expectedSynced: true, 614 }, 615 { 616 actions: []func(f *DeltaFIFO){ 617 func(f *DeltaFIFO) { f.Replace([]interface{}{}, "0") }, 618 }, 619 expectedSynced: true, 620 }, 621 { 622 actions: []func(f *DeltaFIFO){ 623 func(f *DeltaFIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") }, 624 }, 625 expectedSynced: false, 626 }, 627 { 628 actions: []func(f *DeltaFIFO){ 629 func(f *DeltaFIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") }, 630 func(f *DeltaFIFO) { Pop(f) }, 631 }, 632 expectedSynced: false, 633 }, 634 { 635 actions: []func(f *DeltaFIFO){ 636 func(f *DeltaFIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") }, 637 func(f *DeltaFIFO) { Pop(f) }, 638 func(f *DeltaFIFO) { Pop(f) }, 639 }, 640 expectedSynced: true, 641 }, 642 { 643 // This test case won't happen in practice since a Reflector, the only producer for delta_fifo today, always passes a complete snapshot consistent in time; 644 // there cannot be duplicate keys in the list or apiserver is broken. 645 actions: []func(f *DeltaFIFO){ 646 func(f *DeltaFIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("a", 2)}, "0") }, 647 func(f *DeltaFIFO) { Pop(f) }, 648 }, 649 expectedSynced: true, 650 }, 651 } 652 653 for i, test := range tests { 654 f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{KeyFunction: testFifoObjectKeyFunc}) 655 656 for _, action := range test.actions { 657 action(f) 658 } 659 if e, a := test.expectedSynced, f.HasSynced(); a != e { 660 t.Errorf("test case %v failed, expected: %v , got %v", i, e, a) 661 } 662 } 663 } 664 665 // TestDeltaFIFO_PopShouldUnblockWhenClosed checks that any blocking Pop on an empty queue 666 // should unblock and return after Close is called. 667 func TestDeltaFIFO_PopShouldUnblockWhenClosed(t *testing.T) { 668 f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{ 669 KeyFunction: testFifoObjectKeyFunc, 670 KnownObjects: literalListerGetter(func() []testFifoObject { 671 return []testFifoObject{mkFifoObj("foo", 5)} 672 }), 673 }) 674 675 c := make(chan struct{}) 676 const jobs = 10 677 for i := 0; i < jobs; i++ { 678 go func() { 679 f.Pop(func(obj interface{}) error { 680 return nil 681 }) 682 c <- struct{}{} 683 }() 684 } 685 686 runtime.Gosched() 687 f.Close() 688 689 for i := 0; i < jobs; i++ { 690 select { 691 case <-c: 692 case <-time.After(500 * time.Millisecond): 693 t.Fatalf("timed out waiting for Pop to return after Close") 694 } 695 } 696 }