leaktest

A small Go package that detects leaked goroutines in tests by diffing stack snapshots taken before and after each test runs.

Library
Go
vv1.3.0
1,044stars
BSD 3-Clause License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
40/100Fair
Development Activity0
Maintenance20
Community52
Maturity60
Momentum28

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
61/100Good
Architecture78
Code Quality72
Innovation40
Learning Curve55

Leaktest is a small, dependency-free Go package that detects goroutine leaks in tests by taking a snapshot of running goroutines before a test runs and comparing it against another snapshot taken afterward. It was refactored out of the goroutine leak detector originally found in Go’s own net/http tests and in the CockroachDB source tree, then packaged as a standalone, reusable module.

Rather than failing on the first mismatch, leaktest polls at a configurable interval (TickerInterval, default 50ms) for up to a timeout (default 5 seconds, a custom duration via CheckTimeout, or an existing context.Context via CheckContext) before reporting any goroutines still running that weren’t present in the original snapshot. It automatically filters out uninteresting goroutines — the test runner itself, the garbage collector, signal handlers, and HTTP keep-alive read/write loops — so failures point at genuine leaks rather than testing-framework noise.

What You Get

  • A single leaktest.Check(t) call that returns a deferred function to run at test end
  • Configurable timeout and polling interval via CheckTimeout and the package-level TickerInterval variable
  • Context-aware leak checking via CheckContext for tying into an existing deadline
  • Built-in exclusion list for runtime, testing, and HTTP keep-alive goroutines to avoid false positives

Common Use Cases

  • Detecting goroutines left running by background workers, pollers, or timers after a test completes
  • Verifying HTTP clients correctly close keep-alive connections instead of leaking read/write-loop goroutines
  • Bounding leak checks to an existing test’s context deadline with CheckContext
  • Confirming mutex, channel, and worker-pool cleanup paths release all spawned goroutines in table-driven tests

Under The Hood

Architecture The package is a single file (leaktest.go) with a flat, funnel-shaped call graph: the three exported entry points (Check, CheckTimeout, CheckContext) all resolve into CheckContext, which snapshots goroutine IDs via interestingGoroutines, then polls leakedGoroutines against a time.Ticker until either the diff clears or the context is cancelled; parsing and filtering live in unexported helpers (interestingGoroutine, a goroutineByID sort type), and a separate leaktest_utils_test.go supplies an httptest-based keep-alive server used only by the test suite. There’s no dependency injection, no internal layering, and no state beyond a single package-level TickerInterval variable — appropriate given the narrow, single-purpose scope.

Tech Stack Standard library only: context, fmt, runtime, sort, strconv, strings, and time in the implementation, plus net/http/net/http/httptest in tests. There is no go.mod — the repo predates mandatory Go modules and hasn’t been updated to adopt one, though it still resolves fine as a tagged import path for module-aware consumers. CI is a .travis.yml running go test -race with coverage uploaded to Codecov across Go 1.8 through tip; there’s no Makefile, linter config, or release automation beyond git tags.

Code Quality Test coverage is genuinely thorough for the package’s size: table-driven cases in leaktest_test.go cover leaking and non-leaking scenarios (infinite loops, blocked selects, mutex contention, HTTP keep-alives with and without DisableKeepAlives), plus dedicated tests for timeout behavior, context cancellation, and the internal stack-parsing logic (TestInterestingGoroutine) including malformed-input error paths. Error handling returns explicit error values from parsing rather than panicking, and naming is clear and idiomatic. There’s no linter configuration and no generics/modern type-safety features, consistent with a package last touched before those became standard, but the -race flag in CI is a meaningful quality signal for concurrency-focused code.

What Makes It Unique Leaktest doesn’t invent a new detection technique — it’s a direct, tidied-up extraction of the leak-checking pattern already used internally by Go’s own net/http tests and by CockroachDB, repackaged so other projects can import it directly instead of copy-pasting the pattern. Its main design contributions are the poll-with-timeout loop (versus a single point-in-time diff) and the explicit, hand-maintained exclusion list for runtime/testing/HTTP-internal goroutines. It has been inactive since 2020 and predates more actively maintained successors in the same space (e.g. go.uber.org/goleak), so it’s a solid, narrowly-scoped utility rather than a novel one.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search