Coverage Instrumentation and Fuzz Engine
Coverage Instrumentation and Fuzz Engine
Overview
go test is not only assert runners — it can instrument binaries for coverage and drive a fuzzing engine that mutates inputs to hit new edges.
Diagram: Fuzz loop
seed corpus
│
v
mutate → run FuzzXxx → new coverage?
│ │
│ yes → keep input
│ │
└── crash/hang ──► testdata/fuzz/...
Coverage
go test -cover ./...
go test -coverprofile=c.out ./...
go tool cover -html=c.out
go test -covermode=atomic ./... # concurrent-safe countsIntegration-style:
go build -cover -o app .
GOCOVERDIR=covdata ./app # exercise
go tool covdata textfmt -i=covdata -o=c.outUse coverage to find untested error paths, not as a vanity percentage.
Fuzz Engine Model
seed corpus
-> mutate
-> execute FuzzXxx
-> instrumentation: new coverage edge?
yes -> keep interesting input
-> crash/hang -> fail and write testdata/fuzz/...
go test -fuzz=FuzzParse -fuzztime=30sCommit failing inputs under testdata/fuzz for regression.
Writing Good Fuzz Targets
- Round-trip properties (
decode(encode(x))) - Invariants (no panic, bounds)
- Avoid non-determinism and network
- Keep work per input small
Coordination With Race
go test -race -fuzz=FuzzX -fuzztime=10sHeavier but catches races fuzz alone may miss.
Experiment
mkdir /tmp/fuzzdemo && cd /tmp/fuzzdemo
go mod init example// reverse.go
package example
func Reverse(s string) string {
r := []rune(s)
for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
r[i], r[j] = r[j], r[i]
}
return string(r)
}// reverse_test.go
package example
import "testing"
func FuzzReverse(f *testing.F) {
f.Add("gopher")
f.Fuzz(func(t *testing.T, s string) {
if Reverse(Reverse(s)) != s {
t.Fatalf("%q", s)
}
})
}go test -fuzz=FuzzReverse -fuzztime=5s -coverWhat to notice: Fuzz spends time exploring UTF-8 edges; coverage rises as new branches hit.
Try next: Fuzz a parser you own; keep the first crash input as a unit test.