// Copyright 2023 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package trace import ( "fmt" "strings" "testing" "slices" ) func TestHeap(t *testing.T) { var heap []*batchCursor // Insert a bunch of values into the heap. checkHeap(t, heap) heap = heapInsert(heap, makeBatchCursor(5)) checkHeap(t, heap) for i := int64(-20); i < 20; i++ { heap = heapInsert(heap, makeBatchCursor(i)) checkHeap(t, heap) } // Update an element in the middle to be the new minimum. for i := range heap { if heap[i].ev.time == 5 { heap[i].ev.time = -21 heapUpdate(heap, i) break } } checkHeap(t, heap) if heap[0].ev.time != -21 { t.Fatalf("heap update failed, expected %d as heap min: %s", -21, heapDebugString(heap)) } // Update the minimum element to be smaller. There should be no change. heap[0].ev.time = -22 heapUpdate(heap, 0) checkHeap(t, heap) if heap[0].ev.time != -22 { t.Fatalf("heap update failed, expected %d as heap min: %s", -22, heapDebugString(heap)) } // Update the last element to be larger. There should be no change. heap[len(heap)-1].ev.time = 21 heapUpdate(heap, len(heap)-1) checkHeap(t, heap) if heap[len(heap)-1].ev.time != 21 { t.Fatalf("heap update failed, expected %d as heap min: %s", 21, heapDebugString(heap)) } // Update the last element to be smaller. heap[len(heap)-1].ev.time = 7 heapUpdate(heap, len(heap)-1) checkHeap(t, heap) if heap[len(heap)-1].ev.time == 21 { t.Fatalf("heap update failed, unexpected %d as heap min: %s", 21, heapDebugString(heap)) } // Remove an element in the middle. for i := range heap { if heap[i].ev.time == 5 { heap = heapRemove(heap, i) break } } checkHeap(t, heap) for i := range heap { if heap[i].ev.time == 5 { t.Fatalf("failed to remove heap elem with time %d: %s", 5, heapDebugString(heap)) } } // Remove tail. heap = heapRemove(heap, len(heap)-1) checkHeap(t, heap) // Remove from the head, and make sure the result is sorted. l := len(heap) var removed []*batchCursor for i := 0; i < l; i++ { removed = append(removed, heap[0]) heap = heapRemove(heap, 0) checkHeap(t, heap) } if !slices.IsSortedFunc(removed, (*batchCursor).compare) { t.Fatalf("heap elements not removed in sorted order, got: %s", heapDebugString(removed)) } } func makeBatchCursor(v int64) *batchCursor { return &batchCursor{ev: baseEvent{time: Time(v)}} } func heapDebugString(heap []*batchCursor) string { var sb strings.Builder fmt.Fprintf(&sb, "[") for i := range heap { if i != 0 { fmt.Fprintf(&sb, ", ") } fmt.Fprintf(&sb, "%d", heap[i].ev.time) } fmt.Fprintf(&sb, "]") return sb.String() } func checkHeap(t *testing.T, heap []*batchCursor) { t.Helper() for i := range heap { if i == 0 { continue } if heap[(i-1)/2].compare(heap[i]) > 0 { t.Errorf("heap invariant not maintained between index %d and parent %d: %s", i, i/2, heapDebugString(heap)) } } if t.Failed() { t.FailNow() } }