mirror of
https://github.com/golang/go
synced 2024-11-06 22:26:11 -07:00
150 lines
4.2 KiB
Go
150 lines
4.2 KiB
Go
|
// 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 fake
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"fmt"
|
||
|
"io/ioutil"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"strings"
|
||
|
|
||
|
"golang.org/x/tools/internal/gocommand"
|
||
|
"golang.org/x/tools/internal/lsp/protocol"
|
||
|
"golang.org/x/tools/txtar"
|
||
|
)
|
||
|
|
||
|
// Sandbox holds a collection of temporary resources to use for working with Go
|
||
|
// code in tests.
|
||
|
type Sandbox struct {
|
||
|
name string
|
||
|
gopath string
|
||
|
basedir string
|
||
|
env []string
|
||
|
Proxy *Proxy
|
||
|
Workdir *Workdir
|
||
|
}
|
||
|
|
||
|
// NewSandbox creates a collection of named temporary resources, with a
|
||
|
// working directory populated by the txtar-encoded content in srctxt, and a
|
||
|
// file-based module proxy populated with the txtar-encoded content in
|
||
|
// proxytxt.
|
||
|
func NewSandbox(name, srctxt, proxytxt string, env ...string) (_ *Sandbox, err error) {
|
||
|
sb := &Sandbox{
|
||
|
name: name,
|
||
|
env: env,
|
||
|
}
|
||
|
defer func() {
|
||
|
// Clean up if we fail at any point in this constructor.
|
||
|
if err != nil {
|
||
|
sb.Close()
|
||
|
}
|
||
|
}()
|
||
|
basedir, err := ioutil.TempDir("", fmt.Sprintf("goplstest-sandbox-%s-", name))
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("creating temporary workdir: %v", err)
|
||
|
}
|
||
|
sb.basedir = basedir
|
||
|
sb.gopath = filepath.Join(sb.basedir, "gopath")
|
||
|
workdir := filepath.Join(sb.basedir, "work")
|
||
|
proxydir := filepath.Join(sb.basedir, "proxy")
|
||
|
for _, subdir := range []string{sb.gopath, workdir, proxydir} {
|
||
|
if err := os.Mkdir(subdir, 0755); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
}
|
||
|
sb.Proxy, err = NewProxy(proxydir, proxytxt)
|
||
|
sb.Workdir, err = NewWorkdir(workdir, srctxt)
|
||
|
return sb, nil
|
||
|
}
|
||
|
|
||
|
func unpackTxt(txt string) map[string][]byte {
|
||
|
dataMap := make(map[string][]byte)
|
||
|
archive := txtar.Parse([]byte(txt))
|
||
|
for _, f := range archive.Files {
|
||
|
dataMap[f.Name] = f.Data
|
||
|
}
|
||
|
return dataMap
|
||
|
}
|
||
|
|
||
|
// splitModuleVersionPath extracts module information from files stored in the
|
||
|
// directory structure modulePath@version/suffix.
|
||
|
// For example:
|
||
|
// splitModuleVersionPath("mod.com@v1.2.3/package") = ("mod.com", "v1.2.3", "package")
|
||
|
func splitModuleVersionPath(path string) (modulePath, version, suffix string) {
|
||
|
parts := strings.Split(path, "/")
|
||
|
var modulePathParts []string
|
||
|
for i, p := range parts {
|
||
|
if strings.Contains(p, "@") {
|
||
|
mv := strings.SplitN(p, "@", 2)
|
||
|
modulePathParts = append(modulePathParts, mv[0])
|
||
|
return strings.Join(modulePathParts, "/"), mv[1], strings.Join(parts[i+1:], "/")
|
||
|
}
|
||
|
modulePathParts = append(modulePathParts, p)
|
||
|
}
|
||
|
// Default behavior: this is just a module path.
|
||
|
return path, "", ""
|
||
|
}
|
||
|
|
||
|
// GOPATH returns the value of the Sandbox GOPATH.
|
||
|
func (sb *Sandbox) GOPATH() string {
|
||
|
return sb.gopath
|
||
|
}
|
||
|
|
||
|
// GoEnv returns the default environment variables that can be used for
|
||
|
// invoking Go commands in the sandbox.
|
||
|
func (sb *Sandbox) GoEnv() []string {
|
||
|
return append([]string{
|
||
|
"GOPATH=" + sb.GOPATH(),
|
||
|
"GOPROXY=" + sb.Proxy.GOPROXY(),
|
||
|
"GO111MODULE=",
|
||
|
"GOSUMDB=off",
|
||
|
}, sb.env...)
|
||
|
}
|
||
|
|
||
|
// RunGoCommand executes a go command in the sandbox.
|
||
|
func (sb *Sandbox) RunGoCommand(ctx context.Context, verb string, args ...string) error {
|
||
|
inv := gocommand.Invocation{
|
||
|
Verb: verb,
|
||
|
Args: args,
|
||
|
WorkingDir: sb.Workdir.workdir,
|
||
|
Env: sb.GoEnv(),
|
||
|
}
|
||
|
gocmdRunner := &gocommand.Runner{}
|
||
|
_, stderr, _, err := gocmdRunner.RunRaw(ctx, inv)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
// Hardcoded "file watcher": If the command executed was "go mod init",
|
||
|
// send a file creation event for a go.mod in the working directory.
|
||
|
if strings.HasPrefix(stderr.String(), "go: creating new go.mod") {
|
||
|
modpath := filepath.Join(sb.Workdir.workdir, "go.mod")
|
||
|
sb.Workdir.sendEvents(ctx, []FileEvent{{
|
||
|
Path: modpath,
|
||
|
ProtocolEvent: protocol.FileEvent{
|
||
|
URI: toURI(modpath),
|
||
|
Type: protocol.Created,
|
||
|
},
|
||
|
}})
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Close removes all state associated with the sandbox.
|
||
|
func (sb *Sandbox) Close() error {
|
||
|
var goCleanErr error
|
||
|
if sb.gopath != "" {
|
||
|
if err := sb.RunGoCommand(context.Background(), "clean", "-modcache"); err != nil {
|
||
|
goCleanErr = fmt.Errorf("cleaning modcache: %v", err)
|
||
|
}
|
||
|
}
|
||
|
err := os.RemoveAll(sb.basedir)
|
||
|
if err != nil || goCleanErr != nil {
|
||
|
return fmt.Errorf("error(s) cleaning sandbox: cleaning modcache: %v; removing files: %v", goCleanErr, err)
|
||
|
}
|
||
|
return nil
|
||
|
}
|