github.com/jd-ly/tools@v0.5.7/internal/lsp/tests/tests.go (about)

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