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  }