| // 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 |
| // +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) { |
| 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 an 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) |
| } |
| } |