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