github.com/jhump/golang-x-tools@v0.0.0-20220218190644-4958d6d39439/internal/lsp/regtest/regtest.go (about) 1 // Copyright 2020 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package regtest 6 7 import ( 8 "context" 9 "flag" 10 "fmt" 11 "io/ioutil" 12 "os" 13 "runtime" 14 "testing" 15 "time" 16 17 "github.com/jhump/golang-x-tools/internal/lsp/cmd" 18 "github.com/jhump/golang-x-tools/internal/lsp/source" 19 "github.com/jhump/golang-x-tools/internal/testenv" 20 "github.com/jhump/golang-x-tools/internal/tool" 21 ) 22 23 var ( 24 runSubprocessTests = flag.Bool("enable_gopls_subprocess_tests", false, "run regtests against a gopls subprocess") 25 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") 26 regtestTimeout = flag.Duration("regtest_timeout", defaultRegtestTimeout(), "if nonzero, default timeout for each regtest; defaults to GOPLS_REGTEST_TIMEOUT") 27 skipCleanup = flag.Bool("regtest_skip_cleanup", false, "whether to skip cleaning up temp directories") 28 printGoroutinesOnFailure = flag.Bool("regtest_print_goroutines", false, "whether to print goroutines info on failure") 29 ) 30 31 func defaultRegtestTimeout() time.Duration { 32 s := os.Getenv("GOPLS_REGTEST_TIMEOUT") 33 if s == "" { 34 return 0 35 } 36 d, err := time.ParseDuration(s) 37 if err != nil { 38 fmt.Fprintf(os.Stderr, "invalid GOPLS_REGTEST_TIMEOUT %q: %v\n", s, err) 39 os.Exit(2) 40 } 41 return d 42 } 43 44 var runner *Runner 45 46 type regtestRunner interface { 47 Run(t *testing.T, files string, f TestFunc) 48 } 49 50 func Run(t *testing.T, files string, f TestFunc) { 51 runner.Run(t, files, f) 52 } 53 54 func WithOptions(opts ...RunOption) configuredRunner { 55 return configuredRunner{opts: opts} 56 } 57 58 type configuredRunner struct { 59 opts []RunOption 60 } 61 62 func (r configuredRunner) Run(t *testing.T, files string, f TestFunc) { 63 runner.Run(t, files, f, r.opts...) 64 } 65 66 type RunMultiple []struct { 67 Name string 68 Runner regtestRunner 69 } 70 71 func (r RunMultiple) Run(t *testing.T, files string, f TestFunc) { 72 for _, runner := range r { 73 t.Run(runner.Name, func(t *testing.T) { 74 runner.Runner.Run(t, files, f) 75 }) 76 } 77 } 78 79 // The regtests run significantly slower on these operating systems, due to (we 80 // believe) kernel locking behavior. Only run in singleton mode on these 81 // operating system when using -short. 82 var slowGOOS = map[string]bool{ 83 "darwin": true, 84 "openbsd": true, 85 "plan9": true, 86 } 87 88 func DefaultModes() Mode { 89 normal := Singleton | Experimental 90 if slowGOOS[runtime.GOOS] && testing.Short() { 91 normal = Singleton 92 } 93 if *runSubprocessTests { 94 return normal | SeparateProcess 95 } 96 return normal 97 } 98 99 // Main sets up and tears down the shared regtest state. 100 func Main(m *testing.M, hook func(*source.Options)) { 101 testenv.ExitIfSmallMachine() 102 103 // Disable GOPACKAGESDRIVER, as it can cause spurious test failures. 104 os.Setenv("GOPACKAGESDRIVER", "off") 105 106 flag.Parse() 107 if os.Getenv("_GOPLS_TEST_BINARY_RUN_AS_GOPLS") == "true" { 108 tool.Main(context.Background(), cmd.New("gopls", "", nil, nil), os.Args[1:]) 109 os.Exit(0) 110 } 111 112 runner = &Runner{ 113 DefaultModes: DefaultModes(), 114 Timeout: *regtestTimeout, 115 PrintGoroutinesOnFailure: *printGoroutinesOnFailure, 116 SkipCleanup: *skipCleanup, 117 OptionsHook: hook, 118 } 119 if *runSubprocessTests { 120 goplsPath := *goplsBinaryPath 121 if goplsPath == "" { 122 var err error 123 goplsPath, err = os.Executable() 124 if err != nil { 125 panic(fmt.Sprintf("finding test binary path: %v", err)) 126 } 127 } 128 runner.GoplsPath = goplsPath 129 } 130 dir, err := ioutil.TempDir("", "gopls-regtest-") 131 if err != nil { 132 panic(fmt.Errorf("creating regtest temp directory: %v", err)) 133 } 134 runner.TempDir = dir 135 136 code := m.Run() 137 if err := runner.Close(); err != nil { 138 fmt.Fprintf(os.Stderr, "closing test runner: %v\n", err) 139 // Regtest cleanup is broken in go1.12 and earlier, and sometimes flakes on 140 // Windows due to file locking, but this is OK for our CI. 141 // 142 // Fail on go1.13+, except for windows and android which have shutdown problems. 143 if testenv.Go1Point() >= 13 && runtime.GOOS != "windows" && runtime.GOOS != "android" { 144 os.Exit(1) 145 } 146 } 147 os.Exit(code) 148 }