| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/chromeos/drive/file_cache.h" |
| |
| #include <string> |
| #include <vector> |
| |
| #include "base/file_util.h" |
| #include "base/files/file_enumerator.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/run_loop.h" |
| #include "base/threading/sequenced_worker_pool.h" |
| #include "chrome/browser/chromeos/drive/drive.pb.h" |
| #include "chrome/browser/chromeos/drive/fake_free_disk_space_getter.h" |
| #include "chrome/browser/chromeos/drive/file_system_util.h" |
| #include "chrome/browser/chromeos/drive/mock_file_cache_observer.h" |
| #include "chrome/browser/chromeos/drive/test_util.h" |
| #include "chrome/browser/google_apis/test_util.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/test/test_browser_thread_bundle.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using ::testing::StrictMock; |
| |
| namespace drive { |
| namespace internal { |
| namespace { |
| |
| // Copies results from Iterate(). |
| void OnIterate(std::vector<std::string>* out_resource_ids, |
| std::vector<FileCacheEntry>* out_cache_entries, |
| const std::string& resource_id, |
| const FileCacheEntry& cache_entry) { |
| out_resource_ids->push_back(resource_id); |
| out_cache_entries->push_back(cache_entry); |
| } |
| |
| // Called upon completion of Iterate(). |
| void OnIterateCompleted(bool* out_is_called) { |
| *out_is_called = true; |
| } |
| |
| } // namespace |
| |
| // Tests FileCache methods from UI thread. It internally uses a real blocking |
| // pool and tests the interaction among threads. |
| // TODO(hashimoto): remove this class. crbug.com/231221. |
| class FileCacheTestOnUIThread : public testing::Test { |
| protected: |
| FileCacheTestOnUIThread() |
| : expected_error_(FILE_ERROR_OK), |
| expected_cache_state_(0), |
| expected_sub_dir_type_(FileCache::CACHE_TYPE_META), |
| expected_success_(true) { |
| } |
| |
| virtual void SetUp() OVERRIDE { |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(), |
| &dummy_file_path_)); |
| fake_free_disk_space_getter_.reset(new FakeFreeDiskSpaceGetter); |
| |
| scoped_refptr<base::SequencedWorkerPool> pool = |
| content::BrowserThread::GetBlockingPool(); |
| blocking_task_runner_ = |
| pool->GetSequencedTaskRunner(pool->GetSequenceToken()); |
| cache_.reset(new FileCache(temp_dir_.path(), |
| blocking_task_runner_, |
| fake_free_disk_space_getter_.get())); |
| |
| mock_cache_observer_.reset(new StrictMock<MockCacheObserver>); |
| cache_->AddObserver(mock_cache_observer_.get()); |
| |
| bool success = false; |
| cache_->RequestInitialize( |
| google_apis::test_util::CreateCopyResultCallback(&success)); |
| google_apis::test_util::RunBlockingPoolTask(); |
| ASSERT_TRUE(success); |
| } |
| |
| virtual void TearDown() OVERRIDE { |
| cache_.reset(); |
| } |
| |
| void TestGetFileFromCacheByResourceIdAndMd5( |
| const std::string& resource_id, |
| const std::string& md5, |
| FileError expected_error, |
| const std::string& expected_file_extension) { |
| FileError error = FILE_ERROR_OK; |
| base::FilePath cache_file_path; |
| cache_->GetFileOnUIThread(resource_id, md5, |
| google_apis::test_util::CreateCopyResultCallback( |
| &error, &cache_file_path)); |
| google_apis::test_util::RunBlockingPoolTask(); |
| |
| EXPECT_EQ(expected_error, error); |
| if (error == FILE_ERROR_OK) { |
| // Verify filename of |cache_file_path|. |
| base::FilePath base_name = cache_file_path.BaseName(); |
| EXPECT_EQ(util::EscapeCacheFileName(resource_id) + |
| base::FilePath::kExtensionSeparator + |
| util::EscapeCacheFileName( |
| expected_file_extension.empty() ? |
| md5 : expected_file_extension), |
| base_name.value()); |
| } else { |
| EXPECT_TRUE(cache_file_path.empty()); |
| } |
| } |
| |
| void TestStoreToCache( |
| const std::string& resource_id, |
| const std::string& md5, |
| const base::FilePath& source_path, |
| FileError expected_error, |
| int expected_cache_state, |
| FileCache::CacheSubDirectoryType expected_sub_dir_type) { |
| expected_error_ = expected_error; |
| expected_cache_state_ = expected_cache_state; |
| expected_sub_dir_type_ = expected_sub_dir_type; |
| |
| FileError error = FILE_ERROR_OK; |
| cache_->StoreOnUIThread( |
| resource_id, md5, source_path, |
| FileCache::FILE_OPERATION_COPY, |
| google_apis::test_util::CreateCopyResultCallback(&error)); |
| google_apis::test_util::RunBlockingPoolTask(); |
| VerifyCacheFileState(error, resource_id, md5); |
| } |
| |
| void TestStoreLocallyModifiedToCache( |
| const std::string& resource_id, |
| const std::string& md5, |
| const base::FilePath& source_path, |
| FileError expected_error, |
| int expected_cache_state, |
| FileCache::CacheSubDirectoryType expected_sub_dir_type) { |
| expected_error_ = expected_error; |
| expected_cache_state_ = expected_cache_state; |
| expected_sub_dir_type_ = expected_sub_dir_type; |
| |
| FileError error = FILE_ERROR_OK; |
| cache_->StoreLocallyModifiedOnUIThread( |
| resource_id, md5, source_path, |
| FileCache::FILE_OPERATION_COPY, |
| google_apis::test_util::CreateCopyResultCallback(&error)); |
| google_apis::test_util::RunBlockingPoolTask(); |
| VerifyCacheFileState(error, resource_id, md5); |
| } |
| |
| void TestRemoveFromCache(const std::string& resource_id, |
| FileError expected_error) { |
| expected_error_ = expected_error; |
| |
| FileError error = FILE_ERROR_OK; |
| cache_->RemoveOnUIThread( |
| resource_id, |
| google_apis::test_util::CreateCopyResultCallback(&error)); |
| google_apis::test_util::RunBlockingPoolTask(); |
| VerifyRemoveFromCache(error, resource_id, ""); |
| } |
| |
| // Returns number of files matching to |path_pattern|. |
| int CountFilesWithPathPattern(const base::FilePath& path_pattern) { |
| int result = 0; |
| base::FileEnumerator enumerator( |
| path_pattern.DirName(), false /* not recursive*/, |
| base::FileEnumerator::FILES, |
| path_pattern.BaseName().value()); |
| for (base::FilePath current = enumerator.Next(); !current.empty(); |
| current = enumerator.Next()) |
| ++result; |
| return result; |
| } |
| |
| void VerifyRemoveFromCache(FileError error, |
| const std::string& resource_id, |
| const std::string& md5) { |
| EXPECT_EQ(expected_error_, error); |
| |
| // Verify cache map. |
| FileCacheEntry cache_entry; |
| const bool cache_entry_found = |
| GetCacheEntryFromOriginThread(resource_id, md5, &cache_entry); |
| if (cache_entry_found) |
| EXPECT_TRUE(cache_entry.is_dirty()); |
| |
| // If entry doesn't exist, verify that no files with "<resource_id>.*" |
| // exist in persistent and tmp dirs. |
| const base::FilePath path_pattern_tmp = |
| cache_->GetCacheFilePath(resource_id, "*", |
| FileCache::CACHE_TYPE_TMP, |
| FileCache::CACHED_FILE_FROM_SERVER); |
| const base::FilePath path_pattern_persistent = |
| cache_->GetCacheFilePath(resource_id, "*", |
| FileCache::CACHE_TYPE_PERSISTENT, |
| FileCache::CACHED_FILE_FROM_SERVER); |
| |
| EXPECT_EQ(0, CountFilesWithPathPattern(path_pattern_tmp)); |
| if (!cache_entry_found) { |
| EXPECT_EQ(0, CountFilesWithPathPattern(path_pattern_persistent)); |
| } else { |
| // Entry is dirty, verify that only 1 "<resource_id>.local" exists in |
| // persistent dir. |
| EXPECT_EQ(1, CountFilesWithPathPattern(path_pattern_persistent)); |
| EXPECT_TRUE(file_util::PathExists( |
| GetCacheFilePath(resource_id, |
| std::string(), |
| FileCache::CACHE_TYPE_PERSISTENT, |
| FileCache::CACHED_FILE_LOCALLY_MODIFIED))); |
| } |
| } |
| |
| void TestPin( |
| const std::string& resource_id, |
| const std::string& md5, |
| FileError expected_error, |
| int expected_cache_state, |
| FileCache::CacheSubDirectoryType expected_sub_dir_type) { |
| expected_error_ = expected_error; |
| expected_cache_state_ = expected_cache_state; |
| expected_sub_dir_type_ = expected_sub_dir_type; |
| |
| FileError error = FILE_ERROR_OK; |
| cache_->PinOnUIThread( |
| resource_id, md5, |
| google_apis::test_util::CreateCopyResultCallback(&error)); |
| google_apis::test_util::RunBlockingPoolTask(); |
| VerifyCacheFileState(error, resource_id, md5); |
| } |
| |
| void TestUnpin( |
| const std::string& resource_id, |
| const std::string& md5, |
| FileError expected_error, |
| int expected_cache_state, |
| FileCache::CacheSubDirectoryType expected_sub_dir_type) { |
| expected_error_ = expected_error; |
| expected_cache_state_ = expected_cache_state; |
| expected_sub_dir_type_ = expected_sub_dir_type; |
| |
| FileError error = FILE_ERROR_OK; |
| cache_->UnpinOnUIThread( |
| resource_id, md5, |
| google_apis::test_util::CreateCopyResultCallback(&error)); |
| google_apis::test_util::RunBlockingPoolTask(); |
| VerifyCacheFileState(error, resource_id, md5); |
| } |
| |
| void TestMarkDirty(const std::string& resource_id, |
| const std::string& md5, |
| FileError expected_error, |
| int expected_cache_state, |
| FileCache::CacheSubDirectoryType expected_sub_dir_type) { |
| expected_error_ = expected_error; |
| expected_cache_state_ = expected_cache_state; |
| expected_sub_dir_type_ = expected_sub_dir_type; |
| |
| FileError error = FILE_ERROR_OK; |
| cache_->MarkDirtyOnUIThread( |
| resource_id, md5, |
| google_apis::test_util::CreateCopyResultCallback(&error)); |
| google_apis::test_util::RunBlockingPoolTask(); |
| |
| VerifyCacheFileState(error, resource_id, md5); |
| |
| // Verify filename. |
| if (error == FILE_ERROR_OK) { |
| base::FilePath cache_file_path; |
| cache_->GetFileOnUIThread( |
| resource_id, md5, |
| google_apis::test_util::CreateCopyResultCallback( |
| &error, &cache_file_path)); |
| google_apis::test_util::RunBlockingPoolTask(); |
| |
| EXPECT_EQ(FILE_ERROR_OK, error); |
| base::FilePath base_name = cache_file_path.BaseName(); |
| EXPECT_EQ(util::EscapeCacheFileName(resource_id) + |
| base::FilePath::kExtensionSeparator + |
| "local", |
| base_name.value()); |
| } |
| } |
| |
| void TestCommitDirty( |
| const std::string& resource_id, |
| const std::string& md5, |
| FileError expected_error, |
| int expected_cache_state, |
| FileCache::CacheSubDirectoryType expected_sub_dir_type) { |
| expected_error_ = expected_error; |
| expected_cache_state_ = expected_cache_state; |
| expected_sub_dir_type_ = expected_sub_dir_type; |
| |
| FileError error = FILE_ERROR_OK; |
| cache_->CommitDirtyOnUIThread( |
| resource_id, md5, |
| google_apis::test_util::CreateCopyResultCallback(&error)); |
| google_apis::test_util::RunBlockingPoolTask(); |
| VerifyCacheFileState(error, resource_id, md5); |
| } |
| |
| void TestClearDirty( |
| const std::string& resource_id, |
| const std::string& md5, |
| FileError expected_error, |
| int expected_cache_state, |
| FileCache::CacheSubDirectoryType expected_sub_dir_type) { |
| expected_error_ = expected_error; |
| expected_cache_state_ = expected_cache_state; |
| expected_sub_dir_type_ = expected_sub_dir_type; |
| |
| FileError error = FILE_ERROR_OK; |
| PostTaskAndReplyWithResult( |
| blocking_task_runner_, |
| FROM_HERE, |
| base::Bind(&FileCache::ClearDirty, |
| base::Unretained(cache_.get()), |
| resource_id, md5), |
| google_apis::test_util::CreateCopyResultCallback(&error)); |
| google_apis::test_util::RunBlockingPoolTask(); |
| VerifyCacheFileState(error, resource_id, md5); |
| } |
| |
| void TestMarkAsMounted( |
| const std::string& resource_id, |
| const std::string& md5, |
| FileError expected_error, |
| int expected_cache_state, |
| FileCache::CacheSubDirectoryType expected_sub_dir_type) { |
| expected_error_ = expected_error; |
| expected_cache_state_ = expected_cache_state; |
| expected_sub_dir_type_ = expected_sub_dir_type; |
| |
| FileError error = FILE_ERROR_OK; |
| base::FilePath cache_file_path; |
| cache_->MarkAsMountedOnUIThread( |
| resource_id, md5, |
| google_apis::test_util::CreateCopyResultCallback( |
| &error, &cache_file_path)); |
| google_apis::test_util::RunBlockingPoolTask(); |
| |
| EXPECT_TRUE(file_util::PathExists(cache_file_path)); |
| EXPECT_EQ(cache_file_path, |
| cache_->GetCacheFilePath(resource_id, |
| md5, |
| expected_sub_dir_type_, |
| FileCache::CACHED_FILE_MOUNTED)); |
| } |
| |
| void TestMarkAsUnmounted( |
| const std::string& resource_id, |
| const std::string& md5, |
| const base::FilePath& file_path, |
| FileError expected_error, |
| int expected_cache_state, |
| FileCache::CacheSubDirectoryType expected_sub_dir_type) { |
| expected_error_ = expected_error; |
| expected_cache_state_ = expected_cache_state; |
| expected_sub_dir_type_ = expected_sub_dir_type; |
| |
| FileError error = FILE_ERROR_OK; |
| cache_->MarkAsUnmountedOnUIThread( |
| file_path, |
| google_apis::test_util::CreateCopyResultCallback(&error)); |
| google_apis::test_util::RunBlockingPoolTask(); |
| |
| base::FilePath cache_file_path; |
| cache_->GetFileOnUIThread( |
| resource_id, md5, |
| google_apis::test_util::CreateCopyResultCallback( |
| &error, &cache_file_path)); |
| google_apis::test_util::RunBlockingPoolTask(); |
| EXPECT_EQ(FILE_ERROR_OK, error); |
| |
| EXPECT_TRUE(file_util::PathExists(cache_file_path)); |
| EXPECT_EQ(cache_file_path, |
| cache_->GetCacheFilePath(resource_id, |
| md5, |
| expected_sub_dir_type_, |
| FileCache::CACHED_FILE_FROM_SERVER)); |
| } |
| |
| void VerifyCacheFileState(FileError error, |
| const std::string& resource_id, |
| const std::string& md5) { |
| EXPECT_EQ(expected_error_, error); |
| |
| // Verify cache map. |
| FileCacheEntry cache_entry; |
| const bool cache_entry_found = |
| GetCacheEntryFromOriginThread(resource_id, md5, &cache_entry); |
| if (test_util::ToCacheEntry(expected_cache_state_).is_present() || |
| test_util::ToCacheEntry(expected_cache_state_).is_pinned()) { |
| ASSERT_TRUE(cache_entry_found); |
| EXPECT_TRUE(test_util::CacheStatesEqual( |
| test_util::ToCacheEntry(expected_cache_state_), |
| cache_entry)); |
| EXPECT_EQ(expected_sub_dir_type_, |
| FileCache::GetSubDirectoryType(cache_entry)); |
| } else { |
| EXPECT_FALSE(cache_entry_found); |
| } |
| |
| // Verify actual cache file. |
| base::FilePath dest_path = cache_->GetCacheFilePath( |
| resource_id, |
| md5, |
| test_util::ToCacheEntry(expected_cache_state_).is_pinned() || |
| test_util::ToCacheEntry(expected_cache_state_).is_dirty() ? |
| FileCache::CACHE_TYPE_PERSISTENT : |
| FileCache::CACHE_TYPE_TMP, |
| test_util::ToCacheEntry(expected_cache_state_).is_dirty() ? |
| FileCache::CACHED_FILE_LOCALLY_MODIFIED : |
| FileCache::CACHED_FILE_FROM_SERVER); |
| bool exists = file_util::PathExists(dest_path); |
| if (test_util::ToCacheEntry(expected_cache_state_).is_present()) |
| EXPECT_TRUE(exists); |
| else |
| EXPECT_FALSE(exists); |
| } |
| |
| base::FilePath GetCacheFilePath(const std::string& resource_id, |
| const std::string& md5, |
| FileCache::CacheSubDirectoryType sub_dir_type, |
| FileCache::CachedFileOrigin file_origin) { |
| return cache_->GetCacheFilePath(resource_id, md5, sub_dir_type, |
| file_origin); |
| } |
| |
| // Helper function to call GetCacheEntry from origin thread. |
| bool GetCacheEntryFromOriginThread(const std::string& resource_id, |
| const std::string& md5, |
| FileCacheEntry* cache_entry) { |
| bool result = false; |
| cache_->GetCacheEntryOnUIThread( |
| resource_id, md5, |
| google_apis::test_util::CreateCopyResultCallback(&result, cache_entry)); |
| google_apis::test_util::RunBlockingPoolTask(); |
| return result; |
| } |
| |
| // Returns true if the cache entry exists for the given resource ID and MD5. |
| bool CacheEntryExists(const std::string& resource_id, |
| const std::string& md5) { |
| FileCacheEntry cache_entry; |
| return GetCacheEntryFromOriginThread(resource_id, md5, &cache_entry); |
| } |
| |
| void TestGetCacheFilePath(const std::string& resource_id, |
| const std::string& md5, |
| const std::string& expected_filename) { |
| base::FilePath actual_path = cache_->GetCacheFilePath( |
| resource_id, |
| md5, |
| FileCache::CACHE_TYPE_TMP, |
| FileCache::CACHED_FILE_FROM_SERVER); |
| base::FilePath expected_path = |
| cache_->GetCacheDirectoryPath(FileCache::CACHE_TYPE_TMP); |
| expected_path = expected_path.Append( |
| base::FilePath::FromUTF8Unsafe(expected_filename)); |
| EXPECT_EQ(expected_path, actual_path); |
| |
| base::FilePath base_name = actual_path.BaseName(); |
| |
| // base::FilePath::Extension returns ".", so strip it. |
| std::string unescaped_md5 = util::UnescapeCacheFileName( |
| base_name.Extension().substr(1)); |
| EXPECT_EQ(md5, unescaped_md5); |
| std::string unescaped_resource_id = util::UnescapeCacheFileName( |
| base_name.RemoveExtension().value()); |
| EXPECT_EQ(resource_id, unescaped_resource_id); |
| } |
| |
| // Returns the number of the cache files with name <resource_id>, and Confirm |
| // that they have the <md5>. This should return 1 or 0. |
| size_t CountCacheFiles(const std::string& resource_id, |
| const std::string& md5) { |
| base::FilePath path = GetCacheFilePath( |
| resource_id, "*", |
| (test_util::ToCacheEntry(expected_cache_state_).is_pinned() ? |
| FileCache::CACHE_TYPE_PERSISTENT : |
| FileCache::CACHE_TYPE_TMP), |
| FileCache::CACHED_FILE_FROM_SERVER); |
| base::FileEnumerator enumerator(path.DirName(), false, |
| base::FileEnumerator::FILES, |
| path.BaseName().value()); |
| size_t num_files_found = 0; |
| for (base::FilePath current = enumerator.Next(); !current.empty(); |
| current = enumerator.Next()) { |
| ++num_files_found; |
| EXPECT_EQ(util::EscapeCacheFileName(resource_id) + |
| base::FilePath::kExtensionSeparator + |
| util::EscapeCacheFileName(md5), |
| current.BaseName().value()); |
| } |
| return num_files_found; |
| } |
| |
| content::TestBrowserThreadBundle thread_bundle_; |
| scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_; |
| base::ScopedTempDir temp_dir_; |
| base::FilePath dummy_file_path_; |
| |
| scoped_ptr<FileCache, test_util::DestroyHelperForTests> cache_; |
| scoped_ptr<FakeFreeDiskSpaceGetter> fake_free_disk_space_getter_; |
| scoped_ptr<StrictMock<MockCacheObserver> > mock_cache_observer_; |
| |
| FileError expected_error_; |
| int expected_cache_state_; |
| FileCache::CacheSubDirectoryType expected_sub_dir_type_; |
| bool expected_success_; |
| std::string expected_file_extension_; |
| }; |
| |
| TEST_F(FileCacheTestOnUIThread, GetCacheFilePath) { |
| // Use alphanumeric characters for resource id. |
| std::string resource_id("pdf:1a2b"); |
| std::string md5("abcdef0123456789"); |
| TestGetCacheFilePath(resource_id, md5, |
| resource_id + base::FilePath::kExtensionSeparator + md5); |
| |
| // Use non-alphanumeric characters for resource id, including '.' which is an |
| // extension separator, to test that the characters are escaped and unescaped |
| // correctly, and '.' doesn't mess up the filename format and operations. |
| resource_id = "pdf:`~!@#$%^&*()-_=+[{|]}\\;',<.>/?"; |
| std::string escaped_resource_id = util::EscapeCacheFileName(resource_id); |
| std::string escaped_md5 = util::EscapeCacheFileName(md5); |
| TestGetCacheFilePath( |
| resource_id, md5, escaped_resource_id + |
| base::FilePath::kExtensionSeparator + escaped_md5); |
| } |
| |
| TEST_F(FileCacheTestOnUIThread, StoreToCacheSimple) { |
| std::string resource_id("pdf:1a2b"); |
| std::string md5("abcdef0123456789"); |
| |
| // Store an existing file. |
| TestStoreToCache(resource_id, md5, dummy_file_path_, |
| FILE_ERROR_OK, test_util::TEST_CACHE_STATE_PRESENT, |
| FileCache::CACHE_TYPE_TMP); |
| |
| // Store a non-existent file to the same |resource_id| and |md5|. |
| TestStoreToCache(resource_id, md5, |
| base::FilePath::FromUTF8Unsafe("non_existent_file"), |
| FILE_ERROR_FAILED, |
| test_util::TEST_CACHE_STATE_PRESENT, |
| FileCache::CACHE_TYPE_TMP); |
| |
| // Store a different existing file to the same |resource_id| but different |
| // |md5|. |
| md5 = "new_md5"; |
| TestStoreToCache(resource_id, md5, dummy_file_path_, |
| FILE_ERROR_OK, test_util::TEST_CACHE_STATE_PRESENT, |
| FileCache::CACHE_TYPE_TMP); |
| |
| // Verify that there's only one file with name <resource_id>, i.e. previously |
| // cached file with the different md5 should be deleted. |
| EXPECT_EQ(1U, CountCacheFiles(resource_id, md5)); |
| } |
| |
| TEST_F(FileCacheTestOnUIThread, LocallyModifiedSimple) { |
| std::string resource_id("pdf:1a2b"); |
| std::string md5("abcdef0123456789"); |
| |
| const int kDirtyCacheState = |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_DIRTY | |
| test_util::TEST_CACHE_STATE_PERSISTENT; |
| |
| EXPECT_CALL(*mock_cache_observer_, OnCacheCommitted(resource_id)).Times(1); |
| TestStoreLocallyModifiedToCache( |
| resource_id, md5, |
| dummy_file_path_, |
| FILE_ERROR_OK, kDirtyCacheState, FileCache::CACHE_TYPE_PERSISTENT); |
| } |
| |
| TEST_F(FileCacheTestOnUIThread, GetFromCacheSimple) { |
| std::string resource_id("pdf:1a2b"); |
| std::string md5("abcdef0123456789"); |
| // First store a file to cache. |
| TestStoreToCache(resource_id, md5, dummy_file_path_, |
| FILE_ERROR_OK, test_util::TEST_CACHE_STATE_PRESENT, |
| FileCache::CACHE_TYPE_TMP); |
| |
| // Then try to get the existing file from cache. |
| TestGetFileFromCacheByResourceIdAndMd5( |
| resource_id, md5, FILE_ERROR_OK, md5); |
| |
| // Get file from cache with same resource id as existing file but different |
| // md5. |
| TestGetFileFromCacheByResourceIdAndMd5( |
| resource_id, "9999", FILE_ERROR_NOT_FOUND, md5); |
| |
| // Get file from cache with different resource id from existing file but same |
| // md5. |
| resource_id = "document:1a2b"; |
| TestGetFileFromCacheByResourceIdAndMd5( |
| resource_id, md5, FILE_ERROR_NOT_FOUND, md5); |
| } |
| |
| TEST_F(FileCacheTestOnUIThread, RemoveFromCacheSimple) { |
| // Use alphanumeric characters for resource id. |
| std::string resource_id("pdf:1a2b"); |
| std::string md5("abcdef0123456789"); |
| // First store a file to cache. |
| TestStoreToCache(resource_id, md5, dummy_file_path_, |
| FILE_ERROR_OK, test_util::TEST_CACHE_STATE_PRESENT, |
| FileCache::CACHE_TYPE_TMP); |
| |
| // Then try to remove existing file from cache. |
| TestRemoveFromCache(resource_id, FILE_ERROR_OK); |
| |
| // Repeat using non-alphanumeric characters for resource id, including '.' |
| // which is an extension separator. |
| resource_id = "pdf:`~!@#$%^&*()-_=+[{|]}\\;',<.>/?"; |
| TestStoreToCache(resource_id, md5, dummy_file_path_, |
| FILE_ERROR_OK, test_util::TEST_CACHE_STATE_PRESENT, |
| FileCache::CACHE_TYPE_TMP); |
| |
| TestRemoveFromCache(resource_id, FILE_ERROR_OK); |
| } |
| |
| TEST_F(FileCacheTestOnUIThread, PinAndUnpin) { |
| std::string resource_id("pdf:1a2b"); |
| std::string md5("abcdef0123456789"); |
| EXPECT_CALL(*mock_cache_observer_, OnCachePinned(resource_id, md5)).Times(2); |
| EXPECT_CALL(*mock_cache_observer_, OnCacheUnpinned(resource_id, md5)) |
| .Times(1); |
| |
| // First store a file to cache. |
| TestStoreToCache(resource_id, md5, dummy_file_path_, |
| FILE_ERROR_OK, test_util::TEST_CACHE_STATE_PRESENT, |
| FileCache::CACHE_TYPE_TMP); |
| |
| // Pin the existing file in cache. |
| TestPin(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_PINNED | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| |
| // Unpin the existing file in cache. |
| TestUnpin(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT, |
| FileCache::CACHE_TYPE_TMP); |
| |
| // Pin back the same existing file in cache. |
| TestPin(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_PINNED | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| |
| // Pin a non-existent file in cache. |
| resource_id = "document:1a2b"; |
| EXPECT_CALL(*mock_cache_observer_, OnCachePinned(resource_id, md5)).Times(1); |
| EXPECT_CALL(*mock_cache_observer_, OnCacheUnpinned(resource_id, md5)) |
| .Times(1); |
| |
| TestPin(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PINNED, |
| FileCache::CACHE_TYPE_TMP); |
| |
| // Unpin the previously pinned non-existent file in cache. |
| TestUnpin(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_NONE, |
| FileCache::CACHE_TYPE_TMP); |
| |
| // Unpin a file that doesn't exist in cache and is not pinned, i.e. cache |
| // has zero knowledge of the file. |
| resource_id = "not-in-cache:1a2b"; |
| // Because unpinning will fail, OnCacheUnpinned() won't be run. |
| EXPECT_CALL(*mock_cache_observer_, OnCacheUnpinned(resource_id, md5)) |
| .Times(0); |
| |
| TestUnpin(resource_id, md5, FILE_ERROR_NOT_FOUND, |
| test_util::TEST_CACHE_STATE_NONE, |
| FileCache::CACHE_TYPE_TMP /* non-applicable */); |
| } |
| |
| TEST_F(FileCacheTestOnUIThread, StoreToCachePinned) { |
| std::string resource_id("pdf:1a2b"); |
| std::string md5("abcdef0123456789"); |
| EXPECT_CALL(*mock_cache_observer_, OnCachePinned(resource_id, md5)).Times(1); |
| |
| // Pin a non-existent file. |
| TestPin(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PINNED, |
| FileCache::CACHE_TYPE_TMP); |
| |
| // Store an existing file to a previously pinned file. |
| TestStoreToCache(resource_id, md5, dummy_file_path_, |
| FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_PINNED | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| |
| // Store a non-existent file to a previously pinned and stored file. |
| TestStoreToCache(resource_id, md5, |
| base::FilePath::FromUTF8Unsafe("non_existent_file"), |
| FILE_ERROR_FAILED, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_PINNED | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| } |
| |
| TEST_F(FileCacheTestOnUIThread, GetFromCachePinned) { |
| std::string resource_id("pdf:1a2b"); |
| std::string md5("abcdef0123456789"); |
| EXPECT_CALL(*mock_cache_observer_, OnCachePinned(resource_id, md5)).Times(1); |
| |
| // Pin a non-existent file. |
| TestPin(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PINNED, |
| FileCache::CACHE_TYPE_TMP); |
| |
| // Get the non-existent pinned file from cache. |
| TestGetFileFromCacheByResourceIdAndMd5( |
| resource_id, md5, FILE_ERROR_NOT_FOUND, md5); |
| |
| // Store an existing file to the previously pinned non-existent file. |
| TestStoreToCache(resource_id, md5, dummy_file_path_, |
| FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_PINNED | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| |
| // Get the previously pinned and stored file from cache. |
| TestGetFileFromCacheByResourceIdAndMd5( |
| resource_id, md5, FILE_ERROR_OK, md5); |
| } |
| |
| TEST_F(FileCacheTestOnUIThread, RemoveFromCachePinned) { |
| // Use alphanumeric characters for resource_id. |
| std::string resource_id("pdf:1a2b"); |
| std::string md5("abcdef0123456789"); |
| EXPECT_CALL(*mock_cache_observer_, OnCachePinned(resource_id, md5)).Times(1); |
| |
| // Store a file to cache, and pin it. |
| TestStoreToCache(resource_id, md5, dummy_file_path_, |
| FILE_ERROR_OK, test_util::TEST_CACHE_STATE_PRESENT, |
| FileCache::CACHE_TYPE_TMP); |
| TestPin(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_PINNED | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| |
| // Remove |resource_id| from cache. |
| TestRemoveFromCache(resource_id, FILE_ERROR_OK); |
| |
| // Repeat using non-alphanumeric characters for resource id, including '.' |
| // which is an extension separator. |
| resource_id = "pdf:`~!@#$%^&*()-_=+[{|]}\\;',<.>/?"; |
| EXPECT_CALL(*mock_cache_observer_, OnCachePinned(resource_id, md5)).Times(1); |
| |
| TestStoreToCache(resource_id, md5, dummy_file_path_, |
| FILE_ERROR_OK, test_util::TEST_CACHE_STATE_PRESENT, |
| FileCache::CACHE_TYPE_TMP); |
| TestPin(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_PINNED | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| |
| TestRemoveFromCache(resource_id, FILE_ERROR_OK); |
| } |
| |
| TEST_F(FileCacheTestOnUIThread, DirtyCacheSimple) { |
| std::string resource_id("pdf:1a2b"); |
| std::string md5("abcdef0123456789"); |
| EXPECT_CALL(*mock_cache_observer_, OnCacheCommitted(resource_id)).Times(1); |
| |
| // First store a file to cache. |
| TestStoreToCache(resource_id, md5, dummy_file_path_, |
| FILE_ERROR_OK, test_util::TEST_CACHE_STATE_PRESENT, |
| FileCache::CACHE_TYPE_TMP); |
| |
| // Mark the file dirty. |
| TestMarkDirty(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_DIRTY | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| |
| // Commit the file dirty. |
| TestCommitDirty(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_DIRTY | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| |
| // Clear dirty state of the file. |
| TestClearDirty(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT, |
| FileCache::CACHE_TYPE_TMP); |
| } |
| |
| TEST_F(FileCacheTestOnUIThread, DirtyCachePinned) { |
| std::string resource_id("pdf:1a2b"); |
| std::string md5("abcdef0123456789"); |
| EXPECT_CALL(*mock_cache_observer_, OnCachePinned(resource_id, md5)).Times(1); |
| EXPECT_CALL(*mock_cache_observer_, OnCacheCommitted(resource_id)).Times(1); |
| |
| // First store a file to cache and pin it. |
| TestStoreToCache(resource_id, md5, dummy_file_path_, |
| FILE_ERROR_OK, test_util::TEST_CACHE_STATE_PRESENT, |
| FileCache::CACHE_TYPE_TMP); |
| TestPin(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_PINNED | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| |
| // Mark the file dirty. |
| TestMarkDirty(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_DIRTY | |
| test_util::TEST_CACHE_STATE_PINNED | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| |
| // Commit the file dirty. |
| TestCommitDirty(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_DIRTY | |
| test_util::TEST_CACHE_STATE_PINNED | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| |
| // Clear dirty state of the file. |
| TestClearDirty(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_PINNED | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| } |
| |
| TEST_F(FileCacheTestOnUIThread, PinAndUnpinDirtyCache) { |
| std::string resource_id("pdf:1a2b"); |
| std::string md5("abcdef0123456789"); |
| EXPECT_CALL(*mock_cache_observer_, OnCachePinned(resource_id, md5)).Times(1); |
| EXPECT_CALL(*mock_cache_observer_, OnCacheUnpinned(resource_id, md5)) |
| .Times(1); |
| |
| // First store a file to cache and mark it as dirty. |
| TestStoreToCache(resource_id, md5, dummy_file_path_, |
| FILE_ERROR_OK, test_util::TEST_CACHE_STATE_PRESENT, |
| FileCache::CACHE_TYPE_TMP); |
| TestMarkDirty(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_DIRTY | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| |
| // Verifies dirty file exists. |
| base::FilePath dirty_path; |
| FileError error = FILE_ERROR_FAILED; |
| cache_->GetFileOnUIThread( |
| resource_id, md5, |
| google_apis::test_util::CreateCopyResultCallback(&error, &dirty_path)); |
| google_apis::test_util::RunBlockingPoolTask(); |
| EXPECT_EQ(FILE_ERROR_OK, error); |
| EXPECT_TRUE(file_util::PathExists(dirty_path)); |
| |
| // Pin the dirty file. |
| TestPin(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_DIRTY | |
| test_util::TEST_CACHE_STATE_PINNED | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| |
| // Verify dirty file still exist at the same pathname. |
| EXPECT_TRUE(file_util::PathExists(dirty_path)); |
| |
| // Unpin the dirty file. |
| TestUnpin(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_DIRTY | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| |
| // Verify dirty file still exist at the same pathname. |
| EXPECT_TRUE(file_util::PathExists(dirty_path)); |
| } |
| |
| TEST_F(FileCacheTestOnUIThread, DirtyCacheRepetitive) { |
| std::string resource_id("pdf:1a2b"); |
| std::string md5("abcdef0123456789"); |
| EXPECT_CALL(*mock_cache_observer_, OnCacheCommitted(resource_id)).Times(3); |
| |
| // First store a file to cache. |
| TestStoreToCache(resource_id, md5, dummy_file_path_, |
| FILE_ERROR_OK, test_util::TEST_CACHE_STATE_PRESENT, |
| FileCache::CACHE_TYPE_TMP); |
| |
| // Mark the file dirty. |
| TestMarkDirty(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_DIRTY | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| |
| // Again, mark the file dirty. Nothing should change. |
| TestMarkDirty(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_DIRTY | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| |
| // Commit the file dirty. |
| TestCommitDirty(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_DIRTY | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| |
| // Again, commit the file dirty. Nothing should change. |
| TestCommitDirty(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_DIRTY | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| |
| // Mark the file dirty again after it's being committed. |
| TestMarkDirty(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_DIRTY | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| |
| // Commit the file dirty. |
| TestCommitDirty(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_DIRTY | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| |
| // Clear dirty state of the file. |
| TestClearDirty(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT, |
| FileCache::CACHE_TYPE_TMP); |
| |
| // Again, clear dirty state of the file, which is no longer dirty. |
| TestClearDirty(resource_id, md5, FILE_ERROR_INVALID_OPERATION, |
| test_util::TEST_CACHE_STATE_PRESENT, |
| FileCache::CACHE_TYPE_TMP); |
| } |
| |
| TEST_F(FileCacheTestOnUIThread, DirtyCacheInvalid) { |
| std::string resource_id("pdf:1a2b"); |
| std::string md5("abcdef0123456789"); |
| |
| // Mark a non-existent file dirty. |
| TestMarkDirty(resource_id, md5, FILE_ERROR_NOT_FOUND, |
| test_util::TEST_CACHE_STATE_NONE, |
| FileCache::CACHE_TYPE_TMP); |
| |
| // Clear dirty state of a non-existent file. |
| TestClearDirty(resource_id, md5, FILE_ERROR_NOT_FOUND, |
| test_util::TEST_CACHE_STATE_NONE, |
| FileCache::CACHE_TYPE_TMP); |
| |
| // Store a file to cache. |
| TestStoreToCache(resource_id, md5, dummy_file_path_, |
| FILE_ERROR_OK, test_util::TEST_CACHE_STATE_PRESENT, |
| FileCache::CACHE_TYPE_TMP); |
| |
| // Clear dirty state of a non-dirty existing file. |
| TestClearDirty(resource_id, md5, FILE_ERROR_INVALID_OPERATION, |
| test_util::TEST_CACHE_STATE_PRESENT, |
| FileCache::CACHE_TYPE_TMP); |
| |
| // Mark an existing file dirty, then store a new file to the same resource id |
| // but different md5, which should fail. |
| TestMarkDirty(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_DIRTY | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| md5 = "new_md5"; |
| TestStoreToCache(resource_id, md5, dummy_file_path_, |
| FILE_ERROR_IN_USE, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_DIRTY | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| } |
| |
| TEST_F(FileCacheTestOnUIThread, RemoveFromDirtyCache) { |
| std::string resource_id("pdf:1a2b"); |
| std::string md5("abcdef0123456789"); |
| EXPECT_CALL(*mock_cache_observer_, OnCachePinned(resource_id, md5)).Times(1); |
| EXPECT_CALL(*mock_cache_observer_, OnCacheCommitted(resource_id)).Times(1); |
| |
| // Store a file to cache, pin it, mark it dirty and commit it. |
| TestStoreToCache(resource_id, md5, dummy_file_path_, |
| FILE_ERROR_OK, test_util::TEST_CACHE_STATE_PRESENT, |
| FileCache::CACHE_TYPE_TMP); |
| TestPin(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_PINNED | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| TestMarkDirty(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_PINNED | |
| test_util::TEST_CACHE_STATE_DIRTY | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| TestCommitDirty(resource_id, md5, FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_PINNED | |
| test_util::TEST_CACHE_STATE_DIRTY | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| |
| // Try to remove the file. Since file is dirty, it should not be removed. |
| TestRemoveFromCache(resource_id, FILE_ERROR_OK); |
| } |
| |
| TEST_F(FileCacheTestOnUIThread, MountUnmount) { |
| std::string resource_id("pdf:1a2b"); |
| std::string md5("abcdef0123456789"); |
| |
| // First store a file to cache in the tmp subdir. |
| TestStoreToCache(resource_id, md5, dummy_file_path_, |
| FILE_ERROR_OK, test_util::TEST_CACHE_STATE_PRESENT, |
| FileCache::CACHE_TYPE_TMP); |
| |
| // Mark the file mounted. |
| TestMarkAsMounted(resource_id, |
| md5, |
| FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_MOUNTED | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| EXPECT_TRUE(CacheEntryExists(resource_id, md5)); |
| |
| // Clear mounted state of the file. |
| base::FilePath file_path; |
| FileError error = FILE_ERROR_FAILED; |
| cache_->GetFileOnUIThread( |
| resource_id, md5, |
| google_apis::test_util::CreateCopyResultCallback(&error, &file_path)); |
| google_apis::test_util::RunBlockingPoolTask(); |
| EXPECT_EQ(FILE_ERROR_OK, error); |
| |
| TestMarkAsUnmounted(resource_id, md5, file_path, |
| FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT, |
| FileCache::CACHE_TYPE_TMP); |
| EXPECT_TRUE(CacheEntryExists(resource_id, md5)); |
| |
| // Try to remove the file. |
| TestRemoveFromCache(resource_id, FILE_ERROR_OK); |
| } |
| |
| TEST_F(FileCacheTestOnUIThread, Iterate) { |
| const std::vector<test_util::TestCacheResource> cache_resources( |
| test_util::GetDefaultTestCacheResources()); |
| // Set mock expectations. |
| for (size_t i = 0; i < cache_resources.size(); ++i) { |
| if (cache_resources[i].is_pinned) { |
| EXPECT_CALL(*mock_cache_observer_, |
| OnCachePinned(cache_resources[i].resource_id, |
| cache_resources[i].md5)).Times(1); |
| } |
| if (cache_resources[i].is_dirty) { |
| EXPECT_CALL(*mock_cache_observer_, |
| OnCacheCommitted(cache_resources[i].resource_id)).Times(1); |
| } |
| } |
| ASSERT_TRUE(test_util::PrepareTestCacheResources( |
| cache_.get(), |
| cache_resources)); |
| |
| std::vector<std::string> resource_ids; |
| std::vector<FileCacheEntry> cache_entries; |
| bool completed = false; |
| cache_->IterateOnUIThread( |
| base::Bind(&OnIterate, &resource_ids, &cache_entries), |
| base::Bind(&OnIterateCompleted, &completed)); |
| google_apis::test_util::RunBlockingPoolTask(); |
| |
| ASSERT_TRUE(completed); |
| |
| sort(resource_ids.begin(), resource_ids.end()); |
| ASSERT_EQ(6U, resource_ids.size()); |
| EXPECT_EQ("dirty:existing", resource_ids[0]); |
| EXPECT_EQ("dirty_and_pinned:existing", resource_ids[1]); |
| EXPECT_EQ("pinned:existing", resource_ids[2]); |
| EXPECT_EQ("pinned:non-existent", resource_ids[3]); |
| EXPECT_EQ("tmp:`~!@#$%^&*()-_=+[{|]}\\;',<.>/?", resource_ids[4]); |
| EXPECT_EQ("tmp:resource_id", resource_ids[5]); |
| |
| ASSERT_EQ(6U, cache_entries.size()); |
| } |
| |
| TEST_F(FileCacheTestOnUIThread, ClearAll) { |
| std::string resource_id("pdf:1a2b"); |
| std::string md5("abcdef0123456789"); |
| |
| // Store an existing file. |
| TestStoreToCache(resource_id, md5, dummy_file_path_, |
| FILE_ERROR_OK, test_util::TEST_CACHE_STATE_PRESENT, |
| FileCache::CACHE_TYPE_TMP); |
| |
| // Verify that there's only one cached file. |
| EXPECT_EQ(1U, CountCacheFiles(resource_id, md5)); |
| |
| // Clear cache. |
| bool success = false; |
| cache_->ClearAllOnUIThread( |
| google_apis::test_util::CreateCopyResultCallback(&success)); |
| google_apis::test_util::RunBlockingPoolTask(); |
| EXPECT_TRUE(success); |
| |
| // Verify that all the cache is removed. |
| expected_error_ = FILE_ERROR_OK; |
| VerifyRemoveFromCache(FILE_ERROR_OK, resource_id, md5); |
| EXPECT_EQ(0U, CountCacheFiles(resource_id, md5)); |
| } |
| |
| TEST_F(FileCacheTestOnUIThread, StoreToCacheNoSpace) { |
| fake_free_disk_space_getter_->set_default_value(0); |
| |
| std::string resource_id("pdf:1a2b"); |
| std::string md5("abcdef0123456789"); |
| |
| // Try to store an existing file. |
| TestStoreToCache(resource_id, md5, dummy_file_path_, |
| FILE_ERROR_NO_SPACE, |
| test_util::TEST_CACHE_STATE_NONE, |
| FileCache::CACHE_TYPE_TMP); |
| |
| // Verify that there's no files added. |
| EXPECT_EQ(0U, CountCacheFiles(resource_id, md5)); |
| } |
| |
| // Don't use TEST_F, as we don't want SetUp() and TearDown() for this test. |
| TEST(FileCacheExtraTest, InitializationFailure) { |
| content::TestBrowserThreadBundle thread_bundle; |
| |
| // Set the cache root to a non existent path, so the initialization fails. |
| scoped_ptr<FileCache, test_util::DestroyHelperForTests> cache(new FileCache( |
| base::FilePath::FromUTF8Unsafe("/somewhere/nonexistent/blah/blah"), |
| base::MessageLoopProxy::current(), |
| NULL /* free_disk_space_getter */)); |
| |
| bool success = true; |
| cache->RequestInitialize( |
| google_apis::test_util::CreateCopyResultCallback(&success)); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(success); |
| } |
| |
| TEST_F(FileCacheTestOnUIThread, UpdatePinnedCache) { |
| std::string resource_id("pdf:1a2b"); |
| std::string md5("abcdef0123456789"); |
| std::string md5_modified("aaaaaa0000000000"); |
| |
| // Store an existing file. |
| TestStoreToCache(resource_id, md5, dummy_file_path_, |
| FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT, |
| FileCache::CACHE_TYPE_TMP); |
| |
| // Pin the file. |
| EXPECT_CALL(*mock_cache_observer_, OnCachePinned(resource_id, md5)).Times(1); |
| TestPin(resource_id, md5, |
| FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_PINNED | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| |
| // Store the file with a modified content and md5. It should stay pinned. |
| TestStoreToCache(resource_id, md5_modified, dummy_file_path_, |
| FILE_ERROR_OK, |
| test_util::TEST_CACHE_STATE_PRESENT | |
| test_util::TEST_CACHE_STATE_PINNED | |
| test_util::TEST_CACHE_STATE_PERSISTENT, |
| FileCache::CACHE_TYPE_PERSISTENT); |
| } |
| |
| // Tests FileCache methods working with the blocking task runner. |
| class FileCacheTest : public testing::Test { |
| protected: |
| virtual void SetUp() OVERRIDE { |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| fake_free_disk_space_getter_.reset(new FakeFreeDiskSpaceGetter); |
| |
| cache_.reset(new FileCache(temp_dir_.path(), |
| base::MessageLoopProxy::current(), |
| fake_free_disk_space_getter_.get())); |
| |
| bool success = false; |
| cache_->RequestInitialize( |
| google_apis::test_util::CreateCopyResultCallback(&success)); |
| base::RunLoop().RunUntilIdle(); |
| ASSERT_TRUE(success); |
| } |
| |
| virtual void TearDown() OVERRIDE { |
| cache_.reset(); |
| } |
| |
| content::TestBrowserThreadBundle thread_bundle_; |
| base::ScopedTempDir temp_dir_; |
| |
| scoped_ptr<FileCache, test_util::DestroyHelperForTests> cache_; |
| scoped_ptr<FakeFreeDiskSpaceGetter> fake_free_disk_space_getter_; |
| }; |
| |
| TEST_F(FileCacheTest, FreeDiskSpaceIfNeededFor) { |
| base::FilePath src_file; |
| ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(), &src_file)); |
| |
| // Store a file as a 'temporary' file and remember the path. |
| const std::string resource_id_tmp = "id_tmp", md5_tmp = "md5_tmp"; |
| ASSERT_EQ(FILE_ERROR_OK, |
| cache_->Store(resource_id_tmp, md5_tmp, src_file, |
| FileCache::FILE_OPERATION_COPY)); |
| base::FilePath tmp_path; |
| ASSERT_EQ(FILE_ERROR_OK, |
| cache_->GetFile(resource_id_tmp, md5_tmp, &tmp_path)); |
| |
| // Store a file as a pinned file and remember the path. |
| const std::string resource_id_pinned = "id_pinned", md5_pinned = "md5_pinned"; |
| ASSERT_EQ(FILE_ERROR_OK, |
| cache_->Store(resource_id_pinned, md5_pinned, src_file, |
| FileCache::FILE_OPERATION_COPY)); |
| ASSERT_EQ(FILE_ERROR_OK, cache_->Pin(resource_id_pinned, md5_pinned)); |
| base::FilePath pinned_path; |
| ASSERT_EQ(FILE_ERROR_OK, |
| cache_->GetFile(resource_id_pinned, md5_pinned, &pinned_path)); |
| |
| // Call FreeDiskSpaceIfNeededFor(). |
| fake_free_disk_space_getter_->set_default_value(test_util::kLotsOfSpace); |
| fake_free_disk_space_getter_->PushFakeValue(0); |
| const int64 kNeededBytes = 1; |
| EXPECT_TRUE(cache_->FreeDiskSpaceIfNeededFor(kNeededBytes)); |
| |
| // Only 'temporary' file gets removed. |
| FileCacheEntry entry; |
| EXPECT_FALSE(cache_->GetCacheEntry(resource_id_tmp, md5_tmp, &entry)); |
| EXPECT_FALSE(file_util::PathExists(tmp_path)); |
| |
| EXPECT_TRUE(cache_->GetCacheEntry(resource_id_pinned, md5_pinned, &entry)); |
| EXPECT_TRUE(file_util::PathExists(pinned_path)); |
| |
| // Returns false when disk space cannot be freed. |
| fake_free_disk_space_getter_->set_default_value(0); |
| EXPECT_FALSE(cache_->FreeDiskSpaceIfNeededFor(kNeededBytes)); |
| } |
| |
| } // namespace internal |
| } // namespace drive |