1 package modpkgload
2
3 import (
4 "context"
5 "errors"
6 "fmt"
7 "io/fs"
8 "path"
9 "path/filepath"
10 "strings"
11 "testing"
12
13 "github.com/go-quicktest/qt"
14 "github.com/google/go-cmp/cmp"
15 "golang.org/x/tools/txtar"
16
17 "cuelang.org/go/internal/mod/modrequirements"
18 "cuelang.org/go/internal/txtarfs"
19 "cuelang.org/go/mod/modfile"
20 "cuelang.org/go/mod/module"
21 )
22
23 func TestLoadPackages(t *testing.T) {
24 files, err := filepath.Glob("testdata/*.txtar")
25 qt.Assert(t, qt.IsNil(err))
26 for _, f := range files {
27 ar, err := txtar.ParseFile(f)
28 qt.Assert(t, qt.IsNil(err))
29 tfs := txtarfs.FS(ar)
30 reg := testRegistry{tfs}
31 testDirs, _ := fs.Glob(tfs, "test[0-9]*")
32 for _, testDir := range testDirs {
33 testName := strings.TrimSuffix(filepath.Base(f), ".txtar")
34 t.Run(testName, func(t *testing.T) {
35 t.Logf("test file: %v", f)
36 readTestFile := func(name string) string {
37 data, err := fs.ReadFile(tfs, path.Join(testDir, name))
38 qt.Assert(t, qt.IsNil(err))
39 return string(data)
40 }
41
42 initialRequirementsStr := strings.Fields(readTestFile("initial-requirements"))
43 mainModulePath, moduleVersions := initialRequirementsStr[0], mapSlice(initialRequirementsStr[1:], module.MustParseVersion)
44 defaultMajorVersions := make(map[string]string)
45 for _, f := range strings.Fields(readTestFile("default-major-versions")) {
46 p, v, ok := strings.Cut(f, "@")
47 qt.Assert(t, qt.IsTrue(ok))
48 defaultMajorVersions[p] = v
49 }
50 initialRequirements := modrequirements.NewRequirements(mainModulePath, reg, moduleVersions, defaultMajorVersions)
51
52 rootPackages := strings.Fields(readTestFile("root-packages"))
53 want := readTestFile("want")
54
55 var out strings.Builder
56 printf := func(f string, a ...any) {
57 fmt.Fprintf(&out, f, a...)
58 }
59 pkgs := LoadPackages(
60 context.Background(),
61 mainModulePath,
62 module.SourceLoc{FS: tfs, Dir: "."},
63 initialRequirements,
64 reg,
65 rootPackages,
66 )
67 for _, pkg := range pkgs.All() {
68 printf("%s\n", pkg.ImportPath())
69 printf("\tflags: %v\n", pkg.Flags())
70 if pkg.Error() != nil {
71 printf("\terror: %v\n", pkg.Error())
72 printf("\tmissing: %v\n", errors.As(pkg.Error(), new(*ImportMissingError)))
73 } else {
74 printf("\tmod: %v\n", pkg.Mod())
75 for _, loc := range pkg.Locations() {
76 printf("\tlocation: %v\n", loc.Dir)
77 }
78 if imps := pkg.Imports(); len(imps) > 0 {
79 printf("\timports:\n")
80 for _, imp := range imps {
81 printf("\t\t%v\n", imp.ImportPath())
82 }
83 }
84 }
85 }
86 if diff := cmp.Diff(string(want), out.String()); diff != "" {
87 t.Logf("actual result:\n%s", out.String())
88 t.Fatalf("unexpected results (-want +got):\n%s", diff)
89 }
90 })
91 }
92 }
93 }
94
95 type testRegistry struct {
96 fs fs.FS
97 }
98
99 func (r testRegistry) Fetch(ctx context.Context, m module.Version) (module.SourceLoc, error) {
100 mpath := r.modpath(m)
101 info, err := fs.Stat(r.fs, mpath)
102 if err != nil || !info.IsDir() {
103 return module.SourceLoc{}, fmt.Errorf("module %v not found at %v", m, mpath)
104 }
105 return module.SourceLoc{
106 FS: r.fs,
107 Dir: mpath,
108 }, nil
109 }
110
111 func (r testRegistry) Requirements(ctx context.Context, m module.Version) ([]module.Version, error) {
112 mpath := path.Join(r.modpath(m), "cue.mod/module.cue")
113 data, err := fs.ReadFile(r.fs, mpath)
114 if err != nil {
115 return nil, err
116 }
117 mf, err := modfile.Parse(data, mpath)
118 if err != nil {
119 return nil, fmt.Errorf("cannot parse module file from %v: %v", m, err)
120 }
121 return mf.DepVersions(), nil
122 }
123
124 func (r testRegistry) modpath(m module.Version) string {
125 mpath, _, _ := module.SplitPathVersion(m.Path())
126 return path.Join("_registry", strings.ReplaceAll(mpath, "/", "_")+"_"+m.Version())
127 }
128
129 func mapSlice[From, To any](ss []From, f func(From) To) []To {
130 ts := make([]To, len(ss))
131 for i := range ss {
132 ts[i] = f(ss[i])
133 }
134 return ts
135 }
136
View as plain text