1// Copyright 2018 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
5package packages_test
6
7import (
8	"bytes"
9	"encoding/json"
10	"fmt"
11	"go/ast"
12	constantpkg "go/constant"
13	"go/parser"
14	"go/token"
15	"go/types"
16	"os"
17	"path/filepath"
18	"reflect"
19	"runtime"
20	"sort"
21	"strings"
22	"testing"
23
24	"golang.org/x/tools/go/packages"
25	"golang.org/x/tools/go/packages/packagestest"
26)
27
28// TODO(matloob): remove this once Go 1.12 is released as we will end support
29// for versions of go list before Go 1.10.4.
30var usesOldGolist = false
31
32// TODO(adonovan): more test cases to write:
33//
34// - When the tests fail, make them print a 'cd & load' command
35//   that will allow the maintainer to interact with the failing scenario.
36// - errors in go-list metadata
37// - a foo.test package that cannot be built for some reason (e.g.
38//   import error) will result in a JSON blob with no name and a
39//   nonexistent testmain file in GoFiles. Test that we handle this
40//   gracefully.
41// - test more Flags.
42//
43// LoadSyntax & LoadAllSyntax modes:
44//   - Fset may be user-supplied or not.
45//   - Packages.Info is correctly set.
46//   - typechecker configuration is honored
47//   - import cycles are gracefully handled in type checker.
48//   - test typechecking of generated test main and cgo.
49
50// The zero-value of Config has LoadFiles mode.
51func TestLoadZeroConfig(t *testing.T) {
52	initial, err := packages.Load(nil, "hash")
53	if err != nil {
54		t.Fatal(err)
55	}
56	if len(initial) != 1 {
57		t.Fatalf("got %s, want [hash]", initial)
58	}
59	hash := initial[0]
60	// Even though the hash package has imports,
61	// they are not reported.
62	got := fmt.Sprintf("name=%s srcs=%v imports=%v", hash.Name, srcs(hash), hash.Imports)
63	want := "name=hash srcs=[hash.go] imports=map[]"
64	if got != want {
65		t.Fatalf("got %s, want %s", got, want)
66	}
67}
68
69func TestLoadImportsGraph(t *testing.T) { packagestest.TestAll(t, testLoadImportsGraph) }
70func testLoadImportsGraph(t *testing.T, exporter packagestest.Exporter) {
71	exported := packagestest.Export(t, exporter, []packagestest.Module{{
72		Name: "golang.org/fake",
73		Files: map[string]interface{}{
74			"a/a.go":             `package a; const A = 1`,
75			"b/b.go":             `package b; import ("golang.org/fake/a"; _ "errors"); var B = a.A`,
76			"c/c.go":             `package c; import (_ "golang.org/fake/b"; _ "unsafe")`,
77			"c/c2.go":            "// +build ignore\n\n" + `package c; import _ "fmt"`,
78			"subdir/d/d.go":      `package d`,
79			"subdir/d/d_test.go": `package d; import _ "math/bits"`,
80			"subdir/d/x_test.go": `package d_test; import _ "golang.org/fake/subdir/d"`, // TODO(adonovan): test bad import here
81			"subdir/e/d.go":      `package e`,
82			"e/e.go":             `package main; import _ "golang.org/fake/b"`,
83			"e/e2.go":            `package main; import _ "golang.org/fake/c"`,
84			"f/f.go":             `package f`,
85		}}})
86	defer exported.Cleanup()
87	exported.Config.Mode = packages.LoadImports
88	initial, err := packages.Load(exported.Config, "golang.org/fake/c", "golang.org/fake/subdir/d", "golang.org/fake/e")
89	if err != nil {
90		t.Fatal(err)
91	}
92
93	// Check graph topology.
94	graph, all := importGraph(initial)
95	wantGraph := `
96  errors
97  golang.org/fake/a
98  golang.org/fake/b
99* golang.org/fake/c
100* golang.org/fake/e
101* golang.org/fake/subdir/d
102  unsafe
103  golang.org/fake/b -> errors
104  golang.org/fake/b -> golang.org/fake/a
105  golang.org/fake/c -> golang.org/fake/b
106  golang.org/fake/c -> unsafe
107  golang.org/fake/e -> golang.org/fake/b
108  golang.org/fake/e -> golang.org/fake/c
109`[1:]
110
111	if graph != wantGraph {
112		t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph)
113	}
114
115	exported.Config.Tests = true
116	initial, err = packages.Load(exported.Config, "golang.org/fake/c", "golang.org/fake/subdir/d", "golang.org/fake/e")
117	if err != nil {
118		t.Fatal(err)
119	}
120
121	// Check graph topology.
122	graph, all = importGraph(initial)
123	wantGraph = `
124  errors
125  golang.org/fake/a
126  golang.org/fake/b
127* golang.org/fake/c
128* golang.org/fake/e
129* golang.org/fake/subdir/d
130* golang.org/fake/subdir/d [golang.org/fake/subdir/d.test]
131* golang.org/fake/subdir/d.test
132* golang.org/fake/subdir/d_test [golang.org/fake/subdir/d.test]
133  math/bits
134  unsafe
135  golang.org/fake/b -> errors
136  golang.org/fake/b -> golang.org/fake/a
137  golang.org/fake/c -> golang.org/fake/b
138  golang.org/fake/c -> unsafe
139  golang.org/fake/e -> golang.org/fake/b
140  golang.org/fake/e -> golang.org/fake/c
141  golang.org/fake/subdir/d [golang.org/fake/subdir/d.test] -> math/bits
142  golang.org/fake/subdir/d.test -> golang.org/fake/subdir/d [golang.org/fake/subdir/d.test]
143  golang.org/fake/subdir/d.test -> golang.org/fake/subdir/d_test [golang.org/fake/subdir/d.test]
144  golang.org/fake/subdir/d.test -> os (pruned)
145  golang.org/fake/subdir/d.test -> testing (pruned)
146  golang.org/fake/subdir/d.test -> testing/internal/testdeps (pruned)
147  golang.org/fake/subdir/d_test [golang.org/fake/subdir/d.test] -> golang.org/fake/subdir/d [golang.org/fake/subdir/d.test]
148`[1:]
149
150	if graph != wantGraph {
151		t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph)
152	}
153
154	// Check node information: kind, name, srcs.
155	for _, test := range []struct {
156		id       string
157		wantName string
158		wantKind string
159		wantSrcs string
160	}{
161		{"golang.org/fake/a", "a", "package", "a.go"},
162		{"golang.org/fake/b", "b", "package", "b.go"},
163		{"golang.org/fake/c", "c", "package", "c.go"}, // c2.go is ignored
164		{"golang.org/fake/e", "main", "command", "e.go e2.go"},
165		{"errors", "errors", "package", "errors.go"},
166		{"golang.org/fake/subdir/d", "d", "package", "d.go"},
167		{"golang.org/fake/subdir/d.test", "main", "command", "0.go"},
168		{"unsafe", "unsafe", "package", ""},
169	} {
170		p, ok := all[test.id]
171		if !ok {
172			t.Errorf("no package %s", test.id)
173			continue
174		}
175		if p.Name != test.wantName {
176			t.Errorf("%s.Name = %q, want %q", test.id, p.Name, test.wantName)
177		}
178
179		// kind
180		var kind string
181		if p.Name == "main" {
182			kind += "command"
183		} else {
184			kind += "package"
185		}
186		if kind != test.wantKind {
187			t.Errorf("%s.Kind = %q, want %q", test.id, kind, test.wantKind)
188		}
189
190		if srcs := strings.Join(srcs(p), " "); srcs != test.wantSrcs {
191			t.Errorf("%s.Srcs = [%s], want [%s]", test.id, srcs, test.wantSrcs)
192		}
193	}
194
195	// Test an ad-hoc package, analogous to "go run hello.go".
196	if initial, err := packages.Load(exported.Config, exported.File("golang.org/fake", "c/c.go")); len(initial) == 0 {
197		t.Errorf("failed to obtain metadata for ad-hoc package: %s", err)
198	} else {
199		got := fmt.Sprintf("%s %s", initial[0].ID, srcs(initial[0]))
200		if want := "command-line-arguments [c.go]"; got != want {
201			t.Errorf("oops: got %s, want %s", got, want)
202		}
203	}
204
205	// Wildcards
206	// See StdlibTest for effective test of "std" wildcard.
207	// TODO(adonovan): test "all" returns everything in the current module.
208	{
209		// "..." (subdirectory)
210		initial, err = packages.Load(exported.Config, "golang.org/fake/subdir/...")
211		if err != nil {
212			t.Fatal(err)
213		}
214		graph, all = importGraph(initial)
215		wantGraph = `
216* golang.org/fake/subdir/d
217* golang.org/fake/subdir/d [golang.org/fake/subdir/d.test]
218* golang.org/fake/subdir/d.test
219* golang.org/fake/subdir/d_test [golang.org/fake/subdir/d.test]
220* golang.org/fake/subdir/e
221  math/bits
222  golang.org/fake/subdir/d [golang.org/fake/subdir/d.test] -> math/bits
223  golang.org/fake/subdir/d.test -> golang.org/fake/subdir/d [golang.org/fake/subdir/d.test]
224  golang.org/fake/subdir/d.test -> golang.org/fake/subdir/d_test [golang.org/fake/subdir/d.test]
225  golang.org/fake/subdir/d.test -> os (pruned)
226  golang.org/fake/subdir/d.test -> testing (pruned)
227  golang.org/fake/subdir/d.test -> testing/internal/testdeps (pruned)
228  golang.org/fake/subdir/d_test [golang.org/fake/subdir/d.test] -> golang.org/fake/subdir/d [golang.org/fake/subdir/d.test]
229`[1:]
230
231		if graph != wantGraph {
232			t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph)
233		}
234	}
235}
236
237func TestLoadImportsTestVariants(t *testing.T) { packagestest.TestAll(t, testLoadImportsTestVariants) }
238func testLoadImportsTestVariants(t *testing.T, exporter packagestest.Exporter) {
239	exported := packagestest.Export(t, exporter, []packagestest.Module{{
240		Name: "golang.org/fake",
241		Files: map[string]interface{}{
242			"a/a.go":       `package a; import _ "golang.org/fake/b"`,
243			"b/b.go":       `package b`,
244			"b/b_test.go":  `package b`,
245			"b/bx_test.go": `package b_test; import _ "golang.org/fake/a"`,
246		}}})
247	defer exported.Cleanup()
248	exported.Config.Mode = packages.LoadImports
249	exported.Config.Tests = true
250
251	initial, err := packages.Load(exported.Config, "golang.org/fake/a", "golang.org/fake/b")
252	if err != nil {
253		t.Fatal(err)
254	}
255
256	// Check graph topology.
257	graph, _ := importGraph(initial)
258	wantGraph := `
259* golang.org/fake/a
260  golang.org/fake/a [golang.org/fake/b.test]
261* golang.org/fake/b
262* golang.org/fake/b [golang.org/fake/b.test]
263* golang.org/fake/b.test
264* golang.org/fake/b_test [golang.org/fake/b.test]
265  golang.org/fake/a -> golang.org/fake/b
266  golang.org/fake/a [golang.org/fake/b.test] -> golang.org/fake/b [golang.org/fake/b.test]
267  golang.org/fake/b.test -> golang.org/fake/b [golang.org/fake/b.test]
268  golang.org/fake/b.test -> golang.org/fake/b_test [golang.org/fake/b.test]
269  golang.org/fake/b.test -> os (pruned)
270  golang.org/fake/b.test -> testing (pruned)
271  golang.org/fake/b.test -> testing/internal/testdeps (pruned)
272  golang.org/fake/b_test [golang.org/fake/b.test] -> golang.org/fake/a [golang.org/fake/b.test]
273`[1:]
274
275	if graph != wantGraph {
276		t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph)
277	}
278}
279
280func TestLoadAbsolutePath(t *testing.T) {
281	exported := packagestest.Export(t, packagestest.GOPATH, []packagestest.Module{{
282		Name: "golang.org/gopatha",
283		Files: map[string]interface{}{
284			"a/a.go": `package a`,
285		}}, {
286		Name: "golang.org/gopathb",
287		Files: map[string]interface{}{
288			"b/b.go": `package b`,
289		}}})
290	defer exported.Cleanup()
291
292	initial, err := packages.Load(exported.Config, filepath.Dir(exported.File("golang.org/gopatha", "a/a.go")), filepath.Dir(exported.File("golang.org/gopathb", "b/b.go")))
293	if err != nil {
294		t.Fatalf("failed to load imports: %v", err)
295	}
296
297	got := []string{}
298	for _, p := range initial {
299		got = append(got, p.ID)
300	}
301	sort.Strings(got)
302	want := []string{"golang.org/gopatha/a", "golang.org/gopathb/b"}
303	if !reflect.DeepEqual(got, want) {
304		t.Fatalf("initial packages loaded: got [%s], want [%s]", got, want)
305	}
306}
307
308func TestVendorImports(t *testing.T) {
309	exported := packagestest.Export(t, packagestest.GOPATH, []packagestest.Module{{
310		Name: "golang.org/fake",
311		Files: map[string]interface{}{
312			"a/a.go":          `package a; import _ "b"; import _ "golang.org/fake/c";`,
313			"a/vendor/b/b.go": `package b; import _ "golang.org/fake/c"`,
314			"c/c.go":          `package c; import _ "b"`,
315			"c/vendor/b/b.go": `package b`,
316		}}})
317	defer exported.Cleanup()
318	exported.Config.Mode = packages.LoadImports
319	initial, err := packages.Load(exported.Config, "golang.org/fake/a", "golang.org/fake/c")
320	if err != nil {
321		t.Fatal(err)
322	}
323
324	graph, all := importGraph(initial)
325	wantGraph := `
326* golang.org/fake/a
327  golang.org/fake/a/vendor/b
328* golang.org/fake/c
329  golang.org/fake/c/vendor/b
330  golang.org/fake/a -> golang.org/fake/a/vendor/b
331  golang.org/fake/a -> golang.org/fake/c
332  golang.org/fake/a/vendor/b -> golang.org/fake/c
333  golang.org/fake/c -> golang.org/fake/c/vendor/b
334`[1:]
335	if graph != wantGraph {
336		t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph)
337	}
338
339	for _, test := range []struct {
340		pattern     string
341		wantImports string
342	}{
343		{"golang.org/fake/a", "b:golang.org/fake/a/vendor/b golang.org/fake/c:golang.org/fake/c"},
344		{"golang.org/fake/c", "b:golang.org/fake/c/vendor/b"},
345		{"golang.org/fake/a/vendor/b", "golang.org/fake/c:golang.org/fake/c"},
346		{"golang.org/fake/c/vendor/b", ""},
347	} {
348		// Test the import paths.
349		pkg := all[test.pattern]
350		if imports := strings.Join(imports(pkg), " "); imports != test.wantImports {
351			t.Errorf("package %q: got %s, want %s", test.pattern, imports, test.wantImports)
352		}
353	}
354}
355
356func imports(p *packages.Package) []string {
357	if p == nil {
358		return nil
359	}
360	keys := make([]string, 0, len(p.Imports))
361	for k, v := range p.Imports {
362		keys = append(keys, fmt.Sprintf("%s:%s", k, v.ID))
363	}
364	sort.Strings(keys)
365	return keys
366}
367
368func TestConfigDir(t *testing.T) { packagestest.TestAll(t, testConfigDir) }
369func testConfigDir(t *testing.T, exporter packagestest.Exporter) {
370	exported := packagestest.Export(t, exporter, []packagestest.Module{{
371		Name: "golang.org/fake",
372		Files: map[string]interface{}{
373			"a/a.go":   `package a; const Name = "a" `,
374			"a/b/b.go": `package b; const Name = "a/b"`,
375			"b/b.go":   `package b; const Name = "b"`,
376		}}})
377	defer exported.Cleanup()
378	aDir := filepath.Dir(exported.File("golang.org/fake", "a/a.go"))
379	bDir := filepath.Dir(exported.File("golang.org/fake", "b/b.go"))
380	baseDir := filepath.Dir(aDir)
381
382	for _, test := range []struct {
383		dir     string
384		pattern string
385		want    string // value of Name constant
386		fails   bool
387	}{
388		{dir: bDir, pattern: "golang.org/fake/a", want: `"a"`},
389		{dir: bDir, pattern: "golang.org/fake/b", want: `"b"`},
390		{dir: bDir, pattern: "./a", fails: true},
391		{dir: bDir, pattern: "./b", fails: true},
392		{dir: baseDir, pattern: "golang.org/fake/a", want: `"a"`},
393		{dir: baseDir, pattern: "golang.org/fake/b", want: `"b"`},
394		{dir: baseDir, pattern: "./a", want: `"a"`},
395		{dir: baseDir, pattern: "./b", want: `"b"`},
396		{dir: aDir, pattern: "golang.org/fake/a", want: `"a"`},
397		{dir: aDir, pattern: "golang.org/fake/b", want: `"b"`},
398		{dir: aDir, pattern: "./a", fails: true},
399		{dir: aDir, pattern: "./b", want: `"a/b"`},
400	} {
401		exported.Config.Mode = packages.LoadSyntax // Use LoadSyntax to ensure that files can be opened.
402		exported.Config.Dir = test.dir
403		initial, err := packages.Load(exported.Config, test.pattern)
404		var got string
405		fails := false
406		if err != nil {
407			fails = true
408		} else if len(initial) > 0 {
409			if len(initial[0].Errors) > 0 {
410				fails = true
411			} else if c := constant(initial[0], "Name"); c != nil {
412				got = c.Val().String()
413			}
414		}
415		if got != test.want {
416			t.Errorf("dir %q, pattern %q: got %s, want %s",
417				test.dir, test.pattern, got, test.want)
418		}
419		if fails != test.fails {
420			// TODO: remove when go#28023 is fixed
421			if test.fails && strings.HasPrefix(test.pattern, "./") && exporter == packagestest.Modules {
422				// Currently go list in module mode does not handle missing directories correctly.
423				continue
424			}
425			t.Errorf("dir %q, pattern %q: error %v, want %v",
426				test.dir, test.pattern, fails, test.fails)
427		}
428	}
429}
430
431func TestConfigFlags(t *testing.T) { packagestest.TestAll(t, testConfigFlags) }
432func testConfigFlags(t *testing.T, exporter packagestest.Exporter) {
433	// Test satisfying +build line tags, with -tags flag.
434	exported := packagestest.Export(t, exporter, []packagestest.Module{{
435		Name: "golang.org/fake",
436		Files: map[string]interface{}{
437			// package a
438			"a/a.go": `package a; import _ "golang.org/fake/a/b"`,
439			"a/b.go": `// +build tag
440
441package a`,
442			"a/c.go": `// +build tag tag2
443
444package a`,
445			"a/d.go": `// +build tag,tag2
446
447package a`,
448			// package a/b
449			"a/b/a.go": `package b`,
450			"a/b/b.go": `// +build tag
451
452package b`,
453		}}})
454	defer exported.Cleanup()
455
456	for _, test := range []struct {
457		pattern        string
458		tags           []string
459		wantSrcs       string
460		wantImportSrcs string
461	}{
462		{`golang.org/fake/a`, []string{}, "a.go", "a.go"},
463		{`golang.org/fake/a`, []string{`-tags=tag`}, "a.go b.go c.go", "a.go b.go"},
464		{`golang.org/fake/a`, []string{`-tags=tag2`}, "a.go c.go", "a.go"},
465		{`golang.org/fake/a`, []string{`-tags=tag tag2`}, "a.go b.go c.go d.go", "a.go b.go"},
466	} {
467		exported.Config.Mode = packages.LoadImports
468		exported.Config.BuildFlags = test.tags
469
470		initial, err := packages.Load(exported.Config, test.pattern)
471		if err != nil {
472			t.Error(err)
473			continue
474		}
475		if len(initial) != 1 {
476			t.Errorf("test tags %v: pattern %s, expected 1 package, got %d packages.", test.tags, test.pattern, len(initial))
477			continue
478		}
479		pkg := initial[0]
480		if srcs := strings.Join(srcs(pkg), " "); srcs != test.wantSrcs {
481			t.Errorf("test tags %v: srcs of package %s = [%s], want [%s]", test.tags, test.pattern, srcs, test.wantSrcs)
482		}
483		for path, ipkg := range pkg.Imports {
484			if srcs := strings.Join(srcs(ipkg), " "); srcs != test.wantImportSrcs {
485				t.Errorf("build tags %v: srcs of imported package %s = [%s], want [%s]", test.tags, path, srcs, test.wantImportSrcs)
486			}
487		}
488
489	}
490}
491
492func TestLoadTypes(t *testing.T) { packagestest.TestAll(t, testLoadTypes) }
493func testLoadTypes(t *testing.T, exporter packagestest.Exporter) {
494	// In LoadTypes and LoadSyntax modes, the compiler will
495	// fail to generate an export data file for c, because it has
496	// a type error.  The loader should fall back loading a and c
497	// from source, but use the export data for b.
498
499	exported := packagestest.Export(t, exporter, []packagestest.Module{{
500		Name: "golang.org/fake",
501		Files: map[string]interface{}{
502			"a/a.go": `package a; import "golang.org/fake/b"; import "golang.org/fake/c"; const A = "a" + b.B + c.C`,
503			"b/b.go": `package b; const B = "b"`,
504			"c/c.go": `package c; const C = "c" + 1`,
505		}}})
506	defer exported.Cleanup()
507
508	exported.Config.Mode = packages.LoadTypes
509	initial, err := packages.Load(exported.Config, "golang.org/fake/a")
510	if err != nil {
511		t.Fatal(err)
512	}
513
514	graph, all := importGraph(initial)
515	wantGraph := `
516* golang.org/fake/a
517  golang.org/fake/b
518  golang.org/fake/c
519  golang.org/fake/a -> golang.org/fake/b
520  golang.org/fake/a -> golang.org/fake/c
521`[1:]
522	if graph != wantGraph {
523		t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph)
524	}
525
526	for _, test := range []struct {
527		id         string
528		wantSyntax bool
529	}{
530		{"golang.org/fake/a", true},  // need src, no export data for c
531		{"golang.org/fake/b", false}, // use export data
532		{"golang.org/fake/c", true},  // need src, no export data for c
533	} {
534		if usesOldGolist && !test.wantSyntax {
535			// legacy go list always upgrades to LoadAllSyntax, syntax will be filled in.
536			// still check that types information is complete.
537			test.wantSyntax = true
538		}
539		p := all[test.id]
540		if p == nil {
541			t.Errorf("missing package: %s", test.id)
542			continue
543		}
544		if p.Types == nil {
545			t.Errorf("missing types.Package for %s", p)
546			continue
547		} else if !p.Types.Complete() {
548			t.Errorf("incomplete types.Package for %s", p)
549		}
550		if (p.Syntax != nil) != test.wantSyntax {
551			if test.wantSyntax {
552				t.Errorf("missing ast.Files for %s", p)
553			} else {
554				t.Errorf("unexpected ast.Files for for %s", p)
555			}
556		}
557	}
558}
559
560func TestLoadSyntaxOK(t *testing.T) { packagestest.TestAll(t, testLoadSyntaxOK) }
561func testLoadSyntaxOK(t *testing.T, exporter packagestest.Exporter) {
562	exported := packagestest.Export(t, exporter, []packagestest.Module{{
563		Name: "golang.org/fake",
564		Files: map[string]interface{}{
565			"a/a.go": `package a; import "golang.org/fake/b"; const A = "a" + b.B`,
566			"b/b.go": `package b; import "golang.org/fake/c"; const B = "b" + c.C`,
567			"c/c.go": `package c; import "golang.org/fake/d"; const C = "c" + d.D`,
568			"d/d.go": `package d; import "golang.org/fake/e"; const D = "d" + e.E`,
569			"e/e.go": `package e; import "golang.org/fake/f"; const E = "e" + f.F`,
570			"f/f.go": `package f; const F = "f"`,
571		}}})
572	defer exported.Cleanup()
573
574	exported.Config.Mode = packages.LoadSyntax
575	initial, err := packages.Load(exported.Config, "golang.org/fake/a", "golang.org/fake/c")
576	if err != nil {
577		t.Fatal(err)
578	}
579
580	graph, all := importGraph(initial)
581	wantGraph := `
582* golang.org/fake/a
583  golang.org/fake/b
584* golang.org/fake/c
585  golang.org/fake/d
586  golang.org/fake/e
587  golang.org/fake/f
588  golang.org/fake/a -> golang.org/fake/b
589  golang.org/fake/b -> golang.org/fake/c
590  golang.org/fake/c -> golang.org/fake/d
591  golang.org/fake/d -> golang.org/fake/e
592  golang.org/fake/e -> golang.org/fake/f
593`[1:]
594	if graph != wantGraph {
595		t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph)
596	}
597
598	for _, test := range []struct {
599		id           string
600		wantSyntax   bool
601		wantComplete bool
602	}{
603		{"golang.org/fake/a", true, true},   // source package
604		{"golang.org/fake/b", true, true},   // source package
605		{"golang.org/fake/c", true, true},   // source package
606		{"golang.org/fake/d", false, true},  // export data package
607		{"golang.org/fake/e", false, false}, // export data package
608		{"golang.org/fake/f", false, false}, // export data package
609	} {
610		// TODO(matloob): The legacy go list based support loads
611		// everything from source because it doesn't do a build
612		// and the .a files don't exist.
613		// Can we simulate its existence?
614		if usesOldGolist {
615			test.wantComplete = true
616			test.wantSyntax = true
617		}
618		p := all[test.id]
619		if p == nil {
620			t.Errorf("missing package: %s", test.id)
621			continue
622		}
623		if p.Types == nil {
624			t.Errorf("missing types.Package for %s", p)
625			continue
626		} else if p.Types.Complete() != test.wantComplete {
627			if test.wantComplete {
628				t.Errorf("incomplete types.Package for %s", p)
629			} else {
630				t.Errorf("unexpected complete types.Package for %s", p)
631			}
632		}
633		if (p.Syntax != nil) != test.wantSyntax {
634			if test.wantSyntax {
635				t.Errorf("missing ast.Files for %s", p)
636			} else {
637				t.Errorf("unexpected ast.Files for for %s", p)
638			}
639		}
640		if p.Errors != nil {
641			t.Errorf("errors in package: %s: %s", p, p.Errors)
642		}
643	}
644
645	// Check value of constant.
646	aA := constant(all["golang.org/fake/a"], "A")
647	if aA == nil {
648		t.Fatalf("a.A: got nil")
649	}
650	if got, want := fmt.Sprintf("%v %v", aA, aA.Val()), `const golang.org/fake/a.A untyped string "abcdef"`; got != want {
651		t.Errorf("a.A: got %s, want %s", got, want)
652	}
653}
654
655func TestLoadDiamondTypes(t *testing.T) { packagestest.TestAll(t, testLoadDiamondTypes) }
656func testLoadDiamondTypes(t *testing.T, exporter packagestest.Exporter) {
657	// We make a diamond dependency and check the type d.D is the same through both paths
658	exported := packagestest.Export(t, exporter, []packagestest.Module{{
659		Name: "golang.org/fake",
660		Files: map[string]interface{}{
661			"a/a.go": `package a; import ("golang.org/fake/b"; "golang.org/fake/c"); var _ = b.B == c.C`,
662			"b/b.go": `package b; import "golang.org/fake/d"; var B d.D`,
663			"c/c.go": `package c; import "golang.org/fake/d"; var C d.D`,
664			"d/d.go": `package d; type D int`,
665		}}})
666	defer exported.Cleanup()
667
668	exported.Config.Mode = packages.LoadSyntax
669	initial, err := packages.Load(exported.Config, "golang.org/fake/a")
670	if err != nil {
671		t.Fatal(err)
672	}
673	packages.Visit(initial, nil, func(pkg *packages.Package) {
674		for _, err := range pkg.Errors {
675			t.Errorf("package %s: %v", pkg.ID, err)
676		}
677	})
678
679	graph, _ := importGraph(initial)
680	wantGraph := `
681* golang.org/fake/a
682  golang.org/fake/b
683  golang.org/fake/c
684  golang.org/fake/d
685  golang.org/fake/a -> golang.org/fake/b
686  golang.org/fake/a -> golang.org/fake/c
687  golang.org/fake/b -> golang.org/fake/d
688  golang.org/fake/c -> golang.org/fake/d
689`[1:]
690	if graph != wantGraph {
691		t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph)
692	}
693}
694
695func TestLoadSyntaxError(t *testing.T) { packagestest.TestAll(t, testLoadSyntaxError) }
696func testLoadSyntaxError(t *testing.T, exporter packagestest.Exporter) {
697	// A type error in a lower-level package (e) prevents go list
698	// from producing export data for all packages that depend on it
699	// [a-e]. Only f should be loaded from export data, and the rest
700	// should be IllTyped.
701	exported := packagestest.Export(t, exporter, []packagestest.Module{{
702		Name: "golang.org/fake",
703		Files: map[string]interface{}{
704			"a/a.go": `package a; import "golang.org/fake/b"; const A = "a" + b.B`,
705			"b/b.go": `package b; import "golang.org/fake/c"; const B = "b" + c.C`,
706			"c/c.go": `package c; import "golang.org/fake/d"; const C = "c" + d.D`,
707			"d/d.go": `package d; import "golang.org/fake/e"; const D = "d" + e.E`,
708			"e/e.go": `package e; import "golang.org/fake/f"; const E = "e" + f.F + 1`, // type error
709			"f/f.go": `package f; const F = "f"`,
710		}}})
711	defer exported.Cleanup()
712
713	exported.Config.Mode = packages.LoadSyntax
714	initial, err := packages.Load(exported.Config, "golang.org/fake/a", "golang.org/fake/c")
715	if err != nil {
716		t.Fatal(err)
717	}
718
719	all := make(map[string]*packages.Package)
720	packages.Visit(initial, nil, func(p *packages.Package) {
721		all[p.ID] = p
722	})
723
724	for _, test := range []struct {
725		id           string
726		wantSyntax   bool
727		wantIllTyped bool
728	}{
729		{"golang.org/fake/a", true, true},
730		{"golang.org/fake/b", true, true},
731		{"golang.org/fake/c", true, true},
732		{"golang.org/fake/d", true, true},
733		{"golang.org/fake/e", true, true},
734		{"golang.org/fake/f", false, false},
735	} {
736		if usesOldGolist && !test.wantSyntax {
737			// legacy go list always upgrades to LoadAllSyntax, syntax will be filled in.
738			test.wantSyntax = true
739		}
740		p := all[test.id]
741		if p == nil {
742			t.Errorf("missing package: %s", test.id)
743			continue
744		}
745		if p.Types == nil {
746			t.Errorf("missing types.Package for %s", p)
747			continue
748		} else if !p.Types.Complete() {
749			t.Errorf("incomplete types.Package for %s", p)
750		}
751		if (p.Syntax != nil) != test.wantSyntax {
752			if test.wantSyntax {
753				t.Errorf("missing ast.Files for %s", test.id)
754			} else {
755				t.Errorf("unexpected ast.Files for for %s", test.id)
756			}
757		}
758		if p.IllTyped != test.wantIllTyped {
759			t.Errorf("IllTyped was %t for %s", p.IllTyped, test.id)
760		}
761	}
762
763	// Check value of constant.
764	aA := constant(all["golang.org/fake/a"], "A")
765	if aA == nil {
766		t.Fatalf("a.A: got nil")
767	}
768	if got, want := aA.String(), `const golang.org/fake/a.A invalid type`; got != want {
769		t.Errorf("a.A: got %s, want %s", got, want)
770	}
771}
772
773// This function tests use of the ParseFile hook to modify
774// the AST after parsing.
775func TestParseFileModifyAST(t *testing.T) { packagestest.TestAll(t, testParseFileModifyAST) }
776func testParseFileModifyAST(t *testing.T, exporter packagestest.Exporter) {
777	exported := packagestest.Export(t, exporter, []packagestest.Module{{
778		Name: "golang.org/fake",
779		Files: map[string]interface{}{
780			"a/a.go": `package a; const A = "a" `,
781		}}})
782	defer exported.Cleanup()
783
784	exported.Config.Mode = packages.LoadAllSyntax
785	exported.Config.ParseFile = func(fset *token.FileSet, filename string, src []byte) (*ast.File, error) {
786		const mode = parser.AllErrors | parser.ParseComments
787		f, err := parser.ParseFile(fset, filename, src, mode)
788		// modify AST to change `const A = "a"` to `const A = "b"`
789		spec := f.Decls[0].(*ast.GenDecl).Specs[0].(*ast.ValueSpec)
790		spec.Values[0].(*ast.BasicLit).Value = `"b"`
791		return f, err
792	}
793	initial, err := packages.Load(exported.Config, "golang.org/fake/a")
794	if err != nil {
795		t.Error(err)
796	}
797
798	// Check value of a.A has been set to "b"
799	a := initial[0]
800	got := constant(a, "A").Val().String()
801	if got != `"b"` {
802		t.Errorf("a.A: got %s, want %s", got, `"b"`)
803	}
804}
805
806func TestOverlay(t *testing.T) { packagestest.TestAll(t, testOverlay) }
807func testOverlay(t *testing.T, exporter packagestest.Exporter) {
808	exported := packagestest.Export(t, exporter, []packagestest.Module{{
809		Name: "golang.org/fake",
810		Files: map[string]interface{}{
811			"a/a.go": `package a; import "golang.org/fake/b"; const A = "a" + b.B`,
812			"b/b.go": `package b; import "golang.org/fake/c"; const B = "b" + c.C`,
813			"c/c.go": `package c; const C = "c"`,
814			"d/d.go": `package d; const D = "d"`,
815		}}})
816	defer exported.Cleanup()
817
818	for i, test := range []struct {
819		overlay  map[string][]byte
820		want     string // expected value of a.A
821		wantErrs []string
822	}{
823		{nil, `"abc"`, nil},                 // default
824		{map[string][]byte{}, `"abc"`, nil}, // empty overlay
825		{map[string][]byte{exported.File("golang.org/fake", "c/c.go"): []byte(`package c; const C = "C"`)}, `"abC"`, nil},
826		{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},
827		{map[string][]byte{exported.File("golang.org/fake", "b/b.go"): []byte(`package b; import "d"; const B = "B" + d.D`)}, `unknown`,
828			[]string{`could not import d (no metadata for d)`}},
829	} {
830		exported.Config.Overlay = test.overlay
831		exported.Config.Mode = packages.LoadAllSyntax
832		initial, err := packages.Load(exported.Config, "golang.org/fake/a")
833		if err != nil {
834			t.Error(err)
835			continue
836		}
837
838		// Check value of a.A.
839		a := initial[0]
840		aA := constant(a, "A")
841		if aA == nil {
842			t.Errorf("%d. a.A: got nil", i)
843			continue
844		}
845		got := aA.Val().String()
846		if got != test.want {
847			t.Errorf("%d. a.A: got %s, want %s", i, got, test.want)
848		}
849
850		// Check errors.
851		var errors []packages.Error
852		packages.Visit(initial, nil, func(pkg *packages.Package) {
853			errors = append(errors, pkg.Errors...)
854		})
855		if errs := errorMessages(errors); !reflect.DeepEqual(errs, test.wantErrs) {
856			t.Errorf("%d. got errors %s, want %s", i, errs, test.wantErrs)
857		}
858	}
859}
860
861func TestLoadAllSyntaxImportErrors(t *testing.T) {
862	packagestest.TestAll(t, testLoadAllSyntaxImportErrors)
863}
864func testLoadAllSyntaxImportErrors(t *testing.T, exporter packagestest.Exporter) {
865	// TODO(matloob): Remove this once go list -e -compiled is fixed. See golang.org/issue/26755
866	t.Skip("go list -compiled -e fails with non-zero exit status for empty packages")
867
868	exported := packagestest.Export(t, exporter, []packagestest.Module{{
869		Name: "golang.org/fake",
870		Files: map[string]interface{}{
871			"unicycle/unicycle.go": `package unicycle; import _ "unicycle"`,
872			"bicycle1/bicycle1.go": `package bicycle1; import _ "bicycle2"`,
873			"bicycle2/bicycle2.go": `package bicycle2; import _ "bicycle1"`,
874			"bad/bad.go":           `not a package declaration`,
875			"empty/README.txt":     `not a go file`,
876			"root/root.go": `package root
877import (
878	_ "bicycle1"
879	_ "unicycle"
880	_ "nonesuch"
881	_ "empty"
882	_ "bad"
883)`,
884		}}})
885	defer exported.Cleanup()
886
887	exported.Config.Mode = packages.LoadAllSyntax
888	initial, err := packages.Load(exported.Config, "root")
889	if err != nil {
890		t.Fatal(err)
891	}
892
893	// Cycle-forming edges are removed from the graph:
894	// 	bicycle2 -> bicycle1
895	//      unicycle -> unicycle
896	graph, all := importGraph(initial)
897	wantGraph := `
898  bicycle1
899  bicycle2
900* root
901  unicycle
902  bicycle1 -> bicycle2
903  root -> bicycle1
904  root -> unicycle
905`[1:]
906	if graph != wantGraph {
907		t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph)
908	}
909	for _, test := range []struct {
910		id       string
911		wantErrs []string
912	}{
913		{"bicycle1", nil},
914		{"bicycle2", []string{
915			"could not import bicycle1 (import cycle: [root bicycle1 bicycle2])",
916		}},
917		{"unicycle", []string{
918			"could not import unicycle (import cycle: [root unicycle])",
919		}},
920		{"root", []string{
921			`could not import bad (missing package: "bad")`,
922			`could not import empty (missing package: "empty")`,
923			`could not import nonesuch (missing package: "nonesuch")`,
924		}},
925	} {
926		p := all[test.id]
927		if p == nil {
928			t.Errorf("missing package: %s", test.id)
929			continue
930		}
931		if p.Types == nil {
932			t.Errorf("missing types.Package for %s", test.id)
933		}
934		if p.Syntax == nil {
935			t.Errorf("missing ast.Files for %s", test.id)
936		}
937		if !p.IllTyped {
938			t.Errorf("IllTyped was false for %s", test.id)
939		}
940		if errs := errorMessages(p.Errors); !reflect.DeepEqual(errs, test.wantErrs) {
941			t.Errorf("in package %s, got errors %s, want %s", p, errs, test.wantErrs)
942		}
943	}
944}
945
946func TestAbsoluteFilenames(t *testing.T) { packagestest.TestAll(t, testAbsoluteFilenames) }
947func testAbsoluteFilenames(t *testing.T, exporter packagestest.Exporter) {
948	exported := packagestest.Export(t, exporter, []packagestest.Module{{
949		Name: "golang.org/fake",
950		Files: map[string]interface{}{
951			"a/a.go":          `package a; const A = 1`,
952			"b/b.go":          `package b; import ("golang.org/fake/a"; _ "errors"); var B = a.A`,
953			"b/vendor/a/a.go": `package a; const A = 1`,
954			"c/c.go":          `package c; import (_ "golang.org/fake/b"; _ "unsafe")`,
955			"c/c2.go":         "// +build ignore\n\n" + `package c; import _ "fmt"`,
956			"subdir/d/d.go":   `package d`,
957			"subdir/e/d.go":   `package e`,
958			"e/e.go":          `package main; import _ "golang.org/fake/b"`,
959			"e/e2.go":         `package main; import _ "golang.org/fake/c"`,
960			"f/f.go":          `package f`,
961			"f/f.s":           ``,
962		}}})
963	defer exported.Cleanup()
964	exported.Config.Dir = filepath.Dir(filepath.Dir(exported.File("golang.org/fake", "a/a.go")))
965
966	checkFile := func(filename string) {
967		if !filepath.IsAbs(filename) {
968			t.Errorf("filename is not absolute: %s", filename)
969		}
970		if _, err := os.Stat(filename); err != nil {
971			t.Errorf("stat error, %s: %v", filename, err)
972		}
973	}
974
975	for _, test := range []struct {
976		pattern string
977		want    string
978	}{
979		// Import paths
980		{"golang.org/fake/a", "a.go"},
981		{"golang.org/fake/b/vendor/a", "a.go"},
982		{"golang.org/fake/b", "b.go"},
983		{"golang.org/fake/c", "c.go"},
984		{"golang.org/fake/subdir/d", "d.go"},
985		{"golang.org/fake/subdir/e", "d.go"},
986		{"golang.org/fake/e", "e.go e2.go"},
987		{"golang.org/fake/f", "f.go f.s"},
988		// Relative paths
989		{"./a", "a.go"},
990		{"./b/vendor/a", "a.go"},
991		{"./b", "b.go"},
992		{"./c", "c.go"},
993		{"./subdir/d", "d.go"},
994		{"./subdir/e", "d.go"},
995		{"./e", "e.go e2.go"},
996		{"./f", "f.go f.s"},
997	} {
998		exported.Config.Mode = packages.LoadFiles
999		pkgs, err := packages.Load(exported.Config, test.pattern)
1000		if err != nil {
1001			t.Errorf("pattern %s: %v", test.pattern, err)
1002			continue
1003		}
1004
1005		if got := strings.Join(srcs(pkgs[0]), " "); got != test.want {
1006			t.Errorf("in package %s, got %s, want %s", test.pattern, got, test.want)
1007		}
1008
1009		// Test that files in all packages exist and are absolute paths.
1010		_, all := importGraph(pkgs)
1011		for _, pkg := range all {
1012			for _, filename := range pkg.GoFiles {
1013				checkFile(filename)
1014			}
1015			for _, filename := range pkg.OtherFiles {
1016				checkFile(filename)
1017			}
1018		}
1019	}
1020}
1021
1022func TestContains(t *testing.T) { packagestest.TestAll(t, testContains) }
1023func testContains(t *testing.T, exporter packagestest.Exporter) {
1024	exported := packagestest.Export(t, exporter, []packagestest.Module{{
1025		Name: "golang.org/fake",
1026		Files: map[string]interface{}{
1027			"a/a.go": `package a; import "golang.org/fake/b"`,
1028			"b/b.go": `package b; import "golang.org/fake/c"`,
1029			"c/c.go": `package c`,
1030		}}})
1031	defer exported.Cleanup()
1032	bFile := exported.File("golang.org/fake", "b/b.go")
1033	exported.Config.Mode = packages.LoadImports
1034	initial, err := packages.Load(exported.Config, "file="+bFile)
1035	if err != nil {
1036		t.Fatal(err)
1037	}
1038
1039	graph, _ := importGraph(initial)
1040	wantGraph := `
1041* golang.org/fake/b
1042  golang.org/fake/c
1043  golang.org/fake/b -> golang.org/fake/c
1044`[1:]
1045	if graph != wantGraph {
1046		t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph)
1047	}
1048}
1049
1050// This test ensures that the effective GOARCH variable in the
1051// application determines the Sizes function used by the type checker.
1052// This behavior is a stop-gap until we make the build system's query
1053// too report the correct sizes function for the actual configuration.
1054func TestSizes(t *testing.T) { packagestest.TestAll(t, testSizes) }
1055func testSizes(t *testing.T, exporter packagestest.Exporter) {
1056	exported := packagestest.Export(t, exporter, []packagestest.Module{{
1057		Name: "golang.org/fake",
1058		Files: map[string]interface{}{
1059			"a/a.go": `package a; import "unsafe"; const WordSize = 8*unsafe.Sizeof(int(0))`,
1060		}}})
1061	defer exported.Cleanup()
1062
1063	exported.Config.Mode = packages.LoadSyntax
1064	savedEnv := exported.Config.Env
1065	for arch, wantWordSize := range map[string]int64{"386": 32, "amd64": 64} {
1066		exported.Config.Env = append(savedEnv, "GOARCH="+arch)
1067		initial, err := packages.Load(exported.Config, "golang.org/fake/a")
1068		if err != nil {
1069			t.Fatal(err)
1070		}
1071		if packages.PrintErrors(initial) > 0 {
1072			t.Fatal("there were errors")
1073		}
1074		gotWordSize, _ := constantpkg.Int64Val(constant(initial[0], "WordSize").Val())
1075		if gotWordSize != wantWordSize {
1076			t.Errorf("for GOARCH=%s, got word size %d, want %d", arch, gotWordSize, wantWordSize)
1077		}
1078	}
1079
1080}
1081
1082// TestContains_FallbackSticks ensures that when there are both contains and non-contains queries
1083// the decision whether to fallback to the pre-1.11 go list sticks across both sets of calls to
1084// go list.
1085func TestContains_FallbackSticks(t *testing.T) { packagestest.TestAll(t, testContains_FallbackSticks) }
1086func testContains_FallbackSticks(t *testing.T, exporter packagestest.Exporter) {
1087	exported := packagestest.Export(t, exporter, []packagestest.Module{{
1088		Name: "golang.org/fake",
1089		Files: map[string]interface{}{
1090			"a/a.go": `package a; import "golang.org/fake/b"`,
1091			"b/b.go": `package b; import "golang.org/fake/c"`,
1092			"c/c.go": `package c`,
1093		}}})
1094	defer exported.Cleanup()
1095
1096	exported.Config.Mode = packages.LoadImports
1097	bFile := exported.File("golang.org/fake", "b/b.go")
1098	initial, err := packages.Load(exported.Config, "golang.org/fake/a", "file="+bFile)
1099	if err != nil {
1100		t.Fatal(err)
1101	}
1102
1103	graph, _ := importGraph(initial)
1104	wantGraph := `
1105* golang.org/fake/a
1106* golang.org/fake/b
1107  golang.org/fake/c
1108  golang.org/fake/a -> golang.org/fake/b
1109  golang.org/fake/b -> golang.org/fake/c
1110`[1:]
1111	if graph != wantGraph {
1112		t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph)
1113	}
1114}
1115
1116func TestName(t *testing.T) { packagestest.TestAll(t, testName) }
1117func testName(t *testing.T, exporter packagestest.Exporter) {
1118	exported := packagestest.Export(t, exporter, []packagestest.Module{{
1119		Name: "golang.org/fake",
1120		Files: map[string]interface{}{
1121			"a/needle/needle.go":       `package needle; import "golang.org/fake/c"`,
1122			"b/needle/needle.go":       `package needle;`,
1123			"c/c.go":                   `package c;`,
1124			"irrelevant/irrelevant.go": `package irrelevant;`,
1125		}}})
1126	defer exported.Cleanup()
1127
1128	exported.Config.Mode = packages.LoadImports
1129	initial, err := packages.Load(exported.Config, "name=needle")
1130	if err != nil {
1131		t.Fatal(err)
1132	}
1133	graph, _ := importGraph(initial)
1134	wantGraph := `
1135* golang.org/fake/a/needle
1136* golang.org/fake/b/needle
1137  golang.org/fake/c
1138  golang.org/fake/a/needle -> golang.org/fake/c
1139`[1:]
1140	if graph != wantGraph {
1141		t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph)
1142	}
1143}
1144
1145func TestName_Modules(t *testing.T) {
1146	if usesOldGolist {
1147		t.Skip("pre-modules version of Go")
1148	}
1149
1150	// Test the top-level package case described in runNamedQueries.
1151	// Note that overriding GOPATH below prevents Export from
1152	// creating more than one module.
1153	exported := packagestest.Export(t, packagestest.Modules, []packagestest.Module{{
1154		Name: "golang.org/pkg",
1155		Files: map[string]interface{}{
1156			"pkg.go": `package pkg`,
1157		}}})
1158	defer exported.Cleanup()
1159
1160	wd, err := os.Getwd()
1161	if err != nil {
1162		t.Fatal(err)
1163	}
1164	// testdata/TestNamed_Modules contains:
1165	// - pkg/mod/github.com/heschik/tools-testrepo@v1.0.0/pkg
1166	// - pkg/mod/github.com/heschik/tools-testrepo/v2@v2.0.0/pkg
1167	// - src/b/pkg
1168	exported.Config.Mode = packages.LoadImports
1169	exported.Config.Env = append(exported.Config.Env, "GOPATH="+wd+"/testdata/TestName_Modules")
1170	initial, err := packages.Load(exported.Config, "name=pkg")
1171	if err != nil {
1172		t.Fatal(err)
1173	}
1174	graph, _ := importGraph(initial)
1175	wantGraph := `
1176* github.com/heschik/tools-testrepo/pkg
1177* github.com/heschik/tools-testrepo/v2/pkg
1178* golang.org/pkg
1179`[1:]
1180	if graph != wantGraph {
1181		t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph)
1182	}
1183}
1184
1185func TestName_ModulesDedup(t *testing.T) {
1186	if usesOldGolist {
1187		t.Skip("pre-modules version of Go")
1188	}
1189
1190	exported := packagestest.Export(t, packagestest.Modules, []packagestest.Module{{
1191		Name: "golang.org/fake",
1192		Files: map[string]interface{}{
1193			"fake.go": `package fake`,
1194		}}})
1195	defer exported.Cleanup()
1196
1197	wd, err := os.Getwd()
1198	if err != nil {
1199		t.Fatal(err)
1200	}
1201	// testdata/TestNamed_ModulesDedup contains:
1202	// - pkg/mod/github.com/heschik/tools-testrepo/v2@v2.0.2/pkg/pkg.go
1203	// - pkg/mod/github.com/heschik/tools-testrepo/v2@v2.0.1/pkg/pkg.go
1204	// - pkg/mod/github.com/heschik/tools-testrepo@v1.0.0/pkg/pkg.go
1205	// but, inexplicably, not v2.0.0. Nobody knows why.
1206	exported.Config.Mode = packages.LoadImports
1207	exported.Config.Env = append(exported.Config.Env, "GOPATH="+wd+"/testdata/TestName_ModulesDedup")
1208	initial, err := packages.Load(exported.Config, "name=pkg")
1209	if err != nil {
1210		t.Fatal(err)
1211	}
1212	for _, pkg := range initial {
1213		if strings.Contains(pkg.PkgPath, "v2") {
1214			if strings.Contains(pkg.GoFiles[0], "v2.0.2") {
1215				return
1216			}
1217		}
1218	}
1219	t.Errorf("didn't find v2.0.2 of pkg in Load results: %v", initial)
1220}
1221
1222func TestJSON(t *testing.T) { packagestest.TestAll(t, testJSON) }
1223func testJSON(t *testing.T, exporter packagestest.Exporter) {
1224	//TODO: add in some errors
1225	exported := packagestest.Export(t, exporter, []packagestest.Module{{
1226		Name: "golang.org/fake",
1227		Files: map[string]interface{}{
1228			"a/a.go": `package a; const A = 1`,
1229			"b/b.go": `package b; import "golang.org/fake/a"; var B = a.A`,
1230			"c/c.go": `package c; import "golang.org/fake/b" ; var C = b.B`,
1231			"d/d.go": `package d; import "golang.org/fake/b" ; var D = b.B`,
1232		}}})
1233	defer exported.Cleanup()
1234
1235	exported.Config.Mode = packages.LoadImports
1236	initial, err := packages.Load(exported.Config, "golang.org/fake/c", "golang.org/fake/d")
1237	if err != nil {
1238		t.Fatal(err)
1239	}
1240
1241	// Visit and print all packages.
1242	buf := &bytes.Buffer{}
1243	enc := json.NewEncoder(buf)
1244	enc.SetIndent("", "\t")
1245	packages.Visit(initial, nil, func(pkg *packages.Package) {
1246		// trim the source lists for stable results
1247		pkg.GoFiles = cleanPaths(pkg.GoFiles)
1248		pkg.CompiledGoFiles = cleanPaths(pkg.CompiledGoFiles)
1249		pkg.OtherFiles = cleanPaths(pkg.OtherFiles)
1250		if err := enc.Encode(pkg); err != nil {
1251			t.Fatal(err)
1252		}
1253	})
1254
1255	wantJSON := `
1256{
1257	"ID": "golang.org/fake/a",
1258	"Name": "a",
1259	"PkgPath": "golang.org/fake/a",
1260	"GoFiles": [
1261		"a.go"
1262	],
1263	"CompiledGoFiles": [
1264		"a.go"
1265	]
1266}
1267{
1268	"ID": "golang.org/fake/b",
1269	"Name": "b",
1270	"PkgPath": "golang.org/fake/b",
1271	"GoFiles": [
1272		"b.go"
1273	],
1274	"CompiledGoFiles": [
1275		"b.go"
1276	],
1277	"Imports": {
1278		"golang.org/fake/a": "golang.org/fake/a"
1279	}
1280}
1281{
1282	"ID": "golang.org/fake/c",
1283	"Name": "c",
1284	"PkgPath": "golang.org/fake/c",
1285	"GoFiles": [
1286		"c.go"
1287	],
1288	"CompiledGoFiles": [
1289		"c.go"
1290	],
1291	"Imports": {
1292		"golang.org/fake/b": "golang.org/fake/b"
1293	}
1294}
1295{
1296	"ID": "golang.org/fake/d",
1297	"Name": "d",
1298	"PkgPath": "golang.org/fake/d",
1299	"GoFiles": [
1300		"d.go"
1301	],
1302	"CompiledGoFiles": [
1303		"d.go"
1304	],
1305	"Imports": {
1306		"golang.org/fake/b": "golang.org/fake/b"
1307	}
1308}
1309`[1:]
1310
1311	if buf.String() != wantJSON {
1312		t.Errorf("wrong JSON: got <<%s>>, want <<%s>>", buf.String(), wantJSON)
1313	}
1314	// now decode it again
1315	var decoded []*packages.Package
1316	dec := json.NewDecoder(buf)
1317	for dec.More() {
1318		p := new(packages.Package)
1319		if err := dec.Decode(p); err != nil {
1320			t.Fatal(err)
1321		}
1322		decoded = append(decoded, p)
1323	}
1324	if len(decoded) != 4 {
1325		t.Fatalf("got %d packages, want 4", len(decoded))
1326	}
1327	for i, want := range []*packages.Package{{
1328		ID:   "golang.org/fake/a",
1329		Name: "a",
1330	}, {
1331		ID:   "golang.org/fake/b",
1332		Name: "b",
1333		Imports: map[string]*packages.Package{
1334			"golang.org/fake/a": &packages.Package{ID: "golang.org/fake/a"},
1335		},
1336	}, {
1337		ID:   "golang.org/fake/c",
1338		Name: "c",
1339		Imports: map[string]*packages.Package{
1340			"golang.org/fake/b": &packages.Package{ID: "golang.org/fake/b"},
1341		},
1342	}, {
1343		ID:   "golang.org/fake/d",
1344		Name: "d",
1345		Imports: map[string]*packages.Package{
1346			"golang.org/fake/b": &packages.Package{ID: "golang.org/fake/b"},
1347		},
1348	}} {
1349		got := decoded[i]
1350		if got.ID != want.ID {
1351			t.Errorf("Package %d has ID %q want %q", i, got.ID, want.ID)
1352		}
1353		if got.Name != want.Name {
1354			t.Errorf("Package %q has Name %q want %q", got.ID, got.Name, want.Name)
1355		}
1356		if len(got.Imports) != len(want.Imports) {
1357			t.Errorf("Package %q has %d imports want %d", got.ID, len(got.Imports), len(want.Imports))
1358			continue
1359		}
1360		for path, ipkg := range got.Imports {
1361			if want.Imports[path] == nil {
1362				t.Errorf("Package %q has unexpected import %q", got.ID, path)
1363				continue
1364			}
1365			if want.Imports[path].ID != ipkg.ID {
1366				t.Errorf("Package %q import %q is %q want %q", got.ID, path, ipkg.ID, want.Imports[path].ID)
1367			}
1368		}
1369	}
1370}
1371
1372func TestRejectInvalidQueries(t *testing.T) {
1373	queries := []string{"key=", "key=value"}
1374	cfg := &packages.Config{
1375		Mode: packages.LoadImports,
1376		Env:  append(os.Environ(), "GO111MODULE=off"),
1377	}
1378	for _, q := range queries {
1379		if _, err := packages.Load(cfg, q); err == nil {
1380			t.Errorf("packages.Load(%q) succeeded. Expected \"invalid query type\" error", q)
1381		} else if !strings.Contains(err.Error(), "invalid query type") {
1382			t.Errorf("packages.Load(%q): got error %v, want \"invalid query type\" error", q, err)
1383		}
1384	}
1385}
1386
1387func TestPatternPassthrough(t *testing.T) { packagestest.TestAll(t, testPatternPassthrough) }
1388func testPatternPassthrough(t *testing.T, exporter packagestest.Exporter) {
1389	exported := packagestest.Export(t, exporter, []packagestest.Module{{
1390		Name: "golang.org/fake",
1391		Files: map[string]interface{}{
1392			"a/a.go": `package a;`,
1393		}}})
1394	defer exported.Cleanup()
1395
1396	initial, err := packages.Load(exported.Config, "pattern=a")
1397	if err != nil {
1398		t.Fatal(err)
1399	}
1400
1401	graph, _ := importGraph(initial)
1402	wantGraph := `
1403* a
1404`[1:]
1405	if graph != wantGraph {
1406		t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph)
1407	}
1408
1409}
1410
1411func TestConfigDefaultEnv(t *testing.T) { packagestest.TestAll(t, testConfigDefaultEnv) }
1412func testConfigDefaultEnv(t *testing.T, exporter packagestest.Exporter) {
1413	if runtime.GOOS == "windows" {
1414		// TODO(jayconrod): write an equivalent batch script for windows.
1415		// Hint: "type" can be used to read a file to stdout.
1416		t.Skip("test requires sh")
1417	}
1418	exported := packagestest.Export(t, exporter, []packagestest.Module{{
1419		Name: "golang.org/fake",
1420		Files: map[string]interface{}{
1421			"bin/gopackagesdriver": packagestest.Script(`#!/bin/sh
1422
1423cat - <<'EOF'
1424{
1425  "Roots": ["gopackagesdriver"],
1426  "Packages": [{"ID": "gopackagesdriver", "Name": "gopackagesdriver"}]
1427}
1428EOF
1429`),
1430			"golist/golist.go": "package golist",
1431		}}})
1432	defer exported.Cleanup()
1433	driver := exported.File("golang.org/fake", "bin/gopackagesdriver")
1434	binDir := filepath.Dir(driver)
1435	if err := os.Chmod(driver, 0755); err != nil {
1436		t.Fatal(err)
1437	}
1438
1439	path, ok := os.LookupEnv("PATH")
1440	var pathWithDriver string
1441	if ok {
1442		pathWithDriver = binDir + string(os.PathListSeparator) + path
1443	} else {
1444		pathWithDriver = binDir
1445	}
1446	coreEnv := exported.Config.Env
1447	for _, test := range []struct {
1448		desc    string
1449		path    string
1450		driver  string
1451		wantIDs string
1452	}{
1453		{
1454			desc:    "driver_off",
1455			path:    pathWithDriver,
1456			driver:  "off",
1457			wantIDs: "[golist]",
1458		}, {
1459			desc:    "driver_unset",
1460			path:    pathWithDriver,
1461			driver:  "",
1462			wantIDs: "[gopackagesdriver]",
1463		}, {
1464			desc:    "driver_set",
1465			path:    "",
1466			driver:  driver,
1467			wantIDs: "[gopackagesdriver]",
1468		},
1469	} {
1470		t.Run(test.desc, func(t *testing.T) {
1471			oldPath := os.Getenv("PATH")
1472			os.Setenv("PATH", test.path)
1473			defer os.Setenv("PATH", oldPath)
1474			exported.Config.Env = append(coreEnv, "GOPACKAGESDRIVER="+test.driver)
1475			pkgs, err := packages.Load(exported.Config, "golist")
1476			if err != nil {
1477				t.Fatal(err)
1478			}
1479
1480			gotIds := make([]string, len(pkgs))
1481			for i, pkg := range pkgs {
1482				gotIds[i] = pkg.ID
1483			}
1484			if fmt.Sprint(pkgs) != test.wantIDs {
1485				t.Errorf("got %v; want %v", gotIds, test.wantIDs)
1486			}
1487		})
1488	}
1489}
1490
1491// This test that a simple x test package layout loads correctly.
1492// There was a bug in go list where it returned multiple copies of the same
1493// package (specifically in this case of golang.org/fake/a), and this triggered
1494// a bug in go/packages where it would leave an empty entry in the root package
1495// list. This would then cause a nil pointer crash.
1496// This bug was triggered by the simple package layout below, and thus this
1497// test will make sure the bug remains fixed.
1498func TestBasicXTest(t *testing.T) { packagestest.TestAll(t, testBasicXTest) }
1499func testBasicXTest(t *testing.T, exporter packagestest.Exporter) {
1500	exported := packagestest.Export(t, exporter, []packagestest.Module{{
1501		Name: "golang.org/fake",
1502		Files: map[string]interface{}{
1503			"a/a.go":      `package a;`,
1504			"a/a_test.go": `package a_test;`,
1505		}}})
1506	defer exported.Cleanup()
1507
1508	exported.Config.Mode = packages.LoadFiles
1509	exported.Config.Tests = true
1510	_, err := packages.Load(exported.Config, "golang.org/fake/a")
1511	if err != nil {
1512		t.Fatal(err)
1513	}
1514}
1515
1516func errorMessages(errors []packages.Error) []string {
1517	var msgs []string
1518	for _, err := range errors {
1519		msgs = append(msgs, err.Msg)
1520	}
1521	return msgs
1522}
1523
1524func srcs(p *packages.Package) []string {
1525	return cleanPaths(append(p.GoFiles, p.OtherFiles...))
1526}
1527
1528// cleanPaths attempts to reduce path names to stable forms
1529func cleanPaths(paths []string) []string {
1530	result := make([]string, len(paths))
1531	for i, src := range paths {
1532		// The default location for cache data is a subdirectory named go-build
1533		// in the standard user cache directory for the current operating system.
1534		if strings.Contains(filepath.ToSlash(src), "/go-build/") {
1535			result[i] = fmt.Sprintf("%d.go", i) // make cache names predictable
1536		} else {
1537			result[i] = filepath.Base(src)
1538		}
1539	}
1540	return result
1541}
1542
1543// importGraph returns the import graph as a user-friendly string,
1544// and a map containing all packages keyed by ID.
1545func importGraph(initial []*packages.Package) (string, map[string]*packages.Package) {
1546	out := new(bytes.Buffer)
1547
1548	initialSet := make(map[*packages.Package]bool)
1549	for _, p := range initial {
1550		initialSet[p] = true
1551	}
1552
1553	// We can't use Visit because we need to prune
1554	// the traversal of specific edges, not just nodes.
1555	var nodes, edges []string
1556	res := make(map[string]*packages.Package)
1557	seen := make(map[*packages.Package]bool)
1558	var visit func(p *packages.Package)
1559	visit = func(p *packages.Package) {
1560		if !seen[p] {
1561			seen[p] = true
1562			if res[p.ID] != nil {
1563				panic("duplicate ID: " + p.ID)
1564			}
1565			res[p.ID] = p
1566
1567			star := ' ' // mark initial packages with a star
1568			if initialSet[p] {
1569				star = '*'
1570			}
1571			nodes = append(nodes, fmt.Sprintf("%c %s", star, p.ID))
1572
1573			// To avoid a lot of noise,
1574			// we prune uninteresting dependencies of testmain packages,
1575			// which we identify by this import:
1576			isTestMain := p.Imports["testing/internal/testdeps"] != nil
1577
1578			for _, imp := range p.Imports {
1579				if isTestMain {
1580					switch imp.ID {
1581					case "os", "testing", "testing/internal/testdeps":
1582						edges = append(edges, fmt.Sprintf("%s -> %s (pruned)", p, imp))
1583						continue
1584					}
1585				}
1586				edges = append(edges, fmt.Sprintf("%s -> %s", p, imp))
1587				visit(imp)
1588			}
1589		}
1590	}
1591	for _, p := range initial {
1592		visit(p)
1593	}
1594
1595	// Sort, ignoring leading optional star prefix.
1596	sort.Slice(nodes, func(i, j int) bool { return nodes[i][2:] < nodes[j][2:] })
1597	for _, node := range nodes {
1598		fmt.Fprintf(out, "%s\n", node)
1599	}
1600
1601	sort.Strings(edges)
1602	for _, edge := range edges {
1603		fmt.Fprintf(out, "  %s\n", edge)
1604	}
1605
1606	return out.String(), res
1607}
1608
1609func constant(p *packages.Package, name string) *types.Const {
1610	if p == nil || p.Types == nil {
1611		return nil
1612	}
1613	c := p.Types.Scope().Lookup(name)
1614	if c == nil {
1615		return nil
1616	}
1617	return c.(*types.Const)
1618}
1619