k8s.io/client-go@v0.31.1/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 TestDeltaFIFOW_ReplaceMakesDeletionsForObjectsOnlyInQueue(t *testing.T) {
   125  	obj := mkFifoObj("foo", 2)
   126  	objV2 := mkFifoObj("foo", 3)
   127  	table := []struct {
   128  		name           string
   129  		operations     func(f *DeltaFIFO)
   130  		expectedDeltas Deltas
   131  	}{
   132  		{
   133  			name: "Added object should be deleted on Replace",
   134  			operations: func(f *DeltaFIFO) {
   135  				f.Add(obj)
   136  				f.Replace([]interface{}{}, "0")
   137  			},
   138  			expectedDeltas: Deltas{
   139  				{Added, obj},
   140  				{Deleted, DeletedFinalStateUnknown{Key: "foo", Obj: obj}},
   141  			},
   142  		},
   143  		{
   144  			name: "Replaced object should have only a single Delete",
   145  			operations: func(f *DeltaFIFO) {
   146  				f.emitDeltaTypeReplaced = true
   147  				f.Add(obj)
   148  				f.Replace([]interface{}{obj}, "0")
   149  				f.Replace([]interface{}{}, "0")
   150  			},
   151  			expectedDeltas: Deltas{
   152  				{Added, obj},
   153  				{Replaced, obj},
   154  				{Deleted, DeletedFinalStateUnknown{Key: "foo", Obj: obj}},
   155  			},
   156  		},
   157  		{
   158  			name: "Deleted object should have only a single Delete",
   159  			operations: func(f *DeltaFIFO) {
   160  				f.Add(obj)
   161  				f.Delete(obj)
   162  				f.Replace([]interface{}{}, "0")
   163  			},
   164  			expectedDeltas: Deltas{
   165  				{Added, obj},
   166  				{Deleted, obj},
   167  			},
   168  		},
   169  		{
   170  			name: "Synced objects should have a single delete",
   171  			operations: func(f *DeltaFIFO) {
   172  				f.Add(obj)
   173  				f.Replace([]interface{}{obj}, "0")
   174  				f.Replace([]interface{}{obj}, "0")
   175  				f.Replace([]interface{}{}, "0")
   176  			},
   177  			expectedDeltas: Deltas{
   178  				{Added, obj},
   179  				{Sync, obj},
   180  				{Sync, obj},
   181  				{Deleted, DeletedFinalStateUnknown{Key: "foo", Obj: obj}},
   182  			},
   183  		},
   184  		{
   185  			name: "Added objects should have a single delete on multiple Replaces",
   186  			operations: func(f *DeltaFIFO) {
   187  				f.Add(obj)
   188  				f.Replace([]interface{}{}, "0")
   189  				f.Replace([]interface{}{}, "1")
   190  			},
   191  			expectedDeltas: Deltas{
   192  				{Added, obj},
   193  				{Deleted, DeletedFinalStateUnknown{Key: "foo", Obj: obj}},
   194  			},
   195  		},
   196  		{
   197  			name: "Added and deleted and added object should be deleted",
   198  			operations: func(f *DeltaFIFO) {
   199  				f.Add(obj)
   200  				f.Delete(obj)
   201  				f.Add(objV2)
   202  				f.Replace([]interface{}{}, "0")
   203  			},
   204  			expectedDeltas: Deltas{
   205  				{Added, obj},
   206  				{Deleted, obj},
   207  				{Added, objV2},
   208  				{Deleted, DeletedFinalStateUnknown{Key: "foo", Obj: objV2}},
   209  			},
   210  		},
   211  	}
   212  	for _, tt := range table {
   213  		tt := tt
   214  
   215  		t.Run(tt.name, func(t *testing.T) {
   216  			// Test with a DeltaFIFO with a backing KnownObjects
   217  			fWithKnownObjects := NewDeltaFIFOWithOptions(DeltaFIFOOptions{
   218  				KeyFunction: testFifoObjectKeyFunc,
   219  				KnownObjects: literalListerGetter(func() []testFifoObject {
   220  					return []testFifoObject{}
   221  				}),
   222  			})
   223  			tt.operations(fWithKnownObjects)
   224  			actualDeltasWithKnownObjects := Pop(fWithKnownObjects)
   225  			if !reflect.DeepEqual(tt.expectedDeltas, actualDeltasWithKnownObjects) {
   226  				t.Errorf("expected %#v, got %#v", tt.expectedDeltas, actualDeltasWithKnownObjects)
   227  			}
   228  			if len(fWithKnownObjects.items) != 0 {
   229  				t.Errorf("expected no extra deltas (empty map), got %#v", fWithKnownObjects.items)
   230  			}
   231  
   232  			// Test with a DeltaFIFO without a backing KnownObjects
   233  			fWithoutKnownObjects := NewDeltaFIFOWithOptions(DeltaFIFOOptions{
   234  				KeyFunction: testFifoObjectKeyFunc,
   235  			})
   236  			tt.operations(fWithoutKnownObjects)
   237  			actualDeltasWithoutKnownObjects := Pop(fWithoutKnownObjects)
   238  			if !reflect.DeepEqual(tt.expectedDeltas, actualDeltasWithoutKnownObjects) {
   239  				t.Errorf("expected %#v, got %#v", tt.expectedDeltas, actualDeltasWithoutKnownObjects)
   240  			}
   241  			if len(fWithoutKnownObjects.items) != 0 {
   242  				t.Errorf("expected no extra deltas (empty map), got %#v", fWithoutKnownObjects.items)
   243  			}
   244  		})
   245  	}
   246  }
   247  
   248  func TestDeltaFIFO_requeueOnPop(t *testing.T) {
   249  	f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{KeyFunction: testFifoObjectKeyFunc})
   250  
   251  	f.Add(mkFifoObj("foo", 10))
   252  	_, err := f.Pop(func(obj interface{}, isInInitialList bool) error {
   253  		if obj.(Deltas)[0].Object.(testFifoObject).name != "foo" {
   254  			t.Fatalf("unexpected object: %#v", obj)
   255  		}
   256  		return ErrRequeue{Err: nil}
   257  	})
   258  	if err != nil {
   259  		t.Fatalf("unexpected error: %v", err)
   260  	}
   261  	if _, ok, err := f.GetByKey("foo"); !ok || err != nil {
   262  		t.Fatalf("object should have been requeued: %t %v", ok, err)
   263  	}
   264  
   265  	_, err = f.Pop(func(obj interface{}, isInInitialList bool) error {
   266  		if obj.(Deltas)[0].Object.(testFifoObject).name != "foo" {
   267  			t.Fatalf("unexpected object: %#v", obj)
   268  		}
   269  		return ErrRequeue{Err: fmt.Errorf("test error")}
   270  	})
   271  	if err == nil || err.Error() != "test error" {
   272  		t.Fatalf("unexpected error: %v", err)
   273  	}
   274  	if _, ok, err := f.GetByKey("foo"); !ok || err != nil {
   275  		t.Fatalf("object should have been requeued: %t %v", ok, err)
   276  	}
   277  
   278  	_, err = f.Pop(func(obj interface{}, isInInitialList bool) error {
   279  		if obj.(Deltas)[0].Object.(testFifoObject).name != "foo" {
   280  			t.Fatalf("unexpected object: %#v", obj)
   281  		}
   282  		return nil
   283  	})
   284  	if err != nil {
   285  		t.Fatalf("unexpected error: %v", err)
   286  	}
   287  	if _, ok, err := f.GetByKey("foo"); ok || err != nil {
   288  		t.Fatalf("object should have been removed: %t %v", ok, err)
   289  	}
   290  }
   291  
   292  func TestDeltaFIFO_addUpdate(t *testing.T) {
   293  	f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{KeyFunction: testFifoObjectKeyFunc})
   294  	f.Add(mkFifoObj("foo", 10))
   295  	f.Update(mkFifoObj("foo", 12))
   296  	f.Delete(mkFifoObj("foo", 15))
   297  
   298  	if e, a := []interface{}{mkFifoObj("foo", 15)}, f.List(); !reflect.DeepEqual(e, a) {
   299  		t.Errorf("Expected %+v, got %+v", e, a)
   300  	}
   301  	if e, a := []string{"foo"}, f.ListKeys(); !reflect.DeepEqual(e, a) {
   302  		t.Errorf("Expected %+v, got %+v", e, a)
   303  	}
   304  
   305  	got := make(chan testFifoObject, 2)
   306  	go func() {
   307  		for {
   308  			obj := testPop(f)
   309  			t.Logf("got a thing %#v", obj)
   310  			t.Logf("D len: %v", len(f.queue))
   311  			got <- obj
   312  		}
   313  	}()
   314  
   315  	first := <-got
   316  	if e, a := 15, first.val; e != a {
   317  		t.Errorf("Didn't get updated value (%v), got %v", e, a)
   318  	}
   319  	select {
   320  	case unexpected := <-got:
   321  		t.Errorf("Got second value %v", unexpected.val)
   322  	case <-time.After(50 * time.Millisecond):
   323  	}
   324  	_, exists, _ := f.Get(mkFifoObj("foo", ""))
   325  	if exists {
   326  		t.Errorf("item did not get removed")
   327  	}
   328  }
   329  
   330  type rvAndXfrm struct {
   331  	rv   int
   332  	xfrm int
   333  }
   334  
   335  func TestDeltaFIFO_transformer(t *testing.T) {
   336  	mk := func(name string, rv int) testFifoObject {
   337  		return mkFifoObj(name, &rvAndXfrm{rv, 0})
   338  	}
   339  	xfrm := TransformFunc(func(obj interface{}) (interface{}, error) {
   340  		switch v := obj.(type) {
   341  		case testFifoObject:
   342  			v.val.(*rvAndXfrm).xfrm++
   343  		case DeletedFinalStateUnknown:
   344  			if x := v.Obj.(testFifoObject).val.(*rvAndXfrm).xfrm; x != 1 {
   345  				return nil, fmt.Errorf("object has been transformed wrong number of times: %#v", obj)
   346  			}
   347  		default:
   348  			return nil, fmt.Errorf("unexpected object: %#v", obj)
   349  		}
   350  		return obj, nil
   351  	})
   352  
   353  	must := func(err error) {
   354  		if err != nil {
   355  			t.Fatal(err)
   356  		}
   357  	}
   358  
   359  	f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{
   360  		KeyFunction: testFifoObjectKeyFunc,
   361  		Transformer: xfrm,
   362  	})
   363  	must(f.Add(mk("foo", 10)))
   364  	must(f.Add(mk("bar", 11)))
   365  	must(f.Update(mk("foo", 12)))
   366  	must(f.Delete(mk("foo", 15)))
   367  	must(f.Replace([]interface{}{}, ""))
   368  	must(f.Add(mk("bar", 16)))
   369  	must(f.Replace([]interface{}{}, ""))
   370  
   371  	// Should be empty
   372  	if e, a := []string{"foo", "bar"}, f.ListKeys(); !reflect.DeepEqual(e, a) {
   373  		t.Errorf("Expected %+v, got %+v", e, a)
   374  	}
   375  
   376  	for i := 0; i < 2; i++ {
   377  		obj, err := f.Pop(func(o interface{}, isInInitialList bool) error { return nil })
   378  		if err != nil {
   379  			t.Fatalf("got nothing on try %v?", i)
   380  		}
   381  		obj = obj.(Deltas).Newest().Object
   382  		switch v := obj.(type) {
   383  		case testFifoObject:
   384  			if v.name != "foo" {
   385  				t.Errorf("expected regular deletion of foo, got %q", v.name)
   386  			}
   387  			rx := v.val.(*rvAndXfrm)
   388  			if rx.rv != 15 {
   389  				t.Errorf("expected last message, got %#v", obj)
   390  			}
   391  			if rx.xfrm != 1 {
   392  				t.Errorf("obj %v transformed wrong number of times.", obj)
   393  			}
   394  		case DeletedFinalStateUnknown:
   395  			tf := v.Obj.(testFifoObject)
   396  			rx := tf.val.(*rvAndXfrm)
   397  			if tf.name != "bar" {
   398  				t.Errorf("expected tombstone deletion of bar, got %q", tf.name)
   399  			}
   400  			if rx.rv != 16 {
   401  				t.Errorf("expected last message, got %#v", obj)
   402  			}
   403  			if rx.xfrm != 1 {
   404  				t.Errorf("tombstoned obj %v transformed wrong number of times.", obj)
   405  			}
   406  		default:
   407  			t.Errorf("unknown item %#v", obj)
   408  		}
   409  	}
   410  }
   411  
   412  func TestDeltaFIFO_enqueueingNoLister(t *testing.T) {
   413  	f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{KeyFunction: testFifoObjectKeyFunc})
   414  	f.Add(mkFifoObj("foo", 10))
   415  	f.Update(mkFifoObj("bar", 15))
   416  	f.Add(mkFifoObj("qux", 17))
   417  	f.Delete(mkFifoObj("qux", 18))
   418  
   419  	// This delete does not enqueue anything because baz doesn't exist.
   420  	f.Delete(mkFifoObj("baz", 20))
   421  
   422  	expectList := []int{10, 15, 18}
   423  	for _, expect := range expectList {
   424  		if e, a := expect, testPop(f).val; e != a {
   425  			t.Errorf("Didn't get updated value (%v), got %v", e, a)
   426  		}
   427  	}
   428  	if e, a := 0, len(f.items); e != a {
   429  		t.Errorf("queue unexpectedly not empty: %v != %v\n%#v", e, a, f.items)
   430  	}
   431  }
   432  
   433  func TestDeltaFIFO_enqueueingWithLister(t *testing.T) {
   434  	f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{
   435  		KeyFunction: testFifoObjectKeyFunc,
   436  		KnownObjects: literalListerGetter(func() []testFifoObject {
   437  			return []testFifoObject{mkFifoObj("foo", 5), mkFifoObj("bar", 6), mkFifoObj("baz", 7)}
   438  		}),
   439  	})
   440  	f.Add(mkFifoObj("foo", 10))
   441  	f.Update(mkFifoObj("bar", 15))
   442  
   443  	// This delete does enqueue the deletion, because "baz" is in the key lister.
   444  	f.Delete(mkFifoObj("baz", 20))
   445  
   446  	expectList := []int{10, 15, 20}
   447  	for _, expect := range expectList {
   448  		if e, a := expect, testPop(f).val; e != a {
   449  			t.Errorf("Didn't get updated value (%v), got %v", e, a)
   450  		}
   451  	}
   452  	if e, a := 0, len(f.items); e != a {
   453  		t.Errorf("queue unexpectedly not empty: %v != %v", e, a)
   454  	}
   455  }
   456  
   457  func TestDeltaFIFO_addReplace(t *testing.T) {
   458  	f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{KeyFunction: testFifoObjectKeyFunc})
   459  	f.Add(mkFifoObj("foo", 10))
   460  	f.Replace([]interface{}{mkFifoObj("foo", 15)}, "0")
   461  	got := make(chan testFifoObject, 2)
   462  	go func() {
   463  		for {
   464  			got <- testPop(f)
   465  		}
   466  	}()
   467  
   468  	first := <-got
   469  	if e, a := 15, first.val; e != a {
   470  		t.Errorf("Didn't get updated value (%v), got %v", e, a)
   471  	}
   472  	select {
   473  	case unexpected := <-got:
   474  		t.Errorf("Got second value %v", unexpected.val)
   475  	case <-time.After(50 * time.Millisecond):
   476  	}
   477  	_, exists, _ := f.Get(mkFifoObj("foo", ""))
   478  	if exists {
   479  		t.Errorf("item did not get removed")
   480  	}
   481  }
   482  
   483  func TestDeltaFIFO_ResyncNonExisting(t *testing.T) {
   484  	f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{
   485  		KeyFunction: testFifoObjectKeyFunc,
   486  		KnownObjects: literalListerGetter(func() []testFifoObject {
   487  			return []testFifoObject{mkFifoObj("foo", 5)}
   488  		}),
   489  	})
   490  	f.Delete(mkFifoObj("foo", 10))
   491  	f.Resync()
   492  
   493  	deltas := f.items["foo"]
   494  	if len(deltas) != 1 {
   495  		t.Fatalf("unexpected deltas length: %v", deltas)
   496  	}
   497  	if deltas[0].Type != Deleted {
   498  		t.Errorf("unexpected delta: %v", deltas[0])
   499  	}
   500  }
   501  
   502  func TestDeltaFIFO_Resync(t *testing.T) {
   503  	f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{
   504  		KeyFunction: testFifoObjectKeyFunc,
   505  		KnownObjects: literalListerGetter(func() []testFifoObject {
   506  			return []testFifoObject{mkFifoObj("foo", 5)}
   507  		}),
   508  	})
   509  	f.Resync()
   510  
   511  	deltas := f.items["foo"]
   512  	if len(deltas) != 1 {
   513  		t.Fatalf("unexpected deltas length: %v", deltas)
   514  	}
   515  	if deltas[0].Type != Sync {
   516  		t.Errorf("unexpected delta: %v", deltas[0])
   517  	}
   518  }
   519  
   520  func TestDeltaFIFO_DeleteExistingNonPropagated(t *testing.T) {
   521  	f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{
   522  		KeyFunction: testFifoObjectKeyFunc,
   523  		KnownObjects: literalListerGetter(func() []testFifoObject {
   524  			return []testFifoObject{}
   525  		}),
   526  	})
   527  	f.Add(mkFifoObj("foo", 5))
   528  	f.Delete(mkFifoObj("foo", 6))
   529  
   530  	deltas := f.items["foo"]
   531  	if len(deltas) != 2 {
   532  		t.Fatalf("unexpected deltas length: %v", deltas)
   533  	}
   534  	if deltas[len(deltas)-1].Type != Deleted {
   535  		t.Errorf("unexpected delta: %v", deltas[len(deltas)-1])
   536  	}
   537  }
   538  
   539  func TestDeltaFIFO_ReplaceMakesDeletions(t *testing.T) {
   540  	// We test with only one pre-existing object because there is no
   541  	// promise about how their deletes are ordered.
   542  
   543  	// Try it with a pre-existing Delete
   544  	f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{
   545  		KeyFunction: testFifoObjectKeyFunc,
   546  		KnownObjects: literalListerGetter(func() []testFifoObject {
   547  			return []testFifoObject{mkFifoObj("foo", 5), mkFifoObj("bar", 6), mkFifoObj("baz", 7)}
   548  		}),
   549  	})
   550  	f.Delete(mkFifoObj("baz", 10))
   551  	f.Replace([]interface{}{mkFifoObj("foo", 5)}, "0")
   552  
   553  	expectedList := []Deltas{
   554  		{{Deleted, mkFifoObj("baz", 10)}},
   555  		{{Sync, mkFifoObj("foo", 5)}},
   556  		// Since "bar" didn't have a delete event and wasn't in the Replace list
   557  		// it should get a tombstone key with the right Obj.
   558  		{{Deleted, DeletedFinalStateUnknown{Key: "bar", Obj: mkFifoObj("bar", 6)}}},
   559  	}
   560  
   561  	for _, expected := range expectedList {
   562  		cur := Pop(f).(Deltas)
   563  		if e, a := expected, cur; !reflect.DeepEqual(e, a) {
   564  			t.Errorf("Expected %#v, got %#v", e, a)
   565  		}
   566  	}
   567  
   568  	// Now try starting with an Add instead of a Delete
   569  	f = NewDeltaFIFOWithOptions(DeltaFIFOOptions{
   570  		KeyFunction: testFifoObjectKeyFunc,
   571  		KnownObjects: literalListerGetter(func() []testFifoObject {
   572  			return []testFifoObject{mkFifoObj("foo", 5), mkFifoObj("bar", 6), mkFifoObj("baz", 7)}
   573  		}),
   574  	})
   575  	f.Add(mkFifoObj("baz", 10))
   576  	f.Replace([]interface{}{mkFifoObj("foo", 5)}, "0")
   577  
   578  	expectedList = []Deltas{
   579  		{{Added, mkFifoObj("baz", 10)},
   580  			{Deleted, DeletedFinalStateUnknown{Key: "baz", Obj: mkFifoObj("baz", 10)}}},
   581  		{{Sync, mkFifoObj("foo", 5)}},
   582  		// Since "bar" didn't have a delete event and wasn't in the Replace list
   583  		// it should get a tombstone key with the right Obj.
   584  		{{Deleted, DeletedFinalStateUnknown{Key: "bar", Obj: mkFifoObj("bar", 6)}}},
   585  	}
   586  
   587  	for _, expected := range expectedList {
   588  		cur := Pop(f).(Deltas)
   589  		if e, a := expected, cur; !reflect.DeepEqual(e, a) {
   590  			t.Errorf("Expected %#v, got %#v", e, a)
   591  		}
   592  	}
   593  
   594  	// Now try deleting and recreating the object in the queue, then delete it by a Replace call
   595  	f = NewDeltaFIFOWithOptions(DeltaFIFOOptions{
   596  		KeyFunction: testFifoObjectKeyFunc,
   597  		KnownObjects: literalListerGetter(func() []testFifoObject {
   598  			return []testFifoObject{mkFifoObj("foo", 5), mkFifoObj("bar", 6), mkFifoObj("baz", 7)}
   599  		}),
   600  	})
   601  	f.Delete(mkFifoObj("bar", 6))
   602  	f.Add(mkFifoObj("bar", 100))
   603  	f.Replace([]interface{}{mkFifoObj("foo", 5)}, "0")
   604  
   605  	expectedList = []Deltas{
   606  		{
   607  			{Deleted, mkFifoObj("bar", 6)},
   608  			{Added, mkFifoObj("bar", 100)},
   609  			// Since "bar" has a newer object in the queue than in the state,
   610  			// it should get a tombstone key with the latest object from the queue
   611  			{Deleted, DeletedFinalStateUnknown{Key: "bar", Obj: mkFifoObj("bar", 100)}},
   612  		},
   613  		{{Sync, mkFifoObj("foo", 5)}},
   614  		{{Deleted, DeletedFinalStateUnknown{Key: "baz", Obj: mkFifoObj("baz", 7)}}},
   615  	}
   616  
   617  	for _, expected := range expectedList {
   618  		cur := Pop(f).(Deltas)
   619  		if e, a := expected, cur; !reflect.DeepEqual(e, a) {
   620  			t.Errorf("Expected %#v, got %#v", e, a)
   621  		}
   622  	}
   623  
   624  	// Now try syncing it first to ensure the delete use the latest version
   625  	f = NewDeltaFIFOWithOptions(DeltaFIFOOptions{
   626  		KeyFunction: testFifoObjectKeyFunc,
   627  		KnownObjects: literalListerGetter(func() []testFifoObject {
   628  			return []testFifoObject{mkFifoObj("foo", 5), mkFifoObj("bar", 6), mkFifoObj("baz", 7)}
   629  		}),
   630  	})
   631  	f.Replace([]interface{}{mkFifoObj("bar", 100), mkFifoObj("foo", 5)}, "0")
   632  	f.Replace([]interface{}{mkFifoObj("foo", 5)}, "0")
   633  
   634  	expectedList = []Deltas{
   635  		{
   636  			{Sync, mkFifoObj("bar", 100)},
   637  			// Since "bar" didn't have a delete event and wasn't in the Replace list
   638  			// it should get a tombstone key with the right Obj.
   639  			{Deleted, DeletedFinalStateUnknown{Key: "bar", Obj: mkFifoObj("bar", 100)}},
   640  		},
   641  		{
   642  			{Sync, mkFifoObj("foo", 5)},
   643  			{Sync, mkFifoObj("foo", 5)},
   644  		},
   645  		{{Deleted, DeletedFinalStateUnknown{Key: "baz", Obj: mkFifoObj("baz", 7)}}},
   646  	}
   647  
   648  	for _, expected := range expectedList {
   649  		cur := Pop(f).(Deltas)
   650  		if e, a := expected, cur; !reflect.DeepEqual(e, a) {
   651  			t.Errorf("Expected %#v, got %#v", e, a)
   652  		}
   653  	}
   654  
   655  	// Now try starting without an explicit KeyListerGetter
   656  	f = NewDeltaFIFOWithOptions(DeltaFIFOOptions{KeyFunction: testFifoObjectKeyFunc})
   657  	f.Add(mkFifoObj("baz", 10))
   658  	f.Replace([]interface{}{mkFifoObj("foo", 5)}, "0")
   659  
   660  	expectedList = []Deltas{
   661  		{{Added, mkFifoObj("baz", 10)},
   662  			{Deleted, DeletedFinalStateUnknown{Key: "baz", Obj: mkFifoObj("baz", 10)}}},
   663  		{{Sync, mkFifoObj("foo", 5)}},
   664  	}
   665  
   666  	for _, expected := range expectedList {
   667  		cur := Pop(f).(Deltas)
   668  		if e, a := expected, cur; !reflect.DeepEqual(e, a) {
   669  			t.Errorf("Expected %#v, got %#v", e, a)
   670  		}
   671  	}
   672  }
   673  
   674  // TestDeltaFIFO_ReplaceMakesDeletionsReplaced is the same as the above test, but
   675  // ensures that a Replaced DeltaType is emitted.
   676  func TestDeltaFIFO_ReplaceMakesDeletionsReplaced(t *testing.T) {
   677  	f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{
   678  		KeyFunction: testFifoObjectKeyFunc,
   679  		KnownObjects: literalListerGetter(func() []testFifoObject {
   680  			return []testFifoObject{mkFifoObj("foo", 5), mkFifoObj("bar", 6), mkFifoObj("baz", 7)}
   681  		}),
   682  		EmitDeltaTypeReplaced: true,
   683  	})
   684  
   685  	f.Delete(mkFifoObj("baz", 10))
   686  	f.Replace([]interface{}{mkFifoObj("foo", 6)}, "0")
   687  
   688  	expectedList := []Deltas{
   689  		{{Deleted, mkFifoObj("baz", 10)}},
   690  		{{Replaced, mkFifoObj("foo", 6)}},
   691  		// Since "bar" didn't have a delete event and wasn't in the Replace list
   692  		// it should get a tombstone key with the right Obj.
   693  		{{Deleted, DeletedFinalStateUnknown{Key: "bar", Obj: mkFifoObj("bar", 6)}}},
   694  	}
   695  
   696  	for _, expected := range expectedList {
   697  		cur := Pop(f).(Deltas)
   698  		if e, a := expected, cur; !reflect.DeepEqual(e, a) {
   699  			t.Errorf("Expected %#v, got %#v", e, a)
   700  		}
   701  	}
   702  }
   703  
   704  // TestDeltaFIFO_ReplaceDeltaType checks that passing EmitDeltaTypeReplaced
   705  // means that Replaced is correctly emitted.
   706  func TestDeltaFIFO_ReplaceDeltaType(t *testing.T) {
   707  	f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{
   708  		KeyFunction: testFifoObjectKeyFunc,
   709  		KnownObjects: literalListerGetter(func() []testFifoObject {
   710  			return []testFifoObject{mkFifoObj("foo", 5)}
   711  		}),
   712  		EmitDeltaTypeReplaced: true,
   713  	})
   714  	f.Replace([]interface{}{mkFifoObj("foo", 5)}, "0")
   715  
   716  	expectedList := []Deltas{
   717  		{{Replaced, mkFifoObj("foo", 5)}},
   718  	}
   719  
   720  	for _, expected := range expectedList {
   721  		cur := Pop(f).(Deltas)
   722  		if e, a := expected, cur; !reflect.DeepEqual(e, a) {
   723  			t.Errorf("Expected %#v, got %#v", e, a)
   724  		}
   725  	}
   726  }
   727  
   728  func TestDeltaFIFO_UpdateResyncRace(t *testing.T) {
   729  	f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{
   730  		KeyFunction: testFifoObjectKeyFunc,
   731  		KnownObjects: literalListerGetter(func() []testFifoObject {
   732  			return []testFifoObject{mkFifoObj("foo", 5)}
   733  		}),
   734  	})
   735  	f.Update(mkFifoObj("foo", 6))
   736  	f.Resync()
   737  
   738  	expectedList := []Deltas{
   739  		{{Updated, mkFifoObj("foo", 6)}},
   740  	}
   741  
   742  	for _, expected := range expectedList {
   743  		cur := Pop(f).(Deltas)
   744  		if e, a := expected, cur; !reflect.DeepEqual(e, a) {
   745  			t.Errorf("Expected %#v, got %#v", e, a)
   746  		}
   747  	}
   748  }
   749  
   750  // pop2 captures both parameters, unlike Pop().
   751  func pop2[T any](queue Queue) (T, bool) {
   752  	var result interface{}
   753  	var isList bool
   754  	queue.Pop(func(obj interface{}, isInInitialList bool) error {
   755  		result = obj
   756  		isList = isInInitialList
   757  		return nil
   758  	})
   759  	return result.(T), isList
   760  }
   761  
   762  func TestDeltaFIFO_HasSyncedCorrectOnDeletion(t *testing.T) {
   763  	f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{
   764  		KeyFunction: testFifoObjectKeyFunc,
   765  		KnownObjects: literalListerGetter(func() []testFifoObject {
   766  			return []testFifoObject{mkFifoObj("foo", 5), mkFifoObj("bar", 6), mkFifoObj("baz", 7)}
   767  		}),
   768  	})
   769  	f.Replace([]interface{}{mkFifoObj("foo", 5)}, "0")
   770  
   771  	expectedList := []Deltas{
   772  		{{Sync, mkFifoObj("foo", 5)}},
   773  		// Since "bar" didn't have a delete event and wasn't in the Replace list
   774  		// it should get a tombstone key with the right Obj.
   775  		{{Deleted, DeletedFinalStateUnknown{Key: "bar", Obj: mkFifoObj("bar", 6)}}},
   776  		{{Deleted, DeletedFinalStateUnknown{Key: "baz", Obj: mkFifoObj("baz", 7)}}},
   777  	}
   778  
   779  	for _, expected := range expectedList {
   780  		if f.HasSynced() {
   781  			t.Errorf("Expected HasSynced to be false")
   782  		}
   783  		cur, initial := pop2[Deltas](f)
   784  		if e, a := expected, cur; !reflect.DeepEqual(e, a) {
   785  			t.Errorf("Expected %#v, got %#v", e, a)
   786  		}
   787  		if initial != true {
   788  			t.Error("Expected initial list item")
   789  		}
   790  	}
   791  	if !f.HasSynced() {
   792  		t.Errorf("Expected HasSynced to be true")
   793  	}
   794  }
   795  
   796  func TestDeltaFIFO_detectLineJumpers(t *testing.T) {
   797  	f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{KeyFunction: testFifoObjectKeyFunc})
   798  
   799  	f.Add(mkFifoObj("foo", 10))
   800  	f.Add(mkFifoObj("bar", 1))
   801  	f.Add(mkFifoObj("foo", 11))
   802  	f.Add(mkFifoObj("foo", 13))
   803  	f.Add(mkFifoObj("zab", 30))
   804  
   805  	if e, a := 13, testPop(f).val; a != e {
   806  		t.Fatalf("expected %d, got %d", e, a)
   807  	}
   808  
   809  	f.Add(mkFifoObj("foo", 14)) // ensure foo doesn't jump back in line
   810  
   811  	if e, a := 1, testPop(f).val; a != e {
   812  		t.Fatalf("expected %d, got %d", e, a)
   813  	}
   814  
   815  	if e, a := 30, testPop(f).val; a != e {
   816  		t.Fatalf("expected %d, got %d", e, a)
   817  	}
   818  
   819  	if e, a := 14, testPop(f).val; a != e {
   820  		t.Fatalf("expected %d, got %d", e, a)
   821  	}
   822  }
   823  
   824  func TestDeltaFIFO_addIfNotPresent(t *testing.T) {
   825  	f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{KeyFunction: testFifoObjectKeyFunc})
   826  
   827  	emptyDeltas := Deltas{}
   828  	if err := f.AddIfNotPresent(emptyDeltas); err == nil || !errors.Is(err, ErrZeroLengthDeltasObject) {
   829  		t.Errorf("Expected error '%v', got %v", ErrZeroLengthDeltasObject, err)
   830  	}
   831  
   832  	f.Add(mkFifoObj("b", 3))
   833  	b3 := Pop(f)
   834  	f.Add(mkFifoObj("c", 4))
   835  	c4 := Pop(f)
   836  	if e, a := 0, len(f.items); e != a {
   837  		t.Fatalf("Expected %v, got %v items in queue", e, a)
   838  	}
   839  
   840  	f.Add(mkFifoObj("a", 1))
   841  	f.Add(mkFifoObj("b", 2))
   842  	f.AddIfNotPresent(b3)
   843  	f.AddIfNotPresent(c4)
   844  
   845  	if e, a := 3, len(f.items); a != e {
   846  		t.Fatalf("expected queue length %d, got %d", e, a)
   847  	}
   848  
   849  	expectedValues := []int{1, 2, 4}
   850  	for _, expected := range expectedValues {
   851  		if actual := testPop(f).val; actual != expected {
   852  			t.Fatalf("expected value %d, got %d", expected, actual)
   853  		}
   854  	}
   855  }
   856  
   857  func TestDeltaFIFO_KeyOf(t *testing.T) {
   858  	f := DeltaFIFO{keyFunc: testFifoObjectKeyFunc}
   859  
   860  	table := []struct {
   861  		obj interface{}
   862  		key string
   863  	}{
   864  		{obj: testFifoObject{name: "A"}, key: "A"},
   865  		{obj: DeletedFinalStateUnknown{Key: "B", Obj: nil}, key: "B"},
   866  		{obj: Deltas{{Object: testFifoObject{name: "C"}}}, key: "C"},
   867  		{obj: Deltas{{Object: DeletedFinalStateUnknown{Key: "D", Obj: nil}}}, key: "D"},
   868  	}
   869  
   870  	for _, item := range table {
   871  		got, err := f.KeyOf(item.obj)
   872  		if err != nil {
   873  			t.Errorf("Unexpected error for %q: %v", item.obj, err)
   874  			continue
   875  		}
   876  		if e, a := item.key, got; e != a {
   877  			t.Errorf("Expected %v, got %v", e, a)
   878  		}
   879  	}
   880  }
   881  
   882  func TestDeltaFIFO_HasSynced(t *testing.T) {
   883  	tests := []struct {
   884  		actions        []func(f *DeltaFIFO)
   885  		expectedSynced bool
   886  	}{
   887  		{
   888  			actions:        []func(f *DeltaFIFO){},
   889  			expectedSynced: false,
   890  		},
   891  		{
   892  			actions: []func(f *DeltaFIFO){
   893  				func(f *DeltaFIFO) { f.Add(mkFifoObj("a", 1)) },
   894  			},
   895  			expectedSynced: true,
   896  		},
   897  		{
   898  			actions: []func(f *DeltaFIFO){
   899  				func(f *DeltaFIFO) { f.Replace([]interface{}{}, "0") },
   900  			},
   901  			expectedSynced: true,
   902  		},
   903  		{
   904  			actions: []func(f *DeltaFIFO){
   905  				func(f *DeltaFIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") },
   906  			},
   907  			expectedSynced: false,
   908  		},
   909  		{
   910  			actions: []func(f *DeltaFIFO){
   911  				func(f *DeltaFIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") },
   912  				func(f *DeltaFIFO) { Pop(f) },
   913  			},
   914  			expectedSynced: false,
   915  		},
   916  		{
   917  			actions: []func(f *DeltaFIFO){
   918  				func(f *DeltaFIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") },
   919  				func(f *DeltaFIFO) { Pop(f) },
   920  				func(f *DeltaFIFO) { Pop(f) },
   921  			},
   922  			expectedSynced: true,
   923  		},
   924  		{
   925  			// 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;
   926  			// there cannot be duplicate keys in the list or apiserver is broken.
   927  			actions: []func(f *DeltaFIFO){
   928  				func(f *DeltaFIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("a", 2)}, "0") },
   929  				func(f *DeltaFIFO) { Pop(f) },
   930  			},
   931  			expectedSynced: true,
   932  		},
   933  	}
   934  
   935  	for i, test := range tests {
   936  		f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{KeyFunction: testFifoObjectKeyFunc})
   937  
   938  		for _, action := range test.actions {
   939  			action(f)
   940  		}
   941  		if e, a := test.expectedSynced, f.HasSynced(); a != e {
   942  			t.Errorf("test case %v failed, expected: %v , got %v", i, e, a)
   943  		}
   944  	}
   945  }
   946  
   947  // TestDeltaFIFO_PopShouldUnblockWhenClosed checks that any blocking Pop on an empty queue
   948  // should unblock and return after Close is called.
   949  func TestDeltaFIFO_PopShouldUnblockWhenClosed(t *testing.T) {
   950  	f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{
   951  		KeyFunction: testFifoObjectKeyFunc,
   952  		KnownObjects: literalListerGetter(func() []testFifoObject {
   953  			return []testFifoObject{mkFifoObj("foo", 5)}
   954  		}),
   955  	})
   956  
   957  	c := make(chan struct{})
   958  	const jobs = 10
   959  	for i := 0; i < jobs; i++ {
   960  		go func() {
   961  			f.Pop(func(obj interface{}, isInInitialList bool) error {
   962  				return nil
   963  			})
   964  			c <- struct{}{}
   965  		}()
   966  	}
   967  
   968  	runtime.Gosched()
   969  	f.Close()
   970  
   971  	for i := 0; i < jobs; i++ {
   972  		select {
   973  		case <-c:
   974  		case <-time.After(500 * time.Millisecond):
   975  			t.Fatalf("timed out waiting for Pop to return after Close")
   976  		}
   977  	}
   978  }
   979  
   980  func BenchmarkDeltaFIFOListKeys(b *testing.B) {
   981  	f := NewDeltaFIFOWithOptions(DeltaFIFOOptions{KeyFunction: testFifoObjectKeyFunc})
   982  	const amount = 10000
   983  
   984  	for i := 0; i < amount; i++ {
   985  		f.Add(mkFifoObj(string([]rune{'a', rune(i)}), i+1))
   986  	}
   987  	for u := uint64(0); u < amount; u++ {
   988  		f.Add(mkFifoObj(string([]rune{'b', rune(u)}), u+1))
   989  	}
   990  
   991  	b.ResetTimer()
   992  	b.RunParallel(func(pb *testing.PB) {
   993  		for pb.Next() {
   994  			_ = f.ListKeys()
   995  		}
   996  	})
   997  	b.StopTimer()
   998  }