1 // Copyright (c) 2012 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 "chrome/browser/chromeos/drive/file_cache.h" 6 7 #include <string> 8 #include <vector> 9 10 #include "base/file_util.h" 11 #include "base/files/file_enumerator.h" 12 #include "base/files/scoped_temp_dir.h" 13 #include "base/md5.h" 14 #include "base/path_service.h" 15 #include "base/run_loop.h" 16 #include "base/threading/sequenced_worker_pool.h" 17 #include "chrome/browser/chromeos/drive/drive.pb.h" 18 #include "chrome/browser/chromeos/drive/fake_free_disk_space_getter.h" 19 #include "chrome/browser/chromeos/drive/file_system_util.h" 20 #include "chrome/browser/chromeos/drive/resource_metadata_storage.h" 21 #include "chrome/browser/chromeos/drive/test_util.h" 22 #include "content/public/browser/browser_thread.h" 23 #include "content/public/test/test_browser_thread_bundle.h" 24 #include "google_apis/drive/test_util.h" 25 #include "testing/gtest/include/gtest/gtest.h" 26 27 namespace drive { 28 namespace internal { 29 namespace { 30 31 const char kCacheFileDirectory[] = "files"; 32 33 // Bitmask of cache states in FileCacheEntry. 34 enum TestFileCacheState { 35 TEST_CACHE_STATE_NONE = 0, 36 TEST_CACHE_STATE_PINNED = 1 << 0, 37 TEST_CACHE_STATE_PRESENT = 1 << 1, 38 TEST_CACHE_STATE_DIRTY = 1 << 2, 39 }; 40 41 } // namespace 42 43 // Tests FileCache methods from UI thread. It internally uses a real blocking 44 // pool and tests the interaction among threads. 45 // TODO(hashimoto): remove this class. crbug.com/231221. 46 class FileCacheTestOnUIThread : public testing::Test { 47 protected: 48 FileCacheTestOnUIThread() : expected_error_(FILE_ERROR_OK), 49 expected_cache_state_(0) { 50 } 51 52 virtual void SetUp() OVERRIDE { 53 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 54 const base::FilePath metadata_dir = temp_dir_.path().AppendASCII("meta"); 55 const base::FilePath cache_dir = 56 temp_dir_.path().AppendASCII(kCacheFileDirectory); 57 58 ASSERT_TRUE(base::CreateDirectory(metadata_dir)); 59 ASSERT_TRUE(base::CreateDirectory(cache_dir)); 60 61 ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), 62 &dummy_file_path_)); 63 fake_free_disk_space_getter_.reset(new FakeFreeDiskSpaceGetter); 64 65 scoped_refptr<base::SequencedWorkerPool> pool = 66 content::BrowserThread::GetBlockingPool(); 67 blocking_task_runner_ = 68 pool->GetSequencedTaskRunner(pool->GetSequenceToken()); 69 70 metadata_storage_.reset(new ResourceMetadataStorage( 71 metadata_dir, 72 blocking_task_runner_.get())); 73 74 bool success = false; 75 base::PostTaskAndReplyWithResult( 76 blocking_task_runner_.get(), 77 FROM_HERE, 78 base::Bind(&ResourceMetadataStorage::Initialize, 79 base::Unretained(metadata_storage_.get())), 80 google_apis::test_util::CreateCopyResultCallback(&success)); 81 test_util::RunBlockingPoolTask(); 82 ASSERT_TRUE(success); 83 84 cache_.reset(new FileCache( 85 metadata_storage_.get(), 86 cache_dir, 87 blocking_task_runner_.get(), 88 fake_free_disk_space_getter_.get())); 89 90 success = false; 91 base::PostTaskAndReplyWithResult( 92 blocking_task_runner_.get(), 93 FROM_HERE, 94 base::Bind(&FileCache::Initialize, base::Unretained(cache_.get())), 95 google_apis::test_util::CreateCopyResultCallback(&success)); 96 test_util::RunBlockingPoolTask(); 97 ASSERT_TRUE(success); 98 } 99 100 void TestStoreToCache(const std::string& id, 101 const std::string& md5, 102 const base::FilePath& source_path, 103 FileError expected_error, 104 int expected_cache_state) { 105 expected_error_ = expected_error; 106 expected_cache_state_ = expected_cache_state; 107 108 FileError error = FILE_ERROR_OK; 109 base::PostTaskAndReplyWithResult( 110 blocking_task_runner_, 111 FROM_HERE, 112 base::Bind(&internal::FileCache::Store, 113 base::Unretained(cache_.get()), 114 id, md5, source_path, 115 FileCache::FILE_OPERATION_COPY), 116 google_apis::test_util::CreateCopyResultCallback(&error)); 117 test_util::RunBlockingPoolTask(); 118 119 if (error == FILE_ERROR_OK) { 120 FileCacheEntry cache_entry; 121 EXPECT_TRUE(GetCacheEntryFromOriginThread(id, &cache_entry)); 122 EXPECT_EQ(md5, cache_entry.md5()); 123 } 124 125 VerifyCacheFileState(error, id); 126 } 127 128 void TestRemoveFromCache(const std::string& id, FileError expected_error) { 129 expected_error_ = expected_error; 130 131 FileError error = FILE_ERROR_OK; 132 base::PostTaskAndReplyWithResult( 133 blocking_task_runner_, 134 FROM_HERE, 135 base::Bind(&internal::FileCache::Remove, 136 base::Unretained(cache_.get()), 137 id), 138 google_apis::test_util::CreateCopyResultCallback(&error)); 139 test_util::RunBlockingPoolTask(); 140 VerifyRemoveFromCache(error, id); 141 } 142 143 void VerifyRemoveFromCache(FileError error, const std::string& id) { 144 EXPECT_EQ(expected_error_, error); 145 146 FileCacheEntry cache_entry; 147 if (!GetCacheEntryFromOriginThread(id, &cache_entry)) { 148 EXPECT_EQ(FILE_ERROR_OK, error); 149 150 const base::FilePath path = cache_->GetCacheFilePath(id); 151 EXPECT_FALSE(base::PathExists(path)); 152 } 153 } 154 155 void TestPin(const std::string& id, 156 FileError expected_error, 157 int expected_cache_state) { 158 expected_error_ = expected_error; 159 expected_cache_state_ = expected_cache_state; 160 161 FileError error = FILE_ERROR_OK; 162 base::PostTaskAndReplyWithResult( 163 blocking_task_runner_, 164 FROM_HERE, 165 base::Bind(&internal::FileCache::Pin, 166 base::Unretained(cache_.get()), 167 id), 168 google_apis::test_util::CreateCopyResultCallback(&error)); 169 test_util::RunBlockingPoolTask(); 170 VerifyCacheFileState(error, id); 171 } 172 173 void TestUnpin(const std::string& id, 174 FileError expected_error, 175 int expected_cache_state) { 176 expected_error_ = expected_error; 177 expected_cache_state_ = expected_cache_state; 178 179 FileError error = FILE_ERROR_OK; 180 base::PostTaskAndReplyWithResult( 181 blocking_task_runner_, 182 FROM_HERE, 183 base::Bind(&internal::FileCache::Unpin, 184 base::Unretained(cache_.get()), 185 id), 186 google_apis::test_util::CreateCopyResultCallback(&error)); 187 test_util::RunBlockingPoolTask(); 188 VerifyCacheFileState(error, id); 189 } 190 191 void TestMarkDirty(const std::string& id, 192 FileError expected_error, 193 int expected_cache_state) { 194 expected_error_ = expected_error; 195 expected_cache_state_ = expected_cache_state; 196 197 FileError error = FILE_ERROR_OK; 198 base::PostTaskAndReplyWithResult( 199 blocking_task_runner_, 200 FROM_HERE, 201 base::Bind(&internal::FileCache::MarkDirty, 202 base::Unretained(cache_.get()), 203 id), 204 google_apis::test_util::CreateCopyResultCallback(&error)); 205 test_util::RunBlockingPoolTask(); 206 207 VerifyCacheFileState(error, id); 208 209 // Verify filename. 210 if (error == FILE_ERROR_OK) { 211 base::FilePath cache_file_path; 212 base::PostTaskAndReplyWithResult( 213 blocking_task_runner_, 214 FROM_HERE, 215 base::Bind(&FileCache::GetFile, 216 base::Unretained(cache_.get()), 217 id, &cache_file_path), 218 google_apis::test_util::CreateCopyResultCallback(&error)); 219 test_util::RunBlockingPoolTask(); 220 221 EXPECT_EQ(FILE_ERROR_OK, error); 222 EXPECT_EQ(util::EscapeCacheFileName(id), 223 cache_file_path.BaseName().AsUTF8Unsafe()); 224 } 225 } 226 227 void TestClearDirty(const std::string& id, 228 const std::string& md5, 229 FileError expected_error, 230 int expected_cache_state) { 231 expected_error_ = expected_error; 232 expected_cache_state_ = expected_cache_state; 233 234 FileError error = FILE_ERROR_OK; 235 base::PostTaskAndReplyWithResult( 236 blocking_task_runner_.get(), 237 FROM_HERE, 238 base::Bind(&FileCache::ClearDirty, 239 base::Unretained(cache_.get()), 240 id, 241 md5), 242 google_apis::test_util::CreateCopyResultCallback(&error)); 243 test_util::RunBlockingPoolTask(); 244 245 if (error == FILE_ERROR_OK) { 246 FileCacheEntry cache_entry; 247 EXPECT_TRUE(GetCacheEntryFromOriginThread(id, &cache_entry)); 248 EXPECT_EQ(md5, cache_entry.md5()); 249 } 250 251 VerifyCacheFileState(error, id); 252 } 253 254 void TestMarkAsMounted(const std::string& id, 255 FileError expected_error, 256 int expected_cache_state) { 257 expected_error_ = expected_error; 258 expected_cache_state_ = expected_cache_state; 259 260 FileCacheEntry entry; 261 EXPECT_TRUE(GetCacheEntryFromOriginThread(id, &entry)); 262 263 FileError error = FILE_ERROR_OK; 264 base::FilePath cache_file_path; 265 266 base::PostTaskAndReplyWithResult( 267 blocking_task_runner_.get(), 268 FROM_HERE, 269 base::Bind(&FileCache::MarkAsMounted, 270 base::Unretained(cache_.get()), 271 id, 272 &cache_file_path), 273 google_apis::test_util::CreateCopyResultCallback(&error)); 274 test_util::RunBlockingPoolTask(); 275 276 EXPECT_TRUE(base::PathExists(cache_file_path)); 277 EXPECT_EQ(cache_file_path, cache_->GetCacheFilePath(id)); 278 } 279 280 void TestMarkAsUnmounted(const std::string& id, 281 const base::FilePath& file_path, 282 FileError expected_error, 283 int expected_cache_state) { 284 expected_error_ = expected_error; 285 expected_cache_state_ = expected_cache_state; 286 287 FileError error = FILE_ERROR_OK; 288 base::PostTaskAndReplyWithResult( 289 blocking_task_runner_.get(), 290 FROM_HERE, 291 base::Bind(&FileCache::MarkAsUnmounted, 292 base::Unretained(cache_.get()), 293 file_path), 294 google_apis::test_util::CreateCopyResultCallback(&error)); 295 test_util::RunBlockingPoolTask(); 296 297 base::FilePath cache_file_path; 298 base::PostTaskAndReplyWithResult( 299 blocking_task_runner_, 300 FROM_HERE, 301 base::Bind(&FileCache::GetFile, 302 base::Unretained(cache_.get()), 303 id, &cache_file_path), 304 google_apis::test_util::CreateCopyResultCallback(&error)); 305 test_util::RunBlockingPoolTask(); 306 EXPECT_EQ(FILE_ERROR_OK, error); 307 308 EXPECT_TRUE(base::PathExists(cache_file_path)); 309 EXPECT_EQ(cache_file_path, cache_->GetCacheFilePath(id)); 310 } 311 312 void VerifyCacheFileState(FileError error, const std::string& id) { 313 EXPECT_EQ(expected_error_, error); 314 315 // Verify cache map. 316 FileCacheEntry cache_entry; 317 const bool cache_entry_found = 318 GetCacheEntryFromOriginThread(id, &cache_entry); 319 if ((expected_cache_state_ & TEST_CACHE_STATE_PRESENT) || 320 (expected_cache_state_ & TEST_CACHE_STATE_PINNED)) { 321 ASSERT_TRUE(cache_entry_found); 322 EXPECT_EQ((expected_cache_state_ & TEST_CACHE_STATE_PINNED) != 0, 323 cache_entry.is_pinned()); 324 EXPECT_EQ((expected_cache_state_ & TEST_CACHE_STATE_PRESENT) != 0, 325 cache_entry.is_present()); 326 EXPECT_EQ((expected_cache_state_ & TEST_CACHE_STATE_DIRTY) != 0, 327 cache_entry.is_dirty()); 328 } else { 329 EXPECT_FALSE(cache_entry_found); 330 } 331 332 // Verify actual cache file. 333 base::FilePath dest_path = cache_->GetCacheFilePath(id); 334 EXPECT_EQ((expected_cache_state_ & TEST_CACHE_STATE_PRESENT) != 0, 335 base::PathExists(dest_path)); 336 } 337 338 // Helper function to call GetCacheEntry from origin thread. 339 bool GetCacheEntryFromOriginThread(const std::string& id, 340 FileCacheEntry* cache_entry) { 341 bool result = false; 342 base::PostTaskAndReplyWithResult( 343 blocking_task_runner_, 344 FROM_HERE, 345 base::Bind(&internal::FileCache::GetCacheEntry, 346 base::Unretained(cache_.get()), 347 id, 348 cache_entry), 349 google_apis::test_util::CreateCopyResultCallback(&result)); 350 test_util::RunBlockingPoolTask(); 351 return result; 352 } 353 354 // Returns true if the cache entry exists for the given ID. 355 bool CacheEntryExists(const std::string& id) { 356 FileCacheEntry cache_entry; 357 return GetCacheEntryFromOriginThread(id, &cache_entry); 358 } 359 360 // Returns the number of the cache files with name <id>, and Confirm 361 // that they have the <md5>. This should return 1 or 0. 362 size_t CountCacheFiles(const std::string& id, const std::string& md5) { 363 base::FilePath path = cache_->GetCacheFilePath(id); 364 base::FileEnumerator enumerator(path.DirName(), 365 false, // recursive 366 base::FileEnumerator::FILES, 367 path.BaseName().value()); 368 size_t num_files_found = 0; 369 for (base::FilePath current = enumerator.Next(); !current.empty(); 370 current = enumerator.Next()) { 371 ++num_files_found; 372 EXPECT_EQ(util::EscapeCacheFileName(id), 373 current.BaseName().AsUTF8Unsafe()); 374 } 375 return num_files_found; 376 } 377 378 content::TestBrowserThreadBundle thread_bundle_; 379 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_; 380 base::ScopedTempDir temp_dir_; 381 base::FilePath dummy_file_path_; 382 383 scoped_ptr<ResourceMetadataStorage, test_util::DestroyHelperForTests> 384 metadata_storage_; 385 scoped_ptr<FileCache, test_util::DestroyHelperForTests> cache_; 386 scoped_ptr<FakeFreeDiskSpaceGetter> fake_free_disk_space_getter_; 387 388 FileError expected_error_; 389 int expected_cache_state_; 390 std::string expected_file_extension_; 391 }; 392 393 TEST_F(FileCacheTestOnUIThread, StoreToCacheSimple) { 394 std::string id("pdf:1a2b"); 395 std::string md5("abcdef0123456789"); 396 397 // Store an existing file. 398 TestStoreToCache(id, md5, dummy_file_path_, 399 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT); 400 401 // Store a non-existent file to the same |id| and |md5|. 402 TestStoreToCache(id, md5, 403 base::FilePath::FromUTF8Unsafe("non_existent_file"), 404 FILE_ERROR_FAILED, 405 TEST_CACHE_STATE_PRESENT); 406 407 // Store a different existing file to the same |id| but different 408 // |md5|. 409 md5 = "new_md5"; 410 TestStoreToCache(id, md5, dummy_file_path_, 411 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT); 412 413 // Verify that there's only one file with name <id>, i.e. previously 414 // cached file with the different md5 should be deleted. 415 EXPECT_EQ(1U, CountCacheFiles(id, md5)); 416 } 417 418 TEST_F(FileCacheTestOnUIThread, RemoveFromCacheSimple) { 419 std::string id("pdf:1a2b"); 420 std::string md5("abcdef0123456789"); 421 // First store a file to cache. 422 TestStoreToCache(id, md5, dummy_file_path_, 423 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT); 424 425 // Then try to remove existing file from cache. 426 TestRemoveFromCache(id, FILE_ERROR_OK); 427 428 // Repeat using non-alphanumeric characters for ID, including '.' 429 // which is an extension separator. 430 id = "pdf:`~!@#$%^&*()-_=+[{|]}\\;',<.>/?"; 431 TestStoreToCache(id, md5, dummy_file_path_, 432 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT); 433 434 TestRemoveFromCache(id, FILE_ERROR_OK); 435 } 436 437 TEST_F(FileCacheTestOnUIThread, PinAndUnpin) { 438 std::string id("pdf:1a2b"); 439 std::string md5("abcdef0123456789"); 440 441 // First store a file to cache. 442 TestStoreToCache(id, md5, dummy_file_path_, 443 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT); 444 445 // Pin the existing file in cache. 446 TestPin(id, FILE_ERROR_OK, 447 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED); 448 449 // Unpin the existing file in cache. 450 TestUnpin(id, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT); 451 452 // Pin back the same existing file in cache. 453 TestPin(id, FILE_ERROR_OK, 454 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED); 455 456 // Pin a non-existent file in cache. 457 id = "document:1a2b"; 458 459 TestPin(id, FILE_ERROR_OK, TEST_CACHE_STATE_PINNED); 460 461 // Unpin the previously pinned non-existent file in cache. 462 TestUnpin(id, FILE_ERROR_OK, TEST_CACHE_STATE_NONE); 463 464 // Unpin a file that doesn't exist in cache and is not pinned, i.e. cache 465 // has zero knowledge of the file. 466 id = "not-in-cache:1a2b"; 467 468 TestUnpin(id, FILE_ERROR_NOT_FOUND, TEST_CACHE_STATE_NONE); 469 } 470 471 TEST_F(FileCacheTestOnUIThread, StoreToCachePinned) { 472 std::string id("pdf:1a2b"); 473 std::string md5("abcdef0123456789"); 474 475 // Pin a non-existent file. 476 TestPin(id, FILE_ERROR_OK, TEST_CACHE_STATE_PINNED); 477 478 // Store an existing file to a previously pinned file. 479 TestStoreToCache(id, md5, dummy_file_path_, FILE_ERROR_OK, 480 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED); 481 482 // Store a non-existent file to a previously pinned and stored file. 483 TestStoreToCache(id, md5, 484 base::FilePath::FromUTF8Unsafe("non_existent_file"), 485 FILE_ERROR_FAILED, 486 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED); 487 } 488 489 TEST_F(FileCacheTestOnUIThread, RemoveFromCachePinned) { 490 std::string id("pdf:1a2b"); 491 std::string md5("abcdef0123456789"); 492 493 // Store a file to cache, and pin it. 494 TestStoreToCache(id, md5, dummy_file_path_, 495 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT); 496 TestPin(id, FILE_ERROR_OK, 497 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED); 498 499 // Remove |id| from cache. 500 TestRemoveFromCache(id, FILE_ERROR_OK); 501 502 // Use non-alphanumeric characters for ID, including '.' 503 // which is an extension separator. 504 id = "pdf:`~!@#$%^&*()-_=+[{|]}\\;',<.>/?"; 505 506 TestStoreToCache(id, md5, dummy_file_path_, 507 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT); 508 TestPin(id, FILE_ERROR_OK, 509 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED); 510 511 TestRemoveFromCache(id, FILE_ERROR_OK); 512 } 513 514 TEST_F(FileCacheTestOnUIThread, DirtyCacheSimple) { 515 std::string id("pdf:1a2b"); 516 std::string md5("abcdef0123456789"); 517 518 // First store a file to cache. 519 TestStoreToCache(id, md5, dummy_file_path_, 520 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT); 521 522 // Mark the file dirty. 523 TestMarkDirty(id, FILE_ERROR_OK, 524 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY); 525 526 // Clear dirty state of the file. 527 TestClearDirty(id, md5, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT); 528 } 529 530 TEST_F(FileCacheTestOnUIThread, DirtyCachePinned) { 531 std::string id("pdf:1a2b"); 532 std::string md5("abcdef0123456789"); 533 534 // First store a file to cache and pin it. 535 TestStoreToCache(id, md5, dummy_file_path_, 536 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT); 537 TestPin(id, FILE_ERROR_OK, 538 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED); 539 540 // Mark the file dirty. 541 TestMarkDirty(id, FILE_ERROR_OK, 542 TEST_CACHE_STATE_PRESENT | 543 TEST_CACHE_STATE_DIRTY | 544 TEST_CACHE_STATE_PINNED); 545 546 // Clear dirty state of the file. 547 TestClearDirty(id, md5, FILE_ERROR_OK, 548 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED); 549 } 550 551 TEST_F(FileCacheTestOnUIThread, PinAndUnpinDirtyCache) { 552 std::string id("pdf:1a2b"); 553 std::string md5("abcdef0123456789"); 554 555 // First store a file to cache and mark it as dirty. 556 TestStoreToCache(id, md5, dummy_file_path_, 557 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT); 558 TestMarkDirty(id, FILE_ERROR_OK, 559 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY); 560 561 // Verifies dirty file exists. 562 base::FilePath dirty_path; 563 FileError error = FILE_ERROR_FAILED; 564 base::PostTaskAndReplyWithResult( 565 blocking_task_runner_, 566 FROM_HERE, 567 base::Bind(&FileCache::GetFile, 568 base::Unretained(cache_.get()), 569 id, &dirty_path), 570 google_apis::test_util::CreateCopyResultCallback(&error)); 571 test_util::RunBlockingPoolTask(); 572 EXPECT_EQ(FILE_ERROR_OK, error); 573 EXPECT_TRUE(base::PathExists(dirty_path)); 574 575 // Pin the dirty file. 576 TestPin(id, FILE_ERROR_OK, 577 TEST_CACHE_STATE_PRESENT | 578 TEST_CACHE_STATE_DIRTY | 579 TEST_CACHE_STATE_PINNED); 580 581 // Verify dirty file still exist at the same pathname. 582 EXPECT_TRUE(base::PathExists(dirty_path)); 583 584 // Unpin the dirty file. 585 TestUnpin(id, FILE_ERROR_OK, 586 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY); 587 588 // Verify dirty file still exist at the same pathname. 589 EXPECT_TRUE(base::PathExists(dirty_path)); 590 } 591 592 TEST_F(FileCacheTestOnUIThread, DirtyCacheRepetitive) { 593 std::string id("pdf:1a2b"); 594 std::string md5("abcdef0123456789"); 595 596 // First store a file to cache. 597 TestStoreToCache(id, md5, dummy_file_path_, 598 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT); 599 600 // Mark the file dirty. 601 TestMarkDirty(id, FILE_ERROR_OK, 602 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY); 603 604 // Again, mark the file dirty. Nothing should change. 605 TestMarkDirty(id, FILE_ERROR_OK, 606 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY); 607 608 // Clear dirty state of the file. 609 TestClearDirty(id, md5, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT); 610 611 // Again, clear dirty state of the file, which is no longer dirty. 612 TestClearDirty(id, md5, FILE_ERROR_INVALID_OPERATION, 613 TEST_CACHE_STATE_PRESENT); 614 } 615 616 TEST_F(FileCacheTestOnUIThread, DirtyCacheInvalid) { 617 std::string id("pdf:1a2b"); 618 std::string md5("abcdef0123456789"); 619 620 // Mark a non-existent file dirty. 621 TestMarkDirty(id, FILE_ERROR_NOT_FOUND, TEST_CACHE_STATE_NONE); 622 623 // Clear dirty state of a non-existent file. 624 TestClearDirty(id, md5, FILE_ERROR_NOT_FOUND, TEST_CACHE_STATE_NONE); 625 626 // Store a file to cache. 627 TestStoreToCache(id, md5, dummy_file_path_, 628 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT); 629 630 // Clear dirty state of a non-dirty existing file. 631 TestClearDirty(id, md5, FILE_ERROR_INVALID_OPERATION, 632 TEST_CACHE_STATE_PRESENT); 633 634 // Mark an existing file dirty, then store a new file to the same ID 635 // but different md5, which should fail. 636 TestMarkDirty(id, FILE_ERROR_OK, 637 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY); 638 md5 = "new_md5"; 639 TestStoreToCache(id, md5, dummy_file_path_, FILE_ERROR_IN_USE, 640 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY); 641 } 642 643 TEST_F(FileCacheTestOnUIThread, RemoveFromDirtyCache) { 644 std::string id("pdf:1a2b"); 645 std::string md5("abcdef0123456789"); 646 647 // Store a file to cache, pin it, mark it dirty and commit it. 648 TestStoreToCache(id, md5, dummy_file_path_, 649 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT); 650 TestPin(id, FILE_ERROR_OK, 651 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED); 652 TestMarkDirty(id, FILE_ERROR_OK, 653 TEST_CACHE_STATE_PRESENT | 654 TEST_CACHE_STATE_PINNED | 655 TEST_CACHE_STATE_DIRTY); 656 657 // Try to remove the file. Dirty caches can be removed at the level of 658 // FileCache::Remove. Upper layer cache clearance functions like 659 // FreeDiskSpaceIfNeededFor() and RemoveStaleCacheFiles() takes care of 660 // securing dirty files. 661 TestRemoveFromCache(id, FILE_ERROR_OK); 662 } 663 664 TEST_F(FileCacheTestOnUIThread, MountUnmount) { 665 std::string id("pdf:1a2b"); 666 std::string md5("abcdef0123456789"); 667 668 // First store a file to cache. 669 TestStoreToCache(id, md5, dummy_file_path_, 670 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT); 671 672 // Mark the file mounted. 673 TestMarkAsMounted(id, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT); 674 EXPECT_TRUE(CacheEntryExists(id)); 675 676 // Try to remove the file. 677 TestRemoveFromCache(id, FILE_ERROR_IN_USE); 678 679 // Clear mounted state of the file. 680 base::FilePath file_path; 681 FileError error = FILE_ERROR_FAILED; 682 base::PostTaskAndReplyWithResult( 683 blocking_task_runner_, 684 FROM_HERE, 685 base::Bind(&FileCache::GetFile, 686 base::Unretained(cache_.get()), 687 id, &file_path), 688 google_apis::test_util::CreateCopyResultCallback(&error)); 689 test_util::RunBlockingPoolTask(); 690 EXPECT_EQ(FILE_ERROR_OK, error); 691 692 TestMarkAsUnmounted(id, file_path, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT); 693 EXPECT_TRUE(CacheEntryExists(id)); 694 695 // Try to remove the file. 696 TestRemoveFromCache(id, FILE_ERROR_OK); 697 } 698 699 TEST_F(FileCacheTestOnUIThread, StoreToCacheNoSpace) { 700 fake_free_disk_space_getter_->set_default_value(0); 701 702 std::string id("pdf:1a2b"); 703 std::string md5("abcdef0123456789"); 704 705 // Try to store an existing file. 706 TestStoreToCache(id, md5, dummy_file_path_, 707 FILE_ERROR_NO_LOCAL_SPACE, 708 TEST_CACHE_STATE_NONE); 709 710 // Verify that there's no files added. 711 EXPECT_EQ(0U, CountCacheFiles(id, md5)); 712 } 713 714 TEST_F(FileCacheTestOnUIThread, UpdatePinnedCache) { 715 std::string id("pdf:1a2b"); 716 std::string md5("abcdef0123456789"); 717 std::string md5_modified("aaaaaa0000000000"); 718 719 // Store an existing file. 720 TestStoreToCache(id, md5, dummy_file_path_, FILE_ERROR_OK, 721 TEST_CACHE_STATE_PRESENT); 722 723 // Pin the file. 724 TestPin(id, FILE_ERROR_OK, 725 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED); 726 727 // Store the file with a modified content and md5. It should stay pinned. 728 TestStoreToCache(id, md5_modified, dummy_file_path_, FILE_ERROR_OK, 729 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED); 730 } 731 732 // Tests FileCache methods working with the blocking task runner. 733 class FileCacheTest : public testing::Test { 734 protected: 735 virtual void SetUp() OVERRIDE { 736 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 737 const base::FilePath metadata_dir = temp_dir_.path().AppendASCII("meta"); 738 cache_files_dir_ = temp_dir_.path().AppendASCII(kCacheFileDirectory); 739 740 ASSERT_TRUE(base::CreateDirectory(metadata_dir)); 741 ASSERT_TRUE(base::CreateDirectory(cache_files_dir_)); 742 743 fake_free_disk_space_getter_.reset(new FakeFreeDiskSpaceGetter); 744 745 metadata_storage_.reset(new ResourceMetadataStorage( 746 metadata_dir, 747 base::MessageLoopProxy::current().get())); 748 ASSERT_TRUE(metadata_storage_->Initialize()); 749 750 cache_.reset(new FileCache( 751 metadata_storage_.get(), 752 cache_files_dir_, 753 base::MessageLoopProxy::current().get(), 754 fake_free_disk_space_getter_.get())); 755 ASSERT_TRUE(cache_->Initialize()); 756 } 757 758 static bool RenameCacheFilesToNewFormat(FileCache* cache) { 759 return cache->RenameCacheFilesToNewFormat(); 760 } 761 762 content::TestBrowserThreadBundle thread_bundle_; 763 base::ScopedTempDir temp_dir_; 764 base::FilePath cache_files_dir_; 765 766 scoped_ptr<ResourceMetadataStorage, test_util::DestroyHelperForTests> 767 metadata_storage_; 768 scoped_ptr<FileCache, test_util::DestroyHelperForTests> cache_; 769 scoped_ptr<FakeFreeDiskSpaceGetter> fake_free_disk_space_getter_; 770 }; 771 772 TEST_F(FileCacheTest, RecoverFilesFromCacheDirectory) { 773 base::FilePath dir_source_root; 774 EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &dir_source_root)); 775 const base::FilePath src_path = 776 dir_source_root.AppendASCII("chrome/test/data/chromeos/drive/image.png"); 777 778 // Store files. This file should not be moved. 779 EXPECT_EQ(FILE_ERROR_OK, cache_->Store("id_foo", "md5", src_path, 780 FileCache::FILE_OPERATION_COPY)); 781 782 // Set up files in the cache directory. These files should be moved. 783 const base::FilePath file_directory = 784 temp_dir_.path().AppendASCII(kCacheFileDirectory); 785 ASSERT_TRUE(base::CopyFile(src_path, file_directory.AppendASCII("id_bar"))); 786 ASSERT_TRUE(base::CopyFile(src_path, file_directory.AppendASCII("id_baz"))); 787 788 // Insert a dirty entry with "id_baz" to |recovered_cache_info|. 789 // This should not prevent the file from being recovered. 790 ResourceMetadataStorage::RecoveredCacheInfoMap recovered_cache_info; 791 recovered_cache_info["id_baz"].is_dirty = true; 792 recovered_cache_info["id_baz"].title = "baz.png"; 793 794 // Recover files. 795 const base::FilePath dest_directory = temp_dir_.path().AppendASCII("dest"); 796 EXPECT_TRUE(cache_->RecoverFilesFromCacheDirectory(dest_directory, 797 recovered_cache_info)); 798 799 // Only two files should be recovered. 800 EXPECT_TRUE(base::PathExists(dest_directory)); 801 // base::FileEnumerator does not guarantee the order. 802 if (base::PathExists(dest_directory.AppendASCII("baz00000001.png"))) { 803 EXPECT_TRUE(base::ContentsEqual( 804 src_path, 805 dest_directory.AppendASCII("baz00000001.png"))); 806 EXPECT_TRUE(base::ContentsEqual( 807 src_path, 808 dest_directory.AppendASCII("image00000002.png"))); 809 } else { 810 EXPECT_TRUE(base::ContentsEqual( 811 src_path, 812 dest_directory.AppendASCII("image00000001.png"))); 813 EXPECT_TRUE(base::ContentsEqual( 814 src_path, 815 dest_directory.AppendASCII("baz00000002.png"))); 816 } 817 EXPECT_FALSE(base::PathExists( 818 dest_directory.AppendASCII("image00000003.png"))); 819 } 820 821 TEST_F(FileCacheTest, Iterator) { 822 base::FilePath src_file; 823 ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &src_file)); 824 825 // Prepare entries. 826 std::map<std::string, std::string> md5s; 827 md5s["id1"] = "md5-1"; 828 md5s["id2"] = "md5-2"; 829 md5s["id3"] = "md5-3"; 830 md5s["id4"] = "md5-4"; 831 for (std::map<std::string, std::string>::iterator it = md5s.begin(); 832 it != md5s.end(); ++it) { 833 EXPECT_EQ(FILE_ERROR_OK, cache_->Store( 834 it->first, it->second, src_file, FileCache::FILE_OPERATION_COPY)); 835 } 836 837 // Iterate. 838 std::map<std::string, std::string> result; 839 scoped_ptr<FileCache::Iterator> it = cache_->GetIterator(); 840 for (; !it->IsAtEnd(); it->Advance()) 841 result[it->GetID()] = it->GetValue().md5(); 842 EXPECT_EQ(md5s, result); 843 EXPECT_FALSE(it->HasError()); 844 } 845 846 TEST_F(FileCacheTest, FreeDiskSpaceIfNeededFor) { 847 base::FilePath src_file; 848 ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &src_file)); 849 850 // Store a file as a 'temporary' file and remember the path. 851 const std::string id_tmp = "id_tmp", md5_tmp = "md5_tmp"; 852 ASSERT_EQ(FILE_ERROR_OK, 853 cache_->Store(id_tmp, md5_tmp, src_file, 854 FileCache::FILE_OPERATION_COPY)); 855 base::FilePath tmp_path; 856 ASSERT_EQ(FILE_ERROR_OK, cache_->GetFile(id_tmp, &tmp_path)); 857 858 // Store a file as a pinned file and remember the path. 859 const std::string id_pinned = "id_pinned", md5_pinned = "md5_pinned"; 860 ASSERT_EQ(FILE_ERROR_OK, 861 cache_->Store(id_pinned, md5_pinned, src_file, 862 FileCache::FILE_OPERATION_COPY)); 863 ASSERT_EQ(FILE_ERROR_OK, cache_->Pin(id_pinned)); 864 base::FilePath pinned_path; 865 ASSERT_EQ(FILE_ERROR_OK, cache_->GetFile(id_pinned, &pinned_path)); 866 867 // Call FreeDiskSpaceIfNeededFor(). 868 fake_free_disk_space_getter_->set_default_value(test_util::kLotsOfSpace); 869 fake_free_disk_space_getter_->PushFakeValue(0); 870 const int64 kNeededBytes = 1; 871 EXPECT_TRUE(cache_->FreeDiskSpaceIfNeededFor(kNeededBytes)); 872 873 // Only 'temporary' file gets removed. 874 FileCacheEntry entry; 875 EXPECT_FALSE(cache_->GetCacheEntry(id_tmp, &entry)); 876 EXPECT_FALSE(base::PathExists(tmp_path)); 877 878 EXPECT_TRUE(cache_->GetCacheEntry(id_pinned, &entry)); 879 EXPECT_TRUE(base::PathExists(pinned_path)); 880 881 // Returns false when disk space cannot be freed. 882 fake_free_disk_space_getter_->set_default_value(0); 883 EXPECT_FALSE(cache_->FreeDiskSpaceIfNeededFor(kNeededBytes)); 884 } 885 886 TEST_F(FileCacheTest, GetFile) { 887 const base::FilePath src_file_path = temp_dir_.path().Append("test.dat"); 888 const std::string src_contents = "test"; 889 EXPECT_TRUE(google_apis::test_util::WriteStringToFile(src_file_path, 890 src_contents)); 891 std::string id("id1"); 892 std::string md5(base::MD5String(src_contents)); 893 894 const base::FilePath cache_file_directory = 895 temp_dir_.path().AppendASCII(kCacheFileDirectory); 896 897 // Try to get an existing file from cache. 898 EXPECT_EQ(FILE_ERROR_OK, cache_->Store(id, md5, src_file_path, 899 FileCache::FILE_OPERATION_COPY)); 900 base::FilePath cache_file_path; 901 EXPECT_EQ(FILE_ERROR_OK, cache_->GetFile(id, &cache_file_path)); 902 EXPECT_EQ( 903 cache_file_directory.AppendASCII(util::EscapeCacheFileName(id)).value(), 904 cache_file_path.value()); 905 906 std::string contents; 907 EXPECT_TRUE(base::ReadFileToString(cache_file_path, &contents)); 908 EXPECT_EQ(src_contents, contents); 909 910 // Get file from cache with different id. 911 id = "id2"; 912 EXPECT_EQ(FILE_ERROR_NOT_FOUND, cache_->GetFile(id, &cache_file_path)); 913 914 // Pin a non-existent file. 915 EXPECT_EQ(FILE_ERROR_OK, cache_->Pin(id)); 916 917 // Get the non-existent pinned file from cache. 918 EXPECT_EQ(FILE_ERROR_NOT_FOUND, cache_->GetFile(id, &cache_file_path)); 919 920 // Get a previously pinned and stored file from cache. 921 EXPECT_EQ(FILE_ERROR_OK, cache_->Store(id, md5, src_file_path, 922 FileCache::FILE_OPERATION_COPY)); 923 924 EXPECT_EQ(FILE_ERROR_OK, cache_->GetFile(id, &cache_file_path)); 925 EXPECT_EQ( 926 cache_file_directory.AppendASCII(util::EscapeCacheFileName(id)).value(), 927 cache_file_path.value()); 928 929 contents.clear(); 930 EXPECT_TRUE(base::ReadFileToString(cache_file_path, &contents)); 931 EXPECT_EQ(src_contents, contents); 932 } 933 934 TEST_F(FileCacheTest, RenameCacheFilesToNewFormat) { 935 const base::FilePath file_directory = 936 temp_dir_.path().AppendASCII(kCacheFileDirectory); 937 938 // File with an old style "<prefix>:<ID>.<MD5>" name. 939 ASSERT_TRUE(google_apis::test_util::WriteStringToFile( 940 file_directory.AppendASCII("file:id_koo.md5"), "koo")); 941 942 // File with multiple extensions should be removed. 943 ASSERT_TRUE(google_apis::test_util::WriteStringToFile( 944 file_directory.AppendASCII("id_kyu.md5.mounted"), "kyu (mounted)")); 945 ASSERT_TRUE(google_apis::test_util::WriteStringToFile( 946 file_directory.AppendASCII("id_kyu.md5"), "kyu")); 947 948 // Rename and verify the result. 949 EXPECT_TRUE(RenameCacheFilesToNewFormat(cache_.get())); 950 std::string contents; 951 EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_koo"), 952 &contents)); 953 EXPECT_EQ("koo", contents); 954 contents.clear(); 955 EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_kyu"), 956 &contents)); 957 EXPECT_EQ("kyu", contents); 958 959 // Rename again. 960 EXPECT_TRUE(RenameCacheFilesToNewFormat(cache_.get())); 961 962 // Files with new style names are not affected. 963 contents.clear(); 964 EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_koo"), 965 &contents)); 966 EXPECT_EQ("koo", contents); 967 contents.clear(); 968 EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_kyu"), 969 &contents)); 970 EXPECT_EQ("kyu", contents); 971 } 972 973 TEST_F(FileCacheTest, ClearAll) { 974 const std::string id("pdf:1a2b"); 975 const std::string md5("abcdef0123456789"); 976 977 // Store an existing file. 978 base::FilePath src_file; 979 ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &src_file)); 980 ASSERT_EQ(FILE_ERROR_OK, 981 cache_->Store(id, md5, src_file, FileCache::FILE_OPERATION_COPY)); 982 983 // Verify that the cache entry is created. 984 FileCacheEntry cache_entry; 985 ASSERT_TRUE(cache_->GetCacheEntry(id, &cache_entry)); 986 987 // Clear cache. 988 EXPECT_TRUE(cache_->ClearAll()); 989 990 // Verify that the cache is removed. 991 EXPECT_FALSE(cache_->GetCacheEntry(id, &cache_entry)); 992 EXPECT_TRUE(base::IsDirectoryEmpty(cache_files_dir_)); 993 } 994 995 } // namespace internal 996 } // namespace drive 997