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 }