.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.0 / internal / lsp / source / source_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 source_test
6
7 import (
8         "context"
9         "fmt"
10         "os"
11         "os/exec"
12         "path/filepath"
13         "sort"
14         "strings"
15         "testing"
16
17         "golang.org/x/tools/internal/lsp/cache"
18         "golang.org/x/tools/internal/lsp/diff"
19         "golang.org/x/tools/internal/lsp/diff/myers"
20         "golang.org/x/tools/internal/lsp/fuzzy"
21         "golang.org/x/tools/internal/lsp/protocol"
22         "golang.org/x/tools/internal/lsp/source"
23         "golang.org/x/tools/internal/lsp/source/completion"
24         "golang.org/x/tools/internal/lsp/tests"
25         "golang.org/x/tools/internal/span"
26         "golang.org/x/tools/internal/testenv"
27         errors "golang.org/x/xerrors"
28 )
29
30 func TestMain(m *testing.M) {
31         testenv.ExitIfSmallMachine()
32         os.Exit(m.Run())
33 }
34
35 func TestSource(t *testing.T) {
36         tests.RunTests(t, "../testdata", true, testSource)
37 }
38
39 type runner struct {
40         snapshot    source.Snapshot
41         view        source.View
42         data        *tests.Data
43         ctx         context.Context
44         normalizers []tests.Normalizer
45 }
46
47 func testSource(t *testing.T, datum *tests.Data) {
48         ctx := tests.Context(t)
49
50         cache := cache.New(ctx, nil)
51         session := cache.NewSession(ctx)
52         options := source.DefaultOptions().Clone()
53         tests.DefaultOptions(options)
54         options.SetEnvSlice(datum.Config.Env)
55         view, _, release, err := session.NewView(ctx, "source_test", span.URIFromPath(datum.Config.Dir), "", options)
56         release()
57         if err != nil {
58                 t.Fatal(err)
59         }
60         defer view.Shutdown(ctx)
61
62         // Enable type error analyses for tests.
63         // TODO(golang/go#38212): Delete this once they are enabled by default.
64         tests.EnableAllAnalyzers(view, options)
65         view.SetOptions(ctx, options)
66
67         var modifications []source.FileModification
68         for filename, content := range datum.Config.Overlay {
69                 kind := source.DetectLanguage("", filename)
70                 if kind != source.Go {
71                         continue
72                 }
73                 modifications = append(modifications, source.FileModification{
74                         URI:        span.URIFromPath(filename),
75                         Action:     source.Open,
76                         Version:    -1,
77                         Text:       content,
78                         LanguageID: "go",
79                 })
80         }
81         if err := session.ModifyFiles(ctx, modifications); err != nil {
82                 t.Fatal(err)
83         }
84         snapshot, release := view.Snapshot(ctx)
85         defer release()
86         r := &runner{
87                 view:        view,
88                 snapshot:    snapshot,
89                 data:        datum,
90                 ctx:         ctx,
91                 normalizers: tests.CollectNormalizers(datum.Exported),
92         }
93         tests.Run(t, r, datum)
94 }
95
96 func (r *runner) CallHierarchy(t *testing.T, spn span.Span, expectedCalls *tests.CallHierarchyResult) {
97         mapper, err := r.data.Mapper(spn.URI())
98         if err != nil {
99                 t.Fatal(err)
100         }
101         loc, err := mapper.Location(spn)
102         if err != nil {
103                 t.Fatalf("failed for %v: %v", spn, err)
104         }
105         fh, err := r.snapshot.GetFile(r.ctx, spn.URI())
106         if err != nil {
107                 t.Fatal(err)
108         }
109
110         items, err := source.PrepareCallHierarchy(r.ctx, r.snapshot, fh, loc.Range.Start)
111         if err != nil {
112                 t.Fatal(err)
113         }
114         if len(items) == 0 {
115                 t.Fatalf("expected call hierarchy item to be returned for identifier at %v\n", loc.Range)
116         }
117
118         callLocation := protocol.Location{
119                 URI:   items[0].URI,
120                 Range: items[0].Range,
121         }
122         if callLocation != loc {
123                 t.Fatalf("expected source.PrepareCallHierarchy to return identifier at %v but got %v\n", loc, callLocation)
124         }
125
126         incomingCalls, err := source.IncomingCalls(r.ctx, r.snapshot, fh, loc.Range.Start)
127         if err != nil {
128                 t.Error(err)
129         }
130         var incomingCallItems []protocol.CallHierarchyItem
131         for _, item := range incomingCalls {
132                 incomingCallItems = append(incomingCallItems, item.From)
133         }
134         msg := tests.DiffCallHierarchyItems(incomingCallItems, expectedCalls.IncomingCalls)
135         if msg != "" {
136                 t.Error(fmt.Sprintf("incoming calls differ: %s", msg))
137         }
138
139         outgoingCalls, err := source.OutgoingCalls(r.ctx, r.snapshot, fh, loc.Range.Start)
140         if err != nil {
141                 t.Error(err)
142         }
143         var outgoingCallItems []protocol.CallHierarchyItem
144         for _, item := range outgoingCalls {
145                 outgoingCallItems = append(outgoingCallItems, item.To)
146         }
147         msg = tests.DiffCallHierarchyItems(outgoingCallItems, expectedCalls.OutgoingCalls)
148         if msg != "" {
149                 t.Error(fmt.Sprintf("outgoing calls differ: %s", msg))
150         }
151 }
152
153 func (r *runner) Diagnostics(t *testing.T, uri span.URI, want []*source.Diagnostic) {
154         fileID, got, err := source.FileDiagnostics(r.ctx, r.snapshot, uri)
155         if err != nil {
156                 t.Fatal(err)
157         }
158         // A special case to test that there are no diagnostics for a file.
159         if len(want) == 1 && want[0].Source == "no_diagnostics" {
160                 if len(got) != 0 {
161                         t.Errorf("expected no diagnostics for %s, got %v", uri, got)
162                 }
163                 return
164         }
165         if diff := tests.DiffDiagnostics(fileID.URI, want, got); diff != "" {
166                 t.Error(diff)
167         }
168 }
169
170 func (r *runner) Completion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
171         var want []protocol.CompletionItem
172         for _, pos := range test.CompletionItems {
173                 want = append(want, tests.ToProtocolCompletionItem(*items[pos]))
174         }
175         _, got := r.callCompletion(t, src, func(opts *source.Options) {
176                 opts.Matcher = source.CaseInsensitive
177                 opts.DeepCompletion = false
178                 opts.CompleteUnimported = false
179                 opts.InsertTextFormat = protocol.SnippetTextFormat
180                 if !strings.Contains(string(src.URI()), "literal") {
181                         opts.LiteralCompletions = false
182                 }
183         })
184         got = tests.FilterBuiltins(src, got)
185         if diff := tests.DiffCompletionItems(want, got); diff != "" {
186                 t.Errorf("%s: %s", src, diff)
187         }
188 }
189
190 func (r *runner) CompletionSnippet(t *testing.T, src span.Span, expected tests.CompletionSnippet, placeholders bool, items tests.CompletionItems) {
191         _, list := r.callCompletion(t, src, func(opts *source.Options) {
192                 opts.UsePlaceholders = placeholders
193                 opts.DeepCompletion = true
194                 opts.CompleteUnimported = false
195         })
196         got := tests.FindItem(list, *items[expected.CompletionItem])
197         want := expected.PlainSnippet
198         if placeholders {
199                 want = expected.PlaceholderSnippet
200         }
201         if diff := tests.DiffSnippets(want, got); diff != "" {
202                 t.Errorf("%s: %s", src, diff)
203         }
204 }
205
206 func (r *runner) UnimportedCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
207         var want []protocol.CompletionItem
208         for _, pos := range test.CompletionItems {
209                 want = append(want, tests.ToProtocolCompletionItem(*items[pos]))
210         }
211         _, got := r.callCompletion(t, src, func(opts *source.Options) {})
212         got = tests.FilterBuiltins(src, got)
213         if diff := tests.CheckCompletionOrder(want, got, false); diff != "" {
214                 t.Errorf("%s: %s", src, diff)
215         }
216 }
217
218 func (r *runner) DeepCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
219         var want []protocol.CompletionItem
220         for _, pos := range test.CompletionItems {
221                 want = append(want, tests.ToProtocolCompletionItem(*items[pos]))
222         }
223         prefix, list := r.callCompletion(t, src, func(opts *source.Options) {
224                 opts.DeepCompletion = true
225                 opts.Matcher = source.CaseInsensitive
226                 opts.CompleteUnimported = false
227         })
228         list = tests.FilterBuiltins(src, list)
229         fuzzyMatcher := fuzzy.NewMatcher(prefix)
230         var got []protocol.CompletionItem
231         for _, item := range list {
232                 if fuzzyMatcher.Score(item.Label) <= 0 {
233                         continue
234                 }
235                 got = append(got, item)
236         }
237         if msg := tests.DiffCompletionItems(want, got); msg != "" {
238                 t.Errorf("%s: %s", src, msg)
239         }
240 }
241
242 func (r *runner) FuzzyCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
243         var want []protocol.CompletionItem
244         for _, pos := range test.CompletionItems {
245                 want = append(want, tests.ToProtocolCompletionItem(*items[pos]))
246         }
247         _, got := r.callCompletion(t, src, func(opts *source.Options) {
248                 opts.DeepCompletion = true
249                 opts.Matcher = source.Fuzzy
250                 opts.CompleteUnimported = false
251         })
252         got = tests.FilterBuiltins(src, got)
253         if msg := tests.DiffCompletionItems(want, got); msg != "" {
254                 t.Errorf("%s: %s", src, msg)
255         }
256 }
257
258 func (r *runner) CaseSensitiveCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
259         var want []protocol.CompletionItem
260         for _, pos := range test.CompletionItems {
261                 want = append(want, tests.ToProtocolCompletionItem(*items[pos]))
262         }
263         _, list := r.callCompletion(t, src, func(opts *source.Options) {
264                 opts.Matcher = source.CaseSensitive
265                 opts.CompleteUnimported = false
266         })
267         list = tests.FilterBuiltins(src, list)
268         if diff := tests.DiffCompletionItems(want, list); diff != "" {
269                 t.Errorf("%s: %s", src, diff)
270         }
271 }
272
273 func (r *runner) RankCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
274         var want []protocol.CompletionItem
275         for _, pos := range test.CompletionItems {
276                 want = append(want, tests.ToProtocolCompletionItem(*items[pos]))
277         }
278         _, got := r.callCompletion(t, src, func(opts *source.Options) {
279                 opts.DeepCompletion = true
280                 opts.Matcher = source.Fuzzy
281         })
282         if msg := tests.CheckCompletionOrder(want, got, true); msg != "" {
283                 t.Errorf("%s: %s", src, msg)
284         }
285 }
286
287 func (r *runner) callCompletion(t *testing.T, src span.Span, options func(*source.Options)) (string, []protocol.CompletionItem) {
288         fh, err := r.snapshot.GetFile(r.ctx, src.URI())
289         if err != nil {
290                 t.Fatal(err)
291         }
292         original := r.view.Options()
293         modified := original.Clone()
294         options(modified)
295         newView, err := r.view.SetOptions(r.ctx, modified)
296         if newView != r.view {
297                 t.Fatalf("options change unexpectedly created new view")
298         }
299         if err != nil {
300                 t.Fatal(err)
301         }
302         defer r.view.SetOptions(r.ctx, original)
303
304         list, surrounding, err := completion.Completion(r.ctx, r.snapshot, fh, protocol.Position{
305                 Line:      float64(src.Start().Line() - 1),
306                 Character: float64(src.Start().Column() - 1),
307         }, protocol.CompletionContext{})
308         if err != nil && !errors.As(err, &completion.ErrIsDefinition{}) {
309                 t.Fatalf("failed for %v: %v", src, err)
310         }
311         var prefix string
312         if surrounding != nil {
313                 prefix = strings.ToLower(surrounding.Prefix())
314         }
315
316         var numDeepCompletionsSeen int
317         var items []completion.CompletionItem
318         // Apply deep completion filtering.
319         for _, item := range list {
320                 if item.Depth > 0 {
321                         if !modified.DeepCompletion {
322                                 continue
323                         }
324                         if numDeepCompletionsSeen >= completion.MaxDeepCompletions {
325                                 continue
326                         }
327                         numDeepCompletionsSeen++
328                 }
329                 items = append(items, item)
330         }
331         return prefix, tests.ToProtocolCompletionItems(items)
332 }
333
334 func (r *runner) FoldingRanges(t *testing.T, spn span.Span) {
335         uri := spn.URI()
336
337         fh, err := r.snapshot.GetFile(r.ctx, spn.URI())
338         if err != nil {
339                 t.Fatal(err)
340         }
341         data, err := fh.Read()
342         if err != nil {
343                 t.Error(err)
344                 return
345         }
346
347         // Test all folding ranges.
348         ranges, err := source.FoldingRange(r.ctx, r.snapshot, fh, false)
349         if err != nil {
350                 t.Error(err)
351                 return
352         }
353         r.foldingRanges(t, "foldingRange", uri, string(data), ranges)
354
355         // Test folding ranges with lineFoldingOnly
356         ranges, err = source.FoldingRange(r.ctx, r.snapshot, fh, true)
357         if err != nil {
358                 t.Error(err)
359                 return
360         }
361         r.foldingRanges(t, "foldingRange-lineFolding", uri, string(data), ranges)
362 }
363
364 func (r *runner) foldingRanges(t *testing.T, prefix string, uri span.URI, data string, ranges []*source.FoldingRangeInfo) {
365         t.Helper()
366         // Fold all ranges.
367         nonOverlapping := nonOverlappingRanges(t, ranges)
368         for i, rngs := range nonOverlapping {
369                 got, err := foldRanges(string(data), rngs)
370                 if err != nil {
371                         t.Error(err)
372                         continue
373                 }
374                 tag := fmt.Sprintf("%s-%d", prefix, i)
375                 want := string(r.data.Golden(tag, uri.Filename(), func() ([]byte, error) {
376                         return []byte(got), nil
377                 }))
378
379                 if want != got {
380                         t.Errorf("%s: foldingRanges failed for %s, expected:\n%v\ngot:\n%v", tag, uri.Filename(), want, got)
381                 }
382         }
383
384         // Filter by kind.
385         kinds := []protocol.FoldingRangeKind{protocol.Imports, protocol.Comment}
386         for _, kind := range kinds {
387                 var kindOnly []*source.FoldingRangeInfo
388                 for _, fRng := range ranges {
389                         if fRng.Kind == kind {
390                                 kindOnly = append(kindOnly, fRng)
391                         }
392                 }
393
394                 nonOverlapping := nonOverlappingRanges(t, kindOnly)
395                 for i, rngs := range nonOverlapping {
396                         got, err := foldRanges(string(data), rngs)
397                         if err != nil {
398                                 t.Error(err)
399                                 continue
400                         }
401                         tag := fmt.Sprintf("%s-%s-%d", prefix, kind, i)
402                         want := string(r.data.Golden(tag, uri.Filename(), func() ([]byte, error) {
403                                 return []byte(got), nil
404                         }))
405
406                         if want != got {
407                                 t.Errorf("%s: failed for %s, expected:\n%v\ngot:\n%v", tag, uri.Filename(), want, got)
408                         }
409                 }
410
411         }
412 }
413
414 func nonOverlappingRanges(t *testing.T, ranges []*source.FoldingRangeInfo) (res [][]*source.FoldingRangeInfo) {
415         for _, fRng := range ranges {
416                 setNum := len(res)
417                 for i := 0; i < len(res); i++ {
418                         canInsert := true
419                         for _, rng := range res[i] {
420                                 if conflict(t, rng, fRng) {
421                                         canInsert = false
422                                         break
423                                 }
424                         }
425                         if canInsert {
426                                 setNum = i
427                                 break
428                         }
429                 }
430                 if setNum == len(res) {
431                         res = append(res, []*source.FoldingRangeInfo{})
432                 }
433                 res[setNum] = append(res[setNum], fRng)
434         }
435         return res
436 }
437
438 func conflict(t *testing.T, a, b *source.FoldingRangeInfo) bool {
439         arng, err := a.Range()
440         if err != nil {
441                 t.Fatal(err)
442         }
443         brng, err := b.Range()
444         if err != nil {
445                 t.Fatal(err)
446         }
447         // a start position is <= b start positions
448         return protocol.ComparePosition(arng.Start, brng.Start) <= 0 && protocol.ComparePosition(arng.End, brng.Start) > 0
449 }
450
451 func foldRanges(contents string, ranges []*source.FoldingRangeInfo) (string, error) {
452         foldedText := "<>"
453         res := contents
454         // Apply the folds from the end of the file forward
455         // to preserve the offsets.
456         for i := len(ranges) - 1; i >= 0; i-- {
457                 fRange := ranges[i]
458                 spn, err := fRange.Span()
459                 if err != nil {
460                         return "", err
461                 }
462                 start := spn.Start().Offset()
463                 end := spn.End().Offset()
464
465                 tmp := res[0:start] + foldedText
466                 res = tmp + res[end:]
467         }
468         return res, nil
469 }
470
471 func (r *runner) Format(t *testing.T, spn span.Span) {
472         gofmted := string(r.data.Golden("gofmt", spn.URI().Filename(), func() ([]byte, error) {
473                 cmd := exec.Command("gofmt", spn.URI().Filename())
474                 out, _ := cmd.Output() // ignore error, sometimes we have intentionally ungofmt-able files
475                 return out, nil
476         }))
477         fh, err := r.snapshot.GetFile(r.ctx, spn.URI())
478         if err != nil {
479                 t.Fatal(err)
480         }
481         edits, err := source.Format(r.ctx, r.snapshot, fh)
482         if err != nil {
483                 if gofmted != "" {
484                         t.Error(err)
485                 }
486                 return
487         }
488         data, err := fh.Read()
489         if err != nil {
490                 t.Fatal(err)
491         }
492         m, err := r.data.Mapper(spn.URI())
493         if err != nil {
494                 t.Fatal(err)
495         }
496         diffEdits, err := source.FromProtocolEdits(m, edits)
497         if err != nil {
498                 t.Error(err)
499         }
500         got := diff.ApplyEdits(string(data), diffEdits)
501         if gofmted != got {
502                 t.Errorf("format failed for %s, expected:\n%v\ngot:\n%v", spn.URI().Filename(), gofmted, got)
503         }
504 }
505
506 func (r *runner) SemanticTokens(t *testing.T, spn span.Span) {
507         t.Skip("nothing to test in source")
508 }
509
510 func (r *runner) Import(t *testing.T, spn span.Span) {
511         fh, err := r.snapshot.GetFile(r.ctx, spn.URI())
512         if err != nil {
513                 t.Fatal(err)
514         }
515         edits, _, err := source.AllImportsFixes(r.ctx, r.snapshot, fh)
516         if err != nil {
517                 t.Error(err)
518         }
519         data, err := fh.Read()
520         if err != nil {
521                 t.Fatal(err)
522         }
523         m, err := r.data.Mapper(fh.URI())
524         if err != nil {
525                 t.Fatal(err)
526         }
527         diffEdits, err := source.FromProtocolEdits(m, edits)
528         if err != nil {
529                 t.Error(err)
530         }
531         got := diff.ApplyEdits(string(data), diffEdits)
532         want := string(r.data.Golden("goimports", spn.URI().Filename(), func() ([]byte, error) {
533                 return []byte(got), nil
534         }))
535         if want != got {
536                 d, err := myers.ComputeEdits(spn.URI(), want, got)
537                 if err != nil {
538                         t.Fatal(err)
539                 }
540                 t.Errorf("import failed for %s: %s", spn.URI().Filename(), diff.ToUnified("want", "got", want, d))
541         }
542 }
543
544 func (r *runner) Definition(t *testing.T, spn span.Span, d tests.Definition) {
545         _, srcRng, err := spanToRange(r.data, d.Src)
546         if err != nil {
547                 t.Fatal(err)
548         }
549         fh, err := r.snapshot.GetFile(r.ctx, spn.URI())
550         if err != nil {
551                 t.Fatal(err)
552         }
553         ident, err := source.Identifier(r.ctx, r.snapshot, fh, srcRng.Start)
554         if err != nil {
555                 t.Fatalf("failed for %v: %v", d.Src, err)
556         }
557         h, err := source.HoverIdentifier(r.ctx, ident)
558         if err != nil {
559                 t.Fatalf("failed for %v: %v", d.Src, err)
560         }
561         hover, err := source.FormatHover(h, r.view.Options())
562         if err != nil {
563                 t.Fatal(err)
564         }
565         rng, err := ident.Declaration.MappedRange[0].Range()
566         if err != nil {
567                 t.Fatal(err)
568         }
569         if d.IsType {
570                 rng, err = ident.Type.Range()
571                 if err != nil {
572                         t.Fatal(err)
573                 }
574                 hover = ""
575         }
576         didSomething := false
577         if hover != "" {
578                 didSomething = true
579                 tag := fmt.Sprintf("%s-hover", d.Name)
580                 expectHover := string(r.data.Golden(tag, d.Src.URI().Filename(), func() ([]byte, error) {
581                         return []byte(hover), nil
582                 }))
583                 if hover != expectHover {
584                         t.Errorf("hover for %s failed:\n%s", d.Src, tests.Diff(t, expectHover, hover))
585                 }
586         }
587         if !d.OnlyHover {
588                 didSomething = true
589                 if _, defRng, err := spanToRange(r.data, d.Def); err != nil {
590                         t.Fatal(err)
591                 } else if rng != defRng {
592                         t.Errorf("for %v got %v want %v", d.Src, rng, defRng)
593                 }
594         }
595         if !didSomething {
596                 t.Errorf("no tests ran for %s", d.Src.URI())
597         }
598 }
599
600 func (r *runner) Implementation(t *testing.T, spn span.Span, impls []span.Span) {
601         sm, err := r.data.Mapper(spn.URI())
602         if err != nil {
603                 t.Fatal(err)
604         }
605         loc, err := sm.Location(spn)
606         if err != nil {
607                 t.Fatalf("failed for %v: %v", spn, err)
608         }
609         fh, err := r.snapshot.GetFile(r.ctx, spn.URI())
610         if err != nil {
611                 t.Fatal(err)
612         }
613         locs, err := source.Implementation(r.ctx, r.snapshot, fh, loc.Range.Start)
614         if err != nil {
615                 t.Fatalf("failed for %v: %v", spn, err)
616         }
617         if len(locs) != len(impls) {
618                 t.Fatalf("got %d locations for implementation, expected %d", len(locs), len(impls))
619         }
620         var results []span.Span
621         for i := range locs {
622                 locURI := locs[i].URI.SpanURI()
623                 lm, err := r.data.Mapper(locURI)
624                 if err != nil {
625                         t.Fatal(err)
626                 }
627                 imp, err := lm.Span(locs[i])
628                 if err != nil {
629                         t.Fatalf("failed for %v: %v", locs[i], err)
630                 }
631                 results = append(results, imp)
632         }
633         // Sort results and expected to make tests deterministic.
634         sort.SliceStable(results, func(i, j int) bool {
635                 return span.Compare(results[i], results[j]) == -1
636         })
637         sort.SliceStable(impls, func(i, j int) bool {
638                 return span.Compare(impls[i], impls[j]) == -1
639         })
640         for i := range results {
641                 if results[i] != impls[i] {
642                         t.Errorf("for %dth implementation of %v got %v want %v", i, spn, results[i], impls[i])
643                 }
644         }
645 }
646
647 func (r *runner) Highlight(t *testing.T, src span.Span, locations []span.Span) {
648         ctx := r.ctx
649         m, srcRng, err := spanToRange(r.data, src)
650         if err != nil {
651                 t.Fatal(err)
652         }
653         fh, err := r.snapshot.GetFile(r.ctx, src.URI())
654         if err != nil {
655                 t.Fatal(err)
656         }
657         highlights, err := source.Highlight(ctx, r.snapshot, fh, srcRng.Start)
658         if err != nil {
659                 t.Errorf("highlight failed for %s: %v", src.URI(), err)
660         }
661         if len(highlights) != len(locations) {
662                 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))
663         }
664         // Check to make sure highlights have a valid range.
665         var results []span.Span
666         for i := range highlights {
667                 h, err := m.RangeSpan(highlights[i])
668                 if err != nil {
669                         t.Fatalf("failed for %v: %v", highlights[i], err)
670                 }
671                 results = append(results, h)
672         }
673         // Sort results to make tests deterministic since DocumentHighlight uses a map.
674         sort.SliceStable(results, func(i, j int) bool {
675                 return span.Compare(results[i], results[j]) == -1
676         })
677         // Check to make sure all the expected highlights are found.
678         for i := range results {
679                 if results[i] != locations[i] {
680                         t.Errorf("want %v, got %v\n", locations[i], results[i])
681                 }
682         }
683 }
684
685 func (r *runner) References(t *testing.T, src span.Span, itemList []span.Span) {
686         ctx := r.ctx
687         _, srcRng, err := spanToRange(r.data, src)
688         if err != nil {
689                 t.Fatal(err)
690         }
691         snapshot := r.snapshot
692         fh, err := snapshot.GetFile(r.ctx, src.URI())
693         if err != nil {
694                 t.Fatal(err)
695         }
696         for _, includeDeclaration := range []bool{true, false} {
697                 t.Run(fmt.Sprintf("refs-declaration-%v", includeDeclaration), func(t *testing.T) {
698                         want := make(map[span.Span]bool)
699                         for i, pos := range itemList {
700                                 // We don't want the first result if we aren't including the declaration.
701                                 if i == 0 && !includeDeclaration {
702                                         continue
703                                 }
704                                 want[pos] = true
705                         }
706                         refs, err := source.References(ctx, snapshot, fh, srcRng.Start, includeDeclaration)
707                         if err != nil {
708                                 t.Fatalf("failed for %s: %v", src, err)
709                         }
710                         got := make(map[span.Span]bool)
711                         for _, refInfo := range refs {
712                                 refSpan, err := refInfo.Span()
713                                 if err != nil {
714                                         t.Fatal(err)
715                                 }
716                                 got[refSpan] = true
717                         }
718                         if len(got) != len(want) {
719                                 t.Errorf("references failed: different lengths got %v want %v", len(got), len(want))
720                         }
721                         for spn := range got {
722                                 if !want[spn] {
723                                         t.Errorf("references failed: incorrect references got %v want locations %v", got, want)
724                                 }
725                         }
726                 })
727         }
728 }
729
730 func (r *runner) Rename(t *testing.T, spn span.Span, newText string) {
731         tag := fmt.Sprintf("%s-rename", newText)
732
733         _, srcRng, err := spanToRange(r.data, spn)
734         if err != nil {
735                 t.Fatal(err)
736         }
737         fh, err := r.snapshot.GetFile(r.ctx, spn.URI())
738         if err != nil {
739                 t.Fatal(err)
740         }
741         changes, err := source.Rename(r.ctx, r.snapshot, fh, srcRng.Start, newText)
742         if err != nil {
743                 renamed := string(r.data.Golden(tag, spn.URI().Filename(), func() ([]byte, error) {
744                         return []byte(err.Error()), nil
745                 }))
746                 if err.Error() != renamed {
747                         t.Errorf("rename failed for %s, expected:\n%v\ngot:\n%v\n", newText, renamed, err)
748                 }
749                 return
750         }
751
752         var res []string
753         for editURI, edits := range changes {
754                 fh, err := r.snapshot.GetFile(r.ctx, editURI)
755                 if err != nil {
756                         t.Fatal(err)
757                 }
758                 data, err := fh.Read()
759                 if err != nil {
760                         t.Fatal(err)
761                 }
762                 m, err := r.data.Mapper(fh.URI())
763                 if err != nil {
764                         t.Fatal(err)
765                 }
766                 diffEdits, err := source.FromProtocolEdits(m, edits)
767                 if err != nil {
768                         t.Fatal(err)
769                 }
770                 contents := applyEdits(string(data), diffEdits)
771                 if len(changes) > 1 {
772                         filename := filepath.Base(editURI.Filename())
773                         contents = fmt.Sprintf("%s:\n%s", filename, contents)
774                 }
775                 res = append(res, contents)
776         }
777
778         // Sort on filename
779         sort.Strings(res)
780
781         var got string
782         for i, val := range res {
783                 if i != 0 {
784                         got += "\n"
785                 }
786                 got += val
787         }
788
789         renamed := string(r.data.Golden(tag, spn.URI().Filename(), func() ([]byte, error) {
790                 return []byte(got), nil
791         }))
792
793         if renamed != got {
794                 t.Errorf("rename failed for %s, expected:\n%v\ngot:\n%v", newText, renamed, got)
795         }
796 }
797
798 func applyEdits(contents string, edits []diff.TextEdit) string {
799         res := contents
800
801         // Apply the edits from the end of the file forward
802         // to preserve the offsets
803         for i := len(edits) - 1; i >= 0; i-- {
804                 edit := edits[i]
805                 start := edit.Span.Start().Offset()
806                 end := edit.Span.End().Offset()
807                 tmp := res[0:start] + edit.NewText
808                 res = tmp + res[end:]
809         }
810         return res
811 }
812
813 func (r *runner) PrepareRename(t *testing.T, src span.Span, want *source.PrepareItem) {
814         _, srcRng, err := spanToRange(r.data, src)
815         if err != nil {
816                 t.Fatal(err)
817         }
818         // Find the identifier at the position.
819         fh, err := r.snapshot.GetFile(r.ctx, src.URI())
820         if err != nil {
821                 t.Fatal(err)
822         }
823         item, err := source.PrepareRename(r.ctx, r.snapshot, fh, srcRng.Start)
824         if err != nil {
825                 if want.Text != "" { // expected an ident.
826                         t.Errorf("prepare rename failed for %v: got error: %v", src, err)
827                 }
828                 return
829         }
830         if item == nil {
831                 if want.Text != "" {
832                         t.Errorf("prepare rename failed for %v: got nil", src)
833                 }
834                 return
835         }
836         if want.Text == "" {
837                 t.Errorf("prepare rename failed for %v: expected nil, got %v", src, item)
838                 return
839         }
840         if item.Range.Start == item.Range.End {
841                 // Special case for 0-length ranges. Marks can't specify a 0-length range,
842                 // so just compare the start.
843                 if item.Range.Start != want.Range.Start {
844                         t.Errorf("prepare rename failed: incorrect point, got %v want %v", item.Range.Start, want.Range.Start)
845                 }
846         } else {
847                 if protocol.CompareRange(item.Range, want.Range) != 0 {
848                         t.Errorf("prepare rename failed: incorrect range got %v want %v", item.Range, want.Range)
849                 }
850         }
851 }
852
853 func (r *runner) Symbols(t *testing.T, uri span.URI, expectedSymbols []protocol.DocumentSymbol) {
854         fh, err := r.snapshot.GetFile(r.ctx, uri)
855         if err != nil {
856                 t.Fatal(err)
857         }
858         symbols, err := source.DocumentSymbols(r.ctx, r.snapshot, fh)
859         if err != nil {
860                 t.Errorf("symbols failed for %s: %v", uri, err)
861         }
862         if len(symbols) != len(expectedSymbols) {
863                 t.Errorf("want %d top-level symbols in %v, got %d", len(expectedSymbols), uri, len(symbols))
864                 return
865         }
866         if diff := tests.DiffSymbols(t, uri, expectedSymbols, symbols); diff != "" {
867                 t.Error(diff)
868         }
869 }
870
871 func (r *runner) WorkspaceSymbols(t *testing.T, uri span.URI, query string, typ tests.WorkspaceSymbolsTestType) {
872         r.callWorkspaceSymbols(t, uri, query, typ)
873 }
874
875 func (r *runner) callWorkspaceSymbols(t *testing.T, uri span.URI, query string, typ tests.WorkspaceSymbolsTestType) {
876         t.Helper()
877
878         matcher := tests.WorkspaceSymbolsTestTypeToMatcher(typ)
879         gotSymbols, err := source.WorkspaceSymbols(r.ctx, matcher, r.view.Options().SymbolStyle, []source.View{r.view}, query)
880         if err != nil {
881                 t.Fatal(err)
882         }
883         got, err := tests.WorkspaceSymbolsString(r.ctx, r.data, uri, gotSymbols)
884         if err != nil {
885                 t.Fatal(err)
886         }
887         got = filepath.ToSlash(tests.Normalize(got, r.normalizers))
888         want := string(r.data.Golden(fmt.Sprintf("workspace_symbol-%s-%s", strings.ToLower(string(matcher)), query), uri.Filename(), func() ([]byte, error) {
889                 return []byte(got), nil
890         }))
891         if diff := tests.Diff(t, want, got); diff != "" {
892                 t.Error(diff)
893         }
894 }
895
896 func (r *runner) SignatureHelp(t *testing.T, spn span.Span, want *protocol.SignatureHelp) {
897         _, rng, err := spanToRange(r.data, spn)
898         if err != nil {
899                 t.Fatal(err)
900         }
901         fh, err := r.snapshot.GetFile(r.ctx, spn.URI())
902         if err != nil {
903                 t.Fatal(err)
904         }
905         gotSignature, gotActiveParameter, err := source.SignatureHelp(r.ctx, r.snapshot, fh, rng.Start)
906         if err != nil {
907                 // Only fail if we got an error we did not expect.
908                 if want != nil {
909                         t.Fatalf("failed for %v: %v", spn, err)
910                 }
911                 return
912         }
913         if gotSignature == nil {
914                 if want != nil {
915                         t.Fatalf("got nil signature, but expected %v", want)
916                 }
917                 return
918         }
919         got := &protocol.SignatureHelp{
920                 Signatures:      []protocol.SignatureInformation{*gotSignature},
921                 ActiveParameter: float64(gotActiveParameter),
922         }
923         diff, err := tests.DiffSignatures(spn, want, got)
924         if err != nil {
925                 t.Fatal(err)
926         }
927         if diff != "" {
928                 t.Error(diff)
929         }
930 }
931
932 // These are pure LSP features, no source level functionality to be tested.
933 func (r *runner) Link(t *testing.T, uri span.URI, wantLinks []tests.Link) {}
934 func (r *runner) SuggestedFix(t *testing.T, spn span.Span, actionKinds []string, expectedActions int) {
935 }
936 func (r *runner) FunctionExtraction(t *testing.T, start span.Span, end span.Span) {}
937 func (r *runner) CodeLens(t *testing.T, uri span.URI, want []protocol.CodeLens)   {}
938
939 func spanToRange(data *tests.Data, spn span.Span) (*protocol.ColumnMapper, protocol.Range, error) {
940         m, err := data.Mapper(spn.URI())
941         if err != nil {
942                 return nil, protocol.Range{}, err
943         }
944         srcRng, err := m.Range(spn)
945         if err != nil {
946                 return nil, protocol.Range{}, err
947         }
948         return m, srcRng, nil
949 }