| // 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() |
| } |
| } |