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