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 }