1
0
mirror of https://github.com/golang/go synced 2024-11-18 09:04:49 -07:00

internal/lsp/tests: standardize testdata folder format

This change standardizes the folder structure for testdata that are used for testing the lsp. In particular, it uses the following format:
- dir
  - primarymod
    - .go files
    - packages
    - go.mod (optional)
  - modules
    - repoa
      - mod1
        - .go files
        -  packages
        - go.mod (optional)

As we can see, any folder inside of testdata should be of this format, where the primary test files with the markers are all located inside the primarymod folder. The modules folder is used to hold any potential dependencies that are used for testing.

A consequence of this change is that we can have one directory separated by folders, where each folder is it's own module, this allows us to use internal/lsp/tests with go.mod files. Now, tests.Load() will return an array of Data objects, where each object corresponds to one of the directories structured above.

Updates golang/go#36091

Change-Id: I437cc2a2a9fc1bac93779845737aa74383fbf9c3
Reviewed-on: https://go-review.googlesource.com/c/tools/+/217541
Run-TryBot: Rohan Challa <rohan@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Rohan Challa 2020-02-03 11:12:57 -05:00
parent dd0d5d4851
commit 2529d2857a
206 changed files with 571 additions and 381 deletions

View File

@ -303,6 +303,110 @@ func Copy(source string) Writer {
}
}
// GroupFilesByModules attempts to map directories to the modules within each directory.
// This function assumes that the folder is structured in the following way:
// - dir
// - primarymod
// - .go files
// - packages
// - go.mod (optional)
// - modules
// - repoa
// - mod1
// - .go files
// - packages
// - go.mod (optional)
// It scans the directory tree anchored at root and adds a Copy writer to the
// map for every file found.
// This is to enable the common case in tests where you have a full copy of the
// package in your testdata.
func GroupFilesByModules(root string) ([]Module, error) {
root = filepath.FromSlash(root)
primarymodPath := filepath.Join(root, "primarymod")
_, err := os.Stat(primarymodPath)
if os.IsNotExist(err) {
return nil, fmt.Errorf("could not find primarymod folder within %s", root)
}
primarymod := &Module{
Name: root,
Files: make(map[string]interface{}),
Overlay: make(map[string][]byte),
}
mods := map[string]*Module{
root: primarymod,
}
modules := []Module{*primarymod}
if err := filepath.Walk(primarymodPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
fragment, err := filepath.Rel(primarymodPath, path)
if err != nil {
return err
}
primarymod.Files[filepath.ToSlash(fragment)] = Copy(path)
return nil
}); err != nil {
return nil, err
}
modulesPath := filepath.Join(root, "modules")
if _, err := os.Stat(modulesPath); os.IsNotExist(err) {
return modules, nil
}
var currentRepo, currentModule string
if err := filepath.Walk(modulesPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// If the path is not a directory, then we want to add the path to
// the files map of the currentModule.
if !info.IsDir() {
fragment, err := filepath.Rel(currentModule, path)
if err != nil {
return err
}
mods[currentModule].Files[filepath.ToSlash(fragment)] = Copy(path)
return nil
}
// If the path is a directory and it's enclosing folder is equal to
// the modules folder, then the path is a new repo.
if filepath.Dir(path) == modulesPath {
currentRepo = path
return nil
}
// If the path is a directory and it's enclosing folder is not the same
// as the current repo, then the path is a folder/package of the current module.
if filepath.Dir(path) != currentRepo {
return nil
}
// If the path is a directory and it's enclosing folder is the current repo
// then the path is a new module.
module, err := filepath.Rel(modulesPath, path)
if err != nil {
return err
}
mods[path] = &Module{
Name: filepath.ToSlash(module),
Files: make(map[string]interface{}),
Overlay: make(map[string][]byte),
}
currentModule = path
modules = append(modules, *mods[path])
return nil
}); err != nil {
return nil, err
}
return modules, nil
}
// MustCopyFileTree returns a file set for a module based on a real directory tree.
// It scans the directory tree anchored at root and adds a Copy writer to the
// map for every file found.

View File

