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 }