diff --git a/internal/lsp/regtest/env.go b/internal/lsp/regtest/env.go index 4315c33ca3..7d5e65dd20 100644 --- a/internal/lsp/regtest/env.go +++ b/internal/lsp/regtest/env.go @@ -6,319 +6,19 @@ package regtest import ( - "bytes" "context" "fmt" - "io" - "io/ioutil" - "os" - "os/exec" - "path/filepath" "regexp" - "runtime/pprof" "strings" "sync" "testing" - "time" "golang.org/x/tools/internal/jsonrpc2" "golang.org/x/tools/internal/jsonrpc2/servertest" - "golang.org/x/tools/internal/lsp/cache" - "golang.org/x/tools/internal/lsp/debug" "golang.org/x/tools/internal/lsp/fake" - "golang.org/x/tools/internal/lsp/lsprpc" "golang.org/x/tools/internal/lsp/protocol" ) -// EnvMode is a bitmask that defines in which execution environments a test -// should run. -type EnvMode int - -const ( - // Singleton mode uses a separate cache for each test. - Singleton EnvMode = 1 << iota - - // Forwarded forwards connections to an in-process gopls instance. - Forwarded - // SeparateProcess runs a separate gopls process, and forwards connections to - // it. - SeparateProcess - // NormalModes runs tests in all modes. - NormalModes = Singleton | Forwarded -) - -// A Runner runs tests in gopls execution environments, as specified by its -// modes. For modes that share state (for example, a shared cache or common -// remote), any tests that execute on the same Runner will share the same -// state. -type Runner struct { - DefaultModes EnvMode - Timeout time.Duration - GoplsPath string - AlwaysPrintLogs bool - PrintGoroutinesOnFailure bool - - mu sync.Mutex - ts *servertest.TCPServer - socketDir string - // closers is a queue of clean-up functions to run at the end of the entire - // test suite. - closers []io.Closer -} - -// Modes returns the bitmask of environment modes this runner is configured to -// test. -func (r *Runner) Modes() EnvMode { - return r.DefaultModes -} - -// getTestServer gets the test server instance to connect to, or creates one if -// it doesn't exist. -func (r *Runner) getTestServer() *servertest.TCPServer { - r.mu.Lock() - defer r.mu.Unlock() - if r.ts == nil { - ctx := context.Background() - ctx = debug.WithInstance(ctx, "", "") - ss := lsprpc.NewStreamServer(cache.New(ctx, nil)) - r.ts = servertest.NewTCPServer(context.Background(), ss) - } - return r.ts -} - -// runTestAsGoplsEnvvar triggers TestMain to run gopls instead of running -// tests. It's a trick to allow tests to find a binary to use to start a gopls -// subprocess. -const runTestAsGoplsEnvvar = "_GOPLS_TEST_BINARY_RUN_AS_GOPLS" - -func (r *Runner) getRemoteSocket(t *testing.T) string { - t.Helper() - r.mu.Lock() - defer r.mu.Unlock() - const daemonFile = "gopls-test-daemon" - if r.socketDir != "" { - return filepath.Join(r.socketDir, daemonFile) - } - - if r.GoplsPath == "" { - t.Fatal("cannot run tests with a separate process unless a path to a gopls binary is configured") - } - var err error - r.socketDir, err = ioutil.TempDir("", "gopls-regtests") - if err != nil { - t.Fatalf("creating tempdir: %v", err) - } - socket := filepath.Join(r.socketDir, daemonFile) - args := []string{"serve", "-listen", "unix;" + socket, "-listen.timeout", "10s"} - cmd := exec.Command(r.GoplsPath, args...) - cmd.Env = append(os.Environ(), runTestAsGoplsEnvvar+"=true") - var stderr bytes.Buffer - cmd.Stderr = &stderr - go func() { - if err := cmd.Run(); err != nil { - panic(fmt.Sprintf("error running external gopls: %v\nstderr:\n%s", err, stderr.String())) - } - }() - return socket -} - -// AddCloser schedules a closer to be closed at the end of the test run. This -// is useful for Windows in particular, as -func (r *Runner) AddCloser(closer io.Closer) { - r.mu.Lock() - defer r.mu.Unlock() - r.closers = append(r.closers, closer) -} - -// Close cleans up resource that have been allocated to this workspace. -func (r *Runner) Close() error { - r.mu.Lock() - defer r.mu.Unlock() - - var errmsgs []string - if r.ts != nil { - if err := r.ts.Close(); err != nil { - errmsgs = append(errmsgs, err.Error()) - } - } - if r.socketDir != "" { - if err := os.RemoveAll(r.socketDir); err != nil { - errmsgs = append(errmsgs, err.Error()) - } - } - for _, closer := range r.closers { - if err := closer.Close(); err != nil { - errmsgs = append(errmsgs, err.Error()) - } - } - if len(errmsgs) > 0 { - return fmt.Errorf("errors closing the test runner:\n\t%s", strings.Join(errmsgs, "\n\t")) - } - return nil -} - -type runConfig struct { - modes EnvMode - proxyTxt string - timeout time.Duration - env []string -} - -func (r *Runner) defaultConfig() *runConfig { - return &runConfig{ - modes: r.DefaultModes, - timeout: r.Timeout, - } -} - -// A RunOption augments the behavior of the test runner. -type RunOption interface { - set(*runConfig) -} - -type optionSetter func(*runConfig) - -func (f optionSetter) set(opts *runConfig) { - f(opts) -} - -// WithTimeout configures a custom timeout for this test run. -func WithTimeout(d time.Duration) RunOption { - return optionSetter(func(opts *runConfig) { - opts.timeout = d - }) -} - -// WithProxy configures a file proxy using the given txtar-encoded string. -func WithProxy(txt string) RunOption { - return optionSetter(func(opts *runConfig) { - opts.proxyTxt = txt - }) -} - -// WithModes configures the execution modes that the test should run in. -func WithModes(modes EnvMode) RunOption { - return optionSetter(func(opts *runConfig) { - opts.modes = modes - }) -} - -// WithEnv overlays environment variables encoded by "== 0 { + return fmt.Errorf("errors closing the test runner:\n\t%s", strings.Join(errmsgs, "\n\t")) + } + return nil +}