@ -26,3 +26,79 @@ func TestGOPATHExport(t *testing.T) {
{"golang.org/fake2/v2", "other/a.go", "fake2_v2/src/golang.org/fake2/v2/other/a.go", checkContent("package fake2")},
})
}
func TestGroupFilesByModules(t *testing.T) {
for _, tt := range []struct {
testdir string
want []packagestest.Module
}{
{
testdir: "testdata/groups/one",
want: []packagestest.Module{
{
Name: "testdata/groups/one",
Files: map[string]interface{}{
"main.go": true,
},
},
{
Name: "example.com/extra",
Files: map[string]interface{}{
"help.go": true,
},
},
},
},
{
testdir: "testdata/groups/two",
want: []packagestest.Module{
{
Name: "testdata/groups/two",
Files: map[string]interface{}{
"main.go": true,
"expect/yo.go": true,
},
},
{
Name: "example.com/extra",
Files: map[string]interface{}{
"me.go": true,
"geez/help.go": true,
},
},
{
Name: "example.com/tempmod",
Files: map[string]interface{}{
"main.go": true,
},
},
},
},
} {
t.Run(tt.testdir, func(t *testing.T) {
got, err := packagestest.GroupFilesByModules(tt.testdir)
if err != nil {
t.Fatalf("could not group files %v", err)
}
if len(got) != len(tt.want) {
t.Fatalf("%s: wanted %d modules but got %d", tt.testdir, len(tt.want), len(got))
}
for i, w := range tt.want {
g := got[i]
if filepath.FromSlash(g.Name) != filepath.FromSlash(w.Name) {
t.Fatalf("%s: wanted module[%d].Name to be %s but got %s", tt.testdir, i, filepath.FromSlash(w.Name), filepath.FromSlash(g.Name))
}
for fh := range w.Files {
if _, ok := g.Files[fh]; !ok {
t.Fatalf("%s, module[%d]: wanted %s but could not find", tt.testdir, i, fh)
}
}
for fh := range g.Files {
if _, ok := w.Files[fh]; !ok {
t.Fatalf("%s, module[%d]: found unexpected file %s", tt.testdir, i, fh)
}
}
}
})
}
}

View File

