mirror of
https://github.com/golang/go
synced 2024-11-18 17:04:41 -07:00
internal/lsp: use subtests for all lsp categories
This makes it possible to run just one type of test if needed Also add some verification that the right number of tests is being run And finally collect all the expectations up front, including the completions. Change-Id: Iee6045a8ad89fa399fefd03bc0712770701ec6f8 Reviewed-on: https://go-review.googlesource.com/c/149737 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:
parent
68f7e630ce
commit
3d801af142
@ -28,6 +28,9 @@ func TestLSP(t *testing.T) {
|
|||||||
|
|
||||||
func testLSP(t *testing.T, exporter packagestest.Exporter) {
|
func testLSP(t *testing.T, exporter packagestest.Exporter) {
|
||||||
const dir = "testdata"
|
const dir = "testdata"
|
||||||
|
const expectedCompletionsCount = 4
|
||||||
|
const expectedDiagnosticsCount = 7
|
||||||
|
const expectedFormatCount = 3
|
||||||
|
|
||||||
files := packagestest.MustCopyFileTree(dir)
|
files := packagestest.MustCopyFileTree(dir)
|
||||||
for fragment, operation := range files {
|
for fragment, operation := range files {
|
||||||
@ -48,9 +51,10 @@ func testLSP(t *testing.T, exporter packagestest.Exporter) {
|
|||||||
dirs := make(map[string]bool)
|
dirs := make(map[string]bool)
|
||||||
|
|
||||||
// collect results for certain tests
|
// collect results for certain tests
|
||||||
expectedDiagnostics := make(map[string][]protocol.Diagnostic)
|
expectedDiagnostics := make(diagnostics)
|
||||||
expectedCompletions := make(map[token.Position]*protocol.CompletionItem)
|
completionItems := make(completionItems)
|
||||||
expectedFormat := make(map[string]string)
|
expectedCompletions := make(completions)
|
||||||
|
expectedFormat := make(formats)
|
||||||
|
|
||||||
s := &server{
|
s := &server{
|
||||||
view: source.NewView(),
|
view: source.NewView(),
|
||||||
@ -81,70 +85,76 @@ func testLSP(t *testing.T, exporter packagestest.Exporter) {
|
|||||||
}
|
}
|
||||||
// Collect any data that needs to be used by subsequent tests.
|
// Collect any data that needs to be used by subsequent tests.
|
||||||
if err := exported.Expect(map[string]interface{}{
|
if err := exported.Expect(map[string]interface{}{
|
||||||
"diag": func(pos token.Position, msg string) {
|
"diag": expectedDiagnostics.collect,
|
||||||
collectDiagnostics(t, expectedDiagnostics, pos, msg)
|
"item": completionItems.collect,
|
||||||
},
|
"complete": expectedCompletions.collect,
|
||||||
"item": func(pos token.Position, label, detail, kind string) {
|
"format": expectedFormat.collect,
|
||||||
collectCompletionItems(expectedCompletions, pos, label, detail, kind)
|
|
||||||
},
|
|
||||||
"format": func(pos token.Position) {
|
|
||||||
collectFormat(expectedFormat, pos)
|
|
||||||
},
|
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// test completion
|
t.Run("Completion", func(t *testing.T) {
|
||||||
testCompletion(t, exported, s, expectedCompletions)
|
t.Helper()
|
||||||
|
if len(expectedCompletions) != expectedCompletionsCount {
|
||||||
|
t.Errorf("got %v completions expected %v", len(expectedCompletions), expectedCompletionsCount)
|
||||||
|
}
|
||||||
|
expectedCompletions.test(t, exported, s, completionItems)
|
||||||
|
})
|
||||||
|
|
||||||
// test diagnostics
|
t.Run("Diagnostics", func(t *testing.T) {
|
||||||
var dirList []string
|
t.Helper()
|
||||||
for dir := range dirs {
|
diagnosticsCount := expectedDiagnostics.test(t, exported, s.view, dirs)
|
||||||
dirList = append(dirList, dir)
|
if diagnosticsCount != expectedDiagnosticsCount {
|
||||||
}
|
t.Errorf("got %v diagnostics expected %v", diagnosticsCount, expectedDiagnosticsCount)
|
||||||
exported.Config.Mode = packages.LoadFiles
|
}
|
||||||
pkgs, err := packages.Load(exported.Config, dirList...)
|
})
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
testDiagnostics(t, s.view, pkgs, expectedDiagnostics)
|
|
||||||
|
|
||||||
// test format
|
t.Run("Format", func(t *testing.T) {
|
||||||
testFormat(t, s, expectedFormat)
|
t.Helper()
|
||||||
|
if len(expectedFormat) != expectedFormatCount {
|
||||||
|
t.Errorf("got %v formats expected %v", len(expectedFormat), expectedFormatCount)
|
||||||
|
}
|
||||||
|
expectedFormat.test(t, s)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCompletion(t *testing.T, exported *packagestest.Exported, s *server, wants map[token.Position]*protocol.CompletionItem) {
|
type diagnostics map[string][]protocol.Diagnostic
|
||||||
if err := exported.Expect(map[string]interface{}{
|
type completionItems map[token.Pos]*protocol.CompletionItem
|
||||||
"complete": func(src token.Position, expected []token.Position) {
|
type completions map[token.Position][]token.Pos
|
||||||
var want []protocol.CompletionItem
|
type formats map[string]string
|
||||||
for _, pos := range expected {
|
|
||||||
want = append(want, *wants[pos])
|
func (c completions) test(t *testing.T, exported *packagestest.Exported, s *server, items completionItems) {
|
||||||
}
|
for src, itemList := range c {
|
||||||
list, err := s.Completion(context.Background(), &protocol.CompletionParams{
|
var want []protocol.CompletionItem
|
||||||
TextDocumentPositionParams: protocol.TextDocumentPositionParams{
|
for _, pos := range itemList {
|
||||||
TextDocument: protocol.TextDocumentIdentifier{
|
want = append(want, *items[pos])
|
||||||
URI: protocol.DocumentURI(source.ToURI(src.Filename)),
|
}
|
||||||
},
|
list, err := s.Completion(context.Background(), &protocol.CompletionParams{
|
||||||
Position: protocol.Position{
|
TextDocumentPositionParams: protocol.TextDocumentPositionParams{
|
||||||
Line: float64(src.Line - 1),
|
TextDocument: protocol.TextDocumentIdentifier{
|
||||||
Character: float64(src.Column - 1),
|
URI: protocol.DocumentURI(source.ToURI(src.Filename)),
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
Position: protocol.Position{
|
||||||
if err != nil {
|
Line: float64(src.Line - 1),
|
||||||
t.Fatal(err)
|
Character: float64(src.Column - 1),
|
||||||
}
|
},
|
||||||
got := list.Items
|
},
|
||||||
if equal := reflect.DeepEqual(want, got); !equal {
|
})
|
||||||
t.Errorf("completion failed for %s:%v:%v: (expected: %v), (got: %v)", filepath.Base(src.Filename), src.Line, src.Column, want, got)
|
if err != nil {
|
||||||
}
|
t.Fatal(err)
|
||||||
},
|
}
|
||||||
}); err != nil {
|
got := list.Items
|
||||||
t.Fatal(err)
|
if equal := reflect.DeepEqual(want, got); !equal {
|
||||||
|
t.Errorf("completion failed for %s:%v:%v: (expected: %v), (got: %v)", filepath.Base(src.Filename), src.Line, src.Column, want, got)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectCompletionItems(expectedCompletions map[token.Position]*protocol.CompletionItem, pos token.Position, label, detail, kind string) {
|
func (c completions) collect(src token.Position, expected []token.Pos) {
|
||||||
|
c[src] = expected
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i completionItems) collect(pos token.Pos, label, detail, kind string) {
|
||||||
var k protocol.CompletionItemKind
|
var k protocol.CompletionItemKind
|
||||||
switch kind {
|
switch kind {
|
||||||
case "struct":
|
case "struct":
|
||||||
@ -164,14 +174,26 @@ func collectCompletionItems(expectedCompletions map[token.Position]*protocol.Com
|
|||||||
case "method":
|
case "method":
|
||||||
k = protocol.MethodCompletion
|
k = protocol.MethodCompletion
|
||||||
}
|
}
|
||||||
expectedCompletions[pos] = &protocol.CompletionItem{
|
i[pos] = &protocol.CompletionItem{
|
||||||
Label: label,
|
Label: label,
|
||||||
Detail: detail,
|
Detail: detail,
|
||||||
Kind: float64(k),
|
Kind: float64(k),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDiagnostics(t *testing.T, v *source.View, pkgs []*packages.Package, wants map[string][]protocol.Diagnostic) {
|
func (d diagnostics) test(t *testing.T, exported *packagestest.Exported, v *source.View, dirs map[string]bool) int {
|
||||||
|
// first trigger a load to get the diagnostics
|
||||||
|
var dirList []string
|
||||||
|
for dir := range dirs {
|
||||||
|
dirList = append(dirList, dir)
|
||||||
|
}
|
||||||
|
exported.Config.Mode = packages.LoadFiles
|
||||||
|
pkgs, err := packages.Load(exported.Config, dirList...)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// and now see if they match the expected ones
|
||||||
|
count := 0
|
||||||
for _, pkg := range pkgs {
|
for _, pkg := range pkgs {
|
||||||
for _, filename := range pkg.GoFiles {
|
for _, filename := range pkg.GoFiles {
|
||||||
f := v.GetFile(source.ToURI(filename))
|
f := v.GetFile(source.ToURI(filename))
|
||||||
@ -183,7 +205,7 @@ func testDiagnostics(t *testing.T, v *source.View, pkgs []*packages.Package, wan
|
|||||||
sort.Slice(got, func(i int, j int) bool {
|
sort.Slice(got, func(i int, j int) bool {
|
||||||
return got[i].Range.Start.Line < got[j].Range.Start.Line
|
return got[i].Range.Start.Line < got[j].Range.Start.Line
|
||||||
})
|
})
|
||||||
want := wants[filename]
|
want := d[filename]
|
||||||
if equal := reflect.DeepEqual(want, got); !equal {
|
if equal := reflect.DeepEqual(want, got); !equal {
|
||||||
msg := &bytes.Buffer{}
|
msg := &bytes.Buffer{}
|
||||||
fmt.Fprintf(msg, "diagnostics failed for %s: expected:\n", filepath.Base(filename))
|
fmt.Fprintf(msg, "diagnostics failed for %s: expected:\n", filepath.Base(filename))
|
||||||
@ -196,11 +218,13 @@ func testDiagnostics(t *testing.T, v *source.View, pkgs []*packages.Package, wan
|
|||||||
}
|
}
|
||||||
t.Error(msg.String())
|
t.Error(msg.String())
|
||||||
}
|
}
|
||||||
|
count += len(want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectDiagnostics(t *testing.T, expectedDiagnostics map[string][]protocol.Diagnostic, pos token.Position, msg string) {
|
func (d diagnostics) collect(pos token.Position, msg string) {
|
||||||
line := float64(pos.Line - 1)
|
line := float64(pos.Line - 1)
|
||||||
col := float64(pos.Column - 1)
|
col := float64(pos.Column - 1)
|
||||||
want := protocol.Diagnostic{
|
want := protocol.Diagnostic{
|
||||||
@ -218,15 +242,11 @@ func collectDiagnostics(t *testing.T, expectedDiagnostics map[string][]protocol.
|
|||||||
Source: "LSP",
|
Source: "LSP",
|
||||||
Message: msg,
|
Message: msg,
|
||||||
}
|
}
|
||||||
if _, ok := expectedDiagnostics[pos.Filename]; ok {
|
d[pos.Filename] = append(d[pos.Filename], want)
|
||||||
expectedDiagnostics[pos.Filename] = append(expectedDiagnostics[pos.Filename], want)
|
|
||||||
} else {
|
|
||||||
t.Errorf("unexpected filename: %v", pos.Filename)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testFormat(t *testing.T, s *server, expectedFormat map[string]string) {
|
func (f formats) test(t *testing.T, s *server) {
|
||||||
for filename, gofmted := range expectedFormat {
|
for filename, gofmted := range f {
|
||||||
edits, err := s.Formatting(context.Background(), &protocol.DocumentFormattingParams{
|
edits, err := s.Formatting(context.Background(), &protocol.DocumentFormattingParams{
|
||||||
TextDocument: protocol.TextDocumentIdentifier{
|
TextDocument: protocol.TextDocumentIdentifier{
|
||||||
URI: protocol.DocumentURI(source.ToURI(filename)),
|
URI: protocol.DocumentURI(source.ToURI(filename)),
|
||||||
@ -245,10 +265,10 @@ func testFormat(t *testing.T, s *server, expectedFormat map[string]string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectFormat(expectedFormat map[string]string, pos token.Position) {
|
func (f formats) collect(pos token.Position) {
|
||||||
cmd := exec.Command("gofmt", pos.Filename)
|
cmd := exec.Command("gofmt", pos.Filename)
|
||||||
stdout := bytes.NewBuffer(nil)
|
stdout := bytes.NewBuffer(nil)
|
||||||
cmd.Stdout = stdout
|
cmd.Stdout = stdout
|
||||||
cmd.Run() // ignore error, sometimes we have intentionally ungofmt-able files
|
cmd.Run() // ignore error, sometimes we have intentionally ungofmt-able files
|
||||||
expectedFormat[pos.Filename] = stdout.String()
|
f[pos.Filename] = stdout.String()
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user