github.com/atlassian/git-lob@v0.0.0-20150806085256-2386a5ed291a/core/storage_test.go (about) 1 package core 2 3 import ( 4 "crypto/sha1" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "path" 9 "path/filepath" 10 11 . "github.com/atlassian/git-lob/Godeps/_workspace/src/github.com/onsi/ginkgo" 12 . "github.com/atlassian/git-lob/Godeps/_workspace/src/github.com/onsi/gomega" 13 . "github.com/atlassian/git-lob/util" 14 ) 15 16 var _ = Describe("Storage", func() { 17 18 root := filepath.Join(os.TempDir(), "StorageTest") 19 separateGitDir := filepath.Join(os.TempDir(), "StorageTestGitDir") 20 sharedStore := filepath.Join(os.TempDir(), "StorageTest_SharedStore") 21 folders := []string{ 22 filepath.Join(root, "folder1"), 23 filepath.Join(root, "folder2"), 24 filepath.Join(root, "folder3"), 25 filepath.Join(root, "folder1", "sub1"), 26 filepath.Join(root, "folder1", "sub2"), 27 filepath.Join(root, "folder1", "sub1", "subsub1"), 28 filepath.Join(root, "folder1", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l")} 29 30 Describe("Identifying git repo root", func() { 31 Context("Valid git repo", func() { 32 var oldwd string 33 BeforeEach(func() { 34 oldwd, _ = os.Getwd() 35 // Set up git repo with some subfolders 36 CreateGitRepoForTest(root) 37 38 for _, f := range folders { 39 err := os.MkdirAll(f, 0755) 40 if err != nil { 41 fmt.Printf("Can't MkdirAll %v: %v", f, err) 42 } 43 } 44 45 }) 46 47 AfterEach(func() { 48 // Delete repo 49 err := ForceRemoveAll(root) 50 if err != nil { 51 Fail(err.Error()) 52 } 53 os.Chdir(oldwd) 54 }) 55 56 It("finds root git folder", func() { 57 58 // Need to expand root for symlinks here in order to guarantee string comparison works 59 // /var turns into /private/var on OS X for example 60 // Can't use this for creating repos etc though, OS X doesn't like direct access 61 expandedroot, _ := filepath.EvalSymlinks(root) 62 for _, f := range folders { 63 err := os.Chdir(f) 64 if err != nil { 65 Fail(fmt.Sprintf("Can't chdir to %v: %v", f, err)) 66 } 67 testroot, sep, err := GetRepoRoot() 68 Expect(err).To(BeNil(), "Should be no error getting repo root") 69 Expect(testroot).To(Equal(expandedroot)) 70 Expect(sep).To(Equal(false)) 71 } 72 }) 73 }) 74 Context("Invalid git repo", func() { 75 var oldwd string 76 BeforeEach(func() { 77 oldwd, _ = os.Getwd() 78 }) 79 AfterEach(func() { 80 os.Chdir(oldwd) 81 }) 82 It("Fails safely outside a git repo", func() { 83 // Relies on temp dir not being a git repo, which should be valid assumption 84 os.Chdir(os.TempDir()) 85 testroot, _, err := GetRepoRoot() 86 Expect(testroot).To(Equal("")) 87 Expect(err).ToNot(BeNil(), "Should be error outside git repo") 88 }) 89 90 }) 91 92 }) 93 94 Describe("Finding git dir", func() { 95 Context("Git repo with standard git dir", func() { 96 var oldwd string 97 BeforeEach(func() { 98 oldwd, _ = os.Getwd() 99 // Set up git repo with some subfolders 100 CreateGitRepoForTest(root) 101 102 for _, f := range folders { 103 err := os.MkdirAll(f, 0755) 104 if err != nil { 105 fmt.Printf("Can't MkdirAll %v: %v", f, err) 106 } 107 } 108 109 }) 110 111 AfterEach(func() { 112 os.Chdir(oldwd) 113 // Delete repo 114 err := ForceRemoveAll(root) 115 if err != nil { 116 Fail(err.Error()) 117 } 118 }) 119 120 It("finds git dir", func() { 121 122 // Need to expand root for symlinks here in order to guarantee string comparison works 123 // /var turns into /private/var on OS X for example 124 // Can't use this for creating repos etc though, OS X doesn't like direct access 125 gitdir, _ := filepath.EvalSymlinks(path.Join(root, ".git")) 126 127 for _, f := range folders { 128 err := os.Chdir(f) 129 if err != nil { 130 Fail(fmt.Sprintf("Can't chdir to %v: %v", f, err)) 131 } 132 testgitdir := GetGitDir() 133 Expect(testgitdir).To(Equal(gitdir)) 134 } 135 }) 136 137 }) 138 139 Context("Git repo with separate git dir", func() { 140 var oldwd string 141 BeforeEach(func() { 142 oldwd, _ = os.Getwd() 143 // Set up git repo with some subfolders 144 CreateGitRepoWithSeparateGitDirForTest(root, separateGitDir) 145 146 for _, f := range folders { 147 err := os.MkdirAll(f, 0755) 148 if err != nil { 149 fmt.Printf("Can't MkdirAll %v: %v", f, err) 150 } 151 } 152 153 }) 154 155 AfterEach(func() { 156 os.Chdir(oldwd) 157 // Delete repo 158 err := ForceRemoveAll(root) 159 if err != nil { 160 Fail(err.Error()) 161 } 162 }) 163 164 It("finds git dir", func() { 165 166 // Need to expand root for symlinks here in order to guarantee string comparison works 167 // /var turns into /private/var on OS X for example 168 // Can't use this for creating repos etc though, OS X doesn't like direct access 169 gitdir, _ := filepath.EvalSymlinks(separateGitDir) 170 171 for _, f := range folders { 172 err := os.Chdir(f) 173 if err != nil { 174 Fail(fmt.Sprintf("Can't chdir to %v: %v", f, err)) 175 } 176 testgitdir := GetGitDir() 177 Expect(testgitdir).To(Equal(gitdir)) 178 } 179 }) 180 181 }) 182 }) 183 184 Describe("Storing a LOB", func() { 185 // Common git repo 186 var oldwd string 187 BeforeEach(func() { 188 oldwd, _ = os.Getwd() 189 // Set up git repo with some subfolders 190 CreateGitRepoForTest(root) 191 192 for _, f := range folders { 193 err := os.MkdirAll(f, 0755) 194 if err != nil { 195 fmt.Printf("Can't MkdirAll %v: %v", f, err) 196 } 197 } 198 os.Chdir(root) 199 }) 200 201 AfterEach(func() { 202 os.Chdir(oldwd) 203 // Delete repo 204 err := ForceRemoveAll(root) 205 if err != nil { 206 Fail(err.Error()) 207 } 208 }) 209 210 Context("Store small single chunk LOB", func() { 211 testFileName := path.Join(folders[2], "small.dat") 212 var correctLOBInfo *LOBInfo 213 214 BeforeEach(func() { 215 correctLOBInfo = CreateSmallTestLOBFileForStoring(testFileName) 216 }) 217 AfterEach(func() { 218 os.Remove(testFileName) 219 }) 220 221 It("correctly stores a small file", func() { 222 f, err := os.Open(testFileName) 223 if err != nil { 224 Fail(fmt.Sprintf("Can't reopen test file %v: %v", testFileName, err)) 225 } 226 defer f.Close() 227 // Need to read leader for consistency with real usage 228 leader := make([]byte, SHALineLen) 229 c, err := f.Read(leader) 230 if err != nil { 231 Fail(fmt.Sprintf("Can't read leader of test file %v: %v", testFileName, err)) 232 } 233 lobinfo, err := StoreLOB(f, leader[:c]) 234 Expect(err).To(BeNil(), "Shouldn't be error storing LOB") 235 Expect(lobinfo).To(Equal(correctLOBInfo)) 236 fileinfo, err := os.Stat(GetLocalLOBChunkPath(lobinfo.SHA, 0)) 237 Expect(err).To(BeNil(), "Shouldn't be error opening stored LOB") 238 Expect(fileinfo.Size()).To(Equal(lobinfo.Size), "Stored LOB should be correct size") 239 }) 240 241 }) 242 Context("Store zero size file", func() { 243 zerofile := path.Join(folders[1], "zero.dat") 244 BeforeEach(func() { 245 CreateRandomFileForTest(0, zerofile) 246 }) 247 AfterEach(func() { 248 os.Remove(zerofile) 249 }) 250 251 It("correctly stores zero size files", func() { 252 f, err := os.Open(zerofile) 253 if err != nil { 254 Fail(fmt.Sprintf("Can't reopen test file %v: %v", zerofile, err)) 255 } 256 defer f.Close() 257 // Need TRY to read leader for consistency with real usage 258 leader := make([]byte, SHALineLen) 259 c, err := f.Read(leader) 260 lobinfo, err := StoreLOB(f, leader[:c]) 261 Expect(err).To(BeNil(), "Shouldn't be error storing LOB") 262 Expect(lobinfo.Size).To(BeEquivalentTo(0), "Size should be correct") 263 Expect(lobinfo.NumChunks).To(BeEquivalentTo(0), "Should only be one chunk") 264 shaZero := sha1.New() 265 shaZeroStr := fmt.Sprintf("%x", string(shaZero.Sum(nil))) 266 Expect(lobinfo.SHA).To(Equal(shaZeroStr), "SHA should be the zero file content SHA") 267 268 }) 269 }) 270 271 Context("Store single chunk LOB of exact chunk size", func() { 272 exact1 := path.Join(folders[1], "exact1.dat") 273 exact2 := path.Join(folders[1], "exact2.dat") 274 var oldChunkSize int64 275 276 BeforeEach(func() { 277 // Jig the chunk size for efficient testing 278 oldChunkSize = ChunkSize 279 ChunkSize = 200 280 CreateRandomFileForTest(ChunkSize, exact1) 281 CreateRandomFileForTest(ChunkSize*2, exact2) 282 283 }) 284 AfterEach(func() { 285 os.Remove(exact1) 286 os.Remove(exact2) 287 ChunkSize = oldChunkSize 288 }) 289 290 It("correctly stores files which are exact multiples of chunk size", func() { 291 f, err := os.Open(exact1) 292 if err != nil { 293 Fail(fmt.Sprintf("Can't reopen test file %v: %v", exact1, err)) 294 } 295 defer f.Close() 296 // Need to read leader for consistency with real usage 297 leader := make([]byte, SHALineLen) 298 c, err := f.Read(leader) 299 if err != nil { 300 Fail(fmt.Sprintf("Can't read leader of test file %v: %v", exact1, err)) 301 } 302 lobinfo, err := StoreLOB(f, leader[:c]) 303 Expect(err).To(BeNil(), "Shouldn't be error storing LOB") 304 Expect(lobinfo.Size).To(BeEquivalentTo(ChunkSize), "Size should be correct") 305 Expect(lobinfo.NumChunks).To(BeEquivalentTo(1), "Should only be one chunk") 306 fileinfo, err := os.Stat(GetLocalLOBChunkPath(lobinfo.SHA, 0)) 307 Expect(err).To(BeNil(), "Shouldn't be error opening stored LOB") 308 Expect(fileinfo.Size()).To(Equal(lobinfo.Size), "Stored LOB should be correct size") 309 310 f2, err := os.Open(exact2) 311 if err != nil { 312 Fail(fmt.Sprintf("Can't reopen test file %v: %v", exact2, err)) 313 } 314 defer f2.Close() 315 // Need to read leader for consistency with real usage 316 leader = make([]byte, SHALineLen) 317 c, err = f2.Read(leader) 318 if err != nil { 319 Fail(fmt.Sprintf("Can't read leader of test file %v: %v", exact2, err)) 320 } 321 lobinfo, err = StoreLOB(f2, leader[:c]) 322 Expect(err).To(BeNil(), "Shouldn't be error storing LOB") 323 Expect(lobinfo.Size).To(BeEquivalentTo(ChunkSize*2), "Size should be correct") 324 Expect(lobinfo.NumChunks).To(BeEquivalentTo(2), "Should be 2 chunks") 325 fileinfo, err = os.Stat(GetLocalLOBChunkPath(lobinfo.SHA, 0)) 326 Expect(err).To(BeNil(), "Shouldn't be error opening stored LOB") 327 Expect(fileinfo.Size()).To(Equal(ChunkSize), "Stored LOB should be correct size") 328 fileinfo, err = os.Stat(GetLocalLOBChunkPath(lobinfo.SHA, 1)) 329 Expect(err).To(BeNil(), "Shouldn't be error opening stored LOB") 330 Expect(fileinfo.Size()).To(Equal(ChunkSize), "Stored LOB should be correct size") 331 332 }) 333 334 }) 335 336 Context("Store large multiple chunk LOB [LONGTEST]", func() { 337 338 testFileName := path.Join(folders[2], "large.dat") 339 var correctLOBInfo *LOBInfo 340 341 BeforeEach(func() { 342 correctLOBInfo = CreateLargeTestLOBFileForStoring(testFileName) 343 }) 344 AfterEach(func() { 345 os.Remove(testFileName) 346 }) 347 348 It("correctly stores a large file", func() { 349 f, err := os.Open(testFileName) 350 if err != nil { 351 Fail(fmt.Sprintf("Can't reopen test file %v: %v", testFileName, err)) 352 } 353 defer f.Close() 354 // Need to read leader for consistency with real usage 355 leader := make([]byte, SHALineLen) 356 c, err := f.Read(leader) 357 if err != nil { 358 Fail(fmt.Sprintf("Can't read leader of test file %v: %v", testFileName, err)) 359 } 360 lobinfo, err := StoreLOB(f, leader[:c]) 361 Expect(err).To(BeNil(), "Shouldn't be error storing LOB") 362 Expect(lobinfo).To(Equal(correctLOBInfo)) 363 for i := 0; i < lobinfo.NumChunks; i++ { 364 fileinfo, err := os.Stat(GetLocalLOBChunkPath(lobinfo.SHA, i)) 365 Expect(err).To(BeNil(), "Shouldn't be error opening stored LOB #%v", i) 366 if i+1 < lobinfo.NumChunks { 367 Expect(fileinfo.Size()).To(BeEquivalentTo(ChunkSize), "Stored LOB #%v should be chunk limit size", i) 368 } else { 369 Expect(fileinfo.Size()).To(BeEquivalentTo(lobinfo.Size%ChunkSize), "Stored LOB #%v should be correct size", i) 370 } 371 372 } 373 }) 374 375 }) 376 377 }) 378 379 Describe("Retrieving a LOB", func() { 380 // Common git repo 381 var oldwd string 382 BeforeEach(func() { 383 oldwd, _ = os.Getwd() 384 // Set up git repo with some subfolders 385 CreateGitRepoForTest(root) 386 os.Chdir(root) 387 388 for _, f := range folders { 389 err := os.MkdirAll(f, 0755) 390 if err != nil { 391 fmt.Printf("Can't MkdirAll %v: %v", f, err) 392 } 393 } 394 395 }) 396 397 AfterEach(func() { 398 os.Chdir(oldwd) 399 // Delete repo 400 err := ForceRemoveAll(root) 401 if err != nil { 402 Fail(err.Error()) 403 } 404 }) 405 406 Context("Retrieve small single chunk LOB", func() { 407 var correctLOBInfo *LOBInfo 408 409 BeforeEach(func() { 410 correctLOBInfo = CreateSmallTestLOBDataForRetrieval() 411 }) 412 413 It("correctly retrieves small LOB file", func() { 414 // output to a temp file 415 out, err := ioutil.TempFile("", "lobsmall.dat") 416 Expect(err).To(BeNil(), "Shouldn't be error creating temp file") 417 outFilename := out.Name() 418 info, err := RetrieveLOB(correctLOBInfo.SHA, out) 419 420 Expect(err).To(BeNil(), "Shouldn't be error retrieving LOB") 421 out.Close() 422 423 Expect(info).To(Equal(correctLOBInfo), "Metadata should agree") 424 // Check output file 425 stat, err := os.Stat(outFilename) 426 Expect(err).To(BeNil(), "Shouldn't be error checking output file") 427 Expect(stat.Size()).To(Equal(info.Size), "Size on disk should agree with metadata") 428 429 os.Remove(outFilename) 430 431 }) 432 433 }) 434 Context("Retrieve large multiple chunk LOB [LONGTEST]", func() { 435 var correctLOBInfo *LOBInfo 436 437 BeforeEach(func() { 438 correctLOBInfo = CreateLargeTestLOBDataForRetrieval() 439 }) 440 441 It("correctly retrieves large LOB file", func() { 442 // output to a temp file 443 out, err := ioutil.TempFile("", "loblarge.dat") 444 Expect(err).To(BeNil(), "Shouldn't be error creating temp file") 445 outFilename := out.Name() 446 info, err := RetrieveLOB(correctLOBInfo.SHA, out) 447 448 Expect(err).To(BeNil(), "Shouldn't be error retrieving LOB") 449 out.Close() 450 451 Expect(info).To(Equal(correctLOBInfo), "Metadata should agree") 452 // Check output file 453 stat, err := os.Stat(outFilename) 454 Expect(err).To(BeNil(), "Shouldn't be error checking output file") 455 Expect(stat.Size()).To(Equal(info.Size), "Size on disk should agree with metadata") 456 457 os.Remove(outFilename) 458 459 }) 460 461 }) 462 463 Context("Retrieve a zero size file", func() { 464 It("correctly retrieves zero size LOB file", func() { 465 // Create the zero size storage (separate test for storing) 466 infile := path.Join(folders[1], "zeroin.dat") 467 CreateRandomFileForTest(0, infile) 468 _, err := StoreLOBForTest(infile) 469 os.Remove(infile) 470 if err != nil { 471 Fail(fmt.Sprintf("Error storing zero size file %v", infile)) 472 } 473 474 // Zero size file SHA 475 shaZero := sha1.New() 476 shaZeroStr := fmt.Sprintf("%x", string(shaZero.Sum(nil))) 477 478 // output to a temp file 479 out, err := ioutil.TempFile("", "lobzerotest.dat") 480 Expect(err).To(BeNil(), "Shouldn't be error creating temp file") 481 outFilename := out.Name() 482 info, err := RetrieveLOB(shaZeroStr, out) 483 484 Expect(err).To(BeNil(), "Shouldn't be error retrieving LOB") 485 out.Close() 486 487 Expect(info.SHA).To(Equal(shaZeroStr), "SHA should agree") 488 Expect(info.Size).To(BeEquivalentTo(0), "Should be zero size") 489 Expect(info.NumChunks).To(BeEquivalentTo(0), "Should be no chunks should agree") 490 // Check output file 491 stat, err := os.Stat(outFilename) 492 Expect(err).To(BeNil(), "Shouldn't be error checking output file") 493 Expect(stat.Size()).To(BeEquivalentTo(0), "Size on disk should be zero") 494 495 os.Remove(outFilename) 496 497 }) 498 499 }) 500 501 }) 502 503 // --- Shared tests 504 Describe("Storing a LOB (shared store)", func() { 505 // Common git repo 506 var oldwd string 507 BeforeEach(func() { 508 oldwd, _ = os.Getwd() 509 os.MkdirAll(sharedStore, 0755) 510 GlobalOptions.SharedStore = sharedStore 511 // Set up git repo with some subfolders 512 CreateGitRepoForTest(root) 513 514 for _, f := range folders { 515 err := os.MkdirAll(f, 0755) 516 if err != nil { 517 fmt.Printf("Can't MkdirAll %v: %v", f, err) 518 } 519 } 520 os.Chdir(root) 521 }) 522 523 AfterEach(func() { 524 os.Chdir(oldwd) 525 // Delete repo 526 err := ForceRemoveAll(root) 527 if err != nil { 528 Fail(err.Error()) 529 } 530 err = ForceRemoveAll(sharedStore) 531 if err != nil { 532 Fail(err.Error()) 533 } 534 GlobalOptions.SharedStore = "" 535 }) 536 537 Context("Store small single chunk LOB (shared store)", func() { 538 testFileName := path.Join(folders[2], "small.dat") 539 var correctLOBInfo *LOBInfo 540 541 BeforeEach(func() { 542 correctLOBInfo = CreateSmallTestLOBFileForStoring(testFileName) 543 }) 544 AfterEach(func() { 545 os.Remove(testFileName) 546 }) 547 548 It("correctly stores a small file (shared store)", func() { 549 f, err := os.Open(testFileName) 550 if err != nil { 551 Fail(fmt.Sprintf("Can't reopen test file %v: %v", testFileName, err)) 552 } 553 defer f.Close() 554 // Need to read leader for consistency with real usage 555 leader := make([]byte, SHALineLen) 556 c, err := f.Read(leader) 557 if err != nil { 558 Fail(fmt.Sprintf("Can't read leader of test file %v: %v", testFileName, err)) 559 } 560 lobinfo, err := StoreLOB(f, leader[:c]) 561 Expect(err).To(BeNil(), "Shouldn't be error storing LOB") 562 Expect(lobinfo).To(Equal(correctLOBInfo)) 563 564 lobinfo, err = GetLOBInfo(correctLOBInfo.SHA) 565 Expect(err).To(BeNil(), "Shouldn't be error retrieving LOB info") 566 Expect(lobinfo).To(Equal(correctLOBInfo)) 567 568 fileinfo, err := os.Stat(GetLocalLOBChunkPath(lobinfo.SHA, 0)) 569 Expect(err).To(BeNil(), "Shouldn't be error opening stored LOB (local)") 570 Expect(fileinfo.Size()).To(Equal(lobinfo.Size), "Stored LOB should be correct size (local)") 571 // Also test shared 572 fileinfo, err = os.Stat(GetSharedLOBChunkPath(lobinfo.SHA, 0)) 573 Expect(err).To(BeNil(), "Shouldn't be error opening stored LOB (shared)") 574 Expect(fileinfo.Size()).To(Equal(lobinfo.Size), "Stored LOB should be correct size (shared)") 575 576 links, err := GetHardLinkCount(GetLocalLOBChunkPath(lobinfo.SHA, 0)) 577 Expect(err).To(BeNil(), "Shouldn't be error getting local LOB hard link info") 578 Expect(links).To(Equal(2), "Should be the right number of hard links (shared)") 579 links, err = GetHardLinkCount(GetSharedLOBChunkPath(lobinfo.SHA, 0)) 580 Expect(err).To(BeNil(), "Shouldn't be error getting shared LOB hard link info") 581 Expect(links).To(Equal(2), "Should be the right number of hard links (local)") 582 }) 583 584 }) 585 586 Context("Store large multiple chunk LOB (shared store) [LONGTEST]", func() { 587 588 testFileName := path.Join(folders[2], "large.dat") 589 var correctLOBInfo *LOBInfo 590 591 BeforeEach(func() { 592 correctLOBInfo = CreateLargeTestLOBFileForStoring(testFileName) 593 }) 594 AfterEach(func() { 595 os.Remove(testFileName) 596 }) 597 598 It("correctly stores a large file (shared store)", func() { 599 f, err := os.Open(testFileName) 600 if err != nil { 601 Fail(fmt.Sprintf("Can't reopen test file %v: %v", testFileName, err)) 602 } 603 defer f.Close() 604 // Need to read leader for consistency with real usage 605 leader := make([]byte, SHALineLen) 606 c, err := f.Read(leader) 607 if err != nil { 608 Fail(fmt.Sprintf("Can't read leader of test file %v: %v", testFileName, err)) 609 } 610 lobinfo, err := StoreLOB(f, leader[:c]) 611 Expect(err).To(BeNil(), "Shouldn't be error storing LOB") 612 Expect(lobinfo).To(Equal(correctLOBInfo)) 613 lobinfo, err = GetLOBInfo(correctLOBInfo.SHA) 614 Expect(err).To(BeNil(), "Shouldn't be error retrieving LOB info") 615 Expect(lobinfo).To(Equal(correctLOBInfo)) 616 617 for i := 0; i < lobinfo.NumChunks; i++ { 618 fileinfo, err := os.Stat(GetLocalLOBChunkPath(lobinfo.SHA, i)) 619 Expect(err).To(BeNil(), "Shouldn't be error opening stored LOB #%v", i) 620 if i+1 < lobinfo.NumChunks { 621 Expect(fileinfo.Size()).To(BeEquivalentTo(ChunkSize), "Stored LOB #%v should be chunk limit size", i) 622 } else { 623 Expect(fileinfo.Size()).To(BeEquivalentTo(lobinfo.Size%ChunkSize), "Stored LOB #%v should be correct size", i) 624 } 625 // Also check shared 626 fileinfo, err = os.Stat(GetSharedLOBChunkPath(lobinfo.SHA, i)) 627 Expect(err).To(BeNil(), "Shouldn't be error opening stored LOB #%v", i) 628 if i+1 < lobinfo.NumChunks { 629 Expect(fileinfo.Size()).To(BeEquivalentTo(ChunkSize), "Stored LOB #%v should be chunk limit size", i) 630 } else { 631 Expect(fileinfo.Size()).To(BeEquivalentTo(lobinfo.Size%ChunkSize), "Stored LOB #%v should be correct size", i) 632 } 633 links, err := GetHardLinkCount(GetLocalLOBChunkPath(lobinfo.SHA, i)) 634 Expect(err).To(BeNil(), "Shouldn't be error getting local LOB hard link info") 635 Expect(links).To(Equal(2), "Should be the right number of hard links (shared)") 636 links, err = GetHardLinkCount(GetSharedLOBChunkPath(lobinfo.SHA, i)) 637 Expect(err).To(BeNil(), "Shouldn't be error getting shared LOB hard link info") 638 Expect(links).To(Equal(2), "Should be the right number of hard links (local)") 639 640 } 641 }) 642 643 }) 644 645 }) 646 647 Describe("Retrieving a LOB (shared store)", func() { 648 // Common git repo 649 var oldwd string 650 BeforeEach(func() { 651 os.MkdirAll(sharedStore, 0755) 652 oldwd, _ = os.Getwd() 653 GlobalOptions.SharedStore = sharedStore 654 // Set up git repo with some subfolders 655 CreateGitRepoForTest(root) 656 657 for _, f := range folders { 658 err := os.MkdirAll(f, 0755) 659 if err != nil { 660 fmt.Printf("Can't MkdirAll %v: %v", f, err) 661 } 662 } 663 os.Chdir(root) 664 665 }) 666 667 AfterEach(func() { 668 os.Chdir(oldwd) 669 // Delete repo 670 err := ForceRemoveAll(root) 671 if err != nil { 672 Fail(err.Error()) 673 } 674 err = ForceRemoveAll(sharedStore) 675 if err != nil { 676 Fail(err.Error()) 677 } 678 GlobalOptions.SharedStore = "" 679 }) 680 681 Context("Retrieve small single chunk LOB (shared store)", func() { 682 var correctLOBInfo *LOBInfo 683 684 BeforeEach(func() { 685 correctLOBInfo = CreateSmallTestLOBDataForRetrieval() 686 }) 687 688 It("correctly retrieves small LOB file (shared store)", func() { 689 // output to a temp file 690 out, err := ioutil.TempFile("", "lobsmall.dat") 691 Expect(err).To(BeNil(), "Shouldn't be error creating temp file") 692 outFilename := out.Name() 693 info, err := RetrieveLOB(correctLOBInfo.SHA, out) 694 695 Expect(err).To(BeNil(), "Shouldn't be error retrieving LOB") 696 out.Close() 697 698 Expect(info).To(Equal(correctLOBInfo), "Metadata should agree") 699 // Check output file 700 stat, err := os.Stat(outFilename) 701 Expect(err).To(BeNil(), "Shouldn't be error checking output file") 702 Expect(stat.Size()).To(Equal(info.Size), "Size on disk should agree with metadata") 703 704 os.Remove(outFilename) 705 706 }) 707 708 }) 709 Context("Retrieve large multiple chunk LOB (shared store) [LONGTEST]", func() { 710 var correctLOBInfo *LOBInfo 711 712 BeforeEach(func() { 713 correctLOBInfo = CreateLargeTestLOBDataForRetrieval() 714 }) 715 716 It("correctly retrieves large LOB file (shared store)", func() { 717 // output to a temp file 718 out, err := ioutil.TempFile("", "loblarge.dat") 719 Expect(err).To(BeNil(), "Shouldn't be error creating temp file") 720 outFilename := out.Name() 721 info, err := RetrieveLOB(correctLOBInfo.SHA, out) 722 723 Expect(err).To(BeNil(), "Shouldn't be error retrieving LOB") 724 out.Close() 725 726 Expect(info).To(Equal(correctLOBInfo), "Metadata should agree") 727 // Check output file 728 stat, err := os.Stat(outFilename) 729 Expect(err).To(BeNil(), "Shouldn't be error checking output file") 730 Expect(stat.Size()).To(Equal(info.Size), "Size on disk should agree with metadata") 731 732 os.Remove(outFilename) 733 734 }) 735 736 }) 737 738 }) 739 Describe("Getting & checking LOB files", func() { 740 var lobinfos []*LOBInfo 741 var origDir string 742 var smallFileIdx []int 743 var midFileIdx []int 744 var largeFileIdx []int 745 var savedChunkSize int64 746 BeforeEach(func() { 747 CreateGitRepoForTest(root) 748 origDir, _ = os.Getwd() 749 os.Chdir(root) 750 751 files := []string{ 752 "smallfile1.bin", 753 "smallfile2.bin", 754 "smallfile3.bin", 755 "midfile1.bin", 756 "midfile2.bin", 757 "midfile3.bin", 758 "largefile1.bin", 759 "largefile2.bin"} 760 761 // Reduce global chunk size for test 762 // we need to test many chunks but let's not take lots of time 763 savedChunkSize = ChunkSize 764 ChunkSize = 16384 765 766 sizes := []int64{50, 150, 200, 767 ChunkSize + 100, 768 ChunkSize + 1200, 769 ChunkSize + 3400, 770 ChunkSize*3 - 200, 771 ChunkSize*3 - 1000} 772 773 smallFileIdx = []int{0, 1, 2} 774 midFileIdx = []int{3, 4, 5} 775 largeFileIdx = []int{6, 7} 776 777 // Create a bunch of files 778 lobinfos = make([]*LOBInfo, 0, len(files)) 779 for i, f := range files { 780 sz := sizes[i] 781 filename := path.Join(root, f) 782 CreateRandomFileForTest(sz, filename) 783 info, err := StoreLOBForTest(filename) 784 if err != nil { 785 Fail(err.Error()) 786 } 787 lobinfos = append(lobinfos, info) 788 } 789 790 }) 791 AfterEach(func() { 792 os.Chdir(origDir) 793 // Delete repo 794 err := ForceRemoveAll(root) 795 if err != nil { 796 Fail(err.Error()) 797 } 798 799 ChunkSize = savedChunkSize 800 }) 801 802 It("Shallow checks LOB files", func() { 803 // Initial test, everything should validate (just use check) 804 basedir := GetLocalLOBRoot() 805 for _, li := range lobinfos { 806 files, sz, err := GetLOBFilesForSHA(li.SHA, basedir, true, false) 807 Expect(err).To(BeNil(), "Should be no error when checking LOB file for %v", li.SHA) 808 Expect(files).To(HaveLen(li.NumChunks+1), "Should have the right number of files") 809 Expect(sz).To(BeEquivalentTo(li.Size), "Total size should be correct") 810 } 811 812 // Test for simple corruptions 813 // Remove a meta file 814 var err error 815 metafile := GetLocalLOBMetaPath(lobinfos[smallFileIdx[0]].SHA) 816 os.Remove(metafile) 817 err = CheckLOBFilesForSHA(lobinfos[smallFileIdx[0]].SHA, basedir, false) 818 Expect(err).ToNot(BeNil(), "Should detect missing meta file") 819 820 var chunkfile string 821 // Remove a chunk file (only one) 822 chunkfile = GetLocalLOBChunkPath(lobinfos[smallFileIdx[1]].SHA, 0) 823 os.Remove(chunkfile) 824 err = CheckLOBFilesForSHA(lobinfos[smallFileIdx[1]].SHA, basedir, false) 825 Expect(err).ToNot(BeNil(), "Should detect missing chunk file for single-chunk file") 826 // Remove a chunk file (one of many - first) 827 chunkfile = GetLocalLOBChunkPath(lobinfos[midFileIdx[0]].SHA, 0) 828 os.Remove(chunkfile) 829 err = CheckLOBFilesForSHA(lobinfos[midFileIdx[0]].SHA, basedir, false) 830 Expect(err).ToNot(BeNil(), "Should detect missing first chunk file for 2-chunk file") 831 // Remove a chunk file (one of many - last) 832 chunkfile = GetLocalLOBChunkPath(lobinfos[midFileIdx[1]].SHA, 1) 833 os.Remove(chunkfile) 834 err = CheckLOBFilesForSHA(lobinfos[midFileIdx[1]].SHA, basedir, false) 835 Expect(err).ToNot(BeNil(), "Should detect missing second chunk file for 2-chunk file") 836 837 // Change the size of a chunk file (single chunk) 838 chunkfile = GetLocalLOBChunkPath(lobinfos[smallFileIdx[2]].SHA, 0) 839 f, _ := os.OpenFile(chunkfile, os.O_APPEND|os.O_RDWR, 0644) 840 f.Write([]byte("icorruptthee")) 841 f.Close() 842 err = CheckLOBFilesForSHA(lobinfos[smallFileIdx[2]].SHA, basedir, false) 843 Expect(err).ToNot(BeNil(), "Should detect incorrect size chunk file for single-chunk file") 844 // Change the size of a chunk file (one of many - first) 845 chunkfile = GetLocalLOBChunkPath(lobinfos[midFileIdx[2]].SHA, 0) 846 f, _ = os.OpenFile(chunkfile, os.O_APPEND|os.O_RDWR, 0644) 847 f.Write([]byte("hssss")) 848 f.Close() 849 err = CheckLOBFilesForSHA(lobinfos[midFileIdx[2]].SHA, basedir, false) 850 Expect(err).ToNot(BeNil(), "Should detect incorrect size chunk file for multi-chunk file (first)") 851 // Change the size of a chunk file (one of many - middle) 852 chunkfile = GetLocalLOBChunkPath(lobinfos[largeFileIdx[0]].SHA, 1) 853 f, _ = os.OpenFile(chunkfile, os.O_APPEND|os.O_RDWR, 0644) 854 f.Write([]byte("itburns")) 855 f.Close() 856 err = CheckLOBFilesForSHA(lobinfos[largeFileIdx[0]].SHA, basedir, false) 857 Expect(err).ToNot(BeNil(), "Should detect incorrect size chunk file for multi-chunk file (middle)") 858 // Change the size of a chunk file (one of many - last) 859 chunkfile = GetLocalLOBChunkPath(lobinfos[largeFileIdx[1]].SHA, lobinfos[largeFileIdx[1]].NumChunks-1) 860 f, _ = os.OpenFile(chunkfile, os.O_APPEND|os.O_RDWR, 0644) 861 f.Write([]byte("ngggg")) 862 f.Close() 863 err = CheckLOBFilesForSHA(lobinfos[largeFileIdx[1]].SHA, basedir, false) 864 Expect(err).ToNot(BeNil(), "Should detect incorrect size chunk file for multi-chunk file (last)") 865 866 }) 867 868 It("Deep checks LOB files", func() { 869 // Initial test, everything should validate (just use check) 870 basedir := GetLocalLOBRoot() 871 for _, li := range lobinfos { 872 files, sz, err := GetLOBFilesForSHA(li.SHA, basedir, true, true) 873 Expect(err).To(BeNil(), "Should be no error when checking LOB file for %v", li.SHA) 874 Expect(files).To(HaveLen(li.NumChunks+1), "Should have the right number of files") 875 Expect(sz).To(BeEquivalentTo(li.Size), "Total size should be correct") 876 } 877 878 // Test for deep corruptions 879 var chunkfile string 880 var err error 881 // Change 2 bytes of a chunk file, size unchanged (single chunk) 882 chunkfile = GetLocalLOBChunkPath(lobinfos[smallFileIdx[0]].SHA, 0) 883 f, _ := os.OpenFile(chunkfile, os.O_RDWR|os.O_SYNC, 0644) 884 f.Seek(10, os.SEEK_SET) 885 f.Write([]byte("qq")) 886 f.Close() 887 // check that we wouldn't detect this without checking the SHA 888 err = CheckLOBFilesForSHA(lobinfos[smallFileIdx[0]].SHA, basedir, false) 889 Expect(err).To(BeNil(), "Should not detect the corruption without deep hash check") 890 err = CheckLOBFilesForSHA(lobinfos[smallFileIdx[0]].SHA, basedir, true) 891 Expect(err).ToNot(BeNil(), "Should detect the corruption with deep hash check") 892 // Change 2 bytes of a chunk file, size unchanged (multiple chunk) 893 chunkfile = GetLocalLOBChunkPath(lobinfos[midFileIdx[0]].SHA, 1) 894 f, _ = os.OpenFile(chunkfile, os.O_RDWR|os.O_SYNC, 0644) 895 f.Seek(51, os.SEEK_SET) 896 f.Write([]byte("zf")) 897 f.Close() 898 err = CheckLOBFilesForSHA(lobinfos[midFileIdx[0]].SHA, basedir, false) 899 Expect(err).To(BeNil(), "Should not detect the corruption without deep hash check (second chunk)") 900 err = CheckLOBFilesForSHA(lobinfos[midFileIdx[0]].SHA, basedir, true) 901 Expect(err).ToNot(BeNil(), "Should detect the corruption with deep hash check (second chunk)") 902 903 }) 904 905 }) 906 907 })