internal/lsp/fake: move to a struct for configuring the sandbox

The Sandbox constructor was getting out of control, and this allows
binding regtest options directly to the sandbox configuration struct.

Change-Id: I18da4ddf43c86b3b652516c3ddca7726cd35e3b2
Reviewed-on: https://go-review.googlesource.com/c/tools/+/244439
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
diff --git a/internal/lsp/fake/editor.go b/internal/lsp/fake/editor.go
index a2fbd3c..5e16b02 100644
--- a/internal/lsp/fake/editor.go
+++ b/internal/lsp/fake/editor.go
@@ -68,6 +68,15 @@
 	// SymbolMatcher is the config associated with the "symbolMatcher" gopls
 	// config option.
 	SymbolMatcher *string
+
+	// WithoutWorkspaceFolders is used to simulate opening a single file in the
+	// editor, without a workspace root. In that case, the client sends neither
+	// workspace folders nor a root URI.
+	WithoutWorkspaceFolders bool
+
+	// EditorRootPath specifies the root path of the workspace folder used when
+	// initializing gopls in the sandbox. If empty, the Workdir is used.
+	EditorRootPath string
 }
 
 // NewEditor Creates a new Editor.
@@ -94,7 +103,7 @@
 		protocol.Handlers(
 			protocol.ClientHandler(e.client,
 				jsonrpc2.MethodNotFound)))
