blob: 8f2e17c194923960ea309e579d0d9185cec2e0c7 [file] [log] [blame]
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package mod_test
import (
"context"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"testing"
"golang.org/x/tools/internal/lsp/cache"
"golang.org/x/tools/internal/lsp/mod"
"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"
)
func TestMain(m *testing.M) {
testenv.ExitIfSmallMachine()
os.Exit(m.Run())
}
// 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 TestModfileRemainsUnchanged(t *testing.T) {
ctx := tests.Context(t)
cache := cache.New(nil)
session := cache.NewSession(ctx)
options := tests.DefaultOptions()
options.TempModfile = true
options.Env = append(os.Environ(), "GOPACKAGESDRIVER=off", "GOROOT=")
// Make sure to copy the test directory to a temporary directory so we do not
// modify the test code or add go.sum files when we run the tests.
folder, err := copyToTempDir(filepath.Join("testdata", "unchanged"))
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(folder)
before, err := ioutil.ReadFile(filepath.Join(folder, "go.mod"))
if err != nil {
t.Fatal(err)
}
_, snapshot, err := session.NewView(ctx, "diagnostics_test", span.FileURI(folder), options)
if err != nil {
t.Fatal(err)
}
if !hasTempModfile(ctx, snapshot) {
return
}
after, err := ioutil.ReadFile(filepath.Join(folder, "go.mod"))
if err != nil {
t.Fatal(err)
}
if string(before) != string(after) {
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(ctx)
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 not be an indirect 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: getPos(4, 0), End: getPos(4, 61)},
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: getPos(4, 0), End: getPos(4, 61)},
Severity: protocol.SeverityWarning,
},
},
},
{
testdir: "invalidrequire",
want: []source.Diagnostic{
{
Message: "usage: require module/path v1.2.3",
Source: "go mod tidy",
Range: protocol.Range{Start: getPos(4, 0), End: getPos(4, 17)},
Severity: protocol.SeverityError,
},
},
},
{
testdir: "invalidgo",
want: []source.Diagnostic{
{
Message: "usage: go 1.23",
Source: "go mod tidy",
Range: protocol.Range{Start: getPos(2, 0), End: getPos(2, 4)},
Severity: protocol.SeverityError,
},
},
},
{
testdir: "unknowndirective",
want: []source.Diagnostic{
{
Message: "unknown directive: yo",
Source: "go mod tidy",
Range: protocol.Range{Start: getPos(6, 0), End: getPos(6, 2)},
Severity: protocol.SeverityError,
},
},
},
} {
t.Run(tt.testdir, func(t *testing.T) {
// Make sure to copy the test directory to a temporary directory so we do not
// modify the test code or add go.sum files when we run the tests.
folder, err := copyToTempDir(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
}
fileID, got, err := mod.Diagnostics(ctx, snapshot)
if err != nil {
t.Fatal(err)
}
if diff := tests.DiffDiagnostics(fileID.URI, tt.want, got); diff != "" {
t.Error(diff)
}
})
}
}
func hasTempModfile(ctx context.Context, snapshot source.Snapshot) bool {
_, t, _ := snapshot.ModFiles(ctx)
return t != nil
}
func copyToTempDir(folder string) (string, error) {
if _, err := os.Stat(folder); err != nil {
return "", err
}
dst, err := ioutil.TempDir("", "modfile_test")
if err != nil {
return "", err
}
fds, err := ioutil.ReadDir(folder)
if err != nil {
return "", err
}
for _, fd := range fds {
srcfp := path.Join(folder, fd.Name())
dstfp := path.Join(dst, fd.Name())
stat, err := os.Stat(srcfp)
if err != nil {
return "", err
}
if !stat.Mode().IsRegular() {
return "", fmt.Errorf("cannot copy non regular file %s", srcfp)
}
contents, err := ioutil.ReadFile(srcfp)
if err != nil {
return "", err
}
ioutil.WriteFile(dstfp, contents, stat.Mode())
}
return dst, nil
}
func getPos(line, character int) protocol.Position {
return protocol.Position{
Line: float64(line),
Character: float64(character),
}
}