blob: 3ea92b38cc1400d3c4a4b561d077e3069ee8786d [file] [log] [blame]
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package checker_test
import (
"flag"
"io/ioutil"
"os"
"os/exec"
"path"
"regexp"
"runtime"
"testing"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/analysistest"
"golang.org/x/tools/go/analysis/internal/checker"
"golang.org/x/tools/internal/testenv"
)
func main() {
checker.Fix = true
patterns := flag.Args()
code := checker.Run(patterns, []*analysis.Analyzer{analyzer, other})
os.Exit(code)
}
// TestFixes ensures that checker.Run applies fixes correctly.
// This test fork/execs the main function above.
func TestFixes(t *testing.T) {
oses := map[string]bool{"darwin": true, "linux": true}
if !oses[runtime.GOOS] {
t.Skipf("skipping fork/exec test on this platform")
}
if os.Getenv("TESTFIXES_CHILD") == "1" {
// child process
// replace [progname -test.run=TestFixes -- ...]
// by [progname ...]
os.Args = os.Args[2:]
os.Args[0] = "vet"
main()
panic("unreachable")
}
testenv.NeedsTool(t, "go")
files := map[string]string{
"rename/foo.go": `package rename
func Foo() {
bar := 12
_ = bar
}
// the end
`,
"rename/intestfile_test.go": `package rename
func InTestFile() {
bar := 13
_ = bar
}
// the end
`,
"rename/foo_test.go": `package rename_test
func Foo() {
bar := 14
_ = bar
}
// the end
`,
"duplicate/dup.go": `package duplicate
func Foo() {
bar := 14
_ = bar
}
// the end
`,
}
fixed := map[string]string{
"rename/foo.go": `package rename
func Foo() {
baz := 12
_ = baz
}
// the end
`,
"rename/intestfile_test.go": `package rename
func InTestFile() {
baz := 13
_ = baz
}
// the end
`,
"rename/foo_test.go": `package rename_test
func Foo() {
baz := 14
_ = baz
}
// the end
`,
"duplicate/dup.go": `package duplicate
func Foo() {
baz := 14
_ = baz
}
// the end
`,
}
dir, cleanup, err := analysistest.WriteFiles(files)
if err != nil {
t.Fatalf("Creating test files failed with %s", err)
}
defer cleanup()
args := []string{"-test.run=TestFixes", "--", "rename", "duplicate"}
cmd := exec.Command(os.Args[0], args...)
cmd.Env = append(os.Environ(), "TESTFIXES_CHILD=1", "GOPATH="+dir, "GO111MODULE=off", "GOPROXY=off")
out, err := cmd.CombinedOutput()
if len(out) > 0 {
t.Logf("%s: out=<<%s>>", args, out)
}
var exitcode int
if err, ok := err.(*exec.ExitError); ok {
exitcode = err.ExitCode() // requires go1.12
}
const diagnosticsExitCode = 3
if exitcode != diagnosticsExitCode {
t.Errorf("%s: exited %d, want %d", args, exitcode, diagnosticsExitCode)
}
for name, want := range fixed {
path := path.Join(dir, "src", name)
contents, err := ioutil.ReadFile(path)
if err != nil {
t.Errorf("error reading %s: %v", path, err)
}
if got := string(contents); got != want {
t.Errorf("contents of %s file did not match expectations. got=%s, want=%s", path, got, want)
}
}
}
// TestConflict ensures that checker.Run detects conflicts correctly.
// This test fork/execs the main function above.
func TestConflict(t *testing.T) {
oses := map[string]bool{"darwin": true, "linux": true}
if !oses[runtime.GOOS] {
t.Skipf("skipping fork/exec test on this platform")
}
if os.Getenv("TESTCONFLICT_CHILD") == "1" {
// child process
// replace [progname -test.run=TestConflict -- ...]
// by [progname ...]
os.Args = os.Args[2:]
os.Args[0] = "vet"
main()
panic("unreachable")
}
testenv.NeedsTool(t, "go")
files := map[string]string{
"conflict/foo.go": `package conflict
func Foo() {
bar := 12
_ = bar
}
// the end
`,
}
dir, cleanup, err := analysistest.WriteFiles(files)
if err != nil {
t.Fatalf("Creating test files failed with %s", err)
}
defer cleanup()
args := []string{"-test.run=TestConflict", "--", "conflict"}
cmd := exec.Command(os.Args[0], args...)
cmd.Env = append(os.Environ(), "TESTCONFLICT_CHILD=1", "GOPATH="+dir, "GO111MODULE=off", "GOPROXY=off")
out, err := cmd.CombinedOutput()
var exitcode int
if err, ok := err.(*exec.ExitError); ok {
exitcode = err.ExitCode() // requires go1.12
}
const errExitCode = 1
if exitcode != errExitCode {
t.Errorf("%s: exited %d, want %d", args, exitcode, errExitCode)
}
pattern := `conflicting edits from rename and rename on /.*/conflict/foo.go`
matched, err := regexp.Match(pattern, out)
if err != nil {
t.Errorf("error matching pattern %s: %v", pattern, err)
} else if !matched {
t.Errorf("%s: output was=<<%s>>. Expected it to match <<%s>>", args, out, pattern)
}
// No files updated
for name, want := range files {
path := path.Join(dir, "src", name)
contents, err := ioutil.ReadFile(path)
if err != nil {
t.Errorf("error reading %s: %v", path, err)
}
if got := string(contents); got != want {
t.Errorf("contents of %s file updated. got=%s, want=%s", path, got, want)
}
}
}
// TestOther ensures that checker.Run reports conflicts from
// distinct actions correctly.
// This test fork/execs the main function above.
func TestOther(t *testing.T) {
oses := map[string]bool{"darwin": true, "linux": true}
if !oses[runtime.GOOS] {
t.Skipf("skipping fork/exec test on this platform")
}
if os.Getenv("TESTOTHER_CHILD") == "1" {
// child process
// replace [progname -test.run=TestOther -- ...]
// by [progname ...]
os.Args = os.Args[2:]
os.Args[0] = "vet"
main()
panic("unreachable")
}
testenv.NeedsTool(t, "go")
files := map[string]string{
"other/foo.go": `package other
func Foo() {
bar := 12
_ = bar
}
// the end
`,
}
dir, cleanup, err := analysistest.WriteFiles(files)
if err != nil {
t.Fatalf("Creating test files failed with %s", err)
}
defer cleanup()
args := []string{"-test.run=TestOther", "--", "other"}
cmd := exec.Command(os.Args[0], args...)
cmd.Env = append(os.Environ(), "TESTOTHER_CHILD=1", "GOPATH="+dir, "GO111MODULE=off", "GOPROXY=off")
out, err := cmd.CombinedOutput()
var exitcode int
if err, ok := err.(*exec.ExitError); ok {
exitcode = err.ExitCode() // requires go1.12
}
const errExitCode = 1
if exitcode != errExitCode {
t.Errorf("%s: exited %d, want %d", args, exitcode, errExitCode)
}
pattern := `conflicting edits from other and rename on /.*/other/foo.go`
matched, err := regexp.Match(pattern, out)
if err != nil {
t.Errorf("error matching pattern %s: %v", pattern, err)
} else if !matched {
t.Errorf("%s: output was=<<%s>>. Expected it to match <<%s>>", args, out, pattern)
}
// No files updated
for name, want := range files {
path := path.Join(dir, "src", name)
contents, err := ioutil.ReadFile(path)
if err != nil {
t.Errorf("error reading %s: %v", path, err)
}
if got := string(contents); got != want {
t.Errorf("contents of %s file updated. got=%s, want=%s", path, got, want)
}
}
}