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