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