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