github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/go/analysis/internal/checker/fix_test.go (about)

     1  // Copyright 2022 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 checker_test
     6  
     7  import (
     8  	"flag"
     9  	"io/ioutil"
    10  	"os"
    11  	"os/exec"
    12  	"path"
    13  	"regexp"
    14  	"runtime"
    15  	"testing"
    16  
    17  	"golang.org/x/tools/go/analysis"
    18  	"golang.org/x/tools/go/analysis/analysistest"
    19  	"golang.org/x/tools/go/analysis/internal/checker"
    20  	"golang.org/x/tools/internal/testenv"
    21  )
    22  
    23  func main() {
    24  	checker.Fix = true
    25  	patterns := flag.Args()
    26  
    27  	code := checker.Run(patterns, []*analysis.Analyzer{analyzer, other})
    28  	os.Exit(code)
    29  }
    30  
    31  // TestFixes ensures that checker.Run applies fixes correctly.
    32  // This test fork/execs the main function above.
    33  func TestFixes(t *testing.T) {
    34  	oses := map[string]bool{"darwin": true, "linux": true}
    35  	if !oses[runtime.GOOS] {
    36  		t.Skipf("skipping fork/exec test on this platform")
    37  	}
    38  
    39  	if os.Getenv("TESTFIXES_CHILD") == "1" {
    40  		// child process
    41  
    42  		// replace [progname -test.run=TestFixes -- ...]
    43  		//      by [progname ...]
    44  		os.Args = os.Args[2:]
    45  		os.Args[0] = "vet"
    46  		main()
    47  		panic("unreachable")
    48  	}
    49  
    50  	testenv.NeedsTool(t, "go")
    51  
    52  	files := map[string]string{
    53  		"rename/foo.go": `package rename
    54  
    55  func Foo() {
    56  	bar := 12
    57  	_ = bar
    58  }
    59  
    60  // the end
    61  `,
    62  		"rename/intestfile_test.go": `package rename
    63  
    64  func InTestFile() {
    65  	bar := 13
    66  	_ = bar
    67  }
    68  
    69  // the end
    70  `,
    71  		"rename/foo_test.go": `package rename_test
    72  
    73  func Foo() {
    74  	bar := 14
    75  	_ = bar
    76  }
    77  
    78  // the end
    79  `,
    80  		"duplicate/dup.go": `package duplicate
    81  
    82  func Foo() {
    83  	bar := 14
    84  	_ = bar
    85  }
    86  
    87  // the end
    88  `,
    89  	}
    90  	fixed := map[string]string{
    91  		"rename/foo.go": `package rename
    92  
    93  func Foo() {
    94  	baz := 12
    95  	_ = baz
    96  }
    97  
    98  // the end
    99  `,
   100  		"rename/intestfile_test.go": `package rename
   101  
   102  func InTestFile() {
   103  	baz := 13
   104  	_ = baz
   105  }
   106  
   107  // the end
   108  `,
   109  		"rename/foo_test.go": `package rename_test
   110  
   111  func Foo() {
   112  	baz := 14
   113  	_ = baz
   114  }
   115  
   116  // the end
   117  `,
   118  		"duplicate/dup.go": `package duplicate
   119  
   120  func Foo() {
   121  	baz := 14
   122  	_ = baz
   123  }
   124  
   125  // the end
   126  `,
   127  	}
   128  	dir, cleanup, err := analysistest.WriteFiles(files)
   129  	if err != nil {
   130  		t.Fatalf("Creating test files failed with %s", err)
   131  	}
   132  	defer cleanup()
   133  
   134  	args := []string{"-test.run=TestFixes", "--", "rename", "duplicate"}
   135  	cmd := exec.Command(os.Args[0], args...)
   136  	cmd.Env = append(os.Environ(), "TESTFIXES_CHILD=1", "GOPATH="+dir, "GO111MODULE=off", "GOPROXY=off")
   137  
   138  	out, err := cmd.CombinedOutput()
   139  	if len(out) > 0 {
   140  		t.Logf("%s: out=<<%s>>", args, out)
   141  	}
   142  	var exitcode int
   143  	if err, ok := err.(*exec.ExitError); ok {
   144  		exitcode = err.ExitCode() // requires go1.12
   145  	}
   146  
   147  	const diagnosticsExitCode = 3
   148  	if exitcode != diagnosticsExitCode {
   149  		t.Errorf("%s: exited %d, want %d", args, exitcode, diagnosticsExitCode)
   150  	}
   151  
   152  	for name, want := range fixed {
   153  		path := path.Join(dir, "src", name)
   154  		contents, err := ioutil.ReadFile(path)
   155  		if err != nil {
   156  			t.Errorf("error reading %s: %v", path, err)
   157  		}
   158  		if got := string(contents); got != want {
   159  			t.Errorf("contents of %s file did not match expectations. got=%s, want=%s", path, got, want)
   160  		}
   161  	}
   162  }
   163  
   164  // TestConflict ensures that checker.Run detects conflicts correctly.
   165  // This test fork/execs the main function above.
   166  func TestConflict(t *testing.T) {
   167  	oses := map[string]bool{"darwin": true, "linux": true}
   168  	if !oses[runtime.GOOS] {
   169  		t.Skipf("skipping fork/exec test on this platform")
   170  	}
   171  
   172  	if os.Getenv("TESTCONFLICT_CHILD") == "1" {
   173  		// child process
   174  
   175  		// replace [progname -test.run=TestConflict -- ...]
   176  		//      by [progname ...]
   177  		os.Args = os.Args[2:]
   178  		os.Args[0] = "vet"
   179  		main()
   180  		panic("unreachable")
   181  	}
   182  
   183  	testenv.NeedsTool(t, "go")
   184  
   185  	files := map[string]string{
   186  		"conflict/foo.go": `package conflict
   187  
   188  func Foo() {
   189  	bar := 12
   190  	_ = bar
   191  }
   192  
   193  // the end
   194  `,
   195  	}
   196  	dir, cleanup, err := analysistest.WriteFiles(files)
   197  	if err != nil {
   198  		t.Fatalf("Creating test files failed with %s", err)
   199  	}
   200  	defer cleanup()
   201  
   202  	args := []string{"-test.run=TestConflict", "--", "conflict"}
   203  	cmd := exec.Command(os.Args[0], args...)
   204  	cmd.Env = append(os.Environ(), "TESTCONFLICT_CHILD=1", "GOPATH="+dir, "GO111MODULE=off", "GOPROXY=off")
   205  
   206  	out, err := cmd.CombinedOutput()
   207  	var exitcode int
   208  	if err, ok := err.(*exec.ExitError); ok {
   209  		exitcode = err.ExitCode() // requires go1.12
   210  	}
   211  	const errExitCode = 1
   212  	if exitcode != errExitCode {
   213  		t.Errorf("%s: exited %d, want %d", args, exitcode, errExitCode)
   214  	}
   215  
   216  	pattern := `conflicting edits from rename and rename on /.*/conflict/foo.go`
   217  	matched, err := regexp.Match(pattern, out)
   218  	if err != nil {
   219  		t.Errorf("error matching pattern %s: %v", pattern, err)
   220  	} else if !matched {
   221  		t.Errorf("%s: output was=<<%s>>. Expected it to match <<%s>>", args, out, pattern)
   222  	}
   223  
   224  	// No files updated
   225  	for name, want := range files {
   226  		path := path.Join(dir, "src", name)
   227  		contents, err := ioutil.ReadFile(path)
   228  		if err != nil {
   229  			t.Errorf("error reading %s: %v", path, err)
   230  		}
   231  		if got := string(contents); got != want {
   232  			t.Errorf("contents of %s file updated. got=%s, want=%s", path, got, want)
   233  		}
   234  	}
   235  }
   236  
   237  // TestOther ensures that checker.Run reports conflicts from
   238  // distinct actions correctly.
   239  // This test fork/execs the main function above.
   240  func TestOther(t *testing.T) {
   241  	oses := map[string]bool{"darwin": true, "linux": true}
   242  	if !oses[runtime.GOOS] {
   243  		t.Skipf("skipping fork/exec test on this platform")
   244  	}
   245  
   246  	if os.Getenv("TESTOTHER_CHILD") == "1" {
   247  		// child process
   248  
   249  		// replace [progname -test.run=TestOther -- ...]
   250  		//      by [progname ...]
   251  		os.Args = os.Args[2:]
   252  		os.Args[0] = "vet"
   253  		main()
   254  		panic("unreachable")
   255  	}
   256  
   257  	testenv.NeedsTool(t, "go")
   258  
   259  	files := map[string]string{
   260  		"other/foo.go": `package other
   261  
   262  func Foo() {
   263  	bar := 12
   264  	_ = bar
   265  }
   266  
   267  // the end
   268  `,
   269  	}
   270  	dir, cleanup, err := analysistest.WriteFiles(files)
   271  	if err != nil {
   272  		t.Fatalf("Creating test files failed with %s", err)
   273  	}
   274  	defer cleanup()
   275  
   276  	args := []string{"-test.run=TestOther", "--", "other"}
   277  	cmd := exec.Command(os.Args[0], args...)
   278  	cmd.Env = append(os.Environ(), "TESTOTHER_CHILD=1", "GOPATH="+dir, "GO111MODULE=off", "GOPROXY=off")
   279  
   280  	out, err := cmd.CombinedOutput()
   281  	var exitcode int
   282  	if err, ok := err.(*exec.ExitError); ok {
   283  		exitcode = err.ExitCode() // requires go1.12
   284  	}
   285  	const errExitCode = 1
   286  	if exitcode != errExitCode {
   287  		t.Errorf("%s: exited %d, want %d", args, exitcode, errExitCode)
   288  	}
   289  
   290  	pattern := `conflicting edits from other and rename on /.*/other/foo.go`
   291  	matched, err := regexp.Match(pattern, out)
   292  	if err != nil {
   293  		t.Errorf("error matching pattern %s: %v", pattern, err)
   294  	} else if !matched {
   295  		t.Errorf("%s: output was=<<%s>>. Expected it to match <<%s>>", args, out, pattern)
   296  	}
   297  
   298  	// No files updated
   299  	for name, want := range files {
   300  		path := path.Join(dir, "src", name)
   301  		contents, err := ioutil.ReadFile(path)
   302  		if err != nil {
   303  			t.Errorf("error reading %s: %v", path, err)
   304  		}
   305  		if got := string(contents); got != want {
   306  			t.Errorf("contents of %s file updated. got=%s, want=%s", path, got, want)
   307  		}
   308  	}
   309  }