github.com/golang/dep@v0.5.4/gps/source_cache_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 gps 6 7 import ( 8 "io/ioutil" 9 "log" 10 "path" 11 "reflect" 12 "sort" 13 "testing" 14 "time" 15 16 "github.com/golang/dep/gps/pkgtree" 17 "github.com/golang/dep/internal/test" 18 "github.com/pkg/errors" 19 ) 20 21 func Test_singleSourceCache(t *testing.T) { 22 newMem := func(*testing.T, string) sourceCache { 23 return memoryCache{} 24 } 25 t.Run("mem", singleSourceCacheTest{newCache: newMem}.run) 26 27 epoch := time.Now().Unix() 28 newBolt := func(t *testing.T, cachedir string) sourceCache { 29 bc, err := newBoltCache(cachedir, epoch, log.New(test.Writer{TB: t}, "", 0)) 30 if err != nil { 31 t.Fatal(err) 32 } 33 return bc 34 } 35 t.Run("bolt/keepOpen", singleSourceCacheTest{newCache: newBolt}.run) 36 t.Run("bolt/reOpen", singleSourceCacheTest{newCache: newBolt, persistent: true}.run) 37 38 newMulti := func(t *testing.T, cachedir string) sourceCache { 39 bc, err := newBoltCache(cachedir, epoch, log.New(test.Writer{TB: t}, "", 0)) 40 if err != nil { 41 t.Fatal(err) 42 } 43 return newMultiCache(memoryCache{}, bc) 44 } 45 t.Run("multi/keepOpen", singleSourceCacheTest{newCache: newMulti}.run) 46 t.Run("multi/reOpen", singleSourceCacheTest{persistent: true, newCache: newMulti}.run) 47 48 t.Run("multi/keepOpen/noDisk", singleSourceCacheTest{ 49 newCache: func(*testing.T, string) sourceCache { 50 return newMultiCache(memoryCache{}, discardCache{}) 51 }, 52 }.run) 53 54 t.Run("multi/reOpen/noMem", singleSourceCacheTest{ 55 persistent: true, 56 newCache: func(t *testing.T, cachedir string) sourceCache { 57 bc, err := newBoltCache(cachedir, epoch, log.New(test.Writer{TB: t}, "", 0)) 58 if err != nil { 59 t.Fatal(err) 60 } 61 return newMultiCache(discardCache{}, bc) 62 }, 63 }.run) 64 } 65 66 var testAnalyzerInfo = ProjectAnalyzerInfo{ 67 Name: "test-analyzer", 68 Version: 1, 69 } 70 71 type singleSourceCacheTest struct { 72 newCache func(*testing.T, string) sourceCache 73 persistent bool 74 } 75 76 // run tests singleSourceCache methods of caches returned by test.newCache. 77 // For test.persistent caches, test.newCache is periodically called mid-test to ensure persistence. 78 func (test singleSourceCacheTest) run(t *testing.T) { 79 const root = "example.com/test" 80 pi := mkPI(root).normalize() 81 cpath, err := ioutil.TempDir("", "singlesourcecache") 82 if err != nil { 83 t.Fatalf("Failed to create temp cache dir: %s", err) 84 } 85 86 t.Run("info", func(t *testing.T) { 87 const rev Revision = "revision" 88 89 sc := test.newCache(t, cpath) 90 c := sc.newSingleSourceCache(pi) 91 defer func() { 92 if err := sc.close(); err != nil { 93 t.Fatal("failed to close cache:", err) 94 } 95 }() 96 97 var m Manifest = &simpleRootManifest{ 98 c: ProjectConstraints{ 99 ProjectRoot("foo"): ProjectProperties{ 100 Constraint: Any(), 101 }, 102 ProjectRoot("bar"): ProjectProperties{ 103 Source: "whatever", 104 Constraint: testSemverConstraint(t, "> 1.3"), 105 }, 106 }, 107 ovr: ProjectConstraints{ 108 ProjectRoot("b"): ProjectProperties{ 109 Constraint: testSemverConstraint(t, "2.0.0"), 110 }, 111 }, 112 req: map[string]bool{ 113 "c": true, 114 "d": true, 115 }, 116 ig: pkgtree.NewIgnoredRuleset([]string{"a", "b"}), 117 } 118 var l Lock = &safeLock{ 119 p: []LockedProject{ 120 NewLockedProject(mkPI("github.com/sdboyer/gps"), NewVersion("v0.10.0").Pair("anything"), []string{"gps"}), 121 NewLockedProject(mkPI("github.com/sdboyer/gps2"), NewVersion("v0.10.0").Pair("whatever"), nil), 122 NewLockedProject(mkPI("github.com/sdboyer/gps3"), NewVersion("v0.10.0").Pair("again"), []string{"gps", "flugle"}), 123 NewLockedProject(mkPI("foo"), NewVersion("nada").Pair("itsaliving"), []string{"foo"}), 124 NewLockedProject(mkPI("github.com/sdboyer/gps4"), NewVersion("v0.10.0").Pair("meow"), []string{"flugle", "gps"}), 125 }, 126 } 127 c.setManifestAndLock(rev, testAnalyzerInfo, m, l) 128 129 if test.persistent { 130 if err := sc.close(); err != nil { 131 t.Fatal("failed to close cache:", err) 132 } 133 sc = test.newCache(t, cpath) 134 c = sc.newSingleSourceCache(pi) 135 } 136 137 gotM, gotL, ok := c.getManifestAndLock(rev, testAnalyzerInfo) 138 if !ok { 139 t.Error("no manifest and lock found for revision") 140 } 141 compareManifests(t, m, gotM) 142 // TODO(sdboyer) use DiffLocks after refactoring to avoid import cycles 143 if !locksAreEq(l, gotL) { 144 t.Errorf("locks are different:\n\t(GOT): %s\n\t(WNT): %s", l, gotL) 145 } 146 147 m = &simpleRootManifest{ 148 c: ProjectConstraints{ 149 ProjectRoot("foo"): ProjectProperties{ 150 Source: "whatever", 151 Constraint: Any(), 152 }, 153 }, 154 ovr: ProjectConstraints{ 155 ProjectRoot("bar"): ProjectProperties{ 156 Constraint: testSemverConstraint(t, "2.0.0"), 157 }, 158 }, 159 req: map[string]bool{ 160 "a": true, 161 "b": true, 162 }, 163 ig: pkgtree.NewIgnoredRuleset([]string{"c", "d"}), 164 } 165 l = &safeLock{ 166 p: []LockedProject{ 167 NewLockedProject(mkPI("github.com/sdboyer/gps"), NewVersion("v0.10.0").Pair("278a227dfc3d595a33a77ff3f841fd8ca1bc8cd0"), []string{"gps"}), 168 NewLockedProject(mkPI("github.com/sdboyer/gps2"), NewVersion("v0.11.0").Pair("anything"), []string{"gps"}), 169 NewLockedProject(mkPI("github.com/sdboyer/gps3"), Revision("278a227dfc3d595a33a77ff3f841fd8ca1bc8cd0"), []string{"gps"}), 170 }, 171 } 172 c.setManifestAndLock(rev, testAnalyzerInfo, m, l) 173 174 if test.persistent { 175 if err := sc.close(); err != nil { 176 t.Fatal("failed to close cache:", err) 177 } 178 sc = test.newCache(t, cpath) 179 c = sc.newSingleSourceCache(pi) 180 } 181 182 gotM, gotL, ok = c.getManifestAndLock(rev, testAnalyzerInfo) 183 if !ok { 184 t.Error("no manifest and lock found for revision") 185 } 186 compareManifests(t, m, gotM) 187 // TODO(sdboyer) use DiffLocks after refactoring to avoid import cycles 188 if !locksAreEq(l, gotL) { 189 t.Errorf("locks are different:\n\t(GOT): %s\n\t(WNT): %s", l, gotL) 190 } 191 }) 192 193 t.Run("pkgTree", func(t *testing.T) { 194 sc := test.newCache(t, cpath) 195 c := sc.newSingleSourceCache(pi) 196 defer func() { 197 if err := sc.close(); err != nil { 198 t.Fatal("failed to close cache:", err) 199 } 200 }() 201 202 const rev Revision = "rev_adsfjkl" 203 204 if got, ok := c.getPackageTree(rev, root); ok { 205 t.Fatalf("unexpected result before setting package tree: %v", got) 206 } 207 208 if test.persistent { 209 if err := sc.close(); err != nil { 210 t.Fatal("failed to close cache:", err) 211 } 212 sc = test.newCache(t, cpath) 213 c = sc.newSingleSourceCache(pi) 214 } 215 216 pt := pkgtree.PackageTree{ 217 ImportRoot: root, 218 Packages: map[string]pkgtree.PackageOrErr{ 219 root: { 220 P: pkgtree.Package{ 221 ImportPath: root, 222 CommentPath: "comment", 223 Name: "test", 224 Imports: []string{ 225 "sort", 226 }, 227 }, 228 }, 229 path.Join(root, "simple"): { 230 P: pkgtree.Package{ 231 ImportPath: path.Join(root, "simple"), 232 CommentPath: "comment", 233 Name: "simple", 234 Imports: []string{ 235 "github.com/golang/dep/gps", 236 "sort", 237 }, 238 }, 239 }, 240 path.Join(root, "m1p"): { 241 P: pkgtree.Package{ 242 ImportPath: path.Join(root, "m1p"), 243 CommentPath: "", 244 Name: "m1p", 245 Imports: []string{ 246 "github.com/golang/dep/gps", 247 "os", 248 "sort", 249 }, 250 }, 251 }, 252 }, 253 } 254 c.setPackageTree(rev, pt) 255 256 if test.persistent { 257 if err := sc.close(); err != nil { 258 t.Fatal("failed to close cache:", err) 259 } 260 sc = test.newCache(t, cpath) 261 c = sc.newSingleSourceCache(pi) 262 } 263 264 got, ok := c.getPackageTree(rev, root) 265 if !ok { 266 t.Errorf("no package tree found:\n\t(WNT): %#v", pt) 267 } 268 comparePackageTree(t, pt, got) 269 270 if test.persistent { 271 if err := sc.close(); err != nil { 272 t.Fatal("failed to close cache:", err) 273 } 274 sc = test.newCache(t, cpath) 275 c = sc.newSingleSourceCache(pi) 276 } 277 278 pt = pkgtree.PackageTree{ 279 ImportRoot: root, 280 Packages: map[string]pkgtree.PackageOrErr{ 281 path.Join(root, "test"): { 282 Err: errors.New("error"), 283 }, 284 }, 285 } 286 c.setPackageTree(rev, pt) 287 288 if test.persistent { 289 if err := sc.close(); err != nil { 290 t.Fatal("failed to close cache:", err) 291 } 292 sc = test.newCache(t, cpath) 293 c = sc.newSingleSourceCache(pi) 294 } 295 296 got, ok = c.getPackageTree(rev, root) 297 if !ok { 298 t.Errorf("no package tree found:\n\t(WNT): %#v", pt) 299 } 300 comparePackageTree(t, pt, got) 301 }) 302 303 t.Run("versions", func(t *testing.T) { 304 sc := test.newCache(t, cpath) 305 c := sc.newSingleSourceCache(pi) 306 defer func() { 307 if err := sc.close(); err != nil { 308 t.Fatal("failed to close cache:", err) 309 } 310 }() 311 312 const rev1, rev2 = "rev1", "rev2" 313 const br, ver = "branch_name", "2.10" 314 versions := []PairedVersion{ 315 NewBranch(br).Pair(rev1), 316 NewVersion(ver).Pair(rev2), 317 } 318 SortPairedForDowngrade(versions) 319 c.setVersionMap(versions) 320 321 if test.persistent { 322 if err := sc.close(); err != nil { 323 t.Fatal("failed to close cache:", err) 324 } 325 sc = test.newCache(t, cpath) 326 c = sc.newSingleSourceCache(pi) 327 } 328 329 t.Run("getAllVersions", func(t *testing.T) { 330 got, ok := c.getAllVersions() 331 if !ok || len(got) != len(versions) { 332 t.Errorf("unexpected versions:\n\t(GOT): %#v\n\t(WNT): %#v", got, versions) 333 } else { 334 SortPairedForDowngrade(got) 335 for i := range versions { 336 if !versions[i].identical(got[i]) { 337 t.Errorf("unexpected versions:\n\t(GOT): %#v\n\t(WNT): %#v", got, versions) 338 break 339 } 340 } 341 } 342 }) 343 344 revToUV := map[Revision]UnpairedVersion{ 345 rev1: NewBranch(br), 346 rev2: NewVersion(ver), 347 } 348 349 t.Run("getVersionsFor", func(t *testing.T) { 350 for rev, want := range revToUV { 351 rev, want := rev, want 352 t.Run(string(rev), func(t *testing.T) { 353 uvs, ok := c.getVersionsFor(rev) 354 if !ok { 355 t.Errorf("no version found:\n\t(WNT) %#v", want) 356 } else if len(uvs) != 1 { 357 t.Errorf("expected one result but got %d", len(uvs)) 358 } else { 359 uv := uvs[0] 360 if uv.Type() != want.Type() { 361 t.Errorf("expected version type %d but got %d", want.Type(), uv.Type()) 362 } 363 if uv.String() != want.String() { 364 t.Errorf("expected version %q but got %q", want.String(), uv.String()) 365 } 366 } 367 }) 368 } 369 }) 370 371 t.Run("getRevisionFor", func(t *testing.T) { 372 for want, uv := range revToUV { 373 want, uv := want, uv 374 t.Run(uv.String(), func(t *testing.T) { 375 rev, ok := c.getRevisionFor(uv) 376 if !ok { 377 t.Errorf("expected revision %q but got none", want) 378 } else if rev != want { 379 t.Errorf("expected revision %q but got %q", want, rev) 380 } 381 }) 382 } 383 }) 384 385 t.Run("toRevision", func(t *testing.T) { 386 for want, uv := range revToUV { 387 want, uv := want, uv 388 t.Run(uv.String(), func(t *testing.T) { 389 rev, ok := c.toRevision(uv) 390 if !ok { 391 t.Errorf("expected revision %q but got none", want) 392 } else if rev != want { 393 t.Errorf("expected revision %q but got %q", want, rev) 394 } 395 }) 396 } 397 }) 398 399 t.Run("toUnpaired", func(t *testing.T) { 400 for rev, want := range revToUV { 401 rev, want := rev, want 402 t.Run(want.String(), func(t *testing.T) { 403 uv, ok := c.toUnpaired(rev) 404 if !ok { 405 t.Errorf("no UnpairedVersion found:\n\t(WNT): %#v", uv) 406 } else if !uv.identical(want) { 407 t.Errorf("unexpected UnpairedVersion:\n\t(GOT): %#v\n\t(WNT): %#v", uv, want) 408 } 409 }) 410 } 411 }) 412 }) 413 } 414 415 // compareManifests compares two manifests and reports differences as test errors. 416 func compareManifests(t *testing.T, want, got Manifest) { 417 if (want == nil || got == nil) && (got != nil || want != nil) { 418 t.Errorf("one manifest is nil:\n\t(GOT): %#v\n\t(WNT): %#v", got, want) 419 return 420 } 421 { 422 want, got := want.DependencyConstraints(), got.DependencyConstraints() 423 if !projectConstraintsEqual(want, got) { 424 t.Errorf("unexpected constraints:\n\t(GOT): %#v\n\t(WNT): %#v", got, want) 425 } 426 } 427 428 wantRM, wantOK := want.(RootManifest) 429 gotRM, gotOK := got.(RootManifest) 430 if wantOK && !gotOK { 431 t.Errorf("expected RootManifest:\n\t(GOT): %#v", got) 432 return 433 } 434 if gotOK && !wantOK { 435 t.Errorf("didn't expected RootManifest:\n\t(GOT): %#v", got) 436 return 437 } 438 439 { 440 want, got := wantRM.IgnoredPackages(), gotRM.IgnoredPackages() 441 if !reflect.DeepEqual(want.ToSlice(), got.ToSlice()) { 442 t.Errorf("unexpected ignored packages:\n\t(GOT): %#v\n\t(WNT): %#v", got, want) 443 } 444 } 445 446 { 447 want, got := wantRM.Overrides(), gotRM.Overrides() 448 if !projectConstraintsEqual(want, got) { 449 t.Errorf("unexpected overrides:\n\t(GOT): %#v\n\t(WNT): %#v", got, want) 450 } 451 } 452 453 { 454 want, got := wantRM.RequiredPackages(), gotRM.RequiredPackages() 455 if !mapStringBoolEqual(want, got) { 456 t.Errorf("unexpected required packages:\n\t(GOT): %#v\n\t(WNT): %#v", got, want) 457 } 458 } 459 } 460 461 // comparePackageTree compares two pkgtree.PackageTree and reports differences as test errors. 462 func comparePackageTree(t *testing.T, want, got pkgtree.PackageTree) { 463 if got.ImportRoot != want.ImportRoot { 464 t.Errorf("expected package tree root %q but got %q", want.ImportRoot, got.ImportRoot) 465 } 466 { 467 want, got := want.Packages, got.Packages 468 if len(want) != len(got) { 469 t.Errorf("unexpected packages:\n\t(GOT): %#v\n\t(WNT): %#v", got, want) 470 } else { 471 for k, v := range want { 472 if v2, ok := got[k]; !ok { 473 t.Errorf("key %s: expected %v but got none", k, v) 474 } else if !packageOrErrEqual(v, v2) { 475 t.Errorf("key %s: expected %v but got %v", k, v, v2) 476 } 477 } 478 } 479 } 480 } 481 482 func projectConstraintsEqual(want, got ProjectConstraints) bool { 483 loop, check := want, got 484 if len(got) > len(want) { 485 loop, check = got, want 486 } 487 for pr, pp := range loop { 488 pp2, ok := check[pr] 489 if !ok { 490 return false 491 } 492 if pp.Source != pp2.Source { 493 return false 494 } 495 if pp.Constraint == nil || pp2.Constraint == nil { 496 if pp.Constraint != nil || pp2.Constraint != nil { 497 return false 498 } 499 } else if !pp.Constraint.identical(pp2.Constraint) { 500 return false 501 } 502 } 503 return true 504 } 505 506 func mapStringBoolEqual(exp, got map[string]bool) bool { 507 loop, check := exp, got 508 if len(got) > len(exp) { 509 loop, check = got, exp 510 } 511 for k, v := range loop { 512 v2, ok := check[k] 513 if !ok || v != v2 { 514 return false 515 } 516 } 517 return true 518 } 519 520 func safeError(err error) string { 521 if err == nil { 522 return "" 523 } 524 return err.Error() 525 } 526 527 // packageOrErrEqual return true if the pkgtree.PackageOrErrs are equal. Error equality is 528 // string based. Imports and TestImports are treated as sets, and will be sorted. 529 func packageOrErrEqual(a, b pkgtree.PackageOrErr) bool { 530 if safeError(a.Err) != safeError(b.Err) { 531 return false 532 } 533 if a.P.Name != b.P.Name { 534 return false 535 } 536 if a.P.ImportPath != b.P.ImportPath { 537 return false 538 } 539 if a.P.CommentPath != b.P.CommentPath { 540 return false 541 } 542 543 if len(a.P.Imports) != len(b.P.Imports) { 544 return false 545 } 546 sort.Strings(a.P.Imports) 547 sort.Strings(b.P.Imports) 548 for i := range a.P.Imports { 549 if a.P.Imports[i] != b.P.Imports[i] { 550 return false 551 } 552 } 553 554 if len(a.P.TestImports) != len(b.P.TestImports) { 555 return false 556 } 557 sort.Strings(a.P.TestImports) 558 sort.Strings(b.P.TestImports) 559 for i := range a.P.TestImports { 560 if a.P.TestImports[i] != b.P.TestImports[i] { 561 return false 562 } 563 } 564 565 return true 566 } 567 568 // discardCache produces singleSourceDiscardCaches. 569 type discardCache struct{} 570 571 func (discardCache) newSingleSourceCache(ProjectIdentifier) singleSourceCache { 572 return discard 573 } 574 575 func (discardCache) close() error { return nil } 576 577 var discard singleSourceCache = singleSourceDiscardCache{} 578 579 // singleSourceDiscardCache discards set values and returns nothing. 580 type singleSourceDiscardCache struct{} 581 582 func (singleSourceDiscardCache) setManifestAndLock(Revision, ProjectAnalyzerInfo, Manifest, Lock) {} 583 584 func (singleSourceDiscardCache) getManifestAndLock(Revision, ProjectAnalyzerInfo) (Manifest, Lock, bool) { 585 return nil, nil, false 586 } 587 588 func (singleSourceDiscardCache) setPackageTree(Revision, pkgtree.PackageTree) {} 589 590 func (singleSourceDiscardCache) getPackageTree(Revision, ProjectRoot) (pkgtree.PackageTree, bool) { 591 return pkgtree.PackageTree{}, false 592 } 593 594 func (singleSourceDiscardCache) markRevisionExists(r Revision) {} 595 596 func (singleSourceDiscardCache) setVersionMap(versionList []PairedVersion) {} 597 598 func (singleSourceDiscardCache) getVersionsFor(Revision) ([]UnpairedVersion, bool) { 599 return nil, false 600 } 601 602 func (singleSourceDiscardCache) getAllVersions() ([]PairedVersion, bool) { 603 return nil, false 604 } 605 606 func (singleSourceDiscardCache) getRevisionFor(UnpairedVersion) (Revision, bool) { 607 return "", false 608 } 609 610 func (singleSourceDiscardCache) toRevision(v Version) (Revision, bool) { 611 return "", false 612 } 613 614 func (singleSourceDiscardCache) toUnpaired(v Version) (UnpairedVersion, bool) { 615 return nil, false 616 }