--- /dev/null
+// 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"
+ "testing"
+ "time"
+)
+
+// Pilosa is a repository that has historically caused significant memory
+// problems for Gopls. We use it for a simple stress test that types
+// arbitrarily in a file with lots of dependents.
+
+var pilosaPath = flag.String("pilosa_path", "", "Path to a directory containing "+
+ "github.com/pilosa/pilosa, for stress testing. Do not set this unless you "+
+ "know what you're doing!")
+
+func stressTestOptions(dir string) []RunOption {
+ return []RunOption{
+ // Run in an existing directory, since we're trying to simulate known cases
+ // that cause gopls memory problems.
+ InExistingDir(dir),
+
+ // Enable live debugging.
+ WithDebugAddress(":8087"),
+
+ // Skip logs as they buffer up memory unnaturally.
+ SkipLogs(),
+ // Similarly to logs: disable hooks so that they don't affect performance.
+ SkipHooks(true),
+ // The Debug server only makes sense if running in singleton mode.
+ WithModes(Singleton),
+ // Set a generous timeout. Individual tests should control their own
+ // graceful termination.
+ WithTimeout(20 * time.Minute),
+
+ // Use the actual proxy, since we want our builds to succeed.
+ WithGOPROXY("https://proxy.golang.org"),
+ }
+}
+
+func TestPilosaStress(t *testing.T) {
+ if *pilosaPath == "" {
+ t.Skip("-pilosa_path not configured")
+ }
+ opts := stressTestOptions(*pilosaPath)
+
+ withOptions(opts...).run(t, "", func(t *testing.T, env *Env) {
+ files := []string{
+ "cmd.go",
+ "internal/private.pb.go",
+ "roaring/roaring.go",
+ "roaring/roaring_internal_test.go",
+ "server/handler_test.go",
+ }
+ for _, file := range files {
+ env.OpenFile(file)
+ }
+ ctx, cancel := context.WithTimeout(env.Ctx, 10*time.Minute)
+ defer cancel()
+
+ i := 1
+ // MagicNumber is an identifier that occurs in roaring.go. Just change it
+ // arbitrarily.
+ env.RegexpReplace("roaring/roaring.go", "MagicNumber", fmt.Sprintf("MagicNumber%d", 1))
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ default:
+ }
+ env.RegexpReplace("roaring/roaring.go", fmt.Sprintf("MagicNumber%d", i), fmt.Sprintf("MagicNumber%d", i+1))
+ time.Sleep(20 * time.Millisecond)
+ i++
+ }
+ })
+}