1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
package integrationtests
import (
"strings"
"testing"
"ior/internal/flamegraph"
)
// ExpectedEvent describes an I/O event that should appear in the test output.
type ExpectedEvent struct {
PathContains string // substring match on file path
Tracepoint string // tracepoint name substring, e.g. "openat"
Comm string // expected comm name, e.g. "ioworkload"
MinCount uint64 // minimum total occurrences across all matching records
}
// AssertEventsPresent verifies that each expected event is found in the test result.
// Counts are summed across all matching records before comparing to MinCount.
func AssertEventsPresent(t *testing.T, result TestResult, expected []ExpectedEvent) {
t.Helper()
for _, exp := range expected {
var totalCount uint64
var matched bool
for _, rec := range result.Records {
if matchesExpectation(rec, exp) {
matched = true
totalCount += rec.Cnt.Count
}
}
if !matched {
t.Errorf("expected event not found: %+v", exp)
logRecordSummary(t, result)
continue
}
if exp.MinCount > 0 && totalCount < exp.MinCount {
t.Errorf("event matching %+v has total count %d, want >= %d",
exp, totalCount, exp.MinCount)
}
}
}
func logRecordSummary(t *testing.T, result TestResult) {
t.Helper()
limit := 20
if len(result.Records) < limit {
limit = len(result.Records)
}
t.Logf("captured %d records; first %d:", len(result.Records), limit)
for i := 0; i < limit; i++ {
rec := result.Records[i]
t.Logf(" tracepoint=%s comm=%q pid=%d path=%q count=%d", rec.TraceID.String(), rec.Comm, rec.Pid, rec.Path, rec.Cnt.Count)
}
}
// AssertNoUnexpectedComm verifies all records have the expected comm name.
// Records with empty comm are skipped because BPF may capture events before
// the process name is set in the task struct.
func AssertNoUnexpectedComm(t *testing.T, result TestResult, expectedComm string) {
t.Helper()
var count int
for _, rec := range result.Records {
if rec.Comm == "" {
continue
}
if rec.Comm != expectedComm {
count++
if count <= 5 {
t.Logf("unexpected comm %q (pid=%d tracepoint=%s path=%q)",
rec.Comm, rec.Pid, rec.TraceID.String(), rec.Path)
}
}
}
if count > 0 {
t.Fatalf("found %d records with unexpected comm (want %q)", count, expectedComm)
}
}
// AssertNoUnexpectedPID verifies all records belong to the expected PID.
// Accepts int to match os.Getpid() return type.
func AssertNoUnexpectedPID(t *testing.T, result TestResult, expectedPID int) {
t.Helper()
pid := uint32(expectedPID)
var count int
for _, rec := range result.Records {
if rec.Pid != pid {
count++
if count <= 5 {
t.Logf("unexpected PID %d (tracepoint=%s path=%q comm=%q)",
rec.Pid, rec.TraceID.String(), rec.Path, rec.Comm)
}
}
}
if count > 0 {
t.Fatalf("found %d records with unexpected PID (want %d)", count, expectedPID)
}
}
// AssertEventsAbsent verifies that none of the specified events appear in the test result.
// Each ExpectedEvent must have at least one filter field set to avoid accidentally
// matching all records.
func AssertEventsAbsent(t *testing.T, result TestResult, absent []ExpectedEvent) {
t.Helper()
for _, exp := range absent {
if exp.PathContains == "" && exp.Tracepoint == "" && exp.Comm == "" {
t.Errorf("AssertEventsAbsent: ExpectedEvent must have at least one filter field set: %+v", exp)
continue
}
for _, rec := range result.Records {
if matchesExpectation(rec, exp) {
t.Errorf("event should be absent but was found: %+v (path=%q tracepoint=%s comm=%q)",
exp, rec.Path, rec.TraceID.String(), rec.Comm)
break
}
}
}
}
func matchesExpectation(rec flamegraph.IterRecord, exp ExpectedEvent) bool {
if exp.PathContains != "" && !strings.Contains(rec.Path, exp.PathContains) {
return false
}
if exp.Tracepoint != "" && !strings.Contains(rec.TraceID.String(), exp.Tracepoint) {
return false
}
if exp.Comm != "" && rec.Comm != "" && rec.Comm != exp.Comm {
return false
}
return true
}
|