mirror of https://github.com/golang/go.git
internal/lsp: add more flexible completion tests
Add a new @completePartial note that does not require you to specify the full list of completions. This gets rid of a lot of noise when you just want to test the relative order of some completion candidates but don't care about all the other candidates in scope. I changed one existing test to use @completePartial as an example. Change-Id: I56005405477e562803f094c0cac05ef2b854ad1a Reviewed-on: https://go-review.googlesource.com/c/tools/+/192657 Run-TryBot: Rebecca Stambler <rstambler@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
parent
0673112484
commit
5797d2e298
|
|
@ -117,9 +117,9 @@ func (r *runner) Completion(t *testing.T, data tests.Completions, snippets tests
|
|||
// Set this as a default.
|
||||
modified.Completion.Documentation = true
|
||||
|
||||
for src, itemList := range data {
|
||||
for src, test := range data {
|
||||
var want []source.CompletionItem
|
||||
for _, pos := range itemList {
|
||||
for _, pos := range test.CompletionItems {
|
||||
want = append(want, *items[pos])
|
||||
}
|
||||
|
||||
|
|
@ -138,8 +138,16 @@ func (r *runner) Completion(t *testing.T, data tests.Completions, snippets tests
|
|||
}
|
||||
got = append(got, item)
|
||||
}
|
||||
if diff := diffCompletionItems(t, src, want, got); diff != "" {
|
||||
t.Errorf("%s: %s", src, diff)
|
||||
|
||||
switch test.Type {
|
||||
case tests.CompletionFull:
|
||||
if diff := diffCompletionItems(want, got); diff != "" {
|
||||
t.Errorf("%s: %s", src, diff)
|
||||
}
|
||||
case tests.CompletionPartial:
|
||||
if msg := checkCompletionOrder(want, got); msg != "" {
|
||||
t.Errorf("%s: %s", src, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -219,7 +227,7 @@ func isBuiltin(item protocol.CompletionItem) bool {
|
|||
|
||||
// diffCompletionItems prints the diff between expected and actual completion
|
||||
// test results.
|
||||
func diffCompletionItems(t *testing.T, spn span.Span, want []source.CompletionItem, got []protocol.CompletionItem) string {
|
||||
func diffCompletionItems(want []source.CompletionItem, got []protocol.CompletionItem) string {
|
||||
if len(got) != len(want) {
|
||||
return summarizeCompletionItems(-1, want, got, "different lengths got %v want %v", len(got), len(want))
|
||||
}
|
||||
|
|
@ -243,6 +251,43 @@ func diffCompletionItems(t *testing.T, spn span.Span, want []source.CompletionIt
|
|||
return ""
|
||||
}
|
||||
|
||||
func checkCompletionOrder(want []source.CompletionItem, got []protocol.CompletionItem) string {
|
||||
var (
|
||||
matchedIdxs []int
|
||||
lastGotIdx int
|
||||
inOrder = true
|
||||
)
|
||||
for _, w := range want {
|
||||
var found bool
|
||||
for i, g := range got {
|
||||
if w.Label == g.Label && w.Detail == g.Detail && toProtocolCompletionItemKind(w.Kind) == g.Kind {
|
||||
matchedIdxs = append(matchedIdxs, i)
|
||||
found = true
|
||||
if i < lastGotIdx {
|
||||
inOrder = false
|
||||
}
|
||||
lastGotIdx = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return summarizeCompletionItems(-1, []source.CompletionItem{w}, got, "didn't find expected completion")
|
||||
}
|
||||
}
|
||||
|
||||
sort.Ints(matchedIdxs)
|
||||
matched := make([]protocol.CompletionItem, 0, len(matchedIdxs))
|
||||
for _, idx := range matchedIdxs {
|
||||
matched = append(matched, got[idx])
|
||||
}
|
||||
|
||||
if !inOrder {
|
||||
return summarizeCompletionItems(-1, want, matched, "completions out of order")
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func summarizeCompletionItems(i int, want []source.CompletionItem, got []protocol.CompletionItem, reason string, args ...interface{}) string {
|
||||
msg := &bytes.Buffer{}
|
||||
fmt.Fprint(msg, "completion failed")
|
||||
|
|
|
|||
|
|
@ -86,9 +86,9 @@ func (r *runner) Diagnostics(t *testing.T, data tests.Diagnostics) {
|
|||
|
||||
func (r *runner) Completion(t *testing.T, data tests.Completions, snippets tests.CompletionSnippets, items tests.CompletionItems) {
|
||||
ctx := r.ctx
|
||||
for src, itemList := range data {
|
||||
for src, test := range data {
|
||||
var want []source.CompletionItem
|
||||
for _, pos := range itemList {
|
||||
for _, pos := range test.CompletionItems {
|
||||
want = append(want, *items[pos])
|
||||
}
|
||||
f, err := r.view.GetFile(ctx, src.URI())
|
||||
|
|
@ -142,8 +142,15 @@ func (r *runner) Completion(t *testing.T, data tests.Completions, snippets tests
|
|||
}
|
||||
got = append(got, item)
|
||||
}
|
||||
if diff := diffCompletionItems(t, src, want, got); diff != "" {
|
||||
t.Errorf("%s: %s", src, diff)
|
||||
switch test.Type {
|
||||
case tests.CompletionFull:
|
||||
if diff := diffCompletionItems(want, got); diff != "" {
|
||||
t.Errorf("%s: %s", src, diff)
|
||||
}
|
||||
case tests.CompletionPartial:
|
||||
if msg := checkCompletionOrder(want, got); msg != "" {
|
||||
t.Errorf("%s: %s", src, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, usePlaceholders := range []bool{true, false} {
|
||||
|
|
@ -207,7 +214,7 @@ func isBuiltin(item source.CompletionItem) bool {
|
|||
|
||||
// diffCompletionItems prints the diff between expected and actual completion
|
||||
// test results.
|
||||
func diffCompletionItems(t *testing.T, spn span.Span, want []source.CompletionItem, got []source.CompletionItem) string {
|
||||
func diffCompletionItems(want []source.CompletionItem, got []source.CompletionItem) string {
|
||||
sort.SliceStable(got, func(i, j int) bool {
|
||||
return got[i].Score > got[j].Score
|
||||
})
|
||||
|
|
@ -250,6 +257,43 @@ func diffCompletionItems(t *testing.T, spn span.Span, want []source.CompletionIt
|
|||
return ""
|
||||
}
|
||||
|
||||
func checkCompletionOrder(want []source.CompletionItem, got []source.CompletionItem) string {
|
||||
var (
|
||||
matchedIdxs []int
|
||||
lastGotIdx int
|
||||
inOrder = true
|
||||
)
|
||||
for _, w := range want {
|
||||
var found bool
|
||||
for i, g := range got {
|
||||
if w.Label == g.Label && w.Detail == g.Detail && w.Kind == g.Kind {
|
||||
matchedIdxs = append(matchedIdxs, i)
|
||||
found = true
|
||||
if i < lastGotIdx {
|
||||
inOrder = false
|
||||
}
|
||||
lastGotIdx = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return summarizeCompletionItems(-1, []source.CompletionItem{w}, got, "didn't find expected completion")
|
||||
}
|
||||
}
|
||||
|
||||
sort.Ints(matchedIdxs)
|
||||
matched := make([]source.CompletionItem, 0, len(matchedIdxs))
|
||||
for _, idx := range matchedIdxs {
|
||||
matched = append(matched, got[idx])
|
||||
}
|
||||
|
||||
if !inOrder {
|
||||
return summarizeCompletionItems(-1, want, matched, "completions out of order")
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func summarizeCompletionItems(i int, want []source.CompletionItem, got []source.CompletionItem, reason string, args ...interface{}) string {
|
||||
msg := &bytes.Buffer{}
|
||||
fmt.Fprint(msg, "completion failed")
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
package deepcomplete
|
||||
|
||||
import "context" //@item(ctxPackage, "context", "\"context\"", "package")
|
||||
import "context"
|
||||
|
||||
type deepA struct {
|
||||
b deepB //@item(deepBField, "b", "deepB", "field")
|
||||
|
|
@ -26,15 +26,14 @@ func _() {
|
|||
func wantsContext(context.Context) {}
|
||||
|
||||
func _() {
|
||||
context.Background() //@item(ctxBackground, "context.Background", "func() context.Context", "func", "Background returns a non-nil, empty Context.")
|
||||
context.TODO() //@item(ctxTODO, "context.TODO", "func() context.Context", "func", "TODO returns a non-nil, empty Context.")
|
||||
context.WithValue(nil, nil, nil) //@item(ctxWithValue, "context.WithValue", "func(parent context.Context, key interface{}, val interface{}) context.Context", "func", "WithValue returns a copy of parent in which the value associated with key is val.")
|
||||
context.Background() //@item(ctxBackground, "context.Background", "func() context.Context", "func", "Background returns a non-nil, empty Context.")
|
||||
context.TODO() //@item(ctxTODO, "context.TODO", "func() context.Context", "func", "TODO returns a non-nil, empty Context.")
|
||||
|
||||
wantsContext(c) //@complete(")", ctxBackground, ctxTODO, ctxWithValue, ctxPackage)
|
||||
wantsContext(c) //@completePartial(")", ctxBackground, ctxTODO)
|
||||
}
|
||||
|
||||
func _() {
|
||||
type deepCircle struct { //@item(deepCircleStruct, "deepCircle", "struct{...}", "struct")
|
||||
type deepCircle struct {
|
||||
*deepCircle
|
||||
}
|
||||
var circle deepCircle //@item(deepCircle, "circle", "deepCircle", "var")
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ var updateGolden = flag.Bool("golden", false, "Update golden files")
|
|||
|
||||
type Diagnostics map[span.URI][]source.Diagnostic
|
||||
type CompletionItems map[token.Pos]*source.CompletionItem
|
||||
type Completions map[span.Span][]token.Pos
|
||||
type Completions map[span.Span]Completion
|
||||
type CompletionSnippets map[span.Span]CompletionSnippet
|
||||
type FoldingRanges []span.Span
|
||||
type Formats []span.Span
|
||||
|
|
@ -125,6 +125,21 @@ type Definition struct {
|
|||
Src, Def span.Span
|
||||
}
|
||||
|
||||
type CompletionTestType int
|
||||
|
||||
const (
|
||||
// Full means candidates in test must match full list of candidates.
|
||||
CompletionFull CompletionTestType = iota
|
||||
|
||||
// Partial means candidates in test must be valid and in the right relative order.
|
||||
CompletionPartial
|
||||
)
|
||||
|
||||
type Completion struct {
|
||||
CompletionItems []token.Pos
|
||||
Type CompletionTestType
|
||||
}
|
||||
|
||||
type CompletionSnippet struct {
|
||||
CompletionItem token.Pos
|
||||
PlainSnippet string
|
||||
|
|
@ -232,24 +247,25 @@ func Load(t testing.TB, exporter packagestest.Exporter, dir string) *Data {
|
|||
|
||||
// Collect any data that needs to be used by subsequent tests.
|
||||
if err := data.Exported.Expect(map[string]interface{}{
|
||||
"diag": data.collectDiagnostics,
|
||||
"item": data.collectCompletionItems,
|
||||
"complete": data.collectCompletions,
|
||||
"fold": data.collectFoldingRanges,
|
||||
"format": data.collectFormats,
|
||||
"import": data.collectImports,
|
||||
"godef": data.collectDefinitions,
|
||||
"typdef": data.collectTypeDefinitions,
|
||||
"hover": data.collectHoverDefinitions,
|
||||
"highlight": data.collectHighlights,
|
||||
"refs": data.collectReferences,
|
||||
"rename": data.collectRenames,
|
||||
"prepare": data.collectPrepareRenames,
|
||||
"symbol": data.collectSymbols,
|
||||
"signature": data.collectSignatures,
|
||||
"snippet": data.collectCompletionSnippets,
|
||||
"link": data.collectLinks,
|
||||
"suggestedfix": data.collectSuggestedFixes,
|
||||
"diag": data.collectDiagnostics,
|
||||
"item": data.collectCompletionItems,
|
||||
"complete": data.collectCompletions(CompletionFull),
|
||||
"completePartial": data.collectCompletions(CompletionPartial),
|
||||
"fold": data.collectFoldingRanges,
|
||||
"format": data.collectFormats,
|
||||
"import": data.collectImports,
|
||||
"godef": data.collectDefinitions,
|
||||
"typdef": data.collectTypeDefinitions,
|
||||
"hover": data.collectHoverDefinitions,
|
||||
"highlight": data.collectHighlights,
|
||||
"refs": data.collectReferences,
|
||||
"rename": data.collectRenames,
|
||||
"prepare": data.collectPrepareRenames,
|
||||
"symbol": data.collectSymbols,
|
||||
"signature": data.collectSignatures,
|
||||
"snippet": data.collectCompletionSnippets,
|
||||
"link": data.collectLinks,
|
||||
"suggestedfix": data.collectSuggestedFixes,
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -567,8 +583,13 @@ func summarizeDiagnostics(i int, want []source.Diagnostic, got []source.Diagnost
|
|||
return msg.String()
|
||||
}
|
||||
|
||||
func (data *Data) collectCompletions(src span.Span, expected []token.Pos) {
|
||||
data.Completions[src] = expected
|
||||
func (data *Data) collectCompletions(typ CompletionTestType) func(span.Span, []token.Pos) {
|
||||
return func(src span.Span, expected []token.Pos) {
|
||||
data.Completions[src] = Completion{
|
||||
CompletionItems: expected,
|
||||
Type: typ,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (data *Data) collectCompletionItems(pos token.Pos, args []string) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue