1// Copyright 2015 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// No testdata on Android.
6
7//go:build !android
8// +build !android
9
10package main
11
12import (
13	"bytes"
14	"log"
15	"os"
16	"path/filepath"
17	"runtime"
18	"strings"
19	"testing"
20
21	"golang.org/x/tools/internal/testenv"
22)
23
24// TODO(adonovan):
25// - test introduction of renaming imports.
26// - test induced failures of rewriteFile.
27
28// Guide to the test packages:
29//
30// new.com/one		-- canonical name for old.com/one
31// old.com/one		-- non-canonical; has import comment "new.com/one"
32// old.com/bad		-- has a parse error
33// fruit.io/orange	\
34// fruit.io/banana	 } orange -> pear -> banana -> titanic.biz/bar
35// fruit.io/pear	/
36// titanic.biz/bar	-- domain is sinking; package has jumped ship to new.com/bar
37// titanic.biz/foo	-- domain is sinking but package has no import comment yet
38
39var gopath = filepath.Join(cwd, "testdata")
40
41func init() {
42	if err := os.Setenv("GOPATH", gopath); err != nil {
43		log.Fatal(err)
44	}
45
46	// This test currently requires GOPATH mode.
47	// Explicitly disabling module mode should suffix, but
48	// we'll also turn off GOPROXY just for good measure.
49	if err := os.Setenv("GO111MODULE", "off"); err != nil {
50		log.Fatal(err)
51	}
52	if err := os.Setenv("GOPROXY", "off"); err != nil {
53		log.Fatal(err)
54	}
55}
56
57func TestFixImports(t *testing.T) {
58	testenv.NeedsTool(t, "go")
59
60	defer func() {
61		stderr = os.Stderr
62		*badDomains = "code.google.com"
63		*replaceFlag = ""
64	}()
65
66	for i, test := range []struct {
67		packages    []string // packages to rewrite, "go list" syntax
68		badDomains  string   // -baddomains flag
69		replaceFlag string   // -replace flag
70		wantOK      bool
71		wantStderr  string
72		wantRewrite map[string]string
73	}{
74		// #0. No errors.
75		{
76			packages:   []string{"all"},
77			badDomains: "code.google.com",
78			wantOK:     true,
79			wantStderr: `
80testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF'
81fruit.io/banana
82	fixed: old.com/one -> new.com/one
83	fixed: titanic.biz/bar -> new.com/bar
84`,
85			wantRewrite: map[string]string{
86				"$GOPATH/src/fruit.io/banana/banana.go": `package banana
87
88import (
89	_ "new.com/bar"
90	_ "new.com/one"
91	_ "titanic.biz/foo"
92)`,
93			},
94		},
95		// #1. No packages needed rewriting.
96		{
97			packages:   []string{"titanic.biz/...", "old.com/...", "new.com/..."},
98			badDomains: "code.google.com",
99			wantOK:     true,
100			wantStderr: `
101testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF'
102`,
103		},
104		// #2. Some packages without import comments matched bad domains.
105		{
106			packages:   []string{"all"},
107			badDomains: "titanic.biz",
108			wantOK:     false,
109			wantStderr: `
110testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF'
111fruit.io/banana
112	testdata/src/fruit.io/banana/banana.go:6: import "titanic.biz/foo"
113	fixed: old.com/one -> new.com/one
114	fixed: titanic.biz/bar -> new.com/bar
115	ERROR: titanic.biz/foo has no import comment
116	imported directly by:
117		fruit.io/pear
118	imported indirectly by:
119		fruit.io/orange
120`,
121			wantRewrite: map[string]string{
122				"$GOPATH/src/fruit.io/banana/banana.go": `package banana
123
124import (
125	_ "new.com/bar"
126	_ "new.com/one"
127	_ "titanic.biz/foo"
128)`,
129			},
130		},
131		// #3. The -replace flag lets user supply missing import comments.
132		{
133			packages:    []string{"all"},
134			replaceFlag: "titanic.biz/foo=new.com/foo",
135			wantOK:      true,
136			wantStderr: `
137testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF'
138fruit.io/banana
139	fixed: old.com/one -> new.com/one
140	fixed: titanic.biz/bar -> new.com/bar
141	fixed: titanic.biz/foo -> new.com/foo
142`,
143			wantRewrite: map[string]string{
144				"$GOPATH/src/fruit.io/banana/banana.go": `package banana
145
146import (
147	_ "new.com/bar"
148	_ "new.com/foo"
149	_ "new.com/one"
150)`,
151			},
152		},
153		// #4. The -replace flag supports wildcards.
154		//     An explicit import comment takes precedence.
155		{
156			packages:    []string{"all"},
157			replaceFlag: "titanic.biz/...=new.com/...",
158			wantOK:      true,
159			wantStderr: `
160testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF'
161fruit.io/banana
162	fixed: old.com/one -> new.com/one
163	fixed: titanic.biz/bar -> new.com/bar
164	fixed: titanic.biz/foo -> new.com/foo
165`,
166			wantRewrite: map[string]string{
167				"$GOPATH/src/fruit.io/banana/banana.go": `package banana
168
169import (
170	_ "new.com/bar"
171	_ "new.com/foo"
172	_ "new.com/one"
173)`,
174			},
175		},
176		// #5. The -replace flag trumps -baddomains.
177		{
178			packages:    []string{"all"},
179			badDomains:  "titanic.biz",
180			replaceFlag: "titanic.biz/foo=new.com/foo",
181			wantOK:      true,
182			wantStderr: `
183testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF'
184fruit.io/banana
185	fixed: old.com/one -> new.com/one
186	fixed: titanic.biz/bar -> new.com/bar
187	fixed: titanic.biz/foo -> new.com/foo
188`,
189			wantRewrite: map[string]string{
190				"$GOPATH/src/fruit.io/banana/banana.go": `package banana
191
192import (
193	_ "new.com/bar"
194	_ "new.com/foo"
195	_ "new.com/one"
196)`,
197			},
198		},
199	} {
200		*badDomains = test.badDomains
201		*replaceFlag = test.replaceFlag
202
203		stderr = new(bytes.Buffer)
204		gotRewrite := make(map[string]string)
205		writeFile = func(filename string, content []byte, mode os.FileMode) error {
206			filename = strings.Replace(filename, gopath, "$GOPATH", 1)
207			filename = filepath.ToSlash(filename)
208			gotRewrite[filename] = string(bytes.TrimSpace(content))
209			return nil
210		}
211
212		if runtime.GOOS == "windows" {
213			test.wantStderr = strings.Replace(test.wantStderr, `testdata/src/old.com/bad/bad.go`, `testdata\src\old.com\bad\bad.go`, -1)
214			test.wantStderr = strings.Replace(test.wantStderr, `testdata/src/fruit.io/banana/banana.go`, `testdata\src\fruit.io\banana\banana.go`, -1)
215		}
216		test.wantStderr = strings.TrimSpace(test.wantStderr)
217
218		// Check status code.
219		if fiximports(test.packages...) != test.wantOK {
220			t.Errorf("#%d. fiximports() = %t", i, !test.wantOK)
221		}
222
223		// Compare stderr output.
224		if got := strings.TrimSpace(stderr.(*bytes.Buffer).String()); got != test.wantStderr {
225			if strings.Contains(got, "vendor/golang_org/x/text/unicode/norm") {
226				t.Skip("skipping known-broken test; see golang.org/issue/17417")
227			}
228			t.Errorf("#%d. stderr: got <<\n%s\n>>, want <<\n%s\n>>",
229				i, got, test.wantStderr)
230		}
231
232		// Compare rewrites.
233		for k, v := range gotRewrite {
234			if test.wantRewrite[k] != v {
235				t.Errorf("#%d. rewrite[%s] = <<%s>>, want <<%s>>",
236					i, k, v, test.wantRewrite[k])
237			}
238			delete(test.wantRewrite, k)
239		}
240		for k, v := range test.wantRewrite {
241			t.Errorf("#%d. rewrite[%s] missing, want <<%s>>", i, k, v)
242		}
243	}
244}
245
246// TestDryRun tests that the -n flag suppresses calls to writeFile.
247func TestDryRun(t *testing.T) {
248	testenv.NeedsTool(t, "go")
249
250	*dryrun = true
251	defer func() { *dryrun = false }() // restore
252	stderr = new(bytes.Buffer)
253	writeFile = func(filename string, content []byte, mode os.FileMode) error {
254		t.Fatalf("writeFile(%s) called in dryrun mode", filename)
255		return nil
256	}
257
258	if !fiximports("all") {
259		t.Fatalf("fiximports failed: %s", stderr)
260	}
261}
262