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