github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/go/analysis/unitchecker/unitchecker_test.go (about)

     1  // Copyright 2018 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  //go:build go1.12
     6  // +build go1.12
     7  
     8  package unitchecker_test
     9  
    10  // This test depends on features such as
    11  // go vet's support for vetx files (1.11) and
    12  // the (*os.ProcessState).ExitCode method (1.12).
    13  
    14  import (
    15  	"flag"
    16  	"os"
    17  	"os/exec"
    18  	"regexp"
    19  	"runtime"
    20  	"strings"
    21  	"testing"
    22  
    23  	"golang.org/x/tools/go/analysis/passes/assign"
    24  	"golang.org/x/tools/go/analysis/passes/findcall"
    25  	"golang.org/x/tools/go/analysis/passes/printf"
    26  	"golang.org/x/tools/go/analysis/unitchecker"
    27  	"golang.org/x/tools/go/packages/packagestest"
    28  )
    29  
    30  func TestMain(m *testing.M) {
    31  	if os.Getenv("UNITCHECKER_CHILD") == "1" {
    32  		// child process
    33  		main()
    34  		panic("unreachable")
    35  	}
    36  
    37  	flag.Parse()
    38  	os.Exit(m.Run())
    39  }
    40  
    41  func main() {
    42  	unitchecker.Main(
    43  		findcall.Analyzer,
    44  		printf.Analyzer,
    45  		assign.Analyzer,
    46  	)
    47  }
    48  
    49  // This is a very basic integration test of modular
    50  // analysis with facts using unitchecker under "go vet".
    51  // It fork/execs the main function above.
    52  func TestIntegration(t *testing.T) { packagestest.TestAll(t, testIntegration) }
    53  func testIntegration(t *testing.T, exporter packagestest.Exporter) {
    54  	if runtime.GOOS != "linux" && runtime.GOOS != "darwin" {
    55  		t.Skipf("skipping fork/exec test on this platform")
    56  	}
    57  
    58  	exported := packagestest.Export(t, exporter, []packagestest.Module{{
    59  		Name: "golang.org/fake",
    60  		Files: map[string]interface{}{
    61  			"a/a.go": `package a
    62  
    63  func _() {
    64  	MyFunc123()
    65  }
    66  
    67  func MyFunc123() {}
    68  `,
    69  			"b/b.go": `package b
    70  
    71  import "golang.org/fake/a"
    72  
    73  func _() {
    74  	a.MyFunc123()
    75  	MyFunc123()
    76  }
    77  
    78  func MyFunc123() {}
    79  `,
    80  			"c/c.go": `package c
    81  
    82  func _() {
    83      i := 5
    84      i = i
    85  }
    86  `,
    87  		}}})
    88  	defer exported.Cleanup()
    89  
    90  	const wantA = `# golang.org/fake/a
    91  ([/._\-a-zA-Z0-9]+[\\/]fake[\\/])?a/a.go:4:11: call of MyFunc123\(...\)
    92  `
    93  	const wantB = `# golang.org/fake/b
    94  ([/._\-a-zA-Z0-9]+[\\/]fake[\\/])?b/b.go:6:13: call of MyFunc123\(...\)
    95  ([/._\-a-zA-Z0-9]+[\\/]fake[\\/])?b/b.go:7:11: call of MyFunc123\(...\)
    96  `
    97  	const wantC = `# golang.org/fake/c
    98  ([/._\-a-zA-Z0-9]+[\\/]fake[\\/])?c/c.go:5:5: self-assignment of i to i
    99  `
   100  	const wantAJSON = `# golang.org/fake/a
   101  \{
   102  	"golang.org/fake/a": \{
   103  		"findcall": \[
   104  			\{
   105  				"posn": "([/._\-a-zA-Z0-9]+[\\/]fake[\\/])?a/a.go:4:11",
   106  				"message": "call of MyFunc123\(...\)",
   107  				"suggested_fixes": \[
   108  					\{
   109  						"message": "Add '_TEST_'",
   110  						"edits": \[
   111  							\{
   112  								"filename": "([/._\-a-zA-Z0-9]+[\\/]fake[\\/])?a/a.go",
   113  								"start": 32,
   114  								"end": 32,
   115  								"new": "_TEST_"
   116  							\}
   117  						\]
   118  					\}
   119  				\]
   120  			\}
   121  		\]
   122  	\}
   123  \}
   124  `
   125  	const wantCJSON = `# golang.org/fake/c
   126  \{
   127  	"golang.org/fake/c": \{
   128  		"assign": \[
   129  			\{
   130  				"posn": "([/._\-a-zA-Z0-9]+[\\/]fake[\\/])?c/c.go:5:5",
   131  				"message": "self-assignment of i to i",
   132  				"suggested_fixes": \[
   133  					\{
   134  						"message": "Remove",
   135  						"edits": \[
   136  							\{
   137  								"filename": "([/._\-a-zA-Z0-9]+[\\/]fake[\\/])?c/c.go",
   138  								"start": 37,
   139  								"end": 42,
   140  								"new": ""
   141  							\}
   142  						\]
   143  					\}
   144  				\]
   145  			\}
   146  		\]
   147  	\}
   148  \}
   149  `
   150  	for _, test := range []struct {
   151  		args          string
   152  		wantOut       string
   153  		wantExitError bool
   154  	}{
   155  		{args: "golang.org/fake/a", wantOut: wantA, wantExitError: true},
   156  		{args: "golang.org/fake/b", wantOut: wantB, wantExitError: true},
   157  		{args: "golang.org/fake/c", wantOut: wantC, wantExitError: true},
   158  		{args: "golang.org/fake/a golang.org/fake/b", wantOut: wantA + wantB, wantExitError: true},
   159  		{args: "-json golang.org/fake/a", wantOut: wantAJSON, wantExitError: false},
   160  		{args: "-json golang.org/fake/c", wantOut: wantCJSON, wantExitError: false},
   161  		{args: "-c=0 golang.org/fake/a", wantOut: wantA + "4		MyFunc123\\(\\)\n", wantExitError: true},
   162  	} {
   163  		cmd := exec.Command("go", "vet", "-vettool="+os.Args[0], "-findcall.name=MyFunc123")
   164  		cmd.Args = append(cmd.Args, strings.Fields(test.args)...)
   165  		cmd.Env = append(exported.Config.Env, "UNITCHECKER_CHILD=1")
   166  		cmd.Dir = exported.Config.Dir
   167  
   168  		out, err := cmd.CombinedOutput()
   169  		exitcode := 0
   170  		if exitErr, ok := err.(*exec.ExitError); ok {
   171  			exitcode = exitErr.ExitCode()
   172  		}
   173  		if (exitcode != 0) != test.wantExitError {
   174  			want := "zero"
   175  			if test.wantExitError {
   176  				want = "nonzero"
   177  			}
   178  			t.Errorf("%s: got exit code %d, want %s", test.args, exitcode, want)
   179  		}
   180  
   181  		matched, err := regexp.Match(test.wantOut, out)
   182  		if err != nil {
   183  			t.Fatalf("regexp.Match(<<%s>>): %v", test.wantOut, err)
   184  		}
   185  		if !matched {
   186  			t.Errorf("%s: got <<%s>>, want match of regexp <<%s>>", test.args, out, test.wantOut)
   187  		}
   188  	}
   189  }