Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / internal / imports / mod_test.go
1 package imports
2
3 import (
4         "archive/zip"
5         "context"
6         "fmt"
7         "io/ioutil"
8         "log"
9         "os"
10         "path/filepath"
11         "reflect"
12         "regexp"
13         "sort"
14         "strings"
15         "sync"
16         "testing"
17
18         "golang.org/x/mod/module"
19         "golang.org/x/tools/internal/gocommand"
20         "golang.org/x/tools/internal/gopathwalk"
21         "golang.org/x/tools/internal/proxydir"
22         "golang.org/x/tools/internal/testenv"
23         "golang.org/x/tools/txtar"
24 )
25
26 // Tests that we can find packages in the stdlib.
27 func TestScanStdlib(t *testing.T) {
28         mt := setup(t, `
29 -- go.mod --
30 module x
31 `, "")
32         defer mt.cleanup()
33
34         mt.assertScanFinds("fmt", "fmt")
35 }
36
37 // Tests that we handle a nested module. This is different from other tests
38 // where the module is in scope -- here we have to figure out the import path
39 // without any help from go list.
40 func TestScanOutOfScopeNestedModule(t *testing.T) {
41         mt := setup(t, `
42 -- go.mod --
43 module x
44
45 -- x.go --
46 package x
47
48 -- v2/go.mod --
49 module x
50
51 -- v2/x.go --
52 package x`, "")
53         defer mt.cleanup()
54
55         pkg := mt.assertScanFinds("x/v2", "x")
56         if pkg != nil && !strings.HasSuffix(filepath.ToSlash(pkg.dir), "main/v2") {
57                 t.Errorf("x/v2 was found in %v, wanted .../main/v2", pkg.dir)
58         }
59         // We can't load the package name from the import path, but that should
60         // be okay -- if we end up adding this result, we'll add it with a name
61         // if necessary.
62 }
63
64 // Tests that we don't find a nested module contained in a local replace target.
65 // The code for this case is too annoying to write, so it's just ignored.
66 func TestScanNestedModuleInLocalReplace(t *testing.T) {
67         mt := setup(t, `
68 -- go.mod --
69 module x
70
71 require y v0.0.0
72 replace y => ./y
73
74 -- x.go --
75 package x
76
77 -- y/go.mod --
78 module y
79
80 -- y/y.go --
81 package y
82
83 -- y/z/go.mod --
84 module y/z
85
86 -- y/z/z.go --
87 package z
88 `, "")
89         defer mt.cleanup()
90
91         mt.assertFound("y", "y")
92
93         scan, err := scanToSlice(mt.resolver, nil)
94         if err != nil {
95                 t.Fatal(err)
96         }
97         for _, pkg := range scan {
98                 if strings.HasSuffix(filepath.ToSlash(pkg.dir), "main/y/z") {
99                         t.Errorf("scan found a package %v in dir main/y/z, wanted none", pkg.importPathShort)
100                 }
101         }
102 }
103
104 // Tests that path encoding is handled correctly. Adapted from mod_case.txt.
105 func TestModCase(t *testing.T) {
106         mt := setup(t, `
107 -- go.mod --
108 module x
109
110 require rsc.io/QUOTE v1.5.2
111
112 -- x.go --
113 package x
114
115 import _ "rsc.io/QUOTE/QUOTE"
116 `, "")
117         defer mt.cleanup()
118         mt.assertFound("rsc.io/QUOTE/QUOTE", "QUOTE")
119 }
120
121 // Not obviously relevant to goimports. Adapted from mod_domain_root.txt anyway.
122 func TestModDomainRoot(t *testing.T) {
123         mt := setup(t, `
124 -- go.mod --
125 module x
126
127 require example.com v1.0.0
128
129 -- x.go --
130 package x
131 import _ "example.com"
132 `, "")
133         defer mt.cleanup()
134         mt.assertFound("example.com", "x")
135 }
136
137 // Tests that scanning the module cache > 1 time is able to find the same module.
138 func TestModMultipleScans(t *testing.T) {
139         mt := setup(t, `
140 -- go.mod --
141 module x
142
143 require example.com v1.0.0
144
145 -- x.go --
146 package x
147 import _ "example.com"
148 `, "")
149         defer mt.cleanup()
150
151         mt.assertScanFinds("example.com", "x")
152         mt.assertScanFinds("example.com", "x")
153 }
154
155 // Tests that scanning the module cache > 1 time is able to find the same module
156 // in the module cache.
157 func TestModMultipleScansWithSubdirs(t *testing.T) {
158         mt := setup(t, `
159 -- go.mod --
160 module x
161
162 require rsc.io/quote v1.5.2
163
164 -- x.go --
165 package x
166 import _ "rsc.io/quote"
167 `, "")
168         defer mt.cleanup()
169
170         mt.assertScanFinds("rsc.io/quote", "quote")
171         mt.assertScanFinds("rsc.io/quote", "quote")
172 }
173
174 // Tests that scanning the module cache > 1 after changing a package in module cache to make it unimportable
175 // is able to find the same module.
176 func TestModCacheEditModFile(t *testing.T) {
177         mt := setup(t, `
178 -- go.mod --
179 module x
180
181 require rsc.io/quote v1.5.2
182 -- x.go --
183 package x
184 import _ "rsc.io/quote"
185 `, "")
186         defer mt.cleanup()
187         found := mt.assertScanFinds("rsc.io/quote", "quote")
188         if found == nil {
189                 t.Fatal("rsc.io/quote not found in initial scan.")
190         }
191
192         // Update the go.mod file of example.com so that it changes its module path (not allowed).
193         if err := os.Chmod(filepath.Join(found.dir, "go.mod"), 0644); err != nil {
194                 t.Fatal(err)
195         }
196         if err := ioutil.WriteFile(filepath.Join(found.dir, "go.mod"), []byte("module bad.com\n"), 0644); err != nil {
197                 t.Fatal(err)
198         }
199
200         // Test that with its cache of module packages it still finds the package.
201         mt.assertScanFinds("rsc.io/quote", "quote")
202
203         // Rewrite the main package so that rsc.io/quote is not in scope.
204         if err := ioutil.WriteFile(filepath.Join(mt.env.WorkingDir, "go.mod"), []byte("module x\n"), 0644); err != nil {
205                 t.Fatal(err)
206         }
207         if err := ioutil.WriteFile(filepath.Join(mt.env.WorkingDir, "x.go"), []byte("package x\n"), 0644); err != nil {
208                 t.Fatal(err)
209         }
210
211         // Uninitialize the go.mod dependent cached information and make sure it still finds the package.
212         mt.resolver.ClearForNewMod()
213         mt.assertScanFinds("rsc.io/quote", "quote")
214 }
215
216 // Tests that -mod=vendor works. Adapted from mod_vendor_build.txt.
217 func TestModVendorBuild(t *testing.T) {
218         mt := setup(t, `
219 -- go.mod --
220 module m
221 go 1.12
222 require rsc.io/sampler v1.3.1
223 -- x.go --
224 package x
225 import _ "rsc.io/sampler"
226 `, "")
227         defer mt.cleanup()
228
229         // Sanity-check the setup.
230         mt.assertModuleFoundInDir("rsc.io/sampler", "sampler", `pkg.*mod.*/sampler@.*$`)
231
232         // Populate vendor/ and clear out the mod cache so we can't cheat.
233         if _, err := mt.env.invokeGo(context.Background(), "mod", "vendor"); err != nil {
234                 t.Fatal(err)
235         }
236         if _, err := mt.env.invokeGo(context.Background(), "clean", "-modcache"); err != nil {
237                 t.Fatal(err)
238         }
239
240         // Clear out the resolver's cache, since we've changed the environment.
241         mt.resolver = newModuleResolver(mt.env)
242         mt.env.Env["GOFLAGS"] = "-mod=vendor"
243         mt.assertModuleFoundInDir("rsc.io/sampler", "sampler", `/vendor/`)
244 }
245
246 // Tests that -mod=vendor is auto-enabled only for go1.14 and higher.
247 // Vaguely inspired by mod_vendor_auto.txt.
248 func TestModVendorAuto(t *testing.T) {
249         mt := setup(t, `
250 -- go.mod --
251 module m
252 go 1.14
253 require rsc.io/sampler v1.3.1
254 -- x.go --
255 package x
256 import _ "rsc.io/sampler"
257 `, "")
258         defer mt.cleanup()
259
260         // Populate vendor/.
261         if _, err := mt.env.invokeGo(context.Background(), "mod", "vendor"); err != nil {
262                 t.Fatal(err)
263         }
264
265         wantDir := `pkg.*mod.*/sampler@.*$`
266         if testenv.Go1Point() >= 14 {
267                 wantDir = `/vendor/`
268         }
269         mt.assertModuleFoundInDir("rsc.io/sampler", "sampler", wantDir)
270 }
271
272 // Tests that a module replace works. Adapted from mod_list.txt. We start with
273 // go.mod2; the first part of the test is irrelevant.
274 func TestModList(t *testing.T) {
275         mt := setup(t, `
276 -- go.mod --
277 module x
278 require rsc.io/quote v1.5.1
279 replace rsc.io/sampler v1.3.0 => rsc.io/sampler v1.3.1
280
281 -- x.go --
282 package x
283 import _ "rsc.io/quote"
284 `, "")
285         defer mt.cleanup()
286
287         mt.assertModuleFoundInDir("rsc.io/sampler", "sampler", `pkg.mod.*/sampler@v1.3.1$`)
288 }
289
290 // Tests that a local replace works. Adapted from mod_local_replace.txt.
291 func TestModLocalReplace(t *testing.T) {
292         mt := setup(t, `
293 -- x/y/go.mod --
294 module x/y
295 require zz v1.0.0
296 replace zz v1.0.0 => ../z
297
298 -- x/y/y.go --
299 package y
300 import _ "zz"
301
302 -- x/z/go.mod --
303 module x/z
304
305 -- x/z/z.go --
306 package z
307 `, "x/y")
308         defer mt.cleanup()
309
310         mt.assertFound("zz", "z")
311 }
312
313 // Tests that the package at the root of the main module can be found.
314 // Adapted from the first part of mod_multirepo.txt.
315 func TestModMultirepo1(t *testing.T) {
316         mt := setup(t, `
317 -- go.mod --
318 module rsc.io/quote
319
320 -- x.go --
321 package quote
322 `, "")
323         defer mt.cleanup()
324
325         mt.assertModuleFoundInDir("rsc.io/quote", "quote", `/main`)
326 }
327
328 // Tests that a simple module dependency is found. Adapted from the third part
329 // of mod_multirepo.txt (We skip the case where it doesn't have a go.mod
330 // entry -- we just don't work in that case.)
331 func TestModMultirepo3(t *testing.T) {
332         mt := setup(t, `
333 -- go.mod --
334 module rsc.io/quote
335
336 require rsc.io/quote/v2 v2.0.1
337 -- x.go --
338 package quote
339
340 import _ "rsc.io/quote/v2"
341 `, "")
342         defer mt.cleanup()
343
344         mt.assertModuleFoundInDir("rsc.io/quote", "quote", `/main`)
345         mt.assertModuleFoundInDir("rsc.io/quote/v2", "quote", `pkg.mod.*/v2@v2.0.1$`)
346 }
347
348 // Tests that a nested module is found in the module cache, even though
349 // it's checked out. Adapted from the fourth part of mod_multirepo.txt.
350 func TestModMultirepo4(t *testing.T) {
351         mt := setup(t, `
352 -- go.mod --
353 module rsc.io/quote
354 require rsc.io/quote/v2 v2.0.1
355
356 -- x.go --
357 package quote
358 import _ "rsc.io/quote/v2"
359
360 -- v2/go.mod --
361 package rsc.io/quote/v2
362
363 -- v2/x.go --
364 package quote
365 import _ "rsc.io/quote/v2"
366 `, "")
367         defer mt.cleanup()
368
369         mt.assertModuleFoundInDir("rsc.io/quote", "quote", `/main`)
370         mt.assertModuleFoundInDir("rsc.io/quote/v2", "quote", `pkg.mod.*/v2@v2.0.1$`)
371 }
372
373 // Tests a simple module dependency. Adapted from the first part of mod_replace.txt.
374 func TestModReplace1(t *testing.T) {
375         mt := setup(t, `
376 -- go.mod --
377 module quoter
378
379 require rsc.io/quote/v3 v3.0.0
380
381 -- main.go --
382
383 package main
384 `, "")
385         defer mt.cleanup()
386         mt.assertFound("rsc.io/quote/v3", "quote")
387 }
388
389 // Tests a local replace. Adapted from the second part of mod_replace.txt.
390 func TestModReplace2(t *testing.T) {
391         mt := setup(t, `
392 -- go.mod --
393 module quoter
394
395 require rsc.io/quote/v3 v3.0.0
396 replace rsc.io/quote/v3 => ./local/rsc.io/quote/v3
397 -- main.go --
398 package main
399
400 -- local/rsc.io/quote/v3/go.mod --
401 module rsc.io/quote/v3
402
403 require rsc.io/sampler v1.3.0
404
405 -- local/rsc.io/quote/v3/quote.go --
406 package quote
407
408 import "rsc.io/sampler"
409 `, "")
410         defer mt.cleanup()
411         mt.assertModuleFoundInDir("rsc.io/quote/v3", "quote", `/local/rsc.io/quote/v3`)
412 }
413
414 // Tests that a module can be replaced by a different module path. Adapted
415 // from the third part of mod_replace.txt.
416 func TestModReplace3(t *testing.T) {
417         mt := setup(t, `
418 -- go.mod --
419 module quoter
420
421 require not-rsc.io/quote/v3 v3.1.0
422 replace not-rsc.io/quote/v3 v3.1.0 => ./local/rsc.io/quote/v3
423
424 -- usenewmodule/main.go --
425 package main
426
427 -- local/rsc.io/quote/v3/go.mod --
428 module rsc.io/quote/v3
429
430 require rsc.io/sampler v1.3.0
431
432 -- local/rsc.io/quote/v3/quote.go --
433 package quote
434
435 -- local/not-rsc.io/quote/v3/go.mod --
436 module not-rsc.io/quote/v3
437
438 -- local/not-rsc.io/quote/v3/quote.go --
439 package quote
440 `, "")
441         defer mt.cleanup()
442         mt.assertModuleFoundInDir("not-rsc.io/quote/v3", "quote", "local/rsc.io/quote/v3")
443 }
444
445 // Tests more local replaces, notably the case where an outer module provides
446 // a package that could also be provided by an inner module. Adapted from
447 // mod_replace_import.txt, with example.com/v changed to /vv because Go 1.11
448 // thinks /v is an invalid major version.
449 func TestModReplaceImport(t *testing.T) {
450         mt := setup(t, `
451 -- go.mod --
452 module example.com/m
453
454 replace (
455         example.com/a => ./a
456         example.com/a/b => ./b
457 )
458
459 replace (
460         example.com/x => ./x
461         example.com/x/v3 => ./v3
462 )
463
464 replace (
465         example.com/y/z/w => ./w
466         example.com/y => ./y
467 )
468
469 replace (
470         example.com/vv v1.11.0 => ./v11
471         example.com/vv v1.12.0 => ./v12
472         example.com/vv => ./vv
473 )
474
475 require (
476         example.com/a/b v0.0.0
477         example.com/x/v3 v3.0.0
478         example.com/y v0.0.0
479         example.com/y/z/w v0.0.0
480         example.com/vv v1.12.0
481 )
482 -- m.go --
483 package main
484 import (
485         _ "example.com/a/b"
486         _ "example.com/x/v3"
487         _ "example.com/y/z/w"
488         _ "example.com/vv"
489 )
490 func main() {}
491
492 -- a/go.mod --
493 module a.localhost
494 -- a/a.go --
495 package a
496 -- a/b/b.go--
497 package b
498
499 -- b/go.mod --
500 module a.localhost/b
501 -- b/b.go --
502 package b
503
504 -- x/go.mod --
505 module x.localhost
506 -- x/x.go --
507 package x
508 -- x/v3.go --
509 package v3
510 import _ "x.localhost/v3"
511
512 -- v3/go.mod --
513 module x.localhost/v3
514 -- v3/x.go --
515 package x
516
517 -- w/go.mod --
518 module w.localhost
519 -- w/skip/skip.go --
520 // Package skip is nested below nonexistent package w.
521 package skip
522
523 -- y/go.mod --
524 module y.localhost
525 -- y/z/w/w.go --
526 package w
527
528 -- v12/go.mod --
529 module v.localhost
530 -- v12/v.go --
531 package v
532
533 -- v11/go.mod --
534 module v.localhost
535 -- v11/v.go --
536 package v
537
538 -- vv/go.mod --
539 module v.localhost
540 -- vv/v.go --
541 package v
542 `, "")
543         defer mt.cleanup()
544
545         mt.assertModuleFoundInDir("example.com/a/b", "b", `main/b$`)
546         mt.assertModuleFoundInDir("example.com/x/v3", "x", `main/v3$`)
547         mt.assertModuleFoundInDir("example.com/y/z/w", "w", `main/y/z/w$`)
548         mt.assertModuleFoundInDir("example.com/vv", "v", `main/v12$`)
549 }
550
551 // Tests that we handle GO111MODULE=on with no go.mod file. See #30855.
552 func TestNoMainModule(t *testing.T) {
553         testenv.NeedsGo1Point(t, 12)
554         mt := setup(t, `
555 -- x.go --
556 package x
557 `, "")
558         defer mt.cleanup()
559         if _, err := mt.env.invokeGo(context.Background(), "mod", "download", "rsc.io/quote@v1.5.1"); err != nil {
560                 t.Fatal(err)
561         }
562
563         mt.assertScanFinds("rsc.io/quote", "quote")
564 }
565
566 // assertFound asserts that the package at importPath is found to have pkgName,
567 // and that scanning for pkgName finds it at importPath.
568 func (t *modTest) assertFound(importPath, pkgName string) (string, *pkg) {
569         t.Helper()
570
571         names, err := t.resolver.loadPackageNames([]string{importPath}, t.env.WorkingDir)
572         if err != nil {
573                 t.Errorf("loading package name for %v: %v", importPath, err)
574         }
575         if names[importPath] != pkgName {
576                 t.Errorf("package name for %v = %v, want %v", importPath, names[importPath], pkgName)
577         }
578         pkg := t.assertScanFinds(importPath, pkgName)
579
580         _, foundDir := t.resolver.findPackage(importPath)
581         return foundDir, pkg
582 }
583
584 func (t *modTest) assertScanFinds(importPath, pkgName string) *pkg {
585         t.Helper()
586         scan, err := scanToSlice(t.resolver, nil)
587         if err != nil {
588                 t.Errorf("scan failed: %v", err)
589         }
590         for _, pkg := range scan {
591                 if pkg.importPathShort == importPath {
592                         return pkg
593                 }
594         }
595         t.Errorf("scanning for %v did not find %v", pkgName, importPath)
596         return nil
597 }
598
599 func scanToSlice(resolver Resolver, exclude []gopathwalk.RootType) ([]*pkg, error) {
600         var mu sync.Mutex
601         var result []*pkg
602         filter := &scanCallback{
603                 rootFound: func(root gopathwalk.Root) bool {
604                         for _, rt := range exclude {
605                                 if root.Type == rt {
606                                         return false
607                                 }
608                         }
609                         return true
610                 },
611                 dirFound: func(pkg *pkg) bool {
612                         return true
613                 },
614                 packageNameLoaded: func(pkg *pkg) bool {
615                         mu.Lock()
616                         defer mu.Unlock()
617                         result = append(result, pkg)
618                         return false
619                 },
620         }
621         err := resolver.scan(context.Background(), filter)
622         return result, err
623 }
624
625 // assertModuleFoundInDir is the same as assertFound, but also checks that the
626 // package was found in an active module whose Dir matches dirRE.
627 func (t *modTest) assertModuleFoundInDir(importPath, pkgName, dirRE string) {
628         t.Helper()
629         dir, pkg := t.assertFound(importPath, pkgName)
630         re, err := regexp.Compile(dirRE)
631         if err != nil {
632                 t.Fatal(err)
633         }
634
635         if dir == "" {
636                 t.Errorf("import path %v not found in active modules", importPath)
637         } else {
638                 if !re.MatchString(filepath.ToSlash(dir)) {
639                         t.Errorf("finding dir for %s: dir = %q did not match regex %q", importPath, dir, dirRE)
640                 }
641         }
642         if pkg != nil {
643                 if !re.MatchString(filepath.ToSlash(pkg.dir)) {
644                         t.Errorf("scanning for %s: dir = %q did not match regex %q", pkgName, pkg.dir, dirRE)
645                 }
646         }
647 }
648
649 var proxyOnce sync.Once
650 var proxyDir string
651
652 type modTest struct {
653         *testing.T
654         env      *ProcessEnv
655         gopath   string
656         resolver *ModuleResolver
657         cleanup  func()
658 }
659
660 // setup builds a test environment from a txtar and supporting modules
661 // in testdata/mod, along the lines of TestScript in cmd/go.
662 func setup(t *testing.T, main, wd string) *modTest {
663         t.Helper()
664         testenv.NeedsGo1Point(t, 11)
665         testenv.NeedsTool(t, "go")
666
667         proxyOnce.Do(func() {
668                 var err error
669                 proxyDir, err = ioutil.TempDir("", "proxy-")
670                 if err != nil {
671                         t.Fatal(err)
672                 }
673                 if err := writeProxy(proxyDir, "testdata/mod"); err != nil {
674                         t.Fatal(err)
675                 }
676         })
677
678         dir, err := ioutil.TempDir("", t.Name())
679         if err != nil {
680                 t.Fatal(err)
681         }
682
683         mainDir := filepath.Join(dir, "main")
684         if err := writeModule(mainDir, main); err != nil {
685                 t.Fatal(err)
686         }
687
688         env := &ProcessEnv{
689                 Env: map[string]string{
690                         "GOPATH":      filepath.Join(dir, "gopath"),
691                         "GOMODCACHE":  "",
692                         "GO111MODULE": "on",
693                         "GOSUMDB":     "off",
694                         "GOPROXY":     proxydir.ToURL(proxyDir),
695                 },
696                 WorkingDir:  filepath.Join(mainDir, wd),
697                 GocmdRunner: &gocommand.Runner{},
698         }
699         if *testDebug {
700                 env.Logf = log.Printf
701         }
702         // go mod download gets mad if we don't have a go.mod, so make sure we do.
703         _, err = os.Stat(filepath.Join(mainDir, "go.mod"))
704         if err != nil && !os.IsNotExist(err) {
705                 t.Fatalf("checking if go.mod exists: %v", err)
706         }
707         if err == nil {
708                 if _, err := env.invokeGo(context.Background(), "mod", "download"); err != nil {
709                         t.Fatal(err)
710                 }
711         }
712
713         resolver, err := env.GetResolver()
714         if err != nil {
715                 t.Fatal(err)
716         }
717         return &modTest{
718                 T:        t,
719                 gopath:   env.Env["GOPATH"],
720                 env:      env,
721                 resolver: resolver.(*ModuleResolver),
722                 cleanup:  func() { removeDir(dir) },
723         }
724 }
725
726 // writeModule writes the module in the ar, a txtar, to dir.
727 func writeModule(dir, ar string) error {
728         a := txtar.Parse([]byte(ar))
729
730         for _, f := range a.Files {
731                 fpath := filepath.Join(dir, f.Name)
732                 if err := os.MkdirAll(filepath.Dir(fpath), 0755); err != nil {
733                         return err
734                 }
735
736                 if err := ioutil.WriteFile(fpath, f.Data, 0644); err != nil {
737                         return err
738                 }
739         }
740         return nil
741 }
742
743 // writeProxy writes all the txtar-formatted modules in arDir to a proxy
744 // directory in dir.
745 func writeProxy(dir, arDir string) error {
746         files, err := ioutil.ReadDir(arDir)
747         if err != nil {
748                 return err
749         }
750
751         for _, fi := range files {
752                 if err := writeProxyModule(dir, filepath.Join(arDir, fi.Name())); err != nil {
753                         return err
754                 }
755         }
756         return nil
757 }
758
759 // writeProxyModule writes a txtar-formatted module at arPath to the module
760 // proxy in base.
761 func writeProxyModule(base, arPath string) error {
762         arName := filepath.Base(arPath)
763         i := strings.LastIndex(arName, "_v")
764         ver := strings.TrimSuffix(arName[i+1:], ".txt")
765         modDir := strings.Replace(arName[:i], "_", "/", -1)
766         modPath, err := module.UnescapePath(modDir)
767         if err != nil {
768                 return err
769         }
770
771         dir := filepath.Join(base, modDir, "@v")
772         a, err := txtar.ParseFile(arPath)
773
774         if err != nil {
775                 return err
776         }
777
778         if err := os.MkdirAll(dir, 0755); err != nil {
779                 return err
780         }
781
782         f, err := os.OpenFile(filepath.Join(dir, ver+".zip"), os.O_CREATE|os.O_WRONLY, 0644)
783         if err != nil {
784                 return err
785         }
786         z := zip.NewWriter(f)
787         for _, f := range a.Files {
788                 if f.Name[0] == '.' {
789                         if err := ioutil.WriteFile(filepath.Join(dir, ver+f.Name), f.Data, 0644); err != nil {
790                                 return err
791                         }
792                 } else {
793                         zf, err := z.Create(modPath + "@" + ver + "/" + f.Name)
794                         if err != nil {
795                                 return err
796                         }
797                         if _, err := zf.Write(f.Data); err != nil {
798                                 return err
799                         }
800                 }
801         }
802         if err := z.Close(); err != nil {
803                 return err
804         }
805         if err := f.Close(); err != nil {
806                 return err
807         }
808
809         list, err := os.OpenFile(filepath.Join(dir, "list"), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
810         if err != nil {
811                 return err
812         }
813         if _, err := fmt.Fprintf(list, "%s\n", ver); err != nil {
814                 return err
815         }
816         if err := list.Close(); err != nil {
817                 return err
818         }
819         return nil
820 }
821
822 func removeDir(dir string) {
823         _ = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
824                 if err != nil {
825                         return nil
826                 }
827                 if info.IsDir() {
828                         _ = os.Chmod(path, 0777)
829                 }
830                 return nil
831         })
832         _ = os.RemoveAll(dir) // ignore errors
833 }
834
835 // Tests that findModFile can find the mod files from a path in the module cache.
836 func TestFindModFileModCache(t *testing.T) {
837         mt := setup(t, `
838 -- go.mod --
839 module x
840
841 require rsc.io/quote v1.5.2
842 -- x.go --
843 package x
844 import _ "rsc.io/quote"
845 `, "")
846         defer mt.cleanup()
847         want := filepath.Join(mt.gopath, "pkg/mod", "rsc.io/quote@v1.5.2")
848
849         found := mt.assertScanFinds("rsc.io/quote", "quote")
850         modDir, _ := mt.resolver.modInfo(found.dir)
851         if modDir != want {
852                 t.Errorf("expected: %s, got: %s", want, modDir)
853         }
854 }
855
856 // Tests that crud in the module cache is ignored.
857 func TestInvalidModCache(t *testing.T) {
858         testenv.NeedsGo1Point(t, 11)
859         dir, err := ioutil.TempDir("", t.Name())
860         if err != nil {
861                 t.Fatal(err)
862         }
863         defer removeDir(dir)
864
865         // This doesn't have module@version like it should.
866         if err := os.MkdirAll(filepath.Join(dir, "gopath/pkg/mod/sabotage"), 0777); err != nil {
867                 t.Fatal(err)
868         }
869         if err := ioutil.WriteFile(filepath.Join(dir, "gopath/pkg/mod/sabotage/x.go"), []byte("package foo\n"), 0777); err != nil {
870                 t.Fatal(err)
871         }
872         env := &ProcessEnv{
873                 Env: map[string]string{
874                         "GOPATH":      filepath.Join(dir, "gopath"),
875                         "GO111MODULE": "on",
876                         "GOSUMDB":     "off",
877                 },
878                 GocmdRunner: &gocommand.Runner{},
879                 WorkingDir:  dir,
880         }
881         resolver, err := env.GetResolver()
882         if err != nil {
883                 t.Fatal(err)
884         }
885         scanToSlice(resolver, nil)
886 }
887
888 func TestGetCandidatesRanking(t *testing.T) {
889         mt := setup(t, `
890 -- go.mod --
891 module example.com
892
893 require rsc.io/quote v1.5.1
894 require rsc.io/quote/v3 v3.0.0
895
896 -- rpackage/x.go --
897 package rpackage
898 import (
899         _ "rsc.io/quote"
900         _ "rsc.io/quote/v3"
901 )
902 `, "")
903         defer mt.cleanup()
904
905         if _, err := mt.env.invokeGo(context.Background(), "mod", "download", "rsc.io/quote/v2@v2.0.1"); err != nil {
906                 t.Fatal(err)
907         }
908
909         type res struct {
910                 relevance  float64
911                 name, path string
912         }
913         want := []res{
914                 // Stdlib
915                 {7, "bytes", "bytes"},
916                 {7, "http", "net/http"},
917                 // Main module
918                 {6, "rpackage", "example.com/rpackage"},
919                 // Direct module deps with v2+ major version
920                 {5.003, "quote", "rsc.io/quote/v3"},
921                 // Direct module deps
922                 {5, "quote", "rsc.io/quote"},
923                 // Indirect deps
924                 {4, "language", "golang.org/x/text/language"},
925                 // Out of scope modules
926                 {3, "quote", "rsc.io/quote/v2"},
927         }
928         var mu sync.Mutex
929         var got []res
930         add := func(c ImportFix) {
931                 mu.Lock()
932                 defer mu.Unlock()
933                 for _, w := range want {
934                         if c.StmtInfo.ImportPath == w.path {
935                                 got = append(got, res{c.Relevance, c.IdentName, c.StmtInfo.ImportPath})
936                         }
937                 }
938         }
939         if err := GetAllCandidates(context.Background(), add, "", "foo.go", "foo", mt.env); err != nil {
940                 t.Fatalf("getAllCandidates() = %v", err)
941         }
942         sort.Slice(got, func(i, j int) bool {
943                 ri, rj := got[i], got[j]
944                 if ri.relevance != rj.relevance {
945                         return ri.relevance > rj.relevance // Highest first.
946                 }
947                 return ri.name < rj.name
948         })
949         if !reflect.DeepEqual(want, got) {
950                 t.Errorf("wanted candidates in order %v, got %v", want, got)
951         }
952 }
953
954 func BenchmarkScanModCache(b *testing.B) {
955         testenv.NeedsGo1Point(b, 11)
956         env := &ProcessEnv{
957                 GocmdRunner: &gocommand.Runner{},
958                 Logf:        log.Printf,
959         }
960         exclude := []gopathwalk.RootType{gopathwalk.RootGOROOT}
961         resolver, err := env.GetResolver()
962         if err != nil {
963                 b.Fatal(err)
964         }
965         scanToSlice(resolver, exclude)
966         b.ResetTimer()
967         for i := 0; i < b.N; i++ {
968                 scanToSlice(resolver, exclude)
969                 resolver.(*ModuleResolver).ClearForNewScan()
970         }
971 }