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