summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
Diffstat (limited to 'internal')
-rw-r--r--internal/flamegraph/livetrie_test.go52
-rw-r--r--internal/flamegraph/trie_test.go56
-rw-r--r--internal/tui/flamegraph/renderer_test.go74
3 files changed, 182 insertions, 0 deletions
diff --git a/internal/flamegraph/livetrie_test.go b/internal/flamegraph/livetrie_test.go
index 32e2b40..a12f5d9 100644
--- a/internal/flamegraph/livetrie_test.go
+++ b/internal/flamegraph/livetrie_test.go
@@ -359,6 +359,27 @@ func TestLiveTrieSetHeightFieldRejectsInvalidValue(t *testing.T) {
}
}
+func TestLiveTrieSetHeightFieldNoopKeepsBaseline(t *testing.T) {
+ lt := NewLiveTrie([]string{"comm"}, "count", "bytes")
+ lt.Ingest(newTestPair("svc", 42, 1001, "/tmp/a", 10, 1, 64))
+ beforeVersion := lt.Version()
+
+ if err := lt.SetHeightField("bytes"); err != nil {
+ t.Fatalf("set height field noop: %v", err)
+ }
+ if got := lt.Version(); got != beforeVersion {
+ t.Fatalf("version changed on noop height field set: got %d want %d", got, beforeVersion)
+ }
+
+ snap := decodeLiveSnapshot(t, lt)
+ if got, want := snap.Total, uint64(1); got != want {
+ t.Fatalf("total after noop height switch = %d, want %d", got, want)
+ }
+ if got, want := snap.HeightTotal, uint64(64); got != want {
+ t.Fatalf("height total after noop height switch = %d, want %d", got, want)
+ }
+}
+
func TestLiveTrieHeightFieldEmptyDisablesHeightTotals(t *testing.T) {
lt := NewLiveTrie([]string{"comm"}, "count", "")
lt.Ingest(newTestPair("svc", 42, 1001, "/tmp/a", 10, 1, 64))
@@ -372,6 +393,37 @@ func TestLiveTrieHeightFieldEmptyDisablesHeightTotals(t *testing.T) {
}
}
+func TestLiveTrieSnapshotHeightTotalsAccumulateAcrossBranches(t *testing.T) {
+ lt := NewLiveTrie([]string{"comm", "pid"}, "count", "bytes")
+ lt.Ingest(newTestPair("svc", 101, 1001, "/tmp/a", 10, 1, 100))
+ lt.Ingest(newTestPair("svc", 102, 1002, "/tmp/b", 10, 1, 40))
+ lt.Ingest(newTestPair("db", 201, 1003, "/tmp/c", 10, 1, 10))
+
+ snap := decodeLiveSnapshot(t, lt)
+ if got, want := snap.Total, uint64(3); got != want {
+ t.Fatalf("root total = %d, want %d", got, want)
+ }
+ if got, want := snap.HeightTotal, uint64(150); got != want {
+ t.Fatalf("root height total = %d, want %d", got, want)
+ }
+
+ svc := findSnapshotPath(t, &snap, "svc")
+ if got, want := svc.Total, uint64(2); got != want {
+ t.Fatalf("svc total = %d, want %d", got, want)
+ }
+ if got, want := svc.HeightTotal, uint64(140); got != want {
+ t.Fatalf("svc height total = %d, want %d", got, want)
+ }
+
+ db := findSnapshotPath(t, &snap, "db")
+ if got, want := db.Total, uint64(1); got != want {
+ t.Fatalf("db total = %d, want %d", got, want)
+ }
+ if got, want := db.HeightTotal, uint64(10); got != want {
+ t.Fatalf("db height total = %d, want %d", got, want)
+ }
+}
+
func TestLiveTrieSnapshotJSONCaching(t *testing.T) {
lt := NewLiveTrie([]string{"comm"}, "count", "count")
lt.Ingest(newTestPair("svc", 42, 1001, "/tmp/a", 1, 1, 1))
diff --git a/internal/flamegraph/trie_test.go b/internal/flamegraph/trie_test.go
index 47ef770..9f56b15 100644
--- a/internal/flamegraph/trie_test.go
+++ b/internal/flamegraph/trie_test.go
@@ -167,3 +167,59 @@ func TestInsertTriePathStoresValueAndHeightValueIndependently(t *testing.T) {
t.Fatalf("expected leaf heightValue=5, got %d", b.heightValue)
}
}
+
+func TestTrieComputeTotalsPropagatesDualMetricsAcrossBranches(t *testing.T) {
+ tr := newTrie()
+ insertTriePath(tr.root, []string{"a", "x"}, 5, 100)
+ insertTriePath(tr.root, []string{"a", "y"}, 3, 40)
+ insertTriePath(tr.root, []string{"b", "z"}, 7, 1)
+ tr.computeTotals()
+
+ a := findChild(tr.root, "a")
+ if a == nil {
+ t.Fatal("expected node a")
+ }
+ b := findChild(tr.root, "b")
+ if b == nil {
+ t.Fatal("expected node b")
+ }
+ x := findChild(a, "x")
+ if x == nil {
+ t.Fatal("expected node x")
+ }
+ y := findChild(a, "y")
+ if y == nil {
+ t.Fatal("expected node y")
+ }
+
+ if got, want := x.total, uint64(5); got != want {
+ t.Fatalf("x total = %d, want %d", got, want)
+ }
+ if got, want := x.heightTotal, uint64(100); got != want {
+ t.Fatalf("x heightTotal = %d, want %d", got, want)
+ }
+ if got, want := y.total, uint64(3); got != want {
+ t.Fatalf("y total = %d, want %d", got, want)
+ }
+ if got, want := y.heightTotal, uint64(40); got != want {
+ t.Fatalf("y heightTotal = %d, want %d", got, want)
+ }
+ if got, want := a.total, uint64(8); got != want {
+ t.Fatalf("a total = %d, want %d", got, want)
+ }
+ if got, want := a.heightTotal, uint64(140); got != want {
+ t.Fatalf("a heightTotal = %d, want %d", got, want)
+ }
+ if got, want := b.total, uint64(7); got != want {
+ t.Fatalf("b total = %d, want %d", got, want)
+ }
+ if got, want := b.heightTotal, uint64(1); got != want {
+ t.Fatalf("b heightTotal = %d, want %d", got, want)
+ }
+ if got, want := tr.root.total, uint64(15); got != want {
+ t.Fatalf("root total = %d, want %d", got, want)
+ }
+ if got, want := tr.root.heightTotal, uint64(141); got != want {
+ t.Fatalf("root heightTotal = %d, want %d", got, want)
+ }
+}
diff --git a/internal/tui/flamegraph/renderer_test.go b/internal/tui/flamegraph/renderer_test.go
index f34d23d..9361686 100644
--- a/internal/tui/flamegraph/renderer_test.go
+++ b/internal/tui/flamegraph/renderer_test.go
@@ -449,6 +449,80 @@ func TestRenderLeafRowBandFiltersFramesByBand(t *testing.T) {
}
}
+func TestBuildRenderRowsHeightMetricUsesLeafBandsAndViewportRows(t *testing.T) {
+ frames := []tuiFrame{
+ {Name: "root", Row: 0, Col: 0, Width: 12, Path: "root", Fill: color.RGBA{R: 70, G: 70, B: 70, A: 255}},
+ {Name: "A", Row: 1, Col: 0, Width: 6, Path: "root" + pathSeparator + "A", HeightTotal: 100, Fill: color.RGBA{R: 150, G: 80, B: 80, A: 255}},
+ {Name: "B", Row: 1, Col: 6, Width: 6, Path: "root" + pathSeparator + "B", HeightTotal: 50, Fill: color.RGBA{R: 80, G: 120, B: 180, A: 255}},
+ }
+
+ rows := buildRenderRows(
+ frames,
+ 12, // width
+ 0, // rowOffset
+ 1, // maxRow
+ 1, // barHeight
+ 4, // leafBarHeight
+ 5, // availableRows
+ "root",
+ map[int]bool{0: true, 1: true, 2: true},
+ nil,
+ 0,
+ true, // heightMetricActive
+ true, // isDark
+ false, // searchActive
+ false, // filterActive
+ )
+
+ if got, want := len(rows), 5; got != want {
+ t.Fatalf("row count = %d, want %d", got, want)
+ }
+ // In height mode, the last leaf band (h=0) is where labels are drawn.
+ if got := rows[3]; !strings.Contains(got, "A") || !strings.Contains(got, "B") {
+ t.Fatalf("expected bottom leaf band to show both labels, got %q", got)
+ }
+ // Root row is rendered after all leaf bands.
+ if got := rows[4]; !strings.Contains(got, "root") {
+ t.Fatalf("expected final row to be root row, got %q", got)
+ }
+}
+
+func TestBuildTerminalLayoutHeightTotalUsesSnapshotAggregation(t *testing.T) {
+ snapshot := &snapshotNode{
+ Name: "root",
+ Total: 3,
+ Children: []*snapshotNode{
+ {
+ Name: "A",
+ Total: 2,
+ HeightTotal: 90,
+ },
+ {
+ Name: "B",
+ Total: 1,
+ Children: []*snapshotNode{
+ {Name: "B1", Total: 1, HeightTotal: 30},
+ },
+ },
+ },
+ }
+
+ frames := BuildTerminalLayout(snapshot, 80, 8)
+ root := mustFindFrame(t, frames, "root")
+ a := mustFindFrame(t, frames, "root"+pathSeparator+"A")
+ b := mustFindFrame(t, frames, "root"+pathSeparator+"B")
+
+ if got, want := root.HeightTotal, uint64(120); got != want {
+ t.Fatalf("root HeightTotal = %d, want %d", got, want)
+ }
+ if got, want := a.HeightTotal, uint64(90); got != want {
+ t.Fatalf("A HeightTotal = %d, want %d", got, want)
+ }
+ if got, want := b.HeightTotal, uint64(30); got != want {
+ t.Fatalf("B HeightTotal = %d, want %d", got, want)
+ }
+}
+
func mustFindFrame(t *testing.T, frames []tuiFrame, path string) tuiFrame {
t.Helper()
for _, frame := range frames {