Coverage Instrumentation and Fuzz Engine

Updated

September 8, 2026

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 counts

Integration-style:

go build -cover -o app .
GOCOVERDIR=covdata ./app   # exercise
go tool covdata textfmt -i=covdata -o=c.out

Use 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=30s

Commit failing inputs under testdata/fuzz for regression.

Writing Good Fuzz Targets

  1. Round-trip properties (decode(encode(x)))
  2. Invariants (no panic, bounds)
  3. Avoid non-determinism and network
  4. Keep work per input small

Coordination With Race

go test -race -fuzz=FuzzX -fuzztime=10s

Heavier 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 -cover

What 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.