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/callback_helpers.h"
11 #include "base/file_util.h"
12 #include "base/files/file_enumerator.h"
13 #include "base/files/scoped_temp_dir.h"
15 #include "base/path_service.h"
16 #include "base/run_loop.h"
17 #include "base/threading/sequenced_worker_pool.h"
18 #include "chrome/browser/chromeos/drive/drive.pb.h"
19 #include "chrome/browser/chromeos/drive/fake_free_disk_space_getter.h"
20 #include "chrome/browser/chromeos/drive/file_system_util.h"
21 #include "chrome/browser/chromeos/drive/resource_metadata_storage.h"
22 #include "chrome/browser/chromeos/drive/test_util.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/test/test_browser_thread_bundle.h"
25 #include "google_apis/drive/test_util.h"
26 #include "testing/gtest/include/gtest/gtest.h"
32 const char kCacheFileDirectory[] = "files";
34 // Bitmask of cache states in FileCacheEntry.
35 enum TestFileCacheState {
36 TEST_CACHE_STATE_NONE = 0,
37 TEST_CACHE_STATE_PINNED = 1 << 0,
38 TEST_CACHE_STATE_PRESENT = 1 << 1,
39 TEST_CACHE_STATE_DIRTY = 1 << 2,
44 // Tests FileCache methods from UI thread. It internally uses a real blocking
45 // pool and tests the interaction among threads.
46 // TODO(hashimoto): remove this class. crbug.com/231221.
47 class FileCacheTestOnUIThread : public testing::Test {
49 FileCacheTestOnUIThread() : expected_error_(FILE_ERROR_OK),
50 expected_cache_state_(0) {
53 virtual void SetUp() OVERRIDE {
54 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
55 const base::FilePath metadata_dir = temp_dir_.path().AppendASCII("meta");
56 const base::FilePath cache_dir =
57 temp_dir_.path().AppendASCII(kCacheFileDirectory);
59 ASSERT_TRUE(base::CreateDirectory(metadata_dir));
60 ASSERT_TRUE(base::CreateDirectory(cache_dir));
62 ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(),
64 fake_free_disk_space_getter_.reset(new FakeFreeDiskSpaceGetter);
66 scoped_refptr<base::SequencedWorkerPool> pool =
67 content::BrowserThread::GetBlockingPool();
68 blocking_task_runner_ =
69 pool->GetSequencedTaskRunner(pool->GetSequenceToken());
71 metadata_storage_.reset(new ResourceMetadataStorage(
73 blocking_task_runner_.get()));
76 base::PostTaskAndReplyWithResult(
77 blocking_task_runner_.get(),
79 base::Bind(&ResourceMetadataStorage::Initialize,
80 base::Unretained(metadata_storage_.get())),
81 google_apis::test_util::CreateCopyResultCallback(&success));
82 test_util::RunBlockingPoolTask();
85 cache_.reset(new FileCache(
86 metadata_storage_.get(),
88 blocking_task_runner_.get(),
89 fake_free_disk_space_getter_.get()));
92 base::PostTaskAndReplyWithResult(
93 blocking_task_runner_.get(),
95 base::Bind(&FileCache::Initialize, base::Unretained(cache_.get())),
96 google_apis::test_util::CreateCopyResultCallback(&success));
97 test_util::RunBlockingPoolTask();
101 void TestStoreToCache(const std::string& id,
102 const std::string& md5,
103 const base::FilePath& source_path,
104 FileError expected_error,
105 int expected_cache_state) {
106 expected_error_ = expected_error;
107 expected_cache_state_ = expected_cache_state;
109 FileError error = FILE_ERROR_OK;
110 base::PostTaskAndReplyWithResult(
111 blocking_task_runner_,
113 base::Bind(&internal::FileCache::Store,
114 base::Unretained(cache_.get()),
115 id, md5, source_path,
116 FileCache::FILE_OPERATION_COPY),
117 google_apis::test_util::CreateCopyResultCallback(&error));
118 test_util::RunBlockingPoolTask();
120 if (error == FILE_ERROR_OK) {
121 FileCacheEntry cache_entry;
122 EXPECT_TRUE(GetCacheEntryFromOriginThread(id, &cache_entry));
123 EXPECT_EQ(md5, cache_entry.md5());
126 VerifyCacheFileState(error, id);
129 void TestRemoveFromCache(const std::string& id, FileError expected_error) {
130 expected_error_ = expected_error;
132 FileError error = FILE_ERROR_OK;
133 base::PostTaskAndReplyWithResult(
134 blocking_task_runner_,
136 base::Bind(&internal::FileCache::Remove,
137 base::Unretained(cache_.get()),
139 google_apis::test_util::CreateCopyResultCallback(&error));
140 test_util::RunBlockingPoolTask();
141 VerifyRemoveFromCache(error, id);
144 void VerifyRemoveFromCache(FileError error, const std::string& id) {
145 EXPECT_EQ(expected_error_, error);
147 FileCacheEntry cache_entry;
148 if (!GetCacheEntryFromOriginThread(id, &cache_entry)) {
149 EXPECT_EQ(FILE_ERROR_OK, error);
151 const base::FilePath path = cache_->GetCacheFilePath(id);
152 EXPECT_FALSE(base::PathExists(path));
156 void TestPin(const std::string& id,
157 FileError expected_error,
158 int expected_cache_state) {
159 expected_error_ = expected_error;
160 expected_cache_state_ = expected_cache_state;
162 FileError error = FILE_ERROR_OK;
163 base::PostTaskAndReplyWithResult(
164 blocking_task_runner_,
166 base::Bind(&internal::FileCache::Pin,
167 base::Unretained(cache_.get()),
169 google_apis::test_util::CreateCopyResultCallback(&error));
170 test_util::RunBlockingPoolTask();
171 VerifyCacheFileState(error, id);
174 void TestUnpin(const std::string& id,
175 FileError expected_error,
176 int expected_cache_state) {
177 expected_error_ = expected_error;
178 expected_cache_state_ = expected_cache_state;
180 FileError error = FILE_ERROR_OK;
181 base::PostTaskAndReplyWithResult(
182 blocking_task_runner_,
184 base::Bind(&internal::FileCache::Unpin,
185 base::Unretained(cache_.get()),
187 google_apis::test_util::CreateCopyResultCallback(&error));
188 test_util::RunBlockingPoolTask();
189 VerifyCacheFileState(error, id);
192 void TestMarkAsMounted(const std::string& id,
193 FileError expected_error,
194 int expected_cache_state) {
195 expected_error_ = expected_error;
196 expected_cache_state_ = expected_cache_state;
198 FileCacheEntry entry;
199 EXPECT_TRUE(GetCacheEntryFromOriginThread(id, &entry));
201 FileError error = FILE_ERROR_OK;
202 base::FilePath cache_file_path;
204 base::PostTaskAndReplyWithResult(
205 blocking_task_runner_.get(),
207 base::Bind(&FileCache::MarkAsMounted,
208 base::Unretained(cache_.get()),
211 google_apis::test_util::CreateCopyResultCallback(&error));
212 test_util::RunBlockingPoolTask();
214 EXPECT_TRUE(base::PathExists(cache_file_path));
215 EXPECT_EQ(cache_file_path, cache_->GetCacheFilePath(id));
218 void TestMarkAsUnmounted(const std::string& id,
219 const base::FilePath& file_path,
220 FileError expected_error,
221 int expected_cache_state) {
222 expected_error_ = expected_error;
223 expected_cache_state_ = expected_cache_state;
225 FileError error = FILE_ERROR_OK;
226 base::PostTaskAndReplyWithResult(
227 blocking_task_runner_.get(),
229 base::Bind(&FileCache::MarkAsUnmounted,
230 base::Unretained(cache_.get()),
232 google_apis::test_util::CreateCopyResultCallback(&error));
233 test_util::RunBlockingPoolTask();
235 base::FilePath cache_file_path;
236 base::PostTaskAndReplyWithResult(
237 blocking_task_runner_,
239 base::Bind(&FileCache::GetFile,
240 base::Unretained(cache_.get()),
241 id, &cache_file_path),
242 google_apis::test_util::CreateCopyResultCallback(&error));
243 test_util::RunBlockingPoolTask();
244 EXPECT_EQ(FILE_ERROR_OK, error);
246 EXPECT_TRUE(base::PathExists(cache_file_path));
247 EXPECT_EQ(cache_file_path, cache_->GetCacheFilePath(id));
250 void VerifyCacheFileState(FileError error, const std::string& id) {
251 EXPECT_EQ(expected_error_, error);
254 FileCacheEntry cache_entry;
255 const bool cache_entry_found =
256 GetCacheEntryFromOriginThread(id, &cache_entry);
257 if ((expected_cache_state_ & TEST_CACHE_STATE_PRESENT) ||
258 (expected_cache_state_ & TEST_CACHE_STATE_PINNED)) {
259 ASSERT_TRUE(cache_entry_found);
260 EXPECT_EQ((expected_cache_state_ & TEST_CACHE_STATE_PINNED) != 0,
261 cache_entry.is_pinned());
262 EXPECT_EQ((expected_cache_state_ & TEST_CACHE_STATE_PRESENT) != 0,
263 cache_entry.is_present());
264 EXPECT_EQ((expected_cache_state_ & TEST_CACHE_STATE_DIRTY) != 0,
265 cache_entry.is_dirty());
267 EXPECT_FALSE(cache_entry_found);
270 // Verify actual cache file.
271 base::FilePath dest_path = cache_->GetCacheFilePath(id);
272 EXPECT_EQ((expected_cache_state_ & TEST_CACHE_STATE_PRESENT) != 0,
273 base::PathExists(dest_path));
276 // Helper function to call GetCacheEntry from origin thread.
277 bool GetCacheEntryFromOriginThread(const std::string& id,
278 FileCacheEntry* cache_entry) {
280 base::PostTaskAndReplyWithResult(
281 blocking_task_runner_,
283 base::Bind(&internal::FileCache::GetCacheEntry,
284 base::Unretained(cache_.get()),
287 google_apis::test_util::CreateCopyResultCallback(&result));
288 test_util::RunBlockingPoolTask();
292 // Returns true if the cache entry exists for the given ID.
293 bool CacheEntryExists(const std::string& id) {
294 FileCacheEntry cache_entry;
295 return GetCacheEntryFromOriginThread(id, &cache_entry);
298 // Returns the number of the cache files with name <id>, and Confirm
299 // that they have the <md5>. This should return 1 or 0.
300 size_t CountCacheFiles(const std::string& id, const std::string& md5) {
301 base::FilePath path = cache_->GetCacheFilePath(id);
302 base::FileEnumerator enumerator(path.DirName(),
304 base::FileEnumerator::FILES,
305 path.BaseName().value());
306 size_t num_files_found = 0;
307 for (base::FilePath current = enumerator.Next(); !current.empty();
308 current = enumerator.Next()) {
310 EXPECT_EQ(util::EscapeCacheFileName(id),
311 current.BaseName().AsUTF8Unsafe());
313 return num_files_found;
316 content::TestBrowserThreadBundle thread_bundle_;
317 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
318 base::ScopedTempDir temp_dir_;
319 base::FilePath dummy_file_path_;
321 scoped_ptr<ResourceMetadataStorage, test_util::DestroyHelperForTests>
323 scoped_ptr<FileCache, test_util::DestroyHelperForTests> cache_;
324 scoped_ptr<FakeFreeDiskSpaceGetter> fake_free_disk_space_getter_;
326 FileError expected_error_;
327 int expected_cache_state_;
328 std::string expected_file_extension_;
331 TEST_F(FileCacheTestOnUIThread, StoreToCacheSimple) {
332 std::string id("pdf:1a2b");
333 std::string md5("abcdef0123456789");
335 // Store an existing file.
336 TestStoreToCache(id, md5, dummy_file_path_,
337 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
339 // Store a non-existent file to the same |id| and |md5|.
340 TestStoreToCache(id, md5,
341 base::FilePath::FromUTF8Unsafe("non_existent_file"),
343 TEST_CACHE_STATE_PRESENT);
345 // Store a different existing file to the same |id| but different
348 TestStoreToCache(id, md5, dummy_file_path_,
349 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
351 // Verify that there's only one file with name <id>, i.e. previously
352 // cached file with the different md5 should be deleted.
353 EXPECT_EQ(1U, CountCacheFiles(id, md5));
355 // Passing empty MD5 marks the entry as dirty.
356 TestStoreToCache(id, std::string(), dummy_file_path_, FILE_ERROR_OK,
357 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY);
360 TEST_F(FileCacheTestOnUIThread, RemoveFromCacheSimple) {
361 std::string id("pdf:1a2b");
362 std::string md5("abcdef0123456789");
363 // First store a file to cache.
364 TestStoreToCache(id, md5, dummy_file_path_,
365 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
367 // Then try to remove existing file from cache.
368 TestRemoveFromCache(id, FILE_ERROR_OK);
370 // Repeat using non-alphanumeric characters for ID, including '.'
371 // which is an extension separator.
372 id = "pdf:`~!@#$%^&*()-_=+[{|]}\\;',<.>/?";
373 TestStoreToCache(id, md5, dummy_file_path_,
374 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
376 TestRemoveFromCache(id, FILE_ERROR_OK);
379 TEST_F(FileCacheTestOnUIThread, PinAndUnpin) {
380 std::string id("pdf:1a2b");
381 std::string md5("abcdef0123456789");
383 // First store a file to cache.
384 TestStoreToCache(id, md5, dummy_file_path_,
385 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
387 // Pin the existing file in cache.
388 TestPin(id, FILE_ERROR_OK,
389 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
391 // Unpin the existing file in cache.
392 TestUnpin(id, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
394 // Pin back the same existing file in cache.
395 TestPin(id, FILE_ERROR_OK,
396 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
398 // Pin a non-existent file in cache.
399 id = "document:1a2b";
401 TestPin(id, FILE_ERROR_OK, TEST_CACHE_STATE_PINNED);
403 // Unpin the previously pinned non-existent file in cache.
404 TestUnpin(id, FILE_ERROR_OK, TEST_CACHE_STATE_NONE);
406 // Unpin a file that doesn't exist in cache and is not pinned, i.e. cache
407 // has zero knowledge of the file.
408 id = "not-in-cache:1a2b";
410 TestUnpin(id, FILE_ERROR_NOT_FOUND, TEST_CACHE_STATE_NONE);
413 TEST_F(FileCacheTestOnUIThread, StoreToCachePinned) {
414 std::string id("pdf:1a2b");
415 std::string md5("abcdef0123456789");
417 // Pin a non-existent file.
418 TestPin(id, FILE_ERROR_OK, TEST_CACHE_STATE_PINNED);
420 // Store an existing file to a previously pinned file.
421 TestStoreToCache(id, md5, dummy_file_path_, FILE_ERROR_OK,
422 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
424 // Store a non-existent file to a previously pinned and stored file.
425 TestStoreToCache(id, md5,
426 base::FilePath::FromUTF8Unsafe("non_existent_file"),
428 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
431 TEST_F(FileCacheTestOnUIThread, RemoveFromCachePinned) {
432 std::string id("pdf:1a2b");
433 std::string md5("abcdef0123456789");
435 // Store a file to cache, and pin it.
436 TestStoreToCache(id, md5, dummy_file_path_,
437 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
438 TestPin(id, FILE_ERROR_OK,
439 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
441 // Remove |id| from cache.
442 TestRemoveFromCache(id, FILE_ERROR_OK);
444 // Use non-alphanumeric characters for ID, including '.'
445 // which is an extension separator.
446 id = "pdf:`~!@#$%^&*()-_=+[{|]}\\;',<.>/?";
448 TestStoreToCache(id, md5, dummy_file_path_,
449 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
450 TestPin(id, FILE_ERROR_OK,
451 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
453 TestRemoveFromCache(id, FILE_ERROR_OK);
456 TEST_F(FileCacheTestOnUIThread, MountUnmount) {
457 std::string id("pdf:1a2b");
458 std::string md5("abcdef0123456789");
460 // First store a file to cache.
461 TestStoreToCache(id, md5, dummy_file_path_,
462 FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
464 // Mark the file mounted.
465 TestMarkAsMounted(id, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
466 EXPECT_TRUE(CacheEntryExists(id));
468 // Try to remove the file.
469 TestRemoveFromCache(id, FILE_ERROR_IN_USE);
471 // Clear mounted state of the file.
472 base::FilePath file_path;
473 FileError error = FILE_ERROR_FAILED;
474 base::PostTaskAndReplyWithResult(
475 blocking_task_runner_,
477 base::Bind(&FileCache::GetFile,
478 base::Unretained(cache_.get()),
480 google_apis::test_util::CreateCopyResultCallback(&error));
481 test_util::RunBlockingPoolTask();
482 EXPECT_EQ(FILE_ERROR_OK, error);
484 TestMarkAsUnmounted(id, file_path, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
485 EXPECT_TRUE(CacheEntryExists(id));
487 // Try to remove the file.
488 TestRemoveFromCache(id, FILE_ERROR_OK);
491 TEST_F(FileCacheTestOnUIThread, StoreToCacheNoSpace) {
492 fake_free_disk_space_getter_->set_default_value(0);
494 std::string id("pdf:1a2b");
495 std::string md5("abcdef0123456789");
497 // Try to store an existing file.
498 TestStoreToCache(id, md5, dummy_file_path_,
499 FILE_ERROR_NO_LOCAL_SPACE,
500 TEST_CACHE_STATE_NONE);
502 // Verify that there's no files added.
503 EXPECT_EQ(0U, CountCacheFiles(id, md5));
506 TEST_F(FileCacheTestOnUIThread, UpdatePinnedCache) {
507 std::string id("pdf:1a2b");
508 std::string md5("abcdef0123456789");
509 std::string md5_modified("aaaaaa0000000000");
511 // Store an existing file.
512 TestStoreToCache(id, md5, dummy_file_path_, FILE_ERROR_OK,
513 TEST_CACHE_STATE_PRESENT);
516 TestPin(id, FILE_ERROR_OK,
517 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
519 // Store the file with a modified content and md5. It should stay pinned.
520 TestStoreToCache(id, md5_modified, dummy_file_path_, FILE_ERROR_OK,
521 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
524 // Tests FileCache methods working with the blocking task runner.
525 class FileCacheTest : public testing::Test {
527 virtual void SetUp() OVERRIDE {
528 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
529 const base::FilePath metadata_dir = temp_dir_.path().AppendASCII("meta");
530 cache_files_dir_ = temp_dir_.path().AppendASCII(kCacheFileDirectory);
532 ASSERT_TRUE(base::CreateDirectory(metadata_dir));
533 ASSERT_TRUE(base::CreateDirectory(cache_files_dir_));
535 fake_free_disk_space_getter_.reset(new FakeFreeDiskSpaceGetter);
537 metadata_storage_.reset(new ResourceMetadataStorage(
539 base::MessageLoopProxy::current().get()));
540 ASSERT_TRUE(metadata_storage_->Initialize());
542 cache_.reset(new FileCache(
543 metadata_storage_.get(),
545 base::MessageLoopProxy::current().get(),
546 fake_free_disk_space_getter_.get()));
547 ASSERT_TRUE(cache_->Initialize());
550 static bool RenameCacheFilesToNewFormat(FileCache* cache) {
551 return cache->RenameCacheFilesToNewFormat();
554 content::TestBrowserThreadBundle thread_bundle_;
555 base::ScopedTempDir temp_dir_;
556 base::FilePath cache_files_dir_;
558 scoped_ptr<ResourceMetadataStorage, test_util::DestroyHelperForTests>
560 scoped_ptr<FileCache, test_util::DestroyHelperForTests> cache_;
561 scoped_ptr<FakeFreeDiskSpaceGetter> fake_free_disk_space_getter_;
564 TEST_F(FileCacheTest, RecoverFilesFromCacheDirectory) {
565 base::FilePath dir_source_root;
566 EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &dir_source_root));
567 const base::FilePath src_path =
568 dir_source_root.AppendASCII("chrome/test/data/chromeos/drive/image.png");
570 // Store files. This file should not be moved.
571 EXPECT_EQ(FILE_ERROR_OK, cache_->Store("id_foo", "md5", src_path,
572 FileCache::FILE_OPERATION_COPY));
574 // Set up files in the cache directory. These files should be moved.
575 const base::FilePath file_directory =
576 temp_dir_.path().AppendASCII(kCacheFileDirectory);
577 ASSERT_TRUE(base::CopyFile(src_path, file_directory.AppendASCII("id_bar")));
578 ASSERT_TRUE(base::CopyFile(src_path, file_directory.AppendASCII("id_baz")));
580 // Insert a dirty entry with "id_baz" to |recovered_cache_info|.
581 // This should not prevent the file from being recovered.
582 ResourceMetadataStorage::RecoveredCacheInfoMap recovered_cache_info;
583 recovered_cache_info["id_baz"].is_dirty = true;
584 recovered_cache_info["id_baz"].title = "baz.png";
587 const base::FilePath dest_directory = temp_dir_.path().AppendASCII("dest");
588 EXPECT_TRUE(cache_->RecoverFilesFromCacheDirectory(dest_directory,
589 recovered_cache_info));
591 // Only two files should be recovered.
592 EXPECT_TRUE(base::PathExists(dest_directory));
593 // base::FileEnumerator does not guarantee the order.
594 if (base::PathExists(dest_directory.AppendASCII("baz00000001.png"))) {
595 EXPECT_TRUE(base::ContentsEqual(
597 dest_directory.AppendASCII("baz00000001.png")));
598 EXPECT_TRUE(base::ContentsEqual(
600 dest_directory.AppendASCII("image00000002.png")));
602 EXPECT_TRUE(base::ContentsEqual(
604 dest_directory.AppendASCII("image00000001.png")));
605 EXPECT_TRUE(base::ContentsEqual(
607 dest_directory.AppendASCII("baz00000002.png")));
609 EXPECT_FALSE(base::PathExists(
610 dest_directory.AppendASCII("image00000003.png")));
613 TEST_F(FileCacheTest, Iterator) {
614 base::FilePath src_file;
615 ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &src_file));
618 std::map<std::string, std::string> md5s;
619 md5s["id1"] = "md5-1";
620 md5s["id2"] = "md5-2";
621 md5s["id3"] = "md5-3";
622 md5s["id4"] = "md5-4";
623 for (std::map<std::string, std::string>::iterator it = md5s.begin();
624 it != md5s.end(); ++it) {
625 EXPECT_EQ(FILE_ERROR_OK, cache_->Store(
626 it->first, it->second, src_file, FileCache::FILE_OPERATION_COPY));
630 std::map<std::string, std::string> result;
631 scoped_ptr<FileCache::Iterator> it = cache_->GetIterator();
632 for (; !it->IsAtEnd(); it->Advance())
633 result[it->GetID()] = it->GetValue().md5();
634 EXPECT_EQ(md5s, result);
635 EXPECT_FALSE(it->HasError());
638 TEST_F(FileCacheTest, FreeDiskSpaceIfNeededFor) {
639 base::FilePath src_file;
640 ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &src_file));
642 // Store a file as a 'temporary' file and remember the path.
643 const std::string id_tmp = "id_tmp", md5_tmp = "md5_tmp";
644 ASSERT_EQ(FILE_ERROR_OK,
645 cache_->Store(id_tmp, md5_tmp, src_file,
646 FileCache::FILE_OPERATION_COPY));
647 base::FilePath tmp_path;
648 ASSERT_EQ(FILE_ERROR_OK, cache_->GetFile(id_tmp, &tmp_path));
650 // Store a file as a pinned file and remember the path.
651 const std::string id_pinned = "id_pinned", md5_pinned = "md5_pinned";
652 ASSERT_EQ(FILE_ERROR_OK,
653 cache_->Store(id_pinned, md5_pinned, src_file,
654 FileCache::FILE_OPERATION_COPY));
655 ASSERT_EQ(FILE_ERROR_OK, cache_->Pin(id_pinned));
656 base::FilePath pinned_path;
657 ASSERT_EQ(FILE_ERROR_OK, cache_->GetFile(id_pinned, &pinned_path));
659 // Call FreeDiskSpaceIfNeededFor().
660 fake_free_disk_space_getter_->set_default_value(test_util::kLotsOfSpace);
661 fake_free_disk_space_getter_->PushFakeValue(0);
662 const int64 kNeededBytes = 1;
663 EXPECT_TRUE(cache_->FreeDiskSpaceIfNeededFor(kNeededBytes));
665 // Only 'temporary' file gets removed.
666 FileCacheEntry entry;
667 EXPECT_FALSE(cache_->GetCacheEntry(id_tmp, &entry));
668 EXPECT_FALSE(base::PathExists(tmp_path));
670 EXPECT_TRUE(cache_->GetCacheEntry(id_pinned, &entry));
671 EXPECT_TRUE(base::PathExists(pinned_path));
673 // Returns false when disk space cannot be freed.
674 fake_free_disk_space_getter_->set_default_value(0);
675 EXPECT_FALSE(cache_->FreeDiskSpaceIfNeededFor(kNeededBytes));
678 TEST_F(FileCacheTest, GetFile) {
679 const base::FilePath src_file_path = temp_dir_.path().Append("test.dat");
680 const std::string src_contents = "test";
681 EXPECT_TRUE(google_apis::test_util::WriteStringToFile(src_file_path,
683 std::string id("id1");
684 std::string md5(base::MD5String(src_contents));
686 const base::FilePath cache_file_directory =
687 temp_dir_.path().AppendASCII(kCacheFileDirectory);
689 // Try to get an existing file from cache.
690 EXPECT_EQ(FILE_ERROR_OK, cache_->Store(id, md5, src_file_path,
691 FileCache::FILE_OPERATION_COPY));
692 base::FilePath cache_file_path;
693 EXPECT_EQ(FILE_ERROR_OK, cache_->GetFile(id, &cache_file_path));
695 cache_file_directory.AppendASCII(util::EscapeCacheFileName(id)).value(),
696 cache_file_path.value());
698 std::string contents;
699 EXPECT_TRUE(base::ReadFileToString(cache_file_path, &contents));
700 EXPECT_EQ(src_contents, contents);
702 // Get file from cache with different id.
704 EXPECT_EQ(FILE_ERROR_NOT_FOUND, cache_->GetFile(id, &cache_file_path));
706 // Pin a non-existent file.
707 EXPECT_EQ(FILE_ERROR_OK, cache_->Pin(id));
709 // Get the non-existent pinned file from cache.
710 EXPECT_EQ(FILE_ERROR_NOT_FOUND, cache_->GetFile(id, &cache_file_path));
712 // Get a previously pinned and stored file from cache.
713 EXPECT_EQ(FILE_ERROR_OK, cache_->Store(id, md5, src_file_path,
714 FileCache::FILE_OPERATION_COPY));
716 EXPECT_EQ(FILE_ERROR_OK, cache_->GetFile(id, &cache_file_path));
718 cache_file_directory.AppendASCII(util::EscapeCacheFileName(id)).value(),
719 cache_file_path.value());
722 EXPECT_TRUE(base::ReadFileToString(cache_file_path, &contents));
723 EXPECT_EQ(src_contents, contents);
726 TEST_F(FileCacheTest, OpenForWrite) {
728 base::FilePath src_file;
729 ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &src_file));
731 const std::string id = "id";
732 ASSERT_EQ(FILE_ERROR_OK, cache_->Store(id, "md5", src_file,
733 FileCache::FILE_OPERATION_COPY));
735 // Entry is not dirty nor opened.
736 EXPECT_FALSE(cache_->IsOpenedForWrite(id));
737 FileCacheEntry entry;
738 EXPECT_TRUE(cache_->GetCacheEntry(id, &entry));
739 EXPECT_FALSE(entry.is_dirty());
742 scoped_ptr<base::ScopedClosureRunner> file_closer1;
743 EXPECT_EQ(FILE_ERROR_OK, cache_->OpenForWrite(id, &file_closer1));
744 EXPECT_TRUE(cache_->IsOpenedForWrite(id));
747 EXPECT_TRUE(cache_->GetCacheEntry(id, &entry));
748 EXPECT_TRUE(entry.is_dirty());
751 scoped_ptr<base::ScopedClosureRunner> file_closer2;
752 EXPECT_EQ(FILE_ERROR_OK, cache_->OpenForWrite(id, &file_closer2));
753 EXPECT_TRUE(cache_->IsOpenedForWrite(id));
756 file_closer1.reset();
757 EXPECT_TRUE(cache_->IsOpenedForWrite(id));
760 file_closer2.reset();
761 EXPECT_FALSE(cache_->IsOpenedForWrite(id));
763 // Try to open non-existent file.
764 EXPECT_EQ(FILE_ERROR_NOT_FOUND,
765 cache_->OpenForWrite("nonexistent_id", &file_closer1));
768 TEST_F(FileCacheTest, UpdateMd5) {
770 const base::FilePath src_file_path = temp_dir_.path().Append("test.dat");
771 const std::string contents_before = "before";
772 EXPECT_TRUE(google_apis::test_util::WriteStringToFile(src_file_path,
774 std::string id("id1");
775 EXPECT_EQ(FILE_ERROR_OK, cache_->Store(id, base::MD5String(contents_before),
777 FileCache::FILE_OPERATION_COPY));
779 // Modify the cache file.
780 scoped_ptr<base::ScopedClosureRunner> file_closer;
781 EXPECT_EQ(FILE_ERROR_OK, cache_->OpenForWrite(id, &file_closer));
782 base::FilePath cache_file_path;
783 EXPECT_EQ(FILE_ERROR_OK, cache_->GetFile(id, &cache_file_path));
784 const std::string contents_after = "after";
785 EXPECT_TRUE(google_apis::test_util::WriteStringToFile(cache_file_path,
788 // Cannot update MD5 of an opend file.
789 EXPECT_EQ(FILE_ERROR_IN_USE, cache_->UpdateMd5(id));
794 // MD5 was cleared by OpenForWrite().
795 FileCacheEntry entry;
796 EXPECT_TRUE(cache_->GetCacheEntry(id, &entry));
797 EXPECT_TRUE(entry.md5().empty());
800 EXPECT_EQ(FILE_ERROR_OK, cache_->UpdateMd5(id));
801 EXPECT_TRUE(cache_->GetCacheEntry(id, &entry));
802 EXPECT_EQ(base::MD5String(contents_after), entry.md5());
805 TEST_F(FileCacheTest, ClearDirty) {
807 base::FilePath src_file;
808 ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &src_file));
810 const std::string id = "id";
811 ASSERT_EQ(FILE_ERROR_OK, cache_->Store(id, "md5", src_file,
812 FileCache::FILE_OPERATION_COPY));
815 scoped_ptr<base::ScopedClosureRunner> file_closer;
816 EXPECT_EQ(FILE_ERROR_OK, cache_->OpenForWrite(id, &file_closer));
819 FileCacheEntry entry;
820 EXPECT_TRUE(cache_->GetCacheEntry(id, &entry));
821 EXPECT_TRUE(entry.is_dirty());
823 // Cannot clear the dirty bit of an opened entry.
824 EXPECT_EQ(FILE_ERROR_IN_USE, cache_->ClearDirty(id));
826 // Close the file and clear the dirty bit.
828 EXPECT_EQ(FILE_ERROR_OK, cache_->ClearDirty(id));
830 // Entry is not dirty.
831 EXPECT_TRUE(cache_->GetCacheEntry(id, &entry));
832 EXPECT_FALSE(entry.is_dirty());
835 TEST_F(FileCacheTest, RenameCacheFilesToNewFormat) {
836 const base::FilePath file_directory =
837 temp_dir_.path().AppendASCII(kCacheFileDirectory);
839 // File with an old style "<prefix>:<ID>.<MD5>" name.
840 ASSERT_TRUE(google_apis::test_util::WriteStringToFile(
841 file_directory.AppendASCII("file:id_koo.md5"), "koo"));
843 // File with multiple extensions should be removed.
844 ASSERT_TRUE(google_apis::test_util::WriteStringToFile(
845 file_directory.AppendASCII("id_kyu.md5.mounted"), "kyu (mounted)"));
846 ASSERT_TRUE(google_apis::test_util::WriteStringToFile(
847 file_directory.AppendASCII("id_kyu.md5"), "kyu"));
849 // Rename and verify the result.
850 EXPECT_TRUE(RenameCacheFilesToNewFormat(cache_.get()));
851 std::string contents;
852 EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_koo"),
854 EXPECT_EQ("koo", contents);
856 EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_kyu"),
858 EXPECT_EQ("kyu", contents);
861 EXPECT_TRUE(RenameCacheFilesToNewFormat(cache_.get()));
863 // Files with new style names are not affected.
865 EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_koo"),
867 EXPECT_EQ("koo", contents);
869 EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_kyu"),
871 EXPECT_EQ("kyu", contents);
874 TEST_F(FileCacheTest, ClearAll) {
875 const std::string id("pdf:1a2b");
876 const std::string md5("abcdef0123456789");
878 // Store an existing file.
879 base::FilePath src_file;
880 ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &src_file));
881 ASSERT_EQ(FILE_ERROR_OK,
882 cache_->Store(id, md5, src_file, FileCache::FILE_OPERATION_COPY));
884 // Verify that the cache entry is created.
885 FileCacheEntry cache_entry;
886 ASSERT_TRUE(cache_->GetCacheEntry(id, &cache_entry));
889 EXPECT_TRUE(cache_->ClearAll());
891 // Verify that the cache is removed.
892 EXPECT_FALSE(cache_->GetCacheEntry(id, &cache_entry));
893 EXPECT_TRUE(base::IsDirectoryEmpty(cache_files_dir_));
896 } // namespace internal