diff options
Diffstat (limited to 'go/packages')
-rw-r--r-- | go/packages/golist.go | 208 | ||||
-rw-r--r-- | go/packages/golist_overlay.go | 103 | ||||
-rw-r--r-- | go/packages/packages_test.go | 124 |
3 files changed, 231 insertions, 204 deletions
diff --git a/go/packages/golist.go b/go/packages/golist.go index 5f5f49796..31d669765 100644 --- a/go/packages/golist.go +++ b/go/packages/golist.go @@ -6,6 +6,7 @@ package packages import ( "bytes" + "context" "encoding/json" "fmt" "go/types" @@ -40,16 +41,21 @@ type responseDeduper struct { dr *driverResponse } -// init fills in r with a driverResponse. -func (r *responseDeduper) init(dr *driverResponse) { - r.dr = dr - r.seenRoots = map[string]bool{} - r.seenPackages = map[string]*Package{} +func newDeduper() *responseDeduper { + return &responseDeduper{ + dr: &driverResponse{}, + seenRoots: map[string]bool{}, + seenPackages: map[string]*Package{}, + } +} + +// addAll fills in r with a driverResponse. +func (r *responseDeduper) addAll(dr *driverResponse) { for _, pkg := range dr.Packages { - r.seenPackages[pkg.ID] = pkg + r.addPackage(pkg) } for _, root := range dr.Roots { - r.seenRoots[root] = true + r.addRoot(root) } } @@ -69,25 +75,44 @@ func (r *responseDeduper) addRoot(id string) { r.dr.Roots = append(r.dr.Roots, id) } -// goInfo contains global information from the go tool. -type goInfo struct { - rootDirs map[string]string - env goEnv +type golistState struct { + cfg *Config + ctx context.Context + + envOnce sync.Once + goEnvError error + goEnv map[string]string + + rootsOnce sync.Once + rootDirsError error + rootDirs map[string]string } -type goEnv struct { - modulesOn bool +// getEnv returns Go environment variables. Only specific variables are +// populated -- computing all of them is slow. +func (state *golistState) getEnv() (map[string]string, error) { + state.envOnce.Do(func() { + var b *bytes.Buffer + b, state.goEnvError = state.invokeGo("env", "-json", "GOMOD", "GOPATH") + if state.goEnvError != nil { + return + } + + state.goEnv = make(map[string]string) + decoder := json.NewDecoder(b) + if state.goEnvError = decoder.Decode(&state.goEnv); state.goEnvError != nil { + return + } + }) + return state.goEnv, state.goEnvError } -func determineEnv(cfg *Config) goEnv { - buf, err := invokeGo(cfg, "env", "GOMOD") +// mustGetEnv is a convenience function that can be used if getEnv has already succeeded. +func (state *golistState) mustGetEnv() map[string]string { + env, err := state.getEnv() if err != nil { - return goEnv{} + panic(fmt.Sprintf("mustGetEnv: %v", err)) } - gomod := bytes.TrimSpace(buf.Bytes()) - - env := goEnv{} - env.modulesOn = len(gomod) > 0 return env } @@ -95,42 +120,33 @@ func determineEnv(cfg *Config) goEnv { // the build system package structure. // See driver for more details. func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) { - var sizes types.Sizes + // Make sure that any asynchronous go commands are killed when we return. + parentCtx := cfg.Context + if parentCtx == nil { + parentCtx = context.Background() + } + ctx, cancel := context.WithCancel(parentCtx) + defer cancel() + + response := newDeduper() + + // Fill in response.Sizes asynchronously if necessary. var sizeserr error var sizeswg sync.WaitGroup if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 { sizeswg.Add(1) go func() { - sizes, sizeserr = getSizes(cfg) + var sizes types.Sizes + sizes, sizeserr = packagesdriver.GetSizesGolist(ctx, cfg.BuildFlags, cfg.Env, cfg.Dir, usesExportData(cfg)) + // types.SizesFor always returns nil or a *types.StdSizes. + response.dr.Sizes, _ = sizes.(*types.StdSizes) sizeswg.Done() }() } - defer sizeswg.Wait() - - // start fetching rootDirs - var info goInfo - var rootDirsReady, envReady = make(chan struct{}), make(chan struct{}) - go func() { - info.rootDirs = determineRootDirs(cfg) - close(rootDirsReady) - }() - go func() { - info.env = determineEnv(cfg) - close(envReady) - }() - getGoInfo := func() *goInfo { - <-rootDirsReady - <-envReady - return &info - } - - // Ensure that we don't leak goroutines: Load is synchronous, so callers will - // not expect it to access the fields of cfg after the call returns. - defer getGoInfo() - // always pass getGoInfo to golistDriver - golistDriver := func(cfg *Config, patterns ...string) (*driverResponse, error) { - return golistDriver(cfg, getGoInfo, patterns...) + state := &golistState{ + cfg: cfg, + ctx: ctx, } // Determine files requested in contains patterns @@ -165,46 +181,34 @@ extractQueries: } } - response := &responseDeduper{} - var err error - // See if we have any patterns to pass through to go list. Zero initial // patterns also requires a go list call, since it's the equivalent of // ".". if len(restPatterns) > 0 || len(patterns) == 0 { - dr, err := golistDriver(cfg, restPatterns...) + dr, err := state.createDriverResponse(restPatterns...) if err != nil { return nil, err } - response.init(dr) - } else { - response.init(&driverResponse{}) + response.addAll(dr) } - sizeswg.Wait() - if sizeserr != nil { - return nil, sizeserr - } - // types.SizesFor always returns nil or a *types.StdSizes - response.dr.Sizes, _ = sizes.(*types.StdSizes) - - var containsCandidates []string - if len(containFiles) != 0 { - if err := runContainsQueries(cfg, golistDriver, response, containFiles, getGoInfo); err != nil { + if err := state.runContainsQueries(response, containFiles); err != nil { return nil, err } } - modifiedPkgs, needPkgs, err := processGolistOverlay(cfg, response, getGoInfo) + modifiedPkgs, needPkgs, err := state.processGolistOverlay(response) if err != nil { return nil, err } + + var containsCandidates []string if len(containFiles) > 0 { containsCandidates = append(containsCandidates, modifiedPkgs...) containsCandidates = append(containsCandidates, needPkgs...) } - if err := addNeededOverlayPackages(cfg, golistDriver, response, needPkgs, getGoInfo); err != nil { + if err := state.addNeededOverlayPackages(response, needPkgs); err != nil { return nil, err } // Check candidate packages for containFiles. @@ -233,28 +237,32 @@ extractQueries: } } + sizeswg.Wait() + if sizeserr != nil { + return nil, sizeserr + } return response.dr, nil } -func addNeededOverlayPackages(cfg *Config, driver driver, response *responseDeduper, pkgs []string, getGoInfo func() *goInfo) error { +func (state *golistState) addNeededOverlayPackages(response *responseDeduper, pkgs []string) error { if len(pkgs) == 0 { return nil } - dr, err := driver(cfg, pkgs...) + dr, err := state.createDriverResponse(pkgs...) if err != nil { return err } for _, pkg := range dr.Packages { response.addPackage(pkg) } - _, needPkgs, err := processGolistOverlay(cfg, response, getGoInfo) + _, needPkgs, err := state.processGolistOverlay(response) if err != nil { return err } - return addNeededOverlayPackages(cfg, driver, response, needPkgs, getGoInfo) + return state.addNeededOverlayPackages(response, needPkgs) } -func runContainsQueries(cfg *Config, driver driver, response *responseDeduper, queries []string, goInfo func() *goInfo) error { +func (state *golistState) runContainsQueries(response *responseDeduper, queries []string) error { for _, query := range queries { // TODO(matloob): Do only one query per directory. fdir := filepath.Dir(query) @@ -264,13 +272,13 @@ func runContainsQueries(cfg *Config, driver driver, response *responseDeduper, q if err != nil { return fmt.Errorf("could not determine absolute path of file= query path %q: %v", query, err) } - dirResponse, err := driver(cfg, pattern) + dirResponse, err := state.createDriverResponse(pattern) if err != nil || (len(dirResponse.Packages) == 1 && len(dirResponse.Packages[0].Errors) == 1) { // There was an error loading the package. Try to load the file as an ad-hoc package. // Usually the error will appear in a returned package, but may not if we're in modules mode // and the ad-hoc is located outside a module. var queryErr error - dirResponse, queryErr = driver(cfg, query) + dirResponse, queryErr = state.createDriverResponse(query) if queryErr != nil { // Return the original error if the attempt to fall back failed. return err @@ -294,7 +302,7 @@ func runContainsQueries(cfg *Config, driver driver, response *responseDeduper, q if len(dirResponse.Packages[0].GoFiles) == 0 { filename := filepath.Join(pattern, filepath.Base(query)) // avoid recomputing abspath // TODO(matloob): check if the file is outside of a root dir? - for path := range cfg.Overlay { + for path := range state.cfg.Overlay { if path == filename { dirResponse.Packages[0].Errors = nil dirResponse.Packages[0].GoFiles = []string{path} @@ -329,10 +337,6 @@ func runContainsQueries(cfg *Config, driver driver, response *responseDeduper, q return nil } -func getSizes(cfg *Config) (types.Sizes, error) { - return packagesdriver.GetSizesGolist(cfg.Context, cfg.BuildFlags, cfg.Env, cfg.Dir, usesExportData(cfg)) -} - // Fields must match go list; // see $GOROOT/src/cmd/go/internal/load/pkg.go. type jsonPackage struct { @@ -375,10 +379,9 @@ func otherFiles(p *jsonPackage) [][]string { return [][]string{p.CFiles, p.CXXFiles, p.MFiles, p.HFiles, p.FFiles, p.SFiles, p.SwigFiles, p.SwigCXXFiles, p.SysoFiles} } -// golistDriver uses the "go list" command to expand the pattern -// words and return metadata for the specified packages. dir may be -// "" and env may be nil, as per os/exec.Command. -func golistDriver(cfg *Config, rootsDirs func() *goInfo, words ...string) (*driverResponse, error) { +// createDriverResponse uses the "go list" command to expand the pattern +// words and return a response for the specified packages. +func (state *golistState) createDriverResponse(words ...string) (*driverResponse, error) { // go list uses the following identifiers in ImportPath and Imports: // // "p" -- importable package or main (command) @@ -392,7 +395,7 @@ func golistDriver(cfg *Config, rootsDirs func() *goInfo, words ...string) (*driv // Run "go list" for complete // information on the specified packages. - buf, err := invokeGo(cfg, "list", golistargs(cfg, words)...) + buf, err := state.invokeGo("list", golistargs(state.cfg, words)...) if err != nil { return nil, err } @@ -427,7 +430,10 @@ func golistDriver(cfg *Config, rootsDirs func() *goInfo, words ...string) (*driv // contained in a known module or GOPATH entry. This will allow the package to be // properly "reclaimed" when overlays are processed. if filepath.IsAbs(p.ImportPath) && p.Error != nil { - pkgPath, ok := getPkgPath(cfg, p.ImportPath, rootsDirs) + pkgPath, ok, err := state.getPkgPath(p.ImportPath) + if err != nil { + return nil, err + } if ok { p.ImportPath = pkgPath } @@ -544,22 +550,20 @@ func golistDriver(cfg *Config, rootsDirs func() *goInfo, words ...string) (*driv } // getPkgPath finds the package path of a directory if it's relative to a root directory. -func getPkgPath(cfg *Config, dir string, goInfo func() *goInfo) (string, bool) { +func (state *golistState) getPkgPath(dir string) (string, bool, error) { absDir, err := filepath.Abs(dir) if err != nil { - cfg.Logf("error getting absolute path of %s: %v", dir, err) - return "", false + return "", false, err } - for rdir, rpath := range goInfo().rootDirs { - absRdir, err := filepath.Abs(rdir) - if err != nil { - cfg.Logf("error getting absolute path of %s: %v", rdir, err) - continue - } + roots, err := state.determineRootDirs() + if err != nil { + return "", false, err + } + + for rdir, rpath := range roots { // Make sure that the directory is in the module, // to avoid creating a path relative to another module. - if !strings.HasPrefix(absDir, absRdir) { - cfg.Logf("%s does not have prefix %s", absDir, absRdir) + if !strings.HasPrefix(absDir, rdir) { continue } // TODO(matloob): This doesn't properly handle symlinks. @@ -574,11 +578,11 @@ func getPkgPath(cfg *Config, dir string, goInfo func() *goInfo) (string, bool) { // Once the file is saved, gopls, or the next invocation of the tool will get the correct // result straight from golist. // TODO(matloob): Implement module tiebreaking? - return path.Join(rpath, filepath.ToSlash(r)), true + return path.Join(rpath, filepath.ToSlash(r)), true, nil } - return filepath.ToSlash(r), true + return filepath.ToSlash(r), true, nil } - return "", false + return "", false, nil } // absJoin absolutizes and flattens the lists of files. @@ -613,13 +617,15 @@ func golistargs(cfg *Config, words []string) []string { } // invokeGo returns the stdout of a go command invocation. -func invokeGo(cfg *Config, verb string, args ...string) (*bytes.Buffer, error) { +func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer, error) { + cfg := state.cfg + stdout := new(bytes.Buffer) stderr := new(bytes.Buffer) goArgs := []string{verb} goArgs = append(goArgs, cfg.BuildFlags...) goArgs = append(goArgs, args...) - cmd := exec.CommandContext(cfg.Context, "go", goArgs...) + cmd := exec.CommandContext(state.ctx, "go", goArgs...) // On darwin the cwd gets resolved to the real path, which breaks anything that // expects the working directory to keep the original path, including the // go command when dealing with modules. @@ -631,7 +637,7 @@ func invokeGo(cfg *Config, verb string, args ...string) (*bytes.Buffer, error) { cmd.Stdout = stdout cmd.Stderr = stderr defer func(start time.Time) { - cfg.Logf("%s for %v, stderr: <<%s>> stdout: <<%s>>\n", time.Since(start), cmdDebugStr(cmd, args...), stderr, stdout) + cfg.Logf("%s for %v, stderr: <<%s>> stdout: <<%s>>\n", time.Since(start), cmdDebugStr(cmd, goArgs...), stderr, stdout) }(time.Now()) if err := cmd.Run(); err != nil { diff --git a/go/packages/golist_overlay.go b/go/packages/golist_overlay.go index a7de62299..bc73fd69e 100644 --- a/go/packages/golist_overlay.go +++ b/go/packages/golist_overlay.go @@ -1,7 +1,6 @@ package packages import ( - "bytes" "encoding/json" "fmt" "go/parser" @@ -16,7 +15,7 @@ import ( // sometimes incorrect. // TODO(matloob): Handle unsupported cases, including the following: // - determining the correct package to add given a new import path -func processGolistOverlay(cfg *Config, response *responseDeduper, rootDirs func() *goInfo) (modifiedPkgs, needPkgs []string, err error) { +func (state *golistState) processGolistOverlay(response *responseDeduper) (modifiedPkgs, needPkgs []string, err error) { havePkgs := make(map[string]string) // importPath -> non-test package ID needPkgsSet := make(map[string]bool) modifiedPkgsSet := make(map[string]bool) @@ -34,7 +33,7 @@ func processGolistOverlay(cfg *Config, response *responseDeduper, rootDirs func( // potentially modifying the transitive set of dependencies). var overlayAddsImports bool - for opath, contents := range cfg.Overlay { + for opath, contents := range state.cfg.Overlay { base := filepath.Base(opath) dir := filepath.Dir(opath) var pkg *Package // if opath belongs to both a package and its test variant, this will be the test variant @@ -86,7 +85,10 @@ func processGolistOverlay(cfg *Config, response *responseDeduper, rootDirs func( if pkg == nil { // Try to find the module or gopath dir the file is contained in. // Then for modules, add the module opath to the beginning. - pkgPath, ok := getPkgPath(cfg, dir, rootDirs) + pkgPath, ok, err := state.getPkgPath(dir) + if err != nil { + return nil, nil, err + } if !ok { break } @@ -130,23 +132,23 @@ func processGolistOverlay(cfg *Config, response *responseDeduper, rootDirs func( continue } for _, imp := range imports { - _, found := pkg.Imports[imp] - if !found { - overlayAddsImports = true - // TODO(matloob): Handle cases when the following block isn't correct. - // These include imports of vendored packages, etc. - id, ok := havePkgs[imp] - if !ok { - id = imp - } - pkg.Imports[imp] = &Package{ID: id} - // Add dependencies to the non-test variant version of this package as wel. - if testVariantOf != nil { - testVariantOf.Imports[imp] = &Package{ID: id} - } + if _, found := pkg.Imports[imp]; found { + continue + } + + // TODO(matloob): Handle cases when the following block isn't correct. + // These include imports of vendored packages, etc. + overlayAddsImports = true + id, ok := havePkgs[imp] + if !ok { + id = imp + } + pkg.Imports[imp] = &Package{ID: id} + // Add dependencies to the non-test variant version of this package as well. + if testVariantOf != nil { + testVariantOf.Imports[imp] = &Package{ID: id} } } - continue } // toPkgPath tries to guess the package path given the id. @@ -161,8 +163,8 @@ func processGolistOverlay(cfg *Config, response *responseDeduper, rootDirs func( return id } - // Do another pass now that new packages have been created to determine the - // set of missing packages. + // Now that new packages have been created, do another pass to determine + // the new set of missing packages. for _, pkg := range response.dr.Packages { for _, imp := range pkg.Imports { pkgPath := toPkgPath(imp.ID) @@ -194,44 +196,59 @@ func hasTestFiles(p *Package) bool { return false } -// determineRootDirs returns a mapping from directories code can be contained in to the -// corresponding import path prefixes of those directories. -// Its result is used to try to determine the import path for a package containing -// an overlay file. -func determineRootDirs(cfg *Config) map[string]string { - // Assume modules first: - out, err := invokeGo(cfg, "list", "-m", "-json", "all") +// determineRootDirs returns a mapping from absolute directories that could +// contain code to their corresponding import path prefixes. +func (state *golistState) determineRootDirs() (map[string]string, error) { + env, err := state.getEnv() + if err != nil { + return nil, err + } + if env["GOMOD"] != "" { + state.rootsOnce.Do(func() { + state.rootDirs, state.rootDirsError = state.determineRootDirsModules() + }) + } else { + state.rootsOnce.Do(func() { + state.rootDirs, state.rootDirsError = state.determineRootDirsGOPATH() + }) + } + return state.rootDirs, state.rootDirsError +} + +func (state *golistState) determineRootDirsModules() (map[string]string, error) { + out, err := state.invokeGo("list", "-m", "-json", "all") if err != nil { - return determineRootDirsGOPATH(cfg) + return nil, err } m := map[string]string{} type jsonMod struct{ Path, Dir string } for dec := json.NewDecoder(out); dec.More(); { mod := new(jsonMod) if err := dec.Decode(mod); err != nil { - return m // Give up and return an empty map. Package won't be found for overlay. + return nil, err } if mod.Dir != "" && mod.Path != "" { // This is a valid module; add it to the map. - m[mod.Dir] = mod.Path + absDir, err := filepath.Abs(mod.Dir) + if err != nil { + return nil, err + } + m[absDir] = mod.Path } } - return m + return m, nil } -func determineRootDirsGOPATH(cfg *Config) map[string]string { +func (state *golistState) determineRootDirsGOPATH() (map[string]string, error) { m := map[string]string{} - out, err := invokeGo(cfg, "env", "GOPATH") - if err != nil { - // Could not determine root dir mapping. Everything is best-effort, so just return an empty map. - // When we try to find the import path for a directory, there will be no root-dir match and - // we'll give up. - return m - } - for _, p := range filepath.SplitList(string(bytes.TrimSpace(out.Bytes()))) { - m[filepath.Join(p, "src")] = "" + for _, dir := range filepath.SplitList(state.mustGetEnv()["GOPATH"]) { + absDir, err := filepath.Abs(dir) + if err != nil { + return nil, err + } + m[filepath.Join(absDir, "src")] = "" } - return m + return m, nil } func extractImports(filename string, contents []byte) ([]string, error) { diff --git a/go/packages/packages_test.go b/go/packages/packages_test.go index 9e12c20a6..4e1efb69b 100644 --- a/go/packages/packages_test.go +++ b/go/packages/packages_test.go @@ -1025,78 +1025,82 @@ func testNewPackagesInOverlay(t *testing.T, exporter packagestest.Exporter) { dir := filepath.Dir(filepath.Dir(exported.File("golang.org/fake", "a/a.go"))) - for i, test := range []struct { + for _, test := range []struct { + name string overlay map[string][]byte want string // expected value of e.E }{ - // Overlay with one file. - {map[string][]byte{ - filepath.Join(dir, "e", "e.go"): []byte(`package e; import "golang.org/fake/a"; const E = "e" + a.A`)}, + {"one_file", + map[string][]byte{ + filepath.Join(dir, "e", "e.go"): []byte(`package e; import "golang.org/fake/a"; const E = "e" + a.A`)}, `"eabc"`}, - // Overlay with multiple files in the same package. - {map[string][]byte{ - filepath.Join(dir, "e", "e.go"): []byte(`package e; import "golang.org/fake/a"; const E = "e" + a.A + underscore`), - filepath.Join(dir, "e", "e_util.go"): []byte(`package e; const underscore = "_"`), - }, + {"multiple_files_same_package", + map[string][]byte{ + filepath.Join(dir, "e", "e.go"): []byte(`package e; import "golang.org/fake/a"; const E = "e" + a.A + underscore`), + filepath.Join(dir, "e", "e_util.go"): []byte(`package e; const underscore = "_"`), + }, `"eabc_"`}, - // Overlay with multiple files in different packages. - {map[string][]byte{ - filepath.Join(dir, "e", "e.go"): []byte(`package e; import "golang.org/fake/f"; const E = "e" + f.F + underscore`), - filepath.Join(dir, "e", "e_util.go"): []byte(`package e; const underscore = "_"`), - filepath.Join(dir, "f", "f.go"): []byte(`package f; const F = "f"`), - }, + {"multiple_files_two_packages", + map[string][]byte{ + filepath.Join(dir, "e", "e.go"): []byte(`package e; import "golang.org/fake/f"; const E = "e" + f.F + underscore`), + filepath.Join(dir, "e", "e_util.go"): []byte(`package e; const underscore = "_"`), + filepath.Join(dir, "f", "f.go"): []byte(`package f; const F = "f"`), + }, `"ef_"`}, - {map[string][]byte{ - filepath.Join(dir, "e", "e.go"): []byte(`package e; import "golang.org/fake/f"; const E = "e" + f.F + underscore`), - filepath.Join(dir, "e", "e_util.go"): []byte(`package e; const underscore = "_"`), - filepath.Join(dir, "f", "f.go"): []byte(`package f; import "golang.org/fake/g"; const F = "f" + g.G`), - filepath.Join(dir, "g", "g.go"): []byte(`package g; const G = "g"`), - }, + {"multiple_files_three_packages", + map[string][]byte{ + filepath.Join(dir, "e", "e.go"): []byte(`package e; import "golang.org/fake/f"; const E = "e" + f.F + underscore`), + filepath.Join(dir, "e", "e_util.go"): []byte(`package e; const underscore = "_"`), + filepath.Join(dir, "f", "f.go"): []byte(`package f; import "golang.org/fake/g"; const F = "f" + g.G`), + filepath.Join(dir, "g", "g.go"): []byte(`package g; const G = "g"`), + }, `"efg_"`}, - {map[string][]byte{ - filepath.Join(dir, "e", "e.go"): []byte(`package e; import "golang.org/fake/f"; import "golang.org/fake/h"; const E = "e" + f.F + h.H + underscore`), - filepath.Join(dir, "e", "e_util.go"): []byte(`package e; const underscore = "_"`), - filepath.Join(dir, "f", "f.go"): []byte(`package f; import "golang.org/fake/g"; const F = "f" + g.G`), - filepath.Join(dir, "g", "g.go"): []byte(`package g; const G = "g"`), - filepath.Join(dir, "h", "h.go"): []byte(`package h; const H = "h"`), - }, + {"multiple_files_four_packages", + map[string][]byte{ + filepath.Join(dir, "e", "e.go"): []byte(`package e; import "golang.org/fake/f"; import "golang.org/fake/h"; const E = "e" + f.F + h.H + underscore`), + filepath.Join(dir, "e", "e_util.go"): []byte(`package e; const underscore = "_"`), + filepath.Join(dir, "f", "f.go"): []byte(`package f; import "golang.org/fake/g"; const F = "f" + g.G`), + filepath.Join(dir, "g", "g.go"): []byte(`package g; const G = "g"`), + filepath.Join(dir, "h", "h.go"): []byte(`package h; const H = "h"`), + }, `"efgh_"`}, - {map[string][]byte{ - filepath.Join(dir, "e", "e.go"): []byte(`package e; import "golang.org/fake/f"; const E = "e" + f.F + underscore`), - filepath.Join(dir, "e", "e_util.go"): []byte(`package e; const underscore = "_"`), - filepath.Join(dir, "f", "f.go"): []byte(`package f; import "golang.org/fake/g"; const F = "f" + g.G`), - filepath.Join(dir, "g", "g.go"): []byte(`package g; import "golang.org/fake/h"; const G = "g" + h.H`), - filepath.Join(dir, "h", "h.go"): []byte(`package h; const H = "h"`), - }, + {"multiple_files_four_packages_again", + map[string][]byte{ + filepath.Join(dir, "e", "e.go"): []byte(`package e; import "golang.org/fake/f"; const E = "e" + f.F + underscore`), + filepath.Join(dir, "e", "e_util.go"): []byte(`package e; const underscore = "_"`), + filepath.Join(dir, "f", "f.go"): []byte(`package f; import "golang.org/fake/g"; const F = "f" + g.G`), + filepath.Join(dir, "g", "g.go"): []byte(`package g; import "golang.org/fake/h"; const G = "g" + h.H`), + filepath.Join(dir, "h", "h.go"): []byte(`package h; const H = "h"`), + }, `"efgh_"`}, - // Overlay with package main. - {map[string][]byte{ - filepath.Join(dir, "e", "main.go"): []byte(`package main; import "golang.org/fake/a"; const E = "e" + a.A; func main(){}`)}, + {"main_overlay", + map[string][]byte{ + filepath.Join(dir, "e", "main.go"): []byte(`package main; import "golang.org/fake/a"; const E = "e" + a.A; func main(){}`)}, `"eabc"`}, } { - exported.Config.Overlay = test.overlay - exported.Config.Mode = packages.LoadAllSyntax - exported.Config.Logf = t.Logf - - // With an overlay, we don't know the expected import path, - // so load with the absolute path of the directory. - initial, err := packages.Load(exported.Config, filepath.Join(dir, "e")) - if err != nil { - t.Error(err) - continue - } + t.Run(test.name, func(t *testing.T) { + exported.Config.Overlay = test.overlay + exported.Config.Mode = packages.LoadAllSyntax + exported.Config.Logf = t.Logf + + // With an overlay, we don't know the expected import path, + // so load with the absolute path of the directory. + initial, err := packages.Load(exported.Config, filepath.Join(dir, "e")) + if err != nil { + t.Fatal(err) + } - // Check value of e.E. - e := initial[0] - eE := constant(e, "E") - if eE == nil { - t.Errorf("%d. e.E: got nil", i) - continue - } - got := eE.Val().String() - if got != test.want { - t.Errorf("%d. e.E: got %s, want %s", i, got, test.want) - } + // Check value of e.E. + e := initial[0] + eE := constant(e, "E") + if eE == nil { + t.Fatalf("e.E: was nil in %#v", e) + } + got := eE.Val().String() + if got != test.want { + t.Fatalf("e.E: got %s, want %s", got, test.want) + } + }) } } |