github.com/v2fly/tools@v0.100.0/internal/lsp/source/source_test.go (about)

     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  	"github.com/v2fly/tools/internal/lsp/cache"
    18  	"github.com/v2fly/tools/internal/lsp/diff"
    19  	"github.com/v2fly/tools/internal/lsp/diff/myers"
    20  	"github.com/v2fly/tools/internal/lsp/fuzzy"
    21  	"github.com/v2fly/tools/internal/lsp/protocol"
    22  	"github.com/v2fly/tools/internal/lsp/source"
    23  	"github.com/v2fly/tools/internal/lsp/source/completion"
    24  	"github.com/v2fly/tools/internal/lsp/tests"
    25  	"github.com/v2fly/tools/internal/span"
    26  	"github.com/v2fly/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(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  		opts.LiteralCompletions = strings.Contains(string(src.URI()), "literal")
   181  		opts.ExperimentalPostfixCompletions = strings.Contains(string(src.URI()), "postfix")
   182  	})
   183  	got = tests.FilterBuiltins(src, got)
   184  	if diff := tests.DiffCompletionItems(want, got); diff != "" {
   185  		t.Errorf("%s: %s", src, diff)
   186  	}
   187  }
   188  
   189  func (r *runner) CompletionSnippet(t *testing.T, src span.Span, expected tests.CompletionSnippet, placeholders bool, items tests.CompletionItems) {
   190  	_, list := r.callCompletion(t, src, func(opts *source.Options) {
   191  		opts.UsePlaceholders = placeholders
   192  		opts.DeepCompletion = true
   193  		opts.CompleteUnimported = false
   194  	})
   195  	got := tests.FindItem(list, *items[expected.CompletionItem])
   196  	want := expected.PlainSnippet
   197  	if placeholders {
   198  		want = expected.PlaceholderSnippet
   199  	}
   200  	if diff := tests.DiffSnippets(want, got); diff != "" {
   201  		t.Errorf("%s: %s", src, diff)
   202  	}
   203  }
   204  
   205  func (r *runner) UnimportedCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
   206  	var want []protocol.CompletionItem
   207  	for _, pos := range test.CompletionItems {
   208  		want = append(want, tests.ToProtocolCompletionItem(*items[pos]))
   209  	}
   210  	_, got := r.callCompletion(t, src, func(opts *source.Options) {})
   211  	got = tests.FilterBuiltins(src, got)
   212  	if diff := tests.CheckCompletionOrder(want, got, false); diff != "" {
   213  		t.Errorf("%s: %s", src, diff)
   214  	}
   215  }
   216  
   217  func (r *runner) DeepCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
   218  	var want []protocol.CompletionItem
   219  	for _, pos := range test.CompletionItems {
   220  		want = append(want, tests.ToProtocolCompletionItem(*items[pos]))
   221  	}
   222  	prefix, list := r.callCompletion(t, src, func(opts *source.Options) {
   223  		opts.DeepCompletion = true
   224  		opts.Matcher = source.CaseInsensitive
   225  		opts.CompleteUnimported = false
   226  	})
   227  	list = tests.FilterBuiltins(src, list)
   228  	fuzzyMatcher := fuzzy.NewMatcher(prefix)
   229  	var got []protocol.CompletionItem
   230  	for _, item := range list {
   231  		if fuzzyMatcher.Score(item.Label) <= 0 {
   232  			continue
   233  		}
   234  		got = append(got, item)
   235  	}
   236  	if msg := tests.DiffCompletionItems(want, got); msg != "" {
   237  		t.Errorf("%s: %s", src, msg)
   238  	}
   239  }
   240  
   241  func (r *runner) FuzzyCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
   242  	var want []protocol.CompletionItem
   243  	for _, pos := range test.CompletionItems {
   244  		want = append(want, tests.ToProtocolCompletionItem(*items[pos]))
   245  	}
   246  	_, got := r.callCompletion(t, src, func(opts *source.Options) {
   247  		opts.DeepCompletion = true
   248  		opts.Matcher = source.Fuzzy
   249  		opts.CompleteUnimported = false
   250  	})
   251  	got = tests.FilterBuiltins(src, got)
   252  	if msg := tests.DiffCompletionItems(want, got); msg != "" {
   253  		t.Errorf("%s: %s", src, msg)
   254  	}
   255  }
   256  
   257  func (r *runner) CaseSensitiveCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
   258  	var want []protocol.CompletionItem
   259  	for _, pos := range test.CompletionItems {
   260  		want = append(want, tests.ToProtocolCompletionItem(*items[pos]))
   261  	}
   262  	_, list := r.callCompletion(t, src, func(opts *source.Options) {
   263  		opts.Matcher = source.CaseSensitive
   264  		opts.CompleteUnimported = false
   265  	})
   266  	list = tests.FilterBuiltins(src, list)
   267  	if diff := tests.DiffCompletionItems(want, list); diff != "" {
   268  		t.Errorf("%s: %s", src, diff)
   269  	}
   270  }
   271  
   272  func (r *runner) RankCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
   273  	var want []protocol.CompletionItem
   274  	for _, pos := range test.CompletionItems {
   275  		want = append(want, tests.ToProtocolCompletionItem(*items[pos]))
   276  	}
   277  	_, got := r.callCompletion(t, src, func(opts *source.Options) {
   278  		opts.DeepCompletion = true
   279  		opts.Matcher = source.Fuzzy
   280  		opts.ExperimentalPostfixCompletions = true
   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:      uint32(src.Start().Line() - 1),
   306  		Character: uint32(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 diff := tests.Diff(t, want, got); diff != "" {
   380  			t.Errorf("%s: foldingRanges failed for %s, diff:\n%v", tag, uri.Filename(), diff)
   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 diff := tests.Diff(t, want, got); diff != "" {
   407  				t.Errorf("%s: failed for %s, diff:\n%v", tag, uri.Filename(), diff)
   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: uint32(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  
   935  func (r *runner) SuggestedFix(t *testing.T, spn span.Span, actionKinds []string, expectedActions int) {
   936  }
   937  func (r *runner) FunctionExtraction(t *testing.T, start span.Span, end span.Span) {}
   938  func (r *runner) CodeLens(t *testing.T, uri span.URI, want []protocol.CodeLens)   {}
   939  
   940  func spanToRange(data *tests.Data, spn span.Span) (*protocol.ColumnMapper, protocol.Range, error) {
   941  	m, err := data.Mapper(spn.URI())
   942  	if err != nil {
   943  		return nil, protocol.Range{}, err
   944  	}
   945  	srcRng, err := m.Range(spn)
   946  	if err != nil {
   947  		return nil, protocol.Range{}, err
   948  	}
   949  	return m, srcRng, nil
   950  }