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.
5 #include "chrome/browser/chromeos/drive/file_cache.h"
10 #include "base/file_util.h"
11 #include "base/files/file_enumerator.h"
12 #include "base/files/scoped_temp_dir.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 "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"
31 const char kCacheFileDirectory[] = "files";
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,
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 {
48 FileCacheTestOnUIThread() : expected_error_(FILE_ERROR_OK),
49 expected_cache_state_(0) {
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);
58 ASSERT_TRUE(file_util::CreateDirectory(metadata_dir));
59 ASSERT_TRUE(file_util::CreateDirectory(cache_dir));
61 ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(),
63 fake_free_disk_space_getter_.reset(new FakeFreeDiskSpaceGetter);
65 scoped_refptr<base::SequencedWorkerPool> pool =
66 content::BrowserThread::GetBlockingPool();
67 blocking_task_runner_ =
68 pool->GetSequencedTaskRunner(pool->GetSequenceToken());
70 metadata_storage_.reset(new ResourceMetadataStorage(
72 blocking_task_runner_.get()));
75 base::PostTaskAndReplyWithResult(
76 blocking_task_runner_.get(),
78 base::Bind(&ResourceMetadataStorage::Initialize,
79 base::Unretained(metadata_storage_.get())),
80 google_apis::test_util::CreateCopyResultCallback(&success));
81 test_util::RunBlockingPoolTask();
84 cache_.reset(new FileCache(
85 metadata_storage_.get(),
87 blocking_task_runner_.get(),
88 fake_free_disk_space_getter_.get()));
91 base::PostTaskAndReplyWithResult(
92 blocking_task_runner_.get(),
94 base::Bind(&FileCache::Initialize, base::Unretained(cache_.get())),
95 google_apis::test_util::CreateCopyResultCallback(&success));
96 test_util::RunBlockingPoolTask();
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;
108 FileError error = FILE_ERROR_OK;
109 base::PostTaskAndReplyWithResult(
110 blocking_task_runner_,
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();
119 if (error == FILE_ERROR_OK) {
120 FileCacheEntry cache_entry;
121 EXPECT_TRUE(GetCacheEntryFromOriginThread(id, &cache_entry));
122 EXPECT_EQ(md5, cache_entry.md5());
125 VerifyCacheFileState(error, id);
128 void TestRemoveFromCache(const std::string& id, FileError expected_error) {
129 expected_error_ = expected_error;
131 FileError error = FILE_ERROR_OK;
132 base::PostTaskAndReplyWithResult(
133 blocking_task_runner_,
135 base::Bind(&internal::FileCache::Remove,
136 base::Unretained(cache_.get()),
138 google_apis::test_util::CreateCopyResultCallback(&error));
139 test_util::RunBlockingPoolTask();
140 VerifyRemoveFromCache(error, id);
143 void VerifyRemoveFromCache(FileError error, const std::string& id) {
144 EXPECT_EQ(expected_error_, error);
146 FileCacheEntry cache_entry;
147 if (!GetCacheEntryFromOriginThread(id, &cache_entry)) {
148 EXPECT_EQ(FILE_ERROR_OK, error);
150 const base::FilePath path = cache_->GetCacheFilePath(id);
151 EXPECT_FALSE(base::PathExists(path));
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;
161 FileError error = FILE_ERROR_OK;
162 cache_->PinOnUIThread(
164 google_apis::test_util::CreateCopyResultCallback(&error));
165 test_util::RunBlockingPoolTask();
166 VerifyCacheFileState(error, id);
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;
175 FileError error = FILE_ERROR_OK;
176 cache_->UnpinOnUIThread(
178 google_apis::test_util::CreateCopyResultCallback(&error));
179 test_util::RunBlockingPoolTask();
180 VerifyCacheFileState(error, id);
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;
189 FileError error = FILE_ERROR_OK;
190 base::PostTaskAndReplyWithResult(
191 blocking_task_runner_,
193 base::Bind(&internal::FileCache::MarkDirty,
194 base::Unretained(cache_.get()),
196 google_apis::test_util::CreateCopyResultCallback(&error));
197 test_util::RunBlockingPoolTask();
199 VerifyCacheFileState(error, id);
202 if (error == FILE_ERROR_OK) {
203 base::FilePath cache_file_path;
204 base::PostTaskAndReplyWithResult(
205 blocking_task_runner_,
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();
213 EXPECT_EQ(FILE_ERROR_OK, error);
214 EXPECT_EQ(util::EscapeCacheFileName(id),
215 cache_file_path.BaseName().AsUTF8Unsafe());
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;
226 FileError error = FILE_ERROR_OK;
227 PostTaskAndReplyWithResult(
228 blocking_task_runner_.get(),
230 base::Bind(&FileCache::ClearDirty,
231 base::Unretained(cache_.get()),
234 google_apis::test_util::CreateCopyResultCallback(&error));
235 test_util::RunBlockingPoolTask();
237 if (error == FILE_ERROR_OK) {
238 FileCacheEntry cache_entry;
239 EXPECT_TRUE(GetCacheEntryFromOriginThread(id, &cache_entry));
240 EXPECT_EQ(md5, cache_entry.md5());
243 VerifyCacheFileState(error, id);
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;
252 FileCacheEntry entry;
253 EXPECT_TRUE(GetCacheEntryFromOriginThread(id, &entry));
255 FileError error = FILE_ERROR_OK;
256 base::FilePath cache_file_path;
257 cache_->MarkAsMountedOnUIThread(
259 google_apis::test_util::CreateCopyResultCallback(
260 &error, &cache_file_path));
261 test_util::RunBlockingPoolTask();
263 EXPECT_TRUE(base::PathExists(cache_file_path));
264 EXPECT_EQ(cache_file_path, cache_->GetCacheFilePath(id));
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;
274 FileError error = FILE_ERROR_OK;
275 cache_->MarkAsUnmountedOnUIThread(
277 google_apis::test_util::CreateCopyResultCallback(&error));
278 test_util::RunBlockingPoolTask();
280 base::FilePath cache_file_path;
281 base::PostTaskAndReplyWithResult(
282 blocking_task_runner_,
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);
291 EXPECT_TRUE(base::PathExists(cache_file_path));
292 EXPECT_EQ(cache_file_path, cache_->GetCacheFilePath(id));
295 void VerifyCacheFileState(FileError error, const std::string& id) {
296 EXPECT_EQ(expected_error_, error);
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());
312 EXPECT_FALSE(cache_entry_found);
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));
321 // Helper function to call GetCacheEntry from origin thread.
322 bool GetCacheEntryFromOriginThread(const std::string& id,
323 FileCacheEntry* cache_entry) {
325 base::PostTaskAndReplyWithResult(
326 blocking_task_runner_,
328 base::Bind(&internal::FileCache::GetCacheEntry,
329 base::Unretained(cache_.get()),
332 google_apis::test_util::CreateCopyResultCallback(&result));
333 test_util::RunBlockingPoolTask();
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);
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(),
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()) {
355 EXPECT_EQ(util::EscapeCacheFileName(id),
356 current.BaseName().AsUTF8Unsafe());
358 return num_files_found;
361 content::TestBrowserThreadBundle thread_bundle_;
362 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
363 base::ScopedTempDir temp_dir_;
364 base::FilePath dummy_file_path_;
366 scoped_ptr<ResourceMetadataStorage, test_util::DestroyHelperForTests>
368 scoped_ptr<FileCache, test_util::DestroyHelperForTests> cache_;
369 scoped_ptr<FakeFreeDiskSpaceGetter> fake_free_disk_space_getter_;
371 FileError expected_error_;
372 int expected_cache_state_;
373 std::string expected_file_extension_;
376 TEST_F(FileCacheTestOnUIThread, StoreToCacheSimple) {
377 std::string id("pdf:1a2b");
378 std::string md5("abcdef0123456789");
380 // Store an existing file.
381 TestStoreToCache(id, md5, dummy_file_path_,
382 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
384 // Store a non-existent file to the same |id| and |md5|.
385 TestStoreToCache(id, md5,
386 base::FilePath::FromUTF8Unsafe("non_existent_file"),
388 TEST_CACHE_STATE_PRESENT);
390 // Store a different existing file to the same |id| but different
393 TestStoreToCache(id, md5, dummy_file_path_,
394 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
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));
401 TEST_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);
408 // Then try to remove existing file from cache.
409 TestRemoveFromCache(id, FILE_ERROR_OK);
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);
417 TestRemoveFromCache(id, FILE_ERROR_OK);
420 TEST_F(FileCacheTestOnUIThread, PinAndUnpin) {
421 std::string id("pdf:1a2b");
422 std::string md5("abcdef0123456789");
424 // First store a file to cache.
425 TestStoreToCache(id, md5, dummy_file_path_,
426 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
428 // Pin the existing file in cache.
429 TestPin(id, FILE_ERROR_OK,
430 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
432 // Unpin the existing file in cache.
433 TestUnpin(id, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
435 // Pin back the same existing file in cache.
436 TestPin(id, FILE_ERROR_OK,
437 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
439 // Pin a non-existent file in cache.
440 id = "document:1a2b";
442 TestPin(id, FILE_ERROR_OK, TEST_CACHE_STATE_PINNED);
444 // Unpin the previously pinned non-existent file in cache.
445 TestUnpin(id, FILE_ERROR_OK, TEST_CACHE_STATE_NONE);
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";
451 TestUnpin(id, FILE_ERROR_NOT_FOUND, TEST_CACHE_STATE_NONE);
454 TEST_F(FileCacheTestOnUIThread, StoreToCachePinned) {
455 std::string id("pdf:1a2b");
456 std::string md5("abcdef0123456789");
458 // Pin a non-existent file.
459 TestPin(id, FILE_ERROR_OK, TEST_CACHE_STATE_PINNED);
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);
465 // Store a non-existent file to a previously pinned and stored file.
466 TestStoreToCache(id, md5,
467 base::FilePath::FromUTF8Unsafe("non_existent_file"),
469 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
472 TEST_F(FileCacheTestOnUIThread, RemoveFromCachePinned) {
473 std::string id("pdf:1a2b");
474 std::string md5("abcdef0123456789");
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);
482 // Remove |id| from cache.
483 TestRemoveFromCache(id, FILE_ERROR_OK);
485 // Use non-alphanumeric characters for ID, including '.'
486 // which is an extension separator.
487 id = "pdf:`~!@#$%^&*()-_=+[{|]}\\;',<.>/?";
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);
494 TestRemoveFromCache(id, FILE_ERROR_OK);
497 TEST_F(FileCacheTestOnUIThread, DirtyCacheSimple) {
498 std::string id("pdf:1a2b");
499 std::string md5("abcdef0123456789");
501 // First store a file to cache.
502 TestStoreToCache(id, md5, dummy_file_path_,
503 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
505 // Mark the file dirty.
506 TestMarkDirty(id, FILE_ERROR_OK,
507 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY);
509 // Clear dirty state of the file.
510 TestClearDirty(id, md5, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
513 TEST_F(FileCacheTestOnUIThread, DirtyCachePinned) {
514 std::string id("pdf:1a2b");
515 std::string md5("abcdef0123456789");
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);
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);
529 // Clear dirty state of the file.
530 TestClearDirty(id, md5, FILE_ERROR_OK,
531 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
534 TEST_F(FileCacheTestOnUIThread, PinAndUnpinDirtyCache) {
535 std::string id("pdf:1a2b");
536 std::string md5("abcdef0123456789");
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);
544 // Verifies dirty file exists.
545 base::FilePath dirty_path;
546 FileError error = FILE_ERROR_FAILED;
547 base::PostTaskAndReplyWithResult(
548 blocking_task_runner_,
550 base::Bind(&FileCache::GetFile,
551 base::Unretained(cache_.get()),
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));
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);
564 // Verify dirty file still exist at the same pathname.
565 EXPECT_TRUE(base::PathExists(dirty_path));
567 // Unpin the dirty file.
568 TestUnpin(id, FILE_ERROR_OK,
569 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY);
571 // Verify dirty file still exist at the same pathname.
572 EXPECT_TRUE(base::PathExists(dirty_path));
575 TEST_F(FileCacheTestOnUIThread, DirtyCacheRepetitive) {
576 std::string id("pdf:1a2b");
577 std::string md5("abcdef0123456789");
579 // First store a file to cache.
580 TestStoreToCache(id, md5, dummy_file_path_,
581 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
583 // Mark the file dirty.
584 TestMarkDirty(id, FILE_ERROR_OK,
585 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY);
587 // Again, mark the file dirty. Nothing should change.
588 TestMarkDirty(id, FILE_ERROR_OK,
589 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY);
591 // Clear dirty state of the file.
592 TestClearDirty(id, md5, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
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);
599 TEST_F(FileCacheTestOnUIThread, DirtyCacheInvalid) {
600 std::string id("pdf:1a2b");
601 std::string md5("abcdef0123456789");
603 // Mark a non-existent file dirty.
604 TestMarkDirty(id, FILE_ERROR_NOT_FOUND, TEST_CACHE_STATE_NONE);
606 // Clear dirty state of a non-existent file.
607 TestClearDirty(id, md5, FILE_ERROR_NOT_FOUND, TEST_CACHE_STATE_NONE);
609 // Store a file to cache.
610 TestStoreToCache(id, md5, dummy_file_path_,
611 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
613 // Clear dirty state of a non-dirty existing file.
614 TestClearDirty(id, md5, FILE_ERROR_INVALID_OPERATION,
615 TEST_CACHE_STATE_PRESENT);
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);
622 TestStoreToCache(id, md5, dummy_file_path_, FILE_ERROR_IN_USE,
623 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY);
626 TEST_F(FileCacheTestOnUIThread, RemoveFromDirtyCache) {
627 std::string id("pdf:1a2b");
628 std::string md5("abcdef0123456789");
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);
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);
647 TEST_F(FileCacheTestOnUIThread, MountUnmount) {
648 std::string id("pdf:1a2b");
649 std::string md5("abcdef0123456789");
651 // First store a file to cache.
652 TestStoreToCache(id, md5, dummy_file_path_,
653 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
655 // Mark the file mounted.
656 TestMarkAsMounted(id, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
657 EXPECT_TRUE(CacheEntryExists(id));
659 // Try to remove the file.
660 TestRemoveFromCache(id, FILE_ERROR_IN_USE);
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_,
668 base::Bind(&FileCache::GetFile,
669 base::Unretained(cache_.get()),
671 google_apis::test_util::CreateCopyResultCallback(&error));
672 test_util::RunBlockingPoolTask();
673 EXPECT_EQ(FILE_ERROR_OK, error);
675 TestMarkAsUnmounted(id, file_path, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
676 EXPECT_TRUE(CacheEntryExists(id));
678 // Try to remove the file.
679 TestRemoveFromCache(id, FILE_ERROR_OK);
682 TEST_F(FileCacheTestOnUIThread, StoreToCacheNoSpace) {
683 fake_free_disk_space_getter_->set_default_value(0);
685 std::string id("pdf:1a2b");
686 std::string md5("abcdef0123456789");
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);
693 // Verify that there's no files added.
694 EXPECT_EQ(0U, CountCacheFiles(id, md5));
697 TEST_F(FileCacheTestOnUIThread, UpdatePinnedCache) {
698 std::string id("pdf:1a2b");
699 std::string md5("abcdef0123456789");
700 std::string md5_modified("aaaaaa0000000000");
702 // Store an existing file.
703 TestStoreToCache(id, md5, dummy_file_path_, FILE_ERROR_OK,
704 TEST_CACHE_STATE_PRESENT);
707 TestPin(id, FILE_ERROR_OK,
708 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
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);
715 // Tests FileCache methods working with the blocking task runner.
716 class FileCacheTest : public testing::Test {
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);
723 ASSERT_TRUE(file_util::CreateDirectory(metadata_dir));
724 ASSERT_TRUE(file_util::CreateDirectory(cache_files_dir_));
726 fake_free_disk_space_getter_.reset(new FakeFreeDiskSpaceGetter);
728 metadata_storage_.reset(new ResourceMetadataStorage(
730 base::MessageLoopProxy::current().get()));
731 ASSERT_TRUE(metadata_storage_->Initialize());
733 cache_.reset(new FileCache(
734 metadata_storage_.get(),
736 base::MessageLoopProxy::current().get(),
737 fake_free_disk_space_getter_.get()));
738 ASSERT_TRUE(cache_->Initialize());
741 static bool RenameCacheFilesToNewFormat(FileCache* cache) {
742 return cache->RenameCacheFilesToNewFormat();
745 content::TestBrowserThreadBundle thread_bundle_;
746 base::ScopedTempDir temp_dir_;
747 base::FilePath cache_files_dir_;
749 scoped_ptr<ResourceMetadataStorage, test_util::DestroyHelperForTests>
751 scoped_ptr<FileCache, test_util::DestroyHelperForTests> cache_;
752 scoped_ptr<FakeFreeDiskSpaceGetter> fake_free_disk_space_getter_;
755 TEST_F(FileCacheTest, RecoverFilesFromCacheDirectory) {
756 base::FilePath dir_source_root;
757 EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &dir_source_root));
758 const base::FilePath src_path =
759 dir_source_root.AppendASCII("chrome/test/data/chromeos/drive/image.png");
761 // Store files. This file should not be moved.
762 EXPECT_EQ(FILE_ERROR_OK, cache_->Store("id_foo", "md5", src_path,
763 FileCache::FILE_OPERATION_COPY));
765 // Set up files in the cache directory. These files should be moved.
766 const base::FilePath file_directory =
767 temp_dir_.path().AppendASCII(kCacheFileDirectory);
768 ASSERT_TRUE(base::CopyFile(src_path, file_directory.AppendASCII("id_bar")));
769 ASSERT_TRUE(base::CopyFile(src_path, file_directory.AppendASCII("id_baz")));
771 // Insert a dirty entry with "id_baz" to |recovered_cache_info|.
772 // This should not prevent the file from being recovered.
773 ResourceMetadataStorage::RecoveredCacheInfoMap recovered_cache_info;
774 recovered_cache_info["id_baz"].is_dirty = true;
775 recovered_cache_info["id_baz"].title = "baz.png";
778 const base::FilePath dest_directory = temp_dir_.path().AppendASCII("dest");
779 EXPECT_TRUE(cache_->RecoverFilesFromCacheDirectory(dest_directory,
780 recovered_cache_info));
782 // Only two files should be recovered.
783 EXPECT_TRUE(base::PathExists(dest_directory));
784 // base::FileEnumerator does not guarantee the order.
785 if (base::PathExists(dest_directory.AppendASCII("baz00000001.png"))) {
786 EXPECT_TRUE(base::ContentsEqual(
788 dest_directory.AppendASCII("baz00000001.png")));
789 EXPECT_TRUE(base::ContentsEqual(
791 dest_directory.AppendASCII("image00000002.png")));
793 EXPECT_TRUE(base::ContentsEqual(
795 dest_directory.AppendASCII("image00000001.png")));
796 EXPECT_TRUE(base::ContentsEqual(
798 dest_directory.AppendASCII("baz00000002.png")));
800 EXPECT_FALSE(base::PathExists(
801 dest_directory.AppendASCII("image00000003.png")));
804 TEST_F(FileCacheTest, Iterator) {
805 base::FilePath src_file;
806 ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(), &src_file));
809 std::map<std::string, std::string> md5s;
810 md5s["id1"] = "md5-1";
811 md5s["id2"] = "md5-2";
812 md5s["id3"] = "md5-3";
813 md5s["id4"] = "md5-4";
814 for (std::map<std::string, std::string>::iterator it = md5s.begin();
815 it != md5s.end(); ++it) {
816 EXPECT_EQ(FILE_ERROR_OK, cache_->Store(
817 it->first, it->second, src_file, FileCache::FILE_OPERATION_COPY));
821 std::map<std::string, std::string> result;
822 scoped_ptr<FileCache::Iterator> it = cache_->GetIterator();
823 for (; !it->IsAtEnd(); it->Advance())
824 result[it->GetID()] = it->GetValue().md5();
825 EXPECT_EQ(md5s, result);
826 EXPECT_FALSE(it->HasError());
829 TEST_F(FileCacheTest, FreeDiskSpaceIfNeededFor) {
830 base::FilePath src_file;
831 ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(), &src_file));
833 // Store a file as a 'temporary' file and remember the path.
834 const std::string id_tmp = "id_tmp", md5_tmp = "md5_tmp";
835 ASSERT_EQ(FILE_ERROR_OK,
836 cache_->Store(id_tmp, md5_tmp, src_file,
837 FileCache::FILE_OPERATION_COPY));
838 base::FilePath tmp_path;
839 ASSERT_EQ(FILE_ERROR_OK, cache_->GetFile(id_tmp, &tmp_path));
841 // Store a file as a pinned file and remember the path.
842 const std::string id_pinned = "id_pinned", md5_pinned = "md5_pinned";
843 ASSERT_EQ(FILE_ERROR_OK,
844 cache_->Store(id_pinned, md5_pinned, src_file,
845 FileCache::FILE_OPERATION_COPY));
846 ASSERT_EQ(FILE_ERROR_OK, cache_->Pin(id_pinned));
847 base::FilePath pinned_path;
848 ASSERT_EQ(FILE_ERROR_OK, cache_->GetFile(id_pinned, &pinned_path));
850 // Call FreeDiskSpaceIfNeededFor().
851 fake_free_disk_space_getter_->set_default_value(test_util::kLotsOfSpace);
852 fake_free_disk_space_getter_->PushFakeValue(0);
853 const int64 kNeededBytes = 1;
854 EXPECT_TRUE(cache_->FreeDiskSpaceIfNeededFor(kNeededBytes));
856 // Only 'temporary' file gets removed.
857 FileCacheEntry entry;
858 EXPECT_FALSE(cache_->GetCacheEntry(id_tmp, &entry));
859 EXPECT_FALSE(base::PathExists(tmp_path));
861 EXPECT_TRUE(cache_->GetCacheEntry(id_pinned, &entry));
862 EXPECT_TRUE(base::PathExists(pinned_path));
864 // Returns false when disk space cannot be freed.
865 fake_free_disk_space_getter_->set_default_value(0);
866 EXPECT_FALSE(cache_->FreeDiskSpaceIfNeededFor(kNeededBytes));
869 TEST_F(FileCacheTest, GetFile) {
870 const base::FilePath src_file_path = temp_dir_.path().Append("test.dat");
871 const std::string src_contents = "test";
872 EXPECT_TRUE(google_apis::test_util::WriteStringToFile(src_file_path,
874 std::string id("id1");
875 std::string md5(base::MD5String(src_contents));
877 const base::FilePath cache_file_directory =
878 temp_dir_.path().AppendASCII(kCacheFileDirectory);
880 // Try to get an existing file from cache.
881 EXPECT_EQ(FILE_ERROR_OK, cache_->Store(id, md5, src_file_path,
882 FileCache::FILE_OPERATION_COPY));
883 base::FilePath cache_file_path;
884 EXPECT_EQ(FILE_ERROR_OK, cache_->GetFile(id, &cache_file_path));
886 cache_file_directory.AppendASCII(util::EscapeCacheFileName(id)).value(),
887 cache_file_path.value());
889 std::string contents;
890 EXPECT_TRUE(base::ReadFileToString(cache_file_path, &contents));
891 EXPECT_EQ(src_contents, contents);
893 // Get file from cache with different id.
895 EXPECT_EQ(FILE_ERROR_NOT_FOUND, cache_->GetFile(id, &cache_file_path));
897 // Pin a non-existent file.
898 EXPECT_EQ(FILE_ERROR_OK, cache_->Pin(id));
900 // Get the non-existent pinned file from cache.
901 EXPECT_EQ(FILE_ERROR_NOT_FOUND, cache_->GetFile(id, &cache_file_path));
903 // Get a previously pinned and stored file from cache.
904 EXPECT_EQ(FILE_ERROR_OK, cache_->Store(id, md5, src_file_path,
905 FileCache::FILE_OPERATION_COPY));
907 EXPECT_EQ(FILE_ERROR_OK, cache_->GetFile(id, &cache_file_path));
909 cache_file_directory.AppendASCII(util::EscapeCacheFileName(id)).value(),
910 cache_file_path.value());
913 EXPECT_TRUE(base::ReadFileToString(cache_file_path, &contents));
914 EXPECT_EQ(src_contents, contents);
917 TEST_F(FileCacheTest, RenameCacheFilesToNewFormat) {
918 const base::FilePath file_directory =
919 temp_dir_.path().AppendASCII(kCacheFileDirectory);
921 // File with an old style "<prefix>:<ID>.<MD5>" name.
922 ASSERT_TRUE(google_apis::test_util::WriteStringToFile(
923 file_directory.AppendASCII("file:id_koo.md5"), "koo"));
925 // File with multiple extensions should be removed.
926 ASSERT_TRUE(google_apis::test_util::WriteStringToFile(
927 file_directory.AppendASCII("id_kyu.md5.mounted"), "kyu (mounted)"));
928 ASSERT_TRUE(google_apis::test_util::WriteStringToFile(
929 file_directory.AppendASCII("id_kyu.md5"), "kyu"));
931 // Rename and verify the result.
932 EXPECT_TRUE(RenameCacheFilesToNewFormat(cache_.get()));
933 std::string contents;
934 EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_koo"),
936 EXPECT_EQ("koo", contents);
938 EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_kyu"),
940 EXPECT_EQ("kyu", contents);
943 EXPECT_TRUE(RenameCacheFilesToNewFormat(cache_.get()));
945 // Files with new style names are not affected.
947 EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_koo"),
949 EXPECT_EQ("koo", contents);
951 EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_kyu"),
953 EXPECT_EQ("kyu", contents);
956 TEST_F(FileCacheTest, ClearAll) {
957 const std::string id("pdf:1a2b");
958 const std::string md5("abcdef0123456789");
960 // Store an existing file.
961 base::FilePath src_file;
962 ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(), &src_file));
963 ASSERT_EQ(FILE_ERROR_OK,
964 cache_->Store(id, md5, src_file, FileCache::FILE_OPERATION_COPY));
966 // Verify that the cache entry is created.
967 FileCacheEntry cache_entry;
968 ASSERT_TRUE(cache_->GetCacheEntry(id, &cache_entry));
971 EXPECT_TRUE(cache_->ClearAll());
973 // Verify that the cache is removed.
974 EXPECT_FALSE(cache_->GetCacheEntry(id, &cache_entry));
975 EXPECT_TRUE(file_util::IsDirectoryEmpty(cache_files_dir_));
978 } // namespace internal