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

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

View as plain text