github.com/golang/dep@v0.5.4/context_test.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 dep 6 7 import ( 8 "fmt" 9 "io/ioutil" 10 "log" 11 "os" 12 "path/filepath" 13 "runtime" 14 "strings" 15 "testing" 16 "unicode" 17 18 "github.com/golang/dep/internal/test" 19 ) 20 21 func discardLogger() *log.Logger { 22 return log.New(ioutil.Discard, "", 0) 23 } 24 25 func TestCtx_ProjectImport(t *testing.T) { 26 h := test.NewHelper(t) 27 defer h.Cleanup() 28 29 h.TempDir("src") 30 31 h.Setenv("GOPATH", h.Path(".")) 32 depCtx := &Ctx{GOPATH: h.Path(".")} 33 34 importPaths := []string{ 35 "github.com/pkg/errors", 36 "my/silly/thing", 37 } 38 39 for _, want := range importPaths { 40 fullpath := filepath.Join(depCtx.GOPATH, "src", want) 41 h.TempDir(filepath.Join("src", want)) 42 got, err := depCtx.ImportForAbs(fullpath) 43 if err != nil { 44 t.Fatal(err) 45 } 46 if got != want { 47 t.Fatalf("expected %s, got %s", want, got) 48 } 49 } 50 51 // test where it should return an error when directly within $GOPATH/src 52 got, err := depCtx.ImportForAbs(filepath.Join(depCtx.GOPATH, "src")) 53 if err == nil || !strings.Contains(err.Error(), "GOPATH/src") { 54 t.Fatalf("should have gotten an error for use directly in GOPATH/src, but got %s", got) 55 } 56 57 // test where it should return an error 58 got, err = depCtx.ImportForAbs("tra/la/la/la") 59 if err == nil { 60 t.Fatalf("should have gotten an error but did not for tra/la/la/la: %s", got) 61 } 62 } 63 64 func TestAbsoluteProjectRoot(t *testing.T) { 65 h := test.NewHelper(t) 66 defer h.Cleanup() 67 68 h.TempDir("src") 69 h.Setenv("GOPATH", h.Path(".")) 70 depCtx := &Ctx{GOPATH: h.Path(".")} 71 72 importPaths := map[string]bool{ 73 "github.com/pkg/errors": true, 74 "my/silly/thing": false, 75 } 76 77 for i, create := range importPaths { 78 if create { 79 h.TempDir(filepath.Join("src", i)) 80 } 81 } 82 83 for i, ok := range importPaths { 84 got, err := depCtx.AbsForImport(i) 85 if ok { 86 h.Must(err) 87 want := h.Path(filepath.Join("src", i)) 88 if got != want { 89 t.Fatalf("expected %s, got %q", want, got) 90 } 91 continue 92 } 93 94 if err == nil { 95 t.Fatalf("expected %s to fail", i) 96 } 97 } 98 99 // test that a file fails 100 h.TempFile("src/thing/thing.go", "hello world") 101 _, err := depCtx.AbsForImport("thing/thing.go") 102 if err == nil { 103 t.Fatal("error should not be nil for a file found") 104 } 105 } 106 107 func TestLoadProject(t *testing.T) { 108 h := test.NewHelper(t) 109 defer h.Cleanup() 110 111 h.TempDir(filepath.Join("src", "test1", "sub")) 112 h.TempFile(filepath.Join("src", "test1", ManifestName), "") 113 h.TempFile(filepath.Join("src", "test1", LockName), `memo = "cdafe8641b28cd16fe025df278b0a49b9416859345d8b6ba0ace0272b74925ee"`) 114 h.TempDir(filepath.Join("src", "test2", "sub")) 115 h.TempFile(filepath.Join("src", "test2", ManifestName), "") 116 117 var testcases = []struct { 118 name string 119 lock bool 120 wd string 121 }{ 122 {"direct", true, filepath.Join("src", "test1")}, 123 {"ascending", true, filepath.Join("src", "test1", "sub")}, 124 {"without lock", false, filepath.Join("src", "test2")}, 125 {"ascending without lock", false, filepath.Join("src", "test2", "sub")}, 126 } 127 128 for _, tc := range testcases { 129 t.Run(tc.name, func(t *testing.T) { 130 ctx := &Ctx{ 131 Out: discardLogger(), 132 Err: discardLogger(), 133 } 134 135 err := ctx.SetPaths(h.Path(tc.wd), h.Path(".")) 136 if err != nil { 137 t.Fatalf("%+v", err) 138 } 139 140 p, err := ctx.LoadProject() 141 switch { 142 case err != nil: 143 t.Fatalf("%s: LoadProject failed: %+v", tc.wd, err) 144 case p.Manifest == nil: 145 t.Fatalf("%s: Manifest file didn't load", tc.wd) 146 case tc.lock && p.Lock == nil: 147 t.Fatalf("%s: Lock file didn't load", tc.wd) 148 case !tc.lock && p.Lock != nil: 149 t.Fatalf("%s: Non-existent Lock file loaded", tc.wd) 150 } 151 }) 152 } 153 } 154 155 func TestExplicitRootProject(t *testing.T) { 156 h := test.NewHelper(t) 157 defer h.Cleanup() 158 159 h.TempDir(filepath.Join("src", "test1", "sub")) 160 h.TempFile(filepath.Join("src", "test1", ManifestName), "") 161 h.TempFile(filepath.Join("src", "test1", LockName), `memo = "cdafe8641b28cd16fe025df278b0a49b9416859345d8b6ba0ace0272b74925ee"`) 162 h.TempDir(filepath.Join("src", "test2", "sub")) 163 h.TempFile(filepath.Join("src", "test2", ManifestName), "") 164 h.Setenv("DEP_PROJECT_ROOT", "github.com/user/module") 165 166 type tcase struct { 167 name string 168 lock bool 169 wd string 170 } 171 var testcases = []tcase{ 172 {"direct", true, filepath.Join("src", "test1")}, 173 {"ascending", true, filepath.Join("src", "test1", "sub")}, 174 {"without lock", false, filepath.Join("src", "test2")}, 175 {"ascending without lock", false, filepath.Join("src", "test2", "sub")}, 176 } 177 178 tf := func(withGOPATH bool, tc tcase, t *testing.T) func(t *testing.T) { 179 return func(t *testing.T) { 180 ctx := &Ctx{ 181 Out: discardLogger(), 182 Err: discardLogger(), 183 } 184 185 var err error 186 if withGOPATH { 187 err = ctx.SetPaths(h.Path(tc.wd), h.Path(".")) 188 } else { 189 err = ctx.SetPaths(h.Path(tc.wd)) 190 } 191 if err != nil { 192 t.Fatalf("%+v", err) 193 } 194 ctx.ExplicitRoot = "github.com/user/module" 195 196 p, err := ctx.LoadProject() 197 switch { 198 case err != nil: 199 t.Fatalf("%s: LoadProject failed: %+v", tc.wd, err) 200 case p.Manifest == nil: 201 t.Fatalf("%s: Manifest file didn't load", tc.wd) 202 case tc.lock && p.Lock == nil: 203 t.Fatalf("%s: Lock file didn't load", tc.wd) 204 case !tc.lock && p.Lock != nil: 205 t.Fatalf("%s: Non-existent Lock file loaded", tc.wd) 206 } 207 } 208 } 209 210 for _, tc := range testcases { 211 t.Run(tc.name, func(t *testing.T) { 212 t.Run("within-GOPATH", tf(true, tc, t)) 213 t.Run("outside-GOPATH", tf(false, tc, t)) 214 }) 215 } 216 } 217 218 func TestLoadProjectNotFoundErrors(t *testing.T) { 219 tg := test.NewHelper(t) 220 defer tg.Cleanup() 221 222 tg.TempDir("src") 223 tg.TempDir("src/test1") 224 tg.TempDir("src/test1/sub") 225 tg.Setenv("GOPATH", tg.Path(".")) 226 227 var testcases = []struct { 228 lock bool 229 start string 230 path string 231 }{ 232 {true, filepath.Join("src", "test1"), ""}, //direct 233 {true, filepath.Join("src", "test1", "sub"), ""}, //ascending 234 } 235 236 for _, testcase := range testcases { 237 ctx := &Ctx{GOPATHs: []string{tg.Path(".")}, WorkingDir: tg.Path(testcase.start)} 238 239 _, err := ctx.LoadProject() 240 if err == nil { 241 t.Errorf("%s: should have returned 'No Manifest Found' error", testcase.start) 242 } 243 } 244 } 245 246 func TestLoadProjectManifestParseError(t *testing.T) { 247 tg := test.NewHelper(t) 248 defer tg.Cleanup() 249 250 tg.TempDir("src") 251 tg.TempDir("src/test1") 252 tg.TempFile(filepath.Join("src/test1", ManifestName), `[[constraint]]`) 253 tg.TempFile(filepath.Join("src/test1", LockName), `memo = "cdafe8641b28cd16fe025df278b0a49b9416859345d8b6ba0ace0272b74925ee"\n\n[[projects]]`) 254 tg.Setenv("GOPATH", tg.Path(".")) 255 256 path := filepath.Join("src", "test1") 257 tg.Cd(tg.Path(path)) 258 259 wd, err := os.Getwd() 260 if err != nil { 261 t.Fatal("failed to get working directory", err) 262 } 263 264 ctx := &Ctx{ 265 GOPATH: tg.Path("."), 266 WorkingDir: wd, 267 Out: discardLogger(), 268 Err: discardLogger(), 269 } 270 271 _, err = ctx.LoadProject() 272 if err == nil { 273 t.Fatal("should have returned 'Manifest Syntax' error") 274 } 275 } 276 277 func TestLoadProjectLockParseError(t *testing.T) { 278 tg := test.NewHelper(t) 279 defer tg.Cleanup() 280 281 tg.TempDir("src") 282 tg.TempDir("src/test1") 283 tg.TempFile(filepath.Join("src/test1", ManifestName), `[[constraint]]`) 284 tg.TempFile(filepath.Join("src/test1", LockName), `memo = "cdafe8641b28cd16fe025df278b0a49b9416859345d8b6ba0ace0272b74925ee"\n\n[[projects]]`) 285 tg.Setenv("GOPATH", tg.Path(".")) 286 287 path := filepath.Join("src", "test1") 288 tg.Cd(tg.Path(path)) 289 290 wd, err := os.Getwd() 291 if err != nil { 292 t.Fatal("failed to get working directory", err) 293 } 294 295 ctx := &Ctx{ 296 GOPATH: tg.Path("."), 297 WorkingDir: wd, 298 Out: discardLogger(), 299 Err: discardLogger(), 300 } 301 302 _, err = ctx.LoadProject() 303 if err == nil { 304 t.Fatal("should have returned 'Lock Syntax' error") 305 } 306 } 307 308 func TestLoadProjectNoSrcDir(t *testing.T) { 309 tg := test.NewHelper(t) 310 defer tg.Cleanup() 311 312 tg.TempDir("test1") 313 tg.TempFile(filepath.Join("test1", ManifestName), `[[constraint]]`) 314 tg.TempFile(filepath.Join("test1", LockName), `memo = "cdafe8641b28cd16fe025df278b0a49b9416859345d8b6ba0ace0272b74925ee"\n\n[[projects]]`) 315 tg.Setenv("GOPATH", tg.Path(".")) 316 317 ctx := &Ctx{GOPATH: tg.Path(".")} 318 path := filepath.Join("test1") 319 tg.Cd(tg.Path(path)) 320 321 f, _ := os.OpenFile(filepath.Join(ctx.GOPATH, "src", "test1", LockName), os.O_WRONLY, os.ModePerm) 322 defer f.Close() 323 324 _, err := ctx.LoadProject() 325 if err == nil { 326 t.Fatal("should have returned 'Split Absolute Root' error (no 'src' dir present)") 327 } 328 } 329 330 func TestLoadProjectGopkgFilenames(t *testing.T) { 331 // We are trying to skip this test on file systems which are case-sensiive. We could 332 // have used `fs.IsCaseSensitiveFilesystem` for this check. However, the code we are 333 // testing also relies on `fs.IsCaseSensitiveFilesystem`. So a bug in 334 // `fs.IsCaseSensitiveFilesystem` could prevent this test from being run. This is the 335 // only scenario where we prefer the OS heuristic over doing the actual work of 336 // validating filesystem case sensitivity via `fs.IsCaseSensitiveFilesystem`. 337 if runtime.GOOS != "windows" && runtime.GOOS != "darwin" { 338 t.Skip("skip this test on non-Windows, non-macOS") 339 } 340 341 // Here we test that a manifest filename with incorrect case throws an error. Similar 342 // error will also be thrown for the lock file as well which has been tested in 343 // `project_test.go#TestCheckGopkgFilenames`. So not repeating here. 344 345 h := test.NewHelper(t) 346 defer h.Cleanup() 347 348 invalidMfName := strings.ToLower(ManifestName) 349 350 wd := filepath.Join("src", "test") 351 h.TempFile(filepath.Join(wd, invalidMfName), "") 352 353 ctx := &Ctx{ 354 Out: discardLogger(), 355 Err: discardLogger(), 356 } 357 358 err := ctx.SetPaths(h.Path(wd), h.Path(".")) 359 if err != nil { 360 t.Fatalf("%+v", err) 361 } 362 363 _, err = ctx.LoadProject() 364 365 if err == nil { 366 t.Fatal("should have returned 'Manifest Filename' error") 367 } 368 369 expectedErrMsg := fmt.Sprintf( 370 "manifest filename %q does not match %q", 371 invalidMfName, ManifestName, 372 ) 373 374 if err.Error() != expectedErrMsg { 375 t.Fatalf("unexpected error: %+v", err) 376 } 377 } 378 379 // TestCaseInsensitive is test for Windows. This should work even though set 380 // difference letter cases in GOPATH. 381 func TestCaseInsensitiveGOPATH(t *testing.T) { 382 if runtime.GOOS != "windows" { 383 t.Skip("skip this test on non-Windows") 384 } 385 386 h := test.NewHelper(t) 387 defer h.Cleanup() 388 389 h.TempDir("src") 390 h.TempDir("src/test1") 391 h.TempFile(filepath.Join("src/test1", ManifestName), ` 392 [[constraint]] 393 name = "github.com/foo/bar" 394 branch = "master"`) 395 396 // Shuffle letter case 397 rs := []rune(strings.ToLower(h.Path("."))) 398 for i, r := range rs { 399 if unicode.IsLower(r) { 400 rs[i] = unicode.ToUpper(r) 401 } else { 402 rs[i] = unicode.ToLower(r) 403 } 404 } 405 gopath := string(rs) 406 h.Setenv("GOPATH", gopath) 407 wd := h.Path("src/test1") 408 409 depCtx := &Ctx{} 410 if err := depCtx.SetPaths(wd, gopath); err != nil { 411 t.Fatal(err) 412 } 413 if _, err := depCtx.LoadProject(); err != nil { 414 t.Fatal(err) 415 } 416 417 ip := "github.com/pkg/errors" 418 fullpath := filepath.Join(depCtx.GOPATH, "src", ip) 419 h.TempDir(filepath.Join("src", ip)) 420 pr, err := depCtx.ImportForAbs(fullpath) 421 if err != nil { 422 t.Fatal(err) 423 } 424 if pr != ip { 425 t.Fatalf("expected %s, got %s", ip, pr) 426 } 427 } 428 429 func TestDetectProjectGOPATH(t *testing.T) { 430 h := test.NewHelper(t) 431 defer h.Cleanup() 432 433 h.TempDir(filepath.Join("sym", "symlink")) 434 h.TempDir(filepath.Join("go", "src", "sym", "path")) 435 h.TempDir(filepath.Join("go", "src", "real", "path")) 436 h.TempDir(filepath.Join("go-two", "src", "real", "path")) 437 h.TempDir(filepath.Join("go-two", "src", "sym")) 438 439 ctx := &Ctx{ 440 GOPATHs: []string{h.Path("go"), h.Path("go-two")}, 441 } 442 443 testcases := []struct { 444 name string 445 root string 446 resolvedRoot string 447 GOPATH string 448 expectErr bool 449 }{ 450 { 451 name: "project-with-no-AbsRoot", 452 root: "", 453 resolvedRoot: filepath.Join(ctx.GOPATHs[0], "src", "real", "path"), 454 expectErr: true, 455 }, 456 { 457 name: "project-with-no-ResolvedAbsRoot", 458 root: filepath.Join(ctx.GOPATHs[0], "src", "real", "path"), 459 resolvedRoot: "", 460 expectErr: true, 461 }, 462 { 463 name: "AbsRoot-is-not-within-any-GOPATH", 464 root: filepath.Join(h.Path("."), "src", "real", "path"), 465 resolvedRoot: filepath.Join(h.Path("."), "src", "real", "path"), 466 expectErr: true, 467 }, 468 { 469 name: "neither-AbsRoot-nor-ResolvedAbsRoot-are-in-any-GOPATH", 470 root: filepath.Join(h.Path("."), "src", "sym", "path"), 471 resolvedRoot: filepath.Join(h.Path("."), "src", "real", "path"), 472 expectErr: true, 473 }, 474 { 475 name: "both-AbsRoot-and-ResolvedAbsRoot-are-in-the-same-GOPATH", 476 root: filepath.Join(ctx.GOPATHs[0], "src", "sym", "path"), 477 resolvedRoot: filepath.Join(ctx.GOPATHs[0], "src", "real", "path"), 478 expectErr: true, 479 }, 480 { 481 name: "AbsRoot-and-ResolvedAbsRoot-are-each-within-a-different-GOPATH", 482 root: filepath.Join(ctx.GOPATHs[0], "src", "sym", "path"), 483 resolvedRoot: filepath.Join(ctx.GOPATHs[1], "src", "real", "path"), 484 expectErr: true, 485 }, 486 { 487 name: "AbsRoot-is-not-a-symlink", 488 root: filepath.Join(ctx.GOPATHs[0], "src", "real", "path"), 489 resolvedRoot: filepath.Join(ctx.GOPATHs[0], "src", "real", "path"), 490 GOPATH: ctx.GOPATHs[0], 491 }, 492 { 493 name: "AbsRoot-is-a-symlink-to-ResolvedAbsRoot", 494 root: filepath.Join(h.Path("."), "sym", "symlink"), 495 resolvedRoot: filepath.Join(ctx.GOPATHs[0], "src", "real", "path"), 496 GOPATH: ctx.GOPATHs[0], 497 }, 498 } 499 500 for _, tc := range testcases { 501 t.Run(tc.name, func(t *testing.T) { 502 project := &Project{ 503 AbsRoot: tc.root, 504 ResolvedAbsRoot: tc.resolvedRoot, 505 } 506 507 GOPATH, err := ctx.DetectProjectGOPATH(project) 508 if !tc.expectErr && err != nil { 509 t.Fatalf("%+v", err) 510 } else if tc.expectErr && err == nil { 511 t.Fatalf("expected an error, got nil and gopath %s", GOPATH) 512 } 513 if GOPATH != tc.GOPATH { 514 t.Errorf("expected GOPATH %s, got %s", tc.GOPATH, GOPATH) 515 } 516 }) 517 } 518 } 519 520 func TestDetectGOPATH(t *testing.T) { 521 th := test.NewHelper(t) 522 defer th.Cleanup() 523 524 th.TempDir(filepath.Join("code", "src", "github.com", "username", "package")) 525 th.TempDir(filepath.Join("go", "src", "github.com", "username", "package")) 526 th.TempDir(filepath.Join("gotwo", "src", "github.com", "username", "package")) 527 th.TempDir(filepath.Join("gothree", "sep", "src", "github.com", "username", "package")) 528 529 sep := string(os.PathSeparator) 530 531 ctx := &Ctx{GOPATHs: []string{ 532 th.Path("go"), 533 th.Path("gotwo"), 534 th.Path("gothree") + sep + sep + "sep", 535 }} 536 537 testcases := []struct { 538 GOPATH string 539 path string 540 err bool 541 }{ 542 {th.Path("go"), th.Path(filepath.Join("go", "src", "github.com", "username", "package")), false}, 543 {th.Path("go"), th.Path(filepath.Join("go", "src", "github.com", "username", "package")), false}, 544 {th.Path("gotwo"), th.Path(filepath.Join("gotwo", "src", "github.com", "username", "package")), false}, 545 {th.Path(filepath.Join("gothree", "sep")), 546 th.Path(filepath.Join("gothree", "sep", "src", "github.com", "username", "package")), false}, 547 {"", th.Path(filepath.Join("code", "src", "github.com", "username", "package")), true}, 548 } 549 550 for _, tc := range testcases { 551 GOPATH, err := ctx.detectGOPATH(tc.path) 552 if tc.err && err == nil { 553 t.Error("expected error but got none") 554 } 555 if GOPATH != tc.GOPATH { 556 t.Errorf("expected GOPATH to be %s, got %s", tc.GOPATH, GOPATH) 557 } 558 } 559 } 560 561 func TestDepCachedir(t *testing.T) { 562 h := test.NewHelper(t) 563 defer h.Cleanup() 564 565 h.TempDir("cache") 566 // Create the directory for default cachedir location. 567 h.TempDir(filepath.Join("go", "pkg", "dep")) 568 569 testCachedir := h.Path("cache") 570 gopath := h.Path("go") 571 discardLgr := discardLogger() 572 573 cases := []struct { 574 cachedir string 575 wantCachedir string 576 }{ 577 // If `Cachedir` is not set in the context, it should use `$GOPATH/pkg/dep`. 578 {cachedir: "", wantCachedir: h.Path(filepath.Join("go", "pkg", "dep"))}, 579 // If `Cachedir` is set in the context, it should use that. 580 {cachedir: testCachedir, wantCachedir: testCachedir}, 581 } 582 583 for _, c := range cases { 584 ctx := &Ctx{ 585 GOPATH: gopath, 586 Cachedir: c.cachedir, 587 Out: discardLgr, 588 Err: discardLgr, 589 } 590 sm, err := ctx.SourceManager() 591 h.Must(err) 592 defer sm.Release() 593 594 if sm.Cachedir() != c.wantCachedir { 595 t.Errorf("expected cachedir to be %s, got %s", c.wantCachedir, sm.Cachedir()) 596 } 597 } 598 }