Go 182 lines
//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 <eeco-repo-root>/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
// <bench-fixture>/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 <repo> 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)
}
}