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