1 | // Copyright 2020 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 packages_test |
6 | |
7 | import ( |
8 | "fmt" |
9 | "io/ioutil" |
10 | "log" |
11 | "os" |
12 | "path/filepath" |
13 | "reflect" |
14 | "sort" |
15 | "testing" |
16 | |
17 | "golang.org/x/tools/go/packages" |
18 | "golang.org/x/tools/go/packages/packagestest" |
19 | "golang.org/x/tools/internal/testenv" |
20 | ) |
21 | |
22 | const ( |
23 | commonMode = packages.NeedName | packages.NeedFiles | |
24 | packages.NeedCompiledGoFiles | packages.NeedImports | packages.NeedSyntax |
25 | everythingMode = commonMode | packages.NeedDeps | packages.NeedTypes | |
26 | packages.NeedTypesSizes |
27 | ) |
28 | |
29 | func TestOverlayChangesPackageName(t *testing.T) { |
30 | testAllOrModulesParallel(t, testOverlayChangesPackageName) |
31 | } |
32 | func testOverlayChangesPackageName(t *testing.T, exporter packagestest.Exporter) { |
33 | log.SetFlags(log.Lshortfile) |
34 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
35 | Name: "fake", |
36 | Files: map[string]interface{}{ |
37 | "a.go": "package foo\nfunc f(){}\n", |
38 | }, |
39 | Overlay: map[string][]byte{ |
40 | "a.go": []byte("package foox\nfunc f(){}\n"), |
41 | }, |
42 | }}) |
43 | defer exported.Cleanup() |
44 | exported.Config.Mode = packages.NeedName |
45 | |
46 | initial, err := packages.Load(exported.Config, |
47 | filepath.Dir(exported.File("fake", "a.go"))) |
48 | if err != nil { |
49 | t.Fatalf("failed to load: %v", err) |
50 | } |
51 | if len(initial) != 1 || initial[0].ID != "fake" || initial[0].Name != "foox" { |
52 | t.Fatalf("got %v, expected [fake]", initial) |
53 | } |
54 | if len(initial[0].Errors) != 0 { |
55 | t.Fatalf("got %v, expected no errors", initial[0].Errors) |
56 | } |
57 | log.SetFlags(0) |
58 | } |
59 | func TestOverlayChangesBothPackageNames(t *testing.T) { |
60 | testAllOrModulesParallel(t, testOverlayChangesBothPackageNames) |
61 | } |
62 | func testOverlayChangesBothPackageNames(t *testing.T, exporter packagestest.Exporter) { |
63 | log.SetFlags(log.Lshortfile) |
64 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
65 | Name: "fake", |
66 | Files: map[string]interface{}{ |
67 | "a.go": "package foo\nfunc g(){}\n", |
68 | "a_test.go": "package foo\nfunc f(){}\n", |
69 | }, |
70 | Overlay: map[string][]byte{ |
71 | "a.go": []byte("package foox\nfunc g(){}\n"), |
72 | "a_test.go": []byte("package foox\nfunc f(){}\n"), |
73 | }, |
74 | }}) |
75 | defer exported.Cleanup() |
76 | exported.Config.Mode = commonMode |
77 | |
78 | initial, err := packages.Load(exported.Config, |
79 | filepath.Dir(exported.File("fake", "a.go"))) |
80 | if err != nil { |
81 | t.Fatalf("failed to load: %v", err) |
82 | } |
83 | if len(initial) != 3 { |
84 | t.Errorf("got %d packges, expected 3", len(initial)) |
85 | } |
86 | want := []struct { |
87 | id, name string |
88 | count int |
89 | }{ |
90 | {"fake", "foox", 1}, |
91 | {"fake [fake.test]", "foox", 2}, |
92 | {"fake.test", "main", 1}, |
93 | } |
94 | if len(initial) != 3 { |
95 | t.Fatalf("expected 3 packages, got %v", len(initial)) |
96 | } |
97 | for i := 0; i < 3; i++ { |
98 | if ok := checkPkg(t, initial[i], want[i].id, want[i].name, want[i].count); !ok { |
99 | t.Errorf("%d: got {%s %s %d}, expected %v", i, initial[i].ID, |
100 | initial[i].Name, len(initial[i].Syntax), want[i]) |
101 | } |
102 | if len(initial[i].Errors) != 0 { |
103 | t.Errorf("%d: got %v, expected no errors", i, initial[i].Errors) |
104 | } |
105 | } |
106 | log.SetFlags(0) |
107 | } |
108 | func TestOverlayChangesTestPackageName(t *testing.T) { |
109 | testAllOrModulesParallel(t, testOverlayChangesTestPackageName) |
110 | } |
111 | func testOverlayChangesTestPackageName(t *testing.T, exporter packagestest.Exporter) { |
112 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
113 | Name: "fake", |
114 | Files: map[string]interface{}{ |
115 | "a_test.go": "package foo\nfunc f(){}\n", |
116 | }, |
117 | Overlay: map[string][]byte{ |
118 | "a_test.go": []byte("package foox\nfunc f(){}\n"), |
119 | }, |
120 | }}) |
121 | defer exported.Cleanup() |
122 | exported.Config.Mode = commonMode |
123 | |
124 | initial, err := packages.Load(exported.Config, |
125 | filepath.Dir(exported.File("fake", "a_test.go"))) |
126 | if err != nil { |
127 | t.Fatalf("failed to load: %v", err) |
128 | } |
129 | if len(initial) != 3 { |
130 | t.Errorf("got %d packges, expected 3", len(initial)) |
131 | } |
132 | want := []struct { |
133 | id, name string |
134 | count int |
135 | }{ |
136 | {"fake", "foox", 0}, |
137 | {"fake [fake.test]", "foox", 1}, |
138 | {"fake.test", "main", 1}, |
139 | } |
140 | if len(initial) != 3 { |
141 | t.Fatalf("expected 3 packages, got %v", len(initial)) |
142 | } |
143 | for i := 0; i < 3; i++ { |
144 | if ok := checkPkg(t, initial[i], want[i].id, want[i].name, want[i].count); !ok { |
145 | t.Errorf("got {%s %s %d}, expected %v", initial[i].ID, |
146 | initial[i].Name, len(initial[i].Syntax), want[i]) |
147 | } |
148 | } |
149 | if len(initial[0].Errors) != 0 { |
150 | t.Fatalf("got %v, expected no errors", initial[0].Errors) |
151 | } |
152 | log.SetFlags(0) |
153 | } |
154 | |
155 | func checkPkg(t *testing.T, p *packages.Package, id, name string, syntax int) bool { |
156 | t.Helper() |
157 | if p.ID == id && p.Name == name && len(p.Syntax) == syntax { |
158 | return true |
159 | } |
160 | return false |
161 | } |
162 | |
163 | func TestOverlayXTests(t *testing.T) { |
164 | testAllOrModulesParallel(t, testOverlayXTests) |
165 | } |
166 | |
167 | // This test checks the behavior of go/packages.Load with an overlaid |
168 | // x test. The source of truth is the go/packages.Load results for the |
169 | // exact same package, just on-disk. |
170 | func testOverlayXTests(t *testing.T, exporter packagestest.Exporter) { |
171 | const aFile = `package a; const C = "C"; func Hello() {}` |
172 | const aTestVariant = `package a |
173 | |
174 | import "testing" |
175 | |
176 | const TestC = "test" + C |
177 | |
178 | func TestHello(){ |
179 | Hello() |
180 | }` |
181 | const aXTest = `package a_test |
182 | |
183 | import ( |
184 | "testing" |
185 | |
186 | "golang.org/fake/a" |
187 | ) |
188 | |
189 | const xTestC = "x" + a.C |
190 | |
191 | func TestHello(t *testing.T) { |
192 | a.Hello() |
193 | }` |
194 | |
195 | // First, get the source of truth by loading the package, all on disk. |
196 | onDisk := packagestest.Export(t, exporter, []packagestest.Module{{ |
197 | Name: "golang.org/fake", |
198 | Files: map[string]interface{}{ |
199 | "a/a.go": aFile, |
200 | "a/a_test.go": aTestVariant, |
201 | "a/a_x_test.go": aXTest, |
202 | }, |
203 | }}) |
204 | defer onDisk.Cleanup() |
205 | |
206 | onDisk.Config.Mode = commonMode |
207 | onDisk.Config.Tests = true |
208 | onDisk.Config.Mode = packages.LoadTypes |
209 | initial, err := packages.Load(onDisk.Config, fmt.Sprintf("file=%s", onDisk.File("golang.org/fake", "a/a_x_test.go"))) |
210 | if err != nil { |
211 | t.Fatal(err) |
212 | } |
213 | wantPkg := initial[0] |
214 | |
215 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
216 | Name: "golang.org/fake", |
217 | Files: map[string]interface{}{ |
218 | "a/a.go": aFile, |
219 | "a/a_test.go": aTestVariant, |
220 | "a/a_x_test.go": ``, // empty x test on disk |
221 | }, |
222 | Overlay: map[string][]byte{ |
223 | "a/a_x_test.go": []byte(aXTest), |
224 | }, |
225 | }}) |
226 | defer exported.Cleanup() |
227 | |
228 | if len(initial) != 1 { |
229 | t.Fatalf("expected 1 package, got %d", len(initial)) |
230 | } |
231 | // Confirm that the overlaid package is identical to the on-disk version. |
232 | pkg := initial[0] |
233 | if !reflect.DeepEqual(wantPkg, pkg) { |
234 | t.Fatalf("mismatched packages: want %#v, got %#v", wantPkg, pkg) |
235 | } |
236 | xTestC := constant(pkg, "xTestC") |
237 | if xTestC == nil { |
238 | t.Fatalf("no value for xTestC") |
239 | } |
240 | got := xTestC.Val().String() |
241 | // TODO(rstambler): Ideally, this test would check that the test variant |
242 | // was imported, but that's pretty complicated. |
243 | if want := `"xC"`; got != want { |
244 | t.Errorf("got: %q, want %q", got, want) |
245 | } |
246 | } |
247 | |
248 | func TestOverlay(t *testing.T) { testAllOrModulesParallel(t, testOverlay) } |
249 | func testOverlay(t *testing.T, exporter packagestest.Exporter) { |
250 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
251 | Name: "golang.org/fake", |
252 | Files: map[string]interface{}{ |
253 | "a/a.go": `package a; import "golang.org/fake/b"; const A = "a" + b.B`, |
254 | "b/b.go": `package b; import "golang.org/fake/c"; const B = "b" + c.C`, |
255 | "c/c.go": `package c; const C = "c"`, |
256 | "c/c_test.go": `package c; import "testing"; func TestC(t *testing.T) {}`, |
257 | "d/d.go": `package d; const D = "d"`, |
258 | }}}) |
259 | defer exported.Cleanup() |
260 | |
261 | for i, test := range []struct { |
262 | overlay map[string][]byte |
263 | want string // expected value of a.A |
264 | wantErrs []string |
265 | }{ |
266 | {nil, `"abc"`, nil}, // default |
267 | {map[string][]byte{}, `"abc"`, nil}, // empty overlay |
268 | {map[string][]byte{exported.File("golang.org/fake", "c/c.go"): []byte(`package c; const C = "C"`)}, `"abC"`, nil}, |
269 | {map[string][]byte{exported.File("golang.org/fake", "b/b.go"): []byte(`package b; import "golang.org/fake/c"; const B = "B" + c.C`)}, `"aBc"`, nil}, |
270 | // Overlay with an existing file in an existing package adding a new import. |
271 | {map[string][]byte{exported.File("golang.org/fake", "b/b.go"): []byte(`package b; import "golang.org/fake/d"; const B = "B" + d.D`)}, `"aBd"`, nil}, |
272 | // Overlay with an existing file in an existing package. |
273 | {map[string][]byte{exported.File("golang.org/fake", "c/c.go"): []byte(`package c; import "net/http"; const C = http.MethodGet`)}, `"abGET"`, nil}, |
274 | // Overlay with a new file in an existing package. |
275 | {map[string][]byte{ |
276 | exported.File("golang.org/fake", "c/c.go"): []byte(`package c;`), |
277 | filepath.Join(filepath.Dir(exported.File("golang.org/fake", "c/c.go")), "c_new_file.go"): []byte(`package c; const C = "Ç"`)}, |
278 | `"abÇ"`, nil}, |
279 | // Overlay with a new file in an existing package, adding a new dependency to that package. |
280 | {map[string][]byte{ |
281 | exported.File("golang.org/fake", "c/c.go"): []byte(`package c;`), |
282 | filepath.Join(filepath.Dir(exported.File("golang.org/fake", "c/c.go")), "c_new_file.go"): []byte(`package c; import "golang.org/fake/d"; const C = "c" + d.D`)}, |
283 | `"abcd"`, nil}, |
284 | } { |
285 | exported.Config.Overlay = test.overlay |
286 | exported.Config.Mode = packages.LoadAllSyntax |
287 | initial, err := packages.Load(exported.Config, "golang.org/fake/a") |
288 | if err != nil { |
289 | t.Error(err) |
290 | continue |
291 | } |
292 | |
293 | // Check value of a.A. |
294 | a := initial[0] |
295 | aA := constant(a, "A") |
296 | if aA == nil { |
297 | t.Errorf("%d. a.A: got nil", i) |
298 | continue |
299 | } |
300 | got := aA.Val().String() |
301 | if got != test.want { |
302 | t.Errorf("%d. a.A: got %s, want %s", i, got, test.want) |
303 | } |
304 | |
305 | // Check errors. |
306 | var errors []packages.Error |
307 | packages.Visit(initial, nil, func(pkg *packages.Package) { |
308 | errors = append(errors, pkg.Errors...) |
309 | }) |
310 | if errs := errorMessages(errors); !reflect.DeepEqual(errs, test.wantErrs) { |
311 | t.Errorf("%d. got errors %s, want %s", i, errs, test.wantErrs) |
312 | } |
313 | } |
314 | } |
315 | |
316 | func TestOverlayDeps(t *testing.T) { testAllOrModulesParallel(t, testOverlayDeps) } |
317 | func testOverlayDeps(t *testing.T, exporter packagestest.Exporter) { |
318 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
319 | Name: "golang.org/fake", |
320 | Files: map[string]interface{}{ |
321 | "c/c.go": `package c; const C = "c"`, |
322 | "c/c_test.go": `package c; import "testing"; func TestC(t *testing.T) {}`, |
323 | }, |
324 | }}) |
325 | defer exported.Cleanup() |
326 | |
327 | exported.Config.Overlay = map[string][]byte{exported.File("golang.org/fake", "c/c.go"): []byte(`package c; import "net/http"; const C = http.MethodGet`)} |
328 | exported.Config.Mode = packages.NeedName | |
329 | packages.NeedFiles | |
330 | packages.NeedCompiledGoFiles | |
331 | packages.NeedImports | |
332 | packages.NeedDeps | |
333 | packages.NeedTypesSizes |
334 | pkgs, err := packages.Load(exported.Config, fmt.Sprintf("file=%s", exported.File("golang.org/fake", "c/c.go"))) |
335 | if err != nil { |
336 | t.Error(err) |
337 | } |
338 | |
339 | // Find package golang.org/fake/c |
340 | sort.Slice(pkgs, func(i, j int) bool { return pkgs[i].ID < pkgs[j].ID }) |
341 | if len(pkgs) != 2 { |
342 | t.Fatalf("expected 2 packages, got %v", len(pkgs)) |
343 | } |
344 | pkgc := pkgs[0] |
345 | if pkgc.ID != "golang.org/fake/c" { |
346 | t.Errorf("expected first package in sorted list to be \"golang.org/fake/c\", got %v", pkgc.ID) |
347 | } |
348 | |
349 | // Make sure golang.org/fake/c imports net/http, as per the overlay. |
350 | contains := func(imports map[string]*packages.Package, wantImport string) bool { |
351 | for imp := range imports { |
352 | if imp == wantImport { |
353 | return true |
354 | } |
355 | } |
356 | return false |
357 | } |
358 | if !contains(pkgc.Imports, "net/http") { |
359 | t.Errorf("expected import of %s in package %s, got the following imports: %v", |
360 | "net/http", pkgc.ID, pkgc.Imports) |
361 | } |
362 | |
363 | } |
364 | |
365 | func TestNewPackagesInOverlay(t *testing.T) { testAllOrModulesParallel(t, testNewPackagesInOverlay) } |
366 | func testNewPackagesInOverlay(t *testing.T, exporter packagestest.Exporter) { |
367 | exported := packagestest.Export(t, exporter, []packagestest.Module{ |
368 | { |
369 | Name: "golang.org/fake", |
370 | Files: map[string]interface{}{ |
371 | "a/a.go": `package a; import "golang.org/fake/b"; const A = "a" + b.B`, |
372 | "b/b.go": `package b; import "golang.org/fake/c"; const B = "b" + c.C`, |
373 | "c/c.go": `package c; const C = "c"`, |
374 | "d/d.go": `package d; const D = "d"`, |
375 | }, |
376 | }, |
377 | { |
378 | Name: "example.com/extramodule", |
379 | Files: map[string]interface{}{ |
380 | "pkg/x.go": "package pkg\n", |
381 | }, |
382 | }, |
383 | }) |
384 | defer exported.Cleanup() |
385 | |
386 | dir := filepath.Dir(filepath.Dir(exported.File("golang.org/fake", "a/a.go"))) |
387 | |
388 | for _, test := range []struct { |
389 | name string |
390 | overlay map[string][]byte |
391 | want string // expected value of e.E |
392 | }{ |
393 | {"one_file", |
394 | map[string][]byte{ |
395 | filepath.Join(dir, "e", "e.go"): []byte(`package e; import "golang.org/fake/a"; const E = "e" + a.A`)}, |
396 | `"eabc"`}, |
397 | {"multiple_files_same_package", |
398 | map[string][]byte{ |
399 | filepath.Join(dir, "e", "e.go"): []byte(`package e; import "golang.org/fake/a"; const E = "e" + a.A + underscore`), |
400 | filepath.Join(dir, "e", "e_util.go"): []byte(`package e; const underscore = "_"`), |
401 | }, |
402 | `"eabc_"`}, |
403 | {"multiple_files_two_packages", |
404 | map[string][]byte{ |
405 | filepath.Join(dir, "e", "e.go"): []byte(`package e; import "golang.org/fake/f"; const E = "e" + f.F + underscore`), |
406 | filepath.Join(dir, "e", "e_util.go"): []byte(`package e; const underscore = "_"`), |
407 | filepath.Join(dir, "f", "f.go"): []byte(`package f; const F = "f"`), |
408 | }, |
409 | `"ef_"`}, |
410 | {"multiple_files_three_packages", |
411 | map[string][]byte{ |
412 | filepath.Join(dir, "e", "e.go"): []byte(`package e; import "golang.org/fake/f"; const E = "e" + f.F + underscore`), |
413 | filepath.Join(dir, "e", "e_util.go"): []byte(`package e; const underscore = "_"`), |
414 | filepath.Join(dir, "f", "f.go"): []byte(`package f; import "golang.org/fake/g"; const F = "f" + g.G`), |
415 | filepath.Join(dir, "g", "g.go"): []byte(`package g; const G = "g"`), |
416 | }, |
417 | `"efg_"`}, |
418 | {"multiple_files_four_packages", |
419 | map[string][]byte{ |
420 | filepath.Join(dir, "e", "e.go"): []byte(`package e; import "golang.org/fake/f"; import "golang.org/fake/h"; const E = "e" + f.F + h.H + underscore`), |
421 | filepath.Join(dir, "e", "e_util.go"): []byte(`package e; const underscore = "_"`), |
422 | filepath.Join(dir, "f", "f.go"): []byte(`package f; import "golang.org/fake/g"; const F = "f" + g.G`), |
423 | filepath.Join(dir, "g", "g.go"): []byte(`package g; const G = "g"`), |
424 | filepath.Join(dir, "h", "h.go"): []byte(`package h; const H = "h"`), |
425 | }, |
426 | `"efgh_"`}, |
427 | {"multiple_files_four_packages_again", |
428 | map[string][]byte{ |
429 | filepath.Join(dir, "e", "e.go"): []byte(`package e; import "golang.org/fake/f"; const E = "e" + f.F + underscore`), |
430 | filepath.Join(dir, "e", "e_util.go"): []byte(`package e; const underscore = "_"`), |
431 | filepath.Join(dir, "f", "f.go"): []byte(`package f; import "golang.org/fake/g"; const F = "f" + g.G`), |
432 | filepath.Join(dir, "g", "g.go"): []byte(`package g; import "golang.org/fake/h"; const G = "g" + h.H`), |
433 | filepath.Join(dir, "h", "h.go"): []byte(`package h; const H = "h"`), |
434 | }, |
435 | `"efgh_"`}, |
436 | {"main_overlay", |
437 | map[string][]byte{ |
438 | filepath.Join(dir, "e", "main.go"): []byte(`package main; import "golang.org/fake/a"; const E = "e" + a.A; func main(){}`)}, |
439 | `"eabc"`}, |
440 | } { |
441 | t.Run(test.name, func(t *testing.T) { |
442 | exported.Config.Overlay = test.overlay |
443 | exported.Config.Mode = packages.LoadAllSyntax |
444 | exported.Config.Logf = t.Logf |
445 | |
446 | // With an overlay, we don't know the expected import path, |
447 | // so load with the absolute path of the directory. |
448 | initial, err := packages.Load(exported.Config, filepath.Join(dir, "e")) |
449 | if err != nil { |
450 | t.Fatal(err) |
451 | } |
452 | |
453 | // Check value of e.E. |
454 | e := initial[0] |
455 | eE := constant(e, "E") |
456 | if eE == nil { |
457 | t.Fatalf("e.E: was nil in %#v", e) |
458 | } |
459 | got := eE.Val().String() |
460 | if got != test.want { |
461 | t.Fatalf("e.E: got %s, want %s", got, test.want) |
462 | } |
463 | }) |
464 | } |
465 | } |
466 | |
467 | // Test that we can create a package and its test package in an overlay. |
468 | func TestOverlayNewPackageAndTest(t *testing.T) { |
469 | testAllOrModulesParallel(t, testOverlayNewPackageAndTest) |
470 | } |
471 | func testOverlayNewPackageAndTest(t *testing.T, exporter packagestest.Exporter) { |
472 | exported := packagestest.Export(t, exporter, []packagestest.Module{ |
473 | { |
474 | Name: "golang.org/fake", |
475 | Files: map[string]interface{}{ |
476 | "foo.txt": "placeholder", |
477 | }, |
478 | }, |
479 | }) |
480 | defer exported.Cleanup() |
481 | |
482 | dir := filepath.Dir(exported.File("golang.org/fake", "foo.txt")) |
483 | exported.Config.Overlay = map[string][]byte{ |
484 | filepath.Join(dir, "a.go"): []byte(`package a;`), |
485 | filepath.Join(dir, "a_test.go"): []byte(`package a; import "testing";`), |
486 | } |
487 | initial, err := packages.Load(exported.Config, "file="+filepath.Join(dir, "a.go"), "file="+filepath.Join(dir, "a_test.go")) |
488 | if err != nil { |
489 | t.Fatal(err) |
490 | } |
491 | if len(initial) != 2 { |
492 | t.Errorf("got %v packages, wanted %v", len(initial), 2) |
493 | } |
494 | } |
495 | |
496 | func TestAdHocOverlays(t *testing.T) { |
497 | t.Parallel() |
498 | testenv.NeedsTool(t, "go") |
499 | |
500 | // This test doesn't use packagestest because we are testing ad-hoc packages, |
501 | // which are outside of $GOPATH and outside of a module. |
502 | tmp, err := ioutil.TempDir("", "testAdHocOverlays") |
503 | if err != nil { |
504 | t.Fatal(err) |
505 | } |
506 | defer os.RemoveAll(tmp) |
507 | |
508 | filename := filepath.Join(tmp, "a.go") |
509 | content := []byte(`package a |
510 | const A = 1 |
511 | `) |
512 | |
513 | // Make sure that the user's value of GO111MODULE does not affect test results. |
514 | for _, go111module := range []string{"off", "auto", "on"} { |
515 | t.Run("GO111MODULE="+go111module, func(t *testing.T) { |
516 | config := &packages.Config{ |
517 | Dir: tmp, |
518 | Env: append(os.Environ(), "GOPACKAGESDRIVER=off", fmt.Sprintf("GO111MODULE=%s", go111module)), |
519 | Mode: packages.LoadAllSyntax, |
520 | Overlay: map[string][]byte{ |
521 | filename: content, |
522 | }, |
523 | Logf: t.Logf, |
524 | } |
525 | initial, err := packages.Load(config, fmt.Sprintf("file=%s", filename)) |
526 | if err != nil { |
527 | t.Fatal(err) |
528 | } |
529 | if len(initial) == 0 { |
530 | t.Fatalf("no packages for %s", filename) |
531 | } |
532 | // Check value of a.A. |
533 | a := initial[0] |
534 | if a.Errors != nil { |
535 | t.Fatalf("a: got errors %+v, want no error", err) |
536 | } |
537 | aA := constant(a, "A") |
538 | if aA == nil { |
539 | t.Errorf("a.A: got nil") |
540 | return |
541 | } |
542 | got := aA.Val().String() |
543 | if want := "1"; got != want { |
544 | t.Errorf("a.A: got %s, want %s", got, want) |
545 | } |
546 | }) |
547 | } |
548 | } |
549 | |
550 | // TestOverlayModFileChanges tests the behavior resulting from having files |
551 | // from multiple modules in overlays. |
552 | func TestOverlayModFileChanges(t *testing.T) { |
553 | t.Parallel() |
554 | testenv.NeedsTool(t, "go") |
555 | |
556 | // Create two unrelated modules in a temporary directory. |
557 | tmp, err := ioutil.TempDir("", "tmp") |
558 | if err != nil { |
559 | t.Fatal(err) |
560 | } |
561 | defer os.RemoveAll(tmp) |
562 | |
563 | // mod1 has a dependency on golang.org/x/xerrors. |
564 | mod1, err := ioutil.TempDir(tmp, "mod1") |
565 | if err != nil { |
566 | t.Fatal(err) |
567 | } |
568 | if err := ioutil.WriteFile(filepath.Join(mod1, "go.mod"), []byte(`module mod1 |
569 | |
570 | require ( |
571 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 |
572 | ) |
573 | `), 0775); err != nil { |
574 | t.Fatal(err) |
575 | } |
576 | |
577 | // mod2 does not have any dependencies. |
578 | mod2, err := ioutil.TempDir(tmp, "mod2") |
579 | if err != nil { |
580 | t.Fatal(err) |
581 | } |
582 | |
583 | want := `module mod2 |
584 | |
585 | go 1.11 |
586 | ` |
587 | if err := ioutil.WriteFile(filepath.Join(mod2, "go.mod"), []byte(want), 0775); err != nil { |
588 | t.Fatal(err) |
589 | } |
590 | |
591 | // Run packages.Load on mod2, while passing the contents over mod1/main.go in the overlay. |
592 | config := &packages.Config{ |
593 | Dir: mod2, |
594 | Env: append(os.Environ(), "GOPACKAGESDRIVER=off"), |
595 | Mode: packages.LoadImports, |
596 | Overlay: map[string][]byte{ |
597 | filepath.Join(mod1, "main.go"): []byte(`package main |
598 | import "golang.org/x/xerrors" |
599 | func main() { |
600 | _ = errors.New("") |
601 | } |
602 | `), |
603 | filepath.Join(mod2, "main.go"): []byte(`package main |
604 | func main() {} |
605 | `), |
606 | }, |
607 | } |
608 | if _, err := packages.Load(config, fmt.Sprintf("file=%s", filepath.Join(mod2, "main.go"))); err != nil { |
609 | t.Fatal(err) |
610 | } |
611 | |
612 | // Check that mod2/go.mod has not been modified. |
613 | got, err := ioutil.ReadFile(filepath.Join(mod2, "go.mod")) |
614 | if err != nil { |
615 | t.Fatal(err) |
616 | } |
617 | if string(got) != want { |
618 | t.Errorf("expected %s, got %s", want, string(got)) |
619 | } |
620 | } |
621 | |
622 | func TestOverlayGOPATHVendoring(t *testing.T) { |
623 | t.Parallel() |
624 | |
625 | exported := packagestest.Export(t, packagestest.GOPATH, []packagestest.Module{{ |
626 | Name: "golang.org/fake", |
627 | Files: map[string]interface{}{ |
628 | "vendor/vendor.com/foo/foo.go": `package foo; const X = "hi"`, |
629 | "user/user.go": `package user`, |
630 | }, |
631 | }}) |
632 | defer exported.Cleanup() |
633 | |
634 | exported.Config.Mode = packages.LoadAllSyntax |
635 | exported.Config.Logf = t.Logf |
636 | exported.Config.Overlay = map[string][]byte{ |
637 | exported.File("golang.org/fake", "user/user.go"): []byte(`package user; import "vendor.com/foo"; var x = foo.X`), |
638 | } |
639 | initial, err := packages.Load(exported.Config, "golang.org/fake/user") |
640 | if err != nil { |
641 | t.Fatal(err) |
642 | } |
643 | user := initial[0] |
644 | if len(user.Imports) != 1 { |
645 | t.Fatal("no imports for user") |
646 | } |
647 | if user.Imports["vendor.com/foo"].Name != "foo" { |
648 | t.Errorf("failed to load vendored package foo, imports: %#v", user.Imports["vendor.com/foo"]) |
649 | } |
650 | } |
651 | |
652 | func TestContainsOverlay(t *testing.T) { testAllOrModulesParallel(t, testContainsOverlay) } |
653 | func testContainsOverlay(t *testing.T, exporter packagestest.Exporter) { |
654 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
655 | Name: "golang.org/fake", |
656 | Files: map[string]interface{}{ |
657 | "a/a.go": `package a; import "golang.org/fake/b"`, |
658 | "b/b.go": `package b; import "golang.org/fake/c"`, |
659 | "c/c.go": `package c`, |
660 | }}}) |
661 | defer exported.Cleanup() |
662 | bOverlayFile := filepath.Join(filepath.Dir(exported.File("golang.org/fake", "b/b.go")), "b_overlay.go") |
663 | exported.Config.Mode = packages.LoadImports |
664 | exported.Config.Overlay = map[string][]byte{bOverlayFile: []byte(`package b;`)} |
665 | initial, err := packages.Load(exported.Config, "file="+bOverlayFile) |
666 | if err != nil { |
667 | t.Fatal(err) |
668 | } |
669 | |
670 | graph, _ := importGraph(initial) |
671 | wantGraph := ` |
672 | * golang.org/fake/b |
673 | golang.org/fake/c |
674 | golang.org/fake/b -> golang.org/fake/c |
675 | `[1:] |
676 | if graph != wantGraph { |
677 | t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph) |
678 | } |
679 | } |
680 | |
681 | func TestContainsOverlayXTest(t *testing.T) { testAllOrModulesParallel(t, testContainsOverlayXTest) } |
682 | func testContainsOverlayXTest(t *testing.T, exporter packagestest.Exporter) { |
683 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
684 | Name: "golang.org/fake", |
685 | Files: map[string]interface{}{ |
686 | "a/a.go": `package a; import "golang.org/fake/b"`, |
687 | "b/b.go": `package b; import "golang.org/fake/c"`, |
688 | "c/c.go": `package c`, |
689 | }}}) |
690 | defer exported.Cleanup() |
691 | |
692 | bOverlayXTestFile := filepath.Join(filepath.Dir(exported.File("golang.org/fake", "b/b.go")), "b_overlay_x_test.go") |
693 | exported.Config.Mode = packages.NeedName | packages.NeedFiles | packages.NeedImports |
694 | exported.Config.Overlay = map[string][]byte{bOverlayXTestFile: []byte(`package b_test; import "golang.org/fake/b"`)} |
695 | initial, err := packages.Load(exported.Config, "file="+bOverlayXTestFile) |
696 | if err != nil { |
697 | t.Fatal(err) |
698 | } |
699 | |
700 | graph, _ := importGraph(initial) |
701 | wantGraph := ` |
702 | golang.org/fake/b |
703 | * golang.org/fake/b_test [golang.org/fake/b.test] |
704 | golang.org/fake/c |
705 | golang.org/fake/b -> golang.org/fake/c |
706 | golang.org/fake/b_test [golang.org/fake/b.test] -> golang.org/fake/b |
707 | `[1:] |
708 | if graph != wantGraph { |
709 | t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph) |
710 | } |
711 | } |
712 | |
713 | func TestInvalidFilesBeforeOverlay(t *testing.T) { |
714 | testAllOrModulesParallel(t, testInvalidFilesBeforeOverlay) |
715 | } |
716 | |
717 | func testInvalidFilesBeforeOverlay(t *testing.T, exporter packagestest.Exporter) { |
718 | exported := packagestest.Export(t, exporter, []packagestest.Module{ |
719 | { |
720 | Name: "golang.org/fake", |
721 | Files: map[string]interface{}{ |
722 | "d/d.go": ``, |
723 | "main.go": ``, |
724 | }, |
725 | }, |
726 | }) |
727 | defer exported.Cleanup() |
728 | |
729 | dir := filepath.Dir(filepath.Dir(exported.File("golang.org/fake", "d/d.go"))) |
730 | |
731 | exported.Config.Mode = everythingMode |
732 | exported.Config.Tests = true |
733 | |
734 | // First, check that all packages returned have files associated with them. |
735 | // Tests the work-around for golang/go#39986. |
736 | t.Run("no overlay", func(t *testing.T) { |
737 | initial, err := packages.Load(exported.Config, fmt.Sprintf("%s/...", dir)) |
738 | if err != nil { |
739 | t.Fatal(err) |
740 | } |
741 | for _, pkg := range initial { |
742 | if len(pkg.CompiledGoFiles) == 0 { |
743 | t.Fatalf("expected at least 1 CompiledGoFile for %s, got none", pkg.PkgPath) |
744 | } |
745 | } |
746 | }) |
747 | |
748 | } |
749 | |
750 | // Tests golang/go#35973, fixed in Go 1.14. |
751 | func TestInvalidFilesBeforeOverlayContains(t *testing.T) { |
752 | testAllOrModulesParallel(t, testInvalidFilesBeforeOverlayContains) |
753 | } |
754 | func testInvalidFilesBeforeOverlayContains(t *testing.T, exporter packagestest.Exporter) { |
755 | exported := packagestest.Export(t, exporter, []packagestest.Module{ |
756 | { |
757 | Name: "golang.org/fake", |
758 | Files: map[string]interface{}{ |
759 | "d/d.go": `package d; import "net/http"; const Get = http.MethodGet; const Hello = "hello";`, |
760 | "d/util.go": ``, |
761 | "d/d_test.go": ``, |
762 | "main.go": ``, |
763 | }, |
764 | }, |
765 | }) |
766 | defer exported.Cleanup() |
767 | |
768 | dir := filepath.Dir(filepath.Dir(exported.File("golang.org/fake", "d/d.go"))) |
769 | |
770 | // Additional tests for test variants. |
771 | for i, tt := range []struct { |
772 | name string |
773 | overlay map[string][]byte |
774 | want string // expected value of d.D |
775 | wantID string // expected value for the package ID |
776 | }{ |
777 | // Overlay with a test variant. |
778 | { |
779 | "test_variant", |
780 | map[string][]byte{ |
781 | filepath.Join(dir, "d", "d_test.go"): []byte(`package d; import "testing"; const D = Get + "_test"; func TestD(t *testing.T) {};`), |
782 | }, |
783 | `"GET_test"`, "golang.org/fake/d [golang.org/fake/d.test]", |
784 | }, |
785 | // Overlay in package. |
786 | { |
787 | "second_file", |
788 | map[string][]byte{ |
789 | filepath.Join(dir, "d", "util.go"): []byte(`package d; const D = Get + "_util";`), |
790 | }, |
791 | `"GET_util"`, "golang.org/fake/d", |
792 | }, |
793 | // Overlay on the main file. |
794 | { |
795 | "main", |
796 | map[string][]byte{ |
797 | filepath.Join(dir, "main.go"): []byte(`package main; import "golang.org/fake/d"; const D = d.Get + "_main"; func main() {};`), |
798 | }, |
799 | `"GET_main"`, "golang.org/fake", |
800 | }, |
801 | { |
802 | "xtest", |
803 | map[string][]byte{ |
804 | filepath.Join(dir, "d", "d_test.go"): []byte(`package d_test; import "golang.org/fake/d"; import "testing"; const D = d.Get + "_xtest"; func TestD(t *testing.T) {};`), |
805 | }, |
806 | `"GET_xtest"`, "golang.org/fake/d_test [golang.org/fake/d.test]", |
807 | }, |
808 | } { |
809 | t.Run(tt.name, func(t *testing.T) { |
810 | exported.Config.Overlay = tt.overlay |
811 | exported.Config.Mode = everythingMode |
812 | exported.Config.Tests = true |
813 | |
814 | for f := range tt.overlay { |
815 | initial, err := packages.Load(exported.Config, fmt.Sprintf("file=%s", f)) |
816 | if err != nil { |
817 | t.Fatal(err) |
818 | } |
819 | if len(initial) != 1 && |
820 | (len(initial) != 2 || !isTestVariant(initial[0].ID, initial[1].ID)) { |
821 | t.Fatalf("expected 1 package (perhaps with test variant), got %v", len(initial)) |
822 | } |
823 | pkg := initial[0] |
824 | if pkg.ID != tt.wantID { |
825 | t.Fatalf("expected package ID %q, got %q", tt.wantID, pkg.ID) |
826 | } |
827 | var containsFile bool |
828 | for _, goFile := range pkg.CompiledGoFiles { |
829 | if f == goFile { |
830 | containsFile = true |
831 | break |
832 | } |
833 | } |
834 | if !containsFile { |
835 | t.Fatalf("expected %s in CompiledGoFiles, got %v", f, pkg.CompiledGoFiles) |
836 | } |
837 | // Check value of d.D. |
838 | D := constant(pkg, "D") |
839 | if D == nil { |
840 | t.Fatalf("%d. D: got nil", i) |
841 | } |
842 | got := D.Val().String() |
843 | if got != tt.want { |
844 | t.Fatalf("%d. D: got %s, want %s", i, got, tt.want) |
845 | } |
846 | } |
847 | }) |
848 | } |
849 | } |
850 | |
851 | func isTestVariant(libID, testID string) bool { |
852 | variantID := fmt.Sprintf("%[1]s [%[1]s.test]", libID) |
853 | return variantID == testID |
854 | } |
855 | |
856 | func TestInvalidXTestInGOPATH(t *testing.T) { |
857 | testAllOrModulesParallel(t, testInvalidXTestInGOPATH) |
858 | } |
859 | func testInvalidXTestInGOPATH(t *testing.T, exporter packagestest.Exporter) { |
860 | t.Skip("Not fixed yet. See golang.org/issue/40825.") |
861 | |
862 | exported := packagestest.Export(t, exporter, []packagestest.Module{ |
863 | { |
864 | Name: "golang.org/fake", |
865 | Files: map[string]interface{}{ |
866 | "x/x.go": `package x`, |
867 | "x/x_test.go": ``, |
868 | }, |
869 | }, |
870 | }) |
871 | defer exported.Cleanup() |
872 | |
873 | dir := filepath.Dir(filepath.Dir(exported.File("golang.org/fake", "x/x.go"))) |
874 | |
875 | exported.Config.Mode = everythingMode |
876 | exported.Config.Tests = true |
877 | |
878 | initial, err := packages.Load(exported.Config, fmt.Sprintf("%s/...", dir)) |
879 | if err != nil { |
880 | t.Fatal(err) |
881 | } |
882 | pkg := initial[0] |
883 | if len(pkg.CompiledGoFiles) != 2 { |
884 | t.Fatalf("expected at least 2 CompiledGoFiles for %s, got %v", pkg.PkgPath, len(pkg.CompiledGoFiles)) |
885 | } |
886 | } |
887 | |
888 | // Reproduces golang/go#40685. |
889 | func TestAddImportInOverlay(t *testing.T) { |
890 | testAllOrModulesParallel(t, testAddImportInOverlay) |
891 | } |
892 | func testAddImportInOverlay(t *testing.T, exporter packagestest.Exporter) { |
893 | exported := packagestest.Export(t, exporter, []packagestest.Module{ |
894 | { |
895 | Name: "golang.org/fake", |
896 | Files: map[string]interface{}{ |
897 | "a/a.go": `package a |
898 | |
899 | import ( |
900 | "fmt" |
901 | ) |
902 | |
903 | func _() { |
904 | fmt.Println("") |
905 | os.Stat("") |
906 | }`, |
907 | "a/a_test.go": `package a |
908 | |
909 | import ( |
910 | "os" |
911 | "testing" |
912 | ) |
913 | |
914 | func TestA(t *testing.T) { |
915 | os.Stat("") |
916 | }`, |
917 | }, |
918 | }, |
919 | }) |
920 | defer exported.Cleanup() |
921 | |
922 | exported.Config.Mode = everythingMode |
923 | exported.Config.Tests = true |
924 | |
925 | dir := filepath.Dir(exported.File("golang.org/fake", "a/a.go")) |
926 | exported.Config.Overlay = map[string][]byte{ |
927 | filepath.Join(dir, "a.go"): []byte(`package a |
928 | |
929 | import ( |
930 | "fmt" |
931 | "os" |
932 | ) |
933 | |
934 | func _() { |
935 | fmt.Println("") |
936 | os.Stat("") |
937 | } |
938 | `), |
939 | } |
940 | initial, err := packages.Load(exported.Config, "golang.org/fake/a") |
941 | if err != nil { |
942 | t.Fatal(err) |
943 | } |
944 | pkg := initial[0] |
945 | var foundOs bool |
946 | for _, imp := range pkg.Imports { |
947 | if imp.PkgPath == "os" { |
948 | foundOs = true |
949 | break |
950 | } |
951 | } |
952 | if !foundOs { |
953 | t.Fatalf(`expected import "os", found none: %v`, pkg.Imports) |
954 | } |
955 | } |
956 | |
957 | // Tests that overlays are applied for different kinds of load patterns. |
958 | func TestLoadDifferentPatterns(t *testing.T) { |
959 | testAllOrModulesParallel(t, testLoadDifferentPatterns) |
960 | } |
961 | func testLoadDifferentPatterns(t *testing.T, exporter packagestest.Exporter) { |
962 | exported := packagestest.Export(t, exporter, []packagestest.Module{ |
963 | { |
964 | Name: "golang.org/fake", |
965 | Files: map[string]interface{}{ |
966 | "foo.txt": "placeholder", |
967 | "b/b.go": `package b |
968 | import "golang.org/fake/a" |
969 | func _() { |
970 | a.Hi() |
971 | } |
972 | `, |
973 | }, |
974 | }, |
975 | }) |
976 | defer exported.Cleanup() |
977 | |
978 | exported.Config.Mode = everythingMode |
979 | exported.Config.Tests = true |
980 | |
981 | dir := filepath.Dir(exported.File("golang.org/fake", "foo.txt")) |
982 | exported.Config.Overlay = map[string][]byte{ |
983 | filepath.Join(dir, "a", "a.go"): []byte(`package a |
984 | import "fmt" |
985 | func Hi() { |
986 | fmt.Println("") |
987 | } |
988 | `), |
989 | } |
990 | for _, tc := range []struct { |
991 | pattern string |
992 | }{ |
993 | {"golang.org/fake/a"}, |
994 | {"golang.org/fake/..."}, |
995 | {fmt.Sprintf("file=%s", filepath.Join(dir, "a", "a.go"))}, |
996 | } { |
997 | t.Run(tc.pattern, func(t *testing.T) { |
998 | initial, err := packages.Load(exported.Config, tc.pattern) |
999 | if err != nil { |
1000 | t.Fatal(err) |
1001 | } |
1002 | var match *packages.Package |
1003 | for _, pkg := range initial { |
1004 | if pkg.PkgPath == "golang.org/fake/a" { |
1005 | match = pkg |
1006 | break |
1007 | } |
1008 | } |
1009 | if match == nil { |
1010 | t.Fatalf(`expected package path "golang.org/fake/a", got none`) |
1011 | } |
1012 | if match.PkgPath != "golang.org/fake/a" { |
1013 | t.Fatalf(`expected package path "golang.org/fake/a", got %q`, match.PkgPath) |
1014 | } |
1015 | if _, ok := match.Imports["fmt"]; !ok { |
1016 | t.Fatalf(`expected import "fmt", got none`) |
1017 | } |
1018 | }) |
1019 | } |
1020 | |
1021 | // Now, load "golang.org/fake/b" and confirm that "golang.org/fake/a" is |
1022 | // not returned as a root. |
1023 | initial, err := packages.Load(exported.Config, "golang.org/fake/b") |
1024 | if err != nil { |
1025 | t.Fatal(err) |
1026 | } |
1027 | if len(initial) > 1 { |
1028 | t.Fatalf("expected 1 package, got %v", initial) |
1029 | } |
1030 | pkg := initial[0] |
1031 | if pkg.PkgPath != "golang.org/fake/b" { |
1032 | t.Fatalf(`expected package path "golang.org/fake/b", got %q`, pkg.PkgPath) |
1033 | } |
1034 | if _, ok := pkg.Imports["golang.org/fake/a"]; !ok { |
1035 | t.Fatalf(`expected import "golang.org/fake/a", got none`) |
1036 | } |
1037 | } |
1038 | |
1039 | // Tests that overlays are applied for a replaced module. |
1040 | // This does not use go/packagestest because it needs to write a replace |
1041 | // directive with an absolute path in one of the module's go.mod files. |
1042 | func TestOverlaysInReplace(t *testing.T) { |
1043 | testenv.NeedsGoPackages(t) |
1044 | t.Parallel() |
1045 | |
1046 | // Create module b.com in a temporary directory. Do not add any Go files |
1047 | // on disk. |
1048 | tmpPkgs, err := ioutil.TempDir("", "modules") |
1049 | if err != nil { |
1050 | t.Fatal(err) |
1051 | } |
1052 | defer os.RemoveAll(tmpPkgs) |
1053 | |
1054 | dirB := filepath.Join(tmpPkgs, "b") |
1055 | if err := os.Mkdir(dirB, 0775); err != nil { |
1056 | t.Fatal(err) |
1057 | } |
1058 | if err := ioutil.WriteFile(filepath.Join(dirB, "go.mod"), []byte(fmt.Sprintf("module %s.com", dirB)), 0775); err != nil { |
1059 | t.Fatal(err) |
1060 | } |
1061 | if err := os.MkdirAll(filepath.Join(dirB, "inner"), 0775); err != nil { |
1062 | t.Fatal(err) |
1063 | } |
1064 | |
1065 | // Create a separate module that requires and replaces b.com. |
1066 | tmpWorkspace, err := ioutil.TempDir("", "workspace") |
1067 | if err != nil { |
1068 | t.Fatal(err) |
1069 | } |
1070 | defer os.RemoveAll(tmpWorkspace) |
1071 | goModContent := fmt.Sprintf(`module workspace.com |
1072 | |
1073 | require ( |
1074 | b.com v0.0.0-00010101000000-000000000000 |
1075 | ) |
1076 | |
1077 | replace ( |
1078 | b.com => %s |
1079 | ) |
1080 | `, dirB) |
1081 | if err := ioutil.WriteFile(filepath.Join(tmpWorkspace, "go.mod"), []byte(goModContent), 0775); err != nil { |
1082 | t.Fatal(err) |
1083 | } |
1084 | |
1085 | // Add Go files for b.com/inner in an overlay and try loading it from the |
1086 | // workspace.com module. |
1087 | config := &packages.Config{ |
1088 | Dir: tmpWorkspace, |
1089 | Mode: packages.LoadAllSyntax, |
1090 | Logf: t.Logf, |
1091 | Overlay: map[string][]byte{ |
1092 | filepath.Join(dirB, "inner", "b.go"): []byte(`package inner; import "fmt"; func _() { fmt.Println("");`), |
1093 | }, |
1094 | } |
1095 | initial, err := packages.Load(config, "b.com/...") |
1096 | if err != nil { |
1097 | t.Error(err) |
1098 | } |
1099 | if len(initial) != 1 { |
1100 | t.Fatalf(`expected 1 package, got %v`, len(initial)) |
1101 | } |
1102 | pkg := initial[0] |
1103 | if pkg.PkgPath != "b.com/inner" { |
1104 | t.Fatalf(`expected package path "b.com/inner", got %q`, pkg.PkgPath) |
1105 | } |
1106 | if _, ok := pkg.Imports["fmt"]; !ok { |
1107 | t.Fatalf(`expected import "fmt", got none`) |
1108 | } |
1109 | } |
1110 |
Members