| 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | "os" |
| 5 | "testing" |
| 6 | "time" |
| 7 | ) |
| 8 | |
| 9 | // robustTempDir is a drop-in for t.TempDir whose cleanup retries RemoveAll for a |
| 10 | // short window. The Capabilities/history tests wire a Controller and isolate the |
| 11 | // user-config tree under a temp dir; at teardown a background resource can still |
| 12 | // hold a file under it for a few milliseconds after Close returns. On Windows |
| 13 | // that surfaces as "being used by another process"; on Linux a write racing |
| 14 | // RemoveAll surfaces as "directory not empty". Plain t.TempDir turns that |
| 15 | // teardown race into a red test even though every assertion passed (the |
| 16 | // recurring main-v2 CI flake). Retrying absorbs the race; a dir that never frees |
| 17 | // is logged, not fatal, so a genuine leak stays visible without the flake. |
| 18 | func robustTempDir(t *testing.T) string { |
| 19 | t.Helper() |
| 20 | dir, err := os.MkdirTemp("", "reasonix-test-*") |
| 21 | if err != nil { |
| 22 | t.Fatalf("robustTempDir: %v", err) |
| 23 | } |
| 24 | t.Cleanup(func() { |
| 25 | var rmErr error |
| 26 | for i := 0; i < 100; i++ { |
| 27 | if rmErr = os.RemoveAll(dir); rmErr == nil { |
| 28 | return |
| 29 | } |
| 30 | time.Sleep(20 * time.Millisecond) |
| 31 | } |
| 32 | t.Logf("robustTempDir: cleanup did not converge for %s: %v", dir, rmErr) |
| 33 | }) |
| 34 | return dir |
| 35 | } |
| 36 |