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 | |
10 | package main |
11 | |
12 | import ( |
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 | |
39 | var gopath = filepath.Join(cwd, "testdata") |
40 | |
41 | func 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 | |
57 | func TestFixImports(t *testing.T) { |
58 | if os.Getenv("GO_BUILDER_NAME") == "plan9-arm" { |
59 | t.Skipf("skipping test that times out on plan9-arm; see https://go.dev/issue/50775") |
60 | } |
61 | testenv.NeedsTool(t, "go") |
62 | |
63 | defer func() { |
64 | stderr = os.Stderr |
65 | *badDomains = "code.google.com" |
66 | *replaceFlag = "" |
67 | }() |
68 | |
69 | for i, test := range []struct { |
70 | packages []string // packages to rewrite, "go list" syntax |
71 | badDomains string // -baddomains flag |
72 | replaceFlag string // -replace flag |
73 | wantOK bool |
74 | wantStderr string |
75 | wantRewrite map[string]string |
76 | }{ |
77 | // #0. No errors. |
78 | { |
79 | packages: []string{"all"}, |
80 | badDomains: "code.google.com", |
81 | wantOK: true, |
82 | wantStderr: ` |
83 | testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF' |
84 | fruit.io/banana |
85 | fixed: old.com/one -> new.com/one |
86 | fixed: titanic.biz/bar -> new.com/bar |
87 | `, |
88 | wantRewrite: map[string]string{ |
89 | "$GOPATH/src/fruit.io/banana/banana.go": `package banana |
90 | |
91 | import ( |
92 | _ "new.com/bar" |
93 | _ "new.com/one" |
94 | _ "titanic.biz/foo" |
95 | )`, |
96 | }, |
97 | }, |
98 | // #1. No packages needed rewriting. |
99 | { |
100 | packages: []string{"titanic.biz/...", "old.com/...", "new.com/..."}, |
101 | badDomains: "code.google.com", |
102 | wantOK: true, |
103 | wantStderr: ` |
104 | testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF' |
105 | `, |
106 | }, |
107 | // #2. Some packages without import comments matched bad domains. |
108 | { |
109 | packages: []string{"all"}, |
110 | badDomains: "titanic.biz", |
111 | wantOK: false, |
112 | wantStderr: ` |
113 | testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF' |
114 | fruit.io/banana |
115 | testdata/src/fruit.io/banana/banana.go:6: import "titanic.biz/foo" |
116 | fixed: old.com/one -> new.com/one |
117 | fixed: titanic.biz/bar -> new.com/bar |
118 | ERROR: titanic.biz/foo has no import comment |
119 | imported directly by: |
120 | fruit.io/pear |
121 | imported indirectly by: |
122 | fruit.io/orange |
123 | `, |
124 | wantRewrite: map[string]string{ |
125 | "$GOPATH/src/fruit.io/banana/banana.go": `package banana |
126 | |
127 | import ( |
128 | _ "new.com/bar" |
129 | _ "new.com/one" |
130 | _ "titanic.biz/foo" |
131 | )`, |
132 | }, |
133 | }, |
134 | // #3. The -replace flag lets user supply missing import comments. |
135 | { |
136 | packages: []string{"all"}, |
137 | replaceFlag: "titanic.biz/foo=new.com/foo", |
138 | wantOK: true, |
139 | wantStderr: ` |
140 | testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF' |
141 | fruit.io/banana |
142 | fixed: old.com/one -> new.com/one |
143 | fixed: titanic.biz/bar -> new.com/bar |
144 | fixed: titanic.biz/foo -> new.com/foo |
145 | `, |
146 | wantRewrite: map[string]string{ |
147 | "$GOPATH/src/fruit.io/banana/banana.go": `package banana |
148 | |
149 | import ( |
150 | _ "new.com/bar" |
151 | _ "new.com/foo" |
152 | _ "new.com/one" |
153 | )`, |
154 | }, |
155 | }, |
156 | // #4. The -replace flag supports wildcards. |
157 | // An explicit import comment takes precedence. |
158 | { |
159 | packages: []string{"all"}, |
160 | replaceFlag: "titanic.biz/...=new.com/...", |
161 | wantOK: true, |
162 | wantStderr: ` |
163 | testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF' |
164 | fruit.io/banana |
165 | fixed: old.com/one -> new.com/one |
166 | fixed: titanic.biz/bar -> new.com/bar |
167 | fixed: titanic.biz/foo -> new.com/foo |
168 | `, |
169 | wantRewrite: map[string]string{ |
170 | "$GOPATH/src/fruit.io/banana/banana.go": `package banana |
171 | |
172 | import ( |
173 | _ "new.com/bar" |
174 | _ "new.com/foo" |
175 | _ "new.com/one" |
176 | )`, |
177 | }, |
178 | }, |
179 | // #5. The -replace flag trumps -baddomains. |
180 | { |
181 | packages: []string{"all"}, |
182 | badDomains: "titanic.biz", |
183 | replaceFlag: "titanic.biz/foo=new.com/foo", |
184 | wantOK: true, |
185 | wantStderr: ` |
186 | testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF' |
187 | fruit.io/banana |
188 | fixed: old.com/one -> new.com/one |
189 | fixed: titanic.biz/bar -> new.com/bar |
190 | fixed: titanic.biz/foo -> new.com/foo |
191 | `, |
192 | wantRewrite: map[string]string{ |
193 | "$GOPATH/src/fruit.io/banana/banana.go": `package banana |
194 | |
195 | import ( |
196 | _ "new.com/bar" |
197 | _ "new.com/foo" |
198 | _ "new.com/one" |
199 | )`, |
200 | }, |
201 | }, |
202 | } { |
203 | *badDomains = test.badDomains |
204 | *replaceFlag = test.replaceFlag |
205 | |
206 | stderr = new(bytes.Buffer) |
207 | gotRewrite := make(map[string]string) |
208 | writeFile = func(filename string, content []byte, mode os.FileMode) error { |
209 | filename = strings.Replace(filename, gopath, "$GOPATH", 1) |
210 | filename = filepath.ToSlash(filename) |
211 | gotRewrite[filename] = string(bytes.TrimSpace(content)) |
212 | return nil |
213 | } |
214 | |
215 | if runtime.GOOS == "windows" { |
216 | test.wantStderr = strings.Replace(test.wantStderr, `testdata/src/old.com/bad/bad.go`, `testdata\src\old.com\bad\bad.go`, -1) |
217 | test.wantStderr = strings.Replace(test.wantStderr, `testdata/src/fruit.io/banana/banana.go`, `testdata\src\fruit.io\banana\banana.go`, -1) |
218 | } |
219 | test.wantStderr = strings.TrimSpace(test.wantStderr) |
220 | |
221 | // Check status code. |
222 | if fiximports(test.packages...) != test.wantOK { |
223 | t.Errorf("#%d. fiximports() = %t", i, !test.wantOK) |
224 | } |
225 | |
226 | // Compare stderr output. |
227 | if got := strings.TrimSpace(stderr.(*bytes.Buffer).String()); got != test.wantStderr { |
228 | if strings.Contains(got, "vendor/golang_org/x/text/unicode/norm") { |
229 | t.Skip("skipping known-broken test; see golang.org/issue/17417") |
230 | } |
231 | t.Errorf("#%d. stderr: got <<\n%s\n>>, want <<\n%s\n>>", |
232 | i, got, test.wantStderr) |
233 | } |
234 | |
235 | // Compare rewrites. |
236 | for k, v := range gotRewrite { |
237 | if test.wantRewrite[k] != v { |
238 | t.Errorf("#%d. rewrite[%s] = <<%s>>, want <<%s>>", |
239 | i, k, v, test.wantRewrite[k]) |
240 | } |
241 | delete(test.wantRewrite, k) |
242 | } |
243 | for k, v := range test.wantRewrite { |
244 | t.Errorf("#%d. rewrite[%s] missing, want <<%s>>", i, k, v) |
245 | } |
246 | } |
247 | } |
248 | |
249 | // TestDryRun tests that the -n flag suppresses calls to writeFile. |
250 | func TestDryRun(t *testing.T) { |
251 | if os.Getenv("GO_BUILDER_NAME") == "plan9-arm" { |
252 | t.Skipf("skipping test that times out on plan9-arm; see https://go.dev/issue/50775") |
253 | } |
254 | testenv.NeedsTool(t, "go") |
255 | |
256 | *dryrun = true |
257 | defer func() { *dryrun = false }() // restore |
258 | stderr = new(bytes.Buffer) |
259 | writeFile = func(filename string, content []byte, mode os.FileMode) error { |
260 | t.Fatalf("writeFile(%s) called in dryrun mode", filename) |
261 | return nil |
262 | } |
263 | |
264 | if !fiximports("all") { |
265 | t.Fatalf("fiximports failed: %s", stderr) |
266 | } |
267 | } |
268 |
Members