package modpkgload import ( "context" "errors" "fmt" "io/fs" "path" "path/filepath" "strings" "testing" "github.com/go-quicktest/qt" "github.com/google/go-cmp/cmp" "golang.org/x/tools/txtar" "cuelang.org/go/internal/mod/modrequirements" "cuelang.org/go/internal/txtarfs" "cuelang.org/go/mod/modfile" "cuelang.org/go/mod/module" ) func TestLoadPackages(t *testing.T) { files, err := filepath.Glob("testdata/*.txtar") qt.Assert(t, qt.IsNil(err)) for _, f := range files { ar, err := txtar.ParseFile(f) qt.Assert(t, qt.IsNil(err)) tfs := txtarfs.FS(ar) reg := testRegistry{tfs} testDirs, _ := fs.Glob(tfs, "test[0-9]*") for _, testDir := range testDirs { testName := strings.TrimSuffix(filepath.Base(f), ".txtar") t.Run(testName, func(t *testing.T) { t.Logf("test file: %v", f) readTestFile := func(name string) string { data, err := fs.ReadFile(tfs, path.Join(testDir, name)) qt.Assert(t, qt.IsNil(err)) return string(data) } initialRequirementsStr := strings.Fields(readTestFile("initial-requirements")) mainModulePath, moduleVersions := initialRequirementsStr[0], mapSlice(initialRequirementsStr[1:], module.MustParseVersion) defaultMajorVersions := make(map[string]string) for _, f := range strings.Fields(readTestFile("default-major-versions")) { p, v, ok := strings.Cut(f, "@") qt.Assert(t, qt.IsTrue(ok)) defaultMajorVersions[p] = v } initialRequirements := modrequirements.NewRequirements(mainModulePath, reg, moduleVersions, defaultMajorVersions) rootPackages := strings.Fields(readTestFile("root-packages")) want := readTestFile("want") var out strings.Builder printf := func(f string, a ...any) { fmt.Fprintf(&out, f, a...) } pkgs := LoadPackages( context.Background(), mainModulePath, module.SourceLoc{FS: tfs, Dir: "."}, initialRequirements, reg, rootPackages, ) for _, pkg := range pkgs.All() { printf("%s\n", pkg.ImportPath()) printf("\tflags: %v\n", pkg.Flags()) if pkg.Error() != nil { printf("\terror: %v\n", pkg.Error()) printf("\tmissing: %v\n", errors.As(pkg.Error(), new(*ImportMissingError))) } else { printf("\tmod: %v\n", pkg.Mod()) for _, loc := range pkg.Locations() { printf("\tlocation: %v\n", loc.Dir) } if imps := pkg.Imports(); len(imps) > 0 { printf("\timports:\n") for _, imp := range imps { printf("\t\t%v\n", imp.ImportPath()) } } } } if diff := cmp.Diff(string(want), out.String()); diff != "" { t.Logf("actual result:\n%s", out.String()) t.Fatalf("unexpected results (-want +got):\n%s", diff) } }) } } } type testRegistry struct { fs fs.FS } func (r testRegistry) Fetch(ctx context.Context, m module.Version) (module.SourceLoc, error) { mpath := r.modpath(m) info, err := fs.Stat(r.fs, mpath) if err != nil || !info.IsDir() { return module.SourceLoc{}, fmt.Errorf("module %v not found at %v", m, mpath) } return module.SourceLoc{ FS: r.fs, Dir: mpath, }, nil } func (r testRegistry) Requirements(ctx context.Context, m module.Version) ([]module.Version, error) { mpath := path.Join(r.modpath(m), "cue.mod/module.cue") data, err := fs.ReadFile(r.fs, mpath) if err != nil { return nil, err } mf, err := modfile.Parse(data, mpath) if err != nil { return nil, fmt.Errorf("cannot parse module file from %v: %v", m, err) } return mf.DepVersions(), nil } func (r testRegistry) modpath(m module.Version) string { mpath, _, _ := module.SplitPathVersion(m.Path()) return path.Join("_registry", strings.ReplaceAll(mpath, "/", "_")+"_"+m.Version()) } func mapSlice[From, To any](ss []From, f func(From) To) []To { ts := make([]To, len(ss)) for i := range ss { ts[i] = f(ss[i]) } return ts }