Skip to content

Commit 109c5fc

Browse files
findleyrgopherbot
authored andcommitted
gopls/internal/test: fix path to local go in integration tests
As described in golang/go#69630, our 1.21 and 1.22 builders were not actually testing gopls' integration with the Go command, because go test modifies PATH and GOROOT when performing a toolchain switch. "Fix" this by searching PATH for a local (=non-toolchain) go command, and then mutating PATH and unsetting GOROOT to use this go command. This is very much a hack, as noted in the relevant commentary, but allows us to have much needed test coverage on the builders. In golang/go#69321, we hope to design a better solution to this problem. Many tests are updated to make their go version requirements accurate. Fixes golang/go#69630 Change-Id: I431107b97845e1e99799c2c22f33b04f85ce6dd9 Reviewed-on: https://go-review.googlesource.com/c/tools/+/623175 Reviewed-by: Alan Donovan <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Auto-Submit: Robert Findley <[email protected]>
1 parent 99e8fee commit 109c5fc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+166
-157
lines changed

gopls/internal/licenses/licenses_test.go

-7
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,9 @@ import (
1010
"os/exec"
1111
"runtime"
1212
"testing"
13-
14-
"golang.org/x/tools/internal/testenv"
1513
)
1614

1715
func TestLicenses(t *testing.T) {
18-
// License text differs for older Go versions because staticcheck or gofumpt
19-
// isn't supported for those versions, and this fails for unknown, unrelated
20-
// reasons on Kokoro legacy CI.
21-
testenv.NeedsGo1Point(t, 21)
22-
2316
if runtime.GOOS != "linux" && runtime.GOOS != "darwin" {
2417
t.Skip("generating licenses only works on Unixes")
2518
}

gopls/internal/test/integration/codelens/codelens_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,8 @@ func TestUpgradeCodelens_ModVendor(t *testing.T) {
252252
// This test checks the regression of golang/go#66055. The upgrade codelens
253253
// should work in a mod vendor context (the test above using a go.work file
254254
// was not broken).
255-
testenv.NeedsGo1Point(t, 22)
255+
testenv.NeedsGoCommand1Point(t, 22)
256+
256257
const shouldUpdateDep = `
257258
-- go.mod --
258259
module mod.com/a

gopls/internal/test/integration/codelens/gcdetails_test.go

-4
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,12 @@ import (
1414
. "golang.org/x/tools/gopls/internal/test/integration"
1515
"golang.org/x/tools/gopls/internal/test/integration/fake"
1616
"golang.org/x/tools/gopls/internal/util/bug"
17-
"golang.org/x/tools/internal/testenv"
1817
)
1918

2019
func TestGCDetails_Toggle(t *testing.T) {
2120
if runtime.GOOS == "android" {
2221
t.Skipf("the gc details code lens doesn't work on Android")
2322
}
24-
// The overlay portion of the test fails with go1.19.
25-
// I'm not sure why and not inclined to investigate.
26-
testenv.NeedsGo1Point(t, 20)
2723

2824
const mod = `
2925
-- go.mod --

gopls/internal/test/integration/diagnostics/diagnostics_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ func Hello() {
335335
InitialWorkspaceLoad,
336336
Diagnostics(env.AtRegexp("main.go", `"mod.com/bob"`)),
337337
)
338-
if err := env.Sandbox.RunGoCommand(env.Ctx, "", "mod", []string{"init", "mod.com"}, nil, true); err != nil {
338+
if _, err := env.Sandbox.RunGoCommand(env.Ctx, "", "mod", []string{"init", "mod.com"}, nil, true); err != nil {
339339
t.Fatal(err)
340340
}
341341
env.AfterChange(

gopls/internal/test/integration/fake/sandbox.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -234,10 +234,10 @@ func (sb *Sandbox) goCommandInvocation() gocommand.Invocation {
234234
return inv
235235
}
236236

237-
// RunGoCommand executes a go command in the sandbox. If checkForFileChanges is
238-
// true, the sandbox scans the working directory and emits file change events
239-
// for any file changes it finds.
240-
func (sb *Sandbox) RunGoCommand(ctx context.Context, dir, verb string, args, env []string, checkForFileChanges bool) error {
237+
// RunGoCommand executes a go command in the sandbox and returns its standard
238+
// output. If checkForFileChanges is true, the sandbox scans the working
239+
// directory and emits file change events for any file changes it finds.
240+
func (sb *Sandbox) RunGoCommand(ctx context.Context, dir, verb string, args, env []string, checkForFileChanges bool) ([]byte, error) {
241241
inv := sb.goCommandInvocation()
242242
inv.Verb = verb
243243
inv.Args = args
@@ -247,7 +247,7 @@ func (sb *Sandbox) RunGoCommand(ctx context.Context, dir, verb string, args, env
247247
}
248248
stdout, stderr, _, err := sb.goCommandRunner.RunRaw(ctx, inv)
249249
if err != nil {
250-
return fmt.Errorf("go command failed (stdout: %s) (stderr: %s): %v", stdout.String(), stderr.String(), err)
250+
return nil, fmt.Errorf("go command failed (stdout: %s) (stderr: %s): %v", stdout.String(), stderr.String(), err)
251251
}
252252
// Since running a go command may result in changes to workspace files,
253253
// check if we need to send any "watched" file events.
@@ -256,10 +256,10 @@ func (sb *Sandbox) RunGoCommand(ctx context.Context, dir, verb string, args, env
256256
// for benchmarks. Consider refactoring.
257257
if sb.Workdir != nil && checkForFileChanges {
258258
if err := sb.Workdir.CheckForFileChanges(ctx); err != nil {
259-
return fmt.Errorf("checking for file changes: %w", err)
259+
return nil, fmt.Errorf("checking for file changes: %w", err)
260260
}
261261
}
262-
return nil
262+
return stdout.Bytes(), nil
263263
}
264264

265265
// GoVersion checks the version of the go command.
@@ -275,7 +275,7 @@ func (sb *Sandbox) Close() error {
275275
if sb.gopath != "" {
276276
// Important: run this command in RootDir so that it doesn't interact with
277277
// any toolchain downloads that may occur
278-
goCleanErr = sb.RunGoCommand(context.Background(), sb.RootDir(), "clean", []string{"-modcache"}, nil, false)
278+
_, goCleanErr = sb.RunGoCommand(context.Background(), sb.RootDir(), "clean", []string{"-modcache"}, nil, false)
279279
}
280280
err := robustio.RemoveAll(sb.rootdir)
281281
if err != nil || goCleanErr != nil {

gopls/internal/test/integration/misc/configuration_test.go

-7
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,6 @@ import (
1515
// Test that enabling and disabling produces the expected results of showing
1616
// and hiding staticcheck analysis results.
1717
func TestChangeConfiguration(t *testing.T) {
18-
// Staticcheck only supports Go versions >= 1.20.
19-
// Note: keep this in sync with TestStaticcheckWarning. Below this version we
20-
// should get an error when setting staticcheck configuration.
21-
testenv.NeedsGo1Point(t, 20)
22-
2318
const files = `
2419
-- go.mod --
2520
module mod.com
@@ -164,8 +159,6 @@ type B struct {
164159
//
165160
// Gopls should not get confused about buffer content when recreating the view.
166161
func TestMajorOptionsChange(t *testing.T) {
167-
testenv.NeedsGo1Point(t, 20) // needs staticcheck
168-
169162
const files = `
170163
-- go.mod --
171164
module mod.com

gopls/internal/test/integration/misc/formatting_test.go

-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010

1111
"golang.org/x/tools/gopls/internal/test/compare"
1212
. "golang.org/x/tools/gopls/internal/test/integration"
13-
"golang.org/x/tools/internal/testenv"
1413
)
1514

1615
const unformattedProgram = `
@@ -303,7 +302,6 @@ func main() {
303302
}
304303

305304
func TestGofumptFormatting(t *testing.T) {
306-
testenv.NeedsGo1Point(t, 20) // gofumpt requires go 1.20+
307305
// Exercise some gofumpt formatting rules:
308306
// - No empty lines following an assignment operator
309307
// - Octal integer literals should use the 0o prefix on modules using Go
@@ -367,8 +365,6 @@ const Bar = 42
367365
}
368366

369367
func TestGofumpt_Issue61692(t *testing.T) {
370-
testenv.NeedsGo1Point(t, 21)
371-
372368
const input = `
373369
-- go.mod --
374370
module foo

gopls/internal/test/integration/misc/hover_test.go

-6
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414
"golang.org/x/tools/gopls/internal/protocol"
1515
. "golang.org/x/tools/gopls/internal/test/integration"
1616
"golang.org/x/tools/gopls/internal/test/integration/fake"
17-
"golang.org/x/tools/internal/testenv"
1817
)
1918

2019
func TestHoverUnexported(t *testing.T) {
@@ -282,7 +281,6 @@ go 1.16
282281
}
283282

284283
func TestHoverCompletionMarkdown(t *testing.T) {
285-
testenv.NeedsGo1Point(t, 19)
286284
const source = `
287285
-- go.mod --
288286
module mod.com
@@ -343,7 +341,6 @@ func Hello() string {
343341
// Test that the generated markdown contains links for Go references.
344342
// https://github.com/golang/go/issues/58352
345343
func TestHoverLinks(t *testing.T) {
346-
testenv.NeedsGo1Point(t, 19)
347344
const input = `
348345
-- go.mod --
349346
go 1.19
@@ -465,7 +462,6 @@ SKIPPED
465462
`
466463

467464
func TestHoverEmbedDirective(t *testing.T) {
468-
testenv.NeedsGo1Point(t, 19)
469465
Run(t, embedHover, func(t *testing.T, env *Env) {
470466
env.OpenFile("main.go")
471467
from := env.RegexpSearch("main.go", `\*.txt`)
@@ -606,8 +602,6 @@ func main() {
606602
}
607603

608604
func TestHoverBuiltinFile(t *testing.T) {
609-
testenv.NeedsGo1Point(t, 21) // uses 'min'
610-
611605
// This test verifies that hovering in the builtin file provides the same
612606
// hover content as hovering over a use of a builtin.
613607

gopls/internal/test/integration/misc/imports_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -292,9 +292,9 @@ var _, _ = x.X, y.Y
292292
// inclined to undertake.
293293
func cleanModCache(t *testing.T, modcache string) {
294294
cmd := exec.Command("go", "clean", "-modcache")
295-
cmd.Env = append(os.Environ(), "GOMODCACHE="+modcache)
296-
if err := cmd.Run(); err != nil {
297-
t.Errorf("cleaning modcache: %v", err)
295+
cmd.Env = append(os.Environ(), "GOMODCACHE="+modcache, "GOTOOLCHAIN=local")
296+
if output, err := cmd.CombinedOutput(); err != nil {
297+
t.Errorf("cleaning modcache: %v\noutput:\n%s", err, string(output))
298298
}
299299
}
300300

gopls/internal/test/integration/misc/staticcheck_test.go

-6
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,10 @@ package misc
77
import (
88
"testing"
99

10-
"golang.org/x/tools/internal/testenv"
11-
1210
. "golang.org/x/tools/gopls/internal/test/integration"
1311
)
1412

1513
func TestStaticcheckGenerics(t *testing.T) {
16-
testenv.NeedsGo1Point(t, 20) // staticcheck requires go1.20+
17-
1814
// CL 583778 causes buildir not to run on packages that use
1915
// range-over-func, since it might otherwise crash. But nearly
2016
// all packages will soon meet this description, so the
@@ -85,8 +81,6 @@ var FooErr error = errors.New("foo")
8581
// Test for golang/go#56270: an analysis with related info should not panic if
8682
// analysis.RelatedInformation.End is not set.
8783
func TestStaticcheckRelatedInfo(t *testing.T) {
88-
testenv.NeedsGo1Point(t, 20) // staticcheck is only supported at Go 1.20+
89-
9084
// CL 583778 causes buildir not to run on packages that use
9185
// range-over-func, since it might otherwise crash. But nearly
9286
// all packages will soon meet this description, so the

gopls/internal/test/integration/misc/webserver_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ func f(buf bytes.Buffer, greeting string) {
361361

362362
// TestAssembly is a basic test of the web-based assembly listing.
363363
func TestAssembly(t *testing.T) {
364-
testenv.NeedsGo1Point(t, 22) // for up-to-date assembly listing
364+
testenv.NeedsGoCommand1Point(t, 22) // for up-to-date assembly listing
365365

366366
const files = `
367367
-- go.mod --

gopls/internal/test/integration/regtest.go

+54
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ import (
99
"flag"
1010
"fmt"
1111
"os"
12+
"os/exec"
13+
"path/filepath"
1214
"runtime"
15+
"strings"
1316
"testing"
1417
"time"
1518

@@ -189,5 +192,56 @@ func Main(m *testing.M) (code int) {
189192
}
190193
runner.tempDir = dir
191194

195+
FilterToolchainPathAndGOROOT()
196+
192197
return m.Run()
193198
}
199+
200+
// FilterToolchainPathAndGOROOT updates the PATH and GOROOT environment
201+
// variables for the current process to effectively revert the changes made by
202+
// the go command when performing a toolchain switch in the context of `go
203+
// test` (see golang/go#68005).
204+
//
205+
// It does this by looking through PATH for a go command that is NOT a
206+
// toolchain go command, and adjusting PATH to find that go command. Then it
207+
// unsets GOROOT in order to use the default GOROOT for that go command.
208+
//
209+
// TODO(rfindley): this is very much a hack, so that our 1.21 and 1.22 builders
210+
// actually exercise integration with older go commands. In golang/go#69321, we
211+
// hope to do better.
212+
func FilterToolchainPathAndGOROOT() {
213+
if localGo, first := findLocalGo(); localGo != "" && !first {
214+
dir := filepath.Dir(localGo)
215+
path := os.Getenv("PATH")
216+
os.Setenv("PATH", dir+string(os.PathListSeparator)+path)
217+
os.Unsetenv("GOROOT") // Remove the GOROOT value that was added by toolchain switch.
218+
}
219+
}
220+
221+
// findLocalGo returns a path to a local (=non-toolchain) Go version, or the
222+
// empty string if none is found.
223+
//
224+
// The second result reports if path matches the result of exec.LookPath.
225+
func findLocalGo() (path string, first bool) {
226+
paths := filepath.SplitList(os.Getenv("PATH"))
227+
for _, path := range paths {
228+
// Use a simple heuristic to filter out toolchain paths.
229+
if strings.Contains(path, "[email protected]") && filepath.Base(path) == "bin" {
230+
continue // toolchain path
231+
}
232+
fullPath := filepath.Join(path, "go")
233+
fi, err := os.Stat(fullPath)
234+
if err != nil {
235+
continue
236+
}
237+
if fi.Mode()&0111 != 0 {
238+
first := false
239+
pathGo, err := exec.LookPath("go")
240+
if err == nil {
241+
first = fullPath == pathGo
242+
}
243+
return fullPath, first
244+
}
245+
}
246+
return "", false
247+
}

gopls/internal/test/integration/runner.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ func (r *Runner) Run(t *testing.T, files string, test TestFunc, opts ...RunOptio
203203

204204
// Write the go.sum file for the requested directories, before starting the server.
205205
for _, dir := range config.writeGoSum {
206-
if err := sandbox.RunGoCommand(context.Background(), dir, "list", []string{"-mod=mod", "./..."}, []string{"GOWORK=off"}, true); err != nil {
206+
if _, err := sandbox.RunGoCommand(context.Background(), dir, "list", []string{"-mod=mod", "./..."}, []string{"GOWORK=off"}, true); err != nil {
207207
t.Fatal(err)
208208
}
209209
}

gopls/internal/test/integration/watch/watch_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,7 @@ func main() {
588588
env.AfterChange(
589589
NoDiagnostics(ForFile("main.go")),
590590
)
591-
if err := env.Sandbox.RunGoCommand(env.Ctx, "", "mod", []string{"init", "mod.com"}, nil, true); err != nil {
591+
if _, err := env.Sandbox.RunGoCommand(env.Ctx, "", "mod", []string{"init", "mod.com"}, nil, true); err != nil {
592592
t.Fatal(err)
593593
}
594594

gopls/internal/test/integration/workspace/broken_test.go

-5
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010

1111
"golang.org/x/tools/gopls/internal/server"
1212
. "golang.org/x/tools/gopls/internal/test/integration"
13-
"golang.org/x/tools/internal/testenv"
1413
)
1514

1615
// This file holds various tests for UX with respect to broken workspaces.
@@ -23,10 +22,6 @@ import (
2322

2423
// Test for golang/go#53933
2524
func TestBrokenWorkspace_DuplicateModules(t *testing.T) {
26-
// The go command error message was improved in Go 1.20 to mention multiple
27-
// modules.
28-
testenv.NeedsGo1Point(t, 20)
29-
3025
// This proxy module content is replaced by the workspace, but is still
3126
// required for module resolution to function in the Go command.
3227
const proxy = `

gopls/internal/test/integration/workspace/workspace_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ func TestAutomaticWorkspaceModule_Interdependent(t *testing.T) {
255255
}
256256

257257
func TestWorkspaceVendoring(t *testing.T) {
258-
testenv.NeedsGo1Point(t, 22)
258+
testenv.NeedsGoCommand1Point(t, 22)
259259
WithOptions(
260260
ProxyFiles(workspaceModuleProxy),
261261
).Run(t, multiModule, func(t *testing.T, env *Env) {

gopls/internal/test/integration/wrappers.go

+7-5
Original file line numberDiff line numberDiff line change
@@ -314,18 +314,20 @@ func (e *Env) RunGenerate(dir string) {
314314

315315
// RunGoCommand runs the given command in the sandbox's default working
316316
// directory.
317-
func (e *Env) RunGoCommand(verb string, args ...string) {
317+
func (e *Env) RunGoCommand(verb string, args ...string) []byte {
318318
e.T.Helper()
319-
if err := e.Sandbox.RunGoCommand(e.Ctx, "", verb, args, nil, true); err != nil {
319+
out, err := e.Sandbox.RunGoCommand(e.Ctx, "", verb, args, nil, true)
320+
if err != nil {
320321
e.T.Fatal(err)
321322
}
323+
return out
322324
}
323325

324326
// RunGoCommandInDir is like RunGoCommand, but executes in the given
325327
// relative directory of the sandbox.
326328
func (e *Env) RunGoCommandInDir(dir, verb string, args ...string) {
327329
e.T.Helper()
328-
if err := e.Sandbox.RunGoCommand(e.Ctx, dir, verb, args, nil, true); err != nil {
330+
if _, err := e.Sandbox.RunGoCommand(e.Ctx, dir, verb, args, nil, true); err != nil {
329331
e.T.Fatal(err)
330332
}
331333
}
@@ -334,7 +336,7 @@ func (e *Env) RunGoCommandInDir(dir, verb string, args ...string) {
334336
// relative directory of the sandbox with the given additional environment variables.
335337
func (e *Env) RunGoCommandInDirWithEnv(dir string, env []string, verb string, args ...string) {
336338
e.T.Helper()
337-
if err := e.Sandbox.RunGoCommand(e.Ctx, dir, verb, args, env, true); err != nil {
339+
if _, err := e.Sandbox.RunGoCommand(e.Ctx, dir, verb, args, env, true); err != nil {
338340
e.T.Fatal(err)
339341
}
340342
}
@@ -355,7 +357,7 @@ func (e *Env) GoVersion() int {
355357
func (e *Env) DumpGoSum(dir string) {
356358
e.T.Helper()
357359

358-
if err := e.Sandbox.RunGoCommand(e.Ctx, dir, "list", []string{"-mod=mod", "./..."}, nil, true); err != nil {
360+
if _, err := e.Sandbox.RunGoCommand(e.Ctx, dir, "list", []string{"-mod=mod", "./..."}, nil, true); err != nil {
359361
e.T.Fatal(err)
360362
}
361363
sumFile := path.Join(dir, "go.sum")

0 commit comments

Comments
 (0)