gopls/internal/test/integration: respect empty mode filter in runner
Fix a bug in the integration test runner where specifying Modes(0)
(an empty mode filter) was treated as "unset" due to a zero-value
check, causing the runner to fall back to default modes.
This CL fixes this by changing the runner's modes configuration to a
pointer, allowing us to distinguish between "unset" (nil) and
"explicitly empty" (0).
This bug caused TestSimultaneousEdits to occasionally deadlock on
builders running with -short.
Under -short, DefaultModes() returns only Default mode (1), as defined
in regtest.go. TestSimultaneousEdits restricts its execution to
Modes(DefaultModes() & (Forwarded|SeparateProcess))
The intention was to run the test only in forwarded modes.
However, DefaultModes() = 1, Forwarded=2 and SeparateProcess=4,
so the resultng mode filter always evaluates to 0 (empty).
Due to the fallback bug, the runner saw 0 as "unset" and
incorrectly fell back to running the test in Default mode
(value 1) anyway, triggering a deadlock on the unbuffered net.Pipe
transport.
Also add a regression test in the misc package to ensure Modes(0) is
respected and does not fall back.
For golang/go#76713
diff --git a/gopls/internal/test/integration/misc/runner_test.go b/gopls/internal/test/integration/misc/runner_test.go
new file mode 100644
index 0000000..92268ab
--- /dev/null
+++ b/gopls/internal/test/integration/misc/runner_test.go
@@ -0,0 +1,22 @@
+// Copyright 2026 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 misc
+
+import (
+ "testing"
+
+ . "golang.org/x/tools/gopls/internal/test/integration"
+)
+
+func TestModesZero(t *testing.T) {
+ called := false
+ WithOptions(Modes(0)).Run(t, "", func(t *testing.T, env *Env) {
+ called = true
+ })
+
+ if called {
+ t.Errorf("test function was called even though Modes(0) was specified")
+ }
+}
diff --git a/gopls/internal/test/integration/options.go b/gopls/internal/test/integration/options.go
index 176a8a6..28dd52b 100644
--- a/gopls/internal/test/integration/options.go
+++ b/gopls/internal/test/integration/options.go
@@ -18,7 +18,7 @@
type runConfig struct {
editor fake.EditorConfig
sandbox fake.SandboxConfig
- modes Mode
+ modes *Mode
noLogsOnError bool
writeGoSum []string
}
@@ -71,10 +71,10 @@
// modes.
func Modes(modes Mode) RunOption {
return optionSetter(func(opts *runConfig) {
- if opts.modes != 0 {
+ if opts.modes != nil {
panic("modes set more than once")
}
- opts.modes = modes
+ opts.modes = &modes
})
}
diff --git a/gopls/internal/test/integration/runner.go b/gopls/internal/test/integration/runner.go
index ae77ed1..95b4c01 100644
--- a/gopls/internal/test/integration/runner.go
+++ b/gopls/internal/test/integration/runner.go
@@ -146,8 +146,8 @@
opt.set(&config)
}
modes := r.DefaultModes
- if config.modes != 0 {
- modes = config.modes
+ if config.modes != nil {
+ modes = *config.modes
}
if modes&tc.mode == 0 {
continue
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
func TestRunnerModes(t *testing.T) {Should unset Mode be tested? I can't tell if its guaranteed that existing tests cover it
type Mode int
const (
// Default mode runs gopls with the default options, communicating over pipes
// to emulate the lsp sidecar execution mode, which communicates over
// stdin/stdout.
//
// It uses separate servers for each test, but a shared cache, to avoid
// duplicating work when processing GOROOT.
Default Mode = 1 << iotaIt seems like Mode = 0 is a known state now. Maybe `Empty Mode = 0`? Or `NoMode`?
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Should unset Mode be tested? I can't tell if its guaranteed that existing tests cover it
Done. Added unset mode test.
type Mode int
const (
// Default mode runs gopls with the default options, communicating over pipes
// to emulate the lsp sidecar execution mode, which communicates over
// stdin/stdout.
//
// It uses separate servers for each test, but a shared cache, to avoid
// duplicating work when processing GOROOT.
Default Mode = 1 << iotaIt seems like Mode = 0 is a known state now. Maybe `Empty Mode = 0`? Or `NoMode`?
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
gopls/internal/test/integration: respect empty mode filter in runner
Fix a bug in the integration test runner where specifying Modes(0)
(an empty mode filter) was treated as "unset" due to a zero-value
check, causing the runner to fall back to default modes.
This CL fixes this by changing the runner's modes configuration to a
pointer, allowing us to distinguish between "unset" (nil) and
"explicitly empty" (0, NoMode).
This bug caused TestSimultaneousEdits to occasionally deadlock on
builders running with -short.
Under -short, DefaultModes() returns only Default mode (1), as defined
in regtest.go. TestSimultaneousEdits restricts its execution to
Modes(DefaultModes() & (Forwarded|SeparateProcess))
The intention was to run the test only in forwarded modes.
However, DefaultModes() = 1, Forwarded=2 and SeparateProcess=4,
so the resultng mode filter always evaluates to 0 (empty).
Due to the fallback bug, the runner saw 0 as "unset" and
incorrectly fell back to running the test in Default mode
(value 1) anyway, triggering a deadlock on the unbuffered net.Pipe
transport.
Also add a regression test in the misc package to ensure Modes(0) is
respected and does not fall back.
For golang/go#76713
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |