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.
25 "golang.org/x/tools/go/buildutil"
26 "golang.org/x/tools/go/loader"
27 "golang.org/x/tools/internal/testenv"
30 func TestMain(m *testing.M) {
31 testenv.ExitIfSmallMachine()
35 // TestFromArgs checks that conf.FromArgs populates conf correctly.
37 func TestFromArgs(t *testing.T) {
41 ImportPkgs map[string]bool
42 CreatePkgs []loader.PkgSpec
44 for _, test := range []struct {
49 // Mix of existing and non-existent packages.
51 args: []string{"nosuchpkg", "errors"},
53 ImportPkgs: map[string]bool{"errors": false, "nosuchpkg": false},
56 // Same, with -test flag.
58 args: []string{"nosuchpkg", "errors"},
61 ImportPkgs: map[string]bool{"errors": true, "nosuchpkg": true},
66 args: []string{"fmt", "errors", "--", "surplus"},
68 Rest: []string{"surplus"},
69 ImportPkgs: map[string]bool{"errors": false, "fmt": false},
72 // Ad hoc package specified as *.go files.
74 args: []string{"foo.go", "bar.go"},
75 want: result{CreatePkgs: []loader.PkgSpec{{
76 Filenames: []string{"foo.go", "bar.go"},
79 // Mixture of *.go and import paths.
81 args: []string{"foo.go", "fmt"},
83 Err: "named files must be .go files: fmt",
87 var conf loader.Config
88 rest, err := conf.FromArgs(test.args, test.tests)
91 ImportPkgs: conf.ImportPkgs,
92 CreatePkgs: conf.CreatePkgs,
97 if !reflect.DeepEqual(got, test.want) {
98 t.Errorf("FromArgs(%q) = %+v, want %+v", test.args, got, test.want)
103 func TestLoad_NoInitialPackages(t *testing.T) {
104 var conf loader.Config
106 const wantErr = "no initial packages were loaded"
108 prog, err := conf.Load()
110 t.Errorf("Load succeeded unexpectedly, want %q", wantErr)
111 } else if err.Error() != wantErr {
112 t.Errorf("Load failed with wrong error %q, want %q", err, wantErr)
115 t.Errorf("Load unexpectedly returned a Program")
119 func TestLoad_MissingInitialPackage(t *testing.T) {
120 var conf loader.Config
121 conf.Import("nosuchpkg")
122 conf.Import("errors")
124 const wantErr = "couldn't load packages due to errors: nosuchpkg"
126 prog, err := conf.Load()
128 t.Errorf("Load succeeded unexpectedly, want %q", wantErr)
129 } else if err.Error() != wantErr {
130 t.Errorf("Load failed with wrong error %q, want %q", err, wantErr)
133 t.Errorf("Load unexpectedly returned a Program")
137 func TestLoad_MissingInitialPackage_AllowErrors(t *testing.T) {
138 if runtime.Compiler == "gccgo" {
139 t.Skip("gccgo has no standard library test files")
142 var conf loader.Config
143 conf.AllowErrors = true
144 conf.Import("nosuchpkg")
145 conf.ImportWithTests("errors")
147 prog, err := conf.Load()
149 t.Errorf("Load failed unexpectedly: %v", err)
152 t.Fatalf("Load returned a nil Program")
154 if got, want := created(prog), "errors_test"; got != want {
155 t.Errorf("Created = %s, want %s", got, want)
157 if got, want := imported(prog), "errors"; got != want {
158 t.Errorf("Imported = %s, want %s", got, want)
162 func TestCreateUnnamedPackage(t *testing.T) {
163 var conf loader.Config
164 conf.CreateFromFilenames("")
165 prog, err := conf.Load()
167 t.Fatalf("Load failed: %v", err)
169 if got, want := fmt.Sprint(prog.InitialPackages()), "[(unnamed)]"; got != want {
170 t.Errorf("InitialPackages = %s, want %s", got, want)
174 func TestLoad_MissingFileInCreatedPackage(t *testing.T) {
175 var conf loader.Config
176 conf.CreateFromFilenames("", "missing.go")
178 const wantErr = "couldn't load packages due to errors: (unnamed)"
180 prog, err := conf.Load()
182 t.Errorf("Load unexpectedly returned a Program")
185 t.Fatalf("Load succeeded unexpectedly, want %q", wantErr)
187 if err.Error() != wantErr {
188 t.Fatalf("Load failed with wrong error %q, want %q", err, wantErr)
192 func TestLoad_MissingFileInCreatedPackage_AllowErrors(t *testing.T) {
193 conf := loader.Config{AllowErrors: true}
194 conf.CreateFromFilenames("", "missing.go")
196 prog, err := conf.Load()
198 t.Errorf("Load failed: %v", err)
200 if got, want := fmt.Sprint(prog.InitialPackages()), "[(unnamed)]"; got != want {
201 t.Fatalf("InitialPackages = %s, want %s", got, want)
205 func TestLoad_ParseError(t *testing.T) {
206 var conf loader.Config
207 conf.CreateFromFilenames("badpkg", "testdata/badpkgdecl.go")
209 const wantErr = "couldn't load packages due to errors: badpkg"
211 prog, err := conf.Load()
213 t.Errorf("Load unexpectedly returned a Program")
216 t.Fatalf("Load succeeded unexpectedly, want %q", wantErr)
218 if err.Error() != wantErr {
219 t.Fatalf("Load failed with wrong error %q, want %q", err, wantErr)
223 func TestLoad_ParseError_AllowErrors(t *testing.T) {
224 var conf loader.Config
225 conf.AllowErrors = true
226 conf.CreateFromFilenames("badpkg", "testdata/badpkgdecl.go")
228 prog, err := conf.Load()
230 t.Errorf("Load failed unexpectedly: %v", err)
233 t.Fatalf("Load returned a nil Program")
235 if got, want := created(prog), "badpkg"; got != want {
236 t.Errorf("Created = %s, want %s", got, want)
239 badpkg := prog.Created[0]
240 if len(badpkg.Files) != 1 {
241 t.Errorf("badpkg has %d files, want 1", len(badpkg.Files))
243 wantErr := filepath.Join("testdata", "badpkgdecl.go") + ":1:34: expected 'package', found 'EOF'"
244 if !hasError(badpkg.Errors, wantErr) {
245 t.Errorf("badpkg.Errors = %v, want %s", badpkg.Errors, wantErr)
249 func TestLoad_FromSource_Success(t *testing.T) {
250 var conf loader.Config
251 conf.CreateFromFilenames("P", "testdata/a.go", "testdata/b.go")
253 prog, err := conf.Load()
255 t.Errorf("Load failed unexpectedly: %v", err)
258 t.Fatalf("Load returned a nil Program")
260 if got, want := created(prog), "P"; got != want {
261 t.Errorf("Created = %s, want %s", got, want)
265 func TestLoad_FromImports_Success(t *testing.T) {
266 if runtime.Compiler == "gccgo" {
267 t.Skip("gccgo has no standard library test files")
270 var conf loader.Config
271 conf.ImportWithTests("fmt")
272 conf.ImportWithTests("errors")
274 prog, err := conf.Load()
276 t.Errorf("Load failed unexpectedly: %v", err)
279 t.Fatalf("Load returned a nil Program")
281 if got, want := created(prog), "errors_test fmt_test"; got != want {
282 t.Errorf("Created = %q, want %s", got, want)
284 if got, want := imported(prog), "errors fmt"; got != want {
285 t.Errorf("Imported = %s, want %s", got, want)
287 // Check set of transitive packages.
288 // There are >30 and the set may grow over time, so only check a few.
289 want := map[string]bool{
296 for _, path := range all(prog) {
300 t.Errorf("AllPackages is missing these keys: %q", keys(want))
304 func TestLoad_MissingIndirectImport(t *testing.T) {
305 pkgs := map[string]string{
306 "a": `package a; import _ "b"`,
307 "b": `package b; import _ "c"`,
309 conf := loader.Config{Build: fakeContext(pkgs)}
312 const wantErr = "couldn't load packages due to errors: b"
314 prog, err := conf.Load()
316 t.Errorf("Load succeeded unexpectedly, want %q", wantErr)
317 } else if err.Error() != wantErr {
318 t.Errorf("Load failed with wrong error %q, want %q", err, wantErr)
321 t.Errorf("Load unexpectedly returned a Program")
325 func TestLoad_BadDependency_AllowErrors(t *testing.T) {
326 for _, test := range []struct {
328 pkgs map[string]string
333 descr: "missing dependency",
334 pkgs: map[string]string{
335 "a": `package a; import _ "b"`,
336 "b": `package b; import _ "c"`,
341 descr: "bad package decl in dependency",
342 pkgs: map[string]string{
343 "a": `package a; import _ "b"`,
344 "b": `package b; import _ "c"`,
350 descr: "parse error in dependency",
351 pkgs: map[string]string{
352 "a": `package a; import _ "b"`,
353 "b": `package b; import _ "c"`,
354 "c": `package c; var x = `,
359 conf := loader.Config{
361 Build: fakeContext(test.pkgs),
365 prog, err := conf.Load()
367 t.Errorf("%s: Load failed unexpectedly: %v", test.descr, err)
370 t.Fatalf("%s: Load returned a nil Program", test.descr)
373 if got, want := imported(prog), "a"; got != want {
374 t.Errorf("%s: Imported = %s, want %s", test.descr, got, want)
376 if got := all(prog); strings.Join(got, " ") != test.wantPkgs {
377 t.Errorf("%s: AllPackages = %s, want %s", test.descr, got, test.wantPkgs)
382 func TestCwd(t *testing.T) {
383 ctxt := fakeContext(map[string]string{"one/two/three": `package three`})
384 for _, test := range []struct {
385 cwd, arg, want string
387 {cwd: "/go/src/one", arg: "./two/three", want: "one/two/three"},
388 {cwd: "/go/src/one", arg: "../one/two/three", want: "one/two/three"},
389 {cwd: "/go/src/one", arg: "one/two/three", want: "one/two/three"},
390 {cwd: "/go/src/one/two/three", arg: ".", want: "one/two/three"},
391 {cwd: "/go/src/one", arg: "two/three", want: ""},
393 conf := loader.Config{
397 conf.Import(test.arg)
400 prog, err := conf.Load()
404 if got != test.want {
405 t.Errorf("Load(%s) from %s: Imported = %s, want %s",
406 test.arg, test.cwd, got, test.want)
408 t.Errorf("Load failed: %v", err)
414 func TestLoad_vendor(t *testing.T) {
415 pkgs := map[string]string{
416 "a": `package a; import _ "x"`,
417 "a/vendor": ``, // mkdir a/vendor
418 "a/vendor/x": `package xa`,
419 "b": `package b; import _ "x"`,
420 "b/vendor": ``, // mkdir b/vendor
421 "b/vendor/x": `package xb`,
422 "c": `package c; import _ "x"`,
425 conf := loader.Config{Build: fakeContext(pkgs)}
430 prog, err := conf.Load()
435 // Check that a, b, and c see different versions of x.
436 for _, r := range "abc" {
438 got := prog.Package(name).Pkg.Imports()[0]
440 if got.Name() != want {
441 t.Errorf("package %s import %q = %s, want %s",
442 name, "x", got.Name(), want)
447 func TestVendorCwd(t *testing.T) {
448 // Test the interaction of cwd and vendor directories.
449 ctxt := fakeContext(map[string]string{
450 "net": ``, // mkdir net
451 "net/http": `package http; import _ "hpack"`,
452 "vendor": ``, // mkdir vendor
453 "vendor/hpack": `package vendorhpack`,
454 "hpack": `package hpack`,
456 for i, test := range []struct {
457 cwd, arg, want string
459 {cwd: "/go/src/net", arg: "http"}, // not found
460 {cwd: "/go/src/net", arg: "./http", want: "net/http vendor/hpack"},
461 {cwd: "/go/src/net", arg: "hpack", want: "vendor/hpack"},
462 {cwd: "/go/src/vendor", arg: "hpack", want: "vendor/hpack"},
463 {cwd: "/go/src/vendor", arg: "./hpack", want: "vendor/hpack"},
465 conf := loader.Config{
469 conf.Import(test.arg)
472 prog, err := conf.Load()
474 got = strings.Join(all(prog), " ")
476 if got != test.want {
477 t.Errorf("#%d: Load(%s) from %s: got %s, want %s",
478 i, test.arg, test.cwd, got, test.want)
480 t.Errorf("Load failed: %v", err)
486 func TestVendorCwdIssue16580(t *testing.T) {
487 // Regression test for Go issue 16580.
488 // Import decls in "created" packages were vendor-resolved
489 // w.r.t. cwd, not the parent directory of the package's files.
490 ctxt := fakeContext(map[string]string{
492 "a/vendor": ``, // mkdir a/vendor
493 "a/vendor/b": `package b; const X = true`,
494 "b": `package b; const X = false`,
496 for _, test := range []struct {
498 want bool // expected value of b.X; depends on filename, not on cwd
500 {filename: "c.go", cwd: "/go/src", want: false},
501 {filename: "c.go", cwd: "/go/src/a", want: false},
502 {filename: "c.go", cwd: "/go/src/a/b", want: false},
503 {filename: "c.go", cwd: "/go/src/a/vendor/b", want: false},
505 {filename: "/go/src/a/c.go", cwd: "/go/src", want: true},
506 {filename: "/go/src/a/c.go", cwd: "/go/src/a", want: true},
507 {filename: "/go/src/a/c.go", cwd: "/go/src/a/b", want: true},
508 {filename: "/go/src/a/c.go", cwd: "/go/src/a/vendor/b", want: true},
510 {filename: "/go/src/c/c.go", cwd: "/go/src", want: false},
511 {filename: "/go/src/c/c.go", cwd: "/go/src/a", want: false},
512 {filename: "/go/src/c/c.go", cwd: "/go/src/a/b", want: false},
513 {filename: "/go/src/c/c.go", cwd: "/go/src/a/vendor/b", want: false},
515 conf := loader.Config{
519 f, err := conf.ParseFile(test.filename, `package dummy; import "b"; const X = b.X`)
523 conf.CreateFromFiles("dummy", f)
525 prog, err := conf.Load()
527 t.Errorf("%+v: Load failed: %v", test, err)
531 x := constant.BoolVal(prog.Created[0].Pkg.Scope().Lookup("X").(*types.Const).Val())
533 t.Errorf("%+v: b.X = %t", test, x)
537 // TODO(adonovan): also test imports within XTestGoFiles.
540 // TODO(adonovan): more Load tests:
543 // - to parse package decl of *_test.go files
544 // - to parse package decl of external *_test.go files
545 // - to parse whole of *_test.go files
546 // - to parse whole of external *_test.go files
547 // - to open a *.go file during import scanning
548 // - to import from binary
552 // - PackageCreated hook
553 // - TypeCheckFuncBodies hook
555 func TestTransitivelyErrorFreeFlag(t *testing.T) {
556 // Create an minimal custom build.Context
557 // that fakes the following packages:
559 // a --> b --> c! c has an error
560 // \ d and e are transitively error-free.
563 // Each package [a-e] consists of one file, x.go.
564 pkgs := map[string]string{
565 "a": `package a; import (_ "b"; _ "e")`,
566 "b": `package b; import _ "c"`,
567 "c": `package c; func f() { _ = int(false) }`, // type error within function body
569 "e": `package e; import _ "d"`,
571 conf := loader.Config{
573 Build: fakeContext(pkgs),
577 prog, err := conf.Load()
579 t.Errorf("Load failed: %s", err)
582 t.Fatalf("Load returned nil *Program")
585 for pkg, info := range prog.AllPackages {
586 var wantErr, wantTEF bool
594 t.Errorf("unexpected package: %q", pkg.Path())
598 if (info.Errors != nil) != wantErr {
600 t.Errorf("Package %q.Error = nil, want error", pkg.Path())
602 t.Errorf("Package %q has unexpected Errors: %v",
603 pkg.Path(), info.Errors)
607 if info.TransitivelyErrorFree != wantTEF {
608 t.Errorf("Package %q.TransitivelyErrorFree=%t, want %t",
609 pkg.Path(), info.TransitivelyErrorFree, wantTEF)
614 // Test that syntax (scan/parse), type, and loader errors are recorded
615 // (in PackageInfo.Errors) and reported (via Config.TypeChecker.Error).
616 func TestErrorReporting(t *testing.T) {
617 pkgs := map[string]string{
618 "a": `package a; import (_ "b"; _ "c"); var x int = false`,
619 "b": `package b; 'syntax error!`,
621 conf := loader.Config{
623 Build: fakeContext(pkgs),
626 var allErrors []error
627 conf.TypeChecker.Error = func(err error) {
629 allErrors = append(allErrors, err)
634 prog, err := conf.Load()
636 t.Errorf("Load failed: %s", err)
639 t.Fatalf("Load returned nil *Program")
642 // TODO(adonovan): test keys of ImportMap.
644 // Check errors recorded in each PackageInfo.
645 for pkg, info := range prog.AllPackages {
648 // The match below is unfortunately vague, because in go1.16 the error
649 // message in go/types changed from "cannot convert ..." to "cannot use
650 // ... as ... in assignment".
651 if !hasError(info.Errors, "cannot") {
652 t.Errorf("a.Errors = %v, want bool assignment (type) error", info.Errors)
654 if !hasError(info.Errors, "could not import c") {
655 t.Errorf("a.Errors = %v, want import (loader) error", info.Errors)
658 if !hasError(info.Errors, "rune literal not terminated") {
659 t.Errorf("b.Errors = %v, want unterminated literal (syntax) error", info.Errors)
664 // Check errors reported via error handler.
665 if !hasError(allErrors, "cannot") ||
666 !hasError(allErrors, "rune literal not terminated") ||
667 !hasError(allErrors, "could not import c") {
668 t.Errorf("allErrors = %v, want syntax, type and loader errors", allErrors)
672 func TestCycles(t *testing.T) {
673 for _, test := range []struct {
680 fakeContext(map[string]string{
681 "main": `package main; import _ "selfcycle"`,
682 "selfcycle": `package selfcycle; import _ "selfcycle"`,
684 `import cycle: selfcycle -> selfcycle`,
687 "three-package cycle",
688 fakeContext(map[string]string{
689 "main": `package main; import _ "a"`,
690 "a": `package a; import _ "b"`,
691 "b": `package b; import _ "c"`,
692 "c": `package c; import _ "a"`,
694 `import cycle: c -> a -> b -> c`,
697 "self-cycle in dependency of test file",
698 buildutil.FakeContext(map[string]map[string]string{
700 "main.go": `package main`,
701 "main_test.go": `package main; import _ "a"`,
704 "a.go": `package a; import _ "a"`,
707 `import cycle: a -> a`,
709 // TODO(adonovan): fix: these fail
711 // "two-package cycle in dependency of test file",
712 // buildutil.FakeContext(map[string]map[string]string{
714 // "main.go": `package main`,
715 // "main_test.go": `package main; import _ "a"`,
718 // "a.go": `package a; import _ "main"`,
721 // `import cycle: main -> a -> main`,
724 // "self-cycle in augmented package",
725 // buildutil.FakeContext(map[string]map[string]string{
727 // "main.go": `package main`,
728 // "main_test.go": `package main; import _ "main"`,
731 // `import cycle: main -> main`,
734 conf := loader.Config{
739 var allErrors []error
740 conf.TypeChecker.Error = func(err error) {
742 allErrors = append(allErrors, err)
745 conf.ImportWithTests("main")
747 prog, err := conf.Load()
749 t.Errorf("%s: Load failed: %s", test.descr, err)
752 t.Fatalf("%s: Load returned nil *Program", test.descr)
755 if !hasError(allErrors, test.wantErr) {
756 t.Errorf("%s: Load() errors = %q, want %q",
757 test.descr, allErrors, test.wantErr)
762 // - Test that in a legal test cycle, none of the symbols
763 // defined by augmentation are visible via import.
766 // ---- utilities ----
768 // Simplifying wrapper around buildutil.FakeContext for single-file packages.
769 func fakeContext(pkgs map[string]string) *build.Context {
770 pkgs2 := make(map[string]map[string]string)
771 for path, content := range pkgs {
772 pkgs2[path] = map[string]string{"x.go": content}
774 return buildutil.FakeContext(pkgs2)
777 func hasError(errors []error, substr string) bool {
778 for _, err := range errors {
779 if strings.Contains(err.Error(), substr) {
786 func keys(m map[string]bool) (keys []string) {
788 keys = append(keys, key)
794 // Returns all loaded packages.
795 func all(prog *loader.Program) []string {
797 for _, info := range prog.AllPackages {
798 pkgs = append(pkgs, info.Pkg.Path())
804 // Returns initially imported packages, as a string.
805 func imported(prog *loader.Program) string {
807 for _, info := range prog.Imported {
808 pkgs = append(pkgs, info.Pkg.Path())
811 return strings.Join(pkgs, " ")
814 // Returns initially created packages, as a string.
815 func created(prog *loader.Program) string {
817 for _, info := range prog.Created {
818 pkgs = append(pkgs, info.Pkg.Path())
820 return strings.Join(pkgs, " ")
823 // Load package "io" twice in parallel.
824 // When run with -race, this is a regression test for Go issue 20718, in
825 // which the global "unsafe" package was modified concurrently.
826 func TestLoad1(t *testing.T) { loadIO(t) }
827 func TestLoad2(t *testing.T) { loadIO(t) }
829 func loadIO(t *testing.T) {
831 conf := &loader.Config{ImportPkgs: map[string]bool{"io": false}}
832 if _, err := conf.Load(); err != nil {