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