@ -125,7 +125,6 @@ func (modules) Finalize(exported *Exported) error {
if err := invokeGo(exported.Config, "mod", "download"); err != nil {
return err
}
return nil
}
@ -204,7 +203,7 @@ func modCache(exported *Exported) string {
}
func primaryDir(exported *Exported) string {
return filepath.Join(exported.temp, "primarymod", path.Base(exported.primary))
return filepath.Join(exported.temp, path.Base(exported.primary))
}
func moduleDir(exported *Exported, module string) string {

View File

@ -17,14 +17,14 @@ func TestModulesExport(t *testing.T) {
exported := packagestest.Export(t, packagestest.Modules, testdata)
defer exported.Cleanup()
// Check that the cfg contains all the right bits
var expectDir = filepath.Join(exported.Temp(), "primarymod/fake1")
var expectDir = filepath.Join(exported.Temp(), "fake1")
if exported.Config.Dir != expectDir {
t.Errorf("Got working directory %v expected %v", exported.Config.Dir, expectDir)
}
checkFiles(t, exported, []fileTest{
{"golang.org/fake1", "go.mod", "primarymod/fake1/go.mod", nil},
{"golang.org/fake1", "a.go", "primarymod/fake1/a.go", checkLink("testdata/a.go")},
{"golang.org/fake1", "b.go", "primarymod/fake1/b.go", checkContent("package fake1")},
{"golang.org/fake1", "go.mod", "fake1/go.mod", nil},
{"golang.org/fake1", "a.go", "fake1/a.go", checkLink("testdata/a.go")},
{"golang.org/fake1", "b.go", "fake1/b.go", checkContent("package fake1")},
{"golang.org/fake2", "go.mod", "modcache/pkg/mod/golang.org/fake2@v1.0.0/go.mod", nil},
{"golang.org/fake2", "other/a.go", "modcache/pkg/mod/golang.org/fake2@v1.0.0/other/a.go", checkContent("package fake2")},
{"golang.org/fake2/v2", "other/a.go", "modcache/pkg/mod/golang.org/fake2/v2@v2.0.0/other/a.go", checkContent("package fake2")},

View File

@ -0,0 +1 @@
package extra

View File

@ -0,0 +1 @@
package one

View File

@ -0,0 +1 @@
package example.com/extra/geez

View File

@ -0,0 +1 @@
package example.com/extra

View File

@ -0,0 +1 @@
package example.com/tempmod

View File

@ -0,0 +1 @@
package expect

View File

@ -0,0 +1 @@
package two

View File

@ -37,6 +37,11 @@ func testCommandLine(t *testing.T, exporter packagestest.Exporter) {
t.Skip("testdata directory not present")
}
data := tests.Load(t, exporter, testdata)
defer data.Exported.Cleanup()
tests.Run(t, cmdtest.NewRunner(exporter, data, tests.Context(t), commandLineOptions), data)
for _, data := range data {
defer data.Exported.Cleanup()
t.Run(data.Folder, func(t *testing.T) {
t.Helper()
tests.Run(t, cmdtest.NewRunner(exporter, data, tests.Context(t), commandLineOptions), data)
})
}
}

View File

@ -30,8 +30,13 @@ func TestCommandLine(t *testing.T) {
func testCommandLine(t *testing.T, exporter packagestest.Exporter) {
data := tests.Load(t, exporter, "../testdata")
defer data.Exported.Cleanup()
tests.Run(t, cmdtest.NewRunner(exporter, data, tests.Context(t), nil), data)
for _, datum := range data {
defer datum.Exported.Cleanup()
t.Run(datum.Folder, func(t *testing.T) {
t.Helper()
tests.Run(t, cmdtest.NewRunner(exporter, datum, tests.Context(t), nil), datum)
})
}
}
func TestDefinitionHelpExample(t *testing.T) {

View File

@ -45,54 +45,58 @@ type runner struct {
ctx context.Context
}
const viewName = "lsp_test"
func testLSP(t *testing.T, exporter packagestest.Exporter) {
ctx := tests.Context(t)
data := tests.Load(t, exporter, "testdata")
defer data.Exported.Cleanup()
cache := cache.New(nil)
session := cache.NewSession()
options := tests.DefaultOptions()
session.SetOptions(options)
options.Env = data.Config.Env
if _, _, err := session.NewView(ctx, viewName, span.FileURI(data.Config.Dir), options); err != nil {
t.Fatal(err)
}
var modifications []source.FileModification
for filename, content := range data.Config.Overlay {
kind := source.DetectLanguage("", filename)
if kind != source.Go {
continue
for _, datum := range data {
defer datum.Exported.Cleanup()
cache := cache.New(nil)
session := cache.NewSession()
options := tests.DefaultOptions()
session.SetOptions(options)
options.Env = datum.Config.Env
if _, _, err := session.NewView(ctx, datum.Config.Dir, span.FileURI(datum.Config.Dir), options); err != nil {
t.Fatal(err)
}
modifications = append(modifications, source.FileModification{
URI: span.FileURI(filename),
Action: source.Open,
Version: -1,
Text: content,
LanguageID: "go",
var modifications []source.FileModification
for filename, content := range datum.Config.Overlay {
kind := source.DetectLanguage("", filename)
if kind != source.Go {
continue
}
modifications = append(modifications, source.FileModification{
URI: span.FileURI(filename),
Action: source.Open,
Version: -1,
Text: content,
LanguageID: "go",
})
}
if _, err := session.DidModifyFiles(ctx, modifications); err != nil {
t.Fatal(err)
}
r := &runner{
server: &Server{
session: session,
delivered: map[span.URI]sentDiagnostics{},
},
data: datum,
ctx: ctx,
}
t.Run(datum.Folder, func(t *testing.T) {
t.Helper()
tests.Run(t, r, datum)
})
}
if _, err := session.DidModifyFiles(ctx, modifications); err != nil {
t.Fatal(err)
}
r := &runner{
server: &Server{
session: session,
delivered: map[span.URI]sentDiagnostics{},
},
data: data,
ctx: ctx,
}
tests.Run(t, r, data)
}
func (r *runner) Diagnostics(t *testing.T, uri span.URI, want []source.Diagnostic) {
// Get the diagnostics for this view if we have not done it before.
if r.diagnostics == nil {
r.diagnostics = make(map[span.URI][]source.Diagnostic)
v := r.server.session.View(viewName)
v := r.server.session.View(r.data.Config.Dir)
// Always run diagnostics with analysis.
reports := r.server.diagnose(r.ctx, v.Snapshot(), true)
for key, diags := range reports {
@ -980,9 +984,9 @@ func TestModfileSuggestedFixes(t *testing.T) {
delivered: map[span.URI]sentDiagnostics{},
}
for _, tt := range []string{"indirect", "unused"} {
for _, tt := range []string{"indirect/primarymod", "unused/primarymod"} {
t.Run(tt, func(t *testing.T) {
folder, err := tests.CopyFolderToTempDir(filepath.Join("mod", "testdata", tt))
folder, err := tests.CopyFolderToTempDir(filepath.Join("testdata", tt))
if err != nil {
t.Fatal(err)
}

View File

@ -5,15 +5,12 @@
package mod
import (
"context"
"io/ioutil"
"os"
"path/filepath"
"testing"
"golang.org/x/tools/internal/lsp/cache"
"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"
"golang.org/x/tools/internal/testenv"
@ -50,7 +47,7 @@ func TestModfileRemainsUnchanged(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if !hasTempModfile(ctx, snapshot) {
if _, t := snapshot.View().ModFiles(); t == "" {
return
}
after, err := ioutil.ReadFile(filepath.Join(folder, "go.mod"))
@ -61,119 +58,3 @@ func TestModfileRemainsUnchanged(t *testing.T) {
t.Errorf("the real go.mod file was changed even when tempModfile=true")
}
}
// TODO(golang/go#36091): This file can be refactored to look like lsp_test.go
// when marker support gets added for go.mod files.
func TestDiagnostics(t *testing.T) {
ctx := tests.Context(t)
cache := cache.New(nil)
session := cache.NewSession()
options := tests.DefaultOptions()
options.TempModfile = true
options.Env = append(os.Environ(), "GOPACKAGESDRIVER=off", "GOROOT=")
for _, tt := range []struct {
testdir string
want []source.Diagnostic
}{
{
testdir: "indirect",
want: []source.Diagnostic{
{
Message: "golang.org/x/tools should be a direct dependency.",
Source: "go mod tidy",
// TODO(golang/go#36091): When marker support gets added for go.mod files, we
// can remove these hard coded positions.
Range: protocol.Range{Start: getRawPos(4, 62), End: getRawPos(4, 73)},
Severity: protocol.SeverityWarning,
},
},
},
{
testdir: "unused",
want: []source.Diagnostic{
{
Message: "golang.org/x/tools is not used in this module.",
Source: "go mod tidy",
Range: protocol.Range{Start: getRawPos(4, 0), End: getRawPos(4, 61)},
Severity: protocol.SeverityWarning,
},
},
},
{
testdir: "invalidrequire",
want: []source.Diagnostic{
{
Message: "usage: require module/path v1.2.3",
Source: "syntax",
Range: protocol.Range{Start: getRawPos(4, 0), End: getRawPos(4, 16)},
Severity: protocol.SeverityError,
},
},
},
{
testdir: "invalidgo",
want: []source.Diagnostic{
{
Message: "usage: go 1.23",
Source: "syntax",
Range: protocol.Range{Start: getRawPos(2, 0), End: getRawPos(2, 3)},
Severity: protocol.SeverityError,
},
},
},
{
testdir: "unknowndirective",
want: []source.Diagnostic{
{
Message: "unknown directive: yo",
Source: "syntax",
Range: protocol.Range{Start: getRawPos(6, 0), End: getRawPos(6, 1)},
Severity: protocol.SeverityError,
},
},
},
} {
t.Run(tt.testdir, func(t *testing.T) {
// TODO: Once we refactor this to work with go/packages/packagestest. We do not
// need to copy to a temporary directory.
folder, err := tests.CopyFolderToTempDir(filepath.Join("testdata", tt.testdir))
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(folder)
_, snapshot, err := session.NewView(ctx, "diagnostics_test", span.FileURI(folder), options)
if err != nil {
t.Fatal(err)
}
// TODO: Add testing for when the -modfile flag is turned off and we still get diagnostics.
if !hasTempModfile(ctx, snapshot) {
return
}
reports, _, err := Diagnostics(ctx, snapshot)
if err != nil {
t.Fatal(err)
}
if len(reports) != 1 {
t.Errorf("expected 1 diagnostic, got %d", len(reports))
}
for fh, got := range reports {
if diff := tests.DiffDiagnostics(fh.URI, tt.want, got); diff != "" {
t.Error(diff)
}
}
})
}
}
func hasTempModfile(ctx context.Context, snapshot source.Snapshot) bool {
_, t := snapshot.View().ModFiles()
return t != ""
}
func getRawPos(line, character int) protocol.Position {
return protocol.Position{
Line: float64(line),
Character: float64(character),
}
}

View File

@ -1,5 +0,0 @@
module invalidgo
go 1
require golang.org/x/tools v0.0.0-20191219192050-56b0b28a00f7 // indirect

View File

@ -1,10 +0,0 @@
// Package invalidgo does something
package invalidgo
import (
"golang.org/x/tools/go/packages"
)
func Yo() {
var _ packages.Config
}

View File

@ -1,5 +0,0 @@
module invalidrequire
go 1.12
require golang.or

View File

@ -1,10 +0,0 @@
// Package invalidrequire does something
package invalidrequire
import (
"golang.org/x/tools/go/packages"
)
func Yo() {
var _ packages.Config
}

View File

@ -0,0 +1,24 @@
-- summary --
CompletionsCount = 0
CompletionSnippetCount = 0
UnimportedCompletionsCount = 0
DeepCompletionsCount = 0
FuzzyCompletionsCount = 0
RankedCompletionsCount = 0
CaseSensitiveCompletionsCount = 0
DiagnosticsCount = 0
FoldingRangesCount = 0
FormatCount = 0
ImportCount = 0
SuggestedFixCount = 0
DefinitionsCount = 0
TypeDefinitionsCount = 0
HighlightsCount = 0
ReferencesCount = 0
RenamesCount = 0
PrepareRenamesCount = 0
SymbolsCount = 0
SignaturesCount = 0
LinksCount = 0
ImplementationsCount = 0

View File

@ -1,7 +0,0 @@
module unknowndirective
go 1.12
require golang.org/x/tools v0.0.0-20191219192050-56b0b28a00f7
yo

View File

@ -1,10 +0,0 @@
// Package unknowndirective does something
package unknowndirective
import (
"golang.org/x/tools/go/packages"
)
func Yo() {
var _ packages.Config
}

View File

@ -45,39 +45,44 @@ type runner struct {
func testSource(t *testing.T, exporter packagestest.Exporter) {
ctx := tests.Context(t)
data := tests.Load(t, exporter, "../testdata")
defer data.Exported.Cleanup()
for _, datum := range data {
defer datum.Exported.Cleanup()
cache := cache.New(nil)
session := cache.NewSession()
options := tests.DefaultOptions()
options.Env = data.Config.Env
view, _, err := session.NewView(ctx, "source_test", span.FileURI(data.Config.Dir), options)
if err != nil {
t.Fatal(err)
}
r := &runner{
view: view,
data: data,
ctx: ctx,
}
var modifications []source.FileModification
for filename, content := range data.Config.Overlay {
kind := source.DetectLanguage("", filename)
if kind != source.Go {
continue
cache := cache.New(nil)
session := cache.NewSession()
options := tests.DefaultOptions()
options.Env = datum.Config.Env
view, _, err := session.NewView(ctx, "source_test", span.FileURI(datum.Config.Dir), options)
if err != nil {
t.Fatal(err)
}
modifications = append(modifications, source.FileModification{
URI: span.FileURI(filename),
Action: source.Open,
Version: -1,
Text: content,
LanguageID: "go",
r := &runner{
view: view,
data: datum,
ctx: ctx,
}
var modifications []source.FileModification
for filename, content := range datum.Config.Overlay {
kind := source.DetectLanguage("", filename)
if kind != source.Go {
continue
}
modifications = append(modifications, source.FileModification{
URI: span.FileURI(filename),
Action: source.Open,
Version: -1,
Text: content,
LanguageID: "go",
})
}
if _, err := session.DidModifyFiles(ctx, modifications); err != nil {
t.Fatal(err)
}
t.Run(datum.Folder, func(t *testing.T) {
t.Helper()
tests.Run(t, r, datum)
})
}
if _, err := session.DidModifyFiles(ctx, modifications); err != nil {
t.Fatal(err)
}
tests.Run(t, r, data)
}
func (r *runner) Diagnostics(t *testing.T, uri span.URI, want []source.Diagnostic) {

View File

@ -0,0 +1,25 @@
-- summary --
CompletionsCount = 0
CompletionSnippetCount = 0
UnimportedCompletionsCount = 0
DeepCompletionsCount = 0
FuzzyCompletionsCount = 0
RankedCompletionsCount = 0
CaseSensitiveCompletionsCount = 0
DiagnosticsCount = 0
FoldingRangesCount = 0
FormatCount = 0
ImportCount = 0
SuggestedFixCount = 0
DefinitionsCount = 0
TypeDefinitionsCount = 0
HighlightsCount = 0
ReferencesCount = 0
RenamesCount = 0
PrepareRenamesCount = 0
SymbolsCount = 0
WorkspaceSymbolsCount = 0
SignaturesCount = 0
LinksCount = 0
ImplementationsCount = 0

View File

@ -0,0 +1,3 @@
package pkg
const Test = 1

Some files were not shown because too many files have changed in this diff Show More