blob: 8c57a4b812d3a119197ca2acc75dc777759a6182 [file] [log] [blame]
Earl Lee2e463fb2025-04-17 11:22:22 -07001package claudetool
2
3import (
4 "context"
5 "encoding/json"
Philip Zeyligerb60f0f22025-04-23 18:19:32 +00006 "os"
7 "path/filepath"
Earl Lee2e463fb2025-04-17 11:22:22 -07008 "strings"
Philip Zeyligerb60f0f22025-04-23 18:19:32 +00009 "syscall"
Earl Lee2e463fb2025-04-17 11:22:22 -070010 "testing"
11 "time"
12)
13
Josh Bleecher Snyder17b2fd92025-07-09 22:47:13 +000014func TestBashSlowOk(t *testing.T) {
15 // Test that slow_ok flag is properly handled
16 t.Run("SlowOk Flag", func(t *testing.T) {
17 input := json.RawMessage(`{"command":"echo 'slow test'","slow_ok":true}`)
18
19 bashTool := (&BashTool{}).Tool()
Josh Bleecher Snyder43b60b92025-07-21 14:57:10 -070020 toolOut := bashTool.Run(context.Background(), input)
21 if toolOut.Error != nil {
22 t.Fatalf("Unexpected error: %v", toolOut.Error)
Josh Bleecher Snyder17b2fd92025-07-09 22:47:13 +000023 }
Josh Bleecher Snyder43b60b92025-07-21 14:57:10 -070024 result := toolOut.LLMContent
Josh Bleecher Snyder17b2fd92025-07-09 22:47:13 +000025
26 expected := "slow test\n"
27 if len(result) == 0 || result[0].Text != expected {
28 t.Errorf("Expected %q, got %q", expected, result[0].Text)
29 }
30 })
31
32 // Test that slow_ok with background works
33 t.Run("SlowOk with Background", func(t *testing.T) {
34 input := json.RawMessage(`{"command":"echo 'slow background test'","slow_ok":true,"background":true}`)
35
36 bashTool := (&BashTool{}).Tool()
Josh Bleecher Snyder43b60b92025-07-21 14:57:10 -070037 toolOut := bashTool.Run(context.Background(), input)
38 if toolOut.Error != nil {
39 t.Fatalf("Unexpected error: %v", toolOut.Error)
Josh Bleecher Snyder17b2fd92025-07-09 22:47:13 +000040 }
Josh Bleecher Snyder43b60b92025-07-21 14:57:10 -070041 result := toolOut.LLMContent
Josh Bleecher Snyder17b2fd92025-07-09 22:47:13 +000042
43 // Should return background result JSON
44 var bgResult BackgroundResult
45 resultStr := result[0].Text
46 if err := json.Unmarshal([]byte(resultStr), &bgResult); err != nil {
47 t.Fatalf("Failed to unmarshal background result: %v", err)
48 }
49
50 if bgResult.PID <= 0 {
51 t.Errorf("Invalid PID returned: %d", bgResult.PID)
52 }
53
54 // Clean up
55 os.Remove(bgResult.StdoutFile)
56 os.Remove(bgResult.StderrFile)
57 os.Remove(filepath.Dir(bgResult.StdoutFile))
58 })
59}
60
Philip Zeyligercfd0fe62025-06-21 02:17:41 +000061func TestBashTool(t *testing.T) {
Josh Bleecher Snyder17b2fd92025-07-09 22:47:13 +000062 var bashTool BashTool
63 tool := bashTool.Tool()
64
Earl Lee2e463fb2025-04-17 11:22:22 -070065 // Test basic functionality
66 t.Run("Basic Command", func(t *testing.T) {
67 input := json.RawMessage(`{"command":"echo 'Hello, world!'"}`)
68
Josh Bleecher Snyder43b60b92025-07-21 14:57:10 -070069 toolOut := tool.Run(context.Background(), input)
70 if toolOut.Error != nil {
71 t.Fatalf("Unexpected error: %v", toolOut.Error)
Earl Lee2e463fb2025-04-17 11:22:22 -070072 }
Josh Bleecher Snyder43b60b92025-07-21 14:57:10 -070073 result := toolOut.LLMContent
Earl Lee2e463fb2025-04-17 11:22:22 -070074
75 expected := "Hello, world!\n"
Philip Zeyligercfd0fe62025-06-21 02:17:41 +000076 if len(result) == 0 || result[0].Text != expected {
77 t.Errorf("Expected %q, got %q", expected, result[0].Text)
Earl Lee2e463fb2025-04-17 11:22:22 -070078 }
79 })
80
81 // Test with arguments
82 t.Run("Command With Arguments", func(t *testing.T) {
83 input := json.RawMessage(`{"command":"echo -n foo && echo -n bar"}`)
84
Josh Bleecher Snyder43b60b92025-07-21 14:57:10 -070085 toolOut := tool.Run(context.Background(), input)
86 if toolOut.Error != nil {
87 t.Fatalf("Unexpected error: %v", toolOut.Error)
Earl Lee2e463fb2025-04-17 11:22:22 -070088 }
Josh Bleecher Snyder43b60b92025-07-21 14:57:10 -070089 result := toolOut.LLMContent
Earl Lee2e463fb2025-04-17 11:22:22 -070090
91 expected := "foobar"
Philip Zeyligercfd0fe62025-06-21 02:17:41 +000092 if len(result) == 0 || result[0].Text != expected {
93 t.Errorf("Expected %q, got %q", expected, result[0].Text)
Earl Lee2e463fb2025-04-17 11:22:22 -070094 }
95 })
96
Josh Bleecher Snyder17b2fd92025-07-09 22:47:13 +000097 // Test with slow_ok parameter
98 t.Run("With SlowOK", func(t *testing.T) {
Earl Lee2e463fb2025-04-17 11:22:22 -070099 inputObj := struct {
100 Command string `json:"command"`
Josh Bleecher Snyder17b2fd92025-07-09 22:47:13 +0000101 SlowOK bool `json:"slow_ok"`
Earl Lee2e463fb2025-04-17 11:22:22 -0700102 }{
103 Command: "sleep 0.1 && echo 'Completed'",
Josh Bleecher Snyder17b2fd92025-07-09 22:47:13 +0000104 SlowOK: true,
Earl Lee2e463fb2025-04-17 11:22:22 -0700105 }
106 inputJSON, err := json.Marshal(inputObj)
107 if err != nil {
108 t.Fatalf("Failed to marshal input: %v", err)
109 }
110
Josh Bleecher Snyder43b60b92025-07-21 14:57:10 -0700111 toolOut := tool.Run(context.Background(), inputJSON)
112 if toolOut.Error != nil {
113 t.Fatalf("Unexpected error: %v", toolOut.Error)
Earl Lee2e463fb2025-04-17 11:22:22 -0700114 }
Josh Bleecher Snyder43b60b92025-07-21 14:57:10 -0700115 result := toolOut.LLMContent
Earl Lee2e463fb2025-04-17 11:22:22 -0700116
117 expected := "Completed\n"
Philip Zeyligercfd0fe62025-06-21 02:17:41 +0000118 if len(result) == 0 || result[0].Text != expected {
119 t.Errorf("Expected %q, got %q", expected, result[0].Text)
Earl Lee2e463fb2025-04-17 11:22:22 -0700120 }
121 })
122
Josh Bleecher Snyder17b2fd92025-07-09 22:47:13 +0000123 // Test command timeout with custom timeout config
Earl Lee2e463fb2025-04-17 11:22:22 -0700124 t.Run("Command Timeout", func(t *testing.T) {
Josh Bleecher Snyder17b2fd92025-07-09 22:47:13 +0000125 // Use a custom BashTool with very short timeout
126 customTimeouts := &Timeouts{
127 Fast: 100 * time.Millisecond,
128 Slow: 100 * time.Millisecond,
129 Background: 100 * time.Millisecond,
Earl Lee2e463fb2025-04-17 11:22:22 -0700130 }
Josh Bleecher Snyder17b2fd92025-07-09 22:47:13 +0000131 customBash := &BashTool{
132 Timeouts: customTimeouts,
Earl Lee2e463fb2025-04-17 11:22:22 -0700133 }
Josh Bleecher Snyder17b2fd92025-07-09 22:47:13 +0000134 tool := customBash.Tool()
Earl Lee2e463fb2025-04-17 11:22:22 -0700135
Josh Bleecher Snyder17b2fd92025-07-09 22:47:13 +0000136 input := json.RawMessage(`{"command":"sleep 0.5 && echo 'Should not see this'"}`)
137
Josh Bleecher Snyder43b60b92025-07-21 14:57:10 -0700138 toolOut := tool.Run(context.Background(), input)
139 if toolOut.Error == nil {
Earl Lee2e463fb2025-04-17 11:22:22 -0700140 t.Errorf("Expected timeout error, got none")
Josh Bleecher Snyder43b60b92025-07-21 14:57:10 -0700141 } else if !strings.Contains(toolOut.Error.Error(), "timed out") {
142 t.Errorf("Expected timeout error, got: %v", toolOut.Error)
Earl Lee2e463fb2025-04-17 11:22:22 -0700143 }
144 })
145
146 // Test command that fails
147 t.Run("Failed Command", func(t *testing.T) {
148 input := json.RawMessage(`{"command":"exit 1"}`)
149
Josh Bleecher Snyder43b60b92025-07-21 14:57:10 -0700150 toolOut := tool.Run(context.Background(), input)
151 if toolOut.Error == nil {
Earl Lee2e463fb2025-04-17 11:22:22 -0700152 t.Errorf("Expected error for failed command, got none")
153 }
154 })
155
156 // Test invalid input
157 t.Run("Invalid JSON Input", func(t *testing.T) {
158 input := json.RawMessage(`{"command":123}`) // Invalid JSON (command must be string)
159
Josh Bleecher Snyder43b60b92025-07-21 14:57:10 -0700160 toolOut := tool.Run(context.Background(), input)
161 if toolOut.Error == nil {
Earl Lee2e463fb2025-04-17 11:22:22 -0700162 t.Errorf("Expected error for invalid input, got none")
163 }
164 })
165}
166
167func TestExecuteBash(t *testing.T) {
168 ctx := context.Background()
169
170 // Test successful command
171 t.Run("Successful Command", func(t *testing.T) {
172 req := bashInput{
173 Command: "echo 'Success'",
Earl Lee2e463fb2025-04-17 11:22:22 -0700174 }
175
Josh Bleecher Snyder17b2fd92025-07-09 22:47:13 +0000176 output, err := executeBash(ctx, req, 5*time.Second)
Earl Lee2e463fb2025-04-17 11:22:22 -0700177 if err != nil {
178 t.Fatalf("Unexpected error: %v", err)
179 }
180
181 want := "Success\n"
182 if output != want {
183 t.Errorf("Expected %q, got %q", want, output)
184 }
185 })
186
Pokey Ruleb6d6d382025-05-07 10:29:03 +0100187 // Test SKETCH=1 environment variable is set
188 t.Run("SKETCH Environment Variable", func(t *testing.T) {
189 req := bashInput{
190 Command: "echo $SKETCH",
Pokey Ruleb6d6d382025-05-07 10:29:03 +0100191 }
192
Josh Bleecher Snyder17b2fd92025-07-09 22:47:13 +0000193 output, err := executeBash(ctx, req, 5*time.Second)
Pokey Ruleb6d6d382025-05-07 10:29:03 +0100194 if err != nil {
195 t.Fatalf("Unexpected error: %v", err)
196 }
197
198 want := "1\n"
199 if output != want {
200 t.Errorf("Expected SKETCH=1, got %q", output)
201 }
202 })
203
Earl Lee2e463fb2025-04-17 11:22:22 -0700204 // Test command with output to stderr
205 t.Run("Command with stderr", func(t *testing.T) {
206 req := bashInput{
207 Command: "echo 'Error message' >&2 && echo 'Success'",
Earl Lee2e463fb2025-04-17 11:22:22 -0700208 }
209
Josh Bleecher Snyder17b2fd92025-07-09 22:47:13 +0000210 output, err := executeBash(ctx, req, 5*time.Second)
Earl Lee2e463fb2025-04-17 11:22:22 -0700211 if err != nil {
212 t.Fatalf("Unexpected error: %v", err)
213 }
214
215 want := "Error message\nSuccess\n"
216 if output != want {
217 t.Errorf("Expected %q, got %q", want, output)
218 }
219 })
220
221 // Test command that fails with stderr
222 t.Run("Failed Command with stderr", func(t *testing.T) {
223 req := bashInput{
224 Command: "echo 'Error message' >&2 && exit 1",
Earl Lee2e463fb2025-04-17 11:22:22 -0700225 }
226
Josh Bleecher Snyder17b2fd92025-07-09 22:47:13 +0000227 _, err := executeBash(ctx, req, 5*time.Second)
Earl Lee2e463fb2025-04-17 11:22:22 -0700228 if err == nil {
229 t.Errorf("Expected error for failed command, got none")
230 } else if !strings.Contains(err.Error(), "Error message") {
231 t.Errorf("Expected stderr in error message, got: %v", err)
232 }
233 })
234
235 // Test timeout
236 t.Run("Command Timeout", func(t *testing.T) {
237 req := bashInput{
238 Command: "sleep 1 && echo 'Should not see this'",
Earl Lee2e463fb2025-04-17 11:22:22 -0700239 }
240
241 start := time.Now()
Josh Bleecher Snyder17b2fd92025-07-09 22:47:13 +0000242 _, err := executeBash(ctx, req, 100*time.Millisecond)
Earl Lee2e463fb2025-04-17 11:22:22 -0700243 elapsed := time.Since(start)
244
245 // Command should time out after ~100ms, not wait for full 1 second
246 if elapsed >= 1*time.Second {
247 t.Errorf("Command did not respect timeout, took %v", elapsed)
248 }
249
250 if err == nil {
251 t.Errorf("Expected timeout error, got none")
252 } else if !strings.Contains(err.Error(), "timed out") {
253 t.Errorf("Expected timeout error, got: %v", err)
254 }
255 })
256}
Philip Zeyligerb60f0f22025-04-23 18:19:32 +0000257
258func TestBackgroundBash(t *testing.T) {
Josh Bleecher Snyder17b2fd92025-07-09 22:47:13 +0000259 var bashTool BashTool
260 tool := bashTool.Tool()
261
Philip Zeyligerb60f0f22025-04-23 18:19:32 +0000262 // Test basic background execution
263 t.Run("Basic Background Command", func(t *testing.T) {
264 inputObj := struct {
265 Command string `json:"command"`
266 Background bool `json:"background"`
267 }{
Pokey Ruleb6d6d382025-05-07 10:29:03 +0100268 Command: "echo 'Hello from background' $SKETCH",
Philip Zeyligerb60f0f22025-04-23 18:19:32 +0000269 Background: true,
270 }
271 inputJSON, err := json.Marshal(inputObj)
272 if err != nil {
273 t.Fatalf("Failed to marshal input: %v", err)
274 }
275
Josh Bleecher Snyder43b60b92025-07-21 14:57:10 -0700276 toolOut := tool.Run(context.Background(), inputJSON)
277 if toolOut.Error != nil {
278 t.Fatalf("Unexpected error: %v", toolOut.Error)
Philip Zeyligerb60f0f22025-04-23 18:19:32 +0000279 }
Josh Bleecher Snyder43b60b92025-07-21 14:57:10 -0700280 result := toolOut.LLMContent
Philip Zeyligerb60f0f22025-04-23 18:19:32 +0000281
282 // Parse the returned JSON
283 var bgResult BackgroundResult
Philip Zeyligercfd0fe62025-06-21 02:17:41 +0000284 resultStr := result[0].Text
Philip Zeyliger72252cb2025-05-10 17:00:08 -0700285 if err := json.Unmarshal([]byte(resultStr), &bgResult); err != nil {
Philip Zeyligerb60f0f22025-04-23 18:19:32 +0000286 t.Fatalf("Failed to unmarshal background result: %v", err)
287 }
288
289 // Verify we got a valid PID
290 if bgResult.PID <= 0 {
291 t.Errorf("Invalid PID returned: %d", bgResult.PID)
292 }
293
294 // Verify output files exist
295 if _, err := os.Stat(bgResult.StdoutFile); os.IsNotExist(err) {
296 t.Errorf("Stdout file doesn't exist: %s", bgResult.StdoutFile)
297 }
298 if _, err := os.Stat(bgResult.StderrFile); os.IsNotExist(err) {
299 t.Errorf("Stderr file doesn't exist: %s", bgResult.StderrFile)
300 }
301
302 // Wait for the command output to be written to file
303 waitForFile(t, bgResult.StdoutFile)
304
305 // Check file contents
306 stdoutContent, err := os.ReadFile(bgResult.StdoutFile)
307 if err != nil {
308 t.Fatalf("Failed to read stdout file: %v", err)
309 }
Pokey Ruleb6d6d382025-05-07 10:29:03 +0100310 expected := "Hello from background 1\n"
Philip Zeyligerb60f0f22025-04-23 18:19:32 +0000311 if string(stdoutContent) != expected {
312 t.Errorf("Expected stdout content %q, got %q", expected, string(stdoutContent))
313 }
314
315 // Clean up
316 os.Remove(bgResult.StdoutFile)
317 os.Remove(bgResult.StderrFile)
318 os.Remove(filepath.Dir(bgResult.StdoutFile))
319 })
320
321 // Test background command with stderr output
322 t.Run("Background Command with stderr", func(t *testing.T) {
323 inputObj := struct {
324 Command string `json:"command"`
325 Background bool `json:"background"`
326 }{
327 Command: "echo 'Output to stdout' && echo 'Output to stderr' >&2",
328 Background: true,
329 }
330 inputJSON, err := json.Marshal(inputObj)
331 if err != nil {
332 t.Fatalf("Failed to marshal input: %v", err)
333 }
334
Josh Bleecher Snyder43b60b92025-07-21 14:57:10 -0700335 toolOut := tool.Run(context.Background(), inputJSON)
336 if toolOut.Error != nil {
337 t.Fatalf("Unexpected error: %v", toolOut.Error)
Philip Zeyligerb60f0f22025-04-23 18:19:32 +0000338 }
Josh Bleecher Snyder43b60b92025-07-21 14:57:10 -0700339 result := toolOut.LLMContent
Philip Zeyligerb60f0f22025-04-23 18:19:32 +0000340
341 // Parse the returned JSON
342 var bgResult BackgroundResult
Philip Zeyligercfd0fe62025-06-21 02:17:41 +0000343 resultStr := result[0].Text
Philip Zeyliger72252cb2025-05-10 17:00:08 -0700344 if err := json.Unmarshal([]byte(resultStr), &bgResult); err != nil {
Philip Zeyligerb60f0f22025-04-23 18:19:32 +0000345 t.Fatalf("Failed to unmarshal background result: %v", err)
346 }
347
348 // Wait for the command output to be written to files
349 waitForFile(t, bgResult.StdoutFile)
350 waitForFile(t, bgResult.StderrFile)
351
352 // Check stdout content
353 stdoutContent, err := os.ReadFile(bgResult.StdoutFile)
354 if err != nil {
355 t.Fatalf("Failed to read stdout file: %v", err)
356 }
357 expectedStdout := "Output to stdout\n"
358 if string(stdoutContent) != expectedStdout {
359 t.Errorf("Expected stdout content %q, got %q", expectedStdout, string(stdoutContent))
360 }
361
362 // Check stderr content
363 stderrContent, err := os.ReadFile(bgResult.StderrFile)
364 if err != nil {
365 t.Fatalf("Failed to read stderr file: %v", err)
366 }
367 expectedStderr := "Output to stderr\n"
368 if string(stderrContent) != expectedStderr {
369 t.Errorf("Expected stderr content %q, got %q", expectedStderr, string(stderrContent))
370 }
371
372 // Clean up
373 os.Remove(bgResult.StdoutFile)
374 os.Remove(bgResult.StderrFile)
375 os.Remove(filepath.Dir(bgResult.StdoutFile))
376 })
377
378 // Test background command running without waiting
379 t.Run("Background Command Running", func(t *testing.T) {
380 // Create a script that will continue running after we check
381 inputObj := struct {
382 Command string `json:"command"`
383 Background bool `json:"background"`
384 }{
385 Command: "echo 'Running in background' && sleep 5",
386 Background: true,
387 }
388 inputJSON, err := json.Marshal(inputObj)
389 if err != nil {
390 t.Fatalf("Failed to marshal input: %v", err)
391 }
392
393 // Start the command in the background
Josh Bleecher Snyder43b60b92025-07-21 14:57:10 -0700394 toolOut := tool.Run(context.Background(), inputJSON)
395 if toolOut.Error != nil {
396 t.Fatalf("Unexpected error: %v", toolOut.Error)
Philip Zeyligerb60f0f22025-04-23 18:19:32 +0000397 }
Josh Bleecher Snyder43b60b92025-07-21 14:57:10 -0700398 result := toolOut.LLMContent
Philip Zeyligerb60f0f22025-04-23 18:19:32 +0000399
400 // Parse the returned JSON
401 var bgResult BackgroundResult
Philip Zeyligercfd0fe62025-06-21 02:17:41 +0000402 resultStr := result[0].Text
Philip Zeyliger72252cb2025-05-10 17:00:08 -0700403 if err := json.Unmarshal([]byte(resultStr), &bgResult); err != nil {
Philip Zeyligerb60f0f22025-04-23 18:19:32 +0000404 t.Fatalf("Failed to unmarshal background result: %v", err)
405 }
406
407 // Wait for the command output to be written to file
408 waitForFile(t, bgResult.StdoutFile)
409
410 // Check stdout content
411 stdoutContent, err := os.ReadFile(bgResult.StdoutFile)
412 if err != nil {
413 t.Fatalf("Failed to read stdout file: %v", err)
414 }
415 expectedStdout := "Running in background\n"
416 if string(stdoutContent) != expectedStdout {
417 t.Errorf("Expected stdout content %q, got %q", expectedStdout, string(stdoutContent))
418 }
419
420 // Verify the process is still running
421 process, _ := os.FindProcess(bgResult.PID)
422 err = process.Signal(syscall.Signal(0))
423 if err != nil {
424 // Process not running, which is unexpected
425 t.Error("Process is not running")
426 } else {
427 // Expected: process should be running
428 t.Log("Process correctly running in background")
429 // Kill it for cleanup
430 process.Kill()
431 }
432
433 // Clean up
434 os.Remove(bgResult.StdoutFile)
435 os.Remove(bgResult.StderrFile)
436 os.Remove(filepath.Dir(bgResult.StdoutFile))
437 })
Philip Zeyligerb60f0f22025-04-23 18:19:32 +0000438}
439
440func TestBashTimeout(t *testing.T) {
441 // Test default timeout values
442 t.Run("Default Timeout Values", func(t *testing.T) {
443 // Test foreground default timeout
444 foreground := bashInput{
445 Command: "echo 'test'",
446 Background: false,
447 }
Josh Bleecher Snyder17b2fd92025-07-09 22:47:13 +0000448 fgTimeout := foreground.timeout(nil)
449 expectedFg := 30 * time.Second
Philip Zeyligerb60f0f22025-04-23 18:19:32 +0000450 if fgTimeout != expectedFg {
451 t.Errorf("Expected foreground default timeout to be %v, got %v", expectedFg, fgTimeout)
452 }
453
454 // Test background default timeout
455 background := bashInput{
456 Command: "echo 'test'",
457 Background: true,
458 }
Josh Bleecher Snyder17b2fd92025-07-09 22:47:13 +0000459 bgTimeout := background.timeout(nil)
460 expectedBg := 24 * time.Hour
Philip Zeyligerb60f0f22025-04-23 18:19:32 +0000461 if bgTimeout != expectedBg {
462 t.Errorf("Expected background default timeout to be %v, got %v", expectedBg, bgTimeout)
463 }
464
Josh Bleecher Snyder17b2fd92025-07-09 22:47:13 +0000465 // Test slow_ok timeout
466 slowOk := bashInput{
Philip Zeyligerb60f0f22025-04-23 18:19:32 +0000467 Command: "echo 'test'",
Josh Bleecher Snyder17b2fd92025-07-09 22:47:13 +0000468 Background: false,
469 SlowOK: true,
Philip Zeyligerb60f0f22025-04-23 18:19:32 +0000470 }
Josh Bleecher Snyder17b2fd92025-07-09 22:47:13 +0000471 slowTimeout := slowOk.timeout(nil)
472 expectedSlow := 15 * time.Minute
473 if slowTimeout != expectedSlow {
474 t.Errorf("Expected slow_ok timeout to be %v, got %v", expectedSlow, slowTimeout)
475 }
476
477 // Test custom timeout config
478 customTimeouts := &Timeouts{
479 Fast: 5 * time.Second,
480 Slow: 2 * time.Minute,
481 Background: 1 * time.Hour,
482 }
483 customFast := bashInput{
484 Command: "echo 'test'",
485 Background: false,
486 }
487 customTimeout := customFast.timeout(customTimeouts)
488 expectedCustom := 5 * time.Second
489 if customTimeout != expectedCustom {
490 t.Errorf("Expected custom timeout to be %v, got %v", expectedCustom, customTimeout)
Philip Zeyligerb60f0f22025-04-23 18:19:32 +0000491 }
492 })
493}
494
495// waitForFile waits for a file to exist and be non-empty or times out
496func waitForFile(t *testing.T, filepath string) {
497 timeout := time.After(5 * time.Second)
498 tick := time.NewTicker(10 * time.Millisecond)
499 defer tick.Stop()
500
501 for {
502 select {
503 case <-timeout:
504 t.Fatalf("Timed out waiting for file to exist and have contents: %s", filepath)
505 return
506 case <-tick.C:
507 info, err := os.Stat(filepath)
508 if err == nil && info.Size() > 0 {
509 return // File exists and has content
510 }
511 }
512 }
513}
514
515// waitForProcessDeath waits for a process to no longer exist or times out
516func waitForProcessDeath(t *testing.T, pid int) {
517 timeout := time.After(5 * time.Second)
518 tick := time.NewTicker(50 * time.Millisecond)
519 defer tick.Stop()
520
521 for {
522 select {
523 case <-timeout:
524 t.Fatalf("Timed out waiting for process %d to exit", pid)
525 return
526 case <-tick.C:
527 process, _ := os.FindProcess(pid)
528 err := process.Signal(syscall.Signal(0))
529 if err != nil {
530 // Process doesn't exist
531 return
532 }
533 }
534 }
535}