// Package packagestest is an in-memory Go package loader for tests. It type-checks// source provided as a map of path → content, resolving imports against the// in-memory module and a small synthetic subset of the standard library (see// fakeStd), never GOROOT, a subprocess, or the disk. That keeps the fast unit// tests (table-driven, golden, compile-check, fuzz, determinism) hermetic: they// pass even when the test binary is built with -trimpath, which strips the// baked-in GOROOT that the default disk-backed importer would otherwise need.//// File paths encode the package: a file at "example.com/app/x.go" belongs to// the package whose import path is "example.com/app". The package name is taken// from the package clause.
package packagestestimport ()// fakeStd is the standard-library subset the corpus and plumb's generated output// import, given as synthetic source. Resolving stdlib here rather than through// go/importer keeps loads self-contained (no GOROOT, no disk), so the tests// type-check under -trimpath. The declared surface is exactly what the fixtures// and generated code reference: a wider import fails to resolve like any missing// package, which points at the fixture that needs a new entry added here.varfakeStd = map[string]string{"database/sql": "package sql\n\ntype DB struct{}\n","errors": "package errors\n\nfunc Join(errs ...error) error { return nil }\n","fmt": "package fmt\n\nfunc Sprint(a ...any) string { return \"\" }\n","io": "package io\n\ntype Reader interface {\n\tRead(p []byte) (n int, err error)\n}\n","log": "package log\n\ntype Logger struct{}\n","net/url": "package url\n\ntype URL struct{}\n",// os.File carries a Read method so provider_kinds' assertion that *os.File // satisfies io.Reader type-checks as it does against the real standard library."os": "package os\n\n" +"type File struct{}\n\n" +"func (f *File) Read(p []byte) (n int, err error) { return 0, nil }\n\n" +"var Stdin *File\n","strings": "package strings\n\ntype Replacer struct{}\n",}// ErrImportCycle is returned by Load when type-checking hits an import cycle. It// is a sentinel so callers classify the failure with errors.Is rather than// matching message text. (The type-checker also reports the cycle as a per-import// type error, but that loses the error chain.)varErrImportCycle = errors.New("import cycle")// Loaded is the result of loading an in-memory module.typeLoadedstruct {Packages []*discover.Package// TypeErrors are non-fatal type-checking errors (e.g. a stale generated // file). Parse errors are returned as the error result instead.TypeErrors []error}// ShuffleFunc reorders a sequence of n elements by swapping pairs. It has the// signature of math/rand/v2's (*Rand).Shuffle, so a *Rand's Shuffle method can// be passed directly.typeShuffleFuncfunc(n int, swap func(i, j int))// Load parses and type-checks the given files, grouped into packages by their// directory. The companion LoadShuffled additionally reorders the files, the// load order, and the returned package slice to exercise determinism.func ( map[string]string) (*Loaded, error) {returnLoadShuffled(, nil)}// LoadShuffled is Load with an optional reordering of files within packages and// of the packages themselves, used by the determinism test. When shuffle is nil// the deterministic sorted order is used as-is.func ( map[string]string, ShuffleFunc) (*Loaded, error) { := token.NewFileSet()// Group file paths by package import path (their directory). := map[string][]string{}for := range { := path.Dir() [] = append([], ) } := make([]string, 0, len())for := range { = append(, ) }slices.Sort()applyShuffle(, )for , := range {slices.Sort()applyShuffle(, ) }// Parse every file. := map[string][]*ast.File{} // import path → files := map[string]string{}for , := range {for , := range [] {const = parser.ParseComments | parser.SkipObjectResolution , := parser.ParseFile(, , [], )if != nil {returnnil, fmt.Errorf("parse %s: %w", , ) } [] = append([], ) [] = .Name.Name } }// Seed the synthetic standard library. These packages resolve on demand // through recChecker.Import, exactly like an intra-module import, but are not // part of the loaded module: they are excluded from pkgPaths, so they are // neither type-checked eagerly nor returned in l.Packages. Seeded after the // module files so module source positions are unaffected by this map's order.for , := rangefakeStd { , := parser.ParseFile(, +"/std.go", , parser.SkipObjectResolution)if != nil {panic(fmt.Sprintf("packagestest: fakeStd[%q] does not parse: %v", , )) } [] = []*ast.File{} [] = .Name.Name } := &Loaded{} := &recChecker{fset: ,parsed: ,names: ,cache: map[string]*types.Package{},infos: map[string]*types.Info{},typeErr: &.TypeErrors, }for , := range { , , := .check()if != nil {returnnil, } .Packages = append(.Packages, &discover.Package{PkgPath: ,Name: [],Fset: ,Syntax: [],Types: ,Info: , }) }// An import cycle is a structural load failure (the type-checker also records // it as a per-import type error, but that string loses the chain). Surface it // as the sentinel-wrapped error so callers classify with errors.Is.if .cycleErr != nil {returnnil, .cycleErr }slices.SortFunc(.Packages, func(, *discover.Package) int {returnstrings.Compare(.PkgPath, .PkgPath) })applyShuffle(.Packages, )return , nil}// recChecker type-checks the in-memory packages, resolving imports (both// intra-module packages and the synthetic standard library) recursively from// its parsed set. An import outside that set is an unknown package.typerecCheckerstruct {fset *token.FileSetparsedmap[string][]*ast.Filenamesmap[string]stringcachemap[string]*types.Packageinfosmap[string]*types.InfoinProgmap[string]booltypeErr *[]errorcycleErrerror// first import cycle seen, wrapping ErrImportCycle}func ( *recChecker) ( string) (*types.Package, error) {if , := .parsed[]; { , , := .check()return , }returnnil, fmt.Errorf("cannot find package %q", )}func ( *recChecker) ( error) { *.typeErr = append(*.typeErr, )}func ( *recChecker) ( string) (*types.Package, *types.Info, error) {if , := .cache[]; {return , .infos[], nil }if .inProg == nil { .inProg = map[string]bool{} }if .inProg[] {if .cycleErr == nil { .cycleErr = fmt.Errorf("import cycle through %s: %w", , ErrImportCycle) }returnnil, nil, .cycleErr } .inProg[] = truedeferdelete(.inProg, ) := &types.Info{Types: map[ast.Expr]types.TypeAndValue{},Defs: map[*ast.Ident]types.Object{},Uses: map[*ast.Ident]types.Object{},Selections: map[*ast.SelectorExpr]*types.Selection{},Instances: map[*ast.Ident]types.Instance{},Implicits: map[ast.Node]types.Object{},Scopes: map[ast.Node]*types.Scope{}, } := types.Config{Importer: ,Error: .appendError, } , := .Check(, .fset, .parsed[], ) .cache[] = .infos[] = return , , nil}// applyShuffle reorders s in place using the given shuffle function. A nil// shuffle leaves s untouched.func [ ~[], any]( , ShuffleFunc) {if != nil { (len(), func(, int) { [], [] = [], [] }) }}
The pages are generated with Goldsv0.8.4. (GOOS=linux GOARCH=amd64)