sigs.k8s.io/kueue@v0.6.2/pkg/util/heap/heap_test.go (about)

     1  /*
     2  Copyright 2022 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  // This file was copied from client-go/tools/cache/heap.go and modified
    18  // for our non thread-safe heap
    19  
    20  package heap
    21  
    22  import (
    23  	"testing"
    24  )
    25  
    26  func testHeapObjectKeyFunc(obj interface{}) string {
    27  	return obj.(testHeapObject).name
    28  }
    29  
    30  type testHeapObject struct {
    31  	name string
    32  	val  interface{}
    33  }
    34  
    35  func mkHeapObj(name string, val interface{}) testHeapObject {
    36  	return testHeapObject{name: name, val: val}
    37  }
    38  
    39  func compareInts(val1 interface{}, val2 interface{}) bool {
    40  	first := val1.(testHeapObject).val.(int)
    41  	second := val2.(testHeapObject).val.(int)
    42  	return first < second
    43  }
    44  
    45  // TestHeapBasic tests Heap invariant
    46  func TestHeapBasic(t *testing.T) {
    47  	h := New(testHeapObjectKeyFunc, compareInts)
    48  	const amount = 500
    49  	var i int
    50  
    51  	for i = amount; i > 0; i-- {
    52  		h.PushOrUpdate(mkHeapObj(string([]rune{'a', rune(i)}), i))
    53  	}
    54  
    55  	// Make sure that the numbers are popped in ascending order.
    56  	prevNum := 0
    57  	for i := 0; i < amount; i++ {
    58  		obj := h.Pop()
    59  		num := obj.(testHeapObject).val.(int)
    60  		// All the items must be sorted.
    61  		if prevNum > num {
    62  			t.Errorf("got %v out of order, last was %v", obj, prevNum)
    63  		}
    64  		prevNum = num
    65  	}
    66  }
    67  
    68  // Tests Heap.PushOrUpdate and ensures that heap invariant is preserved after adding items.
    69  func TestHeap_Add(t *testing.T) {
    70  	h := New(testHeapObjectKeyFunc, compareInts)
    71  	h.PushOrUpdate(mkHeapObj("foo", 10))
    72  	h.PushOrUpdate(mkHeapObj("bar", 1))
    73  	h.PushOrUpdate(mkHeapObj("baz", 11))
    74  	h.PushOrUpdate(mkHeapObj("zab", 30))
    75  	h.PushOrUpdate(mkHeapObj("foo", 13)) // This updates "foo".
    76  
    77  	item := h.Pop()
    78  	if e, a := 1, item.(testHeapObject).val; a != e {
    79  		t.Fatalf("expected %d, got %d", e, a)
    80  	}
    81  	item = h.Pop()
    82  	if e, a := 11, item.(testHeapObject).val; a != e {
    83  		t.Fatalf("expected %d, got %d", e, a)
    84  	}
    85  	h.Delete("baz")                      // Nothing is deleted.
    86  	h.PushOrUpdate(mkHeapObj("foo", 14)) // foo is updated.
    87  	item = h.Pop()
    88  	if e, a := 14, item.(testHeapObject).val; a != e {
    89  		t.Fatalf("expected %d, got %d", e, a)
    90  	}
    91  	item = h.Pop()
    92  	if e, a := 30, item.(testHeapObject).val; a != e {
    93  		t.Fatalf("expected %d, got %d", e, a)
    94  	}
    95  }
    96  
    97  // TestHeap_PushIfNotPresent tests Heap.PushIfNotPresent and ensures that heap
    98  // invariant is preserved after adding items.
    99  func TestHeap_PushIfNotPresent(t *testing.T) {
   100  	h := New(testHeapObjectKeyFunc, compareInts)
   101  	_ = h.PushIfNotPresent(mkHeapObj("foo", 10))
   102  	_ = h.PushIfNotPresent(mkHeapObj("bar", 1))
   103  	_ = h.PushIfNotPresent(mkHeapObj("baz", 11))
   104  	_ = h.PushIfNotPresent(mkHeapObj("zab", 30))
   105  	_ = h.PushIfNotPresent(mkHeapObj("foo", 13)) // This is not added.
   106  
   107  	if len := len(h.data.items); len != 4 {
   108  		t.Errorf("unexpected number of items: %d", len)
   109  	}
   110  	if val := h.data.items["foo"].obj.(testHeapObject).val; val != 10 {
   111  		t.Errorf("unexpected value: %d", val)
   112  	}
   113  	item := h.Pop()
   114  	if e, a := 1, item.(testHeapObject).val; a != e {
   115  		t.Fatalf("expected %d, got %d", e, a)
   116  	}
   117  	item = h.Pop()
   118  	if e, a := 10, item.(testHeapObject).val; a != e {
   119  		t.Fatalf("expected %d, got %d", e, a)
   120  	}
   121  	// bar is already popped. Let's add another one.
   122  	_ = h.PushIfNotPresent(mkHeapObj("bar", 14))
   123  	item = h.Pop()
   124  	if e, a := 11, item.(testHeapObject).val; a != e {
   125  		t.Fatalf("expected %d, got %d", e, a)
   126  	}
   127  	item = h.Pop()
   128  	if e, a := 14, item.(testHeapObject).val; a != e {
   129  		t.Fatalf("expected %d, got %d", e, a)
   130  	}
   131  }
   132  
   133  // TestHeap_PushOrUpdate tests Heap.PushOrUpdate and ensures that heap
   134  // invariant is preserved after adding/updating items.
   135  func TestHeap_PushOrUpdate(t *testing.T) {
   136  	h := New(testHeapObjectKeyFunc, compareInts)
   137  	h.PushOrUpdate(mkHeapObj("foo", 100))
   138  	h.PushOrUpdate(mkHeapObj("baz", 20))
   139  	h.PushOrUpdate(mkHeapObj("foo", 1)) // This behaves as update.
   140  	h.PushOrUpdate(mkHeapObj("zab", 8)) // This behaves as add.
   141  
   142  	if len := len(h.data.items); len != 3 {
   143  		t.Errorf("unexpected number of items: %d", len)
   144  	}
   145  	item := h.Pop()
   146  	if e, a := 1, item.(testHeapObject).val; a != e {
   147  		t.Fatalf("expected %d, got %d", e, a)
   148  	}
   149  	item = h.Pop()
   150  	if e, a := 8, item.(testHeapObject).val; a != e {
   151  		t.Fatalf("expected %d, got %d", e, a)
   152  	}
   153  }
   154  
   155  // TestHeap_Delete tests Heap.Delete and ensures that heap invariant is
   156  // preserved after deleting items.
   157  func TestHeap_Delete(t *testing.T) {
   158  	h := New(testHeapObjectKeyFunc, compareInts)
   159  	h.PushOrUpdate(mkHeapObj("foo", 10))
   160  	h.PushOrUpdate(mkHeapObj("bar", 1))
   161  	h.PushOrUpdate(mkHeapObj("bal", 31))
   162  	h.PushOrUpdate(mkHeapObj("baz", 11))
   163  
   164  	// Delete head. Delete should work with "key" and doesn't care about the value.
   165  	h.Delete("bar")
   166  	item := h.Pop()
   167  	if e, a := 10, item.(testHeapObject).val; a != e {
   168  		t.Fatalf("expected %d, got %d", e, a)
   169  	}
   170  	h.PushOrUpdate(mkHeapObj("zab", 30))
   171  	h.PushOrUpdate(mkHeapObj("faz", 30))
   172  	len := h.data.Len()
   173  	// Delete non-existing item.
   174  	if h.Delete("non-existent"); len != h.data.Len() {
   175  		t.Fatalf("Didn't expect any item removal")
   176  	}
   177  	// Delete tail.
   178  	h.Delete("bal")
   179  	// Delete one of the items with value 30.
   180  	h.Delete("zab")
   181  	item = h.Pop()
   182  	if e, a := 11, item.(testHeapObject).val; a != e {
   183  		t.Fatalf("expected %d, got %d", e, a)
   184  	}
   185  	item = h.Pop()
   186  	if e, a := 30, item.(testHeapObject).val; a != e {
   187  		t.Fatalf("expected %d, got %d", e, a)
   188  	}
   189  	if h.data.Len() != 0 {
   190  		t.Fatalf("expected an empty heap.")
   191  	}
   192  }
   193  
   194  // TestHeap_Update tests Heap.PushOrUpdate and ensures that heap invariant is
   195  // preserved after adding items.
   196  func TestHeap_Update(t *testing.T) {
   197  	h := New(testHeapObjectKeyFunc, compareInts)
   198  	h.PushOrUpdate(mkHeapObj("foo", 10))
   199  	h.PushOrUpdate(mkHeapObj("bar", 1))
   200  	h.PushOrUpdate(mkHeapObj("bal", 31))
   201  	h.PushOrUpdate(mkHeapObj("baz", 11))
   202  
   203  	// Update an item to a value that should push it to the head.
   204  	h.PushOrUpdate(mkHeapObj("baz", 0))
   205  	if h.data.keys[0] != "baz" || h.data.items["baz"].index != 0 {
   206  		t.Fatalf("expected baz to be at the head")
   207  	}
   208  	item := h.Pop()
   209  	if e, a := 0, item.(testHeapObject).val; a != e {
   210  		t.Fatalf("expected %d, got %d", e, a)
   211  	}
   212  	// Update bar to push it farther back in the queue.
   213  	h.PushOrUpdate(mkHeapObj("bar", 100))
   214  	if h.data.keys[0] != "foo" || h.data.items["foo"].index != 0 {
   215  		t.Fatalf("expected foo to be at the head")
   216  	}
   217  }
   218  
   219  // TestHeap_Get tests Heap.Get.
   220  func TestHeap_Get(t *testing.T) {
   221  	h := New(testHeapObjectKeyFunc, compareInts)
   222  	h.PushOrUpdate(mkHeapObj("foo", 10))
   223  	h.PushOrUpdate(mkHeapObj("bar", 1))
   224  	h.PushOrUpdate(mkHeapObj("bal", 31))
   225  	h.PushOrUpdate(mkHeapObj("baz", 11))
   226  
   227  	// Get works with the key.
   228  	obj := h.Get(mkHeapObj("baz", 0))
   229  	if obj == nil || obj.(testHeapObject).val != 11 {
   230  		t.Fatalf("unexpected error in getting element")
   231  	}
   232  	// Get non-existing object.
   233  	if obj = h.Get(mkHeapObj("non-existing", 0)); obj != nil {
   234  		t.Fatalf("didn't expect to get any object")
   235  	}
   236  }
   237  
   238  // TestHeap_GetByKey tests Heap.GetByKey and is very similar to TestHeap_Get.
   239  func TestHeap_GetByKey(t *testing.T) {
   240  	h := New(testHeapObjectKeyFunc, compareInts)
   241  	h.PushOrUpdate(mkHeapObj("foo", 10))
   242  	h.PushOrUpdate(mkHeapObj("bar", 1))
   243  	h.PushOrUpdate(mkHeapObj("bal", 31))
   244  	h.PushOrUpdate(mkHeapObj("baz", 11))
   245  
   246  	obj := h.GetByKey("baz")
   247  	if obj == nil || obj.(testHeapObject).val != 11 {
   248  		t.Fatalf("unexpected error in getting element")
   249  	}
   250  	// Get non-existing object.
   251  	if obj = h.GetByKey("non-existing"); obj != nil {
   252  		t.Fatalf("didn't expect to get any object")
   253  	}
   254  }
   255  
   256  // TestHeap_List tests Heap.List function.
   257  func TestHeap_List(t *testing.T) {
   258  	h := New(testHeapObjectKeyFunc, compareInts)
   259  	list := h.List()
   260  	if len(list) != 0 {
   261  		t.Errorf("expected an empty list")
   262  	}
   263  
   264  	items := map[string]int{
   265  		"foo": 10,
   266  		"bar": 1,
   267  		"bal": 30,
   268  		"baz": 11,
   269  		"faz": 30,
   270  	}
   271  	for k, v := range items {
   272  		h.PushOrUpdate(mkHeapObj(k, v))
   273  	}
   274  	list = h.List()
   275  	if len(list) != len(items) {
   276  		t.Errorf("expected %d items, got %d", len(items), len(list))
   277  	}
   278  	for _, obj := range list {
   279  		heapObj := obj.(testHeapObject)
   280  		v, ok := items[heapObj.name]
   281  		if !ok || v != heapObj.val {
   282  			t.Errorf("unexpected item in the list: %v", heapObj)
   283  		}
   284  	}
   285  }