//go:build bench package workflow import ( "fmt" "math/rand" "os" "os/exec" "path/filepath" "runtime" "testing" "time" "github.com/ajhahnde/eeco/internal/config" ) // TestMain pins the user-global config dir to an empty temp dir so the // global config layer is a hermetic no-op under the bench fixture. func TestMain(m *testing.M) { gdir, err := os.MkdirTemp("", "eeco-global-") if err != nil { panic(err) } os.Setenv(config.GlobalConfigEnv, gdir) code := m.Run() os.RemoveAll(gdir) os.Exit(code) } const ( benchFileCount = 50_000 // benchSeed is fixed so the fixture is byte-identical every run; only // the wall-clock budget moves between machines. benchSeed = 0xE6C0 benchMaxWall = 5 * time.Second ) // benchFixturePath is /dist/bench-fixture, derived from // the test source location so the bench is invariant to CWD. The // directory is gitignored via the repo's existing `/dist/` rule and is // removed by the `make bench` target after the run. func benchFixturePath(tb testing.TB) string { tb.Helper() _, here, _, ok := runtime.Caller(0) if !ok { tb.Fatal("runtime.Caller failed") } root := filepath.Clean(filepath.Join(filepath.Dir(here), "..", "..")) return filepath.Join(root, "dist", "bench-fixture") } // ensureFixture writes benchFileCount Go-like files into // /repo, distributed across 250 packages of 200 files // each. The marker file under .eeco-fixture-built prevents a rebuild // on a second invocation in the same `make bench` run. func ensureFixture(b *testing.B, dir string) { b.Helper() repo := filepath.Join(dir, "repo") marker := filepath.Join(repo, ".eeco-fixture-built") if _, err := os.Stat(marker); err == nil { return } if err := os.MkdirAll(repo, 0o755); err != nil { b.Fatal(err) } rng := rand.New(rand.NewSource(benchSeed)) for i := range benchFileCount { sub := fmt.Sprintf("pkg%03d", i/200) path := filepath.Join(repo, sub, fmt.Sprintf("file%05d.go", i)) if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { b.Fatal(err) } body := fmt.Sprintf("package %s\n\n// file %d rnd=%d\nfunc F%d() {}\n", sub, i, rng.Int63(), i) if err := os.WriteFile(path, []byte(body), 0o644); err != nil { b.Fatal(err) } } if err := os.WriteFile(marker, []byte("ok"), 0o644); err != nil { b.Fatal(err) } } // ensureGitAdded initialises as a git repo and `git add -A`s the // full tree. Required by leak-guard's `gitx.TrackedFiles` probe. // Cached by a marker so successive Run calls inside one benchmark do // not redo the index work. func ensureGitAdded(b *testing.B, repo string) { b.Helper() marker := filepath.Join(repo, ".eeco-fixture-git") if _, err := os.Stat(marker); err == nil { return } for _, argv := range [][]string{ {"git", "init", "-q"}, {"git", "config", "user.email", "bench@local"}, {"git", "config", "user.name", "bench"}, {"git", "add", "-A"}, } { cmd := exec.Command(argv[0], argv[1:]...) cmd.Dir = repo if out, err := cmd.CombinedOutput(); err != nil { b.Fatalf("%v in %s: %v\n%s", argv, repo, err, out) } } if err := os.WriteFile(marker, []byte("ok"), 0o644); err != nil { b.Fatal(err) } } func benchConfig(b *testing.B, repo string) *config.Config { b.Helper() cfg, err := config.Load(repo, config.DefaultWorkspace) if err != nil { b.Fatalf("config.Load(%s): %v", repo, err) } return cfg } func BenchmarkCommentHygiene_50k(b *testing.B) { dir := benchFixturePath(b) ensureFixture(b, dir) repo := filepath.Join(dir, "repo") if _, err := os.Stat(filepath.Join(repo, ".git")); os.IsNotExist(err) { if err := os.MkdirAll(filepath.Join(repo, ".git"), 0o755); err != nil { b.Fatal(err) } } cfg := benchConfig(b, repo) env := Env{Config: cfg} b.ResetTimer() start := time.Now() for range b.N { res, err := (commentHygiene{}).Run(env) if err != nil { b.Fatalf("comment-hygiene: %v", err) } if res.Code != CodeClean { b.Fatalf("comment-hygiene unexpectedly flagged the fixture: %+v", res) } } wall := time.Since(start) if b.N > 0 { wall /= time.Duration(b.N) } b.ReportMetric(float64(wall.Milliseconds()), "ms/scan") if wall > benchMaxWall { b.Fatalf("comment-hygiene wall %s exceeds %s budget on %d files", wall, benchMaxWall, benchFileCount) } } func BenchmarkLeakGuard_50k(b *testing.B) { dir := benchFixturePath(b) ensureFixture(b, dir) repo := filepath.Join(dir, "repo") ensureGitAdded(b, repo) cfg := benchConfig(b, repo) env := Env{Config: cfg} b.ResetTimer() start := time.Now() for range b.N { res, err := (leakGuard{}).Run(env) if err != nil { b.Fatalf("leak-guard: %v", err) } if res.Code != CodeClean { b.Fatalf("leak-guard unexpectedly flagged the fixture: %+v", res) } } wall := time.Since(start) if b.N > 0 { wall /= time.Duration(b.N) } b.ReportMetric(float64(wall.Milliseconds()), "ms/scan") if wall > benchMaxWall { b.Fatalf("leak-guard wall %s exceeds %s budget on %d files", wall, benchMaxWall, benchFileCount) } }