diff options
Diffstat (limited to 'internal/export/snapshot_csv_test.go')
| -rw-r--r-- | internal/export/snapshot_csv_test.go | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/internal/export/snapshot_csv_test.go b/internal/export/snapshot_csv_test.go new file mode 100644 index 0000000..77a0c3a --- /dev/null +++ b/internal/export/snapshot_csv_test.go @@ -0,0 +1,141 @@ +package export + +import ( + "encoding/csv" + "os" + "path/filepath" + "strings" + "testing" + + "ior/internal/statsengine" +) + +// TestSnapshotCSVNilSnapshot verifies that SnapshotCSV writes a valid CSV file +// with only the header and summary rows when the snapshot is nil. +func TestSnapshotCSVNilSnapshot(t *testing.T) { + // Run inside a temp dir so the timestamped CSV file is automatically + // cleaned up after the test. + dir := t.TempDir() + origDir, err := os.Getwd() + if err != nil { + t.Fatalf("getwd: %v", err) + } + if err := os.Chdir(dir); err != nil { + t.Fatalf("chdir: %v", err) + } + t.Cleanup(func() { _ = os.Chdir(origDir) }) + + name, err := SnapshotCSV(nil) + if err != nil { + t.Fatalf("SnapshotCSV(nil) returned error: %v", err) + } + if name == "" { + t.Fatal("SnapshotCSV returned empty filename") + } + if !strings.HasPrefix(filepath.Base(name), "ior-snapshot-") { + t.Fatalf("unexpected filename prefix: %q", name) + } + + data, err := os.ReadFile(filepath.Join(dir, name)) + if err != nil { + t.Fatalf("read csv file: %v", err) + } + + r := csv.NewReader(strings.NewReader(string(data))) + records, err := r.ReadAll() + if err != nil { + t.Fatalf("parse csv: %v", err) + } + + // Nil snapshot: expect header + 4 summary rows. + if len(records) < 5 { + t.Fatalf("expected at least 5 CSV rows, got %d", len(records)) + } + if records[0][0] != "section" { + t.Fatalf("expected header row first cell 'section', got %q", records[0][0]) + } + for _, row := range records[1:5] { + if row[0] != "summary" { + t.Fatalf("expected summary row, got section=%q", row[0]) + } + } +} + +// TestSnapshotCSVWithData verifies that SnapshotCSV emits syscall, file, and +// process rows when the snapshot contains non-empty data. +func TestSnapshotCSVWithData(t *testing.T) { + dir := t.TempDir() + origDir, err := os.Getwd() + if err != nil { + t.Fatalf("getwd: %v", err) + } + if err := os.Chdir(dir); err != nil { + t.Fatalf("chdir: %v", err) + } + t.Cleanup(func() { _ = os.Chdir(origDir) }) + + snap := statsengine.NewSnapshot( + nil, nil, nil, + []statsengine.SyscallSnapshot{ + {Name: "read", Count: 10, RatePerSec: 1.5, Bytes: 4096}, + }, + []statsengine.FileSnapshot{ + {Path: "/tmp/x", Accesses: 3, BytesRead: 128, BytesWritten: 64, AvgLatencyNs: 100}, + }, + []statsengine.ProcessSnapshot{ + {PID: 42, Comm: "cat", Syscalls: 5, RatePerSec: 0.5, AvgLatencyNs: 200}, + }, + statsengine.NewHistogramSnapshot(1, []statsengine.HistogramBucketSnapshot{ + {Label: "0-1µs", LowerNs: 0, UpperNs: 1000, Count: 1}, + }), + statsengine.NewHistogramSnapshot(0, nil), + ) + + name, err := SnapshotCSV(&snap) + if err != nil { + t.Fatalf("SnapshotCSV returned error: %v", err) + } + + data, err := os.ReadFile(filepath.Join(dir, name)) + if err != nil { + t.Fatalf("read csv file: %v", err) + } + + content := string(data) + for _, want := range []string{"syscall", "read", "file", "/tmp/x", "process", "42", "latency_hist", "0-1µs"} { + if !strings.Contains(content, want) { + t.Fatalf("expected %q in CSV output, got:\n%s", want, content) + } + } +} + +// TestSnapValueHelpers verifies the nil-safe helper functions directly. +func TestSnapValueHelpers(t *testing.T) { + if got := snapValue(nil, func(s *statsengine.Snapshot) uint64 { return s.TotalSyscalls }); got != 0 { + t.Fatalf("snapValue(nil) = %d, want 0", got) + } + if got := snapValueF(nil, func(s *statsengine.Snapshot) float64 { return s.SyscallRatePerSec }); got != 0 { + t.Fatalf("snapValueF(nil) = %f, want 0", got) + } + if got := trendSummary(nil, func(s *statsengine.Snapshot) statsengine.Trend { return s.LatencyTrend }); got != "stable:0.00" { + t.Fatalf("trendSummary(nil) = %q, want stable:0.00", got) + } + + snap := statsengine.NewSnapshot(nil, nil, nil, nil, nil, nil, + statsengine.NewHistogramSnapshot(0, nil), + statsengine.NewHistogramSnapshot(0, nil), + ) + snap.TotalSyscalls = 99 + snap.SyscallRatePerSec = 3.14 + snap.LatencyTrend = statsengine.Trend{Direction: statsengine.TrendRising, DeltaPercent: 12.5} + + if got := snapValue(&snap, func(s *statsengine.Snapshot) uint64 { return s.TotalSyscalls }); got != 99 { + t.Fatalf("snapValue = %d, want 99", got) + } + if got := snapValueF(&snap, func(s *statsengine.Snapshot) float64 { return s.SyscallRatePerSec }); got != 3.14 { + t.Fatalf("snapValueF = %f, want 3.14", got) + } + if got := trendSummary(&snap, func(s *statsengine.Snapshot) statsengine.Trend { return s.LatencyTrend }); got != "rising:12.50" { + t.Fatalf("trendSummary = %q, want rising:12.50", got) + } +} |
