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.
5 #include "third_party/zlib/google/zip_reader.h"
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"
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/gmock/include/gmock/gmock.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "testing/platform_test.h"
24 #include "third_party/zlib/google/zip_internal.h"
26 using ::testing::Return
;
31 const static std::string kQuuxExpectedMD5
= "d1ae4ac8a17a0e09317113ab284b57a6";
40 FileWrapper(const base::FilePath
& path
, AccessMode mode
) {
41 int flags
= base::File::FLAG_READ
;
42 if (mode
== READ_ONLY
)
43 flags
|= base::File::FLAG_OPEN
;
45 flags
|= base::File::FLAG_WRITE
| base::File::FLAG_CREATE_ALWAYS
;
47 file_
.Initialize(path
, flags
);
52 base::PlatformFile
platform_file() { return file_
.GetPlatformFile(); }
54 base::File
* file() { return &file_
; }
60 // A mock that provides methods that can be used as callbacks in asynchronous
61 // unzip functions. Tracks the number of calls and number of bytes reported.
62 // Assumes that progress callbacks will be executed in-order.
63 class MockUnzipListener
: public base::SupportsWeakPtr
<MockUnzipListener
> {
69 current_progress_(0) {
72 // Success callback for async functions.
73 void OnUnzipSuccess() {
77 // Failure callback for async functions.
78 void OnUnzipFailure() {
82 // Progress callback for async functions.
83 void OnUnzipProgress(int64 progress
) {
84 DCHECK(progress
> current_progress_
);
86 current_progress_
= progress
;
89 int success_calls() { return success_calls_
; }
90 int failure_calls() { return failure_calls_
; }
91 int progress_calls() { return progress_calls_
; }
92 int current_progress() { return current_progress_
; }
99 int64 current_progress_
;
102 class MockWriterDelegate
: public zip::WriterDelegate
{
104 MOCK_METHOD0(PrepareOutput
, bool());
105 MOCK_METHOD2(WriteBytes
, bool(const char*, int));
112 // Make the test a PlatformTest to setup autorelease pools properly on Mac.
113 class ZipReaderTest
: public PlatformTest
{
115 virtual void SetUp() {
116 PlatformTest::SetUp();
118 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
119 test_dir_
= temp_dir_
.path();
121 ASSERT_TRUE(GetTestDataDirectory(&test_data_dir_
));
123 test_zip_file_
= test_data_dir_
.AppendASCII("test.zip");
124 evil_zip_file_
= test_data_dir_
.AppendASCII("evil.zip");
125 evil_via_invalid_utf8_zip_file_
= test_data_dir_
.AppendASCII(
126 "evil_via_invalid_utf8.zip");
127 evil_via_absolute_file_name_zip_file_
= test_data_dir_
.AppendASCII(
128 "evil_via_absolute_file_name.zip");
130 test_zip_contents_
.insert(base::FilePath(FILE_PATH_LITERAL("foo/")));
131 test_zip_contents_
.insert(base::FilePath(FILE_PATH_LITERAL("foo/bar/")));
132 test_zip_contents_
.insert(
133 base::FilePath(FILE_PATH_LITERAL("foo/bar/baz.txt")));
134 test_zip_contents_
.insert(
135 base::FilePath(FILE_PATH_LITERAL("foo/bar/quux.txt")));
136 test_zip_contents_
.insert(
137 base::FilePath(FILE_PATH_LITERAL("foo/bar.txt")));
138 test_zip_contents_
.insert(base::FilePath(FILE_PATH_LITERAL("foo.txt")));
139 test_zip_contents_
.insert(
140 base::FilePath(FILE_PATH_LITERAL("foo/bar/.hidden")));
143 virtual void TearDown() {
144 PlatformTest::TearDown();
147 bool GetTestDataDirectory(base::FilePath
* path
) {
148 bool success
= PathService::Get(base::DIR_SOURCE_ROOT
, path
);
149 EXPECT_TRUE(success
);
152 *path
= path
->AppendASCII("third_party");
153 *path
= path
->AppendASCII("zlib");
154 *path
= path
->AppendASCII("google");
155 *path
= path
->AppendASCII("test");
156 *path
= path
->AppendASCII("data");
160 bool CompareFileAndMD5(const base::FilePath
& path
,
161 const std::string expected_md5
) {
162 // Read the output file and compute the MD5.
164 if (!base::ReadFileToString(path
, &output
))
166 const std::string md5
= base::MD5String(output
);
167 return expected_md5
== md5
;
170 // The path to temporary directory used to contain the test operations.
171 base::FilePath test_dir_
;
172 // The path to the test data directory where test.zip etc. are located.
173 base::FilePath test_data_dir_
;
174 // The path to test.zip in the test data directory.
175 base::FilePath test_zip_file_
;
176 // The path to evil.zip in the test data directory.
177 base::FilePath evil_zip_file_
;
178 // The path to evil_via_invalid_utf8.zip in the test data directory.
179 base::FilePath evil_via_invalid_utf8_zip_file_
;
180 // The path to evil_via_absolute_file_name.zip in the test data directory.
181 base::FilePath evil_via_absolute_file_name_zip_file_
;
182 std::set
<base::FilePath
> test_zip_contents_
;
184 base::ScopedTempDir temp_dir_
;
186 base::MessageLoop message_loop_
;
189 TEST_F(ZipReaderTest
, Open_ValidZipFile
) {
191 ASSERT_TRUE(reader
.Open(test_zip_file_
));
194 TEST_F(ZipReaderTest
, Open_ValidZipPlatformFile
) {
196 FileWrapper
zip_fd_wrapper(test_zip_file_
, FileWrapper::READ_ONLY
);
197 ASSERT_TRUE(reader
.OpenFromPlatformFile(zip_fd_wrapper
.platform_file()));
200 TEST_F(ZipReaderTest
, Open_NonExistentFile
) {
202 ASSERT_FALSE(reader
.Open(test_data_dir_
.AppendASCII("nonexistent.zip")));
205 TEST_F(ZipReaderTest
, Open_ExistentButNonZipFile
) {
207 ASSERT_FALSE(reader
.Open(test_data_dir_
.AppendASCII("create_test_zip.sh")));
210 // Iterate through the contents in the test zip file, and compare that the
211 // contents collected from the zip reader matches the expected contents.
212 TEST_F(ZipReaderTest
, Iteration
) {
213 std::set
<base::FilePath
> actual_contents
;
215 ASSERT_TRUE(reader
.Open(test_zip_file_
));
216 while (reader
.HasMore()) {
217 ASSERT_TRUE(reader
.OpenCurrentEntryInZip());
218 actual_contents
.insert(reader
.current_entry_info()->file_path());
219 ASSERT_TRUE(reader
.AdvanceToNextEntry());
221 EXPECT_FALSE(reader
.AdvanceToNextEntry()); // Shouldn't go further.
222 EXPECT_EQ(test_zip_contents_
.size(),
223 static_cast<size_t>(reader
.num_entries()));
224 EXPECT_EQ(test_zip_contents_
.size(), actual_contents
.size());
225 EXPECT_EQ(test_zip_contents_
, actual_contents
);
228 // Open the test zip file from a file descriptor, iterate through its contents,
229 // and compare that they match the expected contents.
230 TEST_F(ZipReaderTest
, PlatformFileIteration
) {
231 std::set
<base::FilePath
> actual_contents
;
233 FileWrapper
zip_fd_wrapper(test_zip_file_
, FileWrapper::READ_ONLY
);
234 ASSERT_TRUE(reader
.OpenFromPlatformFile(zip_fd_wrapper
.platform_file()));
235 while (reader
.HasMore()) {
236 ASSERT_TRUE(reader
.OpenCurrentEntryInZip());
237 actual_contents
.insert(reader
.current_entry_info()->file_path());
238 ASSERT_TRUE(reader
.AdvanceToNextEntry());
240 EXPECT_FALSE(reader
.AdvanceToNextEntry()); // Shouldn't go further.
241 EXPECT_EQ(test_zip_contents_
.size(),
242 static_cast<size_t>(reader
.num_entries()));
243 EXPECT_EQ(test_zip_contents_
.size(), actual_contents
.size());
244 EXPECT_EQ(test_zip_contents_
, actual_contents
);
247 TEST_F(ZipReaderTest
, LocateAndOpenEntry_ValidFile
) {
248 std::set
<base::FilePath
> actual_contents
;
250 ASSERT_TRUE(reader
.Open(test_zip_file_
));
251 base::FilePath
target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
252 ASSERT_TRUE(reader
.LocateAndOpenEntry(target_path
));
253 EXPECT_EQ(target_path
, reader
.current_entry_info()->file_path());
256 TEST_F(ZipReaderTest
, LocateAndOpenEntry_NonExistentFile
) {
257 std::set
<base::FilePath
> actual_contents
;
259 ASSERT_TRUE(reader
.Open(test_zip_file_
));
260 base::FilePath
target_path(FILE_PATH_LITERAL("nonexistent.txt"));
261 ASSERT_FALSE(reader
.LocateAndOpenEntry(target_path
));
262 EXPECT_EQ(NULL
, reader
.current_entry_info());
265 TEST_F(ZipReaderTest
, ExtractCurrentEntryToFilePath_RegularFile
) {
267 ASSERT_TRUE(reader
.Open(test_zip_file_
));
268 base::FilePath
target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
269 ASSERT_TRUE(reader
.LocateAndOpenEntry(target_path
));
270 ASSERT_TRUE(reader
.ExtractCurrentEntryToFilePath(
271 test_dir_
.AppendASCII("quux.txt")));
272 // Read the output file ans compute the MD5.
274 ASSERT_TRUE(base::ReadFileToString(test_dir_
.AppendASCII("quux.txt"),
276 const std::string md5
= base::MD5String(output
);
277 EXPECT_EQ(kQuuxExpectedMD5
, md5
);
278 // quux.txt should be larger than kZipBufSize so that we can exercise
279 // the loop in ExtractCurrentEntry().
280 EXPECT_LT(static_cast<size_t>(internal::kZipBufSize
), output
.size());
283 TEST_F(ZipReaderTest
, PlatformFileExtractCurrentEntryToFilePath_RegularFile
) {
285 FileWrapper
zip_fd_wrapper(test_zip_file_
, FileWrapper::READ_ONLY
);
286 ASSERT_TRUE(reader
.OpenFromPlatformFile(zip_fd_wrapper
.platform_file()));
287 base::FilePath
target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
288 ASSERT_TRUE(reader
.LocateAndOpenEntry(target_path
));
289 ASSERT_TRUE(reader
.ExtractCurrentEntryToFilePath(
290 test_dir_
.AppendASCII("quux.txt")));
291 // Read the output file and compute the MD5.
293 ASSERT_TRUE(base::ReadFileToString(test_dir_
.AppendASCII("quux.txt"),
295 const std::string md5
= base::MD5String(output
);
296 EXPECT_EQ(kQuuxExpectedMD5
, md5
);
297 // quux.txt should be larger than kZipBufSize so that we can exercise
298 // the loop in ExtractCurrentEntry().
299 EXPECT_LT(static_cast<size_t>(internal::kZipBufSize
), output
.size());
302 TEST_F(ZipReaderTest
, PlatformFileExtractCurrentEntryToFile_RegularFile
) {
304 FileWrapper
zip_fd_wrapper(test_zip_file_
, FileWrapper::READ_ONLY
);
305 ASSERT_TRUE(reader
.OpenFromPlatformFile(zip_fd_wrapper
.platform_file()));
306 base::FilePath
target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
307 base::FilePath out_path
= test_dir_
.AppendASCII("quux.txt");
308 FileWrapper
out_fd_w(out_path
, FileWrapper::READ_WRITE
);
309 ASSERT_TRUE(reader
.LocateAndOpenEntry(target_path
));
310 ASSERT_TRUE(reader
.ExtractCurrentEntryToFile(out_fd_w
.file()));
311 // Read the output file and compute the MD5.
313 ASSERT_TRUE(base::ReadFileToString(out_path
, &output
));
314 const std::string md5
= base::MD5String(output
);
315 EXPECT_EQ(kQuuxExpectedMD5
, md5
);
316 // quux.txt should be larger than kZipBufSize so that we can exercise
317 // the loop in ExtractCurrentEntry().
318 EXPECT_LT(static_cast<size_t>(internal::kZipBufSize
), output
.size());
321 TEST_F(ZipReaderTest
, ExtractCurrentEntryToFilePath_Directory
) {
323 ASSERT_TRUE(reader
.Open(test_zip_file_
));
324 base::FilePath
target_path(FILE_PATH_LITERAL("foo/"));
325 ASSERT_TRUE(reader
.LocateAndOpenEntry(target_path
));
326 ASSERT_TRUE(reader
.ExtractCurrentEntryToFilePath(
327 test_dir_
.AppendASCII("foo")));
328 // The directory should be created.
329 ASSERT_TRUE(base::DirectoryExists(test_dir_
.AppendASCII("foo")));
332 TEST_F(ZipReaderTest
, ExtractCurrentEntryIntoDirectory_RegularFile
) {
334 ASSERT_TRUE(reader
.Open(test_zip_file_
));
335 base::FilePath
target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
336 ASSERT_TRUE(reader
.LocateAndOpenEntry(target_path
));
337 ASSERT_TRUE(reader
.ExtractCurrentEntryIntoDirectory(test_dir_
));
338 // Sub directories should be created.
339 ASSERT_TRUE(base::DirectoryExists(test_dir_
.AppendASCII("foo/bar")));
340 // And the file should be created.
342 ASSERT_TRUE(base::ReadFileToString(
343 test_dir_
.AppendASCII("foo/bar/quux.txt"), &output
));
344 const std::string md5
= base::MD5String(output
);
345 EXPECT_EQ(kQuuxExpectedMD5
, md5
);
348 TEST_F(ZipReaderTest
, current_entry_info_RegularFile
) {
350 ASSERT_TRUE(reader
.Open(test_zip_file_
));
351 base::FilePath
target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
352 ASSERT_TRUE(reader
.LocateAndOpenEntry(target_path
));
353 ZipReader::EntryInfo
* current_entry_info
= reader
.current_entry_info();
355 EXPECT_EQ(target_path
, current_entry_info
->file_path());
356 EXPECT_EQ(13527, current_entry_info
->original_size());
358 // The expected time stamp: 2009-05-29 06:22:20
359 base::Time::Exploded exploded
= {}; // Zero-clear.
360 current_entry_info
->last_modified().LocalExplode(&exploded
);
361 EXPECT_EQ(2009, exploded
.year
);
362 EXPECT_EQ(5, exploded
.month
);
363 EXPECT_EQ(29, exploded
.day_of_month
);
364 EXPECT_EQ(6, exploded
.hour
);
365 EXPECT_EQ(22, exploded
.minute
);
366 EXPECT_EQ(20, exploded
.second
);
367 EXPECT_EQ(0, exploded
.millisecond
);
369 EXPECT_FALSE(current_entry_info
->is_unsafe());
370 EXPECT_FALSE(current_entry_info
->is_directory());
373 TEST_F(ZipReaderTest
, current_entry_info_DotDotFile
) {
375 ASSERT_TRUE(reader
.Open(evil_zip_file_
));
376 base::FilePath
target_path(FILE_PATH_LITERAL(
377 "../levilevilevilevilevilevilevilevilevilevilevilevil"));
378 ASSERT_TRUE(reader
.LocateAndOpenEntry(target_path
));
379 ZipReader::EntryInfo
* current_entry_info
= reader
.current_entry_info();
380 EXPECT_EQ(target_path
, current_entry_info
->file_path());
382 // This file is unsafe because of ".." in the file name.
383 EXPECT_TRUE(current_entry_info
->is_unsafe());
384 EXPECT_FALSE(current_entry_info
->is_directory());
387 TEST_F(ZipReaderTest
, current_entry_info_InvalidUTF8File
) {
389 ASSERT_TRUE(reader
.Open(evil_via_invalid_utf8_zip_file_
));
390 // The evil file is the 2nd file in the zip file.
391 // We cannot locate by the file name ".\x80.\\evil.txt",
392 // as FilePath may internally convert the string.
393 ASSERT_TRUE(reader
.AdvanceToNextEntry());
394 ASSERT_TRUE(reader
.OpenCurrentEntryInZip());
395 ZipReader::EntryInfo
* current_entry_info
= reader
.current_entry_info();
397 // This file is unsafe because of invalid UTF-8 in the file name.
398 EXPECT_TRUE(current_entry_info
->is_unsafe());
399 EXPECT_FALSE(current_entry_info
->is_directory());
402 TEST_F(ZipReaderTest
, current_entry_info_AbsoluteFile
) {
404 ASSERT_TRUE(reader
.Open(evil_via_absolute_file_name_zip_file_
));
405 base::FilePath
target_path(FILE_PATH_LITERAL("/evil.txt"));
406 ASSERT_TRUE(reader
.LocateAndOpenEntry(target_path
));
407 ZipReader::EntryInfo
* current_entry_info
= reader
.current_entry_info();
408 EXPECT_EQ(target_path
, current_entry_info
->file_path());
410 // This file is unsafe because of the absolute file name.
411 EXPECT_TRUE(current_entry_info
->is_unsafe());
412 EXPECT_FALSE(current_entry_info
->is_directory());
415 TEST_F(ZipReaderTest
, current_entry_info_Directory
) {
417 ASSERT_TRUE(reader
.Open(test_zip_file_
));
418 base::FilePath
target_path(FILE_PATH_LITERAL("foo/bar/"));
419 ASSERT_TRUE(reader
.LocateAndOpenEntry(target_path
));
420 ZipReader::EntryInfo
* current_entry_info
= reader
.current_entry_info();
422 EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("foo/bar/")),
423 current_entry_info
->file_path());
424 // The directory size should be zero.
425 EXPECT_EQ(0, current_entry_info
->original_size());
427 // The expected time stamp: 2009-05-31 15:49:52
428 base::Time::Exploded exploded
= {}; // Zero-clear.
429 current_entry_info
->last_modified().LocalExplode(&exploded
);
430 EXPECT_EQ(2009, exploded
.year
);
431 EXPECT_EQ(5, exploded
.month
);
432 EXPECT_EQ(31, exploded
.day_of_month
);
433 EXPECT_EQ(15, exploded
.hour
);
434 EXPECT_EQ(49, exploded
.minute
);
435 EXPECT_EQ(52, exploded
.second
);
436 EXPECT_EQ(0, exploded
.millisecond
);
438 EXPECT_FALSE(current_entry_info
->is_unsafe());
439 EXPECT_TRUE(current_entry_info
->is_directory());
442 // Verifies that the ZipReader class can extract a file from a zip archive
443 // stored in memory. This test opens a zip archive in a std::string object,
444 // extracts its content, and verifies the content is the same as the expected
446 TEST_F(ZipReaderTest
, OpenFromString
) {
447 // A zip archive consisting of one file "test.txt", which is a 16-byte text
448 // file that contains "This is a test.\n".
449 const char kTestData
[] =
450 "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\xa4\x66\x24\x41\x13\xe8"
451 "\xcb\x27\x10\x00\x00\x00\x10\x00\x00\x00\x08\x00\x1c\x00\x74\x65"
452 "\x73\x74\x2e\x74\x78\x74\x55\x54\x09\x00\x03\x34\x89\x45\x50\x34"
453 "\x89\x45\x50\x75\x78\x0b\x00\x01\x04\x8e\xf0\x00\x00\x04\x88\x13"
454 "\x00\x00\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20\x74\x65\x73\x74"
455 "\x2e\x0a\x50\x4b\x01\x02\x1e\x03\x0a\x00\x00\x00\x00\x00\xa4\x66"
456 "\x24\x41\x13\xe8\xcb\x27\x10\x00\x00\x00\x10\x00\x00\x00\x08\x00"
457 "\x18\x00\x00\x00\x00\x00\x01\x00\x00\x00\xa4\x81\x00\x00\x00\x00"
458 "\x74\x65\x73\x74\x2e\x74\x78\x74\x55\x54\x05\x00\x03\x34\x89\x45"
459 "\x50\x75\x78\x0b\x00\x01\x04\x8e\xf0\x00\x00\x04\x88\x13\x00\x00"
460 "\x50\x4b\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00\x4e\x00\x00\x00"
461 "\x52\x00\x00\x00\x00\x00";
462 std::string
data(kTestData
, arraysize(kTestData
));
464 ASSERT_TRUE(reader
.OpenFromString(data
));
465 base::FilePath
target_path(FILE_PATH_LITERAL("test.txt"));
466 ASSERT_TRUE(reader
.LocateAndOpenEntry(target_path
));
467 ASSERT_TRUE(reader
.ExtractCurrentEntryToFilePath(
468 test_dir_
.AppendASCII("test.txt")));
471 ASSERT_TRUE(base::ReadFileToString(
472 test_dir_
.AppendASCII("test.txt"), &actual
));
473 EXPECT_EQ(std::string("This is a test.\n"), actual
);
476 // Verifies that the asynchronous extraction to a file works.
477 TEST_F(ZipReaderTest
, ExtractToFileAsync_RegularFile
) {
478 MockUnzipListener listener
;
481 base::FilePath target_file
= test_dir_
.AppendASCII("quux.txt");
482 base::FilePath
target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
483 ASSERT_TRUE(reader
.Open(test_zip_file_
));
484 ASSERT_TRUE(reader
.LocateAndOpenEntry(target_path
));
485 reader
.ExtractCurrentEntryToFilePathAsync(
487 base::Bind(&MockUnzipListener::OnUnzipSuccess
,
488 listener
.AsWeakPtr()),
489 base::Bind(&MockUnzipListener::OnUnzipFailure
,
490 listener
.AsWeakPtr()),
491 base::Bind(&MockUnzipListener::OnUnzipProgress
,
492 listener
.AsWeakPtr()));
494 EXPECT_EQ(0, listener
.success_calls());
495 EXPECT_EQ(0, listener
.failure_calls());
496 EXPECT_EQ(0, listener
.progress_calls());
498 base::RunLoop().RunUntilIdle();
500 EXPECT_EQ(1, listener
.success_calls());
501 EXPECT_EQ(0, listener
.failure_calls());
502 EXPECT_LE(1, listener
.progress_calls());
505 ASSERT_TRUE(base::ReadFileToString(test_dir_
.AppendASCII("quux.txt"),
507 const std::string md5
= base::MD5String(output
);
508 EXPECT_EQ(kQuuxExpectedMD5
, md5
);
511 ASSERT_TRUE(base::GetFileSize(target_file
, &file_size
));
513 EXPECT_EQ(file_size
, listener
.current_progress());
516 // Verifies that the asynchronous extraction to a file works.
517 TEST_F(ZipReaderTest
, ExtractToFileAsync_Directory
) {
518 MockUnzipListener listener
;
521 base::FilePath target_file
= test_dir_
.AppendASCII("foo");
522 base::FilePath
target_path(FILE_PATH_LITERAL("foo/"));
523 ASSERT_TRUE(reader
.Open(test_zip_file_
));
524 ASSERT_TRUE(reader
.LocateAndOpenEntry(target_path
));
525 reader
.ExtractCurrentEntryToFilePathAsync(
527 base::Bind(&MockUnzipListener::OnUnzipSuccess
,
528 listener
.AsWeakPtr()),
529 base::Bind(&MockUnzipListener::OnUnzipFailure
,
530 listener
.AsWeakPtr()),
531 base::Bind(&MockUnzipListener::OnUnzipProgress
,
532 listener
.AsWeakPtr()));
534 EXPECT_EQ(0, listener
.success_calls());
535 EXPECT_EQ(0, listener
.failure_calls());
536 EXPECT_EQ(0, listener
.progress_calls());
538 base::RunLoop().RunUntilIdle();
540 EXPECT_EQ(1, listener
.success_calls());
541 EXPECT_EQ(0, listener
.failure_calls());
542 EXPECT_GE(0, listener
.progress_calls());
544 ASSERT_TRUE(base::DirectoryExists(target_file
));
547 TEST_F(ZipReaderTest
, ExtractCurrentEntryToString
) {
548 // test_mismatch_size.zip contains files with names from 0.txt to 7.txt with
549 // sizes from 0 to 7 bytes respectively, being the contents of each file a
550 // substring of "0123456" starting at '0'.
551 base::FilePath test_zip_file
=
552 test_data_dir_
.AppendASCII("test_mismatch_size.zip");
555 std::string contents
;
556 ASSERT_TRUE(reader
.Open(test_zip_file
));
558 for (size_t i
= 0; i
< 8; i
++) {
559 SCOPED_TRACE(base::StringPrintf("Processing %d.txt", static_cast<int>(i
)));
561 base::FilePath file_name
= base::FilePath::FromUTF8Unsafe(
562 base::StringPrintf("%d.txt", static_cast<int>(i
)));
563 ASSERT_TRUE(reader
.LocateAndOpenEntry(file_name
));
566 // Off by one byte read limit: must fail.
567 EXPECT_FALSE(reader
.ExtractCurrentEntryToString(i
- 1, &contents
));
571 // Exact byte read limit: must pass.
572 EXPECT_TRUE(reader
.ExtractCurrentEntryToString(i
, &contents
));
573 EXPECT_EQ(i
, contents
.size());
574 EXPECT_EQ(0, memcmp(contents
.c_str(), "0123456", i
));
577 // More than necessary byte read limit: must pass.
578 EXPECT_TRUE(reader
.ExtractCurrentEntryToString(16, &contents
));
579 EXPECT_EQ(i
, contents
.size());
580 EXPECT_EQ(0, memcmp(contents
.c_str(), "0123456", i
));
585 // This test exposes http://crbug.com/430959, at least on OS X
586 TEST_F(ZipReaderTest
, DISABLED_LeakDetectionTest
) {
587 for (int i
= 0; i
< 100000; ++i
) {
588 FileWrapper
zip_fd_wrapper(test_zip_file_
, FileWrapper::READ_ONLY
);
590 ASSERT_TRUE(reader
.OpenFromPlatformFile(zip_fd_wrapper
.platform_file()));
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
;
599 EXPECT_CALL(mock_writer
, PrepareOutput())
600 .WillOnce(Return(false));
602 base::FilePath
target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
605 ASSERT_TRUE(reader
.Open(test_zip_file_
));
606 ASSERT_TRUE(reader
.LocateAndOpenEntry(target_path
));
607 ASSERT_FALSE(reader
.ExtractCurrentEntry(&mock_writer
));
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
;
615 EXPECT_CALL(mock_writer
, PrepareOutput())
616 .WillOnce(Return(true));
617 EXPECT_CALL(mock_writer
, WriteBytes(_
, _
))
618 .WillOnce(Return(false));
620 base::FilePath
target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
623 ASSERT_TRUE(reader
.Open(test_zip_file_
));
624 ASSERT_TRUE(reader
.LocateAndOpenEntry(target_path
));
625 ASSERT_FALSE(reader
.ExtractCurrentEntry(&mock_writer
));
628 // Test that extraction succeeds when the writer delegate reports all is well.
629 TEST_F(ZipReaderTest
, ExtractCurrentEntrySuccess
) {
630 testing::StrictMock
<MockWriterDelegate
> mock_writer
;
632 EXPECT_CALL(mock_writer
, PrepareOutput())
633 .WillOnce(Return(true));
634 EXPECT_CALL(mock_writer
, WriteBytes(_
, _
))
635 .WillRepeatedly(Return(true));
637 base::FilePath
target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
640 ASSERT_TRUE(reader
.Open(test_zip_file_
));
641 ASSERT_TRUE(reader
.LocateAndOpenEntry(target_path
));
642 ASSERT_TRUE(reader
.ExtractCurrentEntry(&mock_writer
));
645 class FileWriterDelegateTest
: public ::testing::Test
{
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());
657 // Writes data to the file, leaving the current position at the end of the
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
));
665 base::FilePath temp_file_path_
;
669 TEST_F(FileWriterDelegateTest
, WriteToStartAndTruncate
) {
670 // Write stuff and advance.
673 // This should rewind, write, then truncate.
674 static const char kSomeData
[] = "short";
675 static const int kSomeDataLen
= sizeof(kSomeData
) - 1;
677 FileWriterDelegate
writer(&file_
);
678 ASSERT_TRUE(writer
.PrepareOutput());
679 ASSERT_TRUE(writer
.WriteBytes(kSomeData
, kSomeDataLen
));
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
));