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