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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
|
package integrationtests
import (
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
"testing"
"time"
)
func TestWorkloadCrashReportsError(t *testing.T) {
h := newTestHarness(t)
result, pid, err := h.Run("crash", 5)
if err == nil {
t.Fatal("expected error from crashed workload, got nil")
}
if pid == 0 {
t.Fatal("expected non-zero PID from started workload")
}
if !strings.Contains(err.Error(), "workload") {
t.Errorf("error should mention workload, got: %v", err)
}
if len(result.Records) != 0 {
t.Errorf("expected no records from crashed workload, got %d", len(result.Records))
}
}
func TestWaitBothIorExitError(t *testing.T) {
workloadCmd := exec.Command("true")
iorCmd := exec.Command("false")
if err := workloadCmd.Start(); err != nil {
t.Fatalf("start workload: %v", err)
}
if err := iorCmd.Start(); err != nil {
t.Fatalf("start ior: %v", err)
}
workloadErr, iorErr := waitBoth(workloadCmd, iorCmd, 5, iorShutdownGrace)
if iorErr == nil {
t.Fatal("expected ior error, got nil")
}
if workloadErr != nil {
t.Errorf("expected nil workload error, got: %v", workloadErr)
}
}
func TestWaitBothIorTimeout(t *testing.T) {
workloadCmd := exec.Command("true")
iorCmd := exec.Command("sleep", "60")
if err := workloadCmd.Start(); err != nil {
t.Fatalf("start workload: %v", err)
}
if err := iorCmd.Start(); err != nil {
t.Fatalf("start ior: %v", err)
}
// Use duration=0 and a short grace period so timeout fires quickly.
// Workload ("true") exits instantly; ior ("sleep 60") exceeds the timeout.
workloadErr, iorErr := waitBoth(workloadCmd, iorCmd, 0, 500*time.Millisecond)
if workloadErr != nil {
t.Errorf("expected nil workload error, got: %v", workloadErr)
}
if iorErr == nil {
t.Fatal("expected ior error from timeout, got nil")
}
if !strings.Contains(iorErr.Error(), "timed out") {
t.Errorf("expected timeout error, got: %v", iorErr)
}
}
func TestWaitBothBothTimeout(t *testing.T) {
workloadCmd := exec.Command("sleep", "60")
iorCmd := exec.Command("sleep", "60")
if err := workloadCmd.Start(); err != nil {
t.Fatalf("start workload: %v", err)
}
if err := iorCmd.Start(); err != nil {
t.Fatalf("start ior: %v", err)
}
workloadErr, iorErr := waitBoth(workloadCmd, iorCmd, 0, 500*time.Millisecond)
if workloadErr == nil {
t.Fatal("expected workload timeout error, got nil")
}
if !strings.Contains(workloadErr.Error(), "timed out") {
t.Errorf("expected workload timeout error, got: %v", workloadErr)
}
if iorErr == nil {
t.Fatal("expected ior timeout error, got nil")
}
if !strings.Contains(iorErr.Error(), "timed out") {
t.Errorf("expected ior timeout error, got: %v", iorErr)
}
}
func TestWaitBothBothSucceed(t *testing.T) {
workloadCmd := exec.Command("true")
iorCmd := exec.Command("true")
if err := workloadCmd.Start(); err != nil {
t.Fatalf("start workload: %v", err)
}
if err := iorCmd.Start(); err != nil {
t.Fatalf("start ior: %v", err)
}
workloadErr, iorErr := waitBoth(workloadCmd, iorCmd, 5, iorShutdownGrace)
if workloadErr != nil {
t.Errorf("expected nil workload error, got: %v", workloadErr)
}
if iorErr != nil {
t.Errorf("expected nil ior error, got: %v", iorErr)
}
}
func TestIorCrashReportsError(t *testing.T) {
tmpDir := t.TempDir()
outputDir := t.TempDir()
// Create a fake workload that prints its PID and exits cleanly.
workloadBin := writeScript(t, tmpDir, "workload", `echo $$`)
// Create a fake ior that exits with error immediately.
iorBin := writeScript(t, tmpDir, "ior", `exit 1`)
h := TestHarness{
IorBinary: iorBin,
WorkloadBinary: workloadBin,
BpfObject: filepath.Join(tmpDir, "fake.bpf.o"),
OutputDir: outputDir,
}
result, pid, err := h.Run("test", 5)
if err == nil {
t.Fatal("expected error when ior crashes, got nil")
}
if !strings.Contains(err.Error(), "ior") {
t.Errorf("error should mention ior, got: %v", err)
}
if pid == 0 {
t.Fatal("expected non-zero workload PID")
}
if len(result.Records) != 0 {
t.Errorf("expected no records from crashed ior, got %d", len(result.Records))
}
}
func TestIorStartFailureCleansUpWorkload(t *testing.T) {
tmpDir := t.TempDir()
outputDir := t.TempDir()
// Create a fake workload that prints PID and sleeps.
// Use exec to replace the shell so killing the process kills the sleep too.
workloadBin := writeScript(t, tmpDir, "workload", `echo $$; exec sleep 30`)
h := TestHarness{
IorBinary: "/nonexistent/ior",
WorkloadBinary: workloadBin,
BpfObject: filepath.Join(tmpDir, "fake.bpf.o"),
OutputDir: outputDir,
}
_, pid, err := h.Run("test", 5)
if err == nil {
t.Fatal("expected error when ior binary doesn't exist, got nil")
}
if pid == 0 {
t.Fatal("expected non-zero workload PID even when ior fails to start")
}
// Verify the workload process was cleaned up (killed).
// After Run returns, the workload should no longer be running.
// On Linux, FindProcess always succeeds, so we check with signal 0.
proc, procErr := os.FindProcess(pid)
if procErr == nil {
if signalErr := proc.Signal(syscall.Signal(0)); signalErr == nil {
t.Error("workload process is still running after ior start failure")
}
}
}
func TestStartIorPassesBPFObjectOverrideEnv(t *testing.T) {
tmpDir := t.TempDir()
outputDir := t.TempDir()
overridePath := filepath.Join(tmpDir, "fake.bpf.o")
iorBin := writeScript(t, tmpDir, "ior", `printf '%s' "$IOR_BPF_OBJECT" > "$PWD/override.txt"`)
h := TestHarness{
IorBinary: iorBin,
BpfObject: overridePath,
OutputDir: outputDir,
}
cmd, err := h.startIor(1234, "test", 5, nil)
if err != nil {
t.Fatalf("startIor returned error: %v", err)
}
if err := cmd.Wait(); err != nil {
t.Fatalf("wait for fake ior: %v", err)
}
data, err := os.ReadFile(filepath.Join(outputDir, "override.txt"))
if err != nil {
t.Fatalf("read override marker: %v", err)
}
if got, want := string(data), overridePath; got != want {
t.Fatalf("IOR_BPF_OBJECT = %q, want %q", got, want)
}
}
|