github.com/hashicorp/vault/sdk@v0.13.0/queue/priority_queue_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package queue 5 6 import ( 7 "container/heap" 8 "fmt" 9 "testing" 10 "time" 11 ) 12 13 // Ensure we satisfy the heap.Interface 14 var _ heap.Interface = &queue{} 15 16 // some tests rely on the ordering of items from this method 17 func testCases() (tc []*Item) { 18 // create a slice of items with priority / times offest by these seconds 19 for i, m := range []time.Duration{ 20 5, 21 183600, // 51 hours 22 15, // 15 seconds 23 45, // 45 seconds 24 900, // 15 minutes 25 300, // 5 minutes 26 7200, // 2 hours 27 183600, // 51 hours 28 7201, // 2 hours, 1 second 29 115200, // 32 hours 30 1209600, // 2 weeks 31 } { 32 n := time.Now() 33 ft := n.Add(time.Second * m) 34 tc = append(tc, &Item{ 35 Key: fmt.Sprintf("item-%d", i), 36 Value: 1, 37 Priority: ft.Unix(), 38 }) 39 } 40 return 41 } 42 43 func TestPriorityQueue_New(t *testing.T) { 44 pq := New() 45 46 if len(pq.data) != len(pq.dataMap) || len(pq.data) != 0 { 47 t.Fatalf("error in queue/map size, expected data and map to be initialized, got (%d) and (%d)", len(pq.data), len(pq.dataMap)) 48 } 49 50 if pq.Len() != 0 { 51 t.Fatalf("expected new queue to have zero size, got (%d)", pq.Len()) 52 } 53 } 54 55 func TestPriorityQueue_Push(t *testing.T) { 56 pq := New() 57 58 // don't allow nil pushing 59 if err := pq.Push(nil); err == nil { 60 t.Fatal("Expected error on pushing nil") 61 } 62 63 tc := testCases() 64 tcl := len(tc) 65 for _, i := range tc { 66 if err := pq.Push(i); err != nil { 67 t.Fatal(err) 68 } 69 } 70 71 if pq.Len() != tcl { 72 t.Fatalf("error adding items, expected (%d) items, got (%d)", tcl, pq.Len()) 73 } 74 75 testValidateInternalData(t, pq, len(tc), false) 76 77 item, err := pq.Pop() 78 if err != nil { 79 t.Fatalf("error popping item: %s", err) 80 } 81 if tc[0].Priority != item.Priority { 82 t.Fatalf("expected tc[0] and popped item to match, got (%q) and (%q)", tc[0], item.Priority) 83 } 84 if tc[0].Key != item.Key { 85 t.Fatalf("expected tc[0] and popped item to match, got (%q) and (%q)", tc[0], item.Priority) 86 } 87 88 testValidateInternalData(t, pq, len(tc)-1, false) 89 90 // push item with no key 91 dErr := pq.Push(tc[1]) 92 if dErr != ErrDuplicateItem { 93 t.Fatal(err) 94 } 95 // push item with no key 96 tc[2].Key = "" 97 kErr := pq.Push(tc[2]) 98 if kErr != nil && kErr.Error() != "error adding item: Item Key is required" { 99 t.Fatal(kErr) 100 } 101 102 testValidateInternalData(t, pq, len(tc)-1, true) 103 104 // check nil,nil error for not found 105 i, err := pq.PopByKey("empty") 106 if err != nil && i != nil { 107 t.Fatalf("expected nil error for PopByKey of non-existing key, got: %s", err) 108 } 109 } 110 111 func TestPriorityQueue_Pop(t *testing.T) { 112 pq := New() 113 114 tc := testCases() 115 for _, i := range tc { 116 if err := pq.Push(i); err != nil { 117 t.Fatal(err) 118 } 119 } 120 121 topItem, err := pq.Pop() 122 if err != nil { 123 t.Fatalf("error calling pop: %s", err) 124 } 125 if tc[0].Priority != topItem.Priority { 126 t.Fatalf("expected tc[0] and popped item to match, got (%q) and (%q)", tc[0], topItem.Priority) 127 } 128 if tc[0].Key != topItem.Key { 129 t.Fatalf("expected tc[0] and popped item to match, got (%q) and (%q)", tc[0], topItem.Priority) 130 } 131 132 var items []*Item 133 items = append(items, topItem) 134 // pop the remaining items, compare size of input and output 135 i, _ := pq.Pop() 136 for ; i != nil; i, _ = pq.Pop() { 137 items = append(items, i) 138 } 139 if len(items) != len(tc) { 140 t.Fatalf("expected popped item count to match test cases, got (%d)", len(items)) 141 } 142 } 143 144 func TestPriorityQueue_PopByKey(t *testing.T) { 145 pq := New() 146 147 tc := testCases() 148 for _, i := range tc { 149 if err := pq.Push(i); err != nil { 150 t.Fatal(err) 151 } 152 } 153 154 // grab the top priority item, to capture it's value for checking later 155 item, _ := pq.Pop() 156 oldPriority := item.Priority 157 oldKey := item.Key 158 159 // push the item back on, so it gets removed with PopByKey and we verify 160 // the top item has changed later 161 err := pq.Push(item) 162 if err != nil { 163 t.Fatalf("error re-pushing top item: %s", err) 164 } 165 166 popKeys := []int{2, 4, 7, 1, 0} 167 for _, i := range popKeys { 168 item, err := pq.PopByKey(fmt.Sprintf("item-%d", i)) 169 if err != nil { 170 t.Fatalf("failed to pop item-%d, \n\terr: %s\n\titem: %#v", i, err, item) 171 } 172 } 173 174 testValidateInternalData(t, pq, len(tc)-len(popKeys), false) 175 176 // grab the top priority item again, to compare with the top item priority 177 // from above 178 item, _ = pq.Pop() 179 newPriority := item.Priority 180 newKey := item.Key 181 182 if oldPriority == newPriority || oldKey == newKey { 183 t.Fatalf("expected old/new key and priority to differ, got (%s/%s) and (%d/%d)", oldKey, newKey, oldPriority, newPriority) 184 } 185 186 testValidateInternalData(t, pq, len(tc)-len(popKeys)-1, true) 187 } 188 189 // testValidateInternalData checks the internal data structure of the PriorityQueue 190 // and verifies that items are in-sync. Use drain only at the end of a test, 191 // because it will mutate the input queue 192 func testValidateInternalData(t *testing.T, pq *PriorityQueue, expectedSize int, drain bool) { 193 actualSize := pq.Len() 194 if actualSize != expectedSize { 195 t.Fatalf("expected new queue size to be (%d), got (%d)", expectedSize, actualSize) 196 } 197 198 if len(pq.data) != len(pq.dataMap) || len(pq.data) != expectedSize { 199 t.Fatalf("error in queue/map size, expected data and map to be (%d), got (%d) and (%d)", expectedSize, len(pq.data), len(pq.dataMap)) 200 } 201 202 if drain && pq.Len() > 0 { 203 // pop all the items, verify lengths 204 i, _ := pq.Pop() 205 for ; i != nil; i, _ = pq.Pop() { 206 expectedSize-- 207 if len(pq.data) != len(pq.dataMap) || len(pq.data) != expectedSize { 208 t.Fatalf("error in queue/map size, expected data and map to be (%d), got (%d) and (%d)", expectedSize, len(pq.data), len(pq.dataMap)) 209 } 210 } 211 } 212 }