cuelang.org/go@v0.13.0/internal/mod/modpkgload/pkgload_test.go (about) 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/cue/ast" 18 "cuelang.org/go/internal/mod/modimports" 19 "cuelang.org/go/internal/mod/modrequirements" 20 "cuelang.org/go/mod/modfile" 21 "cuelang.org/go/mod/module" 22 ) 23 24 func TestLoadPackages(t *testing.T) { 25 files, err := filepath.Glob("testdata/*.txtar") 26 qt.Assert(t, qt.IsNil(err)) 27 for _, f := range files { 28 ar, err := txtar.ParseFile(f) 29 qt.Assert(t, qt.IsNil(err)) 30 tfs, err := txtar.FS(ar) 31 qt.Assert(t, qt.IsNil(err)) 32 reg := testRegistry{tfs} 33 testDirs, _ := fs.Glob(tfs, "test[0-9]*") 34 for _, testDir := range testDirs { 35 testName := strings.TrimSuffix(filepath.Base(f), ".txtar") + "/" + testDir 36 t.Run(testName, func(t *testing.T) { 37 t.Logf("test file: %v", f) 38 readTestFile := func(name string) string { 39 data, err := fs.ReadFile(tfs, path.Join(testDir, name)) 40 qt.Assert(t, qt.IsNil(err)) 41 return string(data) 42 } 43 44 initialRequirementsStr := strings.Fields(readTestFile("initial-requirements")) 45 mainModulePath, moduleVersions := initialRequirementsStr[0], mapSlice(initialRequirementsStr[1:], module.MustParseVersion) 46 defaultMajorVersions := make(map[string]string) 47 for _, f := range strings.Fields(readTestFile("default-major-versions")) { 48 p, v, ok := strings.Cut(f, "@") 49 qt.Assert(t, qt.IsTrue(ok)) 50 defaultMajorVersions[p] = v 51 } 52 initialRequirements := modrequirements.NewRequirements(mainModulePath, reg, moduleVersions, defaultMajorVersions) 53 54 rootPackages := strings.Fields(readTestFile("root-packages")) 55 want := readTestFile("want") 56 57 var out strings.Builder 58 printf := func(f string, a ...any) { 59 fmt.Fprintf(&out, f, a...) 60 } 61 pkgs := LoadPackages( 62 context.Background(), 63 mainModulePath, 64 module.SourceLoc{FS: tfs, Dir: "."}, 65 initialRequirements, 66 reg, 67 rootPackages, 68 func(pkgPath string, mod module.Version, fsys fs.FS, mf modimports.ModuleFile) bool { 69 return true 70 }, 71 ) 72 for _, pkg := range pkgs.All() { 73 printf("%s\n", pkg.ImportPath()) 74 printf("\tflags: %v\n", pkg.Flags()) 75 if pkg.Error() != nil { 76 printf("\terror: %v\n", pkg.Error()) 77 printf("\tmissing: %v\n", errors.As(pkg.Error(), new(*ImportMissingError))) 78 } else { 79 printf("\tmod: %v\n", pkg.Mod()) 80 for _, loc := range pkg.Locations() { 81 printf("\tlocation: %v\n", loc.Dir) 82 } 83 if imps := pkg.Imports(); len(imps) > 0 { 84 printf("\timports:\n") 85 for _, imp := range imps { 86 printf("\t\t%v\n", imp.ImportPath()) 87 } 88 } 89 } 90 } 91 if diff := cmp.Diff(want, out.String()); diff != "" { 92 t.Logf("actual result:\n%s", out.String()) 93 t.Fatalf("unexpected results (-want +got):\n%s", diff) 94 } 95 }) 96 } 97 } 98 } 99 100 func TestFindPackageLocations(t *testing.T) { 101 versionForModule := func(ctx context.Context, prefixPath string) (module.Version, error) { 102 t.Logf("versionForModule %q", prefixPath) 103 switch prefixPath { 104 case "foo.bar": 105 return module.Version{}, nil 106 case "foo.bar/a": 107 return module.MustNewVersion("foo.bar/a@v1", "v1.2.3"), nil 108 case "foo.bar/a/b": 109 return module.MustNewVersion("foo.bar/a/b@v0", "v0.2.4"), nil 110 case "foo.bar/a/b/c": 111 return module.MustNewVersion("foo.bar/a/b/c@v0", "v0.3.6"), nil 112 case "foo.bar/a/b/c/d": 113 return module.MustNewVersion("foo.bar/a/b/c/d@v5", "v5.10.20"), nil 114 default: 115 t.Errorf("unexpected call to versionForModule with prefix %q", prefixPath) 116 return module.Version{}, fmt.Errorf("no version") 117 } 118 } 119 tfs, err := txtar.FS(txtar.Parse([]byte(` 120 -- foo.bar_a/b/c/cue.mod/module.cue -- 121 // This should cause foo.bar/a to be excluded from the list 122 // of possible candidates because c is a nested module. 123 module: "something" 124 -- foo.bar_a/b/c/d/x.cue -- 125 package d 126 -- foo.bar_a_b/c/d/x.cue -- 127 package C 128 -- foo.bar_a_b_c/d/x.cue -- 129 package C 130 -- foo.bar_a_b_c_d/x.cue -- 131 package C 132 `))) 133 qt.Assert(t, qt.IsNil(err)) 134 fetch := func(ctx context.Context, m module.Version) (loc module.SourceLoc, isLocal bool, err error) { 135 t.Logf("fetch %v", m) 136 switch m.String() { 137 case "foo.bar/a@v1.2.3": 138 // Note: return true for isLocal to trigger the nested module 139 // checking logic. 140 return module.SourceLoc{ 141 FS: tfs, 142 Dir: "foo.bar_a", 143 }, true, nil 144 case "foo.bar/a/b@v0.2.4": 145 return module.SourceLoc{ 146 FS: tfs, 147 Dir: "foo.bar_a_b", 148 }, false, nil 149 case "foo.bar/a/b/c@v0.3.6": 150 return module.SourceLoc{ 151 FS: tfs, 152 Dir: "foo.bar_a_b_c", 153 }, false, nil 154 case "foo.bar/a/b/c/d@v5.10.20": 155 return module.SourceLoc{ 156 FS: tfs, 157 Dir: "foo.bar_a_b_c_d", 158 }, false, nil 159 default: 160 t.Errorf("unexpected call to versionForModule with module %q", m) 161 return module.SourceLoc{}, false, fmt.Errorf("no module") 162 } 163 } 164 locs, err := FindPackageLocations(context.Background(), "foo.bar/a/b/c/d", versionForModule, fetch) 165 qt.Assert(t, qt.IsNil(err)) 166 var dirs []string 167 for _, loc := range locs { 168 dirs = append(dirs, loc.Locs[0].Dir) 169 } 170 qt.Assert(t, qt.DeepEquals(dirs, []string{ 171 "foo.bar_a_b_c_d", 172 "foo.bar_a_b_c/d", 173 "foo.bar_a_b/c/d", 174 })) 175 } 176 177 type testRegistry struct { 178 fs fs.FS 179 } 180 181 func (r testRegistry) Fetch(ctx context.Context, m module.Version) (module.SourceLoc, error) { 182 mpath := r.modpath(m) 183 info, err := fs.Stat(r.fs, mpath) 184 if err != nil || !info.IsDir() { 185 return module.SourceLoc{}, fmt.Errorf("module %v not found at %v", m, mpath) 186 } 187 return module.SourceLoc{ 188 FS: r.fs, 189 Dir: mpath, 190 }, nil 191 } 192 193 func (r testRegistry) Requirements(ctx context.Context, m module.Version) ([]module.Version, error) { 194 mpath := path.Join(r.modpath(m), "cue.mod/module.cue") 195 data, err := fs.ReadFile(r.fs, mpath) 196 if err != nil { 197 return nil, err 198 } 199 mf, err := modfile.Parse(data, mpath) 200 if err != nil { 201 return nil, fmt.Errorf("cannot parse module file from %v: %v", m, err) 202 } 203 return mf.DepVersions(), nil 204 } 205 206 func (r testRegistry) modpath(m module.Version) string { 207 mpath, _, _ := ast.SplitPackageVersion(m.Path()) 208 return path.Join("_registry", strings.ReplaceAll(mpath, "/", "_")+"_"+m.Version()) 209 } 210 211 func mapSlice[From, To any](ss []From, f func(From) To) []To { 212 ts := make([]To, len(ss)) 213 for i := range ss { 214 ts[i] = f(ss[i]) 215 } 216 return ts 217 }