1 // Copyright 2017 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.
18 "golang.org/x/tools/internal/testenv"
22 offset, from, to string // specify the arguments
23 fileSpecified bool // true if the offset or from args specify a specific file
24 pkgs map[string][]string
26 wantOut string // a substring expected to be in the output
27 packages map[string][]string // a map of the package name to the files contained within, which will be numbered by i.go where i is the index
30 // Test that renaming that would modify cgo files will produce an error and not modify the file.
31 func TestGeneratedFiles(t *testing.T) {
32 testenv.NeedsTool(t, "go")
33 testenv.NeedsTool(t, "cgo")
35 tmp, bin, cleanup := buildGorename(t)
38 srcDir := filepath.Join(tmp, "src")
39 err := os.Mkdir(srcDir, os.ModePerm)
44 var env = []string{fmt.Sprintf("GOPATH=%s", tmp)}
45 for _, envVar := range os.Environ() {
46 if !strings.HasPrefix(envVar, "GOPATH=") {
47 env = append(env, envVar)
50 // gorename currently requires GOPATH mode.
51 env = append(env, "GO111MODULE=off")
53 // Testing renaming in packages that include cgo files:
54 for iter, renameTest := range []test{
56 // Test: variable not used in any cgo file -> no error
57 from: `"mytest"::f`, to: "g",
58 packages: map[string][]string{
59 "mytest": []string{`package mytest; func f() {}`,
64 func z() {C.puts(nil)}`},
67 wantOut: "Renamed 1 occurrence in 1 file in 1 package.",
69 // Test: to name used in cgo file -> rename error
70 from: `"mytest"::f`, to: "g",
71 packages: map[string][]string{
72 "mytest": []string{`package mytest; func f() {}`,
77 func g() {C.puts(nil)}`},
80 wantOut: "conflicts with func in same block",
83 // Test: from name in package in cgo file -> error
84 from: `"mytest"::f`, to: "g",
85 packages: map[string][]string{
86 "mytest": []string{`package mytest
91 func f() { C.puts(nil); }
95 wantOut: "gorename: refusing to modify generated file containing DO NOT EDIT marker:",
97 // Test: from name in cgo file -> error
98 from: filepath.Join("mytest", "0.go") + `::f`, to: "g",
100 packages: map[string][]string{
101 "mytest": []string{`package mytest
103 // #include <stdio.h>
106 func f() { C.puts(nil); }
110 wantOut: "gorename: refusing to modify generated file containing DO NOT EDIT marker:",
112 // Test: offset in cgo file -> identifier in cgo error
113 offset: filepath.Join("main", "0.go") + `:#78`, to: "bar",
116 packages: map[string][]string{
117 "main": {`package main
119 // #include <unistd.h>
130 wantOut: "cannot rename identifiers in generated file containing DO NOT EDIT marker:",
132 // Test: from identifier appears in cgo file in another package -> error
133 from: `"test"::Foo`, to: "Bar",
134 packages: map[string][]string{
138 func Foo(x int) (int){
148 // #include <unistd.h>
160 wantOut: "gorename: refusing to modify generated file containing DO NOT EDIT marker:",
162 // Test: from identifier doesn't appear in cgo file that includes modified package -> rename successful
163 from: `"test".Foo::x`, to: "y",
164 packages: map[string][]string{
168 func Foo(x int) (int){
177 // #include <unistd.h>
189 wantOut: "Renamed 2 occurrences in 1 file in 1 package.",
191 // Test: from name appears in cgo file in same package -> error
192 from: `"mytest"::f`, to: "g",
193 packages: map[string][]string{
194 "mytest": []string{`package mytest; func f() {}`,
196 // #include <stdio.h>
199 func z() {C.puts(nil); f()}`,
201 // #include <unistd.h>
204 func foo() {C.close(3); f()}`,
208 wantOut: "gorename: refusing to modify generated files containing DO NOT EDIT marker:",
210 // Test: from name in file, identifier not used in cgo file -> rename successful
211 from: filepath.Join("mytest", "0.go") + `::f`, to: "g",
213 packages: map[string][]string{
214 "mytest": []string{`package mytest; func f() {}`,
216 // #include <stdio.h>
219 func z() {C.puts(nil)}`},
222 wantOut: "Renamed 1 occurrence in 1 file in 1 package.",
224 // Test: from identifier imported to another package but does not modify cgo file -> rename successful
225 from: `"test".Foo`, to: "Bar",
226 packages: map[string][]string{
230 func Foo(x int) (int){
237 // #include <unistd.h>
247 func g() { fmt.Println(test.Foo(3)) }
252 wantOut: "Renamed 2 occurrences in 2 files in 2 packages.",
255 // Write the test files
256 testCleanup := setUpPackages(t, srcDir, renameTest.packages)
262 if renameTest.offset != "" {
263 arg, val = "-offset", renameTest.offset
265 arg, val = "-from", renameTest.from
268 prefix := fmt.Sprintf("%d: %s %q -to %q", iter, arg, val, renameTest.to)
270 if renameTest.fileSpecified {
271 // add the src dir to the value of the argument
272 val = filepath.Join(srcDir, val)
275 args = append(args, arg, val, "-to", renameTest.to)
278 cmd := exec.Command(bin, args...)
279 cmd.Args[0] = "gorename"
283 out, err := cmd.CombinedOutput()
284 // errors should result in no changes to files
286 if !renameTest.wantErr {
287 t.Errorf("%s: received unexpected error %s", prefix, err)
290 if ok := strings.Contains(string(out), renameTest.wantOut); !ok {
291 t.Errorf("%s: unexpected command output: %s (want: %s)", prefix, out, renameTest.wantOut)
293 // Check that no files were modified
294 if modified := modifiedFiles(t, srcDir, renameTest.packages); len(modified) != 0 {
295 t.Errorf("%s: files unexpectedly modified: %s", prefix, modified)
299 if !renameTest.wantErr {
300 if ok := strings.Contains(string(out), renameTest.wantOut); !ok {
301 t.Errorf("%s: unexpected command output: %s (want: %s)", prefix, out, renameTest.wantOut)
304 t.Errorf("%s: command succeeded unexpectedly, output: %s", prefix, out)
311 // buildGorename builds the gorename executable.
312 // It returns its path, and a cleanup function.
313 func buildGorename(t *testing.T) (tmp, bin string, cleanup func()) {
314 if runtime.GOOS == "android" {
315 t.Skipf("the dependencies are not available on android")
318 tmp, err := ioutil.TempDir("", "gorename-regtest-")
324 if cleanup == nil { // probably, go build failed.
329 bin = filepath.Join(tmp, "gorename")
330 if runtime.GOOS == "windows" {
333 cmd := exec.Command("go", "build", "-o", bin)
334 if err := cmd.Run(); err != nil {
335 t.Fatalf("Building gorename: %v", err)
337 return tmp, bin, func() { os.RemoveAll(tmp) }
340 // setUpPackages sets up the files in a temporary directory provided by arguments.
341 func setUpPackages(t *testing.T, dir string, packages map[string][]string) (cleanup func()) {
344 for pkgName, files := range packages {
345 // Create a directory for the package.
346 pkgDir := filepath.Join(dir, pkgName)
347 pkgDirs = append(pkgDirs, pkgDir)
349 if err := os.Mkdir(pkgDir, os.ModePerm); err != nil {
352 // Write the packages files
353 for i, val := range files {
354 file := filepath.Join(pkgDir, strconv.Itoa(i)+".go")
355 if err := ioutil.WriteFile(file, []byte(val), os.ModePerm); err != nil {
361 for _, dir := range pkgDirs {
367 // modifiedFiles returns a list of files that were renamed (without the prefix dir).
368 func modifiedFiles(t *testing.T, dir string, packages map[string][]string) (results []string) {
370 for pkgName, files := range packages {
371 pkgDir := filepath.Join(dir, pkgName)
373 for i, val := range files {
374 file := filepath.Join(pkgDir, strconv.Itoa(i)+".go")
375 // read file contents and compare to val
376 if contents, err := ioutil.ReadFile(file); err != nil {
377 t.Fatalf("File missing: %s", err)
378 } else if string(contents) != val {
379 results = append(results, strings.TrimPrefix(dir, file))