diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-06 13:36:51 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-06 13:36:51 +0200 |
| commit | ef12ce837176bd21deb455eb50a6c839af02b510 (patch) | |
| tree | c262ceeda0b419236a4b0b1826df8eb5e418b852 /internal/ior_mode_test.go | |
| parent | 10c5d48413afaef88626419d8c4bf9fbf6f1c902 (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.go | 259 |
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 }() |
