github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/cmd/signature-fuzzer/internal/fuzz-generator/gen_test.go (about)

     1  // Copyright 2021 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package generator
     6  
     7  import (
     8  	"bytes"
     9  	"os"
    10  	"os/exec"
    11  	"path/filepath"
    12  	"runtime"
    13  	"testing"
    14  
    15  	"golang.org/x/tools/internal/testenv"
    16  )
    17  
    18  func mkGenState() *genstate {
    19  
    20  	return &genstate{
    21  		GenConfig: GenConfig{
    22  			Tag:              "gen",
    23  			OutDir:           "/tmp",
    24  			NumTestPackages:  1,
    25  			NumTestFunctions: 10,
    26  		},
    27  		ipref:       "foo/",
    28  		derefFuncs:  make(map[string]string),
    29  		assignFuncs: make(map[string]string),
    30  		allocFuncs:  make(map[string]string),
    31  		globVars:    make(map[string]string),
    32  	}
    33  }
    34  
    35  func TestBasic(t *testing.T) {
    36  	checkTunables(tunables)
    37  	s := mkGenState()
    38  	for i := 0; i < 1000; i++ {
    39  		s.wr = NewWrapRand(int64(i), RandCtlChecks|RandCtlPanic)
    40  		fp := s.GenFunc(i, i)
    41  		var buf bytes.Buffer
    42  		var b *bytes.Buffer = &buf
    43  		wr := NewWrapRand(int64(i), RandCtlChecks|RandCtlPanic)
    44  		s.wr = wr
    45  		s.emitCaller(fp, b, i)
    46  		s.wr = NewWrapRand(int64(i), RandCtlChecks|RandCtlPanic)
    47  		s.emitChecker(fp, b, i, true)
    48  		wr.Check(s.wr)
    49  	}
    50  	if s.errs != 0 {
    51  		t.Errorf("%d errors during Generate", s.errs)
    52  	}
    53  }
    54  
    55  func TestMoreComplicated(t *testing.T) {
    56  	saveit := tunables
    57  	defer func() { tunables = saveit }()
    58  
    59  	checkTunables(tunables)
    60  	s := mkGenState()
    61  	for i := 0; i < 10000; i++ {
    62  		s.wr = NewWrapRand(int64(i), RandCtlChecks|RandCtlPanic)
    63  		fp := s.GenFunc(i, i)
    64  		var buf bytes.Buffer
    65  		var b *bytes.Buffer = &buf
    66  		wr := NewWrapRand(int64(i), RandCtlChecks|RandCtlPanic)
    67  		s.wr = wr
    68  		s.emitCaller(fp, b, i)
    69  		verb(1, "finished iter %d caller", i)
    70  		s.wr = NewWrapRand(int64(i), RandCtlChecks|RandCtlPanic)
    71  		s.emitChecker(fp, b, i, true)
    72  		verb(1, "finished iter %d checker", i)
    73  		wr.Check(s.wr)
    74  		if s.errs != 0 {
    75  			t.Errorf("%d errors during Generate iter %d", s.errs, i)
    76  		}
    77  	}
    78  }
    79  
    80  func TestIsBuildable(t *testing.T) {
    81  	testenv.NeedsTool(t, "go")
    82  	if runtime.GOOS == "android" {
    83  		t.Skipf("the dependencies are not available on android")
    84  	}
    85  
    86  	td := t.TempDir()
    87  	verb(1, "generating into temp dir %s", td)
    88  	checkTunables(tunables)
    89  	pack := filepath.Base(td)
    90  	s := GenConfig{
    91  		Tag:              "x",
    92  		OutDir:           td,
    93  		PkgPath:          pack,
    94  		NumTestFunctions: 10,
    95  		NumTestPackages:  10,
    96  		MaxFail:          10,
    97  		RandCtl:          RandCtlChecks | RandCtlPanic,
    98  	}
    99  	errs := Generate(s)
   100  	if errs != 0 {
   101  		t.Errorf("%d errors during Generate", errs)
   102  	}
   103  
   104  	verb(1, "building %s\n", td)
   105  
   106  	cmd := exec.Command("go", "run", ".")
   107  	cmd.Dir = td
   108  	coutput, cerr := cmd.CombinedOutput()
   109  	if cerr != nil {
   110  		t.Errorf("go build command failed: %s\n", string(coutput))
   111  	}
   112  	verb(1, "output is: %s\n", string(coutput))
   113  }
   114  
   115  // TestExhaustive does a series of code genreation runs, starting with
   116  // (relatively) simple code and then getting progressively more
   117  // complex (more params, deeper structs, turning on additional
   118  // features such as address-taken vars and reflect testing). The
   119  // intent here is mainly to insure that the tester still works if you
   120  // turn things on and off, e.g. that each feature is separately
   121  // controllable and not linked to other things.
   122  func TestExhaustive(t *testing.T) {
   123  	testenv.NeedsTool(t, "go")
   124  	if runtime.GOOS == "android" {
   125  		t.Skipf("the dependencies are not available on android")
   126  	}
   127  
   128  	if testing.Short() {
   129  		t.Skip("skipping test in short mode.")
   130  	}
   131  
   132  	td := t.TempDir()
   133  	verb(1, "generating into temp dir %s", td)
   134  
   135  	scenarios := []struct {
   136  		name     string
   137  		adjuster func()
   138  	}{
   139  		{
   140  			"minimal",
   141  			func() {
   142  				tunables.nParmRange = 3
   143  				tunables.nReturnRange = 3
   144  				tunables.structDepth = 1
   145  				tunables.recurPerc = 0
   146  				tunables.methodPerc = 0
   147  				tunables.doReflectCall = false
   148  				tunables.doDefer = false
   149  				tunables.takeAddress = false
   150  				tunables.doFuncCallValues = false
   151  				tunables.doSkipCompare = false
   152  				checkTunables(tunables)
   153  			},
   154  		},
   155  		{
   156  			"moreparms",
   157  			func() {
   158  				tunables.nParmRange = 15
   159  				tunables.nReturnRange = 7
   160  				tunables.structDepth = 3
   161  				checkTunables(tunables)
   162  			},
   163  		},
   164  		{
   165  			"addrecur",
   166  			func() {
   167  				tunables.recurPerc = 20
   168  				checkTunables(tunables)
   169  			},
   170  		},
   171  		{
   172  			"addmethod",
   173  			func() {
   174  				tunables.methodPerc = 25
   175  				tunables.pointerMethodCallPerc = 30
   176  				checkTunables(tunables)
   177  			},
   178  		},
   179  		{
   180  			"addtakeaddr",
   181  			func() {
   182  				tunables.takeAddress = true
   183  				tunables.takenFraction = 20
   184  				checkTunables(tunables)
   185  			},
   186  		},
   187  		{
   188  			"addreflect",
   189  			func() {
   190  				tunables.doReflectCall = true
   191  				checkTunables(tunables)
   192  			},
   193  		},
   194  		{
   195  			"adddefer",
   196  			func() {
   197  				tunables.doDefer = true
   198  				checkTunables(tunables)
   199  			},
   200  		},
   201  		{
   202  			"addfuncval",
   203  			func() {
   204  				tunables.doFuncCallValues = true
   205  				checkTunables(tunables)
   206  			},
   207  		},
   208  		{
   209  			"addfuncval",
   210  			func() {
   211  				tunables.doSkipCompare = true
   212  				checkTunables(tunables)
   213  			},
   214  		},
   215  	}
   216  
   217  	// Loop over scenarios and make sure each one works properly.
   218  	for i, s := range scenarios {
   219  		t.Logf("running %s\n", s.name)
   220  		s.adjuster()
   221  		os.RemoveAll(td)
   222  		pack := filepath.Base(td)
   223  		c := GenConfig{
   224  			Tag:              "x",
   225  			OutDir:           td,
   226  			PkgPath:          pack,
   227  			NumTestFunctions: 10,
   228  			NumTestPackages:  10,
   229  			Seed:             int64(i + 9),
   230  			MaxFail:          10,
   231  			RandCtl:          RandCtlChecks | RandCtlPanic,
   232  		}
   233  		errs := Generate(c)
   234  		if errs != 0 {
   235  			t.Errorf("%d errors during scenarios %q Generate", errs, s.name)
   236  		}
   237  		cmd := exec.Command("go", "run", ".")
   238  		cmd.Dir = td
   239  		coutput, cerr := cmd.CombinedOutput()
   240  		if cerr != nil {
   241  			t.Fatalf("run failed for scenario %q:  %s\n", s.name, string(coutput))
   242  		}
   243  		verb(1, "output is: %s\n", string(coutput))
   244  	}
   245  }
   246  
   247  func TestEmitBadBuildFailure(t *testing.T) {
   248  	testenv.NeedsTool(t, "go")
   249  	if runtime.GOOS == "android" {
   250  		t.Skipf("the dependencies are not available on android")
   251  	}
   252  
   253  	td := t.TempDir()
   254  	verb(1, "generating into temp dir %s", td)
   255  
   256  	checkTunables(tunables)
   257  	pack := filepath.Base(td)
   258  	s := GenConfig{
   259  		Tag:              "x",
   260  		OutDir:           td,
   261  		PkgPath:          pack,
   262  		NumTestFunctions: 10,
   263  		NumTestPackages:  10,
   264  		MaxFail:          10,
   265  		RandCtl:          RandCtlChecks | RandCtlPanic,
   266  		EmitBad:          1,
   267  	}
   268  	errs := Generate(s)
   269  	if errs != 0 {
   270  		t.Errorf("%d errors during Generate", errs)
   271  	}
   272  
   273  	cmd := exec.Command("go", "build", ".")
   274  	cmd.Dir = td
   275  	coutput, cerr := cmd.CombinedOutput()
   276  	if cerr == nil {
   277  		t.Errorf("go build command passed, expected failure. output: %s\n", string(coutput))
   278  	}
   279  }
   280  
   281  func TestEmitBadRunFailure(t *testing.T) {
   282  	testenv.NeedsTool(t, "go")
   283  	if runtime.GOOS == "android" {
   284  		t.Skipf("the dependencies are not available on android")
   285  	}
   286  
   287  	td := t.TempDir()
   288  	verb(1, "generating into temp dir %s", td)
   289  
   290  	checkTunables(tunables)
   291  	pack := filepath.Base(td)
   292  	s := GenConfig{
   293  		Tag:              "x",
   294  		OutDir:           td,
   295  		PkgPath:          pack,
   296  		NumTestFunctions: 10,
   297  		NumTestPackages:  10,
   298  		MaxFail:          10,
   299  		RandCtl:          RandCtlChecks | RandCtlPanic,
   300  		EmitBad:          2,
   301  	}
   302  	errs := Generate(s)
   303  	if errs != 0 {
   304  		t.Errorf("%d errors during Generate", errs)
   305  	}
   306  
   307  	// build
   308  	cmd := exec.Command("go", "build", ".")
   309  	cmd.Dir = td
   310  	coutput, cerr := cmd.CombinedOutput()
   311  	if cerr != nil {
   312  		t.Fatalf("build failed: %s\n", string(coutput))
   313  	}
   314  
   315  	// run
   316  	cmd = exec.Command("./" + pack)
   317  	cmd.Dir = td
   318  	coutput, cerr = cmd.CombinedOutput()
   319  	if cerr == nil {
   320  		t.Fatalf("run passed, expected failure -- run output: %s", string(coutput))
   321  	}
   322  }