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
25namespace {
26
27const static std::string kQuuxExpectedMD5 = "d1ae4ac8a17a0e09317113ab284b57a6";
28
29class 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.
57class 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
98namespace zip {
99
100// Make the test a PlatformTest to setup autorelease pools properly on Mac.
101class 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
177TEST_F(ZipReaderTest, Open_ValidZipFile) {
178  ZipReader reader;
179  ASSERT_TRUE(reader.Open(test_zip_file_));
180}
181
182TEST_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
188TEST_F(ZipReaderTest, Open_NonExistentFile) {
189  ZipReader reader;
190  ASSERT_FALSE(reader.Open(test_data_dir_.AppendASCII("nonexistent.zip")));
191}
192
193TEST_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.
200TEST_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.
218TEST_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
235TEST_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
244TEST_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
253TEST_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
271TEST_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)
291TEST_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
312TEST_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
323TEST_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
339TEST_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
364TEST_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
378TEST_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
393TEST_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
406TEST_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.
437TEST_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.
468TEST_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.
508TEST_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
538TEST_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