Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / internal / lsp / tests / tests.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 tests exports functionality to be used across a variety of gopls tests.
6 package tests
7
8 import (
9         "bytes"
10         "context"
11         "flag"
12         "fmt"
13         "go/ast"
14         "go/token"
15         "io/ioutil"
16         "os"
17         "path/filepath"
18         "regexp"
19         "sort"
20         "strconv"
21         "strings"
22         "sync"
23         "testing"
24         "time"
25
26         "golang.org/x/tools/go/expect"
27         "golang.org/x/tools/go/packages"
28         "golang.org/x/tools/go/packages/packagestest"
29         "golang.org/x/tools/internal/lsp/protocol"
30         "golang.org/x/tools/internal/lsp/source"
31         "golang.org/x/tools/internal/lsp/source/completion"
32         "golang.org/x/tools/internal/span"
33         "golang.org/x/tools/internal/testenv"
34         "golang.org/x/tools/txtar"
35 )
36
37 const (
38         overlayFileSuffix = ".overlay"
39         goldenFileSuffix  = ".golden"
40         inFileSuffix      = ".in"
41         summaryFile       = "summary.txt"
42         testModule        = "golang.org/x/tools/internal/lsp"
43 )
44
45 var UpdateGolden = flag.Bool("golden", false, "Update golden files")
46
47 type CallHierarchy map[span.Span]*CallHierarchyResult
48 type CodeLens map[span.URI][]protocol.CodeLens
49 type Diagnostics map[span.URI][]*source.Diagnostic
50 type CompletionItems map[token.Pos]*completion.CompletionItem
51 type Completions map[span.Span][]Completion
52 type CompletionSnippets map[span.Span][]CompletionSnippet
53 type UnimportedCompletions map[span.Span][]Completion
54 type DeepCompletions map[span.Span][]Completion
55 type FuzzyCompletions map[span.Span][]Completion
56 type CaseSensitiveCompletions map[span.Span][]Completion
57 type RankCompletions map[span.Span][]Completion
58 type FoldingRanges []span.Span
59 type Formats []span.Span
60 type Imports []span.Span
61 type SemanticTokens []span.Span
62 type SuggestedFixes map[span.Span][]string
63 type FunctionExtractions map[span.Span]span.Span
64 type Definitions map[span.Span]Definition
65 type Implementations map[span.Span][]span.Span
66 type Highlights map[span.Span][]span.Span
67 type References map[span.Span][]span.Span
68 type Renames map[span.Span]string
69 type PrepareRenames map[span.Span]*source.PrepareItem
70 type Symbols map[span.URI][]protocol.DocumentSymbol
71 type SymbolsChildren map[string][]protocol.DocumentSymbol
72 type SymbolInformation map[span.Span]protocol.SymbolInformation
73 type WorkspaceSymbols map[string][]protocol.SymbolInformation
74 type Signatures map[span.Span]*protocol.SignatureHelp
75 type Links map[span.URI][]Link
76
77 type Data struct {
78         Config                        packages.Config
79         Exported                      *packagestest.Exported
80         CallHierarchy                 CallHierarchy
81         CodeLens                      CodeLens
82         Diagnostics                   Diagnostics
83         CompletionItems               CompletionItems
84         Completions                   Completions
85         CompletionSnippets            CompletionSnippets
86         UnimportedCompletions         UnimportedCompletions
87         DeepCompletions               DeepCompletions
88         FuzzyCompletions              FuzzyCompletions
89         CaseSensitiveCompletions      CaseSensitiveCompletions
90         RankCompletions               RankCompletions
91         FoldingRanges                 FoldingRanges
92         Formats                       Formats
93         Imports                       Imports
94         SemanticTokens                SemanticTokens
95         SuggestedFixes                SuggestedFixes
96         FunctionExtractions           FunctionExtractions
97         Definitions                   Definitions
98         Implementations               Implementations
99         Highlights                    Highlights
100         References                    References
101         Renames                       Renames
102         PrepareRenames                PrepareRenames
103         Symbols                       Symbols
104         symbolsChildren               SymbolsChildren
105         symbolInformation             SymbolInformation
106         WorkspaceSymbols              WorkspaceSymbols
107         FuzzyWorkspaceSymbols         WorkspaceSymbols
108         CaseSensitiveWorkspaceSymbols WorkspaceSymbols
109         Signatures                    Signatures
110         Links                         Links
111
112         t         testing.TB
113         fragments map[string]string
114         dir       string
115         golden    map[string]*Golden
116         mode      string
117
118         ModfileFlagAvailable bool
119
120         mappersMu sync.Mutex
121         mappers   map[span.URI]*protocol.ColumnMapper
122 }
123
124 type Tests interface {
125         CallHierarchy(*testing.T, span.Span, *CallHierarchyResult)
126         CodeLens(*testing.T, span.URI, []protocol.CodeLens)
127         Diagnostics(*testing.T, span.URI, []*source.Diagnostic)
128         Completion(*testing.T, span.Span, Completion, CompletionItems)
129         CompletionSnippet(*testing.T, span.Span, CompletionSnippet, bool, CompletionItems)
130         UnimportedCompletion(*testing.T, span.Span, Completion, CompletionItems)
131         DeepCompletion(*testing.T, span.Span, Completion, CompletionItems)
132         FuzzyCompletion(*testing.T, span.Span, Completion, CompletionItems)
133         CaseSensitiveCompletion(*testing.T, span.Span, Completion, CompletionItems)
134         RankCompletion(*testing.T, span.Span, Completion, CompletionItems)
135         FoldingRanges(*testing.T, span.Span)
136         Format(*testing.T, span.Span)
137         Import(*testing.T, span.Span)
138         SemanticTokens(*testing.T, span.Span)
139         SuggestedFix(*testing.T, span.Span, []string)
140         FunctionExtraction(*testing.T, span.Span, span.Span)
141         Definition(*testing.T, span.Span, Definition)
142         Implementation(*testing.T, span.Span, []span.Span)
143         Highlight(*testing.T, span.Span, []span.Span)
144         References(*testing.T, span.Span, []span.Span)
145         Rename(*testing.T, span.Span, string)
146         PrepareRename(*testing.T, span.Span, *source.PrepareItem)
147         Symbols(*testing.T, span.URI, []protocol.DocumentSymbol)
148         WorkspaceSymbols(*testing.T, string, []protocol.SymbolInformation, map[string]struct{})
149         FuzzyWorkspaceSymbols(*testing.T, string, []protocol.SymbolInformation, map[string]struct{})
150         CaseSensitiveWorkspaceSymbols(*testing.T, string, []protocol.SymbolInformation, map[string]struct{})
151         SignatureHelp(*testing.T, span.Span, *protocol.SignatureHelp)
152         Link(*testing.T, span.URI, []Link)
153 }
154
155 type Definition struct {
156         Name      string
157         IsType    bool
158         OnlyHover bool
159         Src, Def  span.Span
160 }
161
162 type CompletionTestType int
163
164 const (
165         // Default runs the standard completion tests.
166         CompletionDefault = CompletionTestType(iota)
167
168         // Unimported tests the autocompletion of unimported packages.
169         CompletionUnimported
170
171         // Deep tests deep completion.
172         CompletionDeep
173
174         // Fuzzy tests deep completion and fuzzy matching.
175         CompletionFuzzy
176
177         // CaseSensitive tests case sensitive completion.
178         CompletionCaseSensitive
179
180         // CompletionRank candidates in test must be valid and in the right relative order.
181         CompletionRank
182 )
183
184 type WorkspaceSymbolsTestType int
185
186 const (
187         // Default runs the standard workspace symbols tests.
188         WorkspaceSymbolsDefault = WorkspaceSymbolsTestType(iota)
189
190         // Fuzzy tests workspace symbols with fuzzy matching.
191         WorkspaceSymbolsFuzzy
192
193         // CaseSensitive tests workspace symbols with case sensitive.
194         WorkspaceSymbolsCaseSensitive
195 )
196
197 type Completion struct {
198         CompletionItems []token.Pos
199 }
200
201 type CompletionSnippet struct {
202         CompletionItem     token.Pos
203         PlainSnippet       string
204         PlaceholderSnippet string
205 }
206
207 type CallHierarchyResult struct {
208         IncomingCalls, OutgoingCalls []protocol.CallHierarchyItem
209 }
210
211 type Link struct {
212         Src          span.Span
213         Target       string
214         NotePosition token.Position
215 }
216
217 type Golden struct {
218         Filename string
219         Archive  *txtar.Archive
220         Modified bool
221 }
222
223 func Context(t testing.TB) context.Context {
224         return context.Background()
225 }
226
227 func DefaultOptions(o *source.Options) {
228         o.SupportedCodeActions = map[source.FileKind]map[protocol.CodeActionKind]bool{
229                 source.Go: {
230                         protocol.SourceOrganizeImports: true,
231                         protocol.QuickFix:              true,
232                         protocol.RefactorRewrite:       true,
233                         protocol.RefactorExtract:       true,
234                         protocol.SourceFixAll:          true,
235                 },
236                 source.Mod: {
237                         protocol.SourceOrganizeImports: true,
238                 },
239                 source.Sum: {},
240         }
241         o.ExperimentalOptions.Codelens[source.CommandTest.Name] = true
242         o.HoverKind = source.SynopsisDocumentation
243         o.InsertTextFormat = protocol.SnippetTextFormat
244         o.CompletionBudget = time.Minute
245         o.HierarchicalDocumentSymbolSupport = true
246         o.ExperimentalWorkspaceModule = true
247         o.SemanticTokens = true
248 }
249
250 func RunTests(t *testing.T, dataDir string, includeMultiModule bool, f func(*testing.T, *Data)) {
251         t.Helper()
252         modes := []string{"Modules", "GOPATH"}
253         if includeMultiModule {
254                 modes = append(modes, "MultiModule")
255         }
256         for _, mode := range modes {
257                 t.Run(mode, func(t *testing.T) {
258                         t.Helper()
259                         if mode == "MultiModule" {
260                                 // Some bug in 1.12 breaks reading markers, and it's not worth figuring out.
261                                 testenv.NeedsGo1Point(t, 13)
262                         }
263                         datum := load(t, mode, dataDir)
264                         f(t, datum)
265                 })
266         }
267 }
268
269 func load(t testing.TB, mode string, dir string) *Data {
270         t.Helper()
271
272         datum := &Data{
273                 CallHierarchy:                 make(CallHierarchy),
274                 CodeLens:                      make(CodeLens),
275                 Diagnostics:                   make(Diagnostics),
276                 CompletionItems:               make(CompletionItems),
277                 Completions:                   make(Completions),
278                 CompletionSnippets:            make(CompletionSnippets),
279                 UnimportedCompletions:         make(UnimportedCompletions),
280                 DeepCompletions:               make(DeepCompletions),
281                 FuzzyCompletions:              make(FuzzyCompletions),
282                 RankCompletions:               make(RankCompletions),
283                 CaseSensitiveCompletions:      make(CaseSensitiveCompletions),
284                 Definitions:                   make(Definitions),
285                 Implementations:               make(Implementations),
286                 Highlights:                    make(Highlights),
287                 References:                    make(References),
288                 Renames:                       make(Renames),
289                 PrepareRenames:                make(PrepareRenames),
290                 SuggestedFixes:                make(SuggestedFixes),
291                 FunctionExtractions:           make(FunctionExtractions),
292                 Symbols:                       make(Symbols),
293                 symbolsChildren:               make(SymbolsChildren),
294                 symbolInformation:             make(SymbolInformation),
295                 WorkspaceSymbols:              make(WorkspaceSymbols),
296                 FuzzyWorkspaceSymbols:         make(WorkspaceSymbols),
297                 CaseSensitiveWorkspaceSymbols: make(WorkspaceSymbols),
298                 Signatures:                    make(Signatures),
299                 Links:                         make(Links),
300
301                 t:         t,
302                 dir:       dir,
303                 fragments: map[string]string{},
304                 golden:    map[string]*Golden{},
305                 mode:      mode,
306                 mappers:   map[span.URI]*protocol.ColumnMapper{},
307         }
308
309         if !*UpdateGolden {
310                 summary := filepath.Join(filepath.FromSlash(dir), summaryFile+goldenFileSuffix)
311                 if _, err := os.Stat(summary); os.IsNotExist(err) {
312                         t.Fatalf("could not find golden file summary.txt in %#v", dir)
313                 }
314                 archive, err := txtar.ParseFile(summary)
315                 if err != nil {
316                         t.Fatalf("could not read golden file %v/%v: %v", dir, summary, err)
317                 }
318                 datum.golden[summaryFile] = &Golden{
319                         Filename: summary,
320                         Archive:  archive,
321                 }
322         }
323
324         files := packagestest.MustCopyFileTree(dir)
325         overlays := map[string][]byte{}
326         for fragment, operation := range files {
327                 if trimmed := strings.TrimSuffix(fragment, goldenFileSuffix); trimmed != fragment {
328                         delete(files, fragment)
329                         goldFile := filepath.Join(dir, fragment)
330                         archive, err := txtar.ParseFile(goldFile)
331                         if err != nil {
332                                 t.Fatalf("could not read golden file %v: %v", fragment, err)
333                         }
334                         datum.golden[trimmed] = &Golden{
335                                 Filename: goldFile,
336                                 Archive:  archive,
337                         }
338                 } else if trimmed := strings.TrimSuffix(fragment, inFileSuffix); trimmed != fragment {
339                         delete(files, fragment)
340                         files[trimmed] = operation
341                 } else if index := strings.Index(fragment, overlayFileSuffix); index >= 0 {
342                         delete(files, fragment)
343                         partial := fragment[:index] + fragment[index+len(overlayFileSuffix):]
344                         contents, err := ioutil.ReadFile(filepath.Join(dir, fragment))
345                         if err != nil {
346                                 t.Fatal(err)
347                         }
348                         overlays[partial] = contents
349                 }
350         }
351
352         modules := []packagestest.Module{
353                 {
354                         Name:    testModule,
355                         Files:   files,
356                         Overlay: overlays,
357                 },
358         }
359         switch mode {
360         case "Modules":
361                 datum.Exported = packagestest.Export(t, packagestest.Modules, modules)
362         case "GOPATH":
363                 datum.Exported = packagestest.Export(t, packagestest.GOPATH, modules)
364         case "MultiModule":
365                 files := map[string]interface{}{}
366                 for k, v := range modules[0].Files {
367                         files[filepath.Join("testmodule", k)] = v
368                 }
369                 modules[0].Files = files
370
371                 overlays := map[string][]byte{}
372                 for k, v := range modules[0].Overlay {
373                         overlays[filepath.Join("testmodule", k)] = v
374                 }
375                 modules[0].Overlay = overlays
376
377                 golden := map[string]*Golden{}
378                 for k, v := range datum.golden {
379                         if k == summaryFile {
380                                 golden[k] = v
381                         } else {
382                                 golden[filepath.Join("testmodule", k)] = v
383                         }
384                 }
385                 datum.golden = golden
386
387                 datum.Exported = packagestest.Export(t, packagestest.Modules, modules)
388         default:
389                 panic("unknown mode " + mode)
390         }
391
392         for _, m := range modules {
393                 for fragment := range m.Files {
394                         filename := datum.Exported.File(m.Name, fragment)
395                         datum.fragments[filename] = fragment
396                 }
397         }
398
399         // Turn off go/packages debug logging.
400         datum.Exported.Config.Logf = nil
401         datum.Config.Logf = nil
402
403         // Merge the exported.Config with the view.Config.
404         datum.Config = *datum.Exported.Config
405         datum.Config.Fset = token.NewFileSet()
406         datum.Config.Context = Context(nil)
407         datum.Config.ParseFile = func(fset *token.FileSet, filename string, src []byte) (*ast.File, error) {
408                 panic("ParseFile should not be called")
409         }
410
411         // Do a first pass to collect special markers for completion and workspace symbols.
412         if err := datum.Exported.Expect(map[string]interface{}{
413                 "item": func(name string, r packagestest.Range, _ []string) {
414                         datum.Exported.Mark(name, r)
415                 },
416                 "symbol": func(name string, r packagestest.Range, _ []string) {
417                         datum.Exported.Mark(name, r)
418                 },
419         }); err != nil {
420                 t.Fatal(err)
421         }
422
423         // Collect any data that needs to be used by subsequent tests.
424         if err := datum.Exported.Expect(map[string]interface{}{
425                 "codelens":        datum.collectCodeLens,
426                 "diag":            datum.collectDiagnostics,
427                 "item":            datum.collectCompletionItems,
428                 "complete":        datum.collectCompletions(CompletionDefault),
429                 "unimported":      datum.collectCompletions(CompletionUnimported),
430                 "deep":            datum.collectCompletions(CompletionDeep),
431                 "fuzzy":           datum.collectCompletions(CompletionFuzzy),
432                 "casesensitive":   datum.collectCompletions(CompletionCaseSensitive),
433                 "rank":            datum.collectCompletions(CompletionRank),
434                 "snippet":         datum.collectCompletionSnippets,
435                 "fold":            datum.collectFoldingRanges,
436                 "format":          datum.collectFormats,
437                 "import":          datum.collectImports,
438                 "semantic":        datum.collectSemanticTokens,
439                 "godef":           datum.collectDefinitions,
440                 "implementations": datum.collectImplementations,
441                 "typdef":          datum.collectTypeDefinitions,
442                 "hover":           datum.collectHoverDefinitions,
443                 "highlight":       datum.collectHighlights,
444                 "refs":            datum.collectReferences,
445                 "rename":          datum.collectRenames,
446                 "prepare":         datum.collectPrepareRenames,
447                 "symbol":          datum.collectSymbols,
448                 "signature":       datum.collectSignatures,
449                 "link":            datum.collectLinks,
450                 "suggestedfix":    datum.collectSuggestedFixes,
451                 "extractfunc":     datum.collectFunctionExtractions,
452                 "incomingcalls":   datum.collectIncomingCalls,
453                 "outgoingcalls":   datum.collectOutgoingCalls,
454         }); err != nil {
455                 t.Fatal(err)
456         }
457         for _, symbols := range datum.Symbols {
458                 for i := range symbols {
459                         children := datum.symbolsChildren[symbols[i].Name]
460                         symbols[i].Children = children
461                 }
462         }
463         // Collect names for the entries that require golden files.
464         if err := datum.Exported.Expect(map[string]interface{}{
465                 "godef":                        datum.collectDefinitionNames,
466                 "hover":                        datum.collectDefinitionNames,
467                 "workspacesymbol":              datum.collectWorkspaceSymbols(WorkspaceSymbolsDefault),
468                 "workspacesymbolfuzzy":         datum.collectWorkspaceSymbols(WorkspaceSymbolsFuzzy),
469                 "workspacesymbolcasesensitive": datum.collectWorkspaceSymbols(WorkspaceSymbolsCaseSensitive),
470         }); err != nil {
471                 t.Fatal(err)
472         }
473         if mode == "MultiModule" {
474                 if err := os.Rename(filepath.Join(datum.Config.Dir, "go.mod"), filepath.Join(datum.Config.Dir, "testmodule/go.mod")); err != nil {
475                         t.Fatal(err)
476                 }
477         }
478
479         return datum
480 }
481
482 func Run(t *testing.T, tests Tests, data *Data) {
483         t.Helper()
484         checkData(t, data)
485
486         eachCompletion := func(t *testing.T, cases map[span.Span][]Completion, test func(*testing.T, span.Span, Completion, CompletionItems)) {
487                 t.Helper()
488
489                 for src, exp := range cases {
490                         for i, e := range exp {
491                                 t.Run(SpanName(src)+"_"+strconv.Itoa(i), func(t *testing.T) {
492                                         t.Helper()
493                                         if strings.Contains(t.Name(), "complit") || strings.Contains(t.Name(), "UnimportedCompletion") {
494                                                 if data.mode == "MultiModule" {
495                                                         t.Skip("Unimported completions are broken in multi-module mode")
496                                                 }
497                                         }
498                                         if strings.Contains(t.Name(), "cgo") {
499                                                 testenv.NeedsTool(t, "cgo")
500                                         }
501                                         if strings.Contains(t.Name(), "declarecgo") {
502                                                 testenv.NeedsGo1Point(t, 15)
503                                         }
504                                         test(t, src, e, data.CompletionItems)
505                                 })
506                         }
507
508                 }
509         }
510
511         eachWorkspaceSymbols := func(t *testing.T, cases map[string][]protocol.SymbolInformation, test func(*testing.T, string, []protocol.SymbolInformation, map[string]struct{})) {
512                 t.Helper()
513
514                 for query, expectedSymbols := range cases {
515                         name := query
516                         if name == "" {
517                                 name = "EmptyQuery"
518                         }
519                         t.Run(name, func(t *testing.T) {
520                                 t.Helper()
521                                 dirs := make(map[string]struct{})
522                                 for _, si := range expectedSymbols {
523                                         d := filepath.Dir(si.Location.URI.SpanURI().Filename())
524                                         if _, ok := dirs[d]; !ok {
525                                                 dirs[d] = struct{}{}
526                                         }
527                                 }
528                                 test(t, query, expectedSymbols, dirs)
529                         })
530                 }
531         }
532
533         t.Run("CallHierarchy", func(t *testing.T) {
534                 t.Helper()
535                 for spn, callHierarchyResult := range data.CallHierarchy {
536                         t.Run(SpanName(spn), func(t *testing.T) {
537                                 t.Helper()
538                                 tests.CallHierarchy(t, spn, callHierarchyResult)
539                         })
540                 }
541         })
542
543         t.Run("Completion", func(t *testing.T) {
544                 t.Helper()
545                 eachCompletion(t, data.Completions, tests.Completion)
546         })
547
548         t.Run("CompletionSnippets", func(t *testing.T) {
549                 t.Helper()
550                 for _, placeholders := range []bool{true, false} {
551                         for src, expecteds := range data.CompletionSnippets {
552                                 for i, expected := range expecteds {
553                                         name := SpanName(src) + "_" + strconv.Itoa(i+1)
554                                         if placeholders {
555                                                 name += "_placeholders"
556                                         }
557
558                                         t.Run(name, func(t *testing.T) {
559                                                 t.Helper()
560                                                 tests.CompletionSnippet(t, src, expected, placeholders, data.CompletionItems)
561                                         })
562                                 }
563                         }
564                 }
565         })
566
567         t.Run("UnimportedCompletion", func(t *testing.T) {
568                 t.Helper()
569                 eachCompletion(t, data.UnimportedCompletions, tests.UnimportedCompletion)
570         })
571
572         t.Run("DeepCompletion", func(t *testing.T) {
573                 t.Helper()
574                 eachCompletion(t, data.DeepCompletions, tests.DeepCompletion)
575         })
576
577         t.Run("FuzzyCompletion", func(t *testing.T) {
578                 t.Helper()
579                 eachCompletion(t, data.FuzzyCompletions, tests.FuzzyCompletion)
580         })
581
582         t.Run("CaseSensitiveCompletion", func(t *testing.T) {
583                 t.Helper()
584                 eachCompletion(t, data.CaseSensitiveCompletions, tests.CaseSensitiveCompletion)
585         })
586
587         t.Run("RankCompletions", func(t *testing.T) {
588                 t.Helper()
589                 eachCompletion(t, data.RankCompletions, tests.RankCompletion)
590         })
591
592         t.Run("CodeLens", func(t *testing.T) {
593                 t.Helper()
594                 for uri, want := range data.CodeLens {
595                         // Check if we should skip this URI if the -modfile flag is not available.
596                         if shouldSkip(data, uri) {
597                                 continue
598                         }
599                         t.Run(uriName(uri), func(t *testing.T) {
600                                 t.Helper()
601                                 tests.CodeLens(t, uri, want)
602                         })
603                 }
604         })
605
606         t.Run("Diagnostics", func(t *testing.T) {
607                 t.Helper()
608                 for uri, want := range data.Diagnostics {
609                         // Check if we should skip this URI if the -modfile flag is not available.
610                         if shouldSkip(data, uri) {
611                                 continue
612                         }
613                         t.Run(uriName(uri), func(t *testing.T) {
614                                 t.Helper()
615                                 tests.Diagnostics(t, uri, want)
616                         })
617                 }
618         })
619
620         t.Run("FoldingRange", func(t *testing.T) {
621                 t.Helper()
622                 for _, spn := range data.FoldingRanges {
623                         t.Run(uriName(spn.URI()), func(t *testing.T) {
624                                 t.Helper()
625                                 tests.FoldingRanges(t, spn)
626                         })
627                 }
628         })
629
630         t.Run("Format", func(t *testing.T) {
631                 t.Helper()
632                 for _, spn := range data.Formats {
633                         t.Run(uriName(spn.URI()), func(t *testing.T) {
634                                 t.Helper()
635                                 tests.Format(t, spn)
636                         })
637                 }
638         })
639
640         t.Run("Import", func(t *testing.T) {
641                 t.Helper()
642                 for _, spn := range data.Imports {
643                         t.Run(uriName(spn.URI()), func(t *testing.T) {
644                                 t.Helper()
645                                 tests.Import(t, spn)
646                         })
647                 }
648         })
649
650         t.Run("SemanticTokens", func(t *testing.T) {
651                 t.Helper()
652                 for _, spn := range data.SemanticTokens {
653                         t.Run(uriName(spn.URI()), func(t *testing.T) {
654                                 t.Helper()
655                                 tests.SemanticTokens(t, spn)
656                         })
657                 }
658         })
659
660         t.Run("SuggestedFix", func(t *testing.T) {
661                 t.Helper()
662                 for spn, actionKinds := range data.SuggestedFixes {
663                         // Check if we should skip this spn if the -modfile flag is not available.
664                         if shouldSkip(data, spn.URI()) {
665                                 continue
666                         }
667                         t.Run(SpanName(spn), func(t *testing.T) {
668                                 t.Helper()
669                                 tests.SuggestedFix(t, spn, actionKinds)
670                         })
671                 }
672         })
673
674         t.Run("FunctionExtraction", func(t *testing.T) {
675                 t.Helper()
676                 for start, end := range data.FunctionExtractions {
677                         // Check if we should skip this spn if the -modfile flag is not available.
678                         if shouldSkip(data, start.URI()) {
679                                 continue
680                         }
681                         t.Run(SpanName(start), func(t *testing.T) {
682                                 t.Helper()
683                                 tests.FunctionExtraction(t, start, end)
684                         })
685                 }
686         })
687
688         t.Run("Definition", func(t *testing.T) {
689                 t.Helper()
690                 for spn, d := range data.Definitions {
691                         t.Run(SpanName(spn), func(t *testing.T) {
692                                 t.Helper()
693                                 if strings.Contains(t.Name(), "cgo") {
694                                         testenv.NeedsTool(t, "cgo")
695                                 }
696                                 if strings.Contains(t.Name(), "declarecgo") {
697                                         testenv.NeedsGo1Point(t, 15)
698                                 }
699                                 tests.Definition(t, spn, d)
700                         })
701                 }
702         })
703
704         t.Run("Implementation", func(t *testing.T) {
705                 t.Helper()
706                 for spn, m := range data.Implementations {
707                         t.Run(SpanName(spn), func(t *testing.T) {
708                                 t.Helper()
709                                 tests.Implementation(t, spn, m)
710                         })
711                 }
712         })
713
714         t.Run("Highlight", func(t *testing.T) {
715                 t.Helper()
716                 for pos, locations := range data.Highlights {
717                         t.Run(SpanName(pos), func(t *testing.T) {
718                                 t.Helper()
719                                 tests.Highlight(t, pos, locations)
720                         })
721                 }
722         })
723
724         t.Run("References", func(t *testing.T) {
725                 t.Helper()
726                 for src, itemList := range data.References {
727                         t.Run(SpanName(src), func(t *testing.T) {
728                                 t.Helper()
729                                 tests.References(t, src, itemList)
730                         })
731                 }
732         })
733
734         t.Run("Renames", func(t *testing.T) {
735                 t.Helper()
736                 for spn, newText := range data.Renames {
737                         t.Run(uriName(spn.URI())+"_"+newText, func(t *testing.T) {
738                                 t.Helper()
739                                 tests.Rename(t, spn, newText)
740                         })
741                 }
742         })
743
744         t.Run("PrepareRenames", func(t *testing.T) {
745                 t.Helper()
746                 for src, want := range data.PrepareRenames {
747                         t.Run(SpanName(src), func(t *testing.T) {
748                                 t.Helper()
749                                 tests.PrepareRename(t, src, want)
750                         })
751                 }
752         })
753
754         t.Run("Symbols", func(t *testing.T) {
755                 t.Helper()
756                 for uri, expectedSymbols := range data.Symbols {
757                         t.Run(uriName(uri), func(t *testing.T) {
758                                 t.Helper()
759                                 tests.Symbols(t, uri, expectedSymbols)
760                         })
761                 }
762         })
763
764         t.Run("WorkspaceSymbols", func(t *testing.T) {
765                 t.Helper()
766                 eachWorkspaceSymbols(t, data.WorkspaceSymbols, tests.WorkspaceSymbols)
767         })
768
769         t.Run("FuzzyWorkspaceSymbols", func(t *testing.T) {
770                 t.Helper()
771                 eachWorkspaceSymbols(t, data.FuzzyWorkspaceSymbols, tests.FuzzyWorkspaceSymbols)
772         })
773
774         t.Run("CaseSensitiveWorkspaceSymbols", func(t *testing.T) {
775                 t.Helper()
776                 eachWorkspaceSymbols(t, data.CaseSensitiveWorkspaceSymbols, tests.CaseSensitiveWorkspaceSymbols)
777         })
778
779         t.Run("SignatureHelp", func(t *testing.T) {
780                 t.Helper()
781                 for spn, expectedSignature := range data.Signatures {
782                         t.Run(SpanName(spn), func(t *testing.T) {
783                                 t.Helper()
784                                 tests.SignatureHelp(t, spn, expectedSignature)
785                         })
786                 }
787         })
788
789         t.Run("Link", func(t *testing.T) {
790                 t.Helper()
791                 for uri, wantLinks := range data.Links {
792                         // If we are testing GOPATH, then we do not want links with
793                         // the versions attached (pkg.go.dev/repoa/moda@v1.1.0/pkg),
794                         // unless the file is a go.mod, then we can skip it alltogether.
795                         if data.Exported.Exporter == packagestest.GOPATH {
796                                 if strings.HasSuffix(uri.Filename(), ".mod") {
797                                         continue
798                                 }
799                                 re := regexp.MustCompile(`@v\d+\.\d+\.[\w-]+`)
800                                 for i, link := range wantLinks {
801                                         wantLinks[i].Target = re.ReplaceAllString(link.Target, "")
802                                 }
803                         }
804                         t.Run(uriName(uri), func(t *testing.T) {
805                                 t.Helper()
806                                 tests.Link(t, uri, wantLinks)
807                         })
808                 }
809         })
810
811         if *UpdateGolden {
812                 for _, golden := range data.golden {
813                         if !golden.Modified {
814                                 continue
815                         }
816                         sort.Slice(golden.Archive.Files, func(i, j int) bool {
817                                 return golden.Archive.Files[i].Name < golden.Archive.Files[j].Name
818                         })
819                         if err := ioutil.WriteFile(golden.Filename, txtar.Format(golden.Archive), 0666); err != nil {
820                                 t.Fatal(err)
821                         }
822                 }
823         }
824 }
825
826 func checkData(t *testing.T, data *Data) {
827         buf := &bytes.Buffer{}
828         diagnosticsCount := 0
829         for _, want := range data.Diagnostics {
830                 diagnosticsCount += len(want)
831         }
832         linksCount := 0
833         for _, want := range data.Links {
834                 linksCount += len(want)
835         }
836         definitionCount := 0
837         typeDefinitionCount := 0
838         for _, d := range data.Definitions {
839                 if d.IsType {
840                         typeDefinitionCount++
841                 } else {
842                         definitionCount++
843                 }
844         }
845
846         snippetCount := 0
847         for _, want := range data.CompletionSnippets {
848                 snippetCount += len(want)
849         }
850
851         countCompletions := func(c map[span.Span][]Completion) (count int) {
852                 for _, want := range c {
853                         count += len(want)
854                 }
855                 return count
856         }
857
858         countCodeLens := func(c map[span.URI][]protocol.CodeLens) (count int) {
859                 for _, want := range c {
860                         count += len(want)
861                 }
862                 return count
863         }
864
865         fmt.Fprintf(buf, "CallHierarchyCount = %v\n", len(data.CallHierarchy))
866         fmt.Fprintf(buf, "CodeLensCount = %v\n", countCodeLens(data.CodeLens))
867         fmt.Fprintf(buf, "CompletionsCount = %v\n", countCompletions(data.Completions))
868         fmt.Fprintf(buf, "CompletionSnippetCount = %v\n", snippetCount)
869         fmt.Fprintf(buf, "UnimportedCompletionsCount = %v\n", countCompletions(data.UnimportedCompletions))
870         fmt.Fprintf(buf, "DeepCompletionsCount = %v\n", countCompletions(data.DeepCompletions))
871         fmt.Fprintf(buf, "FuzzyCompletionsCount = %v\n", countCompletions(data.FuzzyCompletions))
872         fmt.Fprintf(buf, "RankedCompletionsCount = %v\n", countCompletions(data.RankCompletions))
873         fmt.Fprintf(buf, "CaseSensitiveCompletionsCount = %v\n", countCompletions(data.CaseSensitiveCompletions))
874         fmt.Fprintf(buf, "DiagnosticsCount = %v\n", diagnosticsCount)
875         fmt.Fprintf(buf, "FoldingRangesCount = %v\n", len(data.FoldingRanges))
876         fmt.Fprintf(buf, "FormatCount = %v\n", len(data.Formats))
877         fmt.Fprintf(buf, "ImportCount = %v\n", len(data.Imports))
878         fmt.Fprintf(buf, "SemanticTokenCount = %v\n", len(data.SemanticTokens))
879         fmt.Fprintf(buf, "SuggestedFixCount = %v\n", len(data.SuggestedFixes))
880         fmt.Fprintf(buf, "FunctionExtractionCount = %v\n", len(data.FunctionExtractions))
881         fmt.Fprintf(buf, "DefinitionsCount = %v\n", definitionCount)
882         fmt.Fprintf(buf, "TypeDefinitionsCount = %v\n", typeDefinitionCount)
883         fmt.Fprintf(buf, "HighlightsCount = %v\n", len(data.Highlights))
884         fmt.Fprintf(buf, "ReferencesCount = %v\n", len(data.References))
885         fmt.Fprintf(buf, "RenamesCount = %v\n", len(data.Renames))
886         fmt.Fprintf(buf, "PrepareRenamesCount = %v\n", len(data.PrepareRenames))
887         fmt.Fprintf(buf, "SymbolsCount = %v\n", len(data.Symbols))
888         fmt.Fprintf(buf, "WorkspaceSymbolsCount = %v\n", len(data.WorkspaceSymbols))
889         fmt.Fprintf(buf, "FuzzyWorkspaceSymbolsCount = %v\n", len(data.FuzzyWorkspaceSymbols))
890         fmt.Fprintf(buf, "CaseSensitiveWorkspaceSymbolsCount = %v\n", len(data.CaseSensitiveWorkspaceSymbols))
891         fmt.Fprintf(buf, "SignaturesCount = %v\n", len(data.Signatures))
892         fmt.Fprintf(buf, "LinksCount = %v\n", linksCount)
893         fmt.Fprintf(buf, "ImplementationsCount = %v\n", len(data.Implementations))
894
895         want := string(data.Golden("summary", summaryFile, func() ([]byte, error) {
896                 return buf.Bytes(), nil
897         }))
898         got := buf.String()
899         if want != got {
900                 t.Errorf("test summary does not match:\n%s", Diff(want, got))
901         }
902 }
903
904 func (data *Data) Mapper(uri span.URI) (*protocol.ColumnMapper, error) {
905         data.mappersMu.Lock()
906         defer data.mappersMu.Unlock()
907
908         if _, ok := data.mappers[uri]; !ok {
909                 content, err := data.Exported.FileContents(uri.Filename())
910                 if err != nil {
911                         return nil, err
912                 }
913                 converter := span.NewContentConverter(uri.Filename(), content)
914                 data.mappers[uri] = &protocol.ColumnMapper{
915                         URI:       uri,
916                         Converter: converter,
917                         Content:   content,
918                 }
919         }
920         return data.mappers[uri], nil
921 }
922
923 func (data *Data) Golden(tag string, target string, update func() ([]byte, error)) []byte {
924         data.t.Helper()
925         fragment, found := data.fragments[target]
926         if !found {
927                 if filepath.IsAbs(target) {
928                         data.t.Fatalf("invalid golden file fragment %v", target)
929                 }
930                 fragment = target
931         }
932         golden := data.golden[fragment]
933         if golden == nil {
934                 if !*UpdateGolden {
935                         data.t.Fatalf("could not find golden file %v: %v", fragment, tag)
936                 }
937                 var subdir string
938                 if fragment != summaryFile {
939                         subdir = "primarymod"
940                 }
941                 golden = &Golden{
942                         Filename: filepath.Join(data.dir, subdir, fragment+goldenFileSuffix),
943                         Archive:  &txtar.Archive{},
944                         Modified: true,
945                 }
946                 data.golden[fragment] = golden
947         }
948         var file *txtar.File
949         for i := range golden.Archive.Files {
950                 f := &golden.Archive.Files[i]
951                 if f.Name == tag {
952                         file = f
953                         break
954                 }
955         }
956         if *UpdateGolden {
957                 if file == nil {
958                         golden.Archive.Files = append(golden.Archive.Files, txtar.File{
959                                 Name: tag,
960                         })
961                         file = &golden.Archive.Files[len(golden.Archive.Files)-1]
962                 }
963                 contents, err := update()
964                 if err != nil {
965                         data.t.Fatalf("could not update golden file %v: %v", fragment, err)
966                 }
967                 file.Data = append(contents, '\n') // add trailing \n for txtar
968                 golden.Modified = true
969
970         }
971         if file == nil {
972                 data.t.Fatalf("could not find golden contents %v: %v", fragment, tag)
973         }
974         if len(file.Data) == 0 {
975                 return file.Data
976         }
977         return file.Data[:len(file.Data)-1] // drop the trailing \n
978 }
979
980 func (data *Data) collectCodeLens(spn span.Span, title, cmd string) {
981         if _, ok := data.CodeLens[spn.URI()]; !ok {
982                 data.CodeLens[spn.URI()] = []protocol.CodeLens{}
983         }
984         m, err := data.Mapper(spn.URI())
985         if err != nil {
986                 return
987         }
988         rng, err := m.Range(spn)
989         if err != nil {
990                 return
991         }
992         data.CodeLens[spn.URI()] = append(data.CodeLens[spn.URI()], protocol.CodeLens{
993                 Range: rng,
994                 Command: protocol.Command{
995                         Title:   title,
996                         Command: cmd,
997                 },
998         })
999 }
1000
1001 func (data *Data) collectDiagnostics(spn span.Span, msgSource, msg, msgSeverity string) {
1002         if _, ok := data.Diagnostics[spn.URI()]; !ok {
1003                 data.Diagnostics[spn.URI()] = []*source.Diagnostic{}
1004         }
1005         m, err := data.Mapper(spn.URI())
1006         if err != nil {
1007                 return
1008         }
1009         rng, err := m.Range(spn)
1010         if err != nil {
1011                 return
1012         }
1013         severity := protocol.SeverityError
1014         switch msgSeverity {
1015         case "error":
1016                 severity = protocol.SeverityError
1017         case "warning":
1018                 severity = protocol.SeverityWarning
1019         case "hint":
1020                 severity = protocol.SeverityHint
1021         case "information":
1022                 severity = protocol.SeverityInformation
1023         }
1024         // This is not the correct way to do this, but it seems excessive to do the full conversion here.
1025         want := &source.Diagnostic{
1026                 Range:    rng,
1027                 Severity: severity,
1028                 Source:   msgSource,
1029                 Message:  msg,
1030         }
1031         data.Diagnostics[spn.URI()] = append(data.Diagnostics[spn.URI()], want)
1032 }
1033
1034 func (data *Data) collectCompletions(typ CompletionTestType) func(span.Span, []token.Pos) {
1035         result := func(m map[span.Span][]Completion, src span.Span, expected []token.Pos) {
1036                 m[src] = append(m[src], Completion{
1037                         CompletionItems: expected,
1038                 })
1039         }
1040         switch typ {
1041         case CompletionDeep:
1042                 return func(src span.Span, expected []token.Pos) {
1043                         result(data.DeepCompletions, src, expected)
1044                 }
1045         case CompletionUnimported:
1046                 return func(src span.Span, expected []token.Pos) {
1047                         result(data.UnimportedCompletions, src, expected)
1048                 }
1049         case CompletionFuzzy:
1050                 return func(src span.Span, expected []token.Pos) {
1051                         result(data.FuzzyCompletions, src, expected)
1052                 }
1053         case CompletionRank:
1054                 return func(src span.Span, expected []token.Pos) {
1055                         result(data.RankCompletions, src, expected)
1056                 }
1057         case CompletionCaseSensitive:
1058                 return func(src span.Span, expected []token.Pos) {
1059                         result(data.CaseSensitiveCompletions, src, expected)
1060                 }
1061         default:
1062                 return func(src span.Span, expected []token.Pos) {
1063                         result(data.Completions, src, expected)
1064                 }
1065         }
1066 }
1067
1068 func (data *Data) collectCompletionItems(pos token.Pos, args []string) {
1069         if len(args) < 3 {
1070                 loc := data.Exported.ExpectFileSet.Position(pos)
1071                 data.t.Fatalf("%s:%d: @item expects at least 3 args, got %d",
1072                         loc.Filename, loc.Line, len(args))
1073         }
1074         label, detail, kind := args[0], args[1], args[2]
1075         var documentation string
1076         if len(args) == 4 {
1077                 documentation = args[3]
1078         }
1079         data.CompletionItems[pos] = &completion.CompletionItem{
1080                 Label:         label,
1081                 Detail:        detail,
1082                 Kind:          protocol.ParseCompletionItemKind(kind),
1083                 Documentation: documentation,
1084         }
1085 }
1086
1087 func (data *Data) collectFoldingRanges(spn span.Span) {
1088         data.FoldingRanges = append(data.FoldingRanges, spn)
1089 }
1090
1091 func (data *Data) collectFormats(spn span.Span) {
1092         data.Formats = append(data.Formats, spn)
1093 }
1094
1095 func (data *Data) collectImports(spn span.Span) {
1096         data.Imports = append(data.Imports, spn)
1097 }
1098
1099 func (data *Data) collectSemanticTokens(spn span.Span) {
1100         data.SemanticTokens = append(data.SemanticTokens, spn)
1101 }
1102
1103 func (data *Data) collectSuggestedFixes(spn span.Span, actionKind string) {
1104         if _, ok := data.SuggestedFixes[spn]; !ok {
1105                 data.SuggestedFixes[spn] = []string{}
1106         }
1107         data.SuggestedFixes[spn] = append(data.SuggestedFixes[spn], actionKind)
1108 }
1109
1110 func (data *Data) collectFunctionExtractions(start span.Span, end span.Span) {
1111         if _, ok := data.FunctionExtractions[start]; !ok {
1112                 data.FunctionExtractions[start] = end
1113         }
1114 }
1115
1116 func (data *Data) collectDefinitions(src, target span.Span) {
1117         data.Definitions[src] = Definition{
1118                 Src: src,
1119                 Def: target,
1120         }
1121 }
1122
1123 func (data *Data) collectImplementations(src span.Span, targets []span.Span) {
1124         data.Implementations[src] = targets
1125 }
1126
1127 func (data *Data) collectIncomingCalls(src span.Span, calls []span.Span) {
1128         for _, call := range calls {
1129                 m, err := data.Mapper(call.URI())
1130                 if err != nil {
1131                         data.t.Fatal(err)
1132                 }
1133                 rng, err := m.Range(call)
1134                 if err != nil {
1135                         data.t.Fatal(err)
1136                 }
1137                 // we're only comparing protocol.range
1138                 if data.CallHierarchy[src] != nil {
1139                         data.CallHierarchy[src].IncomingCalls = append(data.CallHierarchy[src].IncomingCalls,
1140                                 protocol.CallHierarchyItem{
1141                                         URI:   protocol.DocumentURI(call.URI()),
1142                                         Range: rng,
1143                                 })
1144                 } else {
1145                         data.CallHierarchy[src] = &CallHierarchyResult{
1146                                 IncomingCalls: []protocol.CallHierarchyItem{
1147                                         {URI: protocol.DocumentURI(call.URI()), Range: rng},
1148                                 },
1149                         }
1150                 }
1151         }
1152 }
1153
1154 func (data *Data) collectOutgoingCalls(src span.Span, calls []span.Span) {
1155         for _, call := range calls {
1156                 m, err := data.Mapper(call.URI())
1157                 if err != nil {
1158                         data.t.Fatal(err)
1159                 }
1160                 rng, err := m.Range(call)
1161                 if err != nil {
1162                         data.t.Fatal(err)
1163                 }
1164                 // we're only comparing protocol.range
1165                 if data.CallHierarchy[src] != nil {
1166                         data.CallHierarchy[src].OutgoingCalls = append(data.CallHierarchy[src].OutgoingCalls,
1167                                 protocol.CallHierarchyItem{
1168                                         URI:   protocol.DocumentURI(call.URI()),
1169                                         Range: rng,
1170                                 })
1171                 } else {
1172                         data.CallHierarchy[src] = &CallHierarchyResult{
1173                                 OutgoingCalls: []protocol.CallHierarchyItem{
1174                                         {URI: protocol.DocumentURI(call.URI()), Range: rng},
1175                                 },
1176                         }
1177                 }
1178         }
1179 }
1180
1181 func (data *Data) collectHoverDefinitions(src, target span.Span) {
1182         data.Definitions[src] = Definition{
1183                 Src:       src,
1184                 Def:       target,
1185                 OnlyHover: true,
1186         }
1187 }
1188
1189 func (data *Data) collectTypeDefinitions(src, target span.Span) {
1190         data.Definitions[src] = Definition{
1191                 Src:    src,
1192                 Def:    target,
1193                 IsType: true,
1194         }
1195 }
1196
1197 func (data *Data) collectDefinitionNames(src span.Span, name string) {
1198         d := data.Definitions[src]
1199         d.Name = name
1200         data.Definitions[src] = d
1201 }
1202
1203 func (data *Data) collectHighlights(src span.Span, expected []span.Span) {
1204         // Declaring a highlight in a test file: @highlight(src, expected1, expected2)
1205         data.Highlights[src] = append(data.Highlights[src], expected...)
1206 }
1207
1208 func (data *Data) collectReferences(src span.Span, expected []span.Span) {
1209         data.References[src] = expected
1210 }
1211
1212 func (data *Data) collectRenames(src span.Span, newText string) {
1213         data.Renames[src] = newText
1214 }
1215
1216 func (data *Data) collectPrepareRenames(src span.Span, rng span.Range, placeholder string) {
1217         m, err := data.Mapper(src.URI())
1218         if err != nil {
1219                 data.t.Fatal(err)
1220         }
1221         // Convert range to span and then to protocol.Range.
1222         spn, err := rng.Span()
1223         if err != nil {
1224                 data.t.Fatal(err)
1225         }
1226         prng, err := m.Range(spn)
1227         if err != nil {
1228                 data.t.Fatal(err)
1229         }
1230         data.PrepareRenames[src] = &source.PrepareItem{
1231                 Range: prng,
1232                 Text:  placeholder,
1233         }
1234 }
1235
1236 // collectSymbols is responsible for collecting @symbol annotations.
1237 func (data *Data) collectSymbols(name string, spn span.Span, kind string, parentName string, siName string) {
1238         m, err := data.Mapper(spn.URI())
1239         if err != nil {
1240                 data.t.Fatal(err)
1241         }
1242         rng, err := m.Range(spn)
1243         if err != nil {
1244                 data.t.Fatal(err)
1245         }
1246         sym := protocol.DocumentSymbol{
1247                 Name:           name,
1248                 Kind:           protocol.ParseSymbolKind(kind),
1249                 SelectionRange: rng,
1250         }
1251         if parentName == "" {
1252                 data.Symbols[spn.URI()] = append(data.Symbols[spn.URI()], sym)
1253         } else {
1254                 data.symbolsChildren[parentName] = append(data.symbolsChildren[parentName], sym)
1255         }
1256
1257         // Reuse @symbol in the workspace symbols tests.
1258         si := protocol.SymbolInformation{
1259                 Name: siName,
1260                 Kind: sym.Kind,
1261                 Location: protocol.Location{
1262                         URI:   protocol.URIFromSpanURI(spn.URI()),
1263                         Range: sym.SelectionRange,
1264                 },
1265         }
1266         data.symbolInformation[spn] = si
1267 }
1268
1269 func (data *Data) collectWorkspaceSymbols(typ WorkspaceSymbolsTestType) func(string, []span.Span) {
1270         switch typ {
1271         case WorkspaceSymbolsFuzzy:
1272                 return func(query string, targets []span.Span) {
1273                         data.FuzzyWorkspaceSymbols[query] = make([]protocol.SymbolInformation, 0, len(targets))
1274                         for _, target := range targets {
1275                                 data.FuzzyWorkspaceSymbols[query] = append(data.FuzzyWorkspaceSymbols[query], data.symbolInformation[target])
1276                         }
1277                 }
1278         case WorkspaceSymbolsCaseSensitive:
1279                 return func(query string, targets []span.Span) {
1280                         data.CaseSensitiveWorkspaceSymbols[query] = make([]protocol.SymbolInformation, 0, len(targets))
1281                         for _, target := range targets {
1282                                 data.CaseSensitiveWorkspaceSymbols[query] = append(data.CaseSensitiveWorkspaceSymbols[query], data.symbolInformation[target])
1283                         }
1284                 }
1285         default:
1286                 return func(query string, targets []span.Span) {
1287                         data.WorkspaceSymbols[query] = make([]protocol.SymbolInformation, 0, len(targets))
1288                         for _, target := range targets {
1289                                 data.WorkspaceSymbols[query] = append(data.WorkspaceSymbols[query], data.symbolInformation[target])
1290                         }
1291                 }
1292         }
1293 }
1294
1295 func (data *Data) collectSignatures(spn span.Span, signature string, activeParam int64) {
1296         data.Signatures[spn] = &protocol.SignatureHelp{
1297                 Signatures: []protocol.SignatureInformation{
1298                         {
1299                                 Label: signature,
1300                         },
1301                 },
1302                 ActiveParameter: float64(activeParam),
1303         }
1304         // Hardcode special case to test the lack of a signature.
1305         if signature == "" && activeParam == 0 {
1306                 data.Signatures[spn] = nil
1307         }
1308 }
1309
1310 func (data *Data) collectCompletionSnippets(spn span.Span, item token.Pos, plain, placeholder string) {
1311         data.CompletionSnippets[spn] = append(data.CompletionSnippets[spn], CompletionSnippet{
1312                 CompletionItem:     item,
1313                 PlainSnippet:       plain,
1314                 PlaceholderSnippet: placeholder,
1315         })
1316 }
1317
1318 func (data *Data) collectLinks(spn span.Span, link string, note *expect.Note, fset *token.FileSet) {
1319         position := fset.Position(note.Pos)
1320         uri := spn.URI()
1321         data.Links[uri] = append(data.Links[uri], Link{
1322                 Src:          spn,
1323                 Target:       link,
1324                 NotePosition: position,
1325         })
1326 }
1327
1328 func uriName(uri span.URI) string {
1329         return filepath.Base(strings.TrimSuffix(uri.Filename(), ".go"))
1330 }
1331
1332 func SpanName(spn span.Span) string {
1333         return fmt.Sprintf("%v_%v_%v", uriName(spn.URI()), spn.Start().Line(), spn.Start().Column())
1334 }
1335
1336 func CopyFolderToTempDir(folder string) (string, error) {
1337         if _, err := os.Stat(folder); err != nil {
1338                 return "", err
1339         }
1340         dst, err := ioutil.TempDir("", "modfile_test")
1341         if err != nil {
1342                 return "", err
1343         }
1344         fds, err := ioutil.ReadDir(folder)
1345         if err != nil {
1346                 return "", err
1347         }
1348         for _, fd := range fds {
1349                 srcfp := filepath.Join(folder, fd.Name())
1350                 stat, err := os.Stat(srcfp)
1351                 if err != nil {
1352                         return "", err
1353                 }
1354                 if !stat.Mode().IsRegular() {
1355                         return "", fmt.Errorf("cannot copy non regular file %s", srcfp)
1356                 }
1357                 contents, err := ioutil.ReadFile(srcfp)
1358                 if err != nil {
1359                         return "", err
1360                 }
1361                 if err := ioutil.WriteFile(filepath.Join(dst, fd.Name()), contents, stat.Mode()); err != nil {
1362                         return "", err
1363                 }
1364         }
1365         return dst, nil
1366 }
1367
1368 func shouldSkip(data *Data, uri span.URI) bool {
1369         if data.ModfileFlagAvailable {
1370                 return false
1371         }
1372         // If the -modfile flag is not available, then we do not want to run
1373         // any tests on the go.mod file.
1374         if strings.HasSuffix(uri.Filename(), ".mod") {
1375                 return true
1376         }
1377         // If the -modfile flag is not available, then we do not want to test any
1378         // uri that contains "go mod tidy".
1379         m, err := data.Mapper(uri)
1380         return err == nil && strings.Contains(string(m.Content), ", \"go mod tidy\",")
1381 }