Home | History | Annotate | Download | only in google
      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
      3 // found in the LICENSE file.
      4 
      5 #include "third_party/zlib/google/zip_reader.h"
      6 
      7 #include <set>
      8 #include <string>
      9 
     10 #include "base/bind.h"
     11 #include "base/files/file.h"
     12 #include "base/files/file_util.h"
     13 #include "base/files/scoped_temp_dir.h"
     14 #include "base/logging.h"
     15 #include "base/md5.h"
     16 #include "base/path_service.h"
     17 #include "base/run_loop.h"
     18 #include "base/strings/stringprintf.h"
     19 #include "base/strings/utf_string_conversions.h"
     20 #include "base/time/time.h"
     21 #include "testing/gtest/include/gtest/gtest.h"
     22 #include "testing/platform_test.h"
     23 #include "third_party/zlib/google/zip_internal.h"
     24 
     25 namespace {
     26 
     27 const static std::string kQuuxExpectedMD5 = "d1ae4ac8a17a0e09317113ab284b57a6";
     28 
     29 class FileWrapper {
     30  public:
     31   typedef enum {
     32     READ_ONLY,
     33     READ_WRITE
     34   } AccessMode;
     35 
     36   FileWrapper(const base::FilePath& path, AccessMode mode) {
     37     int flags = base::File::FLAG_READ;
     38     if (mode == READ_ONLY)
     39       flags |= base::File::FLAG_OPEN;
     40     else
     41       flags |= base::File::FLAG_WRITE | base::File::FLAG_CREATE_ALWAYS;
     42 
     43     file_.Initialize(path, flags);
     44   }
     45 
     46   ~FileWrapper() {}
     47 
     48   base::PlatformFile platform_file() { return file_.GetPlatformFile(); }
     49 
     50  private:
     51   base::File file_;
     52 };
     53 
     54 // 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.
     56 // Assumes that progress callbacks will be executed in-order.
     57 class MockUnzipListener : public base::SupportsWeakPtr<MockUnzipListener> {
     58  public:
     59   MockUnzipListener()
     60       : success_calls_(0),
     61         failure_calls_(0),
     62         progress_calls_(0),
     63         current_progress_(0) {
     64   }
     65 
     66   // Success callback for async functions.
     67   void OnUnzipSuccess() {
     68     success_calls_++;
     69   }
     70 
     71   // Failure callback for async functions.
     72   void OnUnzipFailure() {
     73     failure_calls_++;
     74   }
     75 
     76   // Progress callback for async functions.
     77   void OnUnzipProgress(int64 progress) {
     78     DCHECK(progress > current_progress_);
     79     progress_calls_++;
     80     current_progress_ = progress;
     81   }
     82 
     83   int success_calls() { return success_calls_; }
     84   int failure_calls() { return failure_calls_; }
     85   int progress_calls() { return progress_calls_; }
     86   int current_progress() { return current_progress_; }
     87 
     88  private:
     89   int success_calls_;
     90   int failure_calls_;
     91   int progress_calls_;
     92 
     93   int64 current_progress_;
     94 };
     95 
     96 }   // namespace
     97 
     98 namespace zip {
     99 
    100 // Make the test a PlatformTest to setup autorelease pools properly on Mac.
    101 class ZipReaderTest : public PlatformTest {
    102  protected:
    103   virtual void SetUp() {
    104     PlatformTest::SetUp();
    105 
    106     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
    107     test_dir_ = temp_dir_.path();
    108 
    109     ASSERT_TRUE(GetTestDataDirectory(&test_data_dir_));
    110 
    111     test_zip_file_ = test_data_dir_.AppendASCII("test.zip");
    112     evil_zip_file_ = test_data_dir_.AppendASCII("evil.zip");
    113     evil_via_invalid_utf8_zip_file_ = test_data_dir_.AppendASCII(
    114         "evil_via_invalid_utf8.zip");
    115     evil_via_absolute_file_name_zip_file_ = test_data_dir_.AppendASCII(
    116         "evil_via_absolute_file_name.zip");
    117 
    118     test_zip_contents_.insert(base::FilePath(FILE_PATH_LITERAL("foo/")));
    119     test_zip_contents_.insert(base::FilePath(FILE_PATH_LITERAL("foo/bar/")));
    120     test_zip_contents_.insert(
    121         base::FilePath(FILE_PATH_LITERAL("foo/bar/baz.txt")));
    122     test_zip_contents_.insert(
    123         base::FilePath(FILE_PATH_LITERAL("foo/bar/quux.txt")));
    124     test_zip_contents_.insert(
    125         base::FilePath(FILE_PATH_LITERAL("foo/bar.txt")));
    126     test_zip_contents_.insert(base::FilePath(FILE_PATH_LITERAL("foo.txt")));
    127     test_zip_contents_.insert(
    128         base::FilePath(FILE_PATH_LITERAL("foo/bar/.hidden")));
    129   }
    130 
    131   virtual void TearDown() {
    132     PlatformTest::TearDown();
    133   }
    134 
    135   bool GetTestDataDirectory(base::FilePath* path) {
    136     bool success = PathService::Get(base::DIR_SOURCE_ROOT, path);
    137     EXPECT_TRUE(success);
    138     if (!success)
    139       return false;
    140     *path = path->AppendASCII("third_party");
    141     *path = path->AppendASCII("zlib");
    142     *path = path->AppendASCII("google");
    143     *path = path->AppendASCII("test");
    144     *path = path->AppendASCII("data");
    145     return true;
    146   }
    147 
    148   bool CompareFileAndMD5(const base::FilePath& path,
    149                          const std::string expected_md5) {
    150     // Read the output file and compute the MD5.
    151     std::string output;
    152     if (!base::ReadFileToString(path, &output))
    153       return false;
    154     const std::string md5 = base::MD5String(output);
    155     return expected_md5 == md5;
    156   }
    157 
    158   // The path to temporary directory used to contain the test operations.
    159   base::FilePath test_dir_;
    160   // The path to the test data directory where test.zip etc. are located.
    161   base::FilePath test_data_dir_;
    162   // The path to test.zip in the test data directory.
    163   base::FilePath test_zip_file_;
    164   // The path to evil.zip in the test data directory.
    165   base::FilePath evil_zip_file_;
    166   // The path to evil_via_invalid_utf8.zip in the test data directory.
    167   base::FilePath evil_via_invalid_utf8_zip_file_;
    168   // The path to evil_via_absolute_file_name.zip in the test data directory.
    169   base::FilePath evil_via_absolute_file_name_zip_file_;
    170   std::set<base::FilePath> test_zip_contents_;
    171 
    172   base::ScopedTempDir temp_dir_;
    173 
    174   base::MessageLoop message_loop_;
    175 };
    176 
    177 TEST_F(ZipReaderTest, Open_ValidZipFile) {
    178   ZipReader reader;
    179   ASSERT_TRUE(reader.Open(test_zip_file_));
    180 }
    181 
    182 TEST_F(ZipReaderTest, Open_ValidZipPlatformFile) {
    183   ZipReader reader;
    184   FileWrapper zip_fd_wrapper(test_zip_file_, FileWrapper::READ_ONLY);
    185   ASSERT_TRUE(reader.OpenFromPlatformFile(zip_fd_wrapper.platform_file()));
    186 }
    187 
    188 TEST_F(ZipReaderTest, Open_NonExistentFile) {
    189   ZipReader reader;
    190   ASSERT_FALSE(reader.Open(test_data_dir_.AppendASCII("nonexistent.zip")));
    191 }
    192 
    193 TEST_F(ZipReaderTest, Open_ExistentButNonZipFile) {
    194   ZipReader reader;
    195   ASSERT_FALSE(reader.Open(test_data_dir_.AppendASCII("create_test_zip.sh")));
    196 }
    197 
    198 // Iterate through the contents in the test zip file, and compare that the
    199 // contents collected from the zip reader matches the expected contents.
    200 TEST_F(ZipReaderTest, Iteration) {
    201   std::set<base::FilePath> actual_contents;
    202   ZipReader reader;
    203   ASSERT_TRUE(reader.Open(test_zip_file_));
    204   while (reader.HasMore()) {
    205     ASSERT_TRUE(reader.OpenCurrentEntryInZip());
    206     actual_contents.insert(reader.current_entry_info()->file_path());
    207     ASSERT_TRUE(reader.AdvanceToNextEntry());
    208   }
    209   EXPECT_FALSE(reader.AdvanceToNextEntry());  // Shouldn't go further.
    210   EXPECT_EQ(test_zip_contents_.size(),
    211             static_cast<size_t>(reader.num_entries()));
    212   EXPECT_EQ(test_zip_contents_.size(), actual_contents.size());
    213   EXPECT_EQ(test_zip_contents_, actual_contents);
    214 }
    215 
    216 // Open the test zip file from a file descriptor, iterate through its contents,
    217 // and compare that they match the expected contents.
    218 TEST_F(ZipReaderTest, PlatformFileIteration) {
    219   std::set<base::FilePath> actual_contents;
    220   ZipReader reader;
    221   FileWrapper zip_fd_wrapper(test_zip_file_, FileWrapper::READ_ONLY);
    222   ASSERT_TRUE(reader.OpenFromPlatformFile(zip_fd_wrapper.platform_file()));
    223   while (reader.HasMore()) {
    224     ASSERT_TRUE(reader.OpenCurrentEntryInZip());
    225     actual_contents.insert(reader.current_entry_info()->file_path());
    226     ASSERT_TRUE(reader.AdvanceToNextEntry());
    227   }
    228   EXPECT_FALSE(reader.AdvanceToNextEntry());  // Shouldn't go further.
    229   EXPECT_EQ(test_zip_contents_.size(),
    230             static_cast<size_t>(reader.num_entries()));
    231   EXPECT_EQ(test_zip_contents_.size(), actual_contents.size());
    232   EXPECT_EQ(test_zip_contents_, actual_contents);
    233 }
    234 
    235 TEST_F(ZipReaderTest, LocateAndOpenEntry_ValidFile) {
    236   std::set<base::FilePath> actual_contents;
    237   ZipReader reader;
    238   ASSERT_TRUE(reader.Open(test_zip_file_));
    239   base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
    240   ASSERT_TRUE(reader.LocateAndOpenEntry(target_path));
    241   EXPECT_EQ(target_path, reader.current_entry_info()->file_path());
    242 }
    243 
    244 TEST_F(ZipReaderTest, LocateAndOpenEntry_NonExistentFile) {
    245   std::set<base::FilePath> actual_contents;
    246   ZipReader reader;
    247   ASSERT_TRUE(reader.Open(test_zip_file_));
    248   base::FilePath target_path(FILE_PATH_LITERAL("nonexistent.txt"));
    249   ASSERT_FALSE(reader.LocateAndOpenEntry(target_path));
    250   EXPECT_EQ(NULL, reader.current_entry_info());
    251 }
    252 
    253 TEST_F(ZipReaderTest, ExtractCurrentEntryToFilePath_RegularFile) {
    254   ZipReader reader;
    255   ASSERT_TRUE(reader.Open(test_zip_file_));
    256   base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
    257   ASSERT_TRUE(reader.LocateAndOpenEntry(target_path));
    258   ASSERT_TRUE(reader.ExtractCurrentEntryToFilePath(
    259       test_dir_.AppendASCII("quux.txt")));
    260   // Read the output file ans compute the MD5.
    261   std::string output;
    262   ASSERT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("quux.txt"),
    263                                      &output));
    264   const std::string md5 = base::MD5String(output);
    265   EXPECT_EQ(kQuuxExpectedMD5, md5);
    266   // quux.txt should be larger than kZipBufSize so that we can exercise
    267   // the loop in ExtractCurrentEntry().
    268   EXPECT_LT(static_cast<size_t>(internal::kZipBufSize), output.size());
    269 }
    270 
    271 TEST_F(ZipReaderTest, PlatformFileExtractCurrentEntryToFilePath_RegularFile) {
    272   ZipReader reader;
    273   FileWrapper zip_fd_wrapper(test_zip_file_, FileWrapper::READ_ONLY);
    274   ASSERT_TRUE(reader.OpenFromPlatformFile(zip_fd_wrapper.platform_file()));
    275   base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
    276   ASSERT_TRUE(reader.LocateAndOpenEntry(target_path));
    277   ASSERT_TRUE(reader.ExtractCurrentEntryToFilePath(
    278       test_dir_.AppendASCII("quux.txt")));
    279   // Read the output file and compute the MD5.
    280   std::string output;
    281   ASSERT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("quux.txt"),
    282                                      &output));
    283   const std::string md5 = base::MD5String(output);
    284   EXPECT_EQ(kQuuxExpectedMD5, md5);
    285   // quux.txt should be larger than kZipBufSize so that we can exercise
    286   // the loop in ExtractCurrentEntry().
    287   EXPECT_LT(static_cast<size_t>(internal::kZipBufSize), output.size());
    288 }
    289 
    290 #if defined(OS_POSIX)
    291 TEST_F(ZipReaderTest, PlatformFileExtractCurrentEntryToFd_RegularFile) {
    292   ZipReader reader;
    293   FileWrapper zip_fd_wrapper(test_zip_file_, FileWrapper::READ_ONLY);
    294   ASSERT_TRUE(reader.OpenFromPlatformFile(zip_fd_wrapper.platform_file()));
    295   base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
    296   base::FilePath out_path = test_dir_.AppendASCII("quux.txt");
    297   FileWrapper out_fd_w(out_path, FileWrapper::READ_WRITE);
    298   ASSERT_TRUE(reader.LocateAndOpenEntry(target_path));
    299   ASSERT_TRUE(reader.ExtractCurrentEntryToFd(out_fd_w.platform_file()));
    300   // Read the output file and compute the MD5.
    301   std::string output;
    302   ASSERT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("quux.txt"),
    303                                      &output));
    304   const std::string md5 = base::MD5String(output);
    305   EXPECT_EQ(kQuuxExpectedMD5, md5);
    306   // quux.txt should be larger than kZipBufSize so that we can exercise
    307   // the loop in ExtractCurrentEntry().
    308   EXPECT_LT(static_cast<size_t>(internal::kZipBufSize), output.size());
    309 }
    310 #endif
    311 
    312 TEST_F(ZipReaderTest, ExtractCurrentEntryToFilePath_Directory) {
    313   ZipReader reader;
    314   ASSERT_TRUE(reader.Open(test_zip_file_));
    315   base::FilePath target_path(FILE_PATH_LITERAL("foo/"));
    316   ASSERT_TRUE(reader.LocateAndOpenEntry(target_path));
    317   ASSERT_TRUE(reader.ExtractCurrentEntryToFilePath(
    318       test_dir_.AppendASCII("foo")));
    319   // The directory should be created.
    320   ASSERT_TRUE(base::DirectoryExists(test_dir_.AppendASCII("foo")));
    321 }
    322 
    323 TEST_F(ZipReaderTest, ExtractCurrentEntryIntoDirectory_RegularFile) {
    324   ZipReader reader;
    325   ASSERT_TRUE(reader.Open(test_zip_file_));
    326   base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
    327   ASSERT_TRUE(reader.LocateAndOpenEntry(target_path));
    328   ASSERT_TRUE(reader.ExtractCurrentEntryIntoDirectory(test_dir_));
    329   // Sub directories should be created.
    330   ASSERT_TRUE(base::DirectoryExists(test_dir_.AppendASCII("foo/bar")));
    331   // And the file should be created.
    332   std::string output;
    333   ASSERT_TRUE(base::ReadFileToString(
    334       test_dir_.AppendASCII("foo/bar/quux.txt"), &output));
    335   const std::string md5 = base::MD5String(output);
    336   EXPECT_EQ(kQuuxExpectedMD5, md5);
    337 }
    338 
    339 TEST_F(ZipReaderTest, current_entry_info_RegularFile) {
    340   ZipReader reader;
    341   ASSERT_TRUE(reader.Open(test_zip_file_));
    342   base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
    343   ASSERT_TRUE(reader.LocateAndOpenEntry(target_path));
    344   ZipReader::EntryInfo* current_entry_info = reader.current_entry_info();
    345 
    346   EXPECT_EQ(target_path, current_entry_info->file_path());
    347   EXPECT_EQ(13527, current_entry_info->original_size());
    348 
    349   // The expected time stamp: 2009-05-29 06:22:20
    350   base::Time::Exploded exploded = {};  // Zero-clear.
    351   current_entry_info->last_modified().LocalExplode(&exploded);
    352   EXPECT_EQ(2009, exploded.year);
    353   EXPECT_EQ(5, exploded.month);
    354   EXPECT_EQ(29, exploded.day_of_month);
    355   EXPECT_EQ(6, exploded.hour);
    356   EXPECT_EQ(22, exploded.minute);
    357   EXPECT_EQ(20, exploded.second);
    358   EXPECT_EQ(0, exploded.millisecond);
    359 
    360   EXPECT_FALSE(current_entry_info->is_unsafe());
    361   EXPECT_FALSE(current_entry_info->is_directory());
    362 }
    363 
    364 TEST_F(ZipReaderTest, current_entry_info_DotDotFile) {
    365   ZipReader reader;
    366   ASSERT_TRUE(reader.Open(evil_zip_file_));
    367   base::FilePath target_path(FILE_PATH_LITERAL(
    368       "../levilevilevilevilevilevilevilevilevilevilevilevil"));
    369   ASSERT_TRUE(reader.LocateAndOpenEntry(target_path));
    370   ZipReader::EntryInfo* current_entry_info = reader.current_entry_info();
    371   EXPECT_EQ(target_path, current_entry_info->file_path());
    372 
    373   // This file is unsafe because of ".." in the file name.
    374   EXPECT_TRUE(current_entry_info->is_unsafe());
    375   EXPECT_FALSE(current_entry_info->is_directory());
    376 }
    377 
    378 TEST_F(ZipReaderTest, current_entry_info_InvalidUTF8File) {
    379   ZipReader reader;
    380   ASSERT_TRUE(reader.Open(evil_via_invalid_utf8_zip_file_));
    381   // The evil file is the 2nd file in the zip file.
    382   // We cannot locate by the file name ".\x80.\\evil.txt",
    383   // as FilePath may internally convert the string.
    384   ASSERT_TRUE(reader.AdvanceToNextEntry());
    385   ASSERT_TRUE(reader.OpenCurrentEntryInZip());
    386   ZipReader::EntryInfo* current_entry_info = reader.current_entry_info();
    387 
    388   // This file is unsafe because of invalid UTF-8 in the file name.
    389   EXPECT_TRUE(current_entry_info->is_unsafe());
    390   EXPECT_FALSE(current_entry_info->is_directory());
    391 }
    392 
    393 TEST_F(ZipReaderTest, current_entry_info_AbsoluteFile) {
    394   ZipReader reader;
    395   ASSERT_TRUE(reader.Open(evil_via_absolute_file_name_zip_file_));
    396   base::FilePath target_path(FILE_PATH_LITERAL("/evil.txt"));
    397   ASSERT_TRUE(reader.LocateAndOpenEntry(target_path));
    398   ZipReader::EntryInfo* current_entry_info = reader.current_entry_info();
    399   EXPECT_EQ(target_path, current_entry_info->file_path());
    400 
    401   // This file is unsafe because of the absolute file name.
    402   EXPECT_TRUE(current_entry_info->is_unsafe());
    403   EXPECT_FALSE(current_entry_info->is_directory());
    404 }
    405 
    406 TEST_F(ZipReaderTest, current_entry_info_Directory) {
    407   ZipReader reader;
    408   ASSERT_TRUE(reader.Open(test_zip_file_));
    409   base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/"));
    410   ASSERT_TRUE(reader.LocateAndOpenEntry(target_path));
    411   ZipReader::EntryInfo* current_entry_info = reader.current_entry_info();
    412 
    413   EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("foo/bar/")),
    414             current_entry_info->file_path());
    415   // The directory size should be zero.
    416   EXPECT_EQ(0, current_entry_info->original_size());
    417 
    418   // The expected time stamp: 2009-05-31 15:49:52
    419   base::Time::Exploded exploded = {};  // Zero-clear.
    420   current_entry_info->last_modified().LocalExplode(&exploded);
    421   EXPECT_EQ(2009, exploded.year);
    422   EXPECT_EQ(5, exploded.month);
    423   EXPECT_EQ(31, exploded.day_of_month);
    424   EXPECT_EQ(15, exploded.hour);
    425   EXPECT_EQ(49, exploded.minute);
    426   EXPECT_EQ(52, exploded.second);
    427   EXPECT_EQ(0, exploded.millisecond);
    428 
    429   EXPECT_FALSE(current_entry_info->is_unsafe());
    430   EXPECT_TRUE(current_entry_info->is_directory());
    431 }
    432 
    433 // Verifies that the ZipReader class can extract a file from a zip archive
    434 // stored in memory. This test opens a zip archive in a std::string object,
    435 // extracts its content, and verifies the content is the same as the expected
    436 // text.
    437 TEST_F(ZipReaderTest, OpenFromString) {
    438   // A zip archive consisting of one file "test.txt", which is a 16-byte text
    439   // file that contains "This is a test.\n".
    440   const char kTestData[] =
    441       "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\xa4\x66\x24\x41\x13\xe8"
    442       "\xcb\x27\x10\x00\x00\x00\x10\x00\x00\x00\x08\x00\x1c\x00\x74\x65"
    443       "\x73\x74\x2e\x74\x78\x74\x55\x54\x09\x00\x03\x34\x89\x45\x50\x34"
    444       "\x89\x45\x50\x75\x78\x0b\x00\x01\x04\x8e\xf0\x00\x00\x04\x88\x13"
    445       "\x00\x00\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20\x74\x65\x73\x74"
    446       "\x2e\x0a\x50\x4b\x01\x02\x1e\x03\x0a\x00\x00\x00\x00\x00\xa4\x66"
    447       "\x24\x41\x13\xe8\xcb\x27\x10\x00\x00\x00\x10\x00\x00\x00\x08\x00"
    448       "\x18\x00\x00\x00\x00\x00\x01\x00\x00\x00\xa4\x81\x00\x00\x00\x00"
    449       "\x74\x65\x73\x74\x2e\x74\x78\x74\x55\x54\x05\x00\x03\x34\x89\x45"
    450       "\x50\x75\x78\x0b\x00\x01\x04\x8e\xf0\x00\x00\x04\x88\x13\x00\x00"
    451       "\x50\x4b\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00\x4e\x00\x00\x00"
    452       "\x52\x00\x00\x00\x00\x00";
    453   std::string data(kTestData, arraysize(kTestData));
    454   ZipReader reader;
    455   ASSERT_TRUE(reader.OpenFromString(data));
    456   base::FilePath target_path(FILE_PATH_LITERAL("test.txt"));
    457   ASSERT_TRUE(reader.LocateAndOpenEntry(target_path));
    458   ASSERT_TRUE(reader.ExtractCurrentEntryToFilePath(
    459       test_dir_.AppendASCII("test.txt")));
    460 
    461   std::string actual;
    462   ASSERT_TRUE(base::ReadFileToString(
    463       test_dir_.AppendASCII("test.txt"), &actual));
    464   EXPECT_EQ(std::string("This is a test.\n"), actual);
    465 }
    466 
    467 // Verifies that the asynchronous extraction to a file works.
    468 TEST_F(ZipReaderTest, ExtractToFileAsync_RegularFile) {
    469   MockUnzipListener listener;
    470 
    471   ZipReader reader;
    472   base::FilePath target_file = test_dir_.AppendASCII("quux.txt");
    473   base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
    474   ASSERT_TRUE(reader.Open(test_zip_file_));
    475   ASSERT_TRUE(reader.LocateAndOpenEntry(target_path));
    476   reader.ExtractCurrentEntryToFilePathAsync(
    477       target_file,
    478       base::Bind(&MockUnzipListener::OnUnzipSuccess,
    479                  listener.AsWeakPtr()),
    480       base::Bind(&MockUnzipListener::OnUnzipFailure,
    481                  listener.AsWeakPtr()),
    482       base::Bind(&MockUnzipListener::OnUnzipProgress,
    483                  listener.AsWeakPtr()));
    484 
    485   EXPECT_EQ(0, listener.success_calls());
    486   EXPECT_EQ(0, listener.failure_calls());
    487   EXPECT_EQ(0, listener.progress_calls());
    488 
    489   base::RunLoop().RunUntilIdle();
    490 
    491   EXPECT_EQ(1, listener.success_calls());
    492   EXPECT_EQ(0, listener.failure_calls());
    493   EXPECT_LE(1, listener.progress_calls());
    494 
    495   std::string output;
    496   ASSERT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("quux.txt"),
    497                                      &output));
    498   const std::string md5 = base::MD5String(output);
    499   EXPECT_EQ(kQuuxExpectedMD5, md5);
    500 
    501   int64 file_size = 0;
    502   ASSERT_TRUE(base::GetFileSize(target_file, &file_size));
    503 
    504   EXPECT_EQ(file_size, listener.current_progress());
    505 }
    506 
    507 // Verifies that the asynchronous extraction to a file works.
    508 TEST_F(ZipReaderTest, ExtractToFileAsync_Directory) {
    509   MockUnzipListener listener;
    510 
    511   ZipReader reader;
    512   base::FilePath target_file = test_dir_.AppendASCII("foo");
    513   base::FilePath target_path(FILE_PATH_LITERAL("foo/"));
    514   ASSERT_TRUE(reader.Open(test_zip_file_));
    515   ASSERT_TRUE(reader.LocateAndOpenEntry(target_path));
    516   reader.ExtractCurrentEntryToFilePathAsync(
    517       target_file,
    518       base::Bind(&MockUnzipListener::OnUnzipSuccess,
    519                  listener.AsWeakPtr()),
    520       base::Bind(&MockUnzipListener::OnUnzipFailure,
    521                  listener.AsWeakPtr()),
    522       base::Bind(&MockUnzipListener::OnUnzipProgress,
    523                  listener.AsWeakPtr()));
    524 
    525   EXPECT_EQ(0, listener.success_calls());
    526   EXPECT_EQ(0, listener.failure_calls());
    527   EXPECT_EQ(0, listener.progress_calls());
    528 
    529   base::RunLoop().RunUntilIdle();
    530 
    531   EXPECT_EQ(1, listener.success_calls());
    532   EXPECT_EQ(0, listener.failure_calls());
    533   EXPECT_GE(0, listener.progress_calls());
    534 
    535   ASSERT_TRUE(base::DirectoryExists(target_file));
    536 }
    537 
    538 TEST_F(ZipReaderTest, ExtractCurrentEntryToString) {
    539   // test_mismatch_size.zip contains files with names from 0.txt to 7.txt with
    540   // sizes from 0 to 7 bytes respectively, being the contents of each file a
    541   // substring of "0123456" starting at '0'.
    542   base::FilePath test_zip_file =
    543       test_data_dir_.AppendASCII("test_mismatch_size.zip");
    544 
    545   ZipReader reader;
    546   std::string contents;
    547   ASSERT_TRUE(reader.Open(test_zip_file));
    548 
    549   for (size_t i = 0; i < 8; i++) {
    550     SCOPED_TRACE(base::StringPrintf("Processing %d.txt", static_cast<int>(i)));
    551 
    552     base::FilePath file_name = base::FilePath::FromUTF8Unsafe(
    553         base::StringPrintf("%d.txt", static_cast<int>(i)));
    554     ASSERT_TRUE(reader.LocateAndOpenEntry(file_name));
    555 
    556     if (i > 1) {
    557       // Off by one byte read limit: must fail.
    558       EXPECT_FALSE(reader.ExtractCurrentEntryToString(i - 1, &contents));
    559     }
    560 
    561     if (i > 0) {
    562       // Exact byte read limit: must pass.
    563       EXPECT_TRUE(reader.ExtractCurrentEntryToString(i, &contents));
    564       EXPECT_EQ(i, contents.size());
    565       EXPECT_EQ(0, memcmp(contents.c_str(), "0123456", i));
    566     }
    567 
    568     // More than necessary byte read limit: must pass.
    569     EXPECT_TRUE(reader.ExtractCurrentEntryToString(16, &contents));
    570     EXPECT_EQ(i, contents.size());
    571     EXPECT_EQ(0, memcmp(contents.c_str(), "0123456", i));
    572   }
    573   reader.Close();
    574 }
    575 
    576 }  // namespace zip
    577