github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/go/loader/loader_test.go (about)

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