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