GoPLS Viewer

Home|gopls/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
5package imports
6
7import (
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.
31func TestScanStdlib(t *testing.T) {
32    mt := setup(tnil`
33-- go.mod --
34module 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.
44func TestScanOutOfScopeNestedModule(t *testing.T) {
45    mt := setup(tnil`
46-- go.mod --
47module x
48
49-- x.go --
50package x
51
52-- v2/go.mod --
53module x
54
55-- v2/x.go --
56package 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.
70func TestScanNestedModuleInLocalReplace(t *testing.T) {
71    mt := setup(tnil`
72-- go.mod --
73module x
74
75require y v0.0.0
76replace y => ./y
77
78-- x.go --
79package x
80
81-- y/go.mod --
82module y
83
84-- y/y.go --
85package y
86
87-- y/z/go.mod --
88module y/z
89
90-- y/z/z.go --
91package z
92`"")
93    defer mt.cleanup()
94
95    mt.assertFound("y""y")
96
97    scanerr := scanToSlice(mt.resolvernil)
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.
109func TestModCase(t *testing.T) {
110    mt := setup(tnil`
111-- go.mod --
112module x
113
114require rsc.io/QUOTE v1.5.2
115
116-- x.go --
117package x
118
119import _ "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.
126func TestModDomainRoot(t *testing.T) {
127    mt := setup(tnil`
128-- go.mod --
129module x
130
131require example.com v1.0.0
132
133-- x.go --
134package x
135import _ "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.
142func TestModMultipleScans(t *testing.T) {
143    mt := setup(tnil`
144-- go.mod --
145module x
146
147require example.com v1.0.0
148
149-- x.go --
150package x
151import _ "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.
161func TestModMultipleScansWithSubdirs(t *testing.T) {
162    mt := setup(tnil`
163-- go.mod --
164module x
165
166require rsc.io/quote v1.5.2
167
168-- x.go --
169package x
170import _ "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.
180func TestModCacheEditModFile(t *testing.T) {
181    mt := setup(tnil`
182-- go.mod --
183module x
184
185require rsc.io/quote v1.5.2
186-- x.go --
187package x
188import _ "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.
221func TestModVendorBuild(t *testing.T) {
222    mt := setup(tnil`
223-- go.mod --
224module m
225go 1.12
226require rsc.io/sampler v1.3.1
227-- x.go --
228package x
229import _ "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.
252func TestModVendorAuto(t *testing.T) {
253    mt := setup(tnil`
254-- go.mod --
255module m
256go 1.14
257require rsc.io/sampler v1.3.1
258-- x.go --
259package x
260import _ "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.
278func TestModList(t *testing.T) {
279    mt := setup(tnil`
280-- go.mod --
281module x
282require rsc.io/quote v1.5.1
283replace rsc.io/sampler v1.3.0 => rsc.io/sampler v1.3.1
284
285-- x.go --
286package x
287import _ "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.
295func TestModLocalReplace(t *testing.T) {
296    mt := setup(tnil`
297-- x/y/go.mod --
298module x/y
299require zz v1.0.0
300replace zz v1.0.0 => ../z
301
302-- x/y/y.go --
303package y
304import _ "zz"
305
306-- x/z/go.mod --
307module x/z
308
309-- x/z/z.go --
310package 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.
319func TestModMultirepo1(t *testing.T) {
320    mt := setup(tnil`
321-- go.mod --
322module rsc.io/quote
323
324-- x.go --
325package 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.)
335func TestModMultirepo3(t *testing.T) {
336    mt := setup(tnil`
337-- go.mod --
338module rsc.io/quote
339
340require rsc.io/quote/v2 v2.0.1
341-- x.go --
342package quote
343
344import _ "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.
354func TestModMultirepo4(t *testing.T) {
355    mt := setup(tnil`
356-- go.mod --
357module rsc.io/quote
358require rsc.io/quote/v2 v2.0.1
359
360-- x.go --
361package quote
362import _ "rsc.io/quote/v2"
363
364-- v2/go.mod --
365package rsc.io/quote/v2
366
367-- v2/x.go --
368package quote
369import _ "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.
378func TestModReplace1(t *testing.T) {
379    mt := setup(tnil`
380-- go.mod --
381module quoter
382
383require rsc.io/quote/v3 v3.0.0
384
385-- main.go --
386
387package 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.
394func TestModReplace2(t *testing.T) {
395    mt := setup(tnil`
396-- go.mod --
397module quoter
398
399require rsc.io/quote/v3 v3.0.0
400replace rsc.io/quote/v3 => ./local/rsc.io/quote/v3
401-- main.go --
402package main
403
404-- local/rsc.io/quote/v3/go.mod --
405module rsc.io/quote/v3
406
407require rsc.io/sampler v1.3.0
408
409-- local/rsc.io/quote/v3/quote.go --
410package quote
411
412import "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.
420func TestModReplace3(t *testing.T) {
421    mt := setup(tnil`
422-- go.mod --
423module quoter
424
425require not-rsc.io/quote/v3 v3.1.0
426replace not-rsc.io/quote/v3 v3.1.0 => ./local/rsc.io/quote/v3
427
428-- usenewmodule/main.go --
429package main
430
431-- local/rsc.io/quote/v3/go.mod --
432module rsc.io/quote/v3
433
434require rsc.io/sampler v1.3.0
435
436-- local/rsc.io/quote/v3/quote.go --
437package quote
438
439-- local/not-rsc.io/quote/v3/go.mod --
440module not-rsc.io/quote/v3
441
442-- local/not-rsc.io/quote/v3/quote.go --
443package 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.
453func TestModReplaceImport(t *testing.T) {
454    mt := setup(tnil`
455-- go.mod --
456module example.com/m
457
458replace (
459    example.com/a => ./a
460    example.com/a/b => ./b
461)
462
463replace (
464    example.com/x => ./x
465    example.com/x/v3 => ./v3
466)
467
468replace (
469    example.com/y/z/w => ./w
470    example.com/y => ./y
471)
472
473replace (
474    example.com/vv v1.11.0 => ./v11
475    example.com/vv v1.12.0 => ./v12
476    example.com/vv => ./vv
477)
478
479require (
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 --
487package main
488import (
489    _ "example.com/a/b"
490    _ "example.com/x/v3"
491    _ "example.com/y/z/w"
492    _ "example.com/vv"
493)
494func main() {}
495
496-- a/go.mod --
497module a.localhost
498-- a/a.go --
499package a
500-- a/b/b.go--
501package b
502
503-- b/go.mod --
504module a.localhost/b
505-- b/b.go --
506package b
507
508-- x/go.mod --
509module x.localhost
510-- x/x.go --
511package x
512-- x/v3.go --
513package v3
514import _ "x.localhost/v3"
515
516-- v3/go.mod --
517module x.localhost/v3
518-- v3/x.go --
519package x
520
521-- w/go.mod --
522module w.localhost
523-- w/skip/skip.go --
524// Package skip is nested below nonexistent package w.
525package skip
526
527-- y/go.mod --
528module y.localhost
529-- y/z/w/w.go --
530package w
531
532-- v12/go.mod --
533module v.localhost
534-- v12/v.go --
535package v
536
537-- v11/go.mod --
538module v.localhost
539-- v11/v.go --
540package v
541
542-- vv/go.mod --
543module v.localhost
544-- vv/v.go --
545package 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 go.work files are respected.
556func TestModWorkspace(t *testing.T) {
557    testenv.NeedsGo1Point(t18)
558
559    mt := setup(tnil`
560-- go.work --
561go 1.18
562
563use (
564    ./a
565    ./b
566)
567-- a/go.mod --
568module example.com/a
569
570go 1.18
571-- a/a.go --
572package a
573-- b/go.mod --
574module example.com/b
575
576go 1.18
577-- b/b.go --
578package b
579`"")
580    defer mt.cleanup()
581
582    mt.assertModuleFoundInDir("example.com/a""a"`main/a$`)
583    mt.assertModuleFoundInDir("example.com/b""b"`main/b$`)
584    mt.assertScanFinds("example.com/a""a")
585    mt.assertScanFinds("example.com/b""b")
586}
587
588// Tests replaces in workspaces. Uses the directory layout in the cmd/go
589// work_replace test. It tests both that replaces in go.work files are
590// respected and that a wildcard replace in go.work overrides a versioned replace
591// in go.mod.
592func TestModWorkspaceReplace(t *testing.T) {
593    testenv.NeedsGo1Point(t18)
594
595    mt := setup(tnil`
596-- go.work --
597use m
598
599replace example.com/dep => ./dep
600replace example.com/other => ./other2
601
602-- m/go.mod --
603module example.com/m
604
605require example.com/dep v1.0.0
606require example.com/other v1.0.0
607
608replace example.com/other v1.0.0 => ./other
609-- m/m.go --
610package m
611
612import "example.com/dep"
613import "example.com/other"
614
615func F() {
616    dep.G()
617    other.H()
618}
619-- dep/go.mod --
620module example.com/dep
621-- dep/dep.go --
622package dep
623
624func G() {
625}
626-- other/go.mod --
627module example.com/other
628-- other/dep.go --
629package other
630
631func G() {
632}
633-- other2/go.mod --
634module example.com/other
635-- other2/dep.go --
636package other2
637
638func G() {
639}
640`"")
641    defer mt.cleanup()
642
643    mt.assertScanFinds("example.com/m""m")
644    mt.assertScanFinds("example.com/dep""dep")
645    mt.assertModuleFoundInDir("example.com/other""other2""main/other2$")
646    mt.assertScanFinds("example.com/other""other2")
647}
648
649// Tests a case where conflicting replaces are overridden by a replace
650// in the go.work file.
651func TestModWorkspaceReplaceOverride(t *testing.T) {
652    testenv.NeedsGo1Point(t18)
653
654    mt := setup(tnil`-- go.work --
655use m
656use n
657replace example.com/dep => ./dep3
658-- m/go.mod --
659module example.com/m
660
661require example.com/dep v1.0.0
662replace example.com/dep => ./dep1
663-- m/m.go --
664package m
665
666import "example.com/dep"
667
668func F() {
669    dep.G()
670}
671-- n/go.mod --
672module example.com/n
673
674require example.com/dep v1.0.0
675replace example.com/dep => ./dep2
676-- n/n.go --
677package n
678
679import "example.com/dep"
680
681func F() {
682    dep.G()
683}
684-- dep1/go.mod --
685module example.com/dep
686-- dep1/dep.go --
687package dep
688
689func G() {
690}
691-- dep2/go.mod --
692module example.com/dep
693-- dep2/dep.go --
694package dep
695
696func G() {
697}
698-- dep3/go.mod --
699module example.com/dep
700-- dep3/dep.go --
701package dep
702
703func G() {
704}
705`"")
706
707    mt.assertScanFinds("example.com/m""m")
708    mt.assertScanFinds("example.com/n""n")
709    mt.assertScanFinds("example.com/dep""dep")
710    mt.assertModuleFoundInDir("example.com/dep""dep""main/dep3$")
711}
712
713// Tests that the correct versions of modules are found in
714// workspaces with module pruning. This is based on the
715// cmd/go mod_prune_all script test.
716func TestModWorkspacePrune(t *testing.T) {
717    testenv.NeedsGo1Point(t18)
718
719    mt := setup(tnil`
720-- go.work --
721go 1.18
722
723use (
724    ./a
725    ./p
726)
727
728replace example.com/b v1.0.0 => ./b
729replace example.com/q v1.0.0 => ./q1_0_0
730replace example.com/q v1.0.5 => ./q1_0_5
731replace example.com/q v1.1.0 => ./q1_1_0
732replace example.com/r v1.0.0 => ./r
733replace example.com/w v1.0.0 => ./w
734replace example.com/x v1.0.0 => ./x
735replace example.com/y v1.0.0 => ./y
736replace example.com/z v1.0.0 => ./z1_0_0
737replace example.com/z v1.1.0 => ./z1_1_0
738
739-- a/go.mod --
740module example.com/a
741
742go 1.18
743
744require example.com/b v1.0.0
745require example.com/z v1.0.0
746-- a/foo.go --
747package main
748
749import "example.com/b"
750
751func main() {
752    b.B()
753}
754-- b/go.mod --
755module example.com/b
756
757go 1.18
758
759require example.com/q v1.1.0
760-- b/b.go --
761package b
762
763func B() {
764}
765-- p/go.mod --
766module example.com/p
767
768go 1.18
769
770require example.com/q v1.0.0
771
772replace example.com/q v1.0.0 => ../q1_0_0
773replace example.com/q v1.1.0 => ../q1_1_0
774-- p/main.go --
775package main
776
777import "example.com/q"
778
779func main() {
780    q.PrintVersion()
781}
782-- q1_0_0/go.mod --
783module example.com/q
784
785go 1.18
786-- q1_0_0/q.go --
787package q
788
789import "fmt"
790
791func PrintVersion() {
792    fmt.Println("version 1.0.0")
793}
794-- q1_0_5/go.mod --
795module example.com/q
796
797go 1.18
798
799require example.com/r v1.0.0
800-- q1_0_5/q.go --
801package q
802
803import _ "example.com/r"
804-- q1_1_0/go.mod --
805module example.com/q
806
807require example.com/w v1.0.0
808require example.com/z v1.1.0
809
810go 1.18
811-- q1_1_0/q.go --
812package q
813
814import _ "example.com/w"
815import _ "example.com/z"
816
817import "fmt"
818
819func PrintVersion() {
820    fmt.Println("version 1.1.0")
821}
822-- r/go.mod --
823module example.com/r
824
825go 1.18
826
827require example.com/r v1.0.0
828-- r/r.go --
829package r
830-- w/go.mod --
831module example.com/w
832
833go 1.18
834
835require example.com/x v1.0.0
836-- w/w.go --
837package w
838-- w/w_test.go --
839package w
840
841import _ "example.com/x"
842-- x/go.mod --
843module example.com/x
844
845go 1.18
846-- x/x.go --
847package x
848-- x/x_test.go --
849package x
850import _ "example.com/y"
851-- y/go.mod --
852module example.com/y
853
854go 1.18
855-- y/y.go --
856package y
857-- z1_0_0/go.mod --
858module example.com/z
859
860go 1.18
861
862require example.com/q v1.0.5
863-- z1_0_0/z.go --
864package z
865
866import _ "example.com/q"
867-- z1_1_0/go.mod --
868module example.com/z
869
870go 1.18
871-- z1_1_0/z.go --
872package z
873`"")
874
875    mt.assertScanFinds("example.com/w""w")
876    mt.assertScanFinds("example.com/q""q")
877    mt.assertScanFinds("example.com/x""x")
878    mt.assertScanFinds("example.com/z""z")
879    mt.assertModuleFoundInDir("example.com/w""w""main/w$")
880    mt.assertModuleFoundInDir("example.com/q""q""main/q1_1_0$")
881    mt.assertModuleFoundInDir("example.com/x""x""main/x$")
882    mt.assertModuleFoundInDir("example.com/z""z""main/z1_1_0$")
883}
884
885// Tests that we handle GO111MODULE=on with no go.mod file. See #30855.
886func TestNoMainModule(t *testing.T) {
887    mt := setup(t, map[string]string{"GO111MODULE""on"}, `
888-- x.go --
889package x
890`"")
891    defer mt.cleanup()
892    if _err := mt.env.invokeGo(context.Background(), "mod""download""rsc.io/quote@v1.5.1"); err != nil {
893        t.Fatal(err)
894    }
895
896    mt.assertScanFinds("rsc.io/quote""quote")
897}
898
899// assertFound asserts that the package at importPath is found to have pkgName,
900// and that scanning for pkgName finds it at importPath.
901func (t *modTestassertFound(importPathpkgName string) (string, *pkg) {
902    t.Helper()
903
904    nameserr := t.resolver.loadPackageNames([]string{importPath}, t.env.WorkingDir)
905    if err != nil {
906        t.Errorf("loading package name for %v: %v"importPatherr)
907    }
908    if names[importPath] != pkgName {
909        t.Errorf("package name for %v = %v, want %v"importPathnames[importPath], pkgName)
910    }
911    pkg := t.assertScanFinds(importPathpkgName)
912
913    _foundDir := t.resolver.findPackage(importPath)
914    return foundDirpkg
915}
916
917func (t *modTestassertScanFinds(importPathpkgName string) *pkg {
918    t.Helper()
919    scanerr := scanToSlice(t.resolvernil)
920    if err != nil {
921        t.Errorf("scan failed: %v"err)
922    }
923    for _pkg := range scan {
924        if pkg.importPathShort == importPath {
925            return pkg
926        }
927    }
928    t.Errorf("scanning for %v did not find %v"pkgNameimportPath)
929    return nil
930}
931
932func scanToSlice(resolver Resolverexclude []gopathwalk.RootType) ([]*pkgerror) {
933    var mu sync.Mutex
934    var result []*pkg
935    filter := &scanCallback{
936        rootFound: func(root gopathwalk.Rootbool {
937            for _rt := range exclude {
938                if root.Type == rt {
939                    return false
940                }
941            }
942            return true
943        },
944        dirFound: func(pkg *pkgbool {
945            return true
946        },
947        packageNameLoaded: func(pkg *pkgbool {
948            mu.Lock()
949            defer mu.Unlock()
950            result = append(resultpkg)
951            return false
952        },
953    }
954    err := resolver.scan(context.Background(), filter)
955    return resulterr
956}
957
958// assertModuleFoundInDir is the same as assertFound, but also checks that the
959// package was found in an active module whose Dir matches dirRE.
960func (t *modTestassertModuleFoundInDir(importPathpkgNamedirRE string) {
961    t.Helper()
962    dirpkg := t.assertFound(importPathpkgName)
963    reerr := regexp.Compile(dirRE)
964    if err != nil {
965        t.Fatal(err)
966    }
967
968    if dir == "" {
969        t.Errorf("import path %v not found in active modules"importPath)
970    } else {
971        if !re.MatchString(filepath.ToSlash(dir)) {
972            t.Errorf("finding dir for %s: dir = %q did not match regex %q"importPathdirdirRE)
973        }
974    }
975    if pkg != nil {
976        if !re.MatchString(filepath.ToSlash(pkg.dir)) {
977            t.Errorf("scanning for %s: dir = %q did not match regex %q"pkgNamepkg.dirdirRE)
978        }
979    }
980}
981
982var proxyOnce sync.Once
983var proxyDir string
984
985type modTest struct {
986    *testing.T
987    env      *ProcessEnv
988    gopath   string
989    resolver *ModuleResolver
990    cleanup  func()
991}
992
993// setup builds a test environment from a txtar and supporting modules
994// in testdata/mod, along the lines of TestScript in cmd/go.
995//
996// extraEnv is applied on top of the default test env.
997func setup(t *testing.TextraEnv map[string]stringmainwd string) *modTest {
998    t.Helper()
999    testenv.NeedsTool(t"go")
1000
1001    proxyOnce.Do(func() {
1002        var err error
1003        proxyDirerr = ioutil.TempDir("""proxy-")
1004        if err != nil {
1005            t.Fatal(err)
1006        }
1007        if err := writeProxy(proxyDir"testdata/mod"); err != nil {
1008            t.Fatal(err)
1009        }
1010    })
1011
1012    direrr := ioutil.TempDir(""t.Name())
1013    if err != nil {
1014        t.Fatal(err)
1015    }
1016
1017    mainDir := filepath.Join(dir"main")
1018    if err := writeModule(mainDirmain); err != nil {
1019        t.Fatal(err)
1020    }
1021
1022    env := &ProcessEnv{
1023        Env: map[string]string{
1024            "GOPATH":      filepath.Join(dir"gopath"),
1025            "GOMODCACHE":  "",
1026            "GO111MODULE""auto",
1027            "GOSUMDB":     "off",
1028            "GOPROXY":     proxydir.ToURL(proxyDir),
1029        },
1030        WorkingDir:  filepath.Join(mainDirwd),
1031        GocmdRunner: &gocommand.Runner{},
1032    }
1033    for kv := range extraEnv {
1034        env.Env[k] = v
1035    }
1036    if *testDebug {
1037        env.Logf = log.Printf
1038    }
1039    // go mod download gets mad if we don't have a go.mod, so make sure we do.
1040    _err = os.Stat(filepath.Join(mainDir"go.mod"))
1041    if err != nil && !os.IsNotExist(err) {
1042        t.Fatalf("checking if go.mod exists: %v"err)
1043    }
1044    if err == nil {
1045        if _err := env.invokeGo(context.Background(), "mod""download""all"); err != nil {
1046            t.Fatal(err)
1047        }
1048    }
1049
1050    resolvererr := env.GetResolver()
1051    if err != nil {
1052        t.Fatal(err)
1053    }
1054    return &modTest{
1055        T:        t,
1056        gopath:   env.Env["GOPATH"],
1057        env:      env,
1058        resolverresolver.(*ModuleResolver),
1059        cleanup:  func() { removeDir(dir) },
1060    }
1061}
1062
1063// writeModule writes the module in the ar, a txtar, to dir.
1064func writeModule(dirar stringerror {
1065    a := txtar.Parse([]byte(ar))
1066
1067    for _f := range a.Files {
1068        fpath := filepath.Join(dirf.Name)
1069        if err := os.MkdirAll(filepath.Dir(fpath), 0755); err != nil {
1070            return err
1071        }
1072
1073        if err := ioutil.WriteFile(fpathf.Data0644); err != nil {
1074            return err
1075        }
1076    }
1077    return nil
1078}
1079
1080// writeProxy writes all the txtar-formatted modules in arDir to a proxy
1081// directory in dir.
1082func writeProxy(dirarDir stringerror {
1083    fileserr := ioutil.ReadDir(arDir)
1084    if err != nil {
1085        return err
1086    }
1087
1088    for _fi := range files {
1089        if err := writeProxyModule(dirfilepath.Join(arDirfi.Name())); err != nil {
1090            return err
1091        }
1092    }
1093    return nil
1094}
1095
1096// writeProxyModule writes a txtar-formatted module at arPath to the module
1097// proxy in base.
1098func writeProxyModule(basearPath stringerror {
1099    arName := filepath.Base(arPath)
1100    i := strings.LastIndex(arName"_v")
1101    ver := strings.TrimSuffix(arName[i+1:], ".txt")
1102    modDir := strings.Replace(arName[:i], "_""/", -1)
1103    modPatherr := module.UnescapePath(modDir)
1104    if err != nil {
1105        return err
1106    }
1107
1108    dir := filepath.Join(basemodDir"@v")
1109    aerr := txtar.ParseFile(arPath)
1110
1111    if err != nil {
1112        return err
1113    }
1114
1115    if err := os.MkdirAll(dir0755); err != nil {
1116        return err
1117    }
1118
1119    ferr := os.OpenFile(filepath.Join(dirver+".zip"), os.O_CREATE|os.O_WRONLY0644)
1120    if err != nil {
1121        return err
1122    }
1123    z := zip.NewWriter(f)
1124    for _f := range a.Files {
1125        if f.Name[0] == '.' {
1126            if err := ioutil.WriteFile(filepath.Join(dirver+f.Name), f.Data0644); err != nil {
1127                return err
1128            }
1129        } else {
1130            zferr := z.Create(modPath + "@" + ver + "/" + f.Name)
1131            if err != nil {
1132                return err
1133            }
1134            if _err := zf.Write(f.Data); err != nil {
1135                return err
1136            }
1137        }
1138    }
1139    if err := z.Close(); err != nil {
1140        return err
1141    }
1142    if err := f.Close(); err != nil {
1143        return err
1144    }
1145
1146    listerr := os.OpenFile(filepath.Join(dir"list"), os.O_CREATE|os.O_APPEND|os.O_WRONLY0644)
1147    if err != nil {
1148        return err
1149    }
1150    if _err := fmt.Fprintf(list"%s\n"ver); err != nil {
1151        return err
1152    }
1153    if err := list.Close(); err != nil {
1154        return err
1155    }
1156    return nil
1157}
1158
1159func removeDir(dir string) {
1160    _ = filepath.Walk(dir, func(path stringinfo os.FileInfoerr errorerror {
1161        if err != nil {
1162            return nil
1163        }
1164        if info.IsDir() {
1165            _ = os.Chmod(path0777)
1166        }
1167        return nil
1168    })
1169    _ = os.RemoveAll(dir// ignore errors
1170}
1171
1172// Tests that findModFile can find the mod files from a path in the module cache.
1173func TestFindModFileModCache(t *testing.T) {
1174    mt := setup(tnil`
1175-- go.mod --
1176module x
1177
1178require rsc.io/quote v1.5.2
1179-- x.go --
1180package x
1181import _ "rsc.io/quote"
1182`"")
1183    defer mt.cleanup()
1184    want := filepath.Join(mt.gopath"pkg/mod""rsc.io/quote@v1.5.2")
1185
1186    found := mt.assertScanFinds("rsc.io/quote""quote")
1187    modDir_ := mt.resolver.modInfo(found.dir)
1188    if modDir != want {
1189        t.Errorf("expected: %s, got: %s"wantmodDir)
1190    }
1191}
1192
1193// Tests that crud in the module cache is ignored.
1194func TestInvalidModCache(t *testing.T) {
1195    direrr := ioutil.TempDir(""t.Name())
1196    if err != nil {
1197        t.Fatal(err)
1198    }
1199    defer removeDir(dir)
1200
1201    // This doesn't have module@version like it should.
1202    if err := os.MkdirAll(filepath.Join(dir"gopath/pkg/mod/sabotage"), 0777); err != nil {
1203        t.Fatal(err)
1204    }
1205    if err := ioutil.WriteFile(filepath.Join(dir"gopath/pkg/mod/sabotage/x.go"), []byte("package foo\n"), 0777); err != nil {
1206        t.Fatal(err)
1207    }
1208    env := &ProcessEnv{
1209        Env: map[string]string{
1210            "GOPATH":      filepath.Join(dir"gopath"),
1211            "GO111MODULE""on",
1212            "GOSUMDB":     "off",
1213        },
1214        GocmdRunner: &gocommand.Runner{},
1215        WorkingDir:  dir,
1216    }
1217    resolvererr := env.GetResolver()
1218    if err != nil {
1219        t.Fatal(err)
1220    }
1221    scanToSlice(resolvernil)
1222}
1223
1224func TestGetCandidatesRanking(t *testing.T) {
1225    mt := setup(tnil`
1226-- go.mod --
1227module example.com
1228
1229require rsc.io/quote v1.5.1
1230require rsc.io/quote/v3 v3.0.0
1231
1232-- rpackage/x.go --
1233package rpackage
1234import (
1235    _ "rsc.io/quote"
1236    _ "rsc.io/quote/v3"
1237)
1238`"")
1239    defer mt.cleanup()
1240
1241    if _err := mt.env.invokeGo(context.Background(), "mod""download""rsc.io/quote/v2@v2.0.1"); err != nil {
1242        t.Fatal(err)
1243    }
1244
1245    type res struct {
1246        relevance  float64
1247        namepath string
1248    }
1249    want := []res{
1250        // Stdlib
1251        {7"bytes""bytes"},
1252        {7"http""net/http"},
1253        // Main module
1254        {6"rpackage""example.com/rpackage"},
1255        // Direct module deps with v2+ major version
1256        {5.003"quote""rsc.io/quote/v3"},
1257        // Direct module deps
1258        {5"quote""rsc.io/quote"},
1259        // Indirect deps
1260        {4"language""golang.org/x/text/language"},
1261        // Out of scope modules
1262        {3"quote""rsc.io/quote/v2"},
1263    }
1264    var mu sync.Mutex
1265    var got []res
1266    add := func(c ImportFix) {
1267        mu.Lock()
1268        defer mu.Unlock()
1269        for _w := range want {
1270            if c.StmtInfo.ImportPath == w.path {
1271                got = append(gotres{c.Relevancec.IdentNamec.StmtInfo.ImportPath})
1272            }
1273        }
1274    }
1275    if err := GetAllCandidates(context.Background(), add"""foo.go""foo"mt.env); err != nil {
1276        t.Fatalf("getAllCandidates() = %v"err)
1277    }
1278    sort.Slice(got, func(ij intbool {
1279        rirj := got[i], got[j]
1280        if ri.relevance != rj.relevance {
1281            return ri.relevance > rj.relevance // Highest first.
1282        }
1283        return ri.name < rj.name
1284    })
1285    if !reflect.DeepEqual(wantgot) {
1286        t.Errorf("wanted candidates in order %v, got %v"wantgot)
1287    }
1288}
1289
1290func BenchmarkScanModCache(b *testing.B) {
1291    env := &ProcessEnv{
1292        GocmdRunner: &gocommand.Runner{},
1293        Logf:        log.Printf,
1294    }
1295    exclude := []gopathwalk.RootType{gopathwalk.RootGOROOT}
1296    resolvererr := env.GetResolver()
1297    if err != nil {
1298        b.Fatal(err)
1299    }
1300    scanToSlice(resolverexclude)
1301    b.ResetTimer()
1302    for i := 0i < b.Ni++ {
1303        scanToSlice(resolverexclude)
1304        resolver.(*ModuleResolver).ClearForNewScan()
1305    }
1306}
1307
MembersX
writeProxy.files
TestFindModFileModCache.found
modTest.assertFound.names
modTest.assertScanFinds.importPath
modTest.assertModuleFoundInDir.dir
BenchmarkScanModCache.err
TestModReplaceImport.mt
TestModVendorBuild.err
modTest.assertFound
TestInvalidModCache.env
TestScanOutOfScopeNestedModule.pkg
modTest.assertScanFinds
writeProxyModule.a
TestGetCandidatesRanking.res.relevance
modTest.assertScanFinds.scan
writeProxyModule.arPath
writeModule
BenchmarkScanModCache
TestModReplace1
TestModLocalReplace
TestModMultirepo1
TestNoMainModule.mt
TestModCase.mt
TestFindModFileModCache.modDir
setup.err
writeModule.dir
writeProxyModule.RangeStmt_23917.BlockStmt.BlockStmt._
TestScanNestedModuleInLocalReplace
modTest.resolver
writeProxy.RangeStmt_23056.fi
TestModVendorAuto.wantDir
TestModWorkspaceReplace
TestNoMainModule.t
TestGetCandidatesRanking.err
TestScanNestedModuleInLocalReplace.mt
TestModMultipleScans.t
TestModWorkspaceReplace.mt
TestModCase.t
TestModVendorAuto.t
TestScanOutOfScopeNestedModule.t
writeProxy.dir
TestScanStdlib.mt
TestModCacheEditModFile.mt
writeProxyModule.list
writeProxyModule.RangeStmt_23917.BlockStmt.BlockStmt.zf
TestInvalidModCache
modTest.assertFound._
modTest.assertFound.foundDir
TestModCacheEditModFile.found
TestModMultipleScans.mt
TestModMultirepo1.t
TestModMultirepo3
writeProxyModule.modPath
TestModMultipleScans
TestFindModFileModCache.mt
TestNoMainModule
TestModVendorBuild
TestScanNestedModuleInLocalReplace.err
TestModDomainRoot.t
TestModLocalReplace.t
modTest.env
setup.RangeStmt_21782.v
zip
scanToSlice.mu
TestModReplace2.t
TestModList
setup.main
writeModule.ar
writeProxyModule.arName
TestFindModFileModCache.t
TestModVendorAuto._
TestInvalidModCache.dir
TestModWorkspacePrune.t
proxyOnce
writeProxyModule.modDir
TestModWorkspaceReplaceOverride
modTest.assertModuleFoundInDir
writeModule.RangeStmt_22597.BlockStmt.fpath
writeProxyModule.dir
modTest.assertFound.err
setup.RangeStmt_21782.k
TestModVendorBuild.mt
TestModList.t
TestNoMainModule.err
setup
TestModMultipleScansWithSubdirs.mt
TestModWorkspaceReplaceOverride.mt
modTest.assertScanFinds.RangeStmt_19114.pkg
modTest
TestInvalidModCache.resolver
TestScanOutOfScopeNestedModule.mt
writeProxy.RangeStmt_23056.BlockStmt.err
TestModReplace3.t
modTest.assertModuleFoundInDir.t
writeModule.a
writeProxyModule.i
writeProxyModule._
TestInvalidModCache.t
writeProxyModule.RangeStmt_23917.BlockStmt.BlockStmt.err
TestModReplace1.t
BenchmarkScanModCache.b
scanToSlice.resolver
TestModMultirepo3.t
modTest.gopath
writeProxyModule.z
TestModCacheEditModFile.err
modTest.assertScanFinds.t
setup.extraEnv
TestModCacheEditModFile.t
TestModMultirepo1.mt
TestModWorkspace.t
scanToSlice.exclude
scanToSlice.Elts.RangeStmt_19483.rt
setup.BlockStmt.err
proxydir
TestModList.mt
TestGetCandidatesRanking.mt
TestGetCandidatesRanking.res.path
TestModVendorAuto
TestModReplace1.mt
modTest.assertModuleFoundInDir.dirRE
TestScanStdlib
TestModCacheEditModFile
TestModReplace3.mt
setup.dir
setup.resolver
TestScanNestedModuleInLocalReplace.t
writeProxyModule.f
TestFindModFileModCache
TestModWorkspaceReplace.t
TestModWorkspacePrune
scanToSlice.filter
modTest.assertModuleFoundInDir.pkg
writeProxy
writeProxyModule
TestModCase
TestModMultipleScansWithSubdirs
TestModReplaceImport.t
setup.t
TestFindModFileModCache.want
TestGetCandidatesRanking.res.name
TestModReplace3
modTest.assertFound.pkgName
writeProxy.err
writeProxyModule.RangeStmt_23917.f
TestGetCandidatesRanking.BlockStmt.RangeStmt_27199.w
modTest.assertModuleFoundInDir.pkgName
TestModMultirepo4.t
TestModLocalReplace.mt
writeModule.RangeStmt_22597.BlockStmt.err
TestGetCandidatesRanking.res
TestScanStdlib.t
TestModWorkspaceReplaceOverride.t
TestScanOutOfScopeNestedModule
TestModDomainRoot
TestGetCandidatesRanking.want
txtar
TestModMultirepo3.mt
TestModReplace2.mt
TestModWorkspace
setup.BlockStmt._
modTest.assertFound.importPath
writeModule.RangeStmt_22597.f
TestGetCandidatesRanking.mu
TestModMultipleScansWithSubdirs.t
TestModMultirepo4.mt
TestModReplace2
modTest.assertFound.pkg
TestGetCandidatesRanking.t
TestModVendorBuild.t
proxyDir
setup.wd
TestGetCandidatesRanking._
scanToSlice.err
writeProxy.arDir
removeDir.dir
modTest.assertFound.t
TestModWorkspacePrune.mt
TestGetCandidatesRanking.got
TestModReplaceImport
writeProxyModule.base
setup.env
modTest.assertModuleFoundInDir.err
TestModVendorBuild._
TestModMultirepo4
modTest.cleanup
removeDir
TestInvalidModCache.err
BenchmarkScanModCache.resolver
TestScanNestedModuleInLocalReplace.RangeStmt_2027.pkg
modTest.assertScanFinds.err
scanToSlice
modTest.assertScanFinds.pkgName
scanToSlice.result
modTest.assertModuleFoundInDir.importPath
TestGetCandidatesRanking
BenchmarkScanModCache.exclude
TestFindModFileModCache._
TestScanNestedModuleInLocalReplace.scan
setup.mainDir
writeProxyModule.err
BenchmarkScanModCache.env
TestModVendorAuto.mt
TestNoMainModule._
writeProxyModule.ver
TestModDomainRoot.mt
TestModWorkspace.mt
modTest.assertModuleFoundInDir.re
BenchmarkScanModCache.i
TestModVendorAuto.err
Members
X