k8s.io/client-go@v0.31.1/tools/cache/heap_test.go (about)

     1  /*
     2  Copyright 2017 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  	"sync"
    21  	"testing"
    22  	"time"
    23  )
    24  
    25  func testHeapObjectKeyFunc(obj interface{}) (string, error) {
    26  	return obj.(testHeapObject).name, nil
    27  }
    28  
    29  type testHeapObject struct {
    30  	name string
    31  	val  interface{}
    32  }
    33  
    34  func mkHeapObj(name string, val interface{}) testHeapObject {
    35  	return testHeapObject{name: name, val: val}
    36  }
    37  
    38  func compareInts(val1 interface{}, val2 interface{}) bool {
    39  	first := val1.(testHeapObject).val.(int)
    40  	second := val2.(testHeapObject).val.(int)
    41  	return first < second
    42  }
    43  
    44  // TestHeapBasic tests Heap invariant and synchronization.
    45  func TestHeapBasic(t *testing.T) {
    46  	h := NewHeap(testHeapObjectKeyFunc, compareInts)
    47  	var wg sync.WaitGroup
    48  	wg.Add(2)
    49  	const amount = 500
    50  	var i, u int
    51  	// Insert items in the heap in opposite orders in two go routines.
    52  	go func() {
    53  		for i = amount; i > 0; i-- {
    54  			h.Add(mkHeapObj(string([]rune{'a', rune(i)}), i))
    55  		}
    56  		wg.Done()
    57  	}()
    58  	go func() {
    59  		for u = 0; u < amount; u++ {
    60  			h.Add(mkHeapObj(string([]rune{'b', rune(u)}), u+1))
    61  		}
    62  		wg.Done()
    63  	}()
    64  	// Wait for the two go routines to finish.
    65  	wg.Wait()
    66  	// Make sure that the numbers are popped in ascending order.
    67  	prevNum := 0
    68  	for i := 0; i < amount*2; i++ {
    69  		obj, err := h.Pop()
    70  		num := obj.(testHeapObject).val.(int)
    71  		// All the items must be sorted.
    72  		if err != nil || prevNum > num {
    73  			t.Errorf("got %v out of order, last was %v", obj, prevNum)
    74  		}
    75  		prevNum = num
    76  	}
    77  }
    78  
    79  // Tests Heap.Add and ensures that heap invariant is preserved after adding items.
    80  func TestHeap_Add(t *testing.T) {
    81  	h := NewHeap(testHeapObjectKeyFunc, compareInts)
    82  	h.Add(mkHeapObj("foo", 10))
    83  	h.Add(mkHeapObj("bar", 1))
    84  	h.Add(mkHeapObj("baz", 11))
    85  	h.Add(mkHeapObj("zab", 30))
    86  	h.Add(mkHeapObj("foo", 13)) // This updates "foo".
    87  
    88  	item, err := h.Pop()
    89  	if e, a := 1, item.(testHeapObject).val; err != nil || a != e {
    90  		t.Fatalf("expected %d, got %d", e, a)
    91  	}
    92  	item, err = h.Pop()
    93  	if e, a := 11, item.(testHeapObject).val; err != nil || a != e {
    94  		t.Fatalf("expected %d, got %d", e, a)
    95  	}
    96  	h.Delete(mkHeapObj("baz", 11)) // Nothing is deleted.
    97  	h.Add(mkHeapObj("foo", 14))    // foo is updated.
    98  	item, err = h.Pop()
    99  	if e, a := 14, item.(testHeapObject).val; err != nil || a != e {
   100  		t.Fatalf("expected %d, got %d", e, a)
   101  	}
   102  	item, err = h.Pop()
   103  	if e, a := 30, item.(testHeapObject).val; err != nil || a != e {
   104  		t.Fatalf("expected %d, got %d", e, a)
   105  	}
   106  }
   107  
   108  // TestHeap_BulkAdd tests Heap.BulkAdd functionality and ensures that all the
   109  // items given to BulkAdd are added to the queue before Pop reads them.
   110  func TestHeap_BulkAdd(t *testing.T) {
   111  	h := NewHeap(testHeapObjectKeyFunc, compareInts)
   112  	const amount = 500
   113  	// Insert items in the heap in opposite orders in a go routine.
   114  	go func() {
   115  		l := []interface{}{}
   116  		for i := amount; i > 0; i-- {
   117  			l = append(l, mkHeapObj(string([]rune{'a', rune(i)}), i))
   118  		}
   119  		h.BulkAdd(l)
   120  	}()
   121  	prevNum := -1
   122  	for i := 0; i < amount; i++ {
   123  		obj, err := h.Pop()
   124  		num := obj.(testHeapObject).val.(int)
   125  		// All the items must be sorted.
   126  		if err != nil || prevNum >= num {
   127  			t.Errorf("got %v out of order, last was %v", obj, prevNum)
   128  		}
   129  		prevNum = num
   130  	}
   131  }
   132  
   133  // TestHeapEmptyPop tests that pop returns properly after heap is closed.
   134  func TestHeapEmptyPop(t *testing.T) {
   135  	h := NewHeap(testHeapObjectKeyFunc, compareInts)
   136  	go func() {
   137  		time.Sleep(1 * time.Second)
   138  		h.Close()
   139  	}()
   140  	_, err := h.Pop()
   141  	if err == nil || err.Error() != closedMsg {
   142  		t.Errorf("pop should have returned heap closed error: %v", err)
   143  	}
   144  }
   145  
   146  // TestHeap_AddIfNotPresent tests Heap.AddIfNotPresent and ensures that heap
   147  // invariant is preserved after adding items.
   148  func TestHeap_AddIfNotPresent(t *testing.T) {
   149  	h := NewHeap(testHeapObjectKeyFunc, compareInts)
   150  	h.AddIfNotPresent(mkHeapObj("foo", 10))
   151  	h.AddIfNotPresent(mkHeapObj("bar", 1))
   152  	h.AddIfNotPresent(mkHeapObj("baz", 11))
   153  	h.AddIfNotPresent(mkHeapObj("zab", 30))
   154  	h.AddIfNotPresent(mkHeapObj("foo", 13)) // This is not added.
   155  
   156  	if len := len(h.data.items); len != 4 {
   157  		t.Errorf("unexpected number of items: %d", len)
   158  	}
   159  	if val := h.data.items["foo"].obj.(testHeapObject).val; val != 10 {
   160  		t.Errorf("unexpected value: %d", val)
   161  	}
   162  	item, err := h.Pop()
   163  	if e, a := 1, item.(testHeapObject).val; err != nil || a != e {
   164  		t.Fatalf("expected %d, got %d", e, a)
   165  	}
   166  	item, err = h.Pop()
   167  	if e, a := 10, item.(testHeapObject).val; err != nil || a != e {
   168  		t.Fatalf("expected %d, got %d", e, a)
   169  	}
   170  	// bar is already popped. Let's add another one.
   171  	h.AddIfNotPresent(mkHeapObj("bar", 14))
   172  	item, err = h.Pop()
   173  	if e, a := 11, item.(testHeapObject).val; err != nil || a != e {
   174  		t.Fatalf("expected %d, got %d", e, a)
   175  	}
   176  	item, err = h.Pop()
   177  	if e, a := 14, item.(testHeapObject).val; err != nil || a != e {
   178  		t.Fatalf("expected %d, got %d", e, a)
   179  	}
   180  }
   181  
   182  // TestHeap_Delete tests Heap.Delete and ensures that heap invariant is
   183  // preserved after deleting items.
   184  func TestHeap_Delete(t *testing.T) {
   185  	h := NewHeap(testHeapObjectKeyFunc, compareInts)
   186  	h.Add(mkHeapObj("foo", 10))
   187  	h.Add(mkHeapObj("bar", 1))
   188  	h.Add(mkHeapObj("bal", 31))
   189  	h.Add(mkHeapObj("baz", 11))
   190  
   191  	// Delete head. Delete should work with "key" and doesn't care about the value.
   192  	if err := h.Delete(mkHeapObj("bar", 200)); err != nil {
   193  		t.Fatalf("Failed to delete head.")
   194  	}
   195  	item, err := h.Pop()
   196  	if e, a := 10, item.(testHeapObject).val; err != nil || a != e {
   197  		t.Fatalf("expected %d, got %d", e, a)
   198  	}
   199  	h.Add(mkHeapObj("zab", 30))
   200  	h.Add(mkHeapObj("faz", 30))
   201  	len := h.data.Len()
   202  	// Delete non-existing item.
   203  	if err = h.Delete(mkHeapObj("non-existent", 10)); err == nil || len != h.data.Len() {
   204  		t.Fatalf("Didn't expect any item removal")
   205  	}
   206  	// Delete tail.
   207  	if err = h.Delete(mkHeapObj("bal", 31)); err != nil {
   208  		t.Fatalf("Failed to delete tail.")
   209  	}
   210  	// Delete one of the items with value 30.
   211  	if err = h.Delete(mkHeapObj("zab", 30)); err != nil {
   212  		t.Fatalf("Failed to delete item.")
   213  	}
   214  	item, err = h.Pop()
   215  	if e, a := 11, item.(testHeapObject).val; err != nil || a != e {
   216  		t.Fatalf("expected %d, got %d", e, a)
   217  	}
   218  	item, err = h.Pop()
   219  	if e, a := 30, item.(testHeapObject).val; err != nil || a != e {
   220  		t.Fatalf("expected %d, got %d", e, a)
   221  	}
   222  	if h.data.Len() != 0 {
   223  		t.Fatalf("expected an empty heap.")
   224  	}
   225  }
   226  
   227  // TestHeap_Update tests Heap.Update and ensures that heap invariant is
   228  // preserved after adding items.
   229  func TestHeap_Update(t *testing.T) {
   230  	h := NewHeap(testHeapObjectKeyFunc, compareInts)
   231  	h.Add(mkHeapObj("foo", 10))
   232  	h.Add(mkHeapObj("bar", 1))
   233  	h.Add(mkHeapObj("bal", 31))
   234  	h.Add(mkHeapObj("baz", 11))
   235  
   236  	// Update an item to a value that should push it to the head.
   237  	h.Update(mkHeapObj("baz", 0))
   238  	if h.data.queue[0] != "baz" || h.data.items["baz"].index != 0 {
   239  		t.Fatalf("expected baz to be at the head")
   240  	}
   241  	item, err := h.Pop()
   242  	if e, a := 0, item.(testHeapObject).val; err != nil || a != e {
   243  		t.Fatalf("expected %d, got %d", e, a)
   244  	}
   245  	// Update bar to push it farther back in the queue.
   246  	h.Update(mkHeapObj("bar", 100))
   247  	if h.data.queue[0] != "foo" || h.data.items["foo"].index != 0 {
   248  		t.Fatalf("expected foo to be at the head")
   249  	}
   250  }
   251  
   252  // TestHeap_Get tests Heap.Get.
   253  func TestHeap_Get(t *testing.T) {
   254  	h := NewHeap(testHeapObjectKeyFunc, compareInts)
   255  	h.Add(mkHeapObj("foo", 10))
   256  	h.Add(mkHeapObj("bar", 1))
   257  	h.Add(mkHeapObj("bal", 31))
   258  	h.Add(mkHeapObj("baz", 11))
   259  
   260  	// Get works with the key.
   261  	obj, exists, err := h.Get(mkHeapObj("baz", 0))
   262  	if err != nil || exists == false || obj.(testHeapObject).val != 11 {
   263  		t.Fatalf("unexpected error in getting element")
   264  	}
   265  	// Get non-existing object.
   266  	_, exists, err = h.Get(mkHeapObj("non-existing", 0))
   267  	if err != nil || exists {
   268  		t.Fatalf("didn't expect to get any object")
   269  	}
   270  }
   271  
   272  // TestHeap_GetByKey tests Heap.GetByKey and is very similar to TestHeap_Get.
   273  func TestHeap_GetByKey(t *testing.T) {
   274  	h := NewHeap(testHeapObjectKeyFunc, compareInts)
   275  	h.Add(mkHeapObj("foo", 10))
   276  	h.Add(mkHeapObj("bar", 1))
   277  	h.Add(mkHeapObj("bal", 31))
   278  	h.Add(mkHeapObj("baz", 11))
   279  
   280  	obj, exists, err := h.GetByKey("baz")
   281  	if err != nil || exists == false || obj.(testHeapObject).val != 11 {
   282  		t.Fatalf("unexpected error in getting element")
   283  	}
   284  	// Get non-existing object.
   285  	_, exists, err = h.GetByKey("non-existing")
   286  	if err != nil || exists {
   287  		t.Fatalf("didn't expect to get any object")
   288  	}
   289  }
   290  
   291  // TestHeap_Close tests Heap.Close and Heap.IsClosed functions.
   292  func TestHeap_Close(t *testing.T) {
   293  	h := NewHeap(testHeapObjectKeyFunc, compareInts)
   294  	h.Add(mkHeapObj("foo", 10))
   295  	h.Add(mkHeapObj("bar", 1))
   296  
   297  	if h.IsClosed() {
   298  		t.Fatalf("didn't expect heap to be closed")
   299  	}
   300  	h.Close()
   301  	if !h.IsClosed() {
   302  		t.Fatalf("expect heap to be closed")
   303  	}
   304  }
   305  
   306  // TestHeap_List tests Heap.List function.
   307  func TestHeap_List(t *testing.T) {
   308  	h := NewHeap(testHeapObjectKeyFunc, compareInts)
   309  	list := h.List()
   310  	if len(list) != 0 {
   311  		t.Errorf("expected an empty list")
   312  	}
   313  
   314  	items := map[string]int{
   315  		"foo": 10,
   316  		"bar": 1,
   317  		"bal": 30,
   318  		"baz": 11,
   319  		"faz": 30,
   320  	}
   321  	for k, v := range items {
   322  		h.Add(mkHeapObj(k, v))
   323  	}
   324  	list = h.List()
   325  	if len(list) != len(items) {
   326  		t.Errorf("expected %d items, got %d", len(items), len(list))
   327  	}
   328  	for _, obj := range list {
   329  		heapObj := obj.(testHeapObject)
   330  		v, ok := items[heapObj.name]
   331  		if !ok || v != heapObj.val {
   332  			t.Errorf("unexpected item in the list: %v", heapObj)
   333  		}
   334  	}
   335  }
   336  
   337  // TestHeap_ListKeys tests Heap.ListKeys function. Scenario is the same as
   338  // TestHeap_list.
   339  func TestHeap_ListKeys(t *testing.T) {
   340  	h := NewHeap(testHeapObjectKeyFunc, compareInts)
   341  	list := h.ListKeys()
   342  	if len(list) != 0 {
   343  		t.Errorf("expected an empty list")
   344  	}
   345  
   346  	items := map[string]int{
   347  		"foo": 10,
   348  		"bar": 1,
   349  		"bal": 30,
   350  		"baz": 11,
   351  		"faz": 30,
   352  	}
   353  	for k, v := range items {
   354  		h.Add(mkHeapObj(k, v))
   355  	}
   356  	list = h.ListKeys()
   357  	if len(list) != len(items) {
   358  		t.Errorf("expected %d items, got %d", len(items), len(list))
   359  	}
   360  	for _, key := range list {
   361  		_, ok := items[key]
   362  		if !ok {
   363  			t.Errorf("unexpected item in the list: %v", key)
   364  		}
   365  	}
   366  }
   367  
   368  // TestHeapAddAfterClose tests that heap returns an error if anything is added
   369  // after it is closed.
   370  func TestHeapAddAfterClose(t *testing.T) {
   371  	h := NewHeap(testHeapObjectKeyFunc, compareInts)
   372  	h.Close()
   373  	if err := h.Add(mkHeapObj("test", 1)); err == nil || err.Error() != closedMsg {
   374  		t.Errorf("expected heap closed error")
   375  	}
   376  	if err := h.AddIfNotPresent(mkHeapObj("test", 1)); err == nil || err.Error() != closedMsg {
   377  		t.Errorf("expected heap closed error")
   378  	}
   379  	if err := h.BulkAdd([]interface{}{mkHeapObj("test", 1)}); err == nil || err.Error() != closedMsg {
   380  		t.Errorf("expected heap closed error")
   381  	}
   382  }