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  }