-	if err := e.initialize(ctx, e.sandbox.withoutWorkspaceFolders, e.sandbox.editorRootPath); err != nil {
+	if err := e.initialize(ctx, e.Config.WithoutWorkspaceFolders, e.Config.EditorRootPath); err != nil {
 		return nil, err
 	}
 	e.sandbox.Workdir.AddWatcher(e.onFileChanges)
diff --git a/internal/lsp/fake/editor_test.go b/internal/lsp/fake/editor_test.go
index e01ac81..f1ce753 100644
--- a/internal/lsp/fake/editor_test.go
+++ b/internal/lsp/fake/editor_test.go
@@ -48,7 +48,7 @@
 `
 
 func TestClientEditing(t *testing.T) {
-	ws, err := NewSandbox("", exampleProgram, "", false, false, "")
+	ws, err := NewSandbox(&SandboxConfig{Files: exampleProgram})
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/internal/lsp/fake/sandbox.go b/internal/lsp/fake/sandbox.go
index 3b3b005..ca9b34f 100644
--- a/internal/lsp/fake/sandbox.go
+++ b/internal/lsp/fake/sandbox.go
@@ -24,15 +24,23 @@
 	basedir string
 	Proxy   *Proxy
 	Workdir *Workdir
+}
 
-	// withoutWorkspaceFolders is used to simulate opening a single file in the
-	// editor, without a workspace root. In that case, the client sends neither
-	// workspace folders nor a root URI.
-	withoutWorkspaceFolders bool
-
-	// editorRootPath specifies the root path of the workspace folder used when
-	// initializing gopls in the sandbox. If empty, the Workdir is used.
-	editorRootPath string
+// SandboxConfig controls the behavior of a test sandbox. The zero value
+// defines a reasonable default.
+type SandboxConfig struct {
+	// RootDir sets the base directory to use when creating temporary
+	// directories. If not specified, defaults to a new temporary directory.
+	RootDir string
+	// Files holds a txtar-encoded archive of files to populate the initial state
+	// of the working directory.
+	Files string
+	// ProxyFiles holds a txtar-encoded archive of files to populate a file-based
+	// Go proxy.
+	ProxyFiles string
+	// InGoPath specifies that the working directory should be within the
+	// temporary GOPATH.
+	InGoPath bool
 }
 
 // NewSandbox creates a collection of named temporary resources, with a
@@ -43,7 +51,10 @@
 // If rootDir is non-empty, it will be used as the root of temporary
 // directories created for the sandbox. Otherwise, a new temporary directory
 // will be used as root.
-func NewSandbox(rootDir, srctxt, proxytxt string, inGopath bool, withoutWorkspaceFolders bool, editorRootPath string) (_ *Sandbox, err error) {
+func NewSandbox(config *SandboxConfig) (_ *Sandbox, err error) {
+	if config == nil {
+		config = new(SandboxConfig)
+	}
 	sb := &Sandbox{}
 	defer func() {
 		// Clean up if we fail at any point in this constructor.
@@ -52,7 +63,7 @@
 		}
 	}()
 
-	baseDir, err := ioutil.TempDir(rootDir, "gopls-sandbox-")
+	baseDir, err := ioutil.TempDir(config.RootDir, "gopls-sandbox-")
 	if err != nil {
 		return nil, fmt.Errorf("creating temporary workdir: %v", err)
 	}
@@ -62,7 +73,7 @@
 	// Set the working directory as $GOPATH/src if inGopath is true.
 	workdir := filepath.Join(sb.gopath, "src")
 	dirs := []string{sb.gopath, proxydir}
-	if !inGopath {
+	if !config.InGoPath {
 		workdir = filepath.Join(sb.basedir, "work")
 		dirs = append(dirs, workdir)
 	}
@@ -71,10 +82,8 @@
 			return nil, err
 		}
 	}
-	sb.Proxy, err = NewProxy(proxydir, proxytxt)
-	sb.Workdir, err = NewWorkdir(workdir, srctxt)
-	sb.withoutWorkspaceFolders = withoutWorkspaceFolders
-	sb.editorRootPath = editorRootPath
+	sb.Proxy, err = NewProxy(proxydir, config.ProxyFiles)
+	sb.Workdir, err = NewWorkdir(workdir, config.Files)
 
 	return sb, nil
 }
diff --git a/internal/lsp/lsprpc/lsprpc_test.go b/internal/lsp/lsprpc/lsprpc_test.go
index 8cdc011..339d1d1 100644
--- a/internal/lsp/lsprpc/lsprpc_test.go
+++ b/internal/lsp/lsprpc/lsprpc_test.go
@@ -196,7 +196,7 @@
 }`
 
 func TestDebugInfoLifecycle(t *testing.T) {
-	sb, err := fake.NewSandbox("", exampleProgram, "", false, false, "")
+	sb, err := fake.NewSandbox(&fake.SandboxConfig{Files: exampleProgram})
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/internal/lsp/regtest/runner.go b/internal/lsp/regtest/runner.go
index 33aa90c..1cf38d8 100644
--- a/internal/lsp/regtest/runner.go
+++ b/internal/lsp/regtest/runner.go
@@ -68,13 +68,10 @@
 }
 
 type runConfig struct {
-	editorConfig            fake.EditorConfig
-	modes                   Mode
-	proxyTxt                string
-	timeout                 time.Duration
-	gopath                  bool
-	withoutWorkspaceFolders bool
-	rootPath                string
+	editor  fake.EditorConfig
+	sandbox fake.SandboxConfig
+	modes   Mode
+	timeout time.Duration
 }
 
 func (r *Runner) defaultConfig() *runConfig {
@@ -105,7 +102,7 @@
 // WithProxy configures a file proxy using the given txtar-encoded string.
 func WithProxy(txt string) RunOption {
 	return optionSetter(func(opts *runConfig) {
-		opts.proxyTxt = txt
+		opts.sandbox.ProxyFiles = txt
 	})
 }
 
@@ -119,7 +116,7 @@
 // WithEditorConfig configures the editor's LSP session.
 func WithEditorConfig(config fake.EditorConfig) RunOption {
 	return optionSetter(func(opts *runConfig) {
-		opts.editorConfig = config
+		opts.editor = config
 	})
 }
 
@@ -129,7 +126,8 @@
 // neither workspace folders nor a root URI.
 func WithoutWorkspaceFolders() RunOption {
 	return optionSetter(func(opts *runConfig) {
-		opts.withoutWorkspaceFolders = false
+		// TODO: this cannot be right.
+		opts.editor.WithoutWorkspaceFolders = false
 	})
 }
 
@@ -138,7 +136,7 @@
 // tests need to check other cases.
 func WithRootPath(path string) RunOption {
 	return optionSetter(func(opts *runConfig) {
-		opts.rootPath = path
+		opts.editor.EditorRootPath = path
 	})
 }
 
@@ -146,7 +144,7 @@
 // than a separate working directory for use with modules.
 func InGOPATH() RunOption {
 	return optionSetter(func(opts *runConfig) {
-		opts.gopath = true
+		opts.sandbox.InGoPath = true
 	})
 }
 
@@ -155,12 +153,8 @@
 // Run executes the test function in the default configured gopls execution
 // modes. For each a test run, a new workspace is created containing the
 // un-txtared files specified by filedata.
-func (r *Runner) Run(t *testing.T, filedata string, test TestFunc, opts ...RunOption) {
+func (r *Runner) Run(t *testing.T, files string, test TestFunc, opts ...RunOption) {
 	t.Helper()
-	config := r.defaultConfig()
-	for _, opt := range opts {
-		opt.set(config)
-	}
 
 	tests := []struct {
 		name      string
@@ -174,6 +168,10 @@
 
 	for _, tc := range tests {
 		tc := tc
+		config := r.defaultConfig()
+		for _, opt := range opts {
+			opt.set(config)
+		}
 		if config.modes&tc.mode == 0 {
 			continue
 		}
@@ -186,7 +184,9 @@
 			if err := os.MkdirAll(tempDir, 0755); err != nil {
 				t.Fatal(err)
 			}
-			sandbox, err := fake.NewSandbox(tempDir, filedata, config.proxyTxt, config.gopath, config.withoutWorkspaceFolders, config.rootPath)
+			config.sandbox.Files = files
+			config.sandbox.RootDir = tempDir
+			sandbox, err := fake.NewSandbox(&config.sandbox)
 			if err != nil {
 				t.Fatal(err)
 			}
@@ -201,7 +201,7 @@
 			ls := &loggingFramer{}
 			framer := ls.framer(jsonrpc2.NewRawStream)
 			ts := servertest.NewPipeServer(ctx, ss, framer)
-			env := NewEnv(ctx, t, sandbox, ts, config.editorConfig)
+			env := NewEnv(ctx, t, sandbox, ts, config.editor)
 			defer func() {
 				if t.Failed() && r.PrintGoroutinesOnFailure {
 					pprof.Lookup("goroutine").WriteTo(os.Stderr, 1)