cuelang.org/go@v0.10.1/internal/golangorgx/gopls/test/integration/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 integration 6 7 import ( 8 "context" 9 "flag" 10 "fmt" 11 "os" 12 "runtime" 13 "testing" 14 "time" 15 16 "cuelang.org/go/internal/golangorgx/gopls/cache" 17 "cuelang.org/go/internal/golangorgx/gopls/cmd" 18 "cuelang.org/go/internal/golangorgx/gopls/settings" 19 "cuelang.org/go/internal/golangorgx/tools/gocommand" 20 "cuelang.org/go/internal/golangorgx/tools/memoize" 21 "cuelang.org/go/internal/golangorgx/tools/testenv" 22 "cuelang.org/go/internal/golangorgx/tools/tool" 23 ) 24 25 var ( 26 runSubprocessTests = flag.Bool("enable_gopls_subprocess_tests", false, "run integration tests against a gopls subprocess (default: in-process)") 27 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") 28 timeout = flag.Duration("timeout", defaultTimeout(), "if nonzero, default timeout for each integration test; defaults to GOPLS_INTEGRATION_TEST_TIMEOUT") 29 skipCleanup = flag.Bool("skip_cleanup", false, "whether to skip cleaning up temp directories") 30 printGoroutinesOnFailure = flag.Bool("print_goroutines", false, "whether to print goroutines info on failure") 31 printLogs = flag.Bool("print_logs", false, "whether to print LSP logs") 32 ) 33 34 func defaultTimeout() time.Duration { 35 s := os.Getenv("GOPLS_INTEGRATION_TEST_TIMEOUT") 36 if s == "" { 37 return 0 38 } 39 d, err := time.ParseDuration(s) 40 if err != nil { 41 fmt.Fprintf(os.Stderr, "invalid GOPLS_INTEGRATION_TEST_TIMEOUT %q: %v\n", s, err) 42 os.Exit(2) 43 } 44 return d 45 } 46 47 var runner *Runner 48 49 // The integrationTestRunner interface abstracts the Run operation, 50 // enables decorators for various optional features. 51 type integrationTestRunner interface { 52 Run(t *testing.T, files string, f TestFunc) 53 } 54 55 func Run(t *testing.T, files string, f TestFunc) { 56 runner.Run(t, files, f) 57 } 58 59 func WithOptions(opts ...RunOption) configuredRunner { 60 return configuredRunner{opts: opts} 61 } 62 63 type configuredRunner struct { 64 opts []RunOption 65 } 66 67 func (r configuredRunner) Run(t *testing.T, files string, f TestFunc) { 68 // Print a warning if the test's temporary directory is not 69 // suitable as a workspace folder, as this may lead to 70 // otherwise-cryptic failures. This situation typically occurs 71 // when an arbitrary string (e.g. "foo.") is used as a subtest 72 // name, on a platform with filename restrictions (e.g. no 73 // trailing period on Windows). 74 tmp := t.TempDir() 75 if err := cache.CheckPathValid(tmp); err != nil { 76 t.Logf("Warning: testing.T.TempDir(%s) is not valid as a workspace folder: %s", 77 tmp, err) 78 } 79 80 runner.Run(t, files, f, r.opts...) 81 } 82 83 type RunMultiple []struct { 84 Name string 85 Runner integrationTestRunner 86 } 87 88 func (r RunMultiple) Run(t *testing.T, files string, f TestFunc) { 89 for _, runner := range r { 90 t.Run(runner.Name, func(t *testing.T) { 91 runner.Runner.Run(t, files, f) 92 }) 93 } 94 } 95 96 // DefaultModes returns the default modes to run for each regression test (they 97 // may be reconfigured by the tests themselves). 98 func DefaultModes() Mode { 99 modes := Default 100 if !testing.Short() { 101 modes |= Experimental | Forwarded 102 } 103 if *runSubprocessTests { 104 modes |= SeparateProcess 105 } 106 return modes 107 } 108 109 // Main sets up and tears down the shared integration test state. 110 func Main(m *testing.M, hook func(*settings.Options)) { 111 // golang/go#54461: enable additional debugging around hanging Go commands. 112 gocommand.DebugHangingGoCommands = true 113 114 // If this magic environment variable is set, run gopls instead of the test 115 // suite. See the documentation for runTestAsGoplsEnvvar for more details. 116 if os.Getenv(runTestAsGoplsEnvvar) == "true" { 117 tool.Main(context.Background(), cmd.New(hook), os.Args[1:]) 118 os.Exit(0) 119 } 120 121 if !testenv.HasExec() { 122 fmt.Printf("skipping all tests: exec not supported on %s/%s\n", runtime.GOOS, runtime.GOARCH) 123 os.Exit(0) 124 } 125 testenv.ExitIfSmallMachine() 126 127 // Disable GOPACKAGESDRIVER, as it can cause spurious test failures. 128 os.Setenv("GOPACKAGESDRIVER", "off") 129 130 flag.Parse() 131 132 runner = &Runner{ 133 DefaultModes: DefaultModes(), 134 Timeout: *timeout, 135 PrintGoroutinesOnFailure: *printGoroutinesOnFailure, 136 SkipCleanup: *skipCleanup, 137 OptionsHook: hook, 138 store: memoize.NewStore(memoize.NeverEvict), 139 } 140 141 runner.goplsPath = *goplsBinaryPath 142 if runner.goplsPath == "" { 143 var err error 144 runner.goplsPath, err = os.Executable() 145 if err != nil { 146 panic(fmt.Sprintf("finding test binary path: %v", err)) 147 } 148 } 149 150 dir, err := os.MkdirTemp("", "cuepls-test-") 151 if err != nil { 152 panic(fmt.Errorf("creating temp directory: %v", err)) 153 } 154 runner.tempDir = dir 155 156 var code int 157 defer func() { 158 if err := runner.Close(); err != nil { 159 fmt.Fprintf(os.Stderr, "closing test runner: %v\n", err) 160 // Cleanup is broken in go1.12 and earlier, and sometimes flakes on 161 // Windows due to file locking, but this is OK for our CI. 162 // 163 // Fail on go1.13+, except for windows and android which have shutdown problems. 164 if testenv.Go1Point() >= 13 && runtime.GOOS != "windows" && runtime.GOOS != "android" { 165 os.Exit(1) 166 } 167 } 168 os.Exit(code) 169 }() 170 code = m.Run() 171 }