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