github.com/golang/dep@v0.5.4/internal/test/integration/testproj.go (about)

     1  // Copyright 2017 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  	"bytes"
     9  	"io"
    10  	"io/ioutil"
    11  	"os"
    12  	"os/exec"
    13  	"path/filepath"
    14  	"runtime"
    15  	"sort"
    16  	"strings"
    17  	"testing"
    18  
    19  	"github.com/golang/dep/internal/test"
    20  	"github.com/pkg/errors"
    21  )
    22  
    23  const (
    24  	projectRoot = "src/github.com/golang/notexist"
    25  )
    26  
    27  // RunFunc defines the function signature for an integration test command to execute.
    28  type RunFunc func(prog string, newargs []string, outW, errW io.Writer, dir string, env []string) error
    29  
    30  // TestProject manages the "virtual" test project directory structure
    31  // and content
    32  type TestProject struct {
    33  	t          *testing.T
    34  	preImports []string
    35  	tempdir    string
    36  	env        []string
    37  	origWd     string
    38  	stdout     bytes.Buffer
    39  	stderr     bytes.Buffer
    40  	run        RunFunc
    41  }
    42  
    43  // NewTestProject initializes a new test's project directory.
    44  func NewTestProject(t *testing.T, initPath, wd string, run RunFunc) *TestProject {
    45  	// Cleaning up the GIT_DIR variable is useful when running tests under git
    46  	// rebase. In any case, since we're operating with temporary clones,
    47  	// no pre-existing value could be useful here.
    48  	// We do it globally because the internal runs don't actually use the
    49  	// TestProject's environment.
    50  	os.Unsetenv("GIT_DIR")
    51  
    52  	new := &TestProject{
    53  		t:      t,
    54  		origWd: wd,
    55  		env:    os.Environ(),
    56  		run:    run,
    57  	}
    58  	new.makeRootTempDir()
    59  	new.TempDir(projectRoot, "vendor")
    60  	new.CopyTree(initPath)
    61  
    62  	new.Setenv("GOPATH", new.tempdir)
    63  
    64  	return new
    65  }
    66  
    67  // Cleanup (remove) the test project's directory.
    68  func (p *TestProject) Cleanup() {
    69  	os.RemoveAll(p.tempdir)
    70  }
    71  
    72  // Path to the test project directory.
    73  func (p *TestProject) Path(args ...string) string {
    74  	return filepath.Join(p.tempdir, filepath.Join(args...))
    75  }
    76  
    77  // ProjPath builds an import path for the test project.
    78  func (p *TestProject) ProjPath(args ...string) string {
    79  	localPath := append([]string{projectRoot}, args...)
    80  	return p.Path(localPath...)
    81  }
    82  
    83  // TempDir creates a temporary directory for the test project.
    84  func (p *TestProject) TempDir(args ...string) {
    85  	fullPath := p.Path(args...)
    86  	if err := os.MkdirAll(fullPath, 0755); err != nil && !os.IsExist(err) {
    87  		p.t.Fatalf("%+v", errors.Errorf("Unable to create temp directory: %s", fullPath))
    88  	}
    89  }
    90  
    91  // TempProjDir builds the path to a package within the test project.
    92  func (p *TestProject) TempProjDir(args ...string) {
    93  	localPath := append([]string{projectRoot}, args...)
    94  	p.TempDir(localPath...)
    95  }
    96  
    97  // VendorPath lists the contents of the test project's vendor directory.
    98  func (p *TestProject) VendorPath(args ...string) string {
    99  	localPath := append([]string{projectRoot, "vendor"}, args...)
   100  	p.TempDir(localPath...)
   101  	return p.Path(localPath...)
   102  }
   103  
   104  // RunGo runs a go command, and expects it to succeed.
   105  func (p *TestProject) RunGo(args ...string) {
   106  	cmd := exec.Command("go", args...)
   107  	p.stdout.Reset()
   108  	p.stderr.Reset()
   109  	cmd.Stdout = &p.stdout
   110  	cmd.Stderr = &p.stderr
   111  	cmd.Dir = p.tempdir
   112  	cmd.Env = p.env
   113  	status := cmd.Run()
   114  	if p.stdout.Len() > 0 {
   115  		p.t.Log("go standard output:")
   116  		p.t.Log(p.stdout.String())
   117  	}
   118  	if p.stderr.Len() > 0 {
   119  		p.t.Log("go standard error:")
   120  		p.t.Log(p.stderr.String())
   121  	}
   122  	if status != nil {
   123  		p.t.Logf("go %v failed unexpectedly: %v", args, status)
   124  		p.t.FailNow()
   125  	}
   126  }
   127  
   128  // RunGit runs a git command, and expects it to succeed.
   129  func (p *TestProject) RunGit(dir string, args ...string) {
   130  	cmd := exec.Command("git", args...)
   131  	p.stdout.Reset()
   132  	p.stderr.Reset()
   133  	cmd.Stdout = &p.stdout
   134  	cmd.Stderr = &p.stderr
   135  	cmd.Dir = dir
   136  	cmd.Env = p.env
   137  	status := cmd.Run()
   138  	if *test.PrintLogs {
   139  		if p.stdout.Len() > 0 {
   140  			p.t.Logf("git %v standard output:", args)
   141  			p.t.Log(p.stdout.String())
   142  		}
   143  		if p.stderr.Len() > 0 {
   144  			p.t.Logf("git %v standard error:", args)
   145  			p.t.Log(p.stderr.String())
   146  		}
   147  	}
   148  	if status != nil {
   149  		p.t.Logf("git %v failed unexpectedly: %v", args, status)
   150  		p.t.FailNow()
   151  	}
   152  }
   153  
   154  // GetStdout gets the Stdout output from test run.
   155  func (p *TestProject) GetStdout() string {
   156  	return p.stdout.String()
   157  }
   158  
   159  // GetStderr gets the Stderr output from test run.
   160  func (p *TestProject) GetStderr() string {
   161  	return p.stderr.String()
   162  }
   163  
   164  // GetVendorGit populates the initial vendor directory for a test project.
   165  func (p *TestProject) GetVendorGit(ip string) {
   166  	parse := strings.Split(ip, "/")
   167  	gitDir := strings.Join(parse[:len(parse)-1], string(filepath.Separator))
   168  	p.TempProjDir("vendor", gitDir)
   169  	p.RunGit(p.ProjPath("vendor", gitDir), "clone", "http://"+ip)
   170  }
   171  
   172  // DoRun executes the integration test command against the test project.
   173  func (p *TestProject) DoRun(args []string) error {
   174  	if *test.PrintLogs {
   175  		p.t.Logf("running testdep %v", args)
   176  	}
   177  	prog := filepath.Join(p.origWd, "testdep"+test.ExeSuffix)
   178  
   179  	newargs := args
   180  	if args[0] != "check" {
   181  		newargs = append([]string{args[0], "-v"}, args[1:]...)
   182  	}
   183  
   184  	p.stdout.Reset()
   185  	p.stderr.Reset()
   186  
   187  	status := p.run(prog, newargs, &p.stdout, &p.stderr, p.ProjPath(""), p.env)
   188  
   189  	if *test.PrintLogs {
   190  		if p.stdout.Len() > 0 {
   191  			p.t.Logf("\nstandard output:%s", p.stdout.String())
   192  		}
   193  		if p.stderr.Len() > 0 {
   194  			p.t.Logf("standard error:\n%s", p.stderr.String())
   195  		}
   196  	}
   197  	return status
   198  }
   199  
   200  // CopyTree recursively copies a source directory into the test project's directory.
   201  func (p *TestProject) CopyTree(src string) {
   202  	filepath.Walk(src,
   203  		func(path string, info os.FileInfo, err error) error {
   204  			if path != src {
   205  				localpath := path[len(src)+1:]
   206  				if info.IsDir() {
   207  					p.TempDir(projectRoot, localpath)
   208  				} else {
   209  					destpath := filepath.Join(p.ProjPath(), localpath)
   210  					copyFile(destpath, path)
   211  				}
   212  			}
   213  			return nil
   214  		})
   215  }
   216  
   217  func copyFile(dest, src string) {
   218  	in, err := os.Open(src)
   219  	if err != nil {
   220  		panic(err)
   221  	}
   222  	defer in.Close()
   223  
   224  	out, err := os.Create(dest)
   225  	if err != nil {
   226  		panic(err)
   227  	}
   228  	defer out.Close()
   229  
   230  	io.Copy(out, in)
   231  }
   232  
   233  // GetVendorPaths collects final vendor paths at a depth of three levels.
   234  func (p *TestProject) GetVendorPaths() []string {
   235  	vendorPath := p.ProjPath("vendor")
   236  	result := make([]string, 0)
   237  	filepath.Walk(
   238  		vendorPath,
   239  		func(path string, info os.FileInfo, err error) error {
   240  			if len(path) > len(vendorPath) && info.IsDir() {
   241  				parse := strings.Split(path[len(vendorPath)+1:], string(filepath.Separator))
   242  				if len(parse) == 3 {
   243  					result = append(result, strings.Join(parse, "/"))
   244  					return filepath.SkipDir
   245  				}
   246  			}
   247  			return nil
   248  		},
   249  	)
   250  	sort.Strings(result)
   251  	return result
   252  }
   253  
   254  // GetImportPaths collect final vendor paths at a depth of three levels.
   255  func (p *TestProject) GetImportPaths() []string {
   256  	importPath := p.Path("src")
   257  	result := make([]string, 0)
   258  	filepath.Walk(
   259  		importPath,
   260  		func(path string, info os.FileInfo, err error) error {
   261  			if len(path) > len(importPath) && info.IsDir() {
   262  				parse := strings.Split(path[len(importPath)+1:], string(filepath.Separator))
   263  				if len(parse) == 3 {
   264  					result = append(result, strings.Join(parse, "/"))
   265  					return filepath.SkipDir
   266  				}
   267  			}
   268  			return nil
   269  		},
   270  	)
   271  	sort.Strings(result)
   272  	return result
   273  }
   274  
   275  // RecordImportPaths takes a snapshot of the import paths before test is run.
   276  func (p *TestProject) RecordImportPaths() {
   277  	p.preImports = p.GetImportPaths()
   278  }
   279  
   280  // CompareImportPaths compares import paths before and after test commands.
   281  func (p *TestProject) CompareImportPaths() {
   282  	wantImportPaths := p.preImports
   283  	gotImportPaths := p.GetImportPaths()
   284  	if len(gotImportPaths) != len(wantImportPaths) {
   285  		p.t.Fatalf("Import path count changed during command: pre %d post %d", len(wantImportPaths), len(gotImportPaths))
   286  	}
   287  	for ind := range gotImportPaths {
   288  		if gotImportPaths[ind] != wantImportPaths[ind] {
   289  			p.t.Errorf("Change in import paths during: pre %s post %s", gotImportPaths, wantImportPaths)
   290  		}
   291  	}
   292  }
   293  
   294  // makeRootTempdir makes a temporary directory for a run of testgo. If
   295  // the temporary directory was already created, this does nothing.
   296  func (p *TestProject) makeRootTempDir() {
   297  	if p.tempdir == "" {
   298  		var err error
   299  		p.tempdir, err = ioutil.TempDir("", "gotest")
   300  		p.Must(err)
   301  
   302  		// Fix for OSX where the tempdir is a symlink:
   303  		if runtime.GOOS == "darwin" {
   304  			p.tempdir, err = filepath.EvalSymlinks(p.tempdir)
   305  			p.Must(err)
   306  		}
   307  	}
   308  }
   309  
   310  // Setenv sets an environment variable to use when running the test go
   311  // command.
   312  func (p *TestProject) Setenv(name, val string) {
   313  	p.env = append(p.env, name+"="+val)
   314  }
   315  
   316  // Must gives a fatal error if err is not nil.
   317  func (p *TestProject) Must(err error) {
   318  	if err != nil {
   319  		p.t.Fatalf("%+v", err)
   320  	}
   321  }