internal/lsp: lift the test loops out into the testing framework

The loops are common to all the testing layers, so lift them.
This prepares for more test improvements, without any funcitonal changes.

Change-Id: Ib750c8a7bb4c424a185cb0bd841674a69db1385b
Reviewed-on: https://go-review.googlesource.com/c/tools/+/197717
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Ian Cottrell 2019-09-26 13:56:23 -04:00
parent 69890759d9
commit c7cf430b80
9 changed files with 1238 additions and 1297 deletions

View File

@ -11,15 +11,14 @@ import (
"testing"
"golang.org/x/tools/internal/lsp/cmd"
"golang.org/x/tools/internal/lsp/tests"
"golang.org/x/tools/internal/lsp/source"
"golang.org/x/tools/internal/span"
"golang.org/x/tools/internal/tool"
)
func (r *runner) Diagnostics(t *testing.T, data tests.Diagnostics) {
for uri, want := range data {
func (r *runner) Diagnostics(t *testing.T, uri span.URI, want []source.Diagnostic) {
if len(want) == 1 && want[0].Message == "" {
continue
return
}
fname := uri.Filename()
args := []string{"-remote=internal", "check", fname}
@ -66,5 +65,4 @@ func (r *runner) Diagnostics(t *testing.T, data tests.Diagnostics) {
for extra, _ := range got {
t.Errorf("extra diagnostic %q", extra)
}
}
}

View File

@ -16,7 +16,10 @@ import (
"testing"
"golang.org/x/tools/go/packages/packagestest"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/lsp/source"
"golang.org/x/tools/internal/lsp/tests"
"golang.org/x/tools/internal/span"
)
type runner struct {
@ -33,67 +36,67 @@ func NewRunner(exporter packagestest.Exporter, data *tests.Data, ctx context.Con
}
}
func (r *runner) Completion(t *testing.T, data tests.Completions, items tests.CompletionItems) {
func (r *runner) Completion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
//TODO: add command line completions tests when it works
}
func (r *runner) CompletionSnippets(t *testing.T, data tests.CompletionSnippets, items tests.CompletionItems) {
func (r *runner) CompletionSnippet(t *testing.T, src span.Span, expected tests.CompletionSnippet, placeholders bool, items tests.CompletionItems) {
//TODO: add command line completions tests when it works
}
func (r *runner) UnimportedCompletions(t *testing.T, data tests.UnimportedCompletions, items tests.CompletionItems) {
func (r *runner) UnimportedCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
//TODO: add command line completions tests when it works
}
func (r *runner) DeepCompletions(t *testing.T, data tests.DeepCompletions, items tests.CompletionItems) {
func (r *runner) DeepCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
//TODO: add command line completions tests when it works
}
func (r *runner) FuzzyCompletions(t *testing.T, data tests.FuzzyCompletions, items tests.CompletionItems) {
func (r *runner) FuzzyCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
//TODO: add command line completions tests when it works
}
func (r *runner) CaseSensitiveCompletions(t *testing.T, data tests.CaseSensitiveCompletions, items tests.CompletionItems) {
func (r *runner) CaseSensitiveCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
//TODO: add command line completions tests when it works
}
func (r *runner) RankCompletions(t *testing.T, data tests.RankCompletions, items tests.CompletionItems) {
func (r *runner) RankCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
//TODO: add command line completions tests when it works
}
func (r *runner) FoldingRange(t *testing.T, data tests.FoldingRanges) {
func (r *runner) FoldingRange(t *testing.T, spn span.Span) {
//TODO: add command line folding range tests when it works
}
func (r *runner) Highlight(t *testing.T, data tests.Highlights) {
func (r *runner) Highlight(t *testing.T, name string, locations []span.Span) {
//TODO: add command line highlight tests when it works
}
func (r *runner) Reference(t *testing.T, data tests.References) {
func (r *runner) Reference(t *testing.T, src span.Span, itemList []span.Span) {
//TODO: add command line references tests when it works
}
func (r *runner) PrepareRename(t *testing.T, data tests.PrepareRenames) {
func (r *runner) PrepareRename(t *testing.T, src span.Span, want *source.PrepareItem) {
//TODO: add command line prepare rename tests when it works
}
func (r *runner) Symbol(t *testing.T, data tests.Symbols) {
func (r *runner) Symbol(t *testing.T, uri span.URI, expectedSymbols []protocol.DocumentSymbol) {
//TODO: add command line symbol tests when it works
}
func (r *runner) SignatureHelp(t *testing.T, data tests.Signatures) {
func (r *runner) SignatureHelp(t *testing.T, spn span.Span, expectedSignature *source.SignatureInformation) {
//TODO: add command line signature tests when it works
}
func (r *runner) Link(t *testing.T, data tests.Links) {
func (r *runner) Link(t *testing.T, uri span.URI, wantLinks []tests.Link) {
//TODO: add command line link tests when it works
}
func (r *runner) Import(t *testing.T, data tests.Imports) {
func (r *runner) Import(t *testing.T, spn span.Span) {
//TODO: add command line imports tests when it works
}
func (r *runner) SuggestedFix(t *testing.T, data tests.SuggestedFixes) {
func (r *runner) SuggestedFix(t *testing.T, spn span.Span) {
//TODO: add suggested fix tests when it works
}

View File

@ -33,15 +33,14 @@ var godefModes = []godefMode{
jsonGoDef,
}
func (r *runner) Definition(t *testing.T, data tests.Definitions) {
func (r *runner) Definition(t *testing.T, spn span.Span, d tests.Definition) {
// TODO: https://golang.org/issue/32794.
if !*tests.UpdateGolden {
t.Skip()
}
for _, d := range data {
if d.IsType || d.OnlyHover {
// TODO: support type definition, hover queries
continue
return
}
d.Src = span.New(d.Src.URI(), span.NewPoint(0, 0, d.Src.Start().Offset()), span.Point{})
for _, mode := range godefModes {
@ -69,5 +68,4 @@ func (r *runner) Definition(t *testing.T, data tests.Definitions) {
t.Errorf("definition %v failed with %#v expected:\n%q\ngot:\n%q", tag, args, expect, got)
}
}
}
}

View File

@ -11,7 +11,7 @@ import (
"testing"
"golang.org/x/tools/internal/lsp/cmd"
"golang.org/x/tools/internal/lsp/tests"
"golang.org/x/tools/internal/span"
"golang.org/x/tools/internal/tool"
)
@ -20,8 +20,7 @@ var formatModes = [][]string{
[]string{"-d"},
}
func (r *runner) Format(t *testing.T, data tests.Formats) {
for _, spn := range data {
func (r *runner) Format(t *testing.T, spn span.Span) {
for _, mode := range formatModes {
tag := "gofmt" + strings.Join(mode, "")
uri := spn.URI()
@ -47,7 +46,6 @@ func (r *runner) Format(t *testing.T, data tests.Formats) {
t.Errorf("format failed with %#v expected:\n%s\ngot:\n%s", args, expect, got)
}
}
}
}
var unifiedHeader = regexp.MustCompile(`^diff -u.*\n(---\s+\S+\.go\.orig)\s+[\d-:. ]+(\n\+\+\+\s+\S+\.go)\s+[\d-:. ]+(\n@@)`)

View File

@ -6,12 +6,10 @@ package cmdtest
import (
"fmt"
"sort"
"strings"
"testing"
"golang.org/x/tools/internal/lsp/cmd"
"golang.org/x/tools/internal/lsp/tests"
"golang.org/x/tools/internal/span"
"golang.org/x/tools/internal/tool"
)
@ -21,20 +19,17 @@ var renameModes = [][]string{
[]string{"-d"},
}
func (r *runner) Rename(t *testing.T, data tests.Renames) {
sortedSpans := sortSpans(data) // run the tests in a repeatable order
for _, spn := range sortedSpans {
tag := data[spn]
func (r *runner) Rename(t *testing.T, spn span.Span, newText string) {
filename := spn.URI().Filename()
for _, mode := range renameModes {
goldenTag := data[spn] + strings.Join(mode, "") + "-rename"
goldenTag := newText + strings.Join(mode, "") + "-rename"
app := cmd.New("gopls-test", r.data.Config.Dir, r.data.Config.Env)
loc := fmt.Sprintf("%v", spn)
args := []string{"-remote=internal", "rename"}
if strings.Join(mode, "") != "" {
args = append(args, strings.Join(mode, ""))
}
args = append(args, loc, tag)
args = append(args, loc, newText)
var err error
got := CaptureStdOut(t, func() {
err = tool.Run(r.ctx, app, args)
@ -50,16 +45,4 @@ func (r *runner) Rename(t *testing.T, data tests.Renames) {
t.Errorf("rename failed with %#v expected:\n%s\ngot:\n%s", args, expect, got)
}
}
}
}
func sortSpans(data map[span.Span]string) []span.Span {
spans := make([]span.Span, 0, len(data))
for spn, _ := range data {
spans = append(spans, spn)
}
sort.Slice(spans, func(i, j int) bool {
return span.Compare(spans[i], spans[j]) < 0
})
return spans
}

View File

@ -11,8 +11,7 @@ import (
"golang.org/x/tools/internal/span"
)
func (r *runner) Completion(t *testing.T, data tests.Completions, items tests.CompletionItems) {
for src, test := range data {
func (r *runner) Completion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
got := r.callCompletion(t, src, source.CompletionOptions{
Deep: false,
FuzzyMatching: false,
@ -25,12 +24,9 @@ func (r *runner) Completion(t *testing.T, data tests.Completions, items tests.Co
if diff := tests.DiffCompletionItems(want, got); diff != "" {
t.Errorf("%s: %s", src, diff)
}
}
}
func (r *runner) CompletionSnippets(t *testing.T, data tests.CompletionSnippets, items tests.CompletionItems) {
for _, placeholders := range []bool{true, false} {
for src, expected := range data {
func (r *runner) CompletionSnippet(t *testing.T, src span.Span, expected tests.CompletionSnippet, placeholders bool, items tests.CompletionItems) {
list := r.callCompletion(t, src, source.CompletionOptions{
Placeholders: placeholders,
Deep: true,
@ -45,12 +41,9 @@ func (r *runner) CompletionSnippets(t *testing.T, data tests.CompletionSnippets,
if diff := tests.DiffSnippets(want, got); diff != "" {
t.Errorf("%s: %v", src, diff)
}
}
}
}
func (r *runner) UnimportedCompletions(t *testing.T, data tests.UnimportedCompletions, items tests.CompletionItems) {
for src, test := range data {
func (r *runner) UnimportedCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
got := r.callCompletion(t, src, source.CompletionOptions{
Unimported: true,
})
@ -61,11 +54,9 @@ func (r *runner) UnimportedCompletions(t *testing.T, data tests.UnimportedComple
if diff := tests.DiffCompletionItems(want, got); diff != "" {
t.Errorf("%s: %s", src, diff)
}
}
}
func (r *runner) DeepCompletions(t *testing.T, data tests.DeepCompletions, items tests.CompletionItems) {
for src, test := range data {
func (r *runner) DeepCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
got := r.callCompletion(t, src, source.CompletionOptions{
Deep: true,
Budget: 5 * time.Second,
@ -78,11 +69,9 @@ func (r *runner) DeepCompletions(t *testing.T, data tests.DeepCompletions, items
if msg := tests.DiffCompletionItems(want, got); msg != "" {
t.Errorf("%s: %s", src, msg)
}
}
}
func (r *runner) FuzzyCompletions(t *testing.T, data tests.FuzzyCompletions, items tests.CompletionItems) {
for src, test := range data {
func (r *runner) FuzzyCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
got := r.callCompletion(t, src, source.CompletionOptions{
FuzzyMatching: true,
Deep: true,
@ -95,11 +84,9 @@ func (r *runner) FuzzyCompletions(t *testing.T, data tests.FuzzyCompletions, ite
if msg := tests.DiffCompletionItems(want, got); msg != "" {
t.Errorf("%s: %s", src, msg)
}
}
}
func (r *runner) CaseSensitiveCompletions(t *testing.T, data tests.CaseSensitiveCompletions, items tests.CompletionItems) {
for src, test := range data {
func (r *runner) CaseSensitiveCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
got := r.callCompletion(t, src, source.CompletionOptions{
CaseSensitive: true,
})
@ -110,11 +97,9 @@ func (r *runner) CaseSensitiveCompletions(t *testing.T, data tests.CaseSensitive
if msg := tests.DiffCompletionItems(want, got); msg != "" {
t.Errorf("%s: %s", src, msg)
}
}
}
func (r *runner) RankCompletions(t *testing.T, data tests.RankCompletions, items tests.CompletionItems) {
for src, test := range data {
func (r *runner) RankCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
got := r.callCompletion(t, src, source.CompletionOptions{
FuzzyMatching: true,
Deep: true,
@ -124,7 +109,6 @@ func (r *runner) RankCompletions(t *testing.T, data tests.RankCompletions, items
if msg := tests.CheckCompletionOrder(want, got); msg != "" {
t.Errorf("%s: %s", src, msg)
}
}
}
func expected(t *testing.T, test tests.Completion, items tests.CompletionItems) []protocol.CompletionItem {

View File

@ -70,9 +70,8 @@ func testLSP(t *testing.T, exporter packagestest.Exporter) {
}
// TODO: Actually test the LSP diagnostics function in this test.
func (r *runner) Diagnostics(t *testing.T, data tests.Diagnostics) {
func (r *runner) Diagnostics(t *testing.T, uri span.URI, want []source.Diagnostic) {
v := r.server.session.View(viewName)
for uri, want := range data {
f, err := v.GetFile(r.ctx, uri)
if err != nil {
t.Fatalf("no file for %s: %v", f, err)
@ -91,16 +90,14 @@ func (r *runner) Diagnostics(t *testing.T, data tests.Diagnostics) {
if len(got) != 0 {
t.Errorf("expected no diagnostics for %s, got %v", uri, got)
}
continue
return
}
if diff := tests.DiffDiagnostics(uri, want, got); diff != "" {
t.Error(diff)
}
}
}
func (r *runner) FoldingRange(t *testing.T, data tests.FoldingRanges) {
for _, spn := range data {
func (r *runner) FoldingRange(t *testing.T, spn span.Span) {
uri := spn.URI()
view := r.server.session.ViewOf(uri)
original := view.Options()
@ -116,7 +113,7 @@ func (r *runner) FoldingRange(t *testing.T, data tests.FoldingRanges) {
})
if err != nil {
t.Error(err)
continue
return
}
r.foldingRanges(t, "foldingRange", uri, ranges)
@ -130,11 +127,10 @@ func (r *runner) FoldingRange(t *testing.T, data tests.FoldingRanges) {
})
if err != nil {
t.Error(err)
continue
return
}
r.foldingRanges(t, "foldingRange-lineFolding", uri, ranges)
view.SetOptions(original)
}
}
func (r *runner) foldingRanges(t *testing.T, prefix string, uri span.URI, ranges []protocol.FoldingRange) {
@ -249,8 +245,7 @@ func foldRanges(m *protocol.ColumnMapper, contents string, ranges []protocol.Fol
return res, nil
}
func (r *runner) Format(t *testing.T, data tests.Formats) {
for _, spn := range data {
func (r *runner) Format(t *testing.T, spn span.Span) {
uri := spn.URI()
filename := uri.Filename()
gofmted := string(r.data.Golden("gofmt", filename, func() ([]byte, error) {
@ -268,7 +263,7 @@ func (r *runner) Format(t *testing.T, data tests.Formats) {
if gofmted != "" {
t.Error(err)
}
continue
return
}
m, err := r.data.Mapper(uri)
if err != nil {
@ -282,11 +277,9 @@ func (r *runner) Format(t *testing.T, data tests.Formats) {
if gofmted != got {
t.Errorf("format failed for %s, expected:\n%v\ngot:\n%v", filename, gofmted, got)
}
}
}
func (r *runner) Import(t *testing.T, data tests.Imports) {
for _, spn := range data {
func (r *runner) Import(t *testing.T, spn span.Span) {
uri := spn.URI()
filename := uri.Filename()
goimported := string(r.data.Golden("goimports", filename, func() ([]byte, error) {
@ -304,7 +297,7 @@ func (r *runner) Import(t *testing.T, data tests.Imports) {
if goimported != "" {
t.Error(err)
}
continue
return
}
m, err := r.data.Mapper(uri)
if err != nil {
@ -324,11 +317,9 @@ func (r *runner) Import(t *testing.T, data tests.Imports) {
if goimported != got {
t.Errorf("import failed for %s, expected:\n%v\ngot:\n%v", filename, goimported, got)
}
}
}
func (r *runner) SuggestedFix(t *testing.T, data tests.SuggestedFixes) {
for _, spn := range data {
func (r *runner) SuggestedFix(t *testing.T, spn span.Span) {
uri := spn.URI()
filename := uri.Filename()
view := r.server.session.ViewOf(uri)
@ -351,7 +342,7 @@ func (r *runner) SuggestedFix(t *testing.T, data tests.SuggestedFixes) {
})
if err != nil {
t.Error(err)
continue
return
}
m, err := r.data.Mapper(f.URI())
if err != nil {
@ -374,11 +365,9 @@ func (r *runner) SuggestedFix(t *testing.T, data tests.SuggestedFixes) {
if fixed != got {
t.Errorf("suggested fixes failed for %s, expected:\n%v\ngot:\n%v", filename, fixed, got)
}
}
}
func (r *runner) Definition(t *testing.T, data tests.Definitions) {
for _, d := range data {
func (r *runner) Definition(t *testing.T, spn span.Span, d tests.Definition) {
sm, err := r.data.Mapper(d.Src.URI())
if err != nil {
t.Fatal(err)
@ -439,11 +428,9 @@ func (r *runner) Definition(t *testing.T, data tests.Definitions) {
} else {
t.Errorf("no tests ran for %s", d.Src.URI())
}
}
}
func (r *runner) Highlight(t *testing.T, data tests.Highlights) {
for name, locations := range data {
func (r *runner) Highlight(t *testing.T, name string, locations []span.Span) {
m, err := r.data.Mapper(locations[0].URI())
if err != nil {
t.Fatal(err)
@ -473,11 +460,9 @@ func (r *runner) Highlight(t *testing.T, data tests.Highlights) {
t.Errorf("want %v, got %v\n", locations[i], h)
}
}
}
}
func (r *runner) Reference(t *testing.T, data tests.References) {
for src, itemList := range data {
func (r *runner) Reference(t *testing.T, src span.Span, itemList []span.Span) {
sm, err := r.data.Mapper(src.URI())
if err != nil {
t.Fatal(err)
@ -517,11 +502,9 @@ func (r *runner) Reference(t *testing.T, data tests.References) {
t.Errorf("references failed: incorrect references got %v want %v", loc, want)
}
}
}
}
func (r *runner) Rename(t *testing.T, data tests.Renames) {
for spn, newText := range data {
func (r *runner) Rename(t *testing.T, spn span.Span, newText string) {
tag := fmt.Sprintf("%s-rename", newText)
uri := spn.URI()
@ -549,7 +532,7 @@ func (r *runner) Rename(t *testing.T, data tests.Renames) {
if err.Error() != renamed {
t.Errorf("rename failed for %s, expected:\n%v\ngot:\n%v\n", newText, renamed, err)
}
continue
return
}
var res []string
@ -588,11 +571,9 @@ func (r *runner) Rename(t *testing.T, data tests.Renames) {
if renamed != got {
t.Errorf("rename failed for %s, expected:\n%v\ngot:\n%v", newText, renamed, got)
}
}
}
func (r *runner) PrepareRename(t *testing.T, data tests.PrepareRenames) {
for src, want := range data {
func (r *runner) PrepareRename(t *testing.T, src span.Span, want *source.PrepareItem) {
m, err := r.data.Mapper(src.URI())
if err != nil {
t.Fatal(err)
@ -611,18 +592,17 @@ func (r *runner) PrepareRename(t *testing.T, data tests.PrepareRenames) {
got, err := r.server.PrepareRename(context.Background(), params)
if err != nil {
t.Errorf("prepare rename failed for %v: got error: %v", src, err)
continue
return
}
if got == nil {
if want.Text != "" { // expected an ident.
t.Errorf("prepare rename failed for %v: got nil", src)
}
continue
return
}
if protocol.CompareRange(*got, want.Range) != 0 {
t.Errorf("prepare rename failed: incorrect range got %v want %v", *got, want.Range)
}
}
}
func applyEdits(contents string, edits []diff.TextEdit) string {
@ -640,8 +620,7 @@ func applyEdits(contents string, edits []diff.TextEdit) string {
return res
}
func (r *runner) Symbol(t *testing.T, data tests.Symbols) {
for uri, expectedSymbols := range data {
func (r *runner) Symbol(t *testing.T, uri span.URI, expectedSymbols []protocol.DocumentSymbol) {
params := &protocol.DocumentSymbolParams{
TextDocument: protocol.TextDocumentIdentifier{
URI: string(uri),
@ -654,12 +633,11 @@ func (r *runner) Symbol(t *testing.T, data tests.Symbols) {
if len(symbols) != len(expectedSymbols) {
t.Errorf("want %d top-level symbols in %v, got %d", len(expectedSymbols), uri, len(symbols))
continue
return
}
if diff := r.diffSymbols(t, uri, expectedSymbols, symbols); diff != "" {
t.Error(diff)
}
}
}
func (r *runner) diffSymbols(t *testing.T, uri span.URI, want []protocol.DocumentSymbol, got []protocol.DocumentSymbol) string {
@ -705,8 +683,7 @@ func summarizeSymbols(t *testing.T, i int, want, got []protocol.DocumentSymbol,
return msg.String()
}
func (r *runner) SignatureHelp(t *testing.T, data tests.Signatures) {
for spn, expectedSignatures := range data {
func (r *runner) SignatureHelp(t *testing.T, spn span.Span, expectedSignature *source.SignatureInformation) {
m, err := r.data.Mapper(spn.URI())
if err != nil {
t.Fatal(err)
@ -727,24 +704,23 @@ func (r *runner) SignatureHelp(t *testing.T, data tests.Signatures) {
gotSignatures, err := r.server.SignatureHelp(r.ctx, params)
if err != nil {
// Only fail if we got an error we did not expect.
if expectedSignatures != nil {
if expectedSignature != nil {
t.Fatal(err)
}
continue
return
}
if expectedSignatures == nil {
if expectedSignature == nil {
if gotSignatures != nil {
t.Errorf("expected no signature, got %v", gotSignatures)
}
continue
return
}
if gotSignatures == nil {
t.Fatalf("expected %v, got nil", expectedSignatures)
t.Fatalf("expected %v, got nil", expectedSignature)
}
if diff := diffSignatures(spn, expectedSignatures, gotSignatures); diff != "" {
if diff := diffSignatures(spn, expectedSignature, gotSignatures); diff != "" {
t.Error(diff)
}
}
}
func diffSignatures(spn span.Span, want *source.SignatureInformation, got *protocol.SignatureHelp) string {
@ -782,8 +758,7 @@ func diffSignatures(spn span.Span, want *source.SignatureInformation, got *proto
return ""
}
func (r *runner) Link(t *testing.T, data tests.Links) {
for uri, wantLinks := range data {
func (r *runner) Link(t *testing.T, uri span.URI, wantLinks []tests.Link) {
m, err := r.data.Mapper(uri)
if err != nil {
t.Fatal(err)
@ -832,7 +807,6 @@ func (r *runner) Link(t *testing.T, data tests.Links) {
for spn, target := range links {
t.Errorf("missing link %v:%v\n", spn, target)
}
}
}
func TestBytesOffset(t *testing.T) {

View File

@ -63,8 +63,7 @@ func testSource(t *testing.T, exporter packagestest.Exporter) {
tests.Run(t, r, data)
}
func (r *runner) Diagnostics(t *testing.T, data tests.Diagnostics) {
for uri, want := range data {
func (r *runner) Diagnostics(t *testing.T, uri span.URI, want []source.Diagnostic) {
f, err := r.view.GetFile(r.ctx, uri)
if err != nil {
t.Fatal(err)
@ -79,16 +78,14 @@ func (r *runner) Diagnostics(t *testing.T, data tests.Diagnostics) {
if len(got) != 0 {
t.Errorf("expected no diagnostics for %s, got %v", uri, got)
}
continue
return
}
if diff := tests.DiffDiagnostics(uri, want, got); diff != "" {
t.Error(diff)
}
}
}
func (r *runner) Completion(t *testing.T, data tests.Completions, items tests.CompletionItems) {
for src, test := range data {
func (r *runner) Completion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
var want []protocol.CompletionItem
for _, pos := range test.CompletionItems {
want = append(want, tests.ToProtocolCompletionItem(*items[pos]))
@ -110,12 +107,9 @@ func (r *runner) Completion(t *testing.T, data tests.Completions, items tests.Co
if diff := tests.DiffCompletionItems(want, got); diff != "" {
t.Errorf("%s: %s", src, diff)
}
}
}
func (r *runner) CompletionSnippets(t *testing.T, data tests.CompletionSnippets, items tests.CompletionItems) {
for _, placeholders := range []bool{true, false} {
for src, expected := range data {
func (r *runner) CompletionSnippet(t *testing.T, src span.Span, expected tests.CompletionSnippet, placeholders bool, items tests.CompletionItems) {
_, list := r.callCompletion(t, src, source.CompletionOptions{
Placeholders: placeholders,
Deep: true,
@ -129,12 +123,9 @@ func (r *runner) CompletionSnippets(t *testing.T, data tests.CompletionSnippets,
if diff := tests.DiffSnippets(want, got); diff != "" {
t.Errorf("%s: %s", src, diff)
}
}
}
}
func (r *runner) UnimportedCompletions(t *testing.T, data tests.UnimportedCompletions, items tests.CompletionItems) {
for src, test := range data {
func (r *runner) UnimportedCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
var want []protocol.CompletionItem
for _, pos := range test.CompletionItems {
want = append(want, tests.ToProtocolCompletionItem(*items[pos]))
@ -148,11 +139,9 @@ func (r *runner) UnimportedCompletions(t *testing.T, data tests.UnimportedComple
if diff := tests.DiffCompletionItems(want, got); diff != "" {
t.Errorf("%s: %s", src, diff)
}
}
}
func (r *runner) DeepCompletions(t *testing.T, data tests.DeepCompletions, items tests.CompletionItems) {
for src, test := range data {
func (r *runner) DeepCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
var want []protocol.CompletionItem
for _, pos := range test.CompletionItems {
want = append(want, tests.ToProtocolCompletionItem(*items[pos]))
@ -176,11 +165,9 @@ func (r *runner) DeepCompletions(t *testing.T, data tests.DeepCompletions, items
if msg := tests.DiffCompletionItems(want, got); msg != "" {
t.Errorf("%s: %s", src, msg)
}
}
}
func (r *runner) FuzzyCompletions(t *testing.T, data tests.FuzzyCompletions, items tests.CompletionItems) {
for src, test := range data {
func (r *runner) FuzzyCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
var want []protocol.CompletionItem
for _, pos := range test.CompletionItems {
want = append(want, tests.ToProtocolCompletionItem(*items[pos]))
@ -207,11 +194,9 @@ func (r *runner) FuzzyCompletions(t *testing.T, data tests.FuzzyCompletions, ite
if msg := tests.DiffCompletionItems(want, got); msg != "" {
t.Errorf("%s: %s", src, msg)
}
}
}
func (r *runner) CaseSensitiveCompletions(t *testing.T, data tests.CaseSensitiveCompletions, items tests.CompletionItems) {
for src, test := range data {
func (r *runner) CaseSensitiveCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
var want []protocol.CompletionItem
for _, pos := range test.CompletionItems {
want = append(want, tests.ToProtocolCompletionItem(*items[pos]))
@ -225,11 +210,9 @@ func (r *runner) CaseSensitiveCompletions(t *testing.T, data tests.CaseSensitive
if diff := tests.DiffCompletionItems(want, list); diff != "" {
t.Errorf("%s: %s", src, diff)
}
}
}
func (r *runner) RankCompletions(t *testing.T, data tests.RankCompletions, items tests.CompletionItems) {
for src, test := range data {
func (r *runner) RankCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
var want []protocol.CompletionItem
for _, pos := range test.CompletionItems {
want = append(want, tests.ToProtocolCompletionItem(*items[pos]))
@ -253,7 +236,6 @@ func (r *runner) RankCompletions(t *testing.T, data tests.RankCompletions, items
if msg := tests.CheckCompletionOrder(want, got); msg != "" {
t.Errorf("%s: %s", src, msg)
}
}
}
func (r *runner) callCompletion(t *testing.T, src span.Span, options source.CompletionOptions) (string, []protocol.CompletionItem) {
@ -295,8 +277,7 @@ func (r *runner) callCompletion(t *testing.T, src span.Span, options source.Comp
return prefix, tests.ToProtocolCompletionItems(items)
}
func (r *runner) FoldingRange(t *testing.T, data tests.FoldingRanges) {
for _, spn := range data {
func (r *runner) FoldingRange(t *testing.T, spn span.Span) {
uri := spn.URI()
f, err := r.view.GetFile(r.ctx, uri)
@ -306,14 +287,14 @@ func (r *runner) FoldingRange(t *testing.T, data tests.FoldingRanges) {
data, _, err := f.Handle(r.ctx).Read(r.ctx)
if err != nil {
t.Error(err)
continue
return
}
// Test all folding ranges.
ranges, err := source.FoldingRange(r.ctx, r.view, f.(source.GoFile), false)
if err != nil {
t.Error(err)
continue
return
}
r.foldingRanges(t, "foldingRange", uri, string(data), ranges)
@ -321,11 +302,9 @@ func (r *runner) FoldingRange(t *testing.T, data tests.FoldingRanges) {
ranges, err = source.FoldingRange(r.ctx, r.view, f.(source.GoFile), true)
if err != nil {
t.Error(err)
continue
return
}
r.foldingRanges(t, "foldingRange-lineFolding", uri, string(data), ranges)
}
}
func (r *runner) foldingRanges(t *testing.T, prefix string, uri span.URI, data string, ranges []*source.FoldingRangeInfo) {
@ -435,9 +414,8 @@ func foldRanges(contents string, ranges []*source.FoldingRangeInfo) (string, err
return res, nil
}
func (r *runner) Format(t *testing.T, data tests.Formats) {
func (r *runner) Format(t *testing.T, spn span.Span) {
ctx := r.ctx
for _, spn := range data {
uri := spn.URI()
filename := uri.Filename()
gofmted := string(r.data.Golden("gofmt", filename, func() ([]byte, error) {
@ -454,7 +432,7 @@ func (r *runner) Format(t *testing.T, data tests.Formats) {
if gofmted != "" {
t.Error(err)
}
continue
return
}
data, _, err := f.Handle(ctx).Read(ctx)
if err != nil {
@ -472,12 +450,10 @@ func (r *runner) Format(t *testing.T, data tests.Formats) {
if gofmted != got {
t.Errorf("format failed for %s, expected:\n%v\ngot:\n%v", filename, gofmted, got)
}
}
}
func (r *runner) Import(t *testing.T, data tests.Imports) {
func (r *runner) Import(t *testing.T, spn span.Span) {
ctx := r.ctx
for _, spn := range data {
uri := spn.URI()
filename := uri.Filename()
goimported := string(r.data.Golden("goimports", filename, func() ([]byte, error) {
@ -503,7 +479,7 @@ func (r *runner) Import(t *testing.T, data tests.Imports) {
if goimported != "" {
t.Error(err)
}
continue
return
}
data, _, err := fh.Read(ctx)
if err != nil {
@ -521,15 +497,13 @@ func (r *runner) Import(t *testing.T, data tests.Imports) {
if goimported != got {
t.Errorf("import failed for %s, expected:\n%v\ngot:\n%v", filename, goimported, got)
}
}
}
func (r *runner) SuggestedFix(t *testing.T, data tests.SuggestedFixes) {
func (r *runner) SuggestedFix(t *testing.T, spn span.Span) {
}
func (r *runner) Definition(t *testing.T, data tests.Definitions) {
func (r *runner) Definition(t *testing.T, spn span.Span, d tests.Definition) {
ctx := r.ctx
for _, d := range data {
f, err := r.view.GetFile(ctx, d.Src.URI())
if err != nil {
t.Fatalf("failed for %v: %v", d.Src, err)
@ -579,12 +553,10 @@ func (r *runner) Definition(t *testing.T, data tests.Definitions) {
} else {
t.Errorf("no tests ran for %s", d.Src.URI())
}
}
}
func (r *runner) Highlight(t *testing.T, data tests.Highlights) {
func (r *runner) Highlight(t *testing.T, name string, locations []span.Span) {
ctx := r.ctx
for name, locations := range data {
src := locations[0]
m, srcRng, err := spanToRange(r.data, src)
if err != nil {
@ -606,12 +578,10 @@ func (r *runner) Highlight(t *testing.T, data tests.Highlights) {
t.Errorf("want %v, got %v\n", want, got)
}
}
}
}
func (r *runner) Reference(t *testing.T, data tests.References) {
func (r *runner) Reference(t *testing.T, src span.Span, itemList []span.Span) {
ctx := r.ctx
for src, itemList := range data {
f, err := r.view.GetFile(ctx, src.URI())
if err != nil {
t.Fatalf("failed for %v: %v", src, err)
@ -653,12 +623,10 @@ func (r *runner) Reference(t *testing.T, data tests.References) {
t.Errorf("references failed: incorrect references got %v want locations %v", got, want)
}
}
}
}
func (r *runner) Rename(t *testing.T, data tests.Renames) {
func (r *runner) Rename(t *testing.T, spn span.Span, newText string) {
ctx := r.ctx
for spn, newText := range data {
tag := fmt.Sprintf("%s-rename", newText)
f, err := r.view.GetFile(ctx, spn.URI())
@ -672,7 +640,7 @@ func (r *runner) Rename(t *testing.T, data tests.Renames) {
ident, err := source.Identifier(r.ctx, r.view, f.(source.GoFile), srcRng.Start)
if err != nil {
t.Error(err)
continue
return
}
changes, err := ident.Rename(r.ctx, r.view, newText)
if err != nil {
@ -682,7 +650,7 @@ func (r *runner) Rename(t *testing.T, data tests.Renames) {
if err.Error() != renamed {
t.Errorf("rename failed for %s, expected:\n%v\ngot:\n%v\n", newText, renamed, err)
}
continue
return
}
var res []string
@ -730,7 +698,6 @@ func (r *runner) Rename(t *testing.T, data tests.Renames) {
if renamed != got {
t.Errorf("rename failed for %s, expected:\n%v\ngot:\n%v", newText, renamed, got)
}
}
}
func applyEdits(contents string, edits []diff.TextEdit) string {
@ -748,9 +715,8 @@ func applyEdits(contents string, edits []diff.TextEdit) string {
return res
}
func (r *runner) PrepareRename(t *testing.T, data tests.PrepareRenames) {
func (r *runner) PrepareRename(t *testing.T, src span.Span, want *source.PrepareItem) {
ctx := context.Background()
for src, want := range data {
f, err := r.view.GetFile(ctx, src.URI())
if err != nil {
t.Fatalf("failed for %v: %v", src, err)
@ -765,27 +731,25 @@ func (r *runner) PrepareRename(t *testing.T, data tests.PrepareRenames) {
if want.Text != "" { // expected an ident.
t.Errorf("prepare rename failed for %v: got error: %v", src, err)
}
continue
return
}
if item == nil {
if want.Text != "" {
t.Errorf("prepare rename failed for %v: got nil", src)
}
continue
return
}
if want.Text == "" && item != nil {
t.Errorf("prepare rename failed for %v: expected nil, got %v", src, item)
continue
return
}
if protocol.CompareRange(want.Range, item.Range) != 0 {
t.Errorf("prepare rename failed: incorrect range got %v want %v", item.Range, want.Range)
}
}
}
func (r *runner) Symbol(t *testing.T, data tests.Symbols) {
func (r *runner) Symbol(t *testing.T, uri span.URI, expectedSymbols []protocol.DocumentSymbol) {
ctx := r.ctx
for uri, expectedSymbols := range data {
f, err := r.view.GetFile(ctx, uri)
if err != nil {
t.Fatalf("failed for %v: %v", uri, err)
@ -796,12 +760,11 @@ func (r *runner) Symbol(t *testing.T, data tests.Symbols) {
}
if len(symbols) != len(expectedSymbols) {
t.Errorf("want %d top-level symbols in %v, got %d", len(expectedSymbols), uri, len(symbols))
continue
return
}
if diff := r.diffSymbols(t, uri, expectedSymbols, symbols); diff != "" {
t.Error(diff)
}
}
}
func (r *runner) diffSymbols(t *testing.T, uri span.URI, want, got []protocol.DocumentSymbol) string {
@ -847,9 +810,8 @@ func summarizeSymbols(t *testing.T, i int, want, got []protocol.DocumentSymbol,
return msg.String()
}
func (r *runner) SignatureHelp(t *testing.T, data tests.Signatures) {
func (r *runner) SignatureHelp(t *testing.T, spn span.Span, expectedSignature *source.SignatureInformation) {
ctx := r.ctx
for spn, expectedSignature := range data {
f, err := r.view.GetFile(ctx, spn.URI())
if err != nil {
t.Fatalf("failed for %v: %v", spn, err)
@ -869,12 +831,11 @@ func (r *runner) SignatureHelp(t *testing.T, data tests.Signatures) {
if gotSignature != nil {
t.Errorf("expected no signature, got %v", gotSignature)
}
continue
return
}
if diff := diffSignatures(spn, expectedSignature, gotSignature); diff != "" {
t.Error(diff)
}
}
}
func diffSignatures(spn span.Span, want *source.SignatureInformation, got *source.SignatureInformation) string {
@ -898,7 +859,7 @@ func diffSignatures(spn span.Span, want *source.SignatureInformation, got *sourc
return ""
}
func (r *runner) Link(t *testing.T, data tests.Links) {
func (r *runner) Link(t *testing.T, uri span.URI, wantLinks []tests.Link) {
// This is a pure LSP feature, no source level functionality to be tested.
}

View File

@ -96,26 +96,26 @@ type Data struct {
}
type Tests interface {
Diagnostics(*testing.T, Diagnostics)
Completion(*testing.T, Completions, CompletionItems)
CompletionSnippets(*testing.T, CompletionSnippets, CompletionItems)
UnimportedCompletions(*testing.T, UnimportedCompletions, CompletionItems)
DeepCompletions(*testing.T, DeepCompletions, CompletionItems)
FuzzyCompletions(*testing.T, FuzzyCompletions, CompletionItems)
CaseSensitiveCompletions(*testing.T, CaseSensitiveCompletions, CompletionItems)
RankCompletions(*testing.T, RankCompletions, CompletionItems)
FoldingRange(*testing.T, FoldingRanges)
Format(*testing.T, Formats)
Import(*testing.T, Imports)
SuggestedFix(*testing.T, SuggestedFixes)
Definition(*testing.T, Definitions)
Highlight(*testing.T, Highlights)
Reference(*testing.T, References)
Rename(*testing.T, Renames)
PrepareRename(*testing.T, PrepareRenames)
Symbol(*testing.T, Symbols)
SignatureHelp(*testing.T, Signatures)
Link(*testing.T, Links)
Diagnostics(*testing.T, span.URI, []source.Diagnostic)
Completion(*testing.T, span.Span, Completion, CompletionItems)
CompletionSnippet(*testing.T, span.Span, CompletionSnippet, bool, CompletionItems)
UnimportedCompletion(*testing.T, span.Span, Completion, CompletionItems)
DeepCompletion(*testing.T, span.Span, Completion, CompletionItems)
FuzzyCompletion(*testing.T, span.Span, Completion, CompletionItems)
CaseSensitiveCompletion(*testing.T, span.Span, Completion, CompletionItems)
RankCompletion(*testing.T, span.Span, Completion, CompletionItems)
FoldingRange(*testing.T, span.Span)
Format(*testing.T, span.Span)
Import(*testing.T, span.Span)
SuggestedFix(*testing.T, span.Span)
Definition(*testing.T, span.Span, Definition)
Highlight(*testing.T, string, []span.Span)
Reference(*testing.T, span.Span, []span.Span)
Rename(*testing.T, span.Span, string)
PrepareRename(*testing.T, span.Span, *source.PrepareItem)
Symbol(*testing.T, span.URI, []protocol.DocumentSymbol)
SignatureHelp(*testing.T, span.Span, *source.SignatureInformation)
Link(*testing.T, span.URI, []Link)
}
type Definition struct {
@ -327,102 +327,144 @@ func Run(t *testing.T, tests Tests, data *Data) {
t.Run("Completion", func(t *testing.T) {
t.Helper()
tests.Completion(t, data.Completions, data.CompletionItems)
for src, test := range data.Completions {
tests.Completion(t, src, test, data.CompletionItems)
}
})
t.Run("CompletionSnippets", func(t *testing.T) {
t.Helper()
tests.CompletionSnippets(t, data.CompletionSnippets, data.CompletionItems)
for _, placeholders := range []bool{true, false} {
for src, expected := range data.CompletionSnippets {
tests.CompletionSnippet(t, src, expected, placeholders, data.CompletionItems)
}
}
})
t.Run("UnimportedCompletion", func(t *testing.T) {
t.Helper()
tests.UnimportedCompletions(t, data.UnimportedCompletions, data.CompletionItems)
for src, test := range data.UnimportedCompletions {
tests.UnimportedCompletion(t, src, test, data.CompletionItems)
}
})
t.Run("DeepCompletion", func(t *testing.T) {
t.Helper()
tests.DeepCompletions(t, data.DeepCompletions, data.CompletionItems)
for src, test := range data.DeepCompletions {
tests.DeepCompletion(t, src, test, data.CompletionItems)
}
})
t.Run("FuzzyCompletion", func(t *testing.T) {
t.Helper()
tests.FuzzyCompletions(t, data.FuzzyCompletions, data.CompletionItems)
for src, test := range data.FuzzyCompletions {
tests.FuzzyCompletion(t, src, test, data.CompletionItems)
}
})
t.Run("CaseSensitiveCompletion", func(t *testing.T) {
t.Helper()
tests.CaseSensitiveCompletions(t, data.CaseSensitiveCompletions, data.CompletionItems)
for src, test := range data.CaseSensitiveCompletions {
tests.CaseSensitiveCompletion(t, src, test, data.CompletionItems)
}
})
t.Run("RankCompletions", func(t *testing.T) {
t.Helper()
tests.RankCompletions(t, data.RankCompletions, data.CompletionItems)
for src, test := range data.RankCompletions {
tests.RankCompletion(t, src, test, data.CompletionItems)
}
})
t.Run("Diagnostics", func(t *testing.T) {
t.Helper()
tests.Diagnostics(t, data.Diagnostics)
for uri, want := range data.Diagnostics {
tests.Diagnostics(t, uri, want)
}
})
t.Run("FoldingRange", func(t *testing.T) {
t.Helper()
tests.FoldingRange(t, data.FoldingRanges)
for _, spn := range data.FoldingRanges {
tests.FoldingRange(t, spn)
}
})
t.Run("Format", func(t *testing.T) {
t.Helper()
tests.Format(t, data.Formats)
for _, spn := range data.Formats {
tests.Format(t, spn)
}
})
t.Run("Import", func(t *testing.T) {
t.Helper()
tests.Import(t, data.Imports)
for _, spn := range data.Imports {
tests.Import(t, spn)
}
})
t.Run("SuggestedFix", func(t *testing.T) {
t.Helper()
tests.SuggestedFix(t, data.SuggestedFixes)
for _, spn := range data.SuggestedFixes {
tests.SuggestedFix(t, spn)
}
})
t.Run("Definition", func(t *testing.T) {
t.Helper()
tests.Definition(t, data.Definitions)
for spn, d := range data.Definitions {
tests.Definition(t, spn, d)
}
})
t.Run("Highlight", func(t *testing.T) {
t.Helper()
tests.Highlight(t, data.Highlights)
for name, locations := range data.Highlights {
tests.Highlight(t, name, locations)
}
})
t.Run("References", func(t *testing.T) {
t.Helper()
tests.Reference(t, data.References)
for src, itemList := range data.References {
tests.Reference(t, src, itemList)
}
})
t.Run("Renames", func(t *testing.T) {
t.Helper()
tests.Rename(t, data.Renames)
for spn, newText := range data.Renames {
tests.Rename(t, spn, newText)
}
})
t.Run("PrepareRenames", func(t *testing.T) {
t.Helper()
tests.PrepareRename(t, data.PrepareRenames)
for src, want := range data.PrepareRenames {
tests.PrepareRename(t, src, want)
}
})
t.Run("Symbols", func(t *testing.T) {
t.Helper()
tests.Symbol(t, data.Symbols)
for uri, expectedSymbols := range data.Symbols {
tests.Symbol(t, uri, expectedSymbols)
}
})
t.Run("SignatureHelp", func(t *testing.T) {
t.Helper()
tests.SignatureHelp(t, data.Signatures)
for spn, expectedSignature := range data.Signatures {
tests.SignatureHelp(t, spn, expectedSignature)
}
})
t.Run("Link", func(t *testing.T) {
t.Helper()
tests.Link(t, data.Links)
for uri, wantLinks := range data.Links {
tests.Link(t, uri, wantLinks)
}
})
if *UpdateGolden {