1 // Copyright 2013 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.
5 // No testdata on Android.
26 "golang.org/x/tools/go/buildutil"
27 "golang.org/x/tools/go/loader"
28 "golang.org/x/tools/internal/testenv"
31 func TestMain(m *testing.M) {
32 testenv.ExitIfSmallMachine()
36 // TestFromArgs checks that conf.FromArgs populates conf correctly.
38 func TestFromArgs(t *testing.T) {
42 ImportPkgs map[string]bool
43 CreatePkgs []loader.PkgSpec
45 for _, test := range []struct {
50 // Mix of existing and non-existent packages.
52 args: []string{"nosuchpkg", "errors"},
54 ImportPkgs: map[string]bool{"errors": false, "nosuchpkg": false},
57 // Same, with -test flag.
59 args: []string{"nosuchpkg", "errors"},
62 ImportPkgs: map[string]bool{"errors": true, "nosuchpkg": true},
67 args: []string{"fmt", "errors", "--", "surplus"},
69 Rest: []string{"surplus"},
70 ImportPkgs: map[string]bool{"errors": false, "fmt": false},
73 // Ad hoc package specified as *.go files.
75 args: []string{"foo.go", "bar.go"},
76 want: result{CreatePkgs: []loader.PkgSpec{{
77 Filenames: []string{"foo.go", "bar.go"},
80 // Mixture of *.go and import paths.
82 args: []string{"foo.go", "fmt"},
84 Err: "named files must be .go files: fmt",
88 var conf loader.Config
89 rest, err := conf.FromArgs(test.args, test.tests)
92 ImportPkgs: conf.ImportPkgs,
93 CreatePkgs: conf.CreatePkgs,
98 if !reflect.DeepEqual(got, test.want) {
99 t.Errorf("FromArgs(%q) = %+v, want %+v", test.args, got, test.want)
104 func TestLoad_NoInitialPackages(t *testing.T) {
105 var conf loader.Config
107 const wantErr = "no initial packages were loaded"
109 prog, err := conf.Load()
111 t.Errorf("Load succeeded unexpectedly, want %q", wantErr)
112 } else if err.Error() != wantErr {
113 t.Errorf("Load failed with wrong error %q, want %q", err, wantErr)
116 t.Errorf("Load unexpectedly returned a Program")
120 func TestLoad_MissingInitialPackage(t *testing.T) {
121 var conf loader.Config
122 conf.Import("nosuchpkg")
123 conf.Import("errors")
125 const wantErr = "couldn't load packages due to errors: nosuchpkg"
127 prog, err := conf.Load()
129 t.Errorf("Load succeeded unexpectedly, want %q", wantErr)
130 } else if err.Error() != wantErr {
131 t.Errorf("Load failed with wrong error %q, want %q", err, wantErr)
134 t.Errorf("Load unexpectedly returned a Program")
138 func TestLoad_MissingInitialPackage_AllowErrors(t *testing.T) {
139 if runtime.Compiler == "gccgo" {
140 t.Skip("gccgo has no standard library test files")
143 var conf loader.Config
144 conf.AllowErrors = true
145 conf.Import("nosuchpkg")
146 conf.ImportWithTests("errors")
148 prog, err := conf.Load()
150 t.Errorf("Load failed unexpectedly: %v", err)
153 t.Fatalf("Load returned a nil Program")
155 if got, want := created(prog), "errors_test"; got != want {
156 t.Errorf("Created = %s, want %s", got, want)
158 if got, want := imported(prog), "errors"; got != want {
159 t.Errorf("Imported = %s, want %s", got, want)
163 func TestCreateUnnamedPackage(t *testing.T) {
164 var conf loader.Config
165 conf.CreateFromFilenames("")
166 prog, err := conf.Load()
168 t.Fatalf("Load failed: %v", err)
170 if got, want := fmt.Sprint(prog.InitialPackages()), "[(unnamed)]"; got != want {
171 t.Errorf("InitialPackages = %s, want %s", got, want)
175 func TestLoad_MissingFileInCreatedPackage(t *testing.T) {
176 var conf loader.Config
177 conf.CreateFromFilenames("", "missing.go")
179 const wantErr = "couldn't load packages due to errors: (unnamed)"
181 prog, err := conf.Load()
183 t.Errorf("Load unexpectedly returned a Program")
186 t.Fatalf("Load succeeded unexpectedly, want %q", wantErr)
188 if err.Error() != wantErr {
189 t.Fatalf("Load failed with wrong error %q, want %q", err, wantErr)
193 func TestLoad_MissingFileInCreatedPackage_AllowErrors(t *testing.T) {
194 conf := loader.Config{AllowErrors: true}
195 conf.CreateFromFilenames("", "missing.go")
197 prog, err := conf.Load()
199 t.Errorf("Load failed: %v", err)
201 if got, want := fmt.Sprint(prog.InitialPackages()), "[(unnamed)]"; got != want {
202 t.Fatalf("InitialPackages = %s, want %s", got, want)
206 func TestLoad_ParseError(t *testing.T) {
207 var conf loader.Config
208 conf.CreateFromFilenames("badpkg", "testdata/badpkgdecl.go")
210 const wantErr = "couldn't load packages due to errors: badpkg"
212 prog, err := conf.Load()
214 t.Errorf("Load unexpectedly returned a Program")
217 t.Fatalf("Load succeeded unexpectedly, want %q", wantErr)
219 if err.Error() != wantErr {
220 t.Fatalf("Load failed with wrong error %q, want %q", err, wantErr)
224 func TestLoad_ParseError_AllowErrors(t *testing.T) {
225 var conf loader.Config
226 conf.AllowErrors = true
227 conf.CreateFromFilenames("badpkg", "testdata/badpkgdecl.go")
229 prog, err := conf.Load()
231 t.Errorf("Load failed unexpectedly: %v", err)
234 t.Fatalf("Load returned a nil Program")
236 if got, want := created(prog), "badpkg"; got != want {
237 t.Errorf("Created = %s, want %s", got, want)
240 badpkg := prog.Created[0]
241 if len(badpkg.Files) != 1 {
242 t.Errorf("badpkg has %d files, want 1", len(badpkg.Files))
244 wantErr := filepath.Join("testdata", "badpkgdecl.go") + ":1:34: expected 'package', found 'EOF'"
245 if !hasError(badpkg.Errors, wantErr) {
246 t.Errorf("badpkg.Errors = %v, want %s", badpkg.Errors, wantErr)
250 func TestLoad_FromSource_Success(t *testing.T) {
251 var conf loader.Config
252 conf.CreateFromFilenames("P", "testdata/a.go", "testdata/b.go")
254 prog, err := conf.Load()
256 t.Errorf("Load failed unexpectedly: %v", err)
259 t.Fatalf("Load returned a nil Program")
261 if got, want := created(prog), "P"; got != want {
262 t.Errorf("Created = %s, want %s", got, want)
266 func TestLoad_FromImports_Success(t *testing.T) {
267 if runtime.Compiler == "gccgo" {
268 t.Skip("gccgo has no standard library test files")
271 var conf loader.Config
272 conf.ImportWithTests("fmt")
273 conf.ImportWithTests("errors")
275 prog, err := conf.Load()
277 t.Errorf("Load failed unexpectedly: %v", err)
280 t.Fatalf("Load returned a nil Program")
282 if got, want := created(prog), "errors_test fmt_test"; got != want {
283 t.Errorf("Created = %q, want %s", got, want)
285 if got, want := imported(prog), "errors fmt"; got != want {
286 t.Errorf("Imported = %s, want %s", got, want)
288 // Check set of transitive packages.
289 // There are >30 and the set may grow over time, so only check a few.
290 want := map[string]bool{
297 for _, path := range all(prog) {
301 t.Errorf("AllPackages is missing these keys: %q", keys(want))
305 func TestLoad_MissingIndirectImport(t *testing.T) {
306 pkgs := map[string]string{
307 "a": `package a; import _ "b"`,
308 "b": `package b; import _ "c"`,
310 conf := loader.Config{Build: fakeContext(pkgs)}
313 const wantErr = "couldn't load packages due to errors: b"
315 prog, err := conf.Load()
317 t.Errorf("Load succeeded unexpectedly, want %q", wantErr)
318 } else if err.Error() != wantErr {
319 t.Errorf("Load failed with wrong error %q, want %q", err, wantErr)
322 t.Errorf("Load unexpectedly returned a Program")
326 func TestLoad_BadDependency_AllowErrors(t *testing.T) {
327 for _, test := range []struct {
329 pkgs map[string]string
334 descr: "missing dependency",
335 pkgs: map[string]string{
336 "a": `package a; import _ "b"`,
337 "b": `package b; import _ "c"`,
342 descr: "bad package decl in dependency",
343 pkgs: map[string]string{
344 "a": `package a; import _ "b"`,
345 "b": `package b; import _ "c"`,
351 descr: "parse error in dependency",
352 pkgs: map[string]string{
353 "a": `package a; import _ "b"`,
354 "b": `package b; import _ "c"`,
355 "c": `package c; var x = `,
360 conf := loader.Config{
362 Build: fakeContext(test.pkgs),
366 prog, err := conf.Load()
368 t.Errorf("%s: Load failed unexpectedly: %v", test.descr, err)
371 t.Fatalf("%s: Load returned a nil Program", test.descr)
374 if got, want := imported(prog), "a"; got != want {
375 t.Errorf("%s: Imported = %s, want %s", test.descr, got, want)
377 if got := all(prog); strings.Join(got, " ") != test.wantPkgs {
378 t.Errorf("%s: AllPackages = %s, want %s", test.descr, got, test.wantPkgs)
383 func TestCwd(t *testing.T) {
384 ctxt := fakeContext(map[string]string{"one/two/three": `package three`})
385 for _, test := range []struct {
386 cwd, arg, want string
388 {cwd: "/go/src/one", arg: "./two/three", want: "one/two/three"},
389 {cwd: "/go/src/one", arg: "../one/two/three", want: "one/two/three"},
390 {cwd: "/go/src/one", arg: "one/two/three", want: "one/two/three"},
391 {cwd: "/go/src/one/two/three", arg: ".", want: "one/two/three"},
392 {cwd: "/go/src/one", arg: "two/three", want: ""},
394 conf := loader.Config{
398 conf.Import(test.arg)
401 prog, err := conf.Load()
405 if got != test.want {
406 t.Errorf("Load(%s) from %s: Imported = %s, want %s",
407 test.arg, test.cwd, got, test.want)
409 t.Errorf("Load failed: %v", err)
415 func TestLoad_vendor(t *testing.T) {
416 pkgs := map[string]string{
417 "a": `package a; import _ "x"`,
418 "a/vendor": ``, // mkdir a/vendor
419 "a/vendor/x": `package xa`,
420 "b": `package b; import _ "x"`,
421 "b/vendor": ``, // mkdir b/vendor
422 "b/vendor/x": `package xb`,
423 "c": `package c; import _ "x"`,
426 conf := loader.Config{Build: fakeContext(pkgs)}
431 prog, err := conf.Load()
436 // Check that a, b, and c see different versions of x.
437 for _, r := range "abc" {
439 got := prog.Package(name).Pkg.Imports()[0]
441 if got.Name() != want {
442 t.Errorf("package %s import %q = %s, want %s",
443 name, "x", got.Name(), want)
448 func TestVendorCwd(t *testing.T) {
449 // Test the interaction of cwd and vendor directories.
450 ctxt := fakeContext(map[string]string{
451 "net": ``, // mkdir net
452 "net/http": `package http; import _ "hpack"`,
453 "vendor": ``, // mkdir vendor
454 "vendor/hpack": `package vendorhpack`,
455 "hpack": `package hpack`,
457 for i, test := range []struct {
458 cwd, arg, want string
460 {cwd: "/go/src/net", arg: "http"}, // not found
461 {cwd: "/go/src/net", arg: "./http", want: "net/http vendor/hpack"},
462 {cwd: "/go/src/net", arg: "hpack", want: "vendor/hpack"},
463 {cwd: "/go/src/vendor", arg: "hpack", want: "vendor/hpack"},
464 {cwd: "/go/src/vendor", arg: "./hpack", want: "vendor/hpack"},
466 conf := loader.Config{
470 conf.Import(test.arg)
473 prog, err := conf.Load()
475 got = strings.Join(all(prog), " ")
477 if got != test.want {
478 t.Errorf("#%d: Load(%s) from %s: got %s, want %s",
479 i, test.arg, test.cwd, got, test.want)
481 t.Errorf("Load failed: %v", err)
487 func TestVendorCwdIssue16580(t *testing.T) {
488 // Regression test for Go issue 16580.
489 // Import decls in "created" packages were vendor-resolved
490 // w.r.t. cwd, not the parent directory of the package's files.
491 ctxt := fakeContext(map[string]string{
493 "a/vendor": ``, // mkdir a/vendor
494 "a/vendor/b": `package b; const X = true`,
495 "b": `package b; const X = false`,
497 for _, test := range []struct {
499 want bool // expected value of b.X; depends on filename, not on cwd
501 {filename: "c.go", cwd: "/go/src", want: false},
502 {filename: "c.go", cwd: "/go/src/a", want: false},
503 {filename: "c.go", cwd: "/go/src/a/b", want: false},
504 {filename: "c.go", cwd: "/go/src/a/vendor/b", want: false},
506 {filename: "/go/src/a/c.go", cwd: "/go/src", want: true},
507 {filename: "/go/src/a/c.go", cwd: "/go/src/a", want: true},
508 {filename: "/go/src/a/c.go", cwd: "/go/src/a/b", want: true},
509 {filename: "/go/src/a/c.go", cwd: "/go/src/a/vendor/b", want: true},
511 {filename: "/go/src/c/c.go", cwd: "/go/src", want: false},
512 {filename: "/go/src/c/c.go", cwd: "/go/src/a", want: false},
513 {filename: "/go/src/c/c.go", cwd: "/go/src/a/b", want: false},
514 {filename: "/go/src/c/c.go", cwd: "/go/src/a/vendor/b", want: false},
516 conf := loader.Config{
520 f, err := conf.ParseFile(test.filename, `package dummy; import "b"; const X = b.X`)
524 conf.CreateFromFiles("dummy", f)
526 prog, err := conf.Load()
528 t.Errorf("%+v: Load failed: %v", test, err)
532 x := constant.BoolVal(prog.Created[0].Pkg.Scope().Lookup("X").(*types.Const).Val())
534 t.Errorf("%+v: b.X = %t", test, x)
538 // TODO(adonovan): also test imports within XTestGoFiles.
541 // TODO(adonovan): more Load tests:
544 // - to parse package decl of *_test.go files
545 // - to parse package decl of external *_test.go files
546 // - to parse whole of *_test.go files
547 // - to parse whole of external *_test.go files
548 // - to open a *.go file during import scanning
549 // - to import from binary
553 // - PackageCreated hook
554 // - TypeCheckFuncBodies hook
556 func TestTransitivelyErrorFreeFlag(t *testing.T) {
557 // Create an minimal custom build.Context
558 // that fakes the following packages:
560 // a --> b --> c! c has an error
561 // \ d and e are transitively error-free.
564 // Each package [a-e] consists of one file, x.go.
565 pkgs := map[string]string{
566 "a": `package a; import (_ "b"; _ "e")`,
567 "b": `package b; import _ "c"`,
568 "c": `package c; func f() { _ = int(false) }`, // type error within function body
570 "e": `package e; import _ "d"`,
572 conf := loader.Config{
574 Build: fakeContext(pkgs),
578 prog, err := conf.Load()
580 t.Errorf("Load failed: %s", err)
583 t.Fatalf("Load returned nil *Program")
586 for pkg, info := range prog.AllPackages {
587 var wantErr, wantTEF bool
595 t.Errorf("unexpected package: %q", pkg.Path())
599 if (info.Errors != nil) != wantErr {
601 t.Errorf("Package %q.Error = nil, want error", pkg.Path())
603 t.Errorf("Package %q has unexpected Errors: %v",
604 pkg.Path(), info.Errors)
608 if info.TransitivelyErrorFree != wantTEF {
609 t.Errorf("Package %q.TransitivelyErrorFree=%t, want %t",
610 pkg.Path(), info.TransitivelyErrorFree, wantTEF)
615 // Test that syntax (scan/parse), type, and loader errors are recorded
616 // (in PackageInfo.Errors) and reported (via Config.TypeChecker.Error).
617 func TestErrorReporting(t *testing.T) {
618 pkgs := map[string]string{
619 "a": `package a; import (_ "b"; _ "c"); var x int = false`,
620 "b": `package b; 'syntax error!`,
622 conf := loader.Config{
624 Build: fakeContext(pkgs),
627 var allErrors []error
628 conf.TypeChecker.Error = func(err error) {
630 allErrors = append(allErrors, err)
635 prog, err := conf.Load()
637 t.Errorf("Load failed: %s", err)
640 t.Fatalf("Load returned nil *Program")
643 // TODO(adonovan): test keys of ImportMap.
645 // Check errors recorded in each PackageInfo.
646 for pkg, info := range prog.AllPackages {
649 // The match below is unfortunately vague, because in go1.16 the error
650 // message in go/types changed from "cannot convert ..." to "cannot use
651 // ... as ... in assignment".
652 if !hasError(info.Errors, "cannot") {
653 t.Errorf("a.Errors = %v, want bool assignment (type) error", info.Errors)
655 if !hasError(info.Errors, "could not import c") {
656 t.Errorf("a.Errors = %v, want import (loader) error", info.Errors)
659 if !hasError(info.Errors, "rune literal not terminated") {
660 t.Errorf("b.Errors = %v, want unterminated literal (syntax) error", info.Errors)
665 // Check errors reported via error handler.
666 if !hasError(allErrors, "cannot") ||
667 !hasError(allErrors, "rune literal not terminated") ||
668 !hasError(allErrors, "could not import c") {
669 t.Errorf("allErrors = %v, want syntax, type and loader errors", allErrors)
673 func TestCycles(t *testing.T) {
674 for _, test := range []struct {
681 fakeContext(map[string]string{
682 "main": `package main; import _ "selfcycle"`,
683 "selfcycle": `package selfcycle; import _ "selfcycle"`,
685 `import cycle: selfcycle -> selfcycle`,
688 "three-package cycle",
689 fakeContext(map[string]string{
690 "main": `package main; import _ "a"`,
691 "a": `package a; import _ "b"`,
692 "b": `package b; import _ "c"`,
693 "c": `package c; import _ "a"`,
695 `import cycle: c -> a -> b -> c`,
698 "self-cycle in dependency of test file",
699 buildutil.FakeContext(map[string]map[string]string{
701 "main.go": `package main`,
702 "main_test.go": `package main; import _ "a"`,
705 "a.go": `package a; import _ "a"`,
708 `import cycle: a -> a`,
710 // TODO(adonovan): fix: these fail
712 // "two-package cycle in dependency of test file",
713 // buildutil.FakeContext(map[string]map[string]string{
715 // "main.go": `package main`,
716 // "main_test.go": `package main; import _ "a"`,
719 // "a.go": `package a; import _ "main"`,
722 // `import cycle: main -> a -> main`,
725 // "self-cycle in augmented package",
726 // buildutil.FakeContext(map[string]map[string]string{
728 // "main.go": `package main`,
729 // "main_test.go": `package main; import _ "main"`,
732 // `import cycle: main -> main`,
735 conf := loader.Config{
740 var allErrors []error
741 conf.TypeChecker.Error = func(err error) {
743 allErrors = append(allErrors, err)
746 conf.ImportWithTests("main")
748 prog, err := conf.Load()
750 t.Errorf("%s: Load failed: %s", test.descr, err)
753 t.Fatalf("%s: Load returned nil *Program", test.descr)
756 if !hasError(allErrors, test.wantErr) {
757 t.Errorf("%s: Load() errors = %q, want %q",
758 test.descr, allErrors, test.wantErr)
763 // - Test that in a legal test cycle, none of the symbols
764 // defined by augmentation are visible via import.
767 // ---- utilities ----
769 // Simplifying wrapper around buildutil.FakeContext for single-file packages.
770 func fakeContext(pkgs map[string]string) *build.Context {
771 pkgs2 := make(map[string]map[string]string)
772 for path, content := range pkgs {
773 pkgs2[path] = map[string]string{"x.go": content}
775 return buildutil.FakeContext(pkgs2)
778 func hasError(errors []error, substr string) bool {
779 for _, err := range errors {
780 if strings.Contains(err.Error(), substr) {
787 func keys(m map[string]bool) (keys []string) {
789 keys = append(keys, key)
795 // Returns all loaded packages.
796 func all(prog *loader.Program) []string {
798 for _, info := range prog.AllPackages {
799 pkgs = append(pkgs, info.Pkg.Path())
805 // Returns initially imported packages, as a string.
806 func imported(prog *loader.Program) string {
808 for _, info := range prog.Imported {
809 pkgs = append(pkgs, info.Pkg.Path())
812 return strings.Join(pkgs, " ")
815 // Returns initially created packages, as a string.
816 func created(prog *loader.Program) string {
818 for _, info := range prog.Created {
819 pkgs = append(pkgs, info.Pkg.Path())
821 return strings.Join(pkgs, " ")
824 // Load package "io" twice in parallel.
825 // When run with -race, this is a regression test for Go issue 20718, in
826 // which the global "unsafe" package was modified concurrently.
827 func TestLoad1(t *testing.T) { loadIO(t) }
828 func TestLoad2(t *testing.T) { loadIO(t) }
830 func loadIO(t *testing.T) {
832 conf := &loader.Config{ImportPkgs: map[string]bool{"io": false}}
833 if _, err := conf.Load(); err != nil {