...

Text file src/cmd/go/testdata/script/test_fuzz_mutator.txt

Documentation: cmd/go/testdata/script

     1[!fuzz] skip
     2
     3# Test basic fuzzing mutator behavior.
     4#
     5# fuzz_test.go has two fuzz targets (FuzzA, FuzzB) which both add a seed value.
     6# Each fuzz function writes the input to a log file. The coordinator and worker
     7# use separate log files. check_logs.go verifies that the coordinator only
     8# tests seed values and the worker tests mutated values on the fuzz target.
     9
    10[short] skip
    11env GOCACHE=$WORK/cache
    12
    13go test -fuzz=FuzzA -fuzztime=100x -parallel=1 -log=fuzz
    14go run check_logs.go fuzz fuzz.worker
    15
    16# TODO(b/181800488): remove -parallel=1, here and below. For now, when a
    17# crash is found, all workers keep running, wasting resources and reducing
    18# the number of executions available to the minimizer, increasing flakiness.
    19
    20# Test that the mutator is good enough to find several unique mutations.
    21! go test -fuzz=FuzzMutator -parallel=1 -fuzztime=100x mutator_test.go
    22! stdout '^ok'
    23stdout FAIL
    24stdout 'mutator found enough unique mutations'
    25
    26-- go.mod --
    27module m
    28
    29go 1.16
    30-- fuzz_test.go --
    31package fuzz_test
    32
    33import (
    34	"flag"
    35	"fmt"
    36	"os"
    37	"testing"
    38)
    39
    40var (
    41	logPath = flag.String("log", "", "path to log file")
    42	logFile *os.File
    43)
    44
    45func TestMain(m *testing.M) {
    46	flag.Parse()
    47	var err error
    48	logFile, err = os.OpenFile(*logPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
    49	if os.IsExist(err) {
    50		*logPath += ".worker"
    51		logFile, err = os.OpenFile(*logPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
    52	}
    53	if err != nil {
    54		fmt.Fprintln(os.Stderr, err)
    55		os.Exit(1)
    56	}
    57	os.Exit(m.Run())
    58}
    59
    60func FuzzA(f *testing.F) {
    61	f.Add([]byte("seed"))
    62	f.Fuzz(func(t *testing.T, b []byte) {
    63		fmt.Fprintf(logFile, "FuzzA %q\n", b)
    64	})
    65}
    66
    67func FuzzB(f *testing.F) {
    68	f.Add([]byte("seed"))
    69	f.Fuzz(func(t *testing.T, b []byte) {
    70		fmt.Fprintf(logFile, "FuzzB %q\n", b)
    71	})
    72}
    73
    74-- check_logs.go --
    75// +build ignore
    76
    77package main
    78
    79import (
    80	"bufio"
    81	"bytes"
    82	"fmt"
    83	"io"
    84	"os"
    85	"strings"
    86)
    87
    88func main() {
    89	coordPath, workerPath := os.Args[1], os.Args[2]
    90
    91	coordLog, err := os.Open(coordPath)
    92	if err != nil {
    93		fmt.Fprintln(os.Stderr, err)
    94		os.Exit(1)
    95	}
    96	defer coordLog.Close()
    97	if err := checkCoordLog(coordLog); err != nil {
    98		fmt.Fprintln(os.Stderr, err)
    99		os.Exit(1)
   100	}
   101
   102	workerLog, err := os.Open(workerPath)
   103	if err != nil {
   104		fmt.Fprintln(os.Stderr, err)
   105		os.Exit(1)
   106	}
   107	defer workerLog.Close()
   108	if err := checkWorkerLog(workerLog); err != nil {
   109		fmt.Fprintln(os.Stderr, err)
   110		os.Exit(1)
   111	}
   112}
   113
   114func checkCoordLog(r io.Reader) error {
   115	b, err := io.ReadAll(r)
   116	if err != nil {
   117		return err
   118	}
   119	if string(bytes.TrimSpace(b)) != `FuzzB "seed"` {
   120		return fmt.Errorf("coordinator: did not test FuzzB seed")
   121	}
   122	return nil
   123}
   124
   125func checkWorkerLog(r io.Reader) error {
   126	scan := bufio.NewScanner(r)
   127	var sawAMutant bool
   128	for scan.Scan() {
   129		line := scan.Text()
   130		if !strings.HasPrefix(line, "FuzzA ") {
   131			return fmt.Errorf("worker: tested something other than target: %s", line)
   132		}
   133		if strings.TrimPrefix(line, "FuzzA ") != `"seed"` {
   134			sawAMutant = true
   135		}
   136	}
   137	if err := scan.Err(); err != nil && err != bufio.ErrTooLong {
   138		return err
   139	}
   140	if !sawAMutant {
   141		return fmt.Errorf("worker: did not test any mutants")
   142	}
   143	return nil
   144}
   145-- mutator_test.go --
   146package fuzz_test
   147
   148import (
   149	"testing"
   150)
   151
   152// TODO(katiehockman): re-work this test once we have a better fuzzing engine
   153// (ie. more mutations, and compiler instrumentation)
   154func FuzzMutator(f *testing.F) {
   155	// TODO(katiehockman): simplify this once we can dedupe crashes (e.g.
   156	// replace map with calls to panic, and simply count the number of crashes
   157	// that were added to testdata)
   158	crashes := make(map[string]bool)
   159	// No seed corpus initiated
   160	f.Fuzz(func(t *testing.T, b []byte) {
   161		crashes[string(b)] = true
   162		if len(crashes) >= 10 {
   163			panic("mutator found enough unique mutations")
   164		}
   165	})
   166}

View as plain text