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