Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / go / loader / loader_test.go
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.
4
5 // No testdata on Android.
6
7 // +build !android
8
9 package loader_test
10
11 import (
12         "fmt"
13         "go/build"
14         "go/constant"
15         "go/types"
16         "os"
17         "path/filepath"
18         "reflect"
19         "runtime"
20         "sort"
21         "strings"
22         "sync"
23         "testing"
24
25         "golang.org/x/tools/go/buildutil"
26         "golang.org/x/tools/go/loader"
27         "golang.org/x/tools/internal/testenv"
28 )
29
30 func TestMain(m *testing.M) {
31         testenv.ExitIfSmallMachine()
32         os.Exit(m.Run())
33 }
34
35 // TestFromArgs checks that conf.FromArgs populates conf correctly.
36 // It does no I/O.
37 func TestFromArgs(t *testing.T) {
38         type result struct {
39                 Err        string
40                 Rest       []string
41                 ImportPkgs map[string]bool
42                 CreatePkgs []loader.PkgSpec
43         }
44         for _, test := range []struct {
45                 args  []string
46                 tests bool
47                 want  result
48         }{
49                 // Mix of existing and non-existent packages.
50                 {
51                         args: []string{"nosuchpkg", "errors"},
52                         want: result{
53                                 ImportPkgs: map[string]bool{"errors": false, "nosuchpkg": false},
54                         },
55                 },
56                 // Same, with -test flag.
57                 {
58                         args:  []string{"nosuchpkg", "errors"},
59                         tests: true,
60                         want: result{
61                                 ImportPkgs: map[string]bool{"errors": true, "nosuchpkg": true},
62                         },
63                 },
64                 // Surplus arguments.
65                 {
66                         args: []string{"fmt", "errors", "--", "surplus"},
67                         want: result{
68                                 Rest:       []string{"surplus"},
69                                 ImportPkgs: map[string]bool{"errors": false, "fmt": false},
70                         },
71                 },
72                 // Ad hoc package specified as *.go files.
73                 {
74                         args: []string{"foo.go", "bar.go"},
75                         want: result{CreatePkgs: []loader.PkgSpec{{
76                                 Filenames: []string{"foo.go", "bar.go"},
77                         }}},
78                 },
79                 // Mixture of *.go and import paths.
80                 {
81                         args: []string{"foo.go", "fmt"},
82                         want: result{
83                                 Err: "named files must be .go files: fmt",
84                         },
85                 },
86         } {
87                 var conf loader.Config
88                 rest, err := conf.FromArgs(test.args, test.tests)
89                 got := result{
90                         Rest:       rest,
91                         ImportPkgs: conf.ImportPkgs,
92                         CreatePkgs: conf.CreatePkgs,
93                 }
94                 if err != nil {
95                         got.Err = err.Error()
96                 }
97                 if !reflect.DeepEqual(got, test.want) {
98                         t.Errorf("FromArgs(%q) = %+v, want %+v", test.args, got, test.want)
99                 }
100         }
101 }
102
103 func TestLoad_NoInitialPackages(t *testing.T) {
104         var conf loader.Config
105
106         const wantErr = "no initial packages were loaded"
107
108         prog, err := conf.Load()
109         if err == nil {
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)
113         }
114         if prog != nil {
115                 t.Errorf("Load unexpectedly returned a Program")
116         }
117 }
118
119 func TestLoad_MissingInitialPackage(t *testing.T) {
120         var conf loader.Config
121         conf.Import("nosuchpkg")
122         conf.Import("errors")
123
124         const wantErr = "couldn't load packages due to errors: nosuchpkg"
125
126         prog, err := conf.Load()
127         if err == nil {
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)
131         }
132         if prog != nil {
133                 t.Errorf("Load unexpectedly returned a Program")
134         }
135 }
136
137 func TestLoad_MissingInitialPackage_AllowErrors(t *testing.T) {
138         if runtime.Compiler == "gccgo" {
139                 t.Skip("gccgo has no standard library test files")
140         }
141
142         var conf loader.Config
143         conf.AllowErrors = true
144         conf.Import("nosuchpkg")
145         conf.ImportWithTests("errors")
146
147         prog, err := conf.Load()
148         if err != nil {
149                 t.Errorf("Load failed unexpectedly: %v", err)
150         }
151         if prog == nil {
152                 t.Fatalf("Load returned a nil Program")
153         }
154         if got, want := created(prog), "errors_test"; got != want {
155                 t.Errorf("Created = %s, want %s", got, want)
156         }
157         if got, want := imported(prog), "errors"; got != want {
158                 t.Errorf("Imported = %s, want %s", got, want)
159         }
160 }
161
162 func TestCreateUnnamedPackage(t *testing.T) {
163         var conf loader.Config
164         conf.CreateFromFilenames("")
165         prog, err := conf.Load()
166         if err != nil {
167                 t.Fatalf("Load failed: %v", err)
168         }
169         if got, want := fmt.Sprint(prog.InitialPackages()), "[(unnamed)]"; got != want {
170                 t.Errorf("InitialPackages = %s, want %s", got, want)
171         }
172 }
173
174 func TestLoad_MissingFileInCreatedPackage(t *testing.T) {
175         var conf loader.Config
176         conf.CreateFromFilenames("", "missing.go")
177
178         const wantErr = "couldn't load packages due to errors: (unnamed)"
179
180         prog, err := conf.Load()
181         if prog != nil {
182                 t.Errorf("Load unexpectedly returned a Program")
183         }
184         if err == nil {
185                 t.Fatalf("Load succeeded unexpectedly, want %q", wantErr)
186         }
187         if err.Error() != wantErr {
188                 t.Fatalf("Load failed with wrong error %q, want %q", err, wantErr)
189         }
190 }
191
192 func TestLoad_MissingFileInCreatedPackage_AllowErrors(t *testing.T) {
193         conf := loader.Config{AllowErrors: true}
194         conf.CreateFromFilenames("", "missing.go")
195
196         prog, err := conf.Load()
197         if err != nil {
198                 t.Errorf("Load failed: %v", err)
199         }
200         if got, want := fmt.Sprint(prog.InitialPackages()), "[(unnamed)]"; got != want {
201                 t.Fatalf("InitialPackages = %s, want %s", got, want)
202         }
203 }
204
205 func TestLoad_ParseError(t *testing.T) {
206         var conf loader.Config
207         conf.CreateFromFilenames("badpkg", "testdata/badpkgdecl.go")
208
209         const wantErr = "couldn't load packages due to errors: badpkg"
210
211         prog, err := conf.Load()
212         if prog != nil {
213                 t.Errorf("Load unexpectedly returned a Program")
214         }
215         if err == nil {
216                 t.Fatalf("Load succeeded unexpectedly, want %q", wantErr)
217         }
218         if err.Error() != wantErr {
219                 t.Fatalf("Load failed with wrong error %q, want %q", err, wantErr)
220         }
221 }
222
223 func TestLoad_ParseError_AllowErrors(t *testing.T) {
224         var conf loader.Config
225         conf.AllowErrors = true
226         conf.CreateFromFilenames("badpkg", "testdata/badpkgdecl.go")
227
228         prog, err := conf.Load()
229         if err != nil {
230                 t.Errorf("Load failed unexpectedly: %v", err)
231         }
232         if prog == nil {
233                 t.Fatalf("Load returned a nil Program")
234         }
235         if got, want := created(prog), "badpkg"; got != want {
236                 t.Errorf("Created = %s, want %s", got, want)
237         }
238
239         badpkg := prog.Created[0]
240         if len(badpkg.Files) != 1 {
241                 t.Errorf("badpkg has %d files, want 1", len(badpkg.Files))
242         }
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)
246         }
247 }
248
249 func TestLoad_FromSource_Success(t *testing.T) {
250         var conf loader.Config
251         conf.CreateFromFilenames("P", "testdata/a.go", "testdata/b.go")
252
253         prog, err := conf.Load()
254         if err != nil {
255                 t.Errorf("Load failed unexpectedly: %v", err)
256         }
257         if prog == nil {
258                 t.Fatalf("Load returned a nil Program")
259         }
260         if got, want := created(prog), "P"; got != want {
261                 t.Errorf("Created = %s, want %s", got, want)
262         }
263 }
264
265 func TestLoad_FromImports_Success(t *testing.T) {
266         if runtime.Compiler == "gccgo" {
267                 t.Skip("gccgo has no standard library test files")
268         }
269
270         var conf loader.Config
271         conf.ImportWithTests("fmt")
272         conf.ImportWithTests("errors")
273
274         prog, err := conf.Load()
275         if err != nil {
276                 t.Errorf("Load failed unexpectedly: %v", err)
277         }
278         if prog == nil {
279                 t.Fatalf("Load returned a nil Program")
280         }
281         if got, want := created(prog), "errors_test fmt_test"; got != want {
282                 t.Errorf("Created = %q, want %s", got, want)
283         }
284         if got, want := imported(prog), "errors fmt"; got != want {
285                 t.Errorf("Imported = %s, want %s", got, want)
286         }
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{
290                 "strings": true,
291                 "time":    true,
292                 "runtime": true,
293                 "testing": true,
294                 "unicode": true,
295         }
296         for _, path := range all(prog) {
297                 delete(want, path)
298         }
299         if len(want) > 0 {
300                 t.Errorf("AllPackages is missing these keys: %q", keys(want))
301         }
302 }
303
304 func TestLoad_MissingIndirectImport(t *testing.T) {
305         pkgs := map[string]string{
306                 "a": `package a; import _ "b"`,
307                 "b": `package b; import _ "c"`,
308         }
309         conf := loader.Config{Build: fakeContext(pkgs)}
310         conf.Import("a")
311
312         const wantErr = "couldn't load packages due to errors: b"
313
314         prog, err := conf.Load()
315         if err == nil {
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)
319         }
320         if prog != nil {
321                 t.Errorf("Load unexpectedly returned a Program")
322         }
323 }
324
325 func TestLoad_BadDependency_AllowErrors(t *testing.T) {
326         for _, test := range []struct {
327                 descr    string
328                 pkgs     map[string]string
329                 wantPkgs string
330         }{
331
332                 {
333                         descr: "missing dependency",
334                         pkgs: map[string]string{
335                                 "a": `package a; import _ "b"`,
336                                 "b": `package b; import _ "c"`,
337                         },
338                         wantPkgs: "a b",
339                 },
340                 {
341                         descr: "bad package decl in dependency",
342                         pkgs: map[string]string{
343                                 "a": `package a; import _ "b"`,
344                                 "b": `package b; import _ "c"`,
345                                 "c": `package`,
346                         },
347                         wantPkgs: "a b",
348                 },
349                 {
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 = `,
355                         },
356                         wantPkgs: "a b c",
357                 },
358         } {
359                 conf := loader.Config{
360                         AllowErrors: true,
361                         Build:       fakeContext(test.pkgs),
362                 }
363                 conf.Import("a")
364
365                 prog, err := conf.Load()
366                 if err != nil {
367                         t.Errorf("%s: Load failed unexpectedly: %v", test.descr, err)
368                 }
369                 if prog == nil {
370                         t.Fatalf("%s: Load returned a nil Program", test.descr)
371                 }
372
373                 if got, want := imported(prog), "a"; got != want {
374                         t.Errorf("%s: Imported = %s, want %s", test.descr, got, want)
375                 }
376                 if got := all(prog); strings.Join(got, " ") != test.wantPkgs {
377                         t.Errorf("%s: AllPackages = %s, want %s", test.descr, got, test.wantPkgs)
378                 }
379         }
380 }
381
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
386         }{
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: ""},
392         } {
393                 conf := loader.Config{
394                         Cwd:   test.cwd,
395                         Build: ctxt,
396                 }
397                 conf.Import(test.arg)
398
399                 var got string
400                 prog, err := conf.Load()
401                 if prog != nil {
402                         got = imported(prog)
403                 }
404                 if got != test.want {
405                         t.Errorf("Load(%s) from %s: Imported = %s, want %s",
406                                 test.arg, test.cwd, got, test.want)
407                         if err != nil {
408                                 t.Errorf("Load failed: %v", err)
409                         }
410                 }
411         }
412 }
413
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"`,
423                 "x":          `package xc`,
424         }
425         conf := loader.Config{Build: fakeContext(pkgs)}
426         conf.Import("a")
427         conf.Import("b")
428         conf.Import("c")
429
430         prog, err := conf.Load()
431         if err != nil {
432                 t.Fatal(err)
433         }
434
435         // Check that a, b, and c see different versions of x.
436         for _, r := range "abc" {
437                 name := string(r)
438                 got := prog.Package(name).Pkg.Imports()[0]
439                 want := "x" + name
440                 if got.Name() != want {
441                         t.Errorf("package %s import %q = %s, want %s",
442                                 name, "x", got.Name(), want)
443                 }
444         }
445 }
446
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`,
455         })
456         for i, test := range []struct {
457                 cwd, arg, want string
458         }{
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"},
464         } {
465                 conf := loader.Config{
466                         Cwd:   test.cwd,
467                         Build: ctxt,
468                 }
469                 conf.Import(test.arg)
470
471                 var got string
472                 prog, err := conf.Load()
473                 if prog != nil {
474                         got = strings.Join(all(prog), " ")
475                 }
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)
479                         if err != nil {
480                                 t.Errorf("Load failed: %v", err)
481                         }
482                 }
483         }
484 }
485
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{
491                 "a":          ``, // mkdir a
492                 "a/vendor":   ``, // mkdir a/vendor
493                 "a/vendor/b": `package b; const X = true`,
494                 "b":          `package b; const X = false`,
495         })
496         for _, test := range []struct {
497                 filename, cwd string
498                 want          bool // expected value of b.X; depends on filename, not on cwd
499         }{
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},
504
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},
509
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},
514         } {
515                 conf := loader.Config{
516                         Cwd:   test.cwd,
517                         Build: ctxt,
518                 }
519                 f, err := conf.ParseFile(test.filename, `package dummy; import "b"; const X = b.X`)
520                 if err != nil {
521                         t.Fatal(f)
522                 }
523                 conf.CreateFromFiles("dummy", f)
524
525                 prog, err := conf.Load()
526                 if err != nil {
527                         t.Errorf("%+v: Load failed: %v", test, err)
528                         continue
529                 }
530
531                 x := constant.BoolVal(prog.Created[0].Pkg.Scope().Lookup("X").(*types.Const).Val())
532                 if x != test.want {
533                         t.Errorf("%+v: b.X = %t", test, x)
534                 }
535         }
536
537         // TODO(adonovan): also test imports within XTestGoFiles.
538 }
539
540 // TODO(adonovan): more Load tests:
541 //
542 // failures:
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
549
550 // features:
551 // - InitialPackages
552 // - PackageCreated hook
553 // - TypeCheckFuncBodies hook
554
555 func TestTransitivelyErrorFreeFlag(t *testing.T) {
556         // Create an minimal custom build.Context
557         // that fakes the following packages:
558         //
559         // a --> b --> c!   c has an error
560         //   \              d and e are transitively error-free.
561         //    e --> d
562         //
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
568                 "d": `package d;`,
569                 "e": `package e; import _ "d"`,
570         }
571         conf := loader.Config{
572                 AllowErrors: true,
573                 Build:       fakeContext(pkgs),
574         }
575         conf.Import("a")
576
577         prog, err := conf.Load()
578         if err != nil {
579                 t.Errorf("Load failed: %s", err)
580         }
581         if prog == nil {
582                 t.Fatalf("Load returned nil *Program")
583         }
584
585         for pkg, info := range prog.AllPackages {
586                 var wantErr, wantTEF bool
587                 switch pkg.Path() {
588                 case "a", "b":
589                 case "c":
590                         wantErr = true
591                 case "d", "e":
592                         wantTEF = true
593                 default:
594                         t.Errorf("unexpected package: %q", pkg.Path())
595                         continue
596                 }
597
598                 if (info.Errors != nil) != wantErr {
599                         if wantErr {
600                                 t.Errorf("Package %q.Error = nil, want error", pkg.Path())
601                         } else {
602                                 t.Errorf("Package %q has unexpected Errors: %v",
603                                         pkg.Path(), info.Errors)
604                         }
605                 }
606
607                 if info.TransitivelyErrorFree != wantTEF {
608                         t.Errorf("Package %q.TransitivelyErrorFree=%t, want %t",
609                                 pkg.Path(), info.TransitivelyErrorFree, wantTEF)
610                 }
611         }
612 }
613
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!`,
620         }
621         conf := loader.Config{
622                 AllowErrors: true,
623                 Build:       fakeContext(pkgs),
624         }
625         var mu sync.Mutex
626         var allErrors []error
627         conf.TypeChecker.Error = func(err error) {
628                 mu.Lock()
629                 allErrors = append(allErrors, err)
630                 mu.Unlock()
631         }
632         conf.Import("a")
633
634         prog, err := conf.Load()
635         if err != nil {
636                 t.Errorf("Load failed: %s", err)
637         }
638         if prog == nil {
639                 t.Fatalf("Load returned nil *Program")
640         }
641
642         // TODO(adonovan): test keys of ImportMap.
643
644         // Check errors recorded in each PackageInfo.
645         for pkg, info := range prog.AllPackages {
646                 switch pkg.Path() {
647                 case "a":
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)
653                         }
654                         if !hasError(info.Errors, "could not import c") {
655                                 t.Errorf("a.Errors = %v, want import (loader) error", info.Errors)
656                         }
657                 case "b":
658                         if !hasError(info.Errors, "rune literal not terminated") {
659                                 t.Errorf("b.Errors = %v, want unterminated literal (syntax) error", info.Errors)
660                         }
661                 }
662         }
663
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)
669         }
670 }
671
672 func TestCycles(t *testing.T) {
673         for _, test := range []struct {
674                 descr   string
675                 ctxt    *build.Context
676                 wantErr string
677         }{
678                 {
679                         "self-cycle",
680                         fakeContext(map[string]string{
681                                 "main":      `package main; import _ "selfcycle"`,
682                                 "selfcycle": `package selfcycle; import _ "selfcycle"`,
683                         }),
684                         `import cycle: selfcycle -> selfcycle`,
685                 },
686                 {
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"`,
693                         }),
694                         `import cycle: c -> a -> b -> c`,
695                 },
696                 {
697                         "self-cycle in dependency of test file",
698                         buildutil.FakeContext(map[string]map[string]string{
699                                 "main": {
700                                         "main.go":      `package main`,
701                                         "main_test.go": `package main; import _ "a"`,
702                                 },
703                                 "a": {
704                                         "a.go": `package a; import _ "a"`,
705                                 },
706                         }),
707                         `import cycle: a -> a`,
708                 },
709                 // TODO(adonovan): fix: these fail
710                 // {
711                 //      "two-package cycle in dependency of test file",
712                 //      buildutil.FakeContext(map[string]map[string]string{
713                 //              "main": {
714                 //                      "main.go":      `package main`,
715                 //                      "main_test.go": `package main; import _ "a"`,
716                 //              },
717                 //              "a": {
718                 //                      "a.go": `package a; import _ "main"`,
719                 //              },
720                 //      }),
721                 //      `import cycle: main -> a -> main`,
722                 // },
723                 // {
724                 //      "self-cycle in augmented package",
725                 //      buildutil.FakeContext(map[string]map[string]string{
726                 //              "main": {
727                 //                      "main.go":      `package main`,
728                 //                      "main_test.go": `package main; import _ "main"`,
729                 //              },
730                 //      }),
731                 //      `import cycle: main -> main`,
732                 // },
733         } {
734                 conf := loader.Config{
735                         AllowErrors: true,
736                         Build:       test.ctxt,
737                 }
738                 var mu sync.Mutex
739                 var allErrors []error
740                 conf.TypeChecker.Error = func(err error) {
741                         mu.Lock()
742                         allErrors = append(allErrors, err)
743                         mu.Unlock()
744                 }
745                 conf.ImportWithTests("main")
746
747                 prog, err := conf.Load()
748                 if err != nil {
749                         t.Errorf("%s: Load failed: %s", test.descr, err)
750                 }
751                 if prog == nil {
752                         t.Fatalf("%s: Load returned nil *Program", test.descr)
753                 }
754
755                 if !hasError(allErrors, test.wantErr) {
756                         t.Errorf("%s: Load() errors = %q, want %q",
757                                 test.descr, allErrors, test.wantErr)
758                 }
759         }
760
761         // TODO(adonovan):
762         // - Test that in a legal test cycle, none of the symbols
763         //   defined by augmentation are visible via import.
764 }
765
766 // ---- utilities ----
767
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}
773         }
774         return buildutil.FakeContext(pkgs2)
775 }
776
777 func hasError(errors []error, substr string) bool {
778         for _, err := range errors {
779                 if strings.Contains(err.Error(), substr) {
780                         return true
781                 }
782         }
783         return false
784 }
785
786 func keys(m map[string]bool) (keys []string) {
787         for key := range m {
788                 keys = append(keys, key)
789         }
790         sort.Strings(keys)
791         return
792 }
793
794 // Returns all loaded packages.
795 func all(prog *loader.Program) []string {
796         var pkgs []string
797         for _, info := range prog.AllPackages {
798                 pkgs = append(pkgs, info.Pkg.Path())
799         }
800         sort.Strings(pkgs)
801         return pkgs
802 }
803
804 // Returns initially imported packages, as a string.
805 func imported(prog *loader.Program) string {
806         var pkgs []string
807         for _, info := range prog.Imported {
808                 pkgs = append(pkgs, info.Pkg.Path())
809         }
810         sort.Strings(pkgs)
811         return strings.Join(pkgs, " ")
812 }
813
814 // Returns initially created packages, as a string.
815 func created(prog *loader.Program) string {
816         var pkgs []string
817         for _, info := range prog.Created {
818                 pkgs = append(pkgs, info.Pkg.Path())
819         }
820         return strings.Join(pkgs, " ")
821 }
822
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) }
828
829 func loadIO(t *testing.T) {
830         t.Parallel()
831         conf := &loader.Config{ImportPkgs: map[string]bool{"io": false}}
832         if _, err := conf.Load(); err != nil {
833                 t.Fatal(err)
834         }
835 }