github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/cmd/signature-fuzzer/fuzz-runner/rnr_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 main 6 7 import ( 8 "fmt" 9 "os" 10 "os/exec" 11 "path/filepath" 12 "runtime" 13 "strings" 14 "testing" 15 16 "golang.org/x/tools/internal/testenv" 17 ) 18 19 func canRace(t *testing.T) bool { 20 _, err := exec.Command("go", "run", "-race", "./testdata/himom.go").CombinedOutput() 21 return err == nil 22 } 23 24 // buildRunner builds the fuzz-runner executable, returning its path. 25 func buildRunner(t *testing.T) string { 26 bindir := filepath.Join(t.TempDir(), "bin") 27 err := os.Mkdir(bindir, os.ModePerm) 28 if err != nil { 29 t.Fatal(err) 30 } 31 binary := filepath.Join(bindir, "runner") 32 if runtime.GOOS == "windows" { 33 binary += ".exe" 34 } 35 cmd := exec.Command("go", "build", "-o", binary) 36 if err := cmd.Run(); err != nil { 37 t.Fatalf("Building fuzz-runner: %v", err) 38 } 39 return binary 40 } 41 42 // TestRunner builds the binary, then kicks off a collection of sub-tests that invoke it. 43 func TestRunner(t *testing.T) { 44 testenv.NeedsTool(t, "go") 45 if runtime.GOOS == "android" { 46 t.Skipf("the dependencies are not available on android") 47 } 48 binaryPath := buildRunner(t) 49 50 // Sub-tests using the binary built above. 51 t.Run("Basic", func(t *testing.T) { testBasic(t, binaryPath) }) 52 t.Run("Race", func(t *testing.T) { testRace(t, binaryPath) }) 53 t.Run("Minimization1", func(t *testing.T) { testMinimization1(t, binaryPath) }) 54 t.Run("Minimization2", func(t *testing.T) { testMinimization2(t, binaryPath) }) 55 } 56 57 func testBasic(t *testing.T, binaryPath string) { 58 t.Parallel() 59 args := []string{"-numit=1", "-numfcns=1", "-numpkgs=1", "-seed=103", "-cleancache=0"} 60 c := exec.Command(binaryPath, args...) 61 b, err := c.CombinedOutput() 62 t.Logf("%s\n", b) 63 if err != nil { 64 t.Fatalf("error invoking fuzz-runner: %v", err) 65 } 66 } 67 68 func testRace(t *testing.T, binaryPath string) { 69 t.Parallel() 70 // For this test to work, the current test platform has to support the 71 // race detector. Check to see if that is the case by running a very 72 // simple Go program through it. 73 if !canRace(t) { 74 t.Skip("current platform does not appear to support the race detector") 75 } 76 77 args := []string{"-v=1", "-numit=1", "-race", "-numfcns=3", "-numpkgs=3", "-seed=987", "-cleancache=0"} 78 c := exec.Command(binaryPath, args...) 79 b, err := c.CombinedOutput() 80 t.Logf("%s\n", b) 81 if err != nil { 82 t.Fatalf("error invoking fuzz-runner: %v", err) 83 } 84 } 85 86 func testMinimization1(t *testing.T, binaryPath string) { 87 if binaryPath == "" { 88 t.Skipf("No runner binary") 89 } 90 t.Parallel() 91 // Fire off the runner passing it -emitbad=1, so that the generated code 92 // contains illegal Go code (which will force the build to fail). Verify that 93 // it does fail, that the error reflects the nature of the failure, and that 94 // we can minimize the error down to a single package. 95 args := []string{"-emitbad=1", "-badfcnidx=2", "-badpkgidx=2", 96 "-forcetmpclean", "-cleancache=0", 97 "-numit=1", "-numfcns=3", "-numpkgs=3", "-seed=909"} 98 invocation := fmt.Sprintf("%s %v", binaryPath, args) 99 c := exec.Command(binaryPath, args...) 100 b, err := c.CombinedOutput() 101 t.Logf("%s\n", b) 102 if err == nil { 103 t.Fatalf("unexpected pass of fuzz-runner (invocation %q): %v", invocation, err) 104 } 105 result := string(b) 106 if !strings.Contains(result, "syntax error") { 107 t.Fatalf("-emitbad=1 did not trigger syntax error (invocation %q): output: %s", invocation, result) 108 } 109 if !strings.Contains(result, "package minimization succeeded: found bad pkg 2") { 110 t.Fatalf("failed to minimize package (invocation %q): output: %s", invocation, result) 111 } 112 if !strings.Contains(result, "function minimization succeeded: found bad fcn 2") { 113 t.Fatalf("failed to minimize package (invocation %q): output: %s", invocation, result) 114 } 115 } 116 117 func testMinimization2(t *testing.T, binaryPath string) { 118 if binaryPath == "" { 119 t.Skipf("No runner binary") 120 } 121 t.Parallel() 122 // Fire off the runner passing it -emitbad=2, so that the 123 // generated code forces a runtime error. Verify that it does 124 // fail, and that the error is reflective. 125 args := []string{"-emitbad=2", "-badfcnidx=1", "-badpkgidx=1", 126 "-forcetmpclean", "-cleancache=0", 127 "-numit=1", "-numfcns=3", "-numpkgs=3", "-seed=55909"} 128 invocation := fmt.Sprintf("%s %v", binaryPath, args) 129 c := exec.Command(binaryPath, args...) 130 b, err := c.CombinedOutput() 131 t.Logf("%s\n", b) 132 if err == nil { 133 t.Fatalf("unexpected pass of fuzz-runner (invocation %q): %v", invocation, err) 134 } 135 result := string(b) 136 if !strings.Contains(result, "Error: fail") || !strings.Contains(result, "Checker1.Test1") { 137 t.Fatalf("-emitbad=2 did not trigger runtime error (invocation %q): output: %s", invocation, result) 138 } 139 if !strings.Contains(result, "package minimization succeeded: found bad pkg 1") { 140 t.Fatalf("failed to minimize package (invocation %q): output: %s", invocation, result) 141 } 142 if !strings.Contains(result, "function minimization succeeded: found bad fcn 1") { 143 t.Fatalf("failed to minimize package (invocation %q): output: %s", invocation, result) 144 } 145 }