mirror of
https://github.com/golang/go
synced 2024-11-18 19:44:46 -07:00
internal/lsp: add some basic tests for imports
This change adds a few simple tests for the goimports behavior of gopls. There are still missing cases for non-standard library, but this is a good start. Change-Id: I2f9bc2cc876dcabf81413384b83fa3508517adf0 Reviewed-on: https://go-review.googlesource.com/c/tools/+/179918 Run-TryBot: Rebecca Stambler <rstambler@golang.org> Reviewed-by: Ian Cottrell <iancottrell@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
70bf279967
commit
8aaa1484dc
@ -61,6 +61,10 @@ func (r *runner) Link(t *testing.T, data tests.Links) {
|
|||||||
//TODO: add command line link tests when it works
|
//TODO: add command line link tests when it works
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *runner) Import(t *testing.T, data tests.Imports) {
|
||||||
|
//TODO: add command line imports tests when it works
|
||||||
|
}
|
||||||
|
|
||||||
func captureStdOut(t testing.TB, f func()) string {
|
func captureStdOut(t testing.TB, f func()) string {
|
||||||
r, out, err := os.Pipe()
|
r, out, err := os.Pipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -322,6 +322,53 @@ func (r *runner) Format(t *testing.T, data tests.Formats) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *runner) Import(t *testing.T, data tests.Imports) {
|
||||||
|
ctx := context.Background()
|
||||||
|
for _, spn := range data {
|
||||||
|
uri := spn.URI()
|
||||||
|
filename, err := uri.Filename()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
goimported := string(r.data.Golden("goimports", filename, func() ([]byte, error) {
|
||||||
|
cmd := exec.Command("goimports", filename)
|
||||||
|
out, _ := cmd.Output() // ignore error, sometimes we have intentionally ungofmt-able files
|
||||||
|
return out, nil
|
||||||
|
}))
|
||||||
|
|
||||||
|
actions, err := r.server.CodeAction(context.Background(), &protocol.CodeActionParams{
|
||||||
|
TextDocument: protocol.TextDocumentIdentifier{
|
||||||
|
URI: protocol.NewURI(uri),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
if goimported != "" {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
_, m, err := getSourceFile(ctx, r.server.session.ViewOf(uri), uri)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
var edits []protocol.TextEdit
|
||||||
|
for _, a := range actions {
|
||||||
|
if a.Title == "Organize Imports" {
|
||||||
|
edits = (*a.Edit.Changes)[string(uri)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sedits, err := FromProtocolEdits(m, edits)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
ops := source.EditsToDiff(sedits)
|
||||||
|
got := strings.Join(diff.ApplyEdits(diff.SplitLines(string(m.Content)), ops), "")
|
||||||
|
if goimported != got {
|
||||||
|
t.Errorf("import failed for %s, expected:\n%v\ngot:\n%v", filename, goimported, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (r *runner) Definition(t *testing.T, data tests.Definitions) {
|
func (r *runner) Definition(t *testing.T, data tests.Definitions) {
|
||||||
for _, d := range data {
|
for _, d := range data {
|
||||||
sm, err := r.mapper(d.Src.URI())
|
sm, err := r.mapper(d.Src.URI())
|
||||||
|
@ -64,7 +64,7 @@ func Imports(ctx context.Context, f GoFile, rng span.Range) ([]TextEdit, error)
|
|||||||
if tok == nil {
|
if tok == nil {
|
||||||
return nil, fmt.Errorf("no token file for %s", f.URI())
|
return nil, fmt.Errorf("no token file for %s", f.URI())
|
||||||
}
|
}
|
||||||
formatted, err := imports.Process(f.GetToken(ctx).Name(), fc.Data, nil)
|
formatted, err := imports.Process(tok.Name(), fc.Data, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -308,6 +308,47 @@ func (r *runner) Format(t *testing.T, data tests.Formats) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *runner) Import(t *testing.T, data tests.Imports) {
|
||||||
|
ctx := context.Background()
|
||||||
|
for _, spn := range data {
|
||||||
|
uri := spn.URI()
|
||||||
|
filename, err := uri.Filename()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
goimported := string(r.data.Golden("goimports", filename, func() ([]byte, error) {
|
||||||
|
cmd := exec.Command("goimports", filename)
|
||||||
|
out, _ := cmd.Output() // ignore error, sometimes we have intentionally ungofmt-able files
|
||||||
|
return out, nil
|
||||||
|
}))
|
||||||
|
f, err := r.view.GetFile(ctx, uri)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed for %v: %v", spn, err)
|
||||||
|
}
|
||||||
|
rng, err := spn.Range(span.NewTokenConverter(f.FileSet(), f.GetToken(ctx)))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed for %v: %v", spn, err)
|
||||||
|
}
|
||||||
|
edits, err := source.Imports(ctx, f.(source.GoFile), rng)
|
||||||
|
if err != nil {
|
||||||
|
if goimported != "" {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ops := source.EditsToDiff(edits)
|
||||||
|
fc := f.Content(ctx)
|
||||||
|
if fc.Error != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
got := strings.Join(diff.ApplyEdits(diff.SplitLines(string(fc.Data)), ops), "")
|
||||||
|
if goimported != got {
|
||||||
|
t.Errorf("import failed for %s, expected:\n%v\ngot:\n%v", filename, goimported, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (r *runner) Definition(t *testing.T, data tests.Definitions) {
|
func (r *runner) Definition(t *testing.T, data tests.Definitions) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
for _, d := range data {
|
for _, d := range data {
|
||||||
|
7
internal/lsp/testdata/imports/good_imports.go
vendored
Normal file
7
internal/lsp/testdata/imports/good_imports.go
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package imports //@import("package")
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
fmt.Println("")
|
||||||
|
}
|
11
internal/lsp/testdata/imports/good_imports.go.golden
vendored
Normal file
11
internal/lsp/testdata/imports/good_imports.go.golden
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
-- goimports --
|
||||||
|
package imports //@import("package")
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
fmt.Println("")
|
||||||
|
}
|
||||||
|
|
||||||
|
-- goimports-d --
|
||||||
|
|
6
internal/lsp/testdata/imports/needs_imports.go
vendored
Normal file
6
internal/lsp/testdata/imports/needs_imports.go
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package imports //@import("package")
|
||||||
|
|
||||||
|
func goodbye() {
|
||||||
|
fmt.Printf("HI")
|
||||||
|
log.Printf("byeeeee")
|
||||||
|
}
|
27
internal/lsp/testdata/imports/needs_imports.go.golden
vendored
Normal file
27
internal/lsp/testdata/imports/needs_imports.go.golden
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
-- goimports --
|
||||||
|
package imports //@import("package")
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func goodbye() {
|
||||||
|
fmt.Printf("HI")
|
||||||
|
log.Printf("byeeeee")
|
||||||
|
}
|
||||||
|
|
||||||
|
-- goimports-d --
|
||||||
|
--- imports/needs_imports.go.orig
|
||||||
|
+++ imports/needs_imports.go
|
||||||
|
@@ -1,5 +1,10 @@
|
||||||
|
package imports //@import("package")
|
||||||
|
|
||||||
|
+import (
|
||||||
|
+ "fmt"
|
||||||
|
+ "log"
|
||||||
|
+)
|
||||||
|
+
|
||||||
|
func goodbye() {
|
||||||
|
fmt.Printf("HI")
|
||||||
|
log.Printf("byeeeee")
|
@ -11,9 +11,7 @@ import (
|
|||||||
"go/parser"
|
"go/parser"
|
||||||
"go/token"
|
"go/token"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@ -32,6 +30,7 @@ const (
|
|||||||
ExpectedCompletionSnippetCount = 14
|
ExpectedCompletionSnippetCount = 14
|
||||||
ExpectedDiagnosticsCount = 17
|
ExpectedDiagnosticsCount = 17
|
||||||
ExpectedFormatCount = 5
|
ExpectedFormatCount = 5
|
||||||
|
ExpectedImportCount = 2
|
||||||
ExpectedDefinitionsCount = 35
|
ExpectedDefinitionsCount = 35
|
||||||
ExpectedTypeDefinitionsCount = 2
|
ExpectedTypeDefinitionsCount = 2
|
||||||
ExpectedHighlightsCount = 2
|
ExpectedHighlightsCount = 2
|
||||||
@ -54,6 +53,7 @@ type CompletionItems map[token.Pos]*source.CompletionItem
|
|||||||
type Completions map[span.Span][]token.Pos
|
type Completions map[span.Span][]token.Pos
|
||||||
type CompletionSnippets map[span.Span]CompletionSnippet
|
type CompletionSnippets map[span.Span]CompletionSnippet
|
||||||
type Formats []span.Span
|
type Formats []span.Span
|
||||||
|
type Imports []span.Span
|
||||||
type Definitions map[span.Span]Definition
|
type Definitions map[span.Span]Definition
|
||||||
type Highlights map[string][]span.Span
|
type Highlights map[string][]span.Span
|
||||||
type Symbols map[span.URI][]source.Symbol
|
type Symbols map[span.URI][]source.Symbol
|
||||||
@ -69,6 +69,7 @@ type Data struct {
|
|||||||
Completions Completions
|
Completions Completions
|
||||||
CompletionSnippets CompletionSnippets
|
CompletionSnippets CompletionSnippets
|
||||||
Formats Formats
|
Formats Formats
|
||||||
|
Imports Imports
|
||||||
Definitions Definitions
|
Definitions Definitions
|
||||||
Highlights Highlights
|
Highlights Highlights
|
||||||
Symbols Symbols
|
Symbols Symbols
|
||||||
@ -86,6 +87,7 @@ type Tests interface {
|
|||||||
Diagnostics(*testing.T, Diagnostics)
|
Diagnostics(*testing.T, Diagnostics)
|
||||||
Completion(*testing.T, Completions, CompletionSnippets, CompletionItems)
|
Completion(*testing.T, Completions, CompletionSnippets, CompletionItems)
|
||||||
Format(*testing.T, Formats)
|
Format(*testing.T, Formats)
|
||||||
|
Import(*testing.T, Imports)
|
||||||
Definition(*testing.T, Definitions)
|
Definition(*testing.T, Definitions)
|
||||||
Highlight(*testing.T, Highlights)
|
Highlight(*testing.T, Highlights)
|
||||||
Symbol(*testing.T, Symbols)
|
Symbol(*testing.T, Symbols)
|
||||||
@ -202,6 +204,7 @@ func Load(t testing.TB, exporter packagestest.Exporter, dir string) *Data {
|
|||||||
"item": data.collectCompletionItems,
|
"item": data.collectCompletionItems,
|
||||||
"complete": data.collectCompletions,
|
"complete": data.collectCompletions,
|
||||||
"format": data.collectFormats,
|
"format": data.collectFormats,
|
||||||
|
"import": data.collectImports,
|
||||||
"godef": data.collectDefinitions,
|
"godef": data.collectDefinitions,
|
||||||
"typdef": data.collectTypeDefinitions,
|
"typdef": data.collectTypeDefinitions,
|
||||||
"hover": data.collectHoverDefinitions,
|
"hover": data.collectHoverDefinitions,
|
||||||
@ -256,20 +259,20 @@ func Run(t *testing.T, tests Tests, data *Data) {
|
|||||||
|
|
||||||
t.Run("Format", func(t *testing.T) {
|
t.Run("Format", func(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
if _, err := exec.LookPath("gofmt"); err != nil {
|
|
||||||
switch runtime.GOOS {
|
|
||||||
case "android":
|
|
||||||
t.Skip("gofmt is not installed")
|
|
||||||
default:
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(data.Formats) != ExpectedFormatCount {
|
if len(data.Formats) != ExpectedFormatCount {
|
||||||
t.Errorf("got %v formats expected %v", len(data.Formats), ExpectedFormatCount)
|
t.Errorf("got %v formats expected %v", len(data.Formats), ExpectedFormatCount)
|
||||||
}
|
}
|
||||||
tests.Format(t, data.Formats)
|
tests.Format(t, data.Formats)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("Import", func(t *testing.T) {
|
||||||
|
t.Helper()
|
||||||
|
if len(data.Imports) != ExpectedImportCount {
|
||||||
|
t.Errorf("got %v imports expected %v", len(data.Imports), ExpectedImportCount)
|
||||||
|
}
|
||||||
|
tests.Import(t, data.Imports)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("Definition", func(t *testing.T) {
|
t.Run("Definition", func(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
if len(data.Definitions) != ExpectedDefinitionsCount {
|
if len(data.Definitions) != ExpectedDefinitionsCount {
|
||||||
@ -416,6 +419,10 @@ func (data *Data) collectFormats(spn span.Span) {
|
|||||||
data.Formats = append(data.Formats, spn)
|
data.Formats = append(data.Formats, spn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (data *Data) collectImports(spn span.Span) {
|
||||||
|
data.Imports = append(data.Imports, spn)
|
||||||
|
}
|
||||||
|
|
||||||
func (data *Data) collectDefinitions(src, target span.Span) {
|
func (data *Data) collectDefinitions(src, target span.Span) {
|
||||||
data.Definitions[src] = Definition{
|
data.Definitions[src] = Definition{
|
||||||
Src: src,
|
Src: src,
|
||||||
|
Loading…
Reference in New Issue
Block a user