.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.0 / go / loader / loader_test.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.0/go/loader/loader_test.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.0/go/loader/loader_test.go
new file mode 100644 (file)
index 0000000..e68405a
--- /dev/null
@@ -0,0 +1,835 @@
+// 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.
+
+// +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)
+       }
+}