summaryrefslogtreecommitdiff
path: root/internal/ior_mode_test.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-06 13:36:51 +0200
committerPaul Buetow <paul@buetow.org>2026-03-06 13:36:51 +0200
commitef12ce837176bd21deb455eb50a6c839af02b510 (patch)
treec262ceeda0b419236a4b0b1826df8eb5e418b852 /internal/ior_mode_test.go
parent10c5d48413afaef88626419d8c4bf9fbf6f1c902 (diff)
Add live flamegraph test modes and dynamic synthetic live feed
Diffstat (limited to 'internal/ior_mode_test.go')
-rw-r--r--internal/ior_mode_test.go259
1 files changed, 259 insertions, 0 deletions
diff --git a/internal/ior_mode_test.go b/internal/ior_mode_test.go
index d509cf2..3ae876a 100644
--- a/internal/ior_mode_test.go
+++ b/internal/ior_mode_test.go
@@ -1,7 +1,9 @@
package internal
import (
+ "bytes"
"context"
+ "encoding/json"
"errors"
"testing"
"time"
@@ -40,6 +42,18 @@ func TestShouldRunTraceMode(t *testing.T) {
if !shouldRunTraceMode(withLive) {
t.Fatalf("expected live mode to use trace mode")
}
+
+ withTestFlames := base
+ withTestFlames.TestFlames = true
+ if shouldRunTraceMode(withTestFlames) {
+ t.Fatalf("expected --testflames to stay in TUI mode")
+ }
+
+ withTestLiveFlames := base
+ withTestLiveFlames.TestLiveFlames = true
+ if shouldRunTraceMode(withTestLiveFlames) {
+ t.Fatalf("expected --testliveflames to stay in TUI mode")
+ }
}
func TestShouldAutoStopByDuration(t *testing.T) {
@@ -76,9 +90,13 @@ func TestShouldAutoStopByDuration(t *testing.T) {
func TestDispatchRunUsesTraceModeWhenRequested(t *testing.T) {
origRunTrace := runTraceFn
origRunTUI := runTUIFn
+ origRunTUITestFlames := runTUITestFlamesFn
+ origRunTUITestLiveFlames := runTUITestLiveFlamesFn
defer func() {
runTraceFn = origRunTrace
runTUIFn = origRunTUI
+ runTUITestFlamesFn = origRunTUITestFlames
+ runTUITestLiveFlamesFn = origRunTUITestLiveFlames
}()
traceCalled := false
@@ -91,6 +109,14 @@ func TestDispatchRunUsesTraceModeWhenRequested(t *testing.T) {
tuiCalled = true
return nil
}
+ runTUITestFlamesFn = func(tui.TraceStarter) error {
+ t.Fatalf("runTUITestFlamesFn should not be called in trace mode")
+ return nil
+ }
+ runTUITestLiveFlamesFn = func(tui.TraceStarter) error {
+ t.Fatalf("runTUITestLiveFlamesFn should not be called in trace mode")
+ return nil
+ }
cfg := flags.Flags{PlainMode: true}
if err := dispatchRun(cfg); err != nil {
@@ -107,9 +133,13 @@ func TestDispatchRunUsesTraceModeWhenRequested(t *testing.T) {
func TestDispatchRunUsesTUIWhenOnlyPprofEnabled(t *testing.T) {
origRunTrace := runTraceFn
origRunTUI := runTUIFn
+ origRunTUITestFlames := runTUITestFlamesFn
+ origRunTUITestLiveFlames := runTUITestLiveFlamesFn
defer func() {
runTraceFn = origRunTrace
runTUIFn = origRunTUI
+ runTUITestFlamesFn = origRunTUITestFlames
+ runTUITestLiveFlamesFn = origRunTUITestLiveFlames
}()
traceCalled := false
@@ -122,6 +152,14 @@ func TestDispatchRunUsesTUIWhenOnlyPprofEnabled(t *testing.T) {
tuiCalled = true
return nil
}
+ runTUITestFlamesFn = func(tui.TraceStarter) error {
+ t.Fatalf("runTUITestFlamesFn should not be called for regular TUI mode")
+ return nil
+ }
+ runTUITestLiveFlamesFn = func(tui.TraceStarter) error {
+ t.Fatalf("runTUITestLiveFlamesFn should not be called for regular TUI mode")
+ return nil
+ }
cfg := flags.Flags{PprofEnable: true}
if err := dispatchRun(cfg); err != nil {
@@ -138,9 +176,13 @@ func TestDispatchRunUsesTUIWhenOnlyPprofEnabled(t *testing.T) {
func TestDispatchRunUsesTUIStarterWhenNotPlain(t *testing.T) {
origRunTraceWithContext := runTraceWithContextFn
origRunTUI := runTUIFn
+ origRunTUITestFlames := runTUITestFlamesFn
+ origRunTUITestLiveFlames := runTUITestLiveFlamesFn
defer func() {
runTraceWithContextFn = origRunTraceWithContext
runTUIFn = origRunTUI
+ runTUITestFlamesFn = origRunTUITestFlames
+ runTUITestLiveFlamesFn = origRunTUITestLiveFlames
}()
traceDone := make(chan struct{}, 1)
@@ -164,6 +206,14 @@ func TestDispatchRunUsesTUIStarterWhenNotPlain(t *testing.T) {
}
return nil
}
+ runTUITestFlamesFn = func(tui.TraceStarter) error {
+ t.Fatalf("runTUITestFlamesFn should not be called for normal starter path")
+ return nil
+ }
+ runTUITestLiveFlamesFn = func(tui.TraceStarter) error {
+ t.Fatalf("runTUITestLiveFlamesFn should not be called for normal starter path")
+ return nil
+ }
cfg := flags.Flags{}
if err := dispatchRun(cfg); err != nil {
@@ -183,9 +233,13 @@ func TestDispatchRunUsesTUIStarterWhenNotPlain(t *testing.T) {
func TestDispatchRunRejectsLiveAndFlamegraph(t *testing.T) {
origRunTrace := runTraceFn
origRunTUI := runTUIFn
+ origRunTUITestFlames := runTUITestFlamesFn
+ origRunTUITestLiveFlames := runTUITestLiveFlamesFn
defer func() {
runTraceFn = origRunTrace
runTUIFn = origRunTUI
+ runTUITestFlamesFn = origRunTUITestFlames
+ runTUITestLiveFlamesFn = origRunTUITestLiveFlames
}()
runTraceFn = func() error {
@@ -196,6 +250,14 @@ func TestDispatchRunRejectsLiveAndFlamegraph(t *testing.T) {
t.Fatalf("runTUIFn should not be called for invalid flag combos")
return nil
}
+ runTUITestFlamesFn = func(tui.TraceStarter) error {
+ t.Fatalf("runTUITestFlamesFn should not be called for invalid flag combos")
+ return nil
+ }
+ runTUITestLiveFlamesFn = func(tui.TraceStarter) error {
+ t.Fatalf("runTUITestLiveFlamesFn should not be called for invalid flag combos")
+ return nil
+ }
cfg := flags.Flags{LiveFlamegraph: true, FlamegraphEnable: true}
err := dispatchRun(cfg)
@@ -207,6 +269,106 @@ func TestDispatchRunRejectsLiveAndFlamegraph(t *testing.T) {
}
}
+func TestDispatchRunUsesTestFlamesModeWhenRequested(t *testing.T) {
+ origRunTrace := runTraceFn
+ origRunTUI := runTUIFn
+ origRunTUITestFlames := runTUITestFlamesFn
+ origRunTUITestLiveFlames := runTUITestLiveFlamesFn
+ defer func() {
+ runTraceFn = origRunTrace
+ runTUIFn = origRunTUI
+ runTUITestFlamesFn = origRunTUITestFlames
+ runTUITestLiveFlamesFn = origRunTUITestLiveFlames
+ }()
+
+ traceCalled := false
+ regularTUICalled := false
+ testFlamesCalled := false
+ runTraceFn = func() error {
+ traceCalled = true
+ return nil
+ }
+ runTUIFn = func(tui.TraceStarter) error {
+ regularTUICalled = true
+ return nil
+ }
+ runTUITestFlamesFn = func(starter tui.TraceStarter) error {
+ testFlamesCalled = true
+ if starter == nil {
+ t.Fatalf("expected non-nil starter for test flames mode")
+ }
+ return starter(context.Background())
+ }
+ runTUITestLiveFlamesFn = func(tui.TraceStarter) error {
+ t.Fatalf("runTUITestLiveFlamesFn should not be called for --testflames")
+ return nil
+ }
+
+ cfg := flags.Flags{TestFlames: true}
+ if err := dispatchRun(cfg); err != nil {
+ t.Fatalf("dispatchRun returned error: %v", err)
+ }
+ if traceCalled {
+ t.Fatalf("did not expect runTraceFn for test flames mode")
+ }
+ if regularTUICalled {
+ t.Fatalf("did not expect runTUIFn for test flames mode")
+ }
+ if !testFlamesCalled {
+ t.Fatalf("expected runTUITestFlamesFn to be called")
+ }
+}
+
+func TestDispatchRunUsesTestLiveFlamesModeWhenRequested(t *testing.T) {
+ origRunTrace := runTraceFn
+ origRunTUI := runTUIFn
+ origRunTUITestFlames := runTUITestFlamesFn
+ origRunTUITestLiveFlames := runTUITestLiveFlamesFn
+ defer func() {
+ runTraceFn = origRunTrace
+ runTUIFn = origRunTUI
+ runTUITestFlamesFn = origRunTUITestFlames
+ runTUITestLiveFlamesFn = origRunTUITestLiveFlames
+ }()
+
+ traceCalled := false
+ regularTUICalled := false
+ testLiveFlamesCalled := false
+ runTraceFn = func() error {
+ traceCalled = true
+ return nil
+ }
+ runTUIFn = func(tui.TraceStarter) error {
+ regularTUICalled = true
+ return nil
+ }
+ runTUITestFlamesFn = func(tui.TraceStarter) error {
+ t.Fatalf("runTUITestFlamesFn should not be called for --testliveflames")
+ return nil
+ }
+ runTUITestLiveFlamesFn = func(starter tui.TraceStarter) error {
+ testLiveFlamesCalled = true
+ if starter == nil {
+ t.Fatalf("expected non-nil starter for test live flames mode")
+ }
+ return starter(context.Background())
+ }
+
+ cfg := flags.Flags{TestLiveFlames: true}
+ if err := dispatchRun(cfg); err != nil {
+ t.Fatalf("dispatchRun returned error: %v", err)
+ }
+ if traceCalled {
+ t.Fatalf("did not expect runTraceFn for test live flames mode")
+ }
+ if regularTUICalled {
+ t.Fatalf("did not expect runTUIFn for test live flames mode")
+ }
+ if !testLiveFlamesCalled {
+ t.Fatalf("expected runTUITestLiveFlamesFn to be called")
+ }
+}
+
func TestValidateRunConfigRejectsIorWatchWithoutIor(t *testing.T) {
cfg := flags.Flags{IorWatchInterval: time.Second}
err := validateRunConfig(cfg)
@@ -229,6 +391,103 @@ func TestValidateRunConfigRejectsNegativeIorWatchInterval(t *testing.T) {
}
}
+func TestValidateRunConfigRejectsTestFlamesWithTraceFlags(t *testing.T) {
+ cfg := flags.Flags{TestFlames: true, PlainMode: true}
+ err := validateRunConfig(cfg)
+ if err == nil {
+ t.Fatalf("expected error for --testflames with trace-mode flags")
+ }
+ if err.Error() != "--testflames cannot be combined with -plain, -flamegraph, or -live" {
+ t.Fatalf("unexpected error: %v", err)
+ }
+}
+
+func TestValidateRunConfigRejectsTestLiveFlamesWithTraceFlags(t *testing.T) {
+ cfg := flags.Flags{TestLiveFlames: true, PlainMode: true}
+ err := validateRunConfig(cfg)
+ if err == nil {
+ t.Fatalf("expected error for --testliveflames with trace-mode flags")
+ }
+ if err.Error() != "--testliveflames cannot be combined with -plain, -flamegraph, or -live" {
+ t.Fatalf("unexpected error: %v", err)
+ }
+}
+
+func TestValidateRunConfigRejectsBothTestModes(t *testing.T) {
+ cfg := flags.Flags{TestFlames: true, TestLiveFlames: true}
+ err := validateRunConfig(cfg)
+ if err == nil {
+ t.Fatalf("expected error when both test flame modes are enabled")
+ }
+ if err.Error() != "--testflames and --testliveflames are mutually exclusive" {
+ t.Fatalf("unexpected error: %v", err)
+ }
+}
+
+func TestBuildTestFlamesRuntimeSeedsLiveTrie(t *testing.T) {
+ cfg := flags.NewFlags()
+ _, streamBuf, liveTrie := buildTestFlamesRuntime(cfg)
+ if streamBuf == nil {
+ t.Fatalf("expected stream buffer in test flames runtime")
+ }
+ if liveTrie == nil {
+ t.Fatalf("expected live trie in test flames runtime")
+ }
+ if liveTrie.Version() == 0 {
+ t.Fatalf("expected seeded live trie version to be non-zero")
+ }
+
+ payload, _ := liveTrie.SnapshotJSON()
+ var snap map[string]any
+ if err := json.Unmarshal(payload, &snap); err != nil {
+ t.Fatalf("decode snapshot: %v", err)
+ }
+ total, ok := snap["t"].(float64)
+ if !ok || total <= 0 {
+ t.Fatalf("expected seeded snapshot total > 0, got %v", snap["t"])
+ }
+}
+
+func TestBuildTestLiveFlamesRuntimeContinuouslyUpdatesLiveTrie(t *testing.T) {
+ cfg := flags.NewFlags()
+ cfg.LiveInterval = 15 * time.Millisecond
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ _, streamBuf, liveTrie := buildTestLiveFlamesRuntime(ctx, cfg)
+ if streamBuf == nil {
+ t.Fatalf("expected stream buffer in test live flames runtime")
+ }
+ if liveTrie == nil {
+ t.Fatalf("expected live trie in test live flames runtime")
+ }
+
+ initialVersion := liveTrie.Version()
+ if initialVersion == 0 {
+ t.Fatalf("expected seeded live trie version to be non-zero")
+ }
+ initialSnapshot, _ := liveTrie.SnapshotJSON()
+
+ sawUpdate := false
+ deadline := time.Now().Add(300 * time.Millisecond)
+ for time.Now().Before(deadline) {
+ if liveTrie.Version() <= initialVersion {
+ time.Sleep(10 * time.Millisecond)
+ continue
+ }
+ currentSnapshot, _ := liveTrie.SnapshotJSON()
+ if !bytes.Equal(initialSnapshot, currentSnapshot) {
+ sawUpdate = true
+ break
+ }
+ time.Sleep(10 * time.Millisecond)
+ }
+ if !sawUpdate {
+ t.Fatalf("expected test live flames snapshot shape to change over time (version > %d)", initialVersion)
+ }
+}
+
func TestRunTraceWithContextRequiresRoot(t *testing.T) {
origGetEUID := getEUID
defer func() { getEUID = origGetEUID }()