diff options
Diffstat (limited to 'gopls/internal/lsp/regtest/regtest.go')
-rw-r--r-- | gopls/internal/lsp/regtest/regtest.go | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/gopls/internal/lsp/regtest/regtest.go b/gopls/internal/lsp/regtest/regtest.go new file mode 100644 index 000000000..5a5232f3e --- /dev/null +++ b/gopls/internal/lsp/regtest/regtest.go @@ -0,0 +1,153 @@ +// Copyright 2020 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 regtest + +import ( + "context" + "flag" + "fmt" + "io/ioutil" + "os" + "runtime" + "testing" + "time" + + "golang.org/x/tools/gopls/internal/lsp/cmd" + "golang.org/x/tools/gopls/internal/lsp/source" + "golang.org/x/tools/internal/gocommand" + "golang.org/x/tools/internal/memoize" + "golang.org/x/tools/internal/testenv" + "golang.org/x/tools/internal/tool" +) + +var ( + runSubprocessTests = flag.Bool("enable_gopls_subprocess_tests", false, "run regtests against a gopls subprocess") + goplsBinaryPath = flag.String("gopls_test_binary", "", "path to the gopls binary for use as a remote, for use with the -enable_gopls_subprocess_tests flag") + regtestTimeout = flag.Duration("regtest_timeout", defaultRegtestTimeout(), "if nonzero, default timeout for each regtest; defaults to GOPLS_REGTEST_TIMEOUT") + skipCleanup = flag.Bool("regtest_skip_cleanup", false, "whether to skip cleaning up temp directories") + printGoroutinesOnFailure = flag.Bool("regtest_print_goroutines", false, "whether to print goroutines info on failure") + printLogs = flag.Bool("regtest_print_logs", false, "whether to print LSP logs") +) + +func defaultRegtestTimeout() time.Duration { + s := os.Getenv("GOPLS_REGTEST_TIMEOUT") + if s == "" { + return 0 + } + d, err := time.ParseDuration(s) + if err != nil { + fmt.Fprintf(os.Stderr, "invalid GOPLS_REGTEST_TIMEOUT %q: %v\n", s, err) + os.Exit(2) + } + return d +} + +var runner *Runner + +type regtestRunner interface { + Run(t *testing.T, files string, f TestFunc) +} + +func Run(t *testing.T, files string, f TestFunc) { + runner.Run(t, files, f) +} + +func WithOptions(opts ...RunOption) configuredRunner { + return configuredRunner{opts: opts} +} + +type configuredRunner struct { + opts []RunOption +} + +func (r configuredRunner) Run(t *testing.T, files string, f TestFunc) { + runner.Run(t, files, f, r.opts...) +} + +type RunMultiple []struct { + Name string + Runner regtestRunner +} + +func (r RunMultiple) Run(t *testing.T, files string, f TestFunc) { + for _, runner := range r { + t.Run(runner.Name, func(t *testing.T) { + runner.Runner.Run(t, files, f) + }) + } +} + +// DefaultModes returns the default modes to run for each regression test (they +// may be reconfigured by the tests themselves). +func DefaultModes() Mode { + modes := Default + if !testing.Short() { + modes |= Experimental | Forwarded + } + if *runSubprocessTests { + modes |= SeparateProcess + } + return modes +} + +// Main sets up and tears down the shared regtest state. +func Main(m *testing.M, hook func(*source.Options)) { + // golang/go#54461: enable additional debugging around hanging Go commands. + gocommand.DebugHangingGoCommands = true + + // If this magic environment variable is set, run gopls instead of the test + // suite. See the documentation for runTestAsGoplsEnvvar for more details. + if os.Getenv(runTestAsGoplsEnvvar) == "true" { + tool.Main(context.Background(), cmd.New("gopls", "", nil, hook), os.Args[1:]) + os.Exit(0) + } + + testenv.ExitIfSmallMachine() + + // Disable GOPACKAGESDRIVER, as it can cause spurious test failures. + os.Setenv("GOPACKAGESDRIVER", "off") + + flag.Parse() + + runner = &Runner{ + DefaultModes: DefaultModes(), + Timeout: *regtestTimeout, + PrintGoroutinesOnFailure: *printGoroutinesOnFailure, + SkipCleanup: *skipCleanup, + OptionsHook: hook, + store: memoize.NewStore(memoize.NeverEvict), + } + + runner.goplsPath = *goplsBinaryPath + if runner.goplsPath == "" { + var err error + runner.goplsPath, err = os.Executable() + if err != nil { + panic(fmt.Sprintf("finding test binary path: %v", err)) + } + } + + dir, err := ioutil.TempDir("", "gopls-regtest-") + if err != nil { + panic(fmt.Errorf("creating regtest temp directory: %v", err)) + } + runner.tempDir = dir + + var code int + defer func() { + if err := runner.Close(); err != nil { + fmt.Fprintf(os.Stderr, "closing test runner: %v\n", err) + // Regtest cleanup is broken in go1.12 and earlier, and sometimes flakes on + // Windows due to file locking, but this is OK for our CI. + // + // Fail on go1.13+, except for windows and android which have shutdown problems. + if testenv.Go1Point() >= 13 && runtime.GOOS != "windows" && runtime.GOOS != "android" { + os.Exit(1) + } + } + os.Exit(code) + }() + code = m.Run() +} |