blob: 995a49880911d9819050ea3e1a1c3e3a7e4d4976 [file] [log] [blame]
// Copyright 2023 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 workspace
import (
"strings"
"testing"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/tests/compare"
"golang.org/x/tools/internal/testenv"
. "golang.org/x/tools/gopls/internal/lsp/regtest"
)
func TestQuickFix_UseModule(t *testing.T) {
testenv.NeedsGo1Point(t, 18) // needs go.work
const files = `
-- go.work --
go 1.20
use (
./a
)
-- a/go.mod --
module mod.com/a
go 1.18
-- a/main.go --
package main
import "mod.com/a/lib"
func main() {
_ = lib.C
}
-- a/lib/lib.go --
package lib
const C = "b"
-- b/go.mod --
module mod.com/b
go 1.18
-- b/main.go --
package main
import "mod.com/b/lib"
func main() {
_ = lib.C
}
-- b/lib/lib.go --
package lib
const C = "b"
`
for _, title := range []string{
"Use this module",
"Use all modules",
} {
t.Run(title, func(t *testing.T) {
Run(t, files, func(t *testing.T, env *Env) {
env.OpenFile("b/main.go")
var d protocol.PublishDiagnosticsParams
env.AfterChange(ReadDiagnostics("b/main.go", &d))
fixes := env.GetQuickFixes("b/main.go", d.Diagnostics)
var toApply []protocol.CodeAction
for _, fix := range fixes {
if strings.Contains(fix.Title, title) {
toApply = append(toApply, fix)
}
}
if len(toApply) != 1 {
t.Fatalf("codeAction: got %d quick fixes matching %q, want 1; got: %v", len(toApply), title, toApply)
}
env.ApplyCodeAction(toApply[0])
env.AfterChange(NoDiagnostics())
want := `go 1.20
use (
./a
./b
)
`
got := env.ReadWorkspaceFile("go.work")
if diff := compare.Text(want, got); diff != "" {
t.Errorf("unexpeced go.work content:\n%s", diff)
}
})
})
}
}
func TestQuickFix_AddGoWork(t *testing.T) {
testenv.NeedsGo1Point(t, 18) // needs go.work
const files = `
-- a/go.mod --
module mod.com/a
go 1.18
-- a/main.go --
package main
import "mod.com/a/lib"
func main() {
_ = lib.C
}
-- a/lib/lib.go --
package lib
const C = "b"
-- b/go.mod --
module mod.com/b
go 1.18
-- b/main.go --
package main
import "mod.com/b/lib"
func main() {
_ = lib.C
}
-- b/lib/lib.go --
package lib
const C = "b"
`
tests := []struct {
name string
file string
title string
want string // expected go.work content, excluding go directive line
}{
{
"use b",
"b/main.go",
"Add a go.work file using this module",
`
use ./b
`,
},
{
"use a",
"a/main.go",
"Add a go.work file using this module",
`
use ./a
`,
},
{
"use all",
"a/main.go",
"Add a go.work file using all modules",
`
use (
./a
./b
)
`,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
Run(t, files, func(t *testing.T, env *Env) {
env.OpenFile(test.file)
var d protocol.PublishDiagnosticsParams
env.AfterChange(ReadDiagnostics(test.file, &d))
fixes := env.GetQuickFixes(test.file, d.Diagnostics)
var toApply []protocol.CodeAction
for _, fix := range fixes {
if strings.Contains(fix.Title, test.title) {
toApply = append(toApply, fix)
}
}
if len(toApply) != 1 {
t.Fatalf("codeAction: got %d quick fixes matching %q, want 1; got: %v", len(toApply), test.title, toApply)
}
env.ApplyCodeAction(toApply[0])
env.AfterChange(
NoDiagnostics(ForFile(test.file)),
)
got := env.ReadWorkspaceFile("go.work")
// Ignore the `go` directive, which we assume is on the first line of
// the go.work file. This allows the test to be independent of go version.
got = strings.Join(strings.Split(got, "\n")[1:], "\n")
if diff := compare.Text(test.want, got); diff != "" {
t.Errorf("unexpected go.work content:\n%s", diff)
}
})
})
}
}
func TestQuickFix_UnsavedGoWork(t *testing.T) {
testenv.NeedsGo1Point(t, 18) // needs go.work
const files = `
-- go.work --
go 1.21
use (
./a
)
-- a/go.mod --
module mod.com/a
go 1.18
-- a/main.go --
package main
func main() {}
-- b/go.mod --
module mod.com/b
go 1.18
-- b/main.go --
package main
func main() {}
`
for _, title := range []string{
"Use this module",
"Use all modules",
} {
t.Run(title, func(t *testing.T) {
Run(t, files, func(t *testing.T, env *Env) {
env.OpenFile("go.work")
env.OpenFile("b/main.go")
env.RegexpReplace("go.work", "go 1.21", "go 1.21 // arbitrary comment")
var d protocol.PublishDiagnosticsParams
env.AfterChange(ReadDiagnostics("b/main.go", &d))
fixes := env.GetQuickFixes("b/main.go", d.Diagnostics)
var toApply []protocol.CodeAction
for _, fix := range fixes {
if strings.Contains(fix.Title, title) {
toApply = append(toApply, fix)
}
}
if len(toApply) != 1 {
t.Fatalf("codeAction: got %d quick fixes matching %q, want 1; got: %v", len(toApply), title, toApply)
}
fix := toApply[0]
err := env.Editor.ApplyCodeAction(env.Ctx, fix)
if err == nil {
t.Fatalf("codeAction(%q) succeeded unexpectedly", fix.Title)
}
if got := err.Error(); !strings.Contains(got, "must save") {
t.Errorf("codeAction(%q) returned error %q, want containing \"must save\"", fix.Title, err)
}
})
})
}
}
func TestQuickFix_GOWORKOff(t *testing.T) {
testenv.NeedsGo1Point(t, 18) // needs go.work
const files = `
-- go.work --
go 1.21
use (
./a
)
-- a/go.mod --
module mod.com/a
go 1.18
-- a/main.go --
package main
func main() {}
-- b/go.mod --
module mod.com/b
go 1.18
-- b/main.go --
package main
func main() {}
`
for _, title := range []string{
"Use this module",
"Use all modules",
} {
t.Run(title, func(t *testing.T) {
WithOptions(
EnvVars{"GOWORK": "off"},
).Run(t, files, func(t *testing.T, env *Env) {
env.OpenFile("go.work")
env.OpenFile("b/main.go")
var d protocol.PublishDiagnosticsParams
env.AfterChange(ReadDiagnostics("b/main.go", &d))
fixes := env.GetQuickFixes("b/main.go", d.Diagnostics)
var toApply []protocol.CodeAction
for _, fix := range fixes {
if strings.Contains(fix.Title, title) {
toApply = append(toApply, fix)
}
}
if len(toApply) != 1 {
t.Fatalf("codeAction: got %d quick fixes matching %q, want 1; got: %v", len(toApply), title, toApply)
}
fix := toApply[0]
err := env.Editor.ApplyCodeAction(env.Ctx, fix)
if err == nil {
t.Fatalf("codeAction(%q) succeeded unexpectedly", fix.Title)
}
if got := err.Error(); !strings.Contains(got, "GOWORK=off") {
t.Errorf("codeAction(%q) returned error %q, want containing \"GOWORK=off\"", fix.Title, err)
}
})
})
}
}