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