.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.0 / internal / lsp / lsp_test.go
1 // Copyright 2018 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 lsp
6
7 import (
8         "context"
9         "fmt"
10         "go/token"
11         "os"
12         "os/exec"
13         "path/filepath"
14         "sort"
15         "strings"
16         "testing"
17
18         "golang.org/x/tools/internal/lsp/cache"
19         "golang.org/x/tools/internal/lsp/diff"
20         "golang.org/x/tools/internal/lsp/diff/myers"
21         "golang.org/x/tools/internal/lsp/protocol"
22         "golang.org/x/tools/internal/lsp/source"
23         "golang.org/x/tools/internal/lsp/tests"
24         "golang.org/x/tools/internal/span"
25         "golang.org/x/tools/internal/testenv"
26 )
27
28 func TestMain(m *testing.M) {
29         testenv.ExitIfSmallMachine()
30         os.Exit(m.Run())
31 }
32
33 func TestLSP(t *testing.T) {
34         tests.RunTests(t, "testdata", true, testLSP)
35 }
36
37 type runner struct {
38         server      *Server
39         data        *tests.Data
40         diagnostics map[span.URI][]*source.Diagnostic
41         ctx         context.Context
42         normalizers []tests.Normalizer
43 }
44
45 func testLSP(t *testing.T, datum *tests.Data) {
46         ctx := tests.Context(t)
47
48         cache := cache.New(ctx, nil)
49         session := cache.NewSession(ctx)
50         options := source.DefaultOptions().Clone()
51         tests.DefaultOptions(options)
52         session.SetOptions(options)
53         options.SetEnvSlice(datum.Config.Env)
54         view, snapshot, release, err := session.NewView(ctx, datum.Config.Dir, span.URIFromPath(datum.Config.Dir), "", options)
55         if err != nil {
56                 t.Fatal(err)
57         }
58
59         defer view.Shutdown(ctx)
60
61         // Enable type error analyses for tests.
62         // TODO(golang/go#38212): Delete this once they are enabled by default.
63         tests.EnableAllAnalyzers(view, options)
64         view.SetOptions(ctx, options)
65
66         // Only run the -modfile specific tests in module mode with Go 1.14 or above.
67         datum.ModfileFlagAvailable = len(snapshot.ModFiles()) > 0 && testenv.Go1Point() >= 14
68         release()
69
70         var modifications []source.FileModification
71         for filename, content := range datum.Config.Overlay {
72                 kind := source.DetectLanguage("", filename)
73                 if kind != source.Go {
74                         continue
75                 }
76                 modifications = append(modifications, source.FileModification{
77                         URI:        span.URIFromPath(filename),
78                         Action:     source.Open,
79                         Version:    -1,
80                         Text:       content,
81                         LanguageID: "go",
82                 })
83         }
84         if err := session.ModifyFiles(ctx, modifications); err != nil {
85                 t.Fatal(err)
86         }
87         r := &runner{
88                 server:      NewServer(session, testClient{}),
89                 data:        datum,
90                 ctx:         ctx,
91                 normalizers: tests.CollectNormalizers(datum.Exported),
92         }
93         tests.Run(t, r, datum)
94 }
95
96 // testClient stubs any client functions that may be called by LSP functions.
97 type testClient struct {
98         protocol.Client
99 }
100
101 // Trivially implement PublishDiagnostics so that we can call
102 // server.publishReports below to de-dup sent diagnostics.
103 func (c testClient) PublishDiagnostics(context.Context, *protocol.PublishDiagnosticsParams) error {
104         return nil
105 }
106
107 func (r *runner) CallHierarchy(t *testing.T, spn span.Span, expectedCalls *tests.CallHierarchyResult) {
108         mapper, err := r.data.Mapper(spn.URI())
109         if err != nil {
110                 t.Fatal(err)
111         }
112         loc, err := mapper.Location(spn)
113         if err != nil {
114                 t.Fatalf("failed for %v: %v", spn, err)
115         }
116
117         params := &protocol.CallHierarchyPrepareParams{
118                 TextDocumentPositionParams: protocol.TextDocumentPositionParams{
119                         TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI},
120                         Position:     loc.Range.Start,
121                 },
122         }
123
124         items, err := r.server.PrepareCallHierarchy(r.ctx, params)
125         if err != nil {
126                 t.Fatal(err)
127         }
128         if len(items) == 0 {
129                 t.Fatalf("expected call hierarchy item to be returned for identifier at %v\n", loc.Range)
130         }
131
132         callLocation := protocol.Location{
133                 URI:   items[0].URI,
134                 Range: items[0].Range,
135         }
136         if callLocation != loc {
137                 t.Fatalf("expected server.PrepareCallHierarchy to return identifier at %v but got %v\n", loc, callLocation)
138         }
139
140         incomingCalls, err := r.server.IncomingCalls(r.ctx, &protocol.CallHierarchyIncomingCallsParams{Item: items[0]})
141         if err != nil {
142                 t.Error(err)
143         }
144         var incomingCallItems []protocol.CallHierarchyItem
145         for _, item := range incomingCalls {
146                 incomingCallItems = append(incomingCallItems, item.From)
147         }
148         msg := tests.DiffCallHierarchyItems(incomingCallItems, expectedCalls.IncomingCalls)
149         if msg != "" {
150                 t.Error(fmt.Sprintf("incoming calls: %s", msg))
151         }
152
153         outgoingCalls, err := r.server.OutgoingCalls(r.ctx, &protocol.CallHierarchyOutgoingCallsParams{Item: items[0]})
154         if err != nil {
155                 t.Error(err)
156         }
157         var outgoingCallItems []protocol.CallHierarchyItem
158         for _, item := range outgoingCalls {
159                 outgoingCallItems = append(outgoingCallItems, item.To)
160         }
161         msg = tests.DiffCallHierarchyItems(outgoingCallItems, expectedCalls.OutgoingCalls)
162         if msg != "" {
163                 t.Error(fmt.Sprintf("outgoing calls: %s", msg))
164         }
165 }
166
167 func (r *runner) CodeLens(t *testing.T, uri span.URI, want []protocol.CodeLens) {
168         if source.DetectLanguage("", uri.Filename()) != source.Mod {
169                 return
170         }
171         got, err := r.server.codeLens(r.ctx, &protocol.CodeLensParams{
172                 TextDocument: protocol.TextDocumentIdentifier{
173                         URI: protocol.DocumentURI(uri),
174                 },
175         })
176         if err != nil {
177                 t.Fatal(err)
178         }
179         if diff := tests.DiffCodeLens(uri, want, got); diff != "" {
180                 t.Errorf("%s: %s", uri, diff)
181         }
182 }
183
184 func (r *runner) Diagnostics(t *testing.T, uri span.URI, want []*source.Diagnostic) {
185         // Get the diagnostics for this view if we have not done it before.
186         v := r.server.session.View(r.data.Config.Dir)
187         r.collectDiagnostics(v)
188         d := r.diagnostics[uri]
189         got := make([]*source.Diagnostic, len(d))
190         copy(got, d)
191         // A special case to test that there are no diagnostics for a file.
192         if len(want) == 1 && want[0].Source == "no_diagnostics" {
193                 if len(got) != 0 {
194                         t.Errorf("expected no diagnostics for %s, got %v", uri, got)
195                 }
196                 return
197         }
198         if diff := tests.DiffDiagnostics(uri, want, got); diff != "" {
199                 t.Error(diff)
200         }
201 }
202
203 func (r *runner) FoldingRanges(t *testing.T, spn span.Span) {
204         uri := spn.URI()
205         view, err := r.server.session.ViewOf(uri)
206         if err != nil {
207                 t.Fatal(err)
208         }
209         original := view.Options()
210         modified := original
211
212         // Test all folding ranges.
213         modified.LineFoldingOnly = false
214         view, err = view.SetOptions(r.ctx, modified)
215         if err != nil {
216                 t.Error(err)
217                 return
218         }
219         ranges, err := r.server.FoldingRange(r.ctx, &protocol.FoldingRangeParams{
220                 TextDocument: protocol.TextDocumentIdentifier{
221                         URI: protocol.URIFromSpanURI(uri),
222                 },
223         })
224         if err != nil {
225                 t.Error(err)
226                 return
227         }
228         r.foldingRanges(t, "foldingRange", uri, ranges)
229
230         // Test folding ranges with lineFoldingOnly = true.
231         modified.LineFoldingOnly = true
232         view, err = view.SetOptions(r.ctx, modified)
233         if err != nil {
234                 t.Error(err)
235                 return
236         }
237         ranges, err = r.server.FoldingRange(r.ctx, &protocol.FoldingRangeParams{
238                 TextDocument: protocol.TextDocumentIdentifier{
239                         URI: protocol.URIFromSpanURI(uri),
240                 },
241         })
242         if err != nil {
243                 t.Error(err)
244                 return
245         }
246         r.foldingRanges(t, "foldingRange-lineFolding", uri, ranges)
247         view.SetOptions(r.ctx, original)
248 }
249
250 func (r *runner) foldingRanges(t *testing.T, prefix string, uri span.URI, ranges []protocol.FoldingRange) {
251         m, err := r.data.Mapper(uri)
252         if err != nil {
253                 t.Fatal(err)
254         }
255         // Fold all ranges.
256         nonOverlapping := nonOverlappingRanges(ranges)
257         for i, rngs := range nonOverlapping {
258                 got, err := foldRanges(m, string(m.Content), rngs)
259                 if err != nil {
260                         t.Error(err)
261                         continue
262                 }
263                 tag := fmt.Sprintf("%s-%d", prefix, i)
264                 want := string(r.data.Golden(tag, uri.Filename(), func() ([]byte, error) {
265                         return []byte(got), nil
266                 }))
267
268                 if want != got {
269                         t.Errorf("%s: foldingRanges failed for %s, expected:\n%v\ngot:\n%v", tag, uri.Filename(), want, got)
270                 }
271         }
272
273         // Filter by kind.
274         kinds := []protocol.FoldingRangeKind{protocol.Imports, protocol.Comment}
275         for _, kind := range kinds {
276                 var kindOnly []protocol.FoldingRange
277                 for _, fRng := range ranges {
278                         if fRng.Kind == string(kind) {
279                                 kindOnly = append(kindOnly, fRng)
280                         }
281                 }
282
283                 nonOverlapping := nonOverlappingRanges(kindOnly)
284                 for i, rngs := range nonOverlapping {
285                         got, err := foldRanges(m, string(m.Content), rngs)
286                         if err != nil {
287                                 t.Error(err)
288                                 continue
289                         }
290                         tag := fmt.Sprintf("%s-%s-%d", prefix, kind, i)
291                         want := string(r.data.Golden(tag, uri.Filename(), func() ([]byte, error) {
292                                 return []byte(got), nil
293                         }))
294
295                         if want != got {
296                                 t.Errorf("%s: foldingRanges failed for %s, expected:\n%v\ngot:\n%v", tag, uri.Filename(), want, got)
297                         }
298                 }
299
300         }
301 }
302
303 func nonOverlappingRanges(ranges []protocol.FoldingRange) (res [][]protocol.FoldingRange) {
304         for _, fRng := range ranges {
305                 setNum := len(res)
306                 for i := 0; i < len(res); i++ {
307                         canInsert := true
308                         for _, rng := range res[i] {
309                                 if conflict(rng, fRng) {
310                                         canInsert = false
311                                         break
312                                 }
313                         }
314                         if canInsert {
315                                 setNum = i
316                                 break
317                         }
318                 }
319                 if setNum == len(res) {
320                         res = append(res, []protocol.FoldingRange{})
321                 }
322                 res[setNum] = append(res[setNum], fRng)
323         }
324         return res
325 }
326
327 func conflict(a, b protocol.FoldingRange) bool {
328         // a start position is <= b start positions
329         return (a.StartLine < b.StartLine || (a.StartLine == b.StartLine && a.StartCharacter <= b.StartCharacter)) &&
330                 (a.EndLine > b.StartLine || (a.EndLine == b.StartLine && a.EndCharacter > b.StartCharacter))
331 }
332
333 func foldRanges(m *protocol.ColumnMapper, contents string, ranges []protocol.FoldingRange) (string, error) {
334         foldedText := "<>"
335         res := contents
336         // Apply the edits from the end of the file forward
337         // to preserve the offsets
338         for i := len(ranges) - 1; i >= 0; i-- {
339                 fRange := ranges[i]
340                 spn, err := m.RangeSpan(protocol.Range{
341                         Start: protocol.Position{
342                                 Line:      fRange.StartLine,
343                                 Character: fRange.StartCharacter,
344                         },
345                         End: protocol.Position{
346                                 Line:      fRange.EndLine,
347                                 Character: fRange.EndCharacter,
348                         },
349                 })
350                 if err != nil {
351                         return "", err
352                 }
353                 start := spn.Start().Offset()
354                 end := spn.End().Offset()
355
356                 tmp := res[0:start] + foldedText
357                 res = tmp + res[end:]
358         }
359         return res, nil
360 }
361
362 func (r *runner) Format(t *testing.T, spn span.Span) {
363         uri := spn.URI()
364         filename := uri.Filename()
365         gofmted := string(r.data.Golden("gofmt", filename, func() ([]byte, error) {
366                 cmd := exec.Command("gofmt", filename)
367                 out, _ := cmd.Output() // ignore error, sometimes we have intentionally ungofmt-able files
368                 return out, nil
369         }))
370
371         edits, err := r.server.Formatting(r.ctx, &protocol.DocumentFormattingParams{
372                 TextDocument: protocol.TextDocumentIdentifier{
373                         URI: protocol.URIFromSpanURI(uri),
374                 },
375         })
376         if err != nil {
377                 if gofmted != "" {
378                         t.Error(err)
379                 }
380                 return
381         }
382         m, err := r.data.Mapper(uri)
383         if err != nil {
384                 t.Fatal(err)
385         }
386         sedits, err := source.FromProtocolEdits(m, edits)
387         if err != nil {
388                 t.Error(err)
389         }
390         got := diff.ApplyEdits(string(m.Content), sedits)
391         if gofmted != got {
392                 t.Errorf("format failed for %s, expected:\n%v\ngot:\n%v", filename, gofmted, got)
393         }
394 }
395
396 func (r *runner) SemanticTokens(t *testing.T, spn span.Span) {
397         uri := spn.URI()
398         filename := uri.Filename()
399         // this is called solely for coverage in semantic.go
400         _, err := r.server.semanticTokensFull(r.ctx, &protocol.SemanticTokensParams{
401                 TextDocument: protocol.TextDocumentIdentifier{
402                         URI: protocol.URIFromSpanURI(uri),
403                 },
404         })
405         if err != nil {
406                 t.Errorf("%v for %s", err, filename)
407         }
408         _, err = r.server.semanticTokensRange(r.ctx, &protocol.SemanticTokensRangeParams{
409                 TextDocument: protocol.TextDocumentIdentifier{
410                         URI: protocol.URIFromSpanURI(uri),
411                 },
412                 // any legal range. Just to exercise the call.
413                 Range: protocol.Range{
414                         Start: protocol.Position{
415                                 Line:      0,
416                                 Character: 0,
417                         },
418                         End: protocol.Position{
419                                 Line:      2,
420                                 Character: 0,
421                         },
422                 },
423         })
424         if err != nil {
425                 t.Errorf("%v for Range %s", err, filename)
426         }
427 }
428
429 func (r *runner) Import(t *testing.T, spn span.Span) {
430         uri := spn.URI()
431         filename := uri.Filename()
432         actions, err := r.server.CodeAction(r.ctx, &protocol.CodeActionParams{
433                 TextDocument: protocol.TextDocumentIdentifier{
434                         URI: protocol.URIFromSpanURI(uri),
435                 },
436         })
437         if err != nil {
438                 t.Fatal(err)
439         }
440         m, err := r.data.Mapper(uri)
441         if err != nil {
442                 t.Fatal(err)
443         }
444         got := string(m.Content)
445         if len(actions) > 0 {
446                 res, err := applyTextDocumentEdits(r, actions[0].Edit.DocumentChanges)
447                 if err != nil {
448                         t.Fatal(err)
449                 }
450                 got = res[uri]
451         }
452         want := string(r.data.Golden("goimports", filename, func() ([]byte, error) {
453                 return []byte(got), nil
454         }))
455         if want != got {
456                 d, err := myers.ComputeEdits(uri, want, got)
457                 if err != nil {
458                         t.Fatal(err)
459                 }
460                 t.Errorf("import failed for %s: %s", filename, diff.ToUnified("want", "got", want, d))
461         }
462 }
463
464 func (r *runner) SuggestedFix(t *testing.T, spn span.Span, actionKinds []string, expectedActions int) {
465         uri := spn.URI()
466         view, err := r.server.session.ViewOf(uri)
467         if err != nil {
468                 t.Fatal(err)
469         }
470
471         snapshot, release := view.Snapshot(r.ctx)
472         defer release()
473
474         fh, err := snapshot.GetVersionedFile(r.ctx, uri)
475         if err != nil {
476                 t.Fatal(err)
477         }
478         m, err := r.data.Mapper(uri)
479         if err != nil {
480                 t.Fatal(err)
481         }
482         rng, err := m.Range(spn)
483         if err != nil {
484                 t.Fatal(err)
485         }
486         // Get the diagnostics for this view if we have not done it before.
487         r.collectDiagnostics(view)
488         var diagnostics []protocol.Diagnostic
489         for _, d := range r.diagnostics[uri] {
490                 // Compare the start positions rather than the entire range because
491                 // some diagnostics have a range with the same start and end position (8:1-8:1).
492                 // The current marker functionality prevents us from having a range of 0 length.
493                 if protocol.ComparePosition(d.Range.Start, rng.Start) == 0 {
494                         diagnostics = append(diagnostics, toProtocolDiagnostics([]*source.Diagnostic{d})...)
495                         break
496                 }
497         }
498         codeActionKinds := []protocol.CodeActionKind{}
499         for _, k := range actionKinds {
500                 codeActionKinds = append(codeActionKinds, protocol.CodeActionKind(k))
501         }
502         actions, err := r.server.CodeAction(r.ctx, &protocol.CodeActionParams{
503                 TextDocument: protocol.TextDocumentIdentifier{
504                         URI: protocol.URIFromSpanURI(uri),
505                 },
506                 Range: rng,
507                 Context: protocol.CodeActionContext{
508                         Only:        codeActionKinds,
509                         Diagnostics: diagnostics,
510                 },
511         })
512         if err != nil {
513                 t.Fatalf("CodeAction %s failed: %v", spn, err)
514         }
515         if len(actions) != expectedActions {
516                 // Hack: We assume that we only get one code action per range.
517                 var cmds []string
518                 for _, a := range actions {
519                         cmds = append(cmds, fmt.Sprintf("%s (%s)", a.Command.Command, a.Title))
520                 }
521                 t.Fatalf("unexpected number of code actions, want %d, got %d: %v", expectedActions, len(actions), cmds)
522         }
523         action := actions[0]
524         var match bool
525         for _, k := range codeActionKinds {
526                 if action.Kind == k {
527                         match = true
528                         break
529                 }
530         }
531         if !match {
532                 t.Fatalf("unexpected kind for code action %s, expected one of %v, got %v", action.Title, codeActionKinds, action.Kind)
533         }
534         var res map[span.URI]string
535         if cmd := action.Command; cmd != nil {
536                 edits, err := commandToEdits(r.ctx, snapshot, fh, rng, action.Command.Command)
537                 if err != nil {
538                         t.Fatalf("error converting command %q to edits: %v", action.Command.Command, err)
539                 }
540                 res, err = applyTextDocumentEdits(r, edits)
541                 if err != nil {
542                         t.Fatal(err)
543                 }
544         } else {
545                 res, err = applyTextDocumentEdits(r, action.Edit.DocumentChanges)
546                 if err != nil {
547                         t.Fatal(err)
548                 }
549         }
550         for u, got := range res {
551                 want := string(r.data.Golden("suggestedfix_"+tests.SpanName(spn), u.Filename(), func() ([]byte, error) {
552                         return []byte(got), nil
553                 }))
554                 if want != got {
555                         t.Errorf("suggested fixes failed for %s:\n%s", u.Filename(), tests.Diff(t, want, got))
556                 }
557         }
558 }
559
560 func commandToEdits(ctx context.Context, snapshot source.Snapshot, fh source.VersionedFileHandle, rng protocol.Range, cmd string) ([]protocol.TextDocumentEdit, error) {
561         var command *source.Command
562         for _, c := range source.Commands {
563                 if c.ID() == cmd {
564                         command = c
565                         break
566                 }
567         }
568         if command == nil {
569                 return nil, fmt.Errorf("no known command for %s", cmd)
570         }
571         if !command.Applies(ctx, snapshot, fh, rng) {
572                 return nil, fmt.Errorf("cannot apply %v", command.ID())
573         }
574         edits, err := command.SuggestedFix(ctx, snapshot, fh, rng)
575         if err != nil {
576                 return nil, fmt.Errorf("error calling command.SuggestedFix: %v", err)
577         }
578         return edits, nil
579 }
580
581 func (r *runner) FunctionExtraction(t *testing.T, start span.Span, end span.Span) {
582         uri := start.URI()
583         view, err := r.server.session.ViewOf(uri)
584         if err != nil {
585                 t.Fatal(err)
586         }
587
588         snapshot, release := view.Snapshot(r.ctx)
589         defer release()
590
591         fh, err := snapshot.GetVersionedFile(r.ctx, uri)
592         if err != nil {
593                 t.Fatal(err)
594         }
595         m, err := r.data.Mapper(uri)
596         if err != nil {
597                 t.Fatal(err)
598         }
599         spn := span.New(start.URI(), start.Start(), end.End())
600         rng, err := m.Range(spn)
601         if err != nil {
602                 t.Fatal(err)
603         }
604         actions, err := r.server.CodeAction(r.ctx, &protocol.CodeActionParams{
605                 TextDocument: protocol.TextDocumentIdentifier{
606                         URI: protocol.URIFromSpanURI(uri),
607                 },
608                 Range: rng,
609                 Context: protocol.CodeActionContext{
610                         Only: []protocol.CodeActionKind{"refactor.extract"},
611                 },
612         })
613         if err != nil {
614                 t.Fatal(err)
615         }
616         // Hack: We assume that we only get one code action per range.
617         // TODO(rstambler): Support multiple code actions per test.
618         if len(actions) == 0 || len(actions) > 1 {
619                 t.Fatalf("unexpected number of code actions, want 1, got %v", len(actions))
620         }
621         edits, err := commandToEdits(r.ctx, snapshot, fh, rng, actions[0].Command.Command)
622         if err != nil {
623                 t.Fatal(err)
624         }
625         res, err := applyTextDocumentEdits(r, edits)
626         if err != nil {
627                 t.Fatal(err)
628         }
629         for u, got := range res {
630                 want := string(r.data.Golden("functionextraction_"+tests.SpanName(spn), u.Filename(), func() ([]byte, error) {
631                         return []byte(got), nil
632                 }))
633                 if want != got {
634                         t.Errorf("function extraction failed for %s:\n%s", u.Filename(), tests.Diff(t, want, got))
635                 }
636         }
637 }
638
639 func (r *runner) Definition(t *testing.T, spn span.Span, d tests.Definition) {
640         sm, err := r.data.Mapper(d.Src.URI())
641         if err != nil {
642                 t.Fatal(err)
643         }
644         loc, err := sm.Location(d.Src)
645         if err != nil {
646                 t.Fatalf("failed for %v: %v", d.Src, err)
647         }
648         tdpp := protocol.TextDocumentPositionParams{
649                 TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI},
650                 Position:     loc.Range.Start,
651         }
652         var locs []protocol.Location
653         var hover *protocol.Hover
654         if d.IsType {
655                 params := &protocol.TypeDefinitionParams{
656                         TextDocumentPositionParams: tdpp,
657                 }
658                 locs, err = r.server.TypeDefinition(r.ctx, params)
659         } else {
660                 params := &protocol.DefinitionParams{
661                         TextDocumentPositionParams: tdpp,
662                 }
663                 locs, err = r.server.Definition(r.ctx, params)
664                 if err != nil {
665                         t.Fatalf("failed for %v: %+v", d.Src, err)
666                 }
667                 v := &protocol.HoverParams{
668                         TextDocumentPositionParams: tdpp,
669                 }
670                 hover, err = r.server.Hover(r.ctx, v)
671         }
672         if err != nil {
673                 t.Fatalf("failed for %v: %v", d.Src, err)
674         }
675         if len(locs) != 1 {
676                 t.Errorf("got %d locations for definition, expected 1", len(locs))
677         }
678         didSomething := false
679         if hover != nil {
680                 didSomething = true
681                 tag := fmt.Sprintf("%s-hover", d.Name)
682                 expectHover := string(r.data.Golden(tag, d.Src.URI().Filename(), func() ([]byte, error) {
683                         return []byte(hover.Contents.Value), nil
684                 }))
685                 if hover.Contents.Value != expectHover {
686                         t.Errorf("%s:\n%s", d.Src, tests.Diff(t, expectHover, hover.Contents.Value))
687                 }
688         }
689         if !d.OnlyHover {
690                 didSomething = true
691                 locURI := locs[0].URI.SpanURI()
692                 lm, err := r.data.Mapper(locURI)
693                 if err != nil {
694                         t.Fatal(err)
695                 }
696                 if def, err := lm.Span(locs[0]); err != nil {
697                         t.Fatalf("failed for %v: %v", locs[0], err)
698                 } else if def != d.Def {
699                         t.Errorf("for %v got %v want %v", d.Src, def, d.Def)
700                 }
701         }
702         if !didSomething {
703                 t.Errorf("no tests ran for %s", d.Src.URI())
704         }
705 }
706
707 func (r *runner) Implementation(t *testing.T, spn span.Span, impls []span.Span) {
708         sm, err := r.data.Mapper(spn.URI())
709         if err != nil {
710                 t.Fatal(err)
711         }
712         loc, err := sm.Location(spn)
713         if err != nil {
714                 t.Fatalf("failed for %v: %v", spn, err)
715         }
716         tdpp := protocol.TextDocumentPositionParams{
717                 TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI},
718                 Position:     loc.Range.Start,
719         }
720         var locs []protocol.Location
721         params := &protocol.ImplementationParams{
722                 TextDocumentPositionParams: tdpp,
723         }
724         locs, err = r.server.Implementation(r.ctx, params)
725         if err != nil {
726                 t.Fatalf("failed for %v: %v", spn, err)
727         }
728         if len(locs) != len(impls) {
729                 t.Fatalf("got %d locations for implementation, expected %d", len(locs), len(impls))
730         }
731
732         var results []span.Span
733         for i := range locs {
734                 locURI := locs[i].URI.SpanURI()
735                 lm, err := r.data.Mapper(locURI)
736                 if err != nil {
737                         t.Fatal(err)
738                 }
739                 imp, err := lm.Span(locs[i])
740                 if err != nil {
741                         t.Fatalf("failed for %v: %v", locs[i], err)
742                 }
743                 results = append(results, imp)
744         }
745         // Sort results and expected to make tests deterministic.
746         sort.SliceStable(results, func(i, j int) bool {
747                 return span.Compare(results[i], results[j]) == -1
748         })
749         sort.SliceStable(impls, func(i, j int) bool {
750                 return span.Compare(impls[i], impls[j]) == -1
751         })
752         for i := range results {
753                 if results[i] != impls[i] {
754                         t.Errorf("for %dth implementation of %v got %v want %v", i, spn, results[i], impls[i])
755                 }
756         }
757 }
758
759 func (r *runner) Highlight(t *testing.T, src span.Span, locations []span.Span) {
760         m, err := r.data.Mapper(src.URI())
761         if err != nil {
762                 t.Fatal(err)
763         }
764         loc, err := m.Location(src)
765         if err != nil {
766                 t.Fatalf("failed for %v: %v", locations[0], err)
767         }
768         tdpp := protocol.TextDocumentPositionParams{
769                 TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI},
770                 Position:     loc.Range.Start,
771         }
772         params := &protocol.DocumentHighlightParams{
773                 TextDocumentPositionParams: tdpp,
774         }
775         highlights, err := r.server.DocumentHighlight(r.ctx, params)
776         if err != nil {
777                 t.Fatal(err)
778         }
779         if len(highlights) != len(locations) {
780                 t.Fatalf("got %d highlights for highlight at %v:%v:%v, expected %d", len(highlights), src.URI().Filename(), src.Start().Line(), src.Start().Column(), len(locations))
781         }
782         // Check to make sure highlights have a valid range.
783         var results []span.Span
784         for i := range highlights {
785                 h, err := m.RangeSpan(highlights[i].Range)
786                 if err != nil {
787                         t.Fatalf("failed for %v: %v", highlights[i], err)
788                 }
789                 results = append(results, h)
790         }
791         // Sort results to make tests deterministic since DocumentHighlight uses a map.
792         sort.SliceStable(results, func(i, j int) bool {
793                 return span.Compare(results[i], results[j]) == -1
794         })
795         // Check to make sure all the expected highlights are found.
796         for i := range results {
797                 if results[i] != locations[i] {
798                         t.Errorf("want %v, got %v\n", locations[i], results[i])
799                 }
800         }
801 }
802
803 func (r *runner) References(t *testing.T, src span.Span, itemList []span.Span) {
804         sm, err := r.data.Mapper(src.URI())
805         if err != nil {
806                 t.Fatal(err)
807         }
808         loc, err := sm.Location(src)
809         if err != nil {
810                 t.Fatalf("failed for %v: %v", src, err)
811         }
812         for _, includeDeclaration := range []bool{true, false} {
813                 t.Run(fmt.Sprintf("refs-declaration-%v", includeDeclaration), func(t *testing.T) {
814                         want := make(map[protocol.Location]bool)
815                         for i, pos := range itemList {
816                                 // We don't want the first result if we aren't including the declaration.
817                                 if i == 0 && !includeDeclaration {
818                                         continue
819                                 }
820                                 m, err := r.data.Mapper(pos.URI())
821                                 if err != nil {
822                                         t.Fatal(err)
823                                 }
824                                 loc, err := m.Location(pos)
825                                 if err != nil {
826                                         t.Fatalf("failed for %v: %v", src, err)
827                                 }
828                                 want[loc] = true
829                         }
830                         params := &protocol.ReferenceParams{
831                                 TextDocumentPositionParams: protocol.TextDocumentPositionParams{
832                                         TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI},
833                                         Position:     loc.Range.Start,
834                                 },
835                                 Context: protocol.ReferenceContext{
836                                         IncludeDeclaration: includeDeclaration,
837                                 },
838                         }
839                         got, err := r.server.References(r.ctx, params)
840                         if err != nil {
841                                 t.Fatalf("failed for %v: %v", src, err)
842                         }
843                         if len(got) != len(want) {
844                                 t.Errorf("references failed: different lengths got %v want %v", len(got), len(want))
845                         }
846                         for _, loc := range got {
847                                 if !want[loc] {
848                                         t.Errorf("references failed: incorrect references got %v want %v", loc, want)
849                                 }
850                         }
851                 })
852         }
853 }
854
855 func (r *runner) Rename(t *testing.T, spn span.Span, newText string) {
856         tag := fmt.Sprintf("%s-rename", newText)
857
858         uri := spn.URI()
859         filename := uri.Filename()
860         sm, err := r.data.Mapper(uri)
861         if err != nil {
862                 t.Fatal(err)
863         }
864         loc, err := sm.Location(spn)
865         if err != nil {
866                 t.Fatalf("failed for %v: %v", spn, err)
867         }
868
869         wedit, err := r.server.Rename(r.ctx, &protocol.RenameParams{
870                 TextDocument: protocol.TextDocumentIdentifier{
871                         URI: protocol.URIFromSpanURI(uri),
872                 },
873                 Position: loc.Range.Start,
874                 NewName:  newText,
875         })
876         if err != nil {
877                 renamed := string(r.data.Golden(tag, filename, func() ([]byte, error) {
878                         return []byte(err.Error()), nil
879                 }))
880                 if err.Error() != renamed {
881                         t.Errorf("rename failed for %s, expected:\n%v\ngot:\n%v\n", newText, renamed, err)
882                 }
883                 return
884         }
885         res, err := applyTextDocumentEdits(r, wedit.DocumentChanges)
886         if err != nil {
887                 t.Fatal(err)
888         }
889         var orderedURIs []string
890         for uri := range res {
891                 orderedURIs = append(orderedURIs, string(uri))
892         }
893         sort.Strings(orderedURIs)
894
895         var got string
896         for i := 0; i < len(res); i++ {
897                 if i != 0 {
898                         got += "\n"
899                 }
900                 uri := span.URIFromURI(orderedURIs[i])
901                 if len(res) > 1 {
902                         got += filepath.Base(uri.Filename()) + ":\n"
903                 }
904                 val := res[uri]
905                 got += val
906         }
907         want := string(r.data.Golden(tag, filename, func() ([]byte, error) {
908                 return []byte(got), nil
909         }))
910         if want != got {
911                 t.Errorf("rename failed for %s:\n%s", newText, tests.Diff(t, want, got))
912         }
913 }
914
915 func (r *runner) PrepareRename(t *testing.T, src span.Span, want *source.PrepareItem) {
916         m, err := r.data.Mapper(src.URI())
917         if err != nil {
918                 t.Fatal(err)
919         }
920         loc, err := m.Location(src)
921         if err != nil {
922                 t.Fatalf("failed for %v: %v", src, err)
923         }
924         tdpp := protocol.TextDocumentPositionParams{
925                 TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI},
926                 Position:     loc.Range.Start,
927         }
928         params := &protocol.PrepareRenameParams{
929                 TextDocumentPositionParams: tdpp,
930         }
931         got, err := r.server.PrepareRename(context.Background(), params)
932         if err != nil {
933                 t.Errorf("prepare rename failed for %v: got error: %v", src, err)
934                 return
935         }
936         // we all love typed nils
937         if got == nil {
938                 if want.Text != "" { // expected an ident.
939                         t.Errorf("prepare rename failed for %v: got nil", src)
940                 }
941                 return
942         }
943         if got.Start == got.End {
944                 // Special case for 0-length ranges. Marks can't specify a 0-length range,
945                 // so just compare the start.
946                 if got.Start != want.Range.Start {
947                         t.Errorf("prepare rename failed: incorrect point, got %v want %v", got.Start, want.Range.Start)
948                 }
949         } else {
950                 if protocol.CompareRange(*got, want.Range) != 0 {
951                         t.Errorf("prepare rename failed: incorrect range got %v want %v", *got, want.Range)
952                 }
953         }
954 }
955
956 func applyTextDocumentEdits(r *runner, edits []protocol.TextDocumentEdit) (map[span.URI]string, error) {
957         res := map[span.URI]string{}
958         for _, docEdits := range edits {
959                 uri := docEdits.TextDocument.URI.SpanURI()
960                 var m *protocol.ColumnMapper
961                 // If we have already edited this file, we use the edited version (rather than the
962                 // file in its original state) so that we preserve our initial changes.
963                 if content, ok := res[uri]; ok {
964                         m = &protocol.ColumnMapper{
965                                 URI: uri,
966                                 Converter: span.NewContentConverter(
967                                         uri.Filename(), []byte(content)),
968                                 Content: []byte(content),
969                         }
970                 } else {
971                         var err error
972                         if m, err = r.data.Mapper(uri); err != nil {
973                                 return nil, err
974                         }
975                 }
976                 res[uri] = string(m.Content)
977                 sedits, err := source.FromProtocolEdits(m, docEdits.Edits)
978                 if err != nil {
979                         return nil, err
980                 }
981                 res[uri] = applyEdits(res[uri], sedits)
982         }
983         return res, nil
984 }
985
986 func applyEdits(contents string, edits []diff.TextEdit) string {
987         res := contents
988
989         // Apply the edits from the end of the file forward
990         // to preserve the offsets
991         for i := len(edits) - 1; i >= 0; i-- {
992                 edit := edits[i]
993                 start := edit.Span.Start().Offset()
994                 end := edit.Span.End().Offset()
995                 tmp := res[0:start] + edit.NewText
996                 res = tmp + res[end:]
997         }
998         return res
999 }
1000
1001 func (r *runner) Symbols(t *testing.T, uri span.URI, expectedSymbols []protocol.DocumentSymbol) {
1002         params := &protocol.DocumentSymbolParams{
1003                 TextDocument: protocol.TextDocumentIdentifier{
1004                         URI: protocol.URIFromSpanURI(uri),
1005                 },
1006         }
1007         got, err := r.server.DocumentSymbol(r.ctx, params)
1008         if err != nil {
1009                 t.Fatal(err)
1010         }
1011         if len(got) != len(expectedSymbols) {
1012                 t.Errorf("want %d top-level symbols in %v, got %d", len(expectedSymbols), uri, len(got))
1013                 return
1014         }
1015         symbols := make([]protocol.DocumentSymbol, len(got))
1016         for i, s := range got {
1017                 s, ok := s.(protocol.DocumentSymbol)
1018                 if !ok {
1019                         t.Fatalf("%v: wanted []DocumentSymbols but got %v", uri, got)
1020                 }
1021                 symbols[i] = s
1022         }
1023         if diff := tests.DiffSymbols(t, uri, expectedSymbols, symbols); diff != "" {
1024                 t.Error(diff)
1025         }
1026 }
1027
1028 func (r *runner) WorkspaceSymbols(t *testing.T, uri span.URI, query string, typ tests.WorkspaceSymbolsTestType) {
1029         r.callWorkspaceSymbols(t, uri, query, typ)
1030 }
1031
1032 func (r *runner) callWorkspaceSymbols(t *testing.T, uri span.URI, query string, typ tests.WorkspaceSymbolsTestType) {
1033         t.Helper()
1034
1035         matcher := tests.WorkspaceSymbolsTestTypeToMatcher(typ)
1036
1037         original := r.server.session.Options()
1038         modified := original
1039         modified.SymbolMatcher = matcher
1040         r.server.session.SetOptions(modified)
1041         defer r.server.session.SetOptions(original)
1042
1043         params := &protocol.WorkspaceSymbolParams{
1044                 Query: query,
1045         }
1046         gotSymbols, err := r.server.Symbol(r.ctx, params)
1047         if err != nil {
1048                 t.Fatal(err)
1049         }
1050         got, err := tests.WorkspaceSymbolsString(r.ctx, r.data, uri, gotSymbols)
1051         if err != nil {
1052                 t.Fatal(err)
1053         }
1054         got = filepath.ToSlash(tests.Normalize(got, r.normalizers))
1055         want := string(r.data.Golden(fmt.Sprintf("workspace_symbol-%s-%s", strings.ToLower(string(matcher)), query), uri.Filename(), func() ([]byte, error) {
1056                 return []byte(got), nil
1057         }))
1058         if diff := tests.Diff(t, want, got); diff != "" {
1059                 t.Error(diff)
1060         }
1061 }
1062
1063 func (r *runner) SignatureHelp(t *testing.T, spn span.Span, want *protocol.SignatureHelp) {
1064         m, err := r.data.Mapper(spn.URI())
1065         if err != nil {
1066                 t.Fatal(err)
1067         }
1068         loc, err := m.Location(spn)
1069         if err != nil {
1070                 t.Fatalf("failed for %v: %v", loc, err)
1071         }
1072         tdpp := protocol.TextDocumentPositionParams{
1073                 TextDocument: protocol.TextDocumentIdentifier{
1074                         URI: protocol.URIFromSpanURI(spn.URI()),
1075                 },
1076                 Position: loc.Range.Start,
1077         }
1078         params := &protocol.SignatureHelpParams{
1079                 TextDocumentPositionParams: tdpp,
1080         }
1081         got, err := r.server.SignatureHelp(r.ctx, params)
1082         if err != nil {
1083                 // Only fail if we got an error we did not expect.
1084                 if want != nil {
1085                         t.Fatal(err)
1086                 }
1087                 return
1088         }
1089         if want == nil {
1090                 if got != nil {
1091                         t.Errorf("expected no signature, got %v", got)
1092                 }
1093                 return
1094         }
1095         if got == nil {
1096                 t.Fatalf("expected %v, got nil", want)
1097         }
1098         diff, err := tests.DiffSignatures(spn, want, got)
1099         if err != nil {
1100                 t.Fatal(err)
1101         }
1102         if diff != "" {
1103                 t.Error(diff)
1104         }
1105 }
1106
1107 func (r *runner) Link(t *testing.T, uri span.URI, wantLinks []tests.Link) {
1108         m, err := r.data.Mapper(uri)
1109         if err != nil {
1110                 t.Fatal(err)
1111         }
1112         got, err := r.server.DocumentLink(r.ctx, &protocol.DocumentLinkParams{
1113                 TextDocument: protocol.TextDocumentIdentifier{
1114                         URI: protocol.URIFromSpanURI(uri),
1115                 },
1116         })
1117         if err != nil {
1118                 t.Fatal(err)
1119         }
1120         if diff := tests.DiffLinks(m, wantLinks, got); diff != "" {
1121                 t.Error(diff)
1122         }
1123 }
1124
1125 func TestBytesOffset(t *testing.T) {
1126         tests := []struct {
1127                 text string
1128                 pos  protocol.Position
1129                 want int
1130         }{
1131                 {text: `a𐐀b`, pos: protocol.Position{Line: 0, Character: 0}, want: 0},
1132                 {text: `a𐐀b`, pos: protocol.Position{Line: 0, Character: 1}, want: 1},
1133                 {text: `a𐐀b`, pos: protocol.Position{Line: 0, Character: 2}, want: 1},
1134                 {text: `a𐐀b`, pos: protocol.Position{Line: 0, Character: 3}, want: 5},
1135                 {text: `a𐐀b`, pos: protocol.Position{Line: 0, Character: 4}, want: 6},
1136                 {text: `a𐐀b`, pos: protocol.Position{Line: 0, Character: 5}, want: -1},
1137                 {text: "aaa\nbbb\n", pos: protocol.Position{Line: 0, Character: 3}, want: 3},
1138                 {text: "aaa\nbbb\n", pos: protocol.Position{Line: 0, Character: 4}, want: 3},
1139                 {text: "aaa\nbbb\n", pos: protocol.Position{Line: 1, Character: 0}, want: 4},
1140                 {text: "aaa\nbbb\n", pos: protocol.Position{Line: 1, Character: 3}, want: 7},
1141                 {text: "aaa\nbbb\n", pos: protocol.Position{Line: 1, Character: 4}, want: 7},
1142                 {text: "aaa\nbbb\n", pos: protocol.Position{Line: 2, Character: 0}, want: 8},
1143                 {text: "aaa\nbbb\n", pos: protocol.Position{Line: 2, Character: 1}, want: -1},
1144                 {text: "aaa\nbbb\n\n", pos: protocol.Position{Line: 2, Character: 0}, want: 8},
1145         }
1146
1147         for i, test := range tests {
1148                 fname := fmt.Sprintf("test %d", i)
1149                 fset := token.NewFileSet()
1150                 f := fset.AddFile(fname, -1, len(test.text))
1151                 f.SetLinesForContent([]byte(test.text))
1152                 uri := span.URIFromPath(fname)
1153                 converter := span.NewContentConverter(fname, []byte(test.text))
1154                 mapper := &protocol.ColumnMapper{
1155                         URI:       uri,
1156                         Converter: converter,
1157                         Content:   []byte(test.text),
1158                 }
1159                 got, err := mapper.Point(test.pos)
1160                 if err != nil && test.want != -1 {
1161                         t.Errorf("unexpected error: %v", err)
1162                 }
1163                 if err == nil && got.Offset() != test.want {
1164                         t.Errorf("want %d for %q(Line:%d,Character:%d), but got %d", test.want, test.text, int(test.pos.Line), int(test.pos.Character), got.Offset())
1165                 }
1166         }
1167 }
1168
1169 func (r *runner) collectDiagnostics(view source.View) {
1170         if r.diagnostics != nil {
1171                 return
1172         }
1173         r.diagnostics = make(map[span.URI][]*source.Diagnostic)
1174
1175         snapshot, release := view.Snapshot(r.ctx)
1176         defer release()
1177
1178         // Always run diagnostics with analysis.
1179         r.server.diagnose(r.ctx, snapshot, true)
1180         for uri, reports := range r.server.diagnostics {
1181                 var diagnostics []*source.Diagnostic
1182                 for _, report := range reports.reports {
1183                         for _, d := range report.diags {
1184                                 diagnostics = append(diagnostics, &source.Diagnostic{
1185                                         Range:    d.Range,
1186                                         Message:  d.Message,
1187                                         Related:  d.Related,
1188                                         Severity: d.Severity,
1189                                         Source:   d.Source,
1190                                         Tags:     d.Tags,
1191                                 })
1192                         }
1193                         r.diagnostics[uri] = diagnostics
1194                 }
1195         }
1196 }