github.com/YousefHaggyHeroku/pack@v1.5.5/internal/archive/archive_test.go (about) 1 package archive_test 2 3 import ( 4 "archive/tar" 5 "io/ioutil" 6 "math/rand" 7 "net" 8 "os" 9 "path/filepath" 10 "runtime" 11 "strings" 12 "testing" 13 "time" 14 15 "github.com/pkg/errors" 16 17 "github.com/heroku/color" 18 "github.com/sclevine/spec" 19 "github.com/sclevine/spec/report" 20 21 "github.com/YousefHaggyHeroku/pack/internal/archive" 22 h "github.com/YousefHaggyHeroku/pack/testhelpers" 23 ) 24 25 func TestArchive(t *testing.T) { 26 color.Disable(true) 27 defer color.Disable(false) 28 rand.Seed(time.Now().UTC().UnixNano()) 29 spec.Run(t, "Archive", testArchive, spec.Sequential(), spec.Report(report.Terminal{})) 30 } 31 32 func testArchive(t *testing.T, when spec.G, it spec.S) { 33 var ( 34 tmpDir string 35 ) 36 37 it.Before(func() { 38 var err error 39 tmpDir, err = ioutil.TempDir("", "create-tar-test") 40 if err != nil { 41 t.Fatalf("failed to create tmp dir %s: %s", tmpDir, err) 42 } 43 }) 44 45 it.After(func() { 46 if err := os.RemoveAll(tmpDir); err != nil { 47 t.Fatalf("failed to clean up tmp dir %s: %s", tmpDir, err) 48 } 49 }) 50 51 when("#ReadDirAsTar", func() { 52 var src string 53 it.Before(func() { 54 src = filepath.Join("testdata", "dir-to-tar") 55 }) 56 57 it("returns a TarReader of the dir", func() { 58 rc := archive.ReadDirAsTar(src, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, nil) 59 60 tr := tar.NewReader(rc) 61 verify := h.NewTarVerifier(t, tr, 1234, 2345) 62 verify.NextFile("/nested/dir/dir-in-archive/some-file.txt", "some-content", int64(os.ModePerm)) 63 verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", int64(os.ModePerm)) 64 if runtime.GOOS != "windows" { 65 verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt") 66 verify.NoMoreFilesExist() 67 h.AssertNil(t, rc.Close()) 68 } 69 }) 70 71 it("returns error if closed multiple times", func() { 72 rc := archive.ReadDirAsTar(src, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, func(s string) bool { return false }) 73 tr := tar.NewReader(rc) 74 verify := h.NewTarVerifier(t, tr, 1234, 2345) 75 verify.NoMoreFilesExist() 76 h.AssertNil(t, rc.Close()) 77 h.AssertError(t, rc.Close(), "reader already closed") 78 }) 79 }) 80 81 when("#ReadZipAsTar", func() { 82 var src string 83 it.Before(func() { 84 src = filepath.Join("testdata", "zip-to-tar.zip") 85 }) 86 87 it("returns a TarReader of the dir", func() { 88 rc := archive.ReadZipAsTar(src, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, nil) 89 90 tr := tar.NewReader(rc) 91 verify := h.NewTarVerifier(t, tr, 1234, 2345) 92 verify.NextFile("/nested/dir/dir-in-archive/some-file.txt", "some-content", int64(os.ModePerm)) 93 verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", int64(os.ModePerm)) 94 verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt") 95 96 verify.NoMoreFilesExist() 97 h.AssertNil(t, rc.Close()) 98 }) 99 }) 100 101 when("#ReadTarEntry", func() { 102 var ( 103 err error 104 tarFile *os.File 105 ) 106 it.Before(func() { 107 tarFile, err = ioutil.TempFile(tmpDir, "file.tgz") 108 h.AssertNil(t, err) 109 }) 110 111 it.After(func() { 112 _ = tarFile.Close() 113 }) 114 115 when("tgz has the path", func() { 116 it.Before(func() { 117 err = archive.CreateSingleFileTar(tarFile.Name(), "file1", "file-1 content") 118 h.AssertNil(t, err) 119 }) 120 121 it("returns the file contents", func() { 122 _, contents, err := archive.ReadTarEntry(tarFile, "file1") 123 h.AssertNil(t, err) 124 h.AssertEq(t, string(contents), "file-1 content") 125 }) 126 }) 127 128 when("tgz has ./path", func() { 129 it.Before(func() { 130 err = archive.CreateSingleFileTar(tarFile.Name(), "./file1", "file-1 content") 131 h.AssertNil(t, err) 132 }) 133 134 it("returns the file contents", func() { 135 _, contents, err := archive.ReadTarEntry(tarFile, "file1") 136 h.AssertNil(t, err) 137 h.AssertEq(t, string(contents), "file-1 content") 138 }) 139 }) 140 141 when("path doesn't exist", func() { 142 it.Before(func() { 143 err = archive.CreateSingleFileTar(tarFile.Name(), "file1", "file-1 content") 144 h.AssertNil(t, err) 145 }) 146 147 it("returns the file contents", func() { 148 _, _, err := archive.ReadTarEntry(tarFile, "file2") 149 h.AssertError(t, err, "could not find entry path") 150 h.AssertTrue(t, archive.IsEntryNotExist(err)) 151 }) 152 }) 153 154 when("reader isn't tar", func() { 155 it("returns the file contents", func() { 156 reader := strings.NewReader("abcde") 157 _, _, err := archive.ReadTarEntry(reader, "file1") 158 h.AssertError(t, err, "get next tar entry") 159 }) 160 }) 161 }) 162 163 when("#CreateSingleFileTarReader", func() { 164 it("returns the file contents", func() { 165 rc := archive.CreateSingleFileTarReader("file1", "file-1 content") 166 _, contents, err := archive.ReadTarEntry(rc, "file1") 167 h.AssertNil(t, err) 168 h.AssertEq(t, string(contents), "file-1 content") 169 }) 170 }) 171 172 when("#IsEntryNotExist", func() { 173 it("works", func() { 174 h.AssertTrue(t, archive.IsEntryNotExist(errors.Wrap(archive.ErrEntryNotExist, "something"))) 175 h.AssertFalse(t, archive.IsEntryNotExist(errors.New("something not err not exist"))) 176 }) 177 }) 178 179 when("#WriteDirToTar", func() { 180 var src string 181 it.Before(func() { 182 src = filepath.Join("testdata", "dir-to-tar") 183 }) 184 185 when("mode is set to 0777", func() { 186 it("writes a tar to the dest dir with 0777", func() { 187 fh, err := os.Create(filepath.Join(tmpDir, "some.tar")) 188 h.AssertNil(t, err) 189 190 tw := tar.NewWriter(fh) 191 192 err = archive.WriteDirToTar(tw, src, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, nil) 193 h.AssertNil(t, err) 194 h.AssertNil(t, tw.Close()) 195 h.AssertNil(t, fh.Close()) 196 197 file, err := os.Open(filepath.Join(tmpDir, "some.tar")) 198 h.AssertNil(t, err) 199 defer file.Close() 200 201 tr := tar.NewReader(file) 202 203 verify := h.NewTarVerifier(t, tr, 1234, 2345) 204 verify.NextFile("/nested/dir/dir-in-archive/some-file.txt", "some-content", int64(os.ModePerm)) 205 verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", int64(os.ModePerm)) 206 if runtime.GOOS != "windows" { 207 verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt") 208 } 209 }) 210 }) 211 212 when("mode is set to -1", func() { 213 it("writes a tar to the dest dir with preexisting file mode", func() { 214 fh, err := os.Create(filepath.Join(tmpDir, "some.tar")) 215 h.AssertNil(t, err) 216 217 tw := tar.NewWriter(fh) 218 219 err = archive.WriteDirToTar(tw, src, "/nested/dir/dir-in-archive", 1234, 2345, -1, true, nil) 220 h.AssertNil(t, err) 221 h.AssertNil(t, tw.Close()) 222 h.AssertNil(t, fh.Close()) 223 224 file, err := os.Open(filepath.Join(tmpDir, "some.tar")) 225 h.AssertNil(t, err) 226 defer file.Close() 227 228 tr := tar.NewReader(file) 229 230 verify := h.NewTarVerifier(t, tr, 1234, 2345) 231 verify.NextFile("/nested/dir/dir-in-archive/some-file.txt", "some-content", fileMode(t, filepath.Join(src, "some-file.txt"))) 232 verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", fileMode(t, filepath.Join(src, "sub-dir"))) 233 if runtime.GOOS != "windows" { 234 verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt") 235 } 236 }) 237 }) 238 239 when("has file filter", func() { 240 it("does not add files against the file filter", func() { 241 tarFile := filepath.Join(tmpDir, "some.tar") 242 fh, err := os.Create(tarFile) 243 h.AssertNil(t, err) 244 245 tw := tar.NewWriter(fh) 246 247 err = archive.WriteDirToTar(tw, src, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, func(path string) bool { 248 return !strings.Contains(path, "some-file.txt") 249 }) 250 h.AssertNil(t, err) 251 h.AssertNil(t, tw.Close()) 252 h.AssertNil(t, fh.Close()) 253 254 file, err := os.Open(filepath.Join(tmpDir, "some.tar")) 255 h.AssertNil(t, err) 256 defer file.Close() 257 258 tr := tar.NewReader(file) 259 260 verify := h.NewTarVerifier(t, tr, 1234, 2345) 261 verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", int64(os.ModePerm)) 262 if runtime.GOOS != "windows" { 263 verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt") 264 } 265 }) 266 }) 267 268 when("normalize mod time is false", func() { 269 it("does not normalize mod times", func() { 270 tarFile := filepath.Join(tmpDir, "some.tar") 271 fh, err := os.Create(tarFile) 272 h.AssertNil(t, err) 273 274 tw := tar.NewWriter(fh) 275 276 err = archive.WriteDirToTar(tw, src, "/foo", 1234, 2345, 0777, false, nil) 277 h.AssertNil(t, err) 278 h.AssertNil(t, tw.Close()) 279 h.AssertNil(t, fh.Close()) 280 281 h.AssertOnTarEntry(t, tarFile, "/foo/some-file.txt", 282 h.DoesNotHaveModTime(archive.NormalizedDateTime), 283 ) 284 }) 285 }) 286 287 when("normalize mod time is true", func() { 288 it("normalizes mod times", func() { 289 tarFile := filepath.Join(tmpDir, "some.tar") 290 fh, err := os.Create(tarFile) 291 h.AssertNil(t, err) 292 293 tw := tar.NewWriter(fh) 294 295 err = archive.WriteDirToTar(tw, src, "/foo", 1234, 2345, 0777, true, nil) 296 h.AssertNil(t, err) 297 h.AssertNil(t, tw.Close()) 298 h.AssertNil(t, fh.Close()) 299 300 h.AssertOnTarEntry(t, tarFile, "/foo/some-file.txt", 301 h.HasModTime(archive.NormalizedDateTime), 302 ) 303 }) 304 }) 305 306 when("is posix", func() { 307 it.Before(func() { 308 h.SkipIf(t, runtime.GOOS == "windows", "Skipping on windows") 309 }) 310 311 when("socket is present", func() { 312 var ( 313 err error 314 tmpSrcDir string 315 fakeSocket net.Listener 316 ) 317 318 it.Before(func() { 319 tmpSrcDir, err = ioutil.TempDir("", "socket-test") 320 h.AssertNil(t, err) 321 322 fakeSocket, err = net.Listen( 323 "unix", 324 filepath.Join(tmpSrcDir, "fake-socket"), 325 ) 326 327 err = ioutil.WriteFile(filepath.Join(tmpSrcDir, "fake-file"), []byte("some-content"), 0777) 328 h.AssertNil(t, err) 329 }) 330 331 it.After(func() { 332 os.RemoveAll(tmpSrcDir) 333 fakeSocket.Close() 334 }) 335 336 it("silently ignore socket", func() { 337 fh, err := os.Create(filepath.Join(tmpDir, "some.tar")) 338 h.AssertNil(t, err) 339 340 tw := tar.NewWriter(fh) 341 342 err = archive.WriteDirToTar(tw, tmpSrcDir, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, nil) 343 h.AssertNil(t, err) 344 h.AssertNil(t, tw.Close()) 345 h.AssertNil(t, fh.Close()) 346 347 file, err := os.Open(filepath.Join(tmpDir, "some.tar")) 348 h.AssertNil(t, err) 349 defer file.Close() 350 351 tr := tar.NewReader(file) 352 353 verify := h.NewTarVerifier(t, tr, 1234, 2345) 354 verify.NextFile( 355 "/nested/dir/dir-in-archive/fake-file", 356 "some-content", 357 0777, 358 ) 359 verify.NoMoreFilesExist() 360 }) 361 }) 362 }) 363 }) 364 365 when("#WriteZipToTar", func() { 366 var src string 367 it.Before(func() { 368 src = filepath.Join("testdata", "zip-to-tar.zip") 369 }) 370 371 when("mode is set to 0777", func() { 372 it("writes a tar to the dest dir with 0777", func() { 373 fh, err := os.Create(filepath.Join(tmpDir, "some.tar")) 374 h.AssertNil(t, err) 375 376 tw := tar.NewWriter(fh) 377 378 err = archive.WriteZipToTar(tw, src, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, nil) 379 h.AssertNil(t, err) 380 h.AssertNil(t, tw.Close()) 381 h.AssertNil(t, fh.Close()) 382 383 file, err := os.Open(filepath.Join(tmpDir, "some.tar")) 384 h.AssertNil(t, err) 385 defer file.Close() 386 387 tr := tar.NewReader(file) 388 389 verify := h.NewTarVerifier(t, tr, 1234, 2345) 390 verify.NextFile("/nested/dir/dir-in-archive/some-file.txt", "some-content", 0777) 391 verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", 0777) 392 verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt") 393 }) 394 }) 395 396 when("mode is set to -1", func() { 397 it("writes a tar to the dest dir with preexisting file mode", func() { 398 fh, err := os.Create(filepath.Join(tmpDir, "some.tar")) 399 h.AssertNil(t, err) 400 401 tw := tar.NewWriter(fh) 402 403 err = archive.WriteZipToTar(tw, src, "/nested/dir/dir-in-archive", 1234, 2345, -1, true, nil) 404 h.AssertNil(t, err) 405 h.AssertNil(t, tw.Close()) 406 h.AssertNil(t, fh.Close()) 407 408 file, err := os.Open(filepath.Join(tmpDir, "some.tar")) 409 h.AssertNil(t, err) 410 defer file.Close() 411 412 tr := tar.NewReader(file) 413 414 verify := h.NewTarVerifier(t, tr, 1234, 2345) 415 verify.NextFile("/nested/dir/dir-in-archive/some-file.txt", "some-content", 0644) 416 verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", 0755) 417 verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt") 418 }) 419 420 when("files are compressed in fat (MSDOS) format", func() { 421 it.Before(func() { 422 src = filepath.Join("testdata", "fat-zip-to-tar.zip") 423 }) 424 425 it("writes a tar to the dest dir with 0777", func() { 426 fh, err := os.Create(filepath.Join(tmpDir, "some.tar")) 427 h.AssertNil(t, err) 428 429 tw := tar.NewWriter(fh) 430 431 err = archive.WriteZipToTar(tw, src, "/nested/dir/dir-in-archive", 1234, 2345, -1, true, nil) 432 h.AssertNil(t, err) 433 h.AssertNil(t, tw.Close()) 434 h.AssertNil(t, fh.Close()) 435 436 file, err := os.Open(filepath.Join(tmpDir, "some.tar")) 437 h.AssertNil(t, err) 438 defer file.Close() 439 440 tr := tar.NewReader(file) 441 442 verify := h.NewTarVerifier(t, tr, 1234, 2345) 443 verify.NextFile("/nested/dir/dir-in-archive/some-file.txt", "some-content", 0777) 444 verify.NoMoreFilesExist() 445 }) 446 }) 447 }) 448 449 when("has file filter", func() { 450 it("follows it when adding files", func() { 451 fh, err := os.Create(filepath.Join(tmpDir, "some.tar")) 452 h.AssertNil(t, err) 453 454 tw := tar.NewWriter(fh) 455 456 err = archive.WriteZipToTar(tw, src, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, func(path string) bool { 457 return !strings.Contains(path, "some-file.txt") 458 }) 459 h.AssertNil(t, err) 460 h.AssertNil(t, tw.Close()) 461 h.AssertNil(t, fh.Close()) 462 463 file, err := os.Open(filepath.Join(tmpDir, "some.tar")) 464 h.AssertNil(t, err) 465 defer file.Close() 466 467 tr := tar.NewReader(file) 468 469 verify := h.NewTarVerifier(t, tr, 1234, 2345) 470 verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", 0777) 471 verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt") 472 }) 473 }) 474 475 when("normalize mod time is false", func() { 476 it("does not normalize mod times", func() { 477 tarFile := filepath.Join(tmpDir, "some.tar") 478 fh, err := os.Create(tarFile) 479 h.AssertNil(t, err) 480 481 tw := tar.NewWriter(fh) 482 483 err = archive.WriteZipToTar(tw, src, "/foo", 1234, 2345, 0777, false, nil) 484 h.AssertNil(t, err) 485 h.AssertNil(t, tw.Close()) 486 h.AssertNil(t, fh.Close()) 487 488 h.AssertOnTarEntry(t, tarFile, "/foo/some-file.txt", 489 h.DoesNotHaveModTime(archive.NormalizedDateTime), 490 ) 491 }) 492 }) 493 494 when("normalize mod time is true", func() { 495 it("normalizes mod times", func() { 496 tarFile := filepath.Join(tmpDir, "some.tar") 497 fh, err := os.Create(tarFile) 498 h.AssertNil(t, err) 499 500 tw := tar.NewWriter(fh) 501 502 err = archive.WriteZipToTar(tw, src, "/foo", 1234, 2345, 0777, true, nil) 503 h.AssertNil(t, err) 504 h.AssertNil(t, tw.Close()) 505 h.AssertNil(t, fh.Close()) 506 507 h.AssertOnTarEntry(t, tarFile, "/foo/some-file.txt", 508 h.HasModTime(archive.NormalizedDateTime), 509 ) 510 }) 511 }) 512 }) 513 514 when("#IsZip", func() { 515 when("file is a zip file", func() { 516 it("returns true", func() { 517 path := filepath.Join("testdata", "zip-to-tar.zip") 518 519 file, err := os.Open(path) 520 h.AssertNil(t, err) 521 defer file.Close() 522 523 isZip, err := archive.IsZip(file) 524 h.AssertNil(t, err) 525 h.AssertTrue(t, isZip) 526 }) 527 }) 528 529 when("file is a jar file", func() { 530 it("returns true", func() { 531 path := filepath.Join("testdata", "jar-file.jar") 532 533 file, err := os.Open(path) 534 h.AssertNil(t, err) 535 defer file.Close() 536 537 isZip, err := archive.IsZip(file) 538 h.AssertNil(t, err) 539 h.AssertTrue(t, isZip) 540 }) 541 }) 542 543 when("file is not a zip file", func() { 544 when("file has some content", func() { 545 it("returns false", func() { 546 file, err := ioutil.TempFile(tmpDir, "file.txt") 547 h.AssertNil(t, err) 548 defer file.Close() 549 550 err = ioutil.WriteFile(file.Name(), []byte("content"), os.ModePerm) 551 h.AssertNil(t, err) 552 553 isZip, err := archive.IsZip(file) 554 h.AssertNil(t, err) 555 h.AssertFalse(t, isZip) 556 }) 557 }) 558 559 when("file doesn't have content", func() { 560 it("returns false", func() { 561 file, err := ioutil.TempFile(tmpDir, "file.txt") 562 h.AssertNil(t, err) 563 defer file.Close() 564 565 isZip, err := archive.IsZip(file) 566 h.AssertNil(t, err) 567 h.AssertFalse(t, isZip) 568 }) 569 }) 570 }) 571 572 when("reader is closed", func() { 573 it("returns error", func() { 574 file, err := ioutil.TempFile(tmpDir, "file.txt") 575 h.AssertNil(t, err) 576 err = file.Close() 577 h.AssertNil(t, err) 578 579 isZip, err := archive.IsZip(file) 580 h.AssertError(t, err, os.ErrClosed.Error()) 581 h.AssertFalse(t, isZip) 582 }) 583 }) 584 }) 585 } 586 587 func fileMode(t *testing.T, path string) int64 { 588 t.Helper() 589 info, err := os.Stat(path) 590 if err != nil { 591 t.Fatalf("failed to stat %s", path) 592 } 593 mode := int64(info.Mode() & os.ModePerm) 594 return mode 595 }