| // Copyright 2013 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // No testdata on Android. | |
| //go:build !android | |
| package loader_test | |
| import ( | |
| "fmt" | |
| "go/build" | |
| "go/constant" | |
| "go/types" | |
| "os" | |
| "path/filepath" | |
| "reflect" | |
| "runtime" | |
| "sort" | |
| "strings" | |
| "sync" | |
| "testing" | |
| "golang.org/x/tools/go/buildutil" | |
| "golang.org/x/tools/go/loader" | |
| "golang.org/x/tools/internal/testenv" | |
| ) | |
| func TestMain(m *testing.M) { | |
| testenv.ExitIfSmallMachine() | |
| os.Exit(m.Run()) | |
| } | |
| // TestFromArgs checks that conf.FromArgs populates conf correctly. | |
| // It does no I/O. | |
| func TestFromArgs(t *testing.T) { | |
| type result struct { | |
| Err string | |
| Rest []string | |
| ImportPkgs map[string]bool | |
| CreatePkgs []loader.PkgSpec | |
| } | |
| for _, test := range []struct { | |
| args []string | |
| tests bool | |
| want result | |
| }{ | |
| // Mix of existing and non-existent packages. | |
| { | |
| args: []string{"nosuchpkg", "errors"}, | |
| want: result{ | |
| ImportPkgs: map[string]bool{"errors": false, "nosuchpkg": false}, | |
| }, | |
| }, | |
| // Same, with -test flag. | |
| { | |
| args: []string{"nosuchpkg", "errors"}, | |
| tests: true, | |
| want: result{ | |
| ImportPkgs: map[string]bool{"errors": true, "nosuchpkg": true}, | |
| }, | |
| }, | |
| // Surplus arguments. | |
| { | |
| args: []string{"fmt", "errors", "--", "surplus"}, | |
| want: result{ | |
| Rest: []string{"surplus"}, | |
| ImportPkgs: map[string]bool{"errors": false, "fmt": false}, | |
| }, | |
| }, | |
| // Ad hoc package specified as *.go files. | |
| { | |
| args: []string{"foo.go", "bar.go"}, | |
| want: result{CreatePkgs: []loader.PkgSpec{{ | |
| Filenames: []string{"foo.go", "bar.go"}, | |
| }}}, | |
| }, | |
| // Mixture of *.go and import paths. | |
| { | |
| args: []string{"foo.go", "fmt"}, | |
| want: result{ | |
| Err: "named files must be .go files: fmt", | |
| }, | |
| }, | |
| } { | |
| var conf loader.Config | |
| rest, err := conf.FromArgs(test.args, test.tests) | |
| got := result{ | |
| Rest: rest, | |
| ImportPkgs: conf.ImportPkgs, | |
| CreatePkgs: conf.CreatePkgs, | |
| } | |
| if err != nil { | |
| got.Err = err.Error() | |
| } | |
| if !reflect.DeepEqual(got, test.want) { | |
| t.Errorf("FromArgs(%q) = %+v, want %+v", test.args, got, test.want) | |
| } | |
| } | |
| } | |
| func TestLoad_NoInitialPackages(t *testing.T) { | |
| var conf loader.Config | |
| const wantErr = "no initial packages were loaded" | |
| prog, err := conf.Load() | |
| if err == nil { | |
| t.Errorf("Load succeeded unexpectedly, want %q", wantErr) | |
| } else if err.Error() != wantErr { | |
| t.Errorf("Load failed with wrong error %q, want %q", err, wantErr) | |
| } | |
| if prog != nil { | |
| t.Errorf("Load unexpectedly returned a Program") | |
| } | |
| } | |
| func TestLoad_MissingInitialPackage(t *testing.T) { | |
| if runtime.GOOS == "wasip1" { | |
| t.Skip("Skipping due to golang/go#64725: fails with EBADF errors") | |
| } | |
| var conf loader.Config | |
| conf.Import("nosuchpkg") | |
| conf.Import("errors") | |
| const wantErr = "couldn't load packages due to errors: nosuchpkg" | |
| prog, err := conf.Load() | |
| if err == nil { | |
| t.Errorf("Load succeeded unexpectedly, want %q", wantErr) | |
| } else if err.Error() != wantErr { | |
| t.Errorf("Load failed with wrong error %q, want %q", err, wantErr) | |
| } | |
| if prog != nil { | |
| t.Errorf("Load unexpectedly returned a Program") | |
| } | |
| } | |
| func TestLoad_MissingInitialPackage_AllowErrors(t *testing.T) { | |
| if runtime.Compiler == "gccgo" { | |
| t.Skip("gccgo has no standard library test files") | |
| } | |
| var conf loader.Config | |
| conf.AllowErrors = true | |
| conf.Import("nosuchpkg") | |
| conf.ImportWithTests("errors") | |
| prog, err := conf.Load() | |
| if err != nil { | |
| t.Errorf("Load failed unexpectedly: %v", err) | |
| } | |
| if prog == nil { | |
| t.Fatalf("Load returned a nil Program") | |
| } | |
| if got, want := created(prog), "errors_test"; got != want { | |
| t.Errorf("Created = %s, want %s", got, want) | |
| } | |
| if got, want := imported(prog), "errors"; got != want { | |
| t.Errorf("Imported = %s, want %s", got, want) | |
| } | |
| } | |
| func TestCreateUnnamedPackage(t *testing.T) { | |
| var conf loader.Config | |
| conf.CreateFromFilenames("") | |
| prog, err := conf.Load() | |
| if err != nil { | |
| t.Fatalf("Load failed: %v", err) | |
| } | |
| if got, want := fmt.Sprint(prog.InitialPackages()), "[(unnamed)]"; got != want { | |
| t.Errorf("InitialPackages = %s, want %s", got, want) | |
| } | |
| } | |
| func TestLoad_MissingFileInCreatedPackage(t *testing.T) { | |
| var conf loader.Config | |
| conf.CreateFromFilenames("", "missing.go") | |
| const wantErr = "couldn't load packages due to errors: (unnamed)" | |
| prog, err := conf.Load() | |
| if prog != nil { | |
| t.Errorf("Load unexpectedly returned a Program") | |
| } | |
| if err == nil { | |
| t.Fatalf("Load succeeded unexpectedly, want %q", wantErr) | |
| } | |
| if err.Error() != wantErr { | |
| t.Fatalf("Load failed with wrong error %q, want %q", err, wantErr) | |
| } | |
| } | |
| func TestLoad_MissingFileInCreatedPackage_AllowErrors(t *testing.T) { | |
| conf := loader.Config{AllowErrors: true} | |
| conf.CreateFromFilenames("", "missing.go") | |
| prog, err := conf.Load() | |
| if err != nil { | |
| t.Errorf("Load failed: %v", err) | |
| } | |
| if got, want := fmt.Sprint(prog.InitialPackages()), "[(unnamed)]"; got != want { | |
| t.Fatalf("InitialPackages = %s, want %s", got, want) | |
| } | |
| } | |
| func TestLoad_ParseError(t *testing.T) { | |
| var conf loader.Config | |
| conf.CreateFromFilenames("badpkg", "testdata/badpkgdecl.go") | |
| const wantErr = "couldn't load packages due to errors: badpkg" | |
| prog, err := conf.Load() | |
| if prog != nil { | |
| t.Errorf("Load unexpectedly returned a Program") | |
| } | |
| if err == nil { | |
| t.Fatalf("Load succeeded unexpectedly, want %q", wantErr) | |
| } | |
| if err.Error() != wantErr { | |
| t.Fatalf("Load failed with wrong error %q, want %q", err, wantErr) | |
| } | |
| } | |
| func TestLoad_ParseError_AllowErrors(t *testing.T) { | |
| var conf loader.Config | |
| conf.AllowErrors = true | |
| conf.CreateFromFilenames("badpkg", "testdata/badpkgdecl.go") | |
| prog, err := conf.Load() | |
| if err != nil { | |
| t.Errorf("Load failed unexpectedly: %v", err) | |
| } | |
| if prog == nil { | |
| t.Fatalf("Load returned a nil Program") | |
| } | |
| if got, want := created(prog), "badpkg"; got != want { | |
| t.Errorf("Created = %s, want %s", got, want) | |
| } | |
| badpkg := prog.Created[0] | |
| if len(badpkg.Files) != 1 { | |
| t.Errorf("badpkg has %d files, want 1", len(badpkg.Files)) | |
| } | |
| wantErr := filepath.Join("testdata", "badpkgdecl.go") + ":1:34: expected 'package', found 'EOF'" | |
| if !hasError(badpkg.Errors, wantErr) { | |
| t.Errorf("badpkg.Errors = %v, want %s", badpkg.Errors, wantErr) | |
| } | |
| } | |
| func TestLoad_FromSource_Success(t *testing.T) { | |
| var conf loader.Config | |
| conf.CreateFromFilenames("P", "testdata/a.go", "testdata/b.go") | |
| prog, err := conf.Load() | |
| if err != nil { | |
| t.Errorf("Load failed unexpectedly: %v", err) | |
| } | |
| if prog == nil { | |
| t.Fatalf("Load returned a nil Program") | |
| } | |
| if got, want := created(prog), "P"; got != want { | |
| t.Errorf("Created = %s, want %s", got, want) | |
| } | |
| } | |
| func TestLoad_FromImports_Success(t *testing.T) { | |
| if runtime.Compiler == "gccgo" { | |
| t.Skip("gccgo has no standard library test files") | |
| } | |
| var conf loader.Config | |
| conf.ImportWithTests("fmt") | |
| conf.ImportWithTests("errors") | |
| prog, err := conf.Load() | |
| if err != nil { | |
| t.Errorf("Load failed unexpectedly: %v", err) | |
| } | |
| if prog == nil { | |
| t.Fatalf("Load returned a nil Program") | |
| } | |
| if got, want := created(prog), "errors_test fmt_test"; got != want { | |
| t.Errorf("Created = %q, want %s", got, want) | |
| } | |
| if got, want := imported(prog), "errors fmt"; got != want { | |
| t.Errorf("Imported = %s, want %s", got, want) | |
| } | |
| // Check set of transitive packages. | |
| // There are >30 and the set may grow over time, so only check a few. | |
| want := map[string]bool{ | |
| "strings": true, | |
| "time": true, | |
| "runtime": true, | |
| "testing": true, | |
| "unicode": true, | |
| } | |
| for _, path := range all(prog) { | |
| delete(want, path) | |
| } | |
| if len(want) > 0 { | |
| t.Errorf("AllPackages is missing these keys: %q", keys(want)) | |
| } | |
| } | |
| func TestLoad_MissingIndirectImport(t *testing.T) { | |
| pkgs := map[string]string{ | |
| "a": `package a; import _ "b"`, | |
| "b": `package b; import _ "c"`, | |
| } | |
| conf := loader.Config{Build: fakeContext(pkgs)} | |
| conf.Import("a") | |
| const wantErr = "couldn't load packages due to errors: b" | |
| prog, err := conf.Load() | |
| if err == nil { | |
| t.Errorf("Load succeeded unexpectedly, want %q", wantErr) | |
| } else if err.Error() != wantErr { | |
| t.Errorf("Load failed with wrong error %q, want %q", err, wantErr) | |
| } | |
| if prog != nil { | |
| t.Errorf("Load unexpectedly returned a Program") | |
| } | |
| } | |
| func TestLoad_BadDependency_AllowErrors(t *testing.T) { | |
| for _, test := range []struct { | |
| descr string | |
| pkgs map[string]string | |
| wantPkgs string | |
| }{ | |
| { | |
| descr: "missing dependency", | |
| pkgs: map[string]string{ | |
| "a": `package a; import _ "b"`, | |
| "b": `package b; import _ "c"`, | |
| }, | |
| wantPkgs: "a b", | |
| }, | |
| { | |
| descr: "bad package decl in dependency", | |
| pkgs: map[string]string{ | |
| "a": `package a; import _ "b"`, | |
| "b": `package b; import _ "c"`, | |
| "c": `package`, | |
| }, | |
| wantPkgs: "a b", | |
| }, | |
| { | |
| descr: "parse error in dependency", | |
| pkgs: map[string]string{ | |
| "a": `package a; import _ "b"`, | |
| "b": `package b; import _ "c"`, | |
| "c": `package c; var x = `, | |
| }, | |
| wantPkgs: "a b c", | |
| }, | |
| } { | |
| conf := loader.Config{ | |
| AllowErrors: true, | |
| Build: fakeContext(test.pkgs), | |
| } | |
| conf.Import("a") | |
| prog, err := conf.Load() | |
| if err != nil { | |
| t.Errorf("%s: Load failed unexpectedly: %v", test.descr, err) | |
| } | |
| if prog == nil { | |
| t.Fatalf("%s: Load returned a nil Program", test.descr) | |
| } | |
| if got, want := imported(prog), "a"; got != want { | |
| t.Errorf("%s: Imported = %s, want %s", test.descr, got, want) | |
| } | |
| if got := all(prog); strings.Join(got, " ") != test.wantPkgs { | |
| t.Errorf("%s: AllPackages = %s, want %s", test.descr, got, test.wantPkgs) | |
| } | |
| } | |
| } | |
| func TestCwd(t *testing.T) { | |
| ctxt := fakeContext(map[string]string{"one/two/three": `package three`}) | |
| for _, test := range []struct { | |
| cwd, arg, want string | |
| }{ | |
| {cwd: "/go/src/one", arg: "./two/three", want: "one/two/three"}, | |
| {cwd: "/go/src/one", arg: "../one/two/three", want: "one/two/three"}, | |
| {cwd: "/go/src/one", arg: "one/two/three", want: "one/two/three"}, | |
| {cwd: "/go/src/one/two/three", arg: ".", want: "one/two/three"}, | |
| {cwd: "/go/src/one", arg: "two/three", want: ""}, | |
| } { | |
| conf := loader.Config{ | |
| Cwd: test.cwd, | |
| Build: ctxt, | |
| } | |
| conf.Import(test.arg) | |
| var got string | |
| prog, err := conf.Load() | |
| if prog != nil { | |
| got = imported(prog) | |
| } | |
| if got != test.want { | |
| t.Errorf("Load(%s) from %s: Imported = %s, want %s", | |
| test.arg, test.cwd, got, test.want) | |
| if err != nil { | |
| t.Errorf("Load failed: %v", err) | |
| } | |
| } | |
| } | |
| } | |
| func TestLoad_vendor(t *testing.T) { | |
| pkgs := map[string]string{ | |
| "a": `package a; import _ "x"`, | |
| "a/vendor": ``, // mkdir a/vendor | |
| "a/vendor/x": `package xa`, | |
| "b": `package b; import _ "x"`, | |
| "b/vendor": ``, // mkdir b/vendor | |
| "b/vendor/x": `package xb`, | |
| "c": `package c; import _ "x"`, | |
| "x": `package xc`, | |
| } | |
| conf := loader.Config{Build: fakeContext(pkgs)} | |
| conf.Import("a") | |
| conf.Import("b") | |
| conf.Import("c") | |
| prog, err := conf.Load() | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| // Check that a, b, and c see different versions of x. | |
| for _, r := range "abc" { | |
| name := string(r) | |
| got := prog.Package(name).Pkg.Imports()[0] | |
| want := "x" + name | |
| if got.Name() != want { | |
| t.Errorf("package %s import %q = %s, want %s", | |
| name, "x", got.Name(), want) | |
| } | |
| } | |
| } | |
| func TestVendorCwd(t *testing.T) { | |
| // Test the interaction of cwd and vendor directories. | |
| ctxt := fakeContext(map[string]string{ | |
| "net": ``, // mkdir net | |
| "net/http": `package http; import _ "hpack"`, | |
| "vendor": ``, // mkdir vendor | |
| "vendor/hpack": `package vendorhpack`, | |
| "hpack": `package hpack`, | |
| }) | |
| for i, test := range []struct { | |
| cwd, arg, want string | |
| }{ | |
| {cwd: "/go/src/net", arg: "http"}, // not found | |
| {cwd: "/go/src/net", arg: "./http", want: "net/http vendor/hpack"}, | |
| {cwd: "/go/src/net", arg: "hpack", want: "vendor/hpack"}, | |
| {cwd: "/go/src/vendor", arg: "hpack", want: "vendor/hpack"}, | |
| {cwd: "/go/src/vendor", arg: "./hpack", want: "vendor/hpack"}, | |
| } { | |
| conf := loader.Config{ | |
| Cwd: test.cwd, | |
| Build: ctxt, | |
| } | |
| conf.Import(test.arg) | |
| var got string | |
| prog, err := conf.Load() | |
| if prog != nil { | |
| got = strings.Join(all(prog), " ") | |
| } | |
| if got != test.want { | |
| t.Errorf("#%d: Load(%s) from %s: got %s, want %s", | |
| i, test.arg, test.cwd, got, test.want) | |
| if err != nil { | |
| t.Errorf("Load failed: %v", err) | |
| } | |
| } | |
| } | |
| } | |
| func TestVendorCwdIssue16580(t *testing.T) { | |
| // Regression test for Go issue 16580. | |
| // Import decls in "created" packages were vendor-resolved | |
| // w.r.t. cwd, not the parent directory of the package's files. | |
| ctxt := fakeContext(map[string]string{ | |
| "a": ``, // mkdir a | |
| "a/vendor": ``, // mkdir a/vendor | |
| "a/vendor/b": `package b; const X = true`, | |
| "b": `package b; const X = false`, | |
| }) | |
| for _, test := range []struct { | |
| filename, cwd string | |
| want bool // expected value of b.X; depends on filename, not on cwd | |
| }{ | |
| {filename: "c.go", cwd: "/go/src", want: false}, | |
| {filename: "c.go", cwd: "/go/src/a", want: false}, | |
| {filename: "c.go", cwd: "/go/src/a/b", want: false}, | |
| {filename: "c.go", cwd: "/go/src/a/vendor/b", want: false}, | |
| {filename: "/go/src/a/c.go", cwd: "/go/src", want: true}, | |
| {filename: "/go/src/a/c.go", cwd: "/go/src/a", want: true}, | |
| {filename: "/go/src/a/c.go", cwd: "/go/src/a/b", want: true}, | |
| {filename: "/go/src/a/c.go", cwd: "/go/src/a/vendor/b", want: true}, | |
| {filename: "/go/src/c/c.go", cwd: "/go/src", want: false}, | |
| {filename: "/go/src/c/c.go", cwd: "/go/src/a", want: false}, | |
| {filename: "/go/src/c/c.go", cwd: "/go/src/a/b", want: false}, | |
| {filename: "/go/src/c/c.go", cwd: "/go/src/a/vendor/b", want: false}, | |
| } { | |
| conf := loader.Config{ | |
| Cwd: test.cwd, | |
| Build: ctxt, | |
| } | |
| f, err := conf.ParseFile(test.filename, `package dummy; import "b"; const X = b.X`) | |
| if err != nil { | |
| t.Fatal(f) | |
| } | |
| conf.CreateFromFiles("dummy", f) | |
| prog, err := conf.Load() | |
| if err != nil { | |
| t.Errorf("%+v: Load failed: %v", test, err) | |
| continue | |
| } | |
| x := constant.BoolVal(prog.Created[0].Pkg.Scope().Lookup("X").(*types.Const).Val()) | |
| if x != test.want { | |
| t.Errorf("%+v: b.X = %t", test, x) | |
| } | |
| } | |
| // TODO(adonovan): also test imports within XTestGoFiles. | |
| } | |
| // TODO(adonovan): more Load tests: | |
| // | |
| // failures: | |
| // - to parse package decl of *_test.go files | |
| // - to parse package decl of external *_test.go files | |
| // - to parse whole of *_test.go files | |
| // - to parse whole of external *_test.go files | |
| // - to open a *.go file during import scanning | |
| // - to import from binary | |
| // features: | |
| // - InitialPackages | |
| // - PackageCreated hook | |
| // - TypeCheckFuncBodies hook | |
| func TestTransitivelyErrorFreeFlag(t *testing.T) { | |
| // Create a minimal custom build.Context | |
| // that fakes the following packages: | |
| // | |
| // a --> b --> c! c has an error | |
| // \ d and e are transitively error-free. | |
| // e --> d | |
| // | |
| // Each package [a-e] consists of one file, x.go. | |
| pkgs := map[string]string{ | |
| "a": `package a; import (_ "b"; _ "e")`, | |
| "b": `package b; import _ "c"`, | |
| "c": `package c; func f() { _ = int(false) }`, // type error within function body | |
| "d": `package d;`, | |
| "e": `package e; import _ "d"`, | |
| } | |
| conf := loader.Config{ | |
| AllowErrors: true, | |
| Build: fakeContext(pkgs), | |
| } | |
| conf.Import("a") | |
| prog, err := conf.Load() | |
| if err != nil { | |
| t.Errorf("Load failed: %s", err) | |
| } | |
| if prog == nil { | |
| t.Fatalf("Load returned nil *Program") | |
| } | |
| for pkg, info := range prog.AllPackages { | |
| var wantErr, wantTEF bool | |
| switch pkg.Path() { | |
| case "a", "b": | |
| case "c": | |
| wantErr = true | |
| case "d", "e": | |
| wantTEF = true | |
| default: | |
| t.Errorf("unexpected package: %q", pkg.Path()) | |
| continue | |
| } | |
| if (info.Errors != nil) != wantErr { | |
| if wantErr { | |
| t.Errorf("Package %q.Error = nil, want error", pkg.Path()) | |
| } else { | |
| t.Errorf("Package %q has unexpected Errors: %v", | |
| pkg.Path(), info.Errors) | |
| } | |
| } | |
| if info.TransitivelyErrorFree != wantTEF { | |
| t.Errorf("Package %q.TransitivelyErrorFree=%t, want %t", | |
| pkg.Path(), info.TransitivelyErrorFree, wantTEF) | |
| } | |
| } | |
| } | |
| // Test that syntax (scan/parse), type, and loader errors are recorded | |
| // (in PackageInfo.Errors) and reported (via Config.TypeChecker.Error). | |
| func TestErrorReporting(t *testing.T) { | |
| pkgs := map[string]string{ | |
| "a": `package a; import (_ "b"; _ "c"); var x int = false`, | |
| "b": `package b; 'syntax error!`, | |
| } | |
| conf := loader.Config{ | |
| AllowErrors: true, | |
| Build: fakeContext(pkgs), | |
| } | |
| var mu sync.Mutex | |
| var allErrors []error | |
| conf.TypeChecker.Error = func(err error) { | |
| mu.Lock() | |
| allErrors = append(allErrors, err) | |
| mu.Unlock() | |
| } | |
| conf.Import("a") | |
| prog, err := conf.Load() | |
| if err != nil { | |
| t.Errorf("Load failed: %s", err) | |
| } | |
| if prog == nil { | |
| t.Fatalf("Load returned nil *Program") | |
| } | |
| // TODO(adonovan): test keys of ImportMap. | |
| // Check errors recorded in each PackageInfo. | |
| for pkg, info := range prog.AllPackages { | |
| switch pkg.Path() { | |
| case "a": | |
| // The match below is unfortunately vague, because in go1.16 the error | |
| // message in go/types changed from "cannot convert ..." to "cannot use | |
| // ... as ... in assignment". | |
| if !hasError(info.Errors, "cannot") { | |
| t.Errorf("a.Errors = %v, want bool assignment (type) error", info.Errors) | |
| } | |
| if !hasError(info.Errors, "could not import c") { | |
| t.Errorf("a.Errors = %v, want import (loader) error", info.Errors) | |
| } | |
| case "b": | |
| if !hasError(info.Errors, "rune literal not terminated") { | |
| t.Errorf("b.Errors = %v, want unterminated literal (syntax) error", info.Errors) | |
| } | |
| } | |
| } | |
| // Check errors reported via error handler. | |
| if !hasError(allErrors, "cannot") || | |
| !hasError(allErrors, "rune literal not terminated") || | |
| !hasError(allErrors, "could not import c") { | |
| t.Errorf("allErrors = %v, want syntax, type and loader errors", allErrors) | |
| } | |
| } | |
| func TestCycles(t *testing.T) { | |
| for _, test := range []struct { | |
| descr string | |
| ctxt *build.Context | |
| wantErr string | |
| }{ | |
| { | |
| "self-cycle", | |
| fakeContext(map[string]string{ | |
| "main": `package main; import _ "selfcycle"`, | |
| "selfcycle": `package selfcycle; import _ "selfcycle"`, | |
| }), | |
| `import cycle: selfcycle -> selfcycle`, | |
| }, | |
| { | |
| "three-package cycle", | |
| fakeContext(map[string]string{ | |
| "main": `package main; import _ "a"`, | |
| "a": `package a; import _ "b"`, | |
| "b": `package b; import _ "c"`, | |
| "c": `package c; import _ "a"`, | |
| }), | |
| `import cycle: c -> a -> b -> c`, | |
| }, | |
| { | |
| "self-cycle in dependency of test file", | |
| buildutil.FakeContext(map[string]map[string]string{ | |
| "main": { | |
| "main.go": `package main`, | |
| "main_test.go": `package main; import _ "a"`, | |
| }, | |
| "a": { | |
| "a.go": `package a; import _ "a"`, | |
| }, | |
| }), | |
| `import cycle: a -> a`, | |
| }, | |
| // TODO(adonovan): fix: these fail | |
| // { | |
| // "two-package cycle in dependency of test file", | |
| // buildutil.FakeContext(map[string]map[string]string{ | |
| // "main": { | |
| // "main.go": `package main`, | |
| // "main_test.go": `package main; import _ "a"`, | |
| // }, | |
| // "a": { | |
| // "a.go": `package a; import _ "main"`, | |
| // }, | |
| // }), | |
| // `import cycle: main -> a -> main`, | |
| // }, | |
| // { | |
| // "self-cycle in augmented package", | |
| // buildutil.FakeContext(map[string]map[string]string{ | |
| // "main": { | |
| // "main.go": `package main`, | |
| // "main_test.go": `package main; import _ "main"`, | |
| // }, | |
| // }), | |
| // `import cycle: main -> main`, | |
| // }, | |
| } { | |
| conf := loader.Config{ | |
| AllowErrors: true, | |
| Build: test.ctxt, | |
| } | |
| var mu sync.Mutex | |
| var allErrors []error | |
| conf.TypeChecker.Error = func(err error) { | |
| mu.Lock() | |
| allErrors = append(allErrors, err) | |
| mu.Unlock() | |
| } | |
| conf.ImportWithTests("main") | |
| prog, err := conf.Load() | |
| if err != nil { | |
| t.Errorf("%s: Load failed: %s", test.descr, err) | |
| } | |
| if prog == nil { | |
| t.Fatalf("%s: Load returned nil *Program", test.descr) | |
| } | |
| if !hasError(allErrors, test.wantErr) { | |
| t.Errorf("%s: Load() errors = %q, want %q", | |
| test.descr, allErrors, test.wantErr) | |
| } | |
| } | |
| // TODO(adonovan): | |
| // - Test that in a legal test cycle, none of the symbols | |
| // defined by augmentation are visible via import. | |
| } | |
| // ---- utilities ---- | |
| // Simplifying wrapper around buildutil.FakeContext for single-file packages. | |
| func fakeContext(pkgs map[string]string) *build.Context { | |
| pkgs2 := make(map[string]map[string]string) | |
| for path, content := range pkgs { | |
| pkgs2[path] = map[string]string{"x.go": content} | |
| } | |
| return buildutil.FakeContext(pkgs2) | |
| } | |
| func hasError(errors []error, substr string) bool { | |
| for _, err := range errors { | |
| if strings.Contains(err.Error(), substr) { | |
| return true | |
| } | |
| } | |
| return false | |
| } | |
| func keys(m map[string]bool) (keys []string) { | |
| for key := range m { | |
| keys = append(keys, key) | |
| } | |
| sort.Strings(keys) | |
| return | |
| } | |
| // Returns all loaded packages. | |
| func all(prog *loader.Program) []string { | |
| var pkgs []string | |
| for _, info := range prog.AllPackages { | |
| pkgs = append(pkgs, info.Pkg.Path()) | |
| } | |
| sort.Strings(pkgs) | |
| return pkgs | |
| } | |
| // Returns initially imported packages, as a string. | |
| func imported(prog *loader.Program) string { | |
| var pkgs []string | |
| for _, info := range prog.Imported { | |
| pkgs = append(pkgs, info.Pkg.Path()) | |
| } | |
| sort.Strings(pkgs) | |
| return strings.Join(pkgs, " ") | |
| } | |
| // Returns initially created packages, as a string. | |
| func created(prog *loader.Program) string { | |
| var pkgs []string | |
| for _, info := range prog.Created { | |
| pkgs = append(pkgs, info.Pkg.Path()) | |
| } | |
| return strings.Join(pkgs, " ") | |
| } | |
| // Load package "io" twice in parallel. | |
| // When run with -race, this is a regression test for Go issue 20718, in | |
| // which the global "unsafe" package was modified concurrently. | |
| func TestLoad1(t *testing.T) { loadIO(t) } | |
| func TestLoad2(t *testing.T) { loadIO(t) } | |
| func loadIO(t *testing.T) { | |
| t.Parallel() | |
| conf := &loader.Config{ImportPkgs: map[string]bool{"io": false}} | |
| if _, err := conf.Load(); err != nil { | |
| t.Fatal(err) | |
| } | |
| } | |
| func TestCgoCwdIssue46877(t *testing.T) { | |
| testenv.NeedsTool(t, "go") | |
| testenv.NeedsTool(t, "cgo") | |
| var conf loader.Config | |
| conf.Import("golang.org/x/tools/go/loader/testdata/issue46877") | |
| if _, err := conf.Load(); err != nil { | |
| t.Errorf("Load failed: %v", err) | |
| } | |
| } |