OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "third_party/zlib/google/zip_reader.h" | 5 #include "third_party/zlib/google/zip_reader.h" |
6 | 6 |
7 #include <set> | 7 #include <set> |
8 #include <string> | 8 #include <string> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/files/file.h" | 11 #include "base/files/file.h" |
12 #include "base/files/file_util.h" | 12 #include "base/files/file_util.h" |
13 #include "base/files/scoped_temp_dir.h" | 13 #include "base/files/scoped_temp_dir.h" |
14 #include "base/logging.h" | 14 #include "base/logging.h" |
15 #include "base/md5.h" | 15 #include "base/md5.h" |
16 #include "base/path_service.h" | 16 #include "base/path_service.h" |
17 #include "base/run_loop.h" | 17 #include "base/run_loop.h" |
18 #include "base/strings/stringprintf.h" | 18 #include "base/strings/stringprintf.h" |
19 #include "base/strings/utf_string_conversions.h" | 19 #include "base/strings/utf_string_conversions.h" |
20 #include "base/time/time.h" | 20 #include "base/time/time.h" |
| 21 #include "testing/gmock/include/gmock/gmock.h" |
21 #include "testing/gtest/include/gtest/gtest.h" | 22 #include "testing/gtest/include/gtest/gtest.h" |
22 #include "testing/platform_test.h" | 23 #include "testing/platform_test.h" |
23 #include "third_party/zlib/google/zip_internal.h" | 24 #include "third_party/zlib/google/zip_internal.h" |
24 | 25 |
| 26 using ::testing::Return; |
| 27 using ::testing::_; |
| 28 |
25 namespace { | 29 namespace { |
26 | 30 |
27 const static std::string kQuuxExpectedMD5 = "d1ae4ac8a17a0e09317113ab284b57a6"; | 31 const static std::string kQuuxExpectedMD5 = "d1ae4ac8a17a0e09317113ab284b57a6"; |
28 | 32 |
29 class FileWrapper { | 33 class FileWrapper { |
30 public: | 34 public: |
31 typedef enum { | 35 typedef enum { |
32 READ_ONLY, | 36 READ_ONLY, |
33 READ_WRITE | 37 READ_WRITE |
34 } AccessMode; | 38 } AccessMode; |
35 | 39 |
36 FileWrapper(const base::FilePath& path, AccessMode mode) { | 40 FileWrapper(const base::FilePath& path, AccessMode mode) { |
37 int flags = base::File::FLAG_READ; | 41 int flags = base::File::FLAG_READ; |
38 if (mode == READ_ONLY) | 42 if (mode == READ_ONLY) |
39 flags |= base::File::FLAG_OPEN; | 43 flags |= base::File::FLAG_OPEN; |
40 else | 44 else |
41 flags |= base::File::FLAG_WRITE | base::File::FLAG_CREATE_ALWAYS; | 45 flags |= base::File::FLAG_WRITE | base::File::FLAG_CREATE_ALWAYS; |
42 | 46 |
43 file_.Initialize(path, flags); | 47 file_.Initialize(path, flags); |
44 } | 48 } |
45 | 49 |
46 ~FileWrapper() {} | 50 ~FileWrapper() {} |
47 | 51 |
48 base::PlatformFile platform_file() { return file_.GetPlatformFile(); } | 52 base::PlatformFile platform_file() { return file_.GetPlatformFile(); } |
49 | 53 |
| 54 base::File* file() { return &file_; } |
| 55 |
50 private: | 56 private: |
51 base::File file_; | 57 base::File file_; |
52 }; | 58 }; |
53 | 59 |
54 // A mock that provides methods that can be used as callbacks in asynchronous | 60 // A mock that provides methods that can be used as callbacks in asynchronous |
55 // unzip functions. Tracks the number of calls and number of bytes reported. | 61 // unzip functions. Tracks the number of calls and number of bytes reported. |
56 // Assumes that progress callbacks will be executed in-order. | 62 // Assumes that progress callbacks will be executed in-order. |
57 class MockUnzipListener : public base::SupportsWeakPtr<MockUnzipListener> { | 63 class MockUnzipListener : public base::SupportsWeakPtr<MockUnzipListener> { |
58 public: | 64 public: |
59 MockUnzipListener() | 65 MockUnzipListener() |
(...skipping 26 matching lines...) Expand all Loading... |
86 int current_progress() { return current_progress_; } | 92 int current_progress() { return current_progress_; } |
87 | 93 |
88 private: | 94 private: |
89 int success_calls_; | 95 int success_calls_; |
90 int failure_calls_; | 96 int failure_calls_; |
91 int progress_calls_; | 97 int progress_calls_; |
92 | 98 |
93 int64 current_progress_; | 99 int64 current_progress_; |
94 }; | 100 }; |
95 | 101 |
| 102 class MockWriterDelegate : public zip::WriterDelegate { |
| 103 public: |
| 104 MOCK_METHOD0(PrepareOutput, bool()); |
| 105 MOCK_METHOD2(WriteBytes, bool(const char*, int)); |
| 106 }; |
| 107 |
96 } // namespace | 108 } // namespace |
97 | 109 |
98 namespace zip { | 110 namespace zip { |
99 | 111 |
100 // Make the test a PlatformTest to setup autorelease pools properly on Mac. | 112 // Make the test a PlatformTest to setup autorelease pools properly on Mac. |
101 class ZipReaderTest : public PlatformTest { | 113 class ZipReaderTest : public PlatformTest { |
102 protected: | 114 protected: |
103 virtual void SetUp() { | 115 virtual void SetUp() { |
104 PlatformTest::SetUp(); | 116 PlatformTest::SetUp(); |
105 | 117 |
(...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
280 std::string output; | 292 std::string output; |
281 ASSERT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("quux.txt"), | 293 ASSERT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("quux.txt"), |
282 &output)); | 294 &output)); |
283 const std::string md5 = base::MD5String(output); | 295 const std::string md5 = base::MD5String(output); |
284 EXPECT_EQ(kQuuxExpectedMD5, md5); | 296 EXPECT_EQ(kQuuxExpectedMD5, md5); |
285 // quux.txt should be larger than kZipBufSize so that we can exercise | 297 // quux.txt should be larger than kZipBufSize so that we can exercise |
286 // the loop in ExtractCurrentEntry(). | 298 // the loop in ExtractCurrentEntry(). |
287 EXPECT_LT(static_cast<size_t>(internal::kZipBufSize), output.size()); | 299 EXPECT_LT(static_cast<size_t>(internal::kZipBufSize), output.size()); |
288 } | 300 } |
289 | 301 |
290 #if defined(OS_POSIX) | 302 TEST_F(ZipReaderTest, PlatformFileExtractCurrentEntryToFile_RegularFile) { |
291 TEST_F(ZipReaderTest, PlatformFileExtractCurrentEntryToFd_RegularFile) { | |
292 ZipReader reader; | 303 ZipReader reader; |
293 FileWrapper zip_fd_wrapper(test_zip_file_, FileWrapper::READ_ONLY); | 304 FileWrapper zip_fd_wrapper(test_zip_file_, FileWrapper::READ_ONLY); |
294 ASSERT_TRUE(reader.OpenFromPlatformFile(zip_fd_wrapper.platform_file())); | 305 ASSERT_TRUE(reader.OpenFromPlatformFile(zip_fd_wrapper.platform_file())); |
295 base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt")); | 306 base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt")); |
296 base::FilePath out_path = test_dir_.AppendASCII("quux.txt"); | 307 base::FilePath out_path = test_dir_.AppendASCII("quux.txt"); |
297 FileWrapper out_fd_w(out_path, FileWrapper::READ_WRITE); | 308 FileWrapper out_fd_w(out_path, FileWrapper::READ_WRITE); |
298 ASSERT_TRUE(reader.LocateAndOpenEntry(target_path)); | 309 ASSERT_TRUE(reader.LocateAndOpenEntry(target_path)); |
299 ASSERT_TRUE(reader.ExtractCurrentEntryToFd(out_fd_w.platform_file())); | 310 ASSERT_TRUE(reader.ExtractCurrentEntryToFile(out_fd_w.file())); |
300 // Read the output file and compute the MD5. | 311 // Read the output file and compute the MD5. |
301 std::string output; | 312 std::string output; |
302 ASSERT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("quux.txt"), | 313 ASSERT_TRUE(base::ReadFileToString(out_path, &output)); |
303 &output)); | |
304 const std::string md5 = base::MD5String(output); | 314 const std::string md5 = base::MD5String(output); |
305 EXPECT_EQ(kQuuxExpectedMD5, md5); | 315 EXPECT_EQ(kQuuxExpectedMD5, md5); |
306 // quux.txt should be larger than kZipBufSize so that we can exercise | 316 // quux.txt should be larger than kZipBufSize so that we can exercise |
307 // the loop in ExtractCurrentEntry(). | 317 // the loop in ExtractCurrentEntry(). |
308 EXPECT_LT(static_cast<size_t>(internal::kZipBufSize), output.size()); | 318 EXPECT_LT(static_cast<size_t>(internal::kZipBufSize), output.size()); |
309 } | 319 } |
310 #endif | |
311 | 320 |
312 TEST_F(ZipReaderTest, ExtractCurrentEntryToFilePath_Directory) { | 321 TEST_F(ZipReaderTest, ExtractCurrentEntryToFilePath_Directory) { |
313 ZipReader reader; | 322 ZipReader reader; |
314 ASSERT_TRUE(reader.Open(test_zip_file_)); | 323 ASSERT_TRUE(reader.Open(test_zip_file_)); |
315 base::FilePath target_path(FILE_PATH_LITERAL("foo/")); | 324 base::FilePath target_path(FILE_PATH_LITERAL("foo/")); |
316 ASSERT_TRUE(reader.LocateAndOpenEntry(target_path)); | 325 ASSERT_TRUE(reader.LocateAndOpenEntry(target_path)); |
317 ASSERT_TRUE(reader.ExtractCurrentEntryToFilePath( | 326 ASSERT_TRUE(reader.ExtractCurrentEntryToFilePath( |
318 test_dir_.AppendASCII("foo"))); | 327 test_dir_.AppendASCII("foo"))); |
319 // The directory should be created. | 328 // The directory should be created. |
320 ASSERT_TRUE(base::DirectoryExists(test_dir_.AppendASCII("foo"))); | 329 ASSERT_TRUE(base::DirectoryExists(test_dir_.AppendASCII("foo"))); |
(...skipping 254 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
575 | 584 |
576 // This test exposes http://crbug.com/430959, at least on OS X | 585 // This test exposes http://crbug.com/430959, at least on OS X |
577 TEST_F(ZipReaderTest, DISABLED_LeakDetectionTest) { | 586 TEST_F(ZipReaderTest, DISABLED_LeakDetectionTest) { |
578 for (int i = 0; i < 100000; ++i) { | 587 for (int i = 0; i < 100000; ++i) { |
579 FileWrapper zip_fd_wrapper(test_zip_file_, FileWrapper::READ_ONLY); | 588 FileWrapper zip_fd_wrapper(test_zip_file_, FileWrapper::READ_ONLY); |
580 ZipReader reader; | 589 ZipReader reader; |
581 ASSERT_TRUE(reader.OpenFromPlatformFile(zip_fd_wrapper.platform_file())); | 590 ASSERT_TRUE(reader.OpenFromPlatformFile(zip_fd_wrapper.platform_file())); |
582 } | 591 } |
583 } | 592 } |
584 | 593 |
| 594 // Test that when WriterDelegate::PrepareMock returns false, no other methods on |
| 595 // the delegate are called and the extraction fails. |
| 596 TEST_F(ZipReaderTest, ExtractCurrentEntryPrepareFailure) { |
| 597 testing::StrictMock<MockWriterDelegate> mock_writer; |
| 598 |
| 599 EXPECT_CALL(mock_writer, PrepareOutput()) |
| 600 .WillOnce(Return(false)); |
| 601 |
| 602 base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt")); |
| 603 ZipReader reader; |
| 604 |
| 605 ASSERT_TRUE(reader.Open(test_zip_file_)); |
| 606 ASSERT_TRUE(reader.LocateAndOpenEntry(target_path)); |
| 607 ASSERT_FALSE(reader.ExtractCurrentEntry(&mock_writer)); |
| 608 } |
| 609 |
| 610 // Test that when WriterDelegate::WriteBytes returns false, no other methods on |
| 611 // the delegate are called and the extraction fails. |
| 612 TEST_F(ZipReaderTest, ExtractCurrentEntryWriteBytesFailure) { |
| 613 testing::StrictMock<MockWriterDelegate> mock_writer; |
| 614 |
| 615 EXPECT_CALL(mock_writer, PrepareOutput()) |
| 616 .WillOnce(Return(true)); |
| 617 EXPECT_CALL(mock_writer, WriteBytes(_, _)) |
| 618 .WillOnce(Return(false)); |
| 619 |
| 620 base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt")); |
| 621 ZipReader reader; |
| 622 |
| 623 ASSERT_TRUE(reader.Open(test_zip_file_)); |
| 624 ASSERT_TRUE(reader.LocateAndOpenEntry(target_path)); |
| 625 ASSERT_FALSE(reader.ExtractCurrentEntry(&mock_writer)); |
| 626 } |
| 627 |
| 628 // Test that extraction succeeds when the writer delegate reports all is well. |
| 629 TEST_F(ZipReaderTest, ExtractCurrentEntrySuccess) { |
| 630 testing::StrictMock<MockWriterDelegate> mock_writer; |
| 631 |
| 632 EXPECT_CALL(mock_writer, PrepareOutput()) |
| 633 .WillOnce(Return(true)); |
| 634 EXPECT_CALL(mock_writer, WriteBytes(_, _)) |
| 635 .WillRepeatedly(Return(true)); |
| 636 |
| 637 base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt")); |
| 638 ZipReader reader; |
| 639 |
| 640 ASSERT_TRUE(reader.Open(test_zip_file_)); |
| 641 ASSERT_TRUE(reader.LocateAndOpenEntry(target_path)); |
| 642 ASSERT_TRUE(reader.ExtractCurrentEntry(&mock_writer)); |
| 643 } |
| 644 |
| 645 class FileWriterDelegateTest : public ::testing::Test { |
| 646 protected: |
| 647 void SetUp() override { |
| 648 ASSERT_TRUE(base::CreateTemporaryFile(&temp_file_path_)); |
| 649 file_.Initialize(temp_file_path_, (base::File::FLAG_CREATE_ALWAYS | |
| 650 base::File::FLAG_READ | |
| 651 base::File::FLAG_WRITE | |
| 652 base::File::FLAG_TEMPORARY | |
| 653 base::File::FLAG_DELETE_ON_CLOSE)); |
| 654 ASSERT_TRUE(file_.IsValid()); |
| 655 } |
| 656 |
| 657 // Writes data to the file, leaving the current position at the end of the |
| 658 // write. |
| 659 void PopulateFile() { |
| 660 static const char kSomeData[] = "this sure is some data."; |
| 661 static const size_t kSomeDataLen = sizeof(kSomeData) - 1; |
| 662 ASSERT_NE(-1LL, file_.Write(0LL, kSomeData, kSomeDataLen)); |
| 663 } |
| 664 |
| 665 base::FilePath temp_file_path_; |
| 666 base::File file_; |
| 667 }; |
| 668 |
| 669 TEST_F(FileWriterDelegateTest, WriteToStartAndTruncate) { |
| 670 // Write stuff and advance. |
| 671 PopulateFile(); |
| 672 |
| 673 // This should rewind, write, then truncate. |
| 674 static const char kSomeData[] = "short"; |
| 675 static const int kSomeDataLen = sizeof(kSomeData) - 1; |
| 676 { |
| 677 FileWriterDelegate writer(&file_); |
| 678 ASSERT_TRUE(writer.PrepareOutput()); |
| 679 ASSERT_TRUE(writer.WriteBytes(kSomeData, kSomeDataLen)); |
| 680 } |
| 681 ASSERT_EQ(kSomeDataLen, file_.GetLength()); |
| 682 char buf[kSomeDataLen] = {}; |
| 683 ASSERT_EQ(kSomeDataLen, file_.Read(0LL, buf, kSomeDataLen)); |
| 684 ASSERT_EQ(std::string(kSomeData), std::string(buf, kSomeDataLen)); |
| 685 } |
| 686 |
585 } // namespace zip | 687 } // namespace zip |
OLD | NEW |