drive: Rename chrome/browser/chromeos/gdata to chrome/browser/chromeos/drive

chrome/browser/chromeos/gdata was a misnomer. This directory is the place
for Drive code, hence should be renamed from 'gdata' to 'drive'.

BUG=155215
TEST=compiles

Review URL: https://codereview.chromium.org/11085065

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@161303 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/chromeos/drive/drive_cache_unittest.cc b/chrome/browser/chromeos/drive/drive_cache_unittest.cc
new file mode 100644
index 0000000..160f9cc5
--- /dev/null
+++ b/chrome/browser/chromeos/drive/drive_cache_unittest.cc
@@ -0,0 +1,1610 @@
+// 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/drive_cache.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "base/file_util.h"
+#include "base/message_loop.h"
+#include "base/path_service.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "chrome/browser/chromeos/drive/drive.pb.h"
+#include "chrome/browser/chromeos/drive/drive_file_system.h"
+#include "chrome/browser/chromeos/drive/drive_file_system_util.h"
+#include "chrome/browser/chromeos/drive/drive_test_util.h"
+#include "chrome/browser/chromeos/drive/mock_drive_cache_observer.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/test_browser_thread.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AtLeast;
+using ::testing::Return;
+using ::testing::StrictMock;
+
+namespace gdata {
+namespace {
+
+const char kSymLinkToDevNull[] = "/dev/null";
+
+struct InitialCacheResource {
+  const char* source_file;              // Source file to be used for cache.
+  const char* resource_id;              // Resource id of cache file.
+  const char* md5;                      // MD5 of cache file.
+  int cache_state;                      // Cache state of cache file.
+  const char* expected_file_extension;  // Expected extension of cached file.
+  // Expected CacheSubDirectoryType of cached file.
+  DriveCache::CacheSubDirectoryType expected_sub_dir_type;
+} const initial_cache_resources[] = {
+  // Cache resource in tmp dir, i.e. not pinned or dirty.
+  { "gdata/root_feed.json", "tmp:resource_id", "md5_tmp_alphanumeric",
+    test_util::TEST_CACHE_STATE_PRESENT,
+    "md5_tmp_alphanumeric", DriveCache::CACHE_TYPE_TMP },
+  // Cache resource in tmp dir, i.e. not pinned or dirty, with resource_id
+  // containing non-alphanumeric characters, to test resource_id is escaped and
+  // unescaped correctly.
+  { "gdata/subdir_feed.json", "tmp:`~!@#$%^&*()-_=+[{|]}\\;',<.>/?",
+    "md5_tmp_non_alphanumeric",
+    test_util::TEST_CACHE_STATE_PRESENT,
+    "md5_tmp_non_alphanumeric", DriveCache::CACHE_TYPE_TMP },
+  // Cache resource that is pinned, to test a pinned file is in persistent dir
+  // with a symlink in pinned dir referencing it.
+  { "gdata/directory_entry_atom.json", "pinned:existing", "md5_pinned_existing",
+    test_util::TEST_CACHE_STATE_PRESENT |
+    test_util::TEST_CACHE_STATE_PINNED |
+    test_util::TEST_CACHE_STATE_PERSISTENT,
+    "md5_pinned_existing", DriveCache::CACHE_TYPE_PERSISTENT },
+  // Cache resource with a non-existent source file that is pinned, to test that
+  // a pinned file can reference a non-existent file.
+  { "", "pinned:non-existent", "md5_pinned_non_existent",
+    test_util::TEST_CACHE_STATE_PINNED,
+    "md5_pinned_non_existent", DriveCache::CACHE_TYPE_TMP },
+  // Cache resource that is dirty, to test a dirty file is in persistent dir
+  // with a symlink in outgoing dir referencing it.
+  { "gdata/account_metadata.json", "dirty:existing", "md5_dirty_existing",
+    test_util::TEST_CACHE_STATE_PRESENT |
+    test_util::TEST_CACHE_STATE_DIRTY |
+    test_util::TEST_CACHE_STATE_PERSISTENT,
+    "local", DriveCache::CACHE_TYPE_PERSISTENT },
+  // Cache resource that is pinned and dirty, to test a dirty pinned file is in
+  // persistent dir with symlink in pinned and outgoing dirs referencing it.
+  { "gdata/basic_feed.json", "dirty_and_pinned:existing",
+    "md5_dirty_and_pinned_existing",
+    test_util::TEST_CACHE_STATE_PRESENT |
+    test_util::TEST_CACHE_STATE_PINNED |
+    test_util::TEST_CACHE_STATE_DIRTY |
+    test_util::TEST_CACHE_STATE_PERSISTENT,
+    "local", DriveCache::CACHE_TYPE_PERSISTENT },
+};
+
+const int64 kLotsOfSpace = kMinFreeSpace * 10;
+
+struct PathToVerify {
+  PathToVerify(const FilePath& in_path_to_scan,
+               const FilePath& in_expected_existing_path) :
+      path_to_scan(in_path_to_scan),
+      expected_existing_path(in_expected_existing_path) {
+  }
+
+  FilePath path_to_scan;
+  FilePath expected_existing_path;
+};
+
+class MockFreeDiskSpaceGetter : public FreeDiskSpaceGetterInterface {
+ public:
+  virtual ~MockFreeDiskSpaceGetter() {}
+  MOCK_CONST_METHOD0(AmountOfFreeDiskSpace, int64());
+};
+
+// Copies results from GetResourceIdsOfBacklogCallback.
+void OnGetResourceIdsOfBacklog(std::vector<std::string>* out_to_fetch,
+                               std::vector<std::string>* out_to_upload,
+                               const std::vector<std::string>& to_fetch,
+                               const std::vector<std::string>& to_upload) {
+  *out_to_fetch = to_fetch;
+  *out_to_upload = to_upload;
+}
+
+// Copies results from GetResourceIdsCallback.
+void OnGetResourceIds(std::vector<std::string>* out_resource_ids,
+                      const std::vector<std::string>& resource_ids) {
+  *out_resource_ids = resource_ids;
+}
+
+// Copies results from ClearAllOnUIThread.
+void OnClearAll(DriveFileError* out_error,
+                FilePath* out_file_path,
+                DriveFileError error,
+                const FilePath& file_path) {
+  *out_file_path = file_path;
+  *out_error = error;
+}
+
+}  // namespace
+
+class DriveCacheTest : public testing::Test {
+ protected:
+  DriveCacheTest()
+      : ui_thread_(content::BrowserThread::UI, &message_loop_),
+        io_thread_(content::BrowserThread::IO),
+        cache_(NULL),
+        num_callback_invocations_(0),
+        expected_error_(DRIVE_FILE_OK),
+        expected_cache_state_(0),
+        expected_sub_dir_type_(DriveCache::CACHE_TYPE_META),
+        expected_success_(true),
+        expect_outgoing_symlink_(false),
+        root_feed_changestamp_(0) {
+  }
+
+  virtual void SetUp() OVERRIDE {
+    io_thread_.StartIOThread();
+
+    profile_.reset(new TestingProfile);
+
+    mock_free_disk_space_checker_ = new MockFreeDiskSpaceGetter;
+    SetFreeDiskSpaceGetterForTesting(mock_free_disk_space_checker_);
+
+    scoped_refptr<base::SequencedWorkerPool> pool =
+        content::BrowserThread::GetBlockingPool();
+    blocking_task_runner_ =
+        pool->GetSequencedTaskRunner(pool->GetSequenceToken());
+    cache_ = DriveCache::CreateDriveCacheOnUIThread(
+        DriveCache::GetCacheRootPath(profile_.get()), blocking_task_runner_);
+
+    mock_cache_observer_.reset(new StrictMock<MockDriveCacheObserver>);
+    cache_->AddObserver(mock_cache_observer_.get());
+
+    bool initialization_success = false;
+    cache_->RequestInitializeOnUIThread(
+        base::Bind(&test_util::CopyResultFromInitializeCacheCallback,
+                   &initialization_success));
+    test_util::RunBlockingPoolTask();
+    ASSERT_TRUE(initialization_success);
+  }
+
+  virtual void TearDown() OVERRIDE {
+    SetFreeDiskSpaceGetterForTesting(NULL);
+    cache_->DestroyOnUIThread();
+    // The cache destruction requires to post a task to the blocking pool.
+    test_util::RunBlockingPoolTask();
+
+    profile_.reset(NULL);
+  }
+
+  void PrepareForInitCacheTest() {
+    DVLOG(1) << "PrepareForInitCacheTest start";
+    // Create drive cache sub directories.
+    ASSERT_TRUE(file_util::CreateDirectory(
+        cache_->GetCacheDirectoryPath(DriveCache::CACHE_TYPE_PERSISTENT)));
+    ASSERT_TRUE(file_util::CreateDirectory(
+        cache_->GetCacheDirectoryPath(DriveCache::CACHE_TYPE_TMP)));
+    ASSERT_TRUE(file_util::CreateDirectory(
+        cache_->GetCacheDirectoryPath(DriveCache::CACHE_TYPE_PINNED)));
+    ASSERT_TRUE(file_util::CreateDirectory(
+        cache_->GetCacheDirectoryPath(DriveCache::CACHE_TYPE_OUTGOING)));
+
+    // Dump some files into cache dirs so that
+    // DriveFileSystem::InitializeCacheOnBlockingPool would scan through them
+    // and populate cache map accordingly.
+
+    // Copy files from data dir to cache dir to act as cached files.
+    for (size_t i = 0; i < ARRAYSIZE_UNSAFE(initial_cache_resources); ++i) {
+      const struct InitialCacheResource& resource = initial_cache_resources[i];
+      // Determine drive cache file absolute path according to cache state.
+      FilePath dest_path = cache_->GetCacheFilePath(
+          resource.resource_id,
+          resource.md5,
+          test_util::ToCacheEntry(resource.cache_state).is_pinned() ||
+          test_util::ToCacheEntry(resource.cache_state).is_dirty() ?
+                  DriveCache::CACHE_TYPE_PERSISTENT :
+                  DriveCache::CACHE_TYPE_TMP,
+          test_util::ToCacheEntry(resource.cache_state).is_dirty() ?
+              DriveCache::CACHED_FILE_LOCALLY_MODIFIED :
+              DriveCache::CACHED_FILE_FROM_SERVER);
+
+      // Copy file from data dir to cache subdir, naming it per cache files
+      // convention.
+      if (test_util::ToCacheEntry(resource.cache_state).is_present()) {
+        FilePath source_path = test_util::GetTestFilePath(resource.source_file);
+        ASSERT_TRUE(file_util::CopyFile(source_path, dest_path));
+      } else {
+        dest_path = FilePath(FILE_PATH_LITERAL(kSymLinkToDevNull));
+      }
+
+      // Create symbolic link in pinned dir, naming it per cache files
+      // convention.
+      if (test_util::ToCacheEntry(resource.cache_state).is_pinned()) {
+        FilePath link_path = cache_->GetCacheFilePath(
+            resource.resource_id,
+            "",
+            DriveCache::CACHE_TYPE_PINNED,
+            DriveCache::CACHED_FILE_FROM_SERVER);
+        ASSERT_TRUE(file_util::CreateSymbolicLink(dest_path, link_path));
+      }
+
+      // Create symbolic link in outgoing dir, naming it per cache files
+      // convention.
+      if (test_util::ToCacheEntry(resource.cache_state).is_dirty()) {
+        FilePath link_path = cache_->GetCacheFilePath(
+            resource.resource_id,
+            "",
+            DriveCache::CACHE_TYPE_OUTGOING,
+            DriveCache::CACHED_FILE_FROM_SERVER);
+        ASSERT_TRUE(file_util::CreateSymbolicLink(dest_path, link_path));
+      }
+    }
+
+    DVLOG(1) << "PrepareForInitCacheTest finished";
+    cache_->ForceRescanOnUIThreadForTesting();
+    test_util::RunBlockingPoolTask();
+  }
+
+  void TestInitializeCache() {
+    for (size_t i = 0; i < ARRAYSIZE_UNSAFE(initial_cache_resources); ++i) {
+      const struct InitialCacheResource& resource = initial_cache_resources[i];
+      // Check cache file.
+      num_callback_invocations_ = 0;
+      TestGetFileFromCacheByResourceIdAndMd5(
+          resource.resource_id,
+          resource.md5,
+          test_util::ToCacheEntry(resource.cache_state).is_present() ?
+          DRIVE_FILE_OK :
+          DRIVE_FILE_ERROR_NOT_FOUND,
+          resource.expected_file_extension);
+      EXPECT_EQ(1, num_callback_invocations_);
+
+      // Verify cache state.
+      std::string md5;
+      if (test_util::ToCacheEntry(resource.cache_state).is_present())
+         md5 = resource.md5;
+      DriveCacheEntry cache_entry;
+      ASSERT_TRUE(GetCacheEntryFromOriginThread(
+          resource.resource_id, md5, &cache_entry));
+      EXPECT_TRUE(test_util::CacheStatesEqual(
+          test_util::ToCacheEntry(resource.cache_state),
+          cache_entry));
+      EXPECT_EQ(resource.expected_sub_dir_type,
+                DriveCache::GetSubDirectoryType(cache_entry));
+    }
+  }
+
+  void TestGetFileFromCacheByResourceIdAndMd5(
+      const std::string& resource_id,
+      const std::string& md5,
+      DriveFileError expected_error,
+      const std::string& expected_file_extension) {
+    expected_error_ = expected_error;
+    expected_file_extension_ = expected_file_extension;
+
+    cache_->GetFileOnUIThread(
+        resource_id,
+        md5,
+        base::Bind(&DriveCacheTest::VerifyGetFromCache,
+                   base::Unretained(this),
+                   resource_id,
+                   md5));
+
+    test_util::RunBlockingPoolTask();
+  }
+
+  void TestStoreToCache(
+      const std::string& resource_id,
+      const std::string& md5,
+      const FilePath& source_path,
+      DriveFileError expected_error,
+      int expected_cache_state,
+      DriveCache::CacheSubDirectoryType expected_sub_dir_type) {
+    expected_error_ = expected_error;
+    expected_cache_state_ = expected_cache_state;
+    expected_sub_dir_type_ = expected_sub_dir_type;
+
+    cache_->StoreOnUIThread(
+        resource_id, md5, source_path,
+        DriveCache::FILE_OPERATION_COPY,
+        base::Bind(&DriveCacheTest::VerifyCacheFileState,
+                   base::Unretained(this)));
+
+    test_util::RunBlockingPoolTask();
+  }
+
+  void VerifyGetFromCache(const std::string& resource_id,
+                          const std::string& md5,
+                          DriveFileError error,
+                          const FilePath& cache_file_path) {
+    ++num_callback_invocations_;
+
+    EXPECT_EQ(expected_error_, error);
+
+    if (error == DRIVE_FILE_OK) {
+      // Verify filename of |cache_file_path|.
+      FilePath base_name = cache_file_path.BaseName();
+      EXPECT_EQ(util::EscapeCacheFileName(resource_id) +
+                FilePath::kExtensionSeparator +
+                util::EscapeCacheFileName(
+                    expected_file_extension_.empty() ?
+                    md5 : expected_file_extension_),
+                base_name.value());
+    } else {
+      EXPECT_TRUE(cache_file_path.empty());
+    }
+  }
+
+  void TestRemoveFromCache(const std::string& resource_id,
+                           DriveFileError expected_error) {
+    expected_error_ = expected_error;
+
+    cache_->RemoveOnUIThread(
+        resource_id,
+        base::Bind(&DriveCacheTest::VerifyRemoveFromCache,
+                   base::Unretained(this)));
+
+    test_util::RunBlockingPoolTask();
+  }
+
+  void VerifyRemoveFromCache(DriveFileError error,
+                             const std::string& resource_id,
+                             const std::string& md5) {
+    ++num_callback_invocations_;
+
+    EXPECT_EQ(expected_error_, error);
+
+    // Verify cache map.
+    DriveCacheEntry 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>.* exists in persistent and tmp dirs
+    // - no "<resource_id>" symlink exists in pinned and outgoing dirs.
+    std::vector<PathToVerify> paths_to_verify;
+    paths_to_verify.push_back(  // Index 0: CACHE_TYPE_TMP.
+        PathToVerify(cache_->GetCacheFilePath(resource_id, "*",
+                     DriveCache::CACHE_TYPE_TMP,
+                     DriveCache::CACHED_FILE_FROM_SERVER), FilePath()));
+    paths_to_verify.push_back(  // Index 1: CACHE_TYPE_PERSISTENT.
+        PathToVerify(cache_->GetCacheFilePath(resource_id, "*",
+                     DriveCache::CACHE_TYPE_PERSISTENT,
+                     DriveCache::CACHED_FILE_FROM_SERVER), FilePath()));
+    paths_to_verify.push_back(  // Index 2: CACHE_TYPE_TMP, but STATE_PINNED.
+        PathToVerify(cache_->GetCacheFilePath(resource_id, "",
+                     DriveCache::CACHE_TYPE_PINNED,
+                     DriveCache::CACHED_FILE_FROM_SERVER), FilePath()));
+    paths_to_verify.push_back(  // Index 3: CACHE_TYPE_OUTGOING.
+        PathToVerify(cache_->GetCacheFilePath(resource_id, "",
+                     DriveCache::CACHE_TYPE_OUTGOING,
+                     DriveCache::CACHED_FILE_FROM_SERVER), FilePath()));
+    if (!cache_entry_found) {
+      for (size_t i = 0; i < paths_to_verify.size(); ++i) {
+        file_util::FileEnumerator enumerator(
+            paths_to_verify[i].path_to_scan.DirName(), false /* not recursive*/,
+            file_util::FileEnumerator::FILES |
+            file_util::FileEnumerator::SHOW_SYM_LINKS,
+            paths_to_verify[i].path_to_scan.BaseName().value());
+        EXPECT_TRUE(enumerator.Next().empty());
+      }
+    } else {
+      // Entry is dirty, verify that:
+      // - no files with "<resource_id>.*" exist in tmp dir
+      // - only 1 "<resource_id>.local" exists in persistent dir
+      // - only 1 <resource_id> exists in outgoing dir
+      // - if entry is pinned, only 1 <resource_id> exists in pinned dir.
+
+      // Change expected_existing_path of CACHE_TYPE_PERSISTENT (index 1).
+      paths_to_verify[1].expected_existing_path =
+          GetCacheFilePath(resource_id,
+                           std::string(),
+                           DriveCache::CACHE_TYPE_PERSISTENT,
+                           DriveCache::CACHED_FILE_LOCALLY_MODIFIED);
+
+      // Change expected_existing_path of CACHE_TYPE_OUTGOING (index 3).
+      paths_to_verify[3].expected_existing_path =
+          GetCacheFilePath(resource_id,
+                           std::string(),
+                           DriveCache::CACHE_TYPE_OUTGOING,
+                           DriveCache::CACHED_FILE_FROM_SERVER);
+
+      if (cache_entry.is_pinned()) {
+         // Change expected_existing_path of CACHE_TYPE_TMP but STATE_PINNED
+         // (index 2).
+         paths_to_verify[2].expected_existing_path =
+             GetCacheFilePath(resource_id,
+                              std::string(),
+                              DriveCache::CACHE_TYPE_PINNED,
+                              DriveCache::CACHED_FILE_FROM_SERVER);
+      }
+
+      for (size_t i = 0; i < paths_to_verify.size(); ++i) {
+        const struct PathToVerify& verify = paths_to_verify[i];
+        file_util::FileEnumerator enumerator(
+            verify.path_to_scan.DirName(), false /* not recursive */,
+            file_util::FileEnumerator::FILES |
+            file_util::FileEnumerator::SHOW_SYM_LINKS,
+            verify.path_to_scan.BaseName().value());
+        size_t num_files_found = 0;
+        for (FilePath current = enumerator.Next(); !current.empty();
+             current = enumerator.Next()) {
+          ++num_files_found;
+          EXPECT_EQ(verify.expected_existing_path, current);
+        }
+        if (verify.expected_existing_path.empty())
+          EXPECT_EQ(0U, num_files_found);
+        else
+          EXPECT_EQ(1U, num_files_found);
+      }
+    }
+  }
+
+  void TestPin(
+      const std::string& resource_id,
+      const std::string& md5,
+      DriveFileError expected_error,
+      int expected_cache_state,
+      DriveCache::CacheSubDirectoryType expected_sub_dir_type) {
+    expected_error_ = expected_error;
+    expected_cache_state_ = expected_cache_state;
+    expected_sub_dir_type_ = expected_sub_dir_type;
+
+    cache_->PinOnUIThread(
+        resource_id, md5,
+        base::Bind(&DriveCacheTest::VerifyCacheFileState,
+                   base::Unretained(this)));
+
+    test_util::RunBlockingPoolTask();
+  }
+
+  void TestUnpin(
+      const std::string& resource_id,
+      const std::string& md5,
+      DriveFileError expected_error,
+      int expected_cache_state,
+      DriveCache::CacheSubDirectoryType expected_sub_dir_type) {
+    expected_error_ = expected_error;
+    expected_cache_state_ = expected_cache_state;
+    expected_sub_dir_type_ = expected_sub_dir_type;
+
+    cache_->UnpinOnUIThread(
+        resource_id, md5,
+        base::Bind(&DriveCacheTest::VerifyCacheFileState,
+                   base::Unretained(this)));
+
+    test_util::RunBlockingPoolTask();
+  }
+
+  void TestMarkDirty(
+      const std::string& resource_id,
+      const std::string& md5,
+      DriveFileError expected_error,
+      int expected_cache_state,
+      DriveCache::CacheSubDirectoryType expected_sub_dir_type) {
+    expected_error_ = expected_error;
+    expected_cache_state_ = expected_cache_state;
+    expected_sub_dir_type_ = expected_sub_dir_type;
+    expect_outgoing_symlink_ = false;
+
+    cache_->MarkDirtyOnUIThread(
+        resource_id,
+        md5,
+        base::Bind(&DriveCacheTest::VerifyMarkDirty,
+                   base::Unretained(this),
+                   resource_id,
+                   md5));
+
+    test_util::RunBlockingPoolTask();
+  }
+
+  void VerifyMarkDirty(const std::string& resource_id,
+                       const std::string& md5,
+                       DriveFileError error,
+                       const FilePath& cache_file_path) {
+    VerifyCacheFileState(error, resource_id, md5);
+
+    // Verify filename of |cache_file_path|.
+    if (error == DRIVE_FILE_OK) {
+      FilePath base_name = cache_file_path.BaseName();
+      EXPECT_EQ(util::EscapeCacheFileName(resource_id) +
+                FilePath::kExtensionSeparator +
+                "local",
+                base_name.value());
+    } else {
+      EXPECT_TRUE(cache_file_path.empty());
+    }
+  }
+
+  void TestCommitDirty(
+      const std::string& resource_id,
+      const std::string& md5,
+      DriveFileError expected_error,
+      int expected_cache_state,
+      DriveCache::CacheSubDirectoryType expected_sub_dir_type) {
+    expected_error_ = expected_error;
+    expected_cache_state_ = expected_cache_state;
+    expected_sub_dir_type_ = expected_sub_dir_type;
+    expect_outgoing_symlink_ = true;
+
+    cache_->CommitDirtyOnUIThread(
+        resource_id, md5,
+        base::Bind(&DriveCacheTest::VerifyCacheFileState,
+                   base::Unretained(this)));
+
+    test_util::RunBlockingPoolTask();
+  }
+
+  void TestClearDirty(
+      const std::string& resource_id,
+      const std::string& md5,
+      DriveFileError expected_error,
+      int expected_cache_state,
+      DriveCache::CacheSubDirectoryType expected_sub_dir_type) {
+    expected_error_ = expected_error;
+    expected_cache_state_ = expected_cache_state;
+    expected_sub_dir_type_ = expected_sub_dir_type;
+    expect_outgoing_symlink_ = false;
+
+    cache_->ClearDirtyOnUIThread(resource_id, md5,
+        base::Bind(&DriveCacheTest::VerifyCacheFileState,
+                   base::Unretained(this)));
+
+    test_util::RunBlockingPoolTask();
+  }
+
+  void TestSetMountedState(
+      const std::string& resource_id,
+      const std::string& md5,
+      const FilePath& file_path,
+      bool to_mount,
+      DriveFileError expected_error,
+      int expected_cache_state,
+      DriveCache::CacheSubDirectoryType expected_sub_dir_type) {
+    expected_error_ = expected_error;
+    expected_cache_state_ = expected_cache_state;
+    expected_sub_dir_type_ = expected_sub_dir_type;
+    expect_outgoing_symlink_ = false;
+
+    cache_->SetMountedStateOnUIThread(file_path, to_mount,
+        base::Bind(&DriveCacheTest::VerifySetMountedState,
+                   base::Unretained(this), resource_id, md5, to_mount));
+
+    test_util::RunBlockingPoolTask();
+  }
+
+  void VerifySetMountedState(const std::string& resource_id,
+                             const std::string& md5,
+                             bool to_mount,
+                             DriveFileError error,
+                             const FilePath& file_path) {
+    ++num_callback_invocations_;
+    EXPECT_TRUE(file_util::PathExists(file_path));
+    EXPECT_TRUE(file_path == cache_->GetCacheFilePath(
+        resource_id,
+        md5,
+        expected_sub_dir_type_,
+        to_mount ?
+            DriveCache::CACHED_FILE_MOUNTED :
+            DriveCache::CACHED_FILE_FROM_SERVER));
+  }
+
+  void VerifyCacheFileState(DriveFileError error,
+                            const std::string& resource_id,
+                            const std::string& md5) {
+    ++num_callback_invocations_;
+
+    EXPECT_EQ(expected_error_, error);
+
+    // Verify cache map.
+    DriveCacheEntry 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_,
+                DriveCache::GetSubDirectoryType(cache_entry));
+    } else {
+      EXPECT_FALSE(cache_entry_found);
+    }
+
+    // Verify actual cache file.
+    FilePath dest_path = cache_->GetCacheFilePath(
+        resource_id,
+        md5,
+        test_util::ToCacheEntry(expected_cache_state_).is_pinned() ||
+        test_util::ToCacheEntry(expected_cache_state_).is_dirty() ?
+                DriveCache::CACHE_TYPE_PERSISTENT :
+                DriveCache::CACHE_TYPE_TMP,
+        test_util::ToCacheEntry(expected_cache_state_).is_dirty() ?
+            DriveCache::CACHED_FILE_LOCALLY_MODIFIED :
+            DriveCache::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);
+
+    // Verify symlink in pinned dir.
+    FilePath symlink_path = cache_->GetCacheFilePath(
+        resource_id,
+        std::string(),
+        DriveCache::CACHE_TYPE_PINNED,
+        DriveCache::CACHED_FILE_FROM_SERVER);
+    // Check that pin symlink exists, without deferencing to target path.
+    exists = file_util::IsLink(symlink_path);
+    if (test_util::ToCacheEntry(expected_cache_state_).is_pinned()) {
+      EXPECT_TRUE(exists);
+      FilePath target_path;
+      EXPECT_TRUE(file_util::ReadSymbolicLink(symlink_path, &target_path));
+      if (test_util::ToCacheEntry(expected_cache_state_).is_present())
+        EXPECT_EQ(dest_path, target_path);
+      else
+        EXPECT_EQ(kSymLinkToDevNull, target_path.value());
+    } else {
+      EXPECT_FALSE(exists);
+    }
+
+    // Verify symlink in outgoing dir.
+    symlink_path = cache_->GetCacheFilePath(
+        resource_id,
+        std::string(),
+        DriveCache::CACHE_TYPE_OUTGOING,
+        DriveCache::CACHED_FILE_FROM_SERVER);
+    // Check that outgoing symlink exists, without deferencing to target path.
+    exists = file_util::IsLink(symlink_path);
+    if (expect_outgoing_symlink_ &&
+        test_util::ToCacheEntry(expected_cache_state_).is_dirty()) {
+      EXPECT_TRUE(exists);
+      FilePath target_path;
+      EXPECT_TRUE(file_util::ReadSymbolicLink(symlink_path, &target_path));
+      EXPECT_TRUE(target_path.value() != kSymLinkToDevNull);
+      if (test_util::ToCacheEntry(expected_cache_state_).is_present())
+        EXPECT_EQ(dest_path, target_path);
+    } else {
+      EXPECT_FALSE(exists);
+    }
+  }
+
+  FilePath GetCacheFilePath(const std::string& resource_id,
+                            const std::string& md5,
+                            DriveCache::CacheSubDirectoryType sub_dir_type,
+                            DriveCache::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,
+                                     DriveCacheEntry* cache_entry) {
+    bool result = false;
+    blocking_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(&DriveCacheTest::GetCacheEntryFromOriginThreadInternal,
+                   base::Unretained(this),
+                   resource_id,
+                   md5,
+                   cache_entry,
+                   &result));
+    test_util::RunBlockingPoolTask();
+    return result;
+  }
+
+  // Used to implement GetCacheEntry.
+  void GetCacheEntryFromOriginThreadInternal(
+      const std::string& resource_id,
+      const std::string& md5,
+      DriveCacheEntry* cache_entry,
+      bool* result) {
+    *result = cache_->GetCacheEntry(resource_id, md5, cache_entry);
+  }
+
+  // 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) {
+    DriveCacheEntry 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) {
+    FilePath actual_path = cache_->GetCacheFilePath(
+        resource_id,
+        md5,
+        DriveCache::CACHE_TYPE_TMP,
+        DriveCache::CACHED_FILE_FROM_SERVER);
+    FilePath expected_path =
+        cache_->GetCacheDirectoryPath(DriveCache::CACHE_TYPE_TMP);
+    expected_path = expected_path.Append(expected_filename);
+    EXPECT_EQ(expected_path, actual_path);
+
+    FilePath base_name = actual_path.BaseName();
+
+    // 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) {
+    FilePath path = GetCacheFilePath(
+        resource_id, "*",
+        (test_util::ToCacheEntry(expected_cache_state_).is_pinned() ?
+         DriveCache::CACHE_TYPE_PERSISTENT :
+         DriveCache::CACHE_TYPE_TMP),
+        DriveCache::CACHED_FILE_FROM_SERVER);
+    file_util::FileEnumerator enumerator(path.DirName(), false,
+                                         file_util::FileEnumerator::FILES,
+                                         path.BaseName().value());
+    size_t num_files_found = 0;
+    for (FilePath current = enumerator.Next(); !current.empty();
+         current = enumerator.Next()) {
+      ++num_files_found;
+      EXPECT_EQ(util::EscapeCacheFileName(resource_id) +
+                FilePath::kExtensionSeparator +
+                util::EscapeCacheFileName(md5),
+                current.BaseName().value());
+    }
+    return num_files_found;
+  }
+
+  MessageLoopForUI message_loop_;
+  // The order of the test threads is important, do not change the order.
+  // See also content/browser/browser_thread_imple.cc.
+  content::TestBrowserThread ui_thread_;
+  content::TestBrowserThread io_thread_;
+  scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
+  scoped_ptr<TestingProfile> profile_;
+  DriveCache* cache_;
+  MockFreeDiskSpaceGetter* mock_free_disk_space_checker_;
+  scoped_ptr<StrictMock<MockDriveCacheObserver> > mock_cache_observer_;
+
+  int num_callback_invocations_;
+  DriveFileError expected_error_;
+  int expected_cache_state_;
+  DriveCache::CacheSubDirectoryType expected_sub_dir_type_;
+  bool expected_success_;
+  bool expect_outgoing_symlink_;
+  std::string expected_file_extension_;
+  int root_feed_changestamp_;
+};
+
+TEST_F(DriveCacheTest, InitializeCache) {
+  PrepareForInitCacheTest();
+  TestInitializeCache();
+}
+
+TEST_F(DriveCacheTest, GetCacheFilePath) {
+  // Use alphanumeric characters for resource id.
+  std::string resource_id("pdf:1a2b");
+  std::string md5("abcdef0123456789");
+  TestGetCacheFilePath(resource_id, md5,
+                       resource_id + FilePath::kExtensionSeparator + md5);
+  EXPECT_EQ(0, num_callback_invocations_);
+
+  // 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);
+  num_callback_invocations_ = 0;
+  TestGetCacheFilePath(resource_id, md5,
+                       escaped_resource_id + FilePath::kExtensionSeparator +
+                       escaped_md5);
+  EXPECT_EQ(0, num_callback_invocations_);
+}
+
+TEST_F(DriveCacheTest, StoreToCacheSimple) {
+  EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace())
+      .Times(AtLeast(1)).WillRepeatedly(Return(kLotsOfSpace));
+
+  std::string resource_id("pdf:1a2b");
+  std::string md5("abcdef0123456789");
+
+  // Store an existing file.
+  TestStoreToCache(resource_id, md5,
+                   test_util::GetTestFilePath("gdata/root_feed.json"),
+                   DRIVE_FILE_OK, test_util::TEST_CACHE_STATE_PRESENT,
+                   DriveCache::CACHE_TYPE_TMP);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Store a non-existent file to the same |resource_id| and |md5|.
+  num_callback_invocations_ = 0;
+  TestStoreToCache(resource_id, md5, FilePath("./non_existent.json"),
+                   DRIVE_FILE_ERROR_FAILED,
+                   test_util::TEST_CACHE_STATE_PRESENT,
+                   DriveCache::CACHE_TYPE_TMP);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Store a different existing file to the same |resource_id| but different
+  // |md5|.
+  md5 = "new_md5";
+  num_callback_invocations_ = 0;
+  TestStoreToCache(resource_id, md5,
+                   test_util::GetTestFilePath("gdata/subdir_feed.json"),
+                   DRIVE_FILE_OK, test_util::TEST_CACHE_STATE_PRESENT,
+                   DriveCache::CACHE_TYPE_TMP);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // 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(DriveCacheTest, GetFromCacheSimple) {
+  EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace())
+      .Times(AtLeast(1)).WillRepeatedly(Return(kLotsOfSpace));
+
+  std::string resource_id("pdf:1a2b");
+  std::string md5("abcdef0123456789");
+  // First store a file to cache.
+  TestStoreToCache(resource_id, md5,
+                   test_util::GetTestFilePath("gdata/root_feed.json"),
+                   DRIVE_FILE_OK, test_util::TEST_CACHE_STATE_PRESENT,
+                   DriveCache::CACHE_TYPE_TMP);
+
+  // Then try to get the existing file from cache.
+  num_callback_invocations_ = 0;
+  TestGetFileFromCacheByResourceIdAndMd5(
+      resource_id, md5, DRIVE_FILE_OK, md5);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Get file from cache with same resource id as existing file but different
+  // md5.
+  num_callback_invocations_ = 0;
+  TestGetFileFromCacheByResourceIdAndMd5(
+      resource_id, "9999", DRIVE_FILE_ERROR_NOT_FOUND, md5);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Get file from cache with different resource id from existing file but same
+  // md5.
+  num_callback_invocations_ = 0;
+  resource_id = "document:1a2b";
+  TestGetFileFromCacheByResourceIdAndMd5(
+      resource_id, md5, DRIVE_FILE_ERROR_NOT_FOUND, md5);
+  EXPECT_EQ(1, num_callback_invocations_);
+}
+
+TEST_F(DriveCacheTest, RemoveFromCacheSimple) {
+  EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace())
+      .Times(AtLeast(1)).WillRepeatedly(Return(kLotsOfSpace));
+
+  // 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,
+                   test_util::GetTestFilePath("gdata/root_feed.json"),
+                   DRIVE_FILE_OK, test_util::TEST_CACHE_STATE_PRESENT,
+                   DriveCache::CACHE_TYPE_TMP);
+
+  // Then try to remove existing file from cache.
+  num_callback_invocations_ = 0;
+  TestRemoveFromCache(resource_id, DRIVE_FILE_OK);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Repeat using non-alphanumeric characters for resource id, including '.'
+  // which is an extension separator.
+  resource_id = "pdf:`~!@#$%^&*()-_=+[{|]}\\;',<.>/?";
+  TestStoreToCache(resource_id, md5,
+                   test_util::GetTestFilePath("gdata/root_feed.json"),
+                   DRIVE_FILE_OK, test_util::TEST_CACHE_STATE_PRESENT,
+                   DriveCache::CACHE_TYPE_TMP);
+
+  num_callback_invocations_ = 0;
+  TestRemoveFromCache(resource_id, DRIVE_FILE_OK);
+  EXPECT_EQ(1, num_callback_invocations_);
+}
+
+TEST_F(DriveCacheTest, PinAndUnpin) {
+  EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace())
+      .Times(AtLeast(1)).WillRepeatedly(Return(kLotsOfSpace));
+
+  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,
+                   test_util::GetTestFilePath("gdata/root_feed.json"),
+                   DRIVE_FILE_OK, test_util::TEST_CACHE_STATE_PRESENT,
+                   DriveCache::CACHE_TYPE_TMP);
+
+  // Pin the existing file in cache.
+  num_callback_invocations_ = 0;
+  TestPin(resource_id, md5, DRIVE_FILE_OK,
+          test_util::TEST_CACHE_STATE_PRESENT |
+          test_util::TEST_CACHE_STATE_PINNED |
+          test_util::TEST_CACHE_STATE_PERSISTENT,
+          DriveCache::CACHE_TYPE_PERSISTENT);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Unpin the existing file in cache.
+  num_callback_invocations_ = 0;
+  TestUnpin(resource_id, md5, DRIVE_FILE_OK,
+            test_util::TEST_CACHE_STATE_PRESENT,
+            DriveCache::CACHE_TYPE_TMP);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Pin back the same existing file in cache.
+  num_callback_invocations_ = 0;
+  TestPin(resource_id, md5, DRIVE_FILE_OK,
+          test_util::TEST_CACHE_STATE_PRESENT |
+          test_util::TEST_CACHE_STATE_PINNED |
+          test_util::TEST_CACHE_STATE_PERSISTENT,
+          DriveCache::CACHE_TYPE_PERSISTENT);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // 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);
+
+  num_callback_invocations_ = 0;
+  TestPin(resource_id, md5, DRIVE_FILE_OK,
+          test_util::TEST_CACHE_STATE_PINNED,
+          DriveCache::CACHE_TYPE_TMP);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Unpin the previously pinned non-existent file in cache.
+  num_callback_invocations_ = 0;
+  TestUnpin(resource_id, md5, DRIVE_FILE_OK,
+            test_util::TEST_CACHE_STATE_NONE,
+            DriveCache::CACHE_TYPE_TMP);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // 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);
+
+  num_callback_invocations_ = 0;
+  TestUnpin(resource_id, md5, DRIVE_FILE_ERROR_NOT_FOUND,
+            test_util::TEST_CACHE_STATE_NONE,
+            DriveCache::CACHE_TYPE_TMP /* non-applicable */);
+  EXPECT_EQ(1, num_callback_invocations_);
+}
+
+TEST_F(DriveCacheTest, StoreToCachePinned) {
+  EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace())
+      .Times(AtLeast(1)).WillRepeatedly(Return(kLotsOfSpace));
+
+  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, DRIVE_FILE_OK,
+          test_util::TEST_CACHE_STATE_PINNED,
+          DriveCache::CACHE_TYPE_TMP);
+
+  // Store an existing file to a previously pinned file.
+  num_callback_invocations_ = 0;
+  TestStoreToCache(resource_id, md5,
+                   test_util::GetTestFilePath("gdata/root_feed.json"),
+                   DRIVE_FILE_OK,
+                   test_util::TEST_CACHE_STATE_PRESENT |
+                   test_util::TEST_CACHE_STATE_PINNED |
+                   test_util::TEST_CACHE_STATE_PERSISTENT,
+                   DriveCache::CACHE_TYPE_PERSISTENT);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Store a non-existent file to a previously pinned and stored file.
+  num_callback_invocations_ = 0;
+  TestStoreToCache(resource_id, md5, FilePath("./non_existent.json"),
+                   DRIVE_FILE_ERROR_FAILED,
+                   test_util::TEST_CACHE_STATE_PRESENT |
+                   test_util::TEST_CACHE_STATE_PINNED |
+                   test_util::TEST_CACHE_STATE_PERSISTENT,
+                   DriveCache::CACHE_TYPE_PERSISTENT);
+  EXPECT_EQ(1, num_callback_invocations_);
+}
+
+TEST_F(DriveCacheTest, GetFromCachePinned) {
+  EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace())
+      .Times(AtLeast(1)).WillRepeatedly(Return(kLotsOfSpace));
+
+  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, DRIVE_FILE_OK,
+          test_util::TEST_CACHE_STATE_PINNED,
+          DriveCache::CACHE_TYPE_TMP);
+
+  // Get the non-existent pinned file from cache.
+  num_callback_invocations_ = 0;
+  TestGetFileFromCacheByResourceIdAndMd5(
+      resource_id, md5, DRIVE_FILE_ERROR_NOT_FOUND, md5);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Store an existing file to the previously pinned non-existent file.
+  TestStoreToCache(resource_id, md5,
+                   test_util::GetTestFilePath("gdata/root_feed.json"),
+                   DRIVE_FILE_OK,
+                   test_util::TEST_CACHE_STATE_PRESENT |
+                   test_util::TEST_CACHE_STATE_PINNED |
+                   test_util::TEST_CACHE_STATE_PERSISTENT,
+                   DriveCache::CACHE_TYPE_PERSISTENT);
+
+  // Get the previously pinned and stored file from cache.
+  num_callback_invocations_ = 0;
+  TestGetFileFromCacheByResourceIdAndMd5(
+      resource_id, md5, DRIVE_FILE_OK, md5);
+  EXPECT_EQ(1, num_callback_invocations_);
+}
+
+TEST_F(DriveCacheTest, RemoveFromCachePinned) {
+  EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace())
+      .Times(AtLeast(1)).WillRepeatedly(Return(kLotsOfSpace));
+
+  // 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,
+                   test_util::GetTestFilePath("gdata/root_feed.json"),
+                   DRIVE_FILE_OK, test_util::TEST_CACHE_STATE_PRESENT,
+                   DriveCache::CACHE_TYPE_TMP);
+  TestPin(resource_id, md5, DRIVE_FILE_OK,
+          test_util::TEST_CACHE_STATE_PRESENT |
+          test_util::TEST_CACHE_STATE_PINNED |
+          test_util::TEST_CACHE_STATE_PERSISTENT,
+          DriveCache::CACHE_TYPE_PERSISTENT);
+
+  // Remove |resource_id| from cache.
+  num_callback_invocations_ = 0;
+  TestRemoveFromCache(resource_id, DRIVE_FILE_OK);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // 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,
+                   test_util::GetTestFilePath("gdata/root_feed.json"),
+                   DRIVE_FILE_OK, test_util::TEST_CACHE_STATE_PRESENT,
+                   DriveCache::CACHE_TYPE_TMP);
+  TestPin(resource_id, md5, DRIVE_FILE_OK,
+          test_util::TEST_CACHE_STATE_PRESENT |
+          test_util::TEST_CACHE_STATE_PINNED |
+          test_util::TEST_CACHE_STATE_PERSISTENT,
+          DriveCache::CACHE_TYPE_PERSISTENT);
+
+  num_callback_invocations_ = 0;
+  TestRemoveFromCache(resource_id, DRIVE_FILE_OK);
+  EXPECT_EQ(1, num_callback_invocations_);
+}
+
+TEST_F(DriveCacheTest, DirtyCacheSimple) {
+  EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace())
+      .Times(AtLeast(1)).WillRepeatedly(Return(kLotsOfSpace));
+
+  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,
+                   test_util::GetTestFilePath("gdata/root_feed.json"),
+                   DRIVE_FILE_OK, test_util::TEST_CACHE_STATE_PRESENT,
+                   DriveCache::CACHE_TYPE_TMP);
+
+  // Mark the file dirty.
+  num_callback_invocations_ = 0;
+  TestMarkDirty(resource_id, md5, DRIVE_FILE_OK,
+                test_util::TEST_CACHE_STATE_PRESENT |
+                test_util::TEST_CACHE_STATE_DIRTY |
+                test_util::TEST_CACHE_STATE_PERSISTENT,
+                DriveCache::CACHE_TYPE_PERSISTENT);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Commit the file dirty.
+  num_callback_invocations_ = 0;
+  TestCommitDirty(resource_id, md5, DRIVE_FILE_OK,
+                  test_util::TEST_CACHE_STATE_PRESENT |
+                  test_util::TEST_CACHE_STATE_DIRTY |
+                  test_util::TEST_CACHE_STATE_PERSISTENT,
+                  DriveCache::CACHE_TYPE_PERSISTENT);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Clear dirty state of the file.
+  num_callback_invocations_ = 0;
+  TestClearDirty(resource_id, md5, DRIVE_FILE_OK,
+                 test_util::TEST_CACHE_STATE_PRESENT,
+                 DriveCache::CACHE_TYPE_TMP);
+  EXPECT_EQ(1, num_callback_invocations_);
+}
+
+TEST_F(DriveCacheTest, DirtyCachePinned) {
+  EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace())
+      .Times(AtLeast(1)).WillRepeatedly(Return(kLotsOfSpace));
+
+  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,
+                   test_util::GetTestFilePath("gdata/root_feed.json"),
+                   DRIVE_FILE_OK, test_util::TEST_CACHE_STATE_PRESENT,
+                   DriveCache::CACHE_TYPE_TMP);
+  TestPin(resource_id, md5, DRIVE_FILE_OK,
+          test_util::TEST_CACHE_STATE_PRESENT |
+          test_util::TEST_CACHE_STATE_PINNED |
+          test_util::TEST_CACHE_STATE_PERSISTENT,
+          DriveCache::CACHE_TYPE_PERSISTENT);
+
+  // Mark the file dirty.
+  num_callback_invocations_ = 0;
+  TestMarkDirty(resource_id, md5, DRIVE_FILE_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,
+                DriveCache::CACHE_TYPE_PERSISTENT);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Commit the file dirty.
+  num_callback_invocations_ = 0;
+  TestCommitDirty(resource_id, md5, DRIVE_FILE_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,
+                  DriveCache::CACHE_TYPE_PERSISTENT);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Clear dirty state of the file.
+  num_callback_invocations_ = 0;
+  TestClearDirty(resource_id, md5, DRIVE_FILE_OK,
+                 test_util::TEST_CACHE_STATE_PRESENT |
+                 test_util::TEST_CACHE_STATE_PINNED |
+                 test_util::TEST_CACHE_STATE_PERSISTENT,
+                 DriveCache::CACHE_TYPE_PERSISTENT);
+  EXPECT_EQ(1, num_callback_invocations_);
+}
+
+// Test is disabled because it is flaky (http://crbug.com/134146)
+TEST_F(DriveCacheTest, PinAndUnpinDirtyCache) {
+  EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace())
+      .Times(AtLeast(1)).WillRepeatedly(Return(kLotsOfSpace));
+
+  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,
+                   test_util::GetTestFilePath("gdata/root_feed.json"),
+                   DRIVE_FILE_OK, test_util::TEST_CACHE_STATE_PRESENT,
+                   DriveCache::CACHE_TYPE_TMP);
+  TestMarkDirty(resource_id, md5, DRIVE_FILE_OK,
+                test_util::TEST_CACHE_STATE_PRESENT |
+                test_util::TEST_CACHE_STATE_DIRTY |
+                test_util::TEST_CACHE_STATE_PERSISTENT,
+                DriveCache::CACHE_TYPE_PERSISTENT);
+
+  // Verifies dirty file exists.
+  FilePath dirty_path = GetCacheFilePath(
+      resource_id,
+      md5,
+      DriveCache::CACHE_TYPE_PERSISTENT,
+      DriveCache::CACHED_FILE_LOCALLY_MODIFIED);
+  EXPECT_TRUE(file_util::PathExists(dirty_path));
+
+  // Pin the dirty file.
+  TestPin(resource_id, md5, DRIVE_FILE_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,
+          DriveCache::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, DRIVE_FILE_OK,
+            test_util::TEST_CACHE_STATE_PRESENT |
+            test_util::TEST_CACHE_STATE_DIRTY |
+            test_util::TEST_CACHE_STATE_PERSISTENT,
+            DriveCache::CACHE_TYPE_PERSISTENT);
+
+  // Verify dirty file still exist at the same pathname.
+  EXPECT_TRUE(file_util::PathExists(dirty_path));
+}
+
+TEST_F(DriveCacheTest, DirtyCacheRepetitive) {
+  EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace())
+      .Times(AtLeast(1)).WillRepeatedly(Return(kLotsOfSpace));
+
+  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,
+                   test_util::GetTestFilePath("gdata/root_feed.json"),
+                   DRIVE_FILE_OK, test_util::TEST_CACHE_STATE_PRESENT,
+                   DriveCache::CACHE_TYPE_TMP);
+
+  // Mark the file dirty.
+  num_callback_invocations_ = 0;
+  TestMarkDirty(resource_id, md5, DRIVE_FILE_OK,
+                test_util::TEST_CACHE_STATE_PRESENT |
+                test_util::TEST_CACHE_STATE_DIRTY |
+                test_util::TEST_CACHE_STATE_PERSISTENT,
+                DriveCache::CACHE_TYPE_PERSISTENT);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Again, mark the file dirty.  Nothing should change.
+  num_callback_invocations_ = 0;
+  TestMarkDirty(resource_id, md5, DRIVE_FILE_OK,
+                test_util::TEST_CACHE_STATE_PRESENT |
+                test_util::TEST_CACHE_STATE_DIRTY |
+                test_util::TEST_CACHE_STATE_PERSISTENT,
+                DriveCache::CACHE_TYPE_PERSISTENT);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Commit the file dirty.  Outgoing symlink should be created.
+  num_callback_invocations_ = 0;
+  TestCommitDirty(resource_id, md5, DRIVE_FILE_OK,
+                  test_util::TEST_CACHE_STATE_PRESENT |
+                  test_util::TEST_CACHE_STATE_DIRTY |
+                  test_util::TEST_CACHE_STATE_PERSISTENT,
+                  DriveCache::CACHE_TYPE_PERSISTENT);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Again, commit the file dirty.  Nothing should change.
+  num_callback_invocations_ = 0;
+  TestCommitDirty(resource_id, md5, DRIVE_FILE_OK,
+                  test_util::TEST_CACHE_STATE_PRESENT |
+                  test_util::TEST_CACHE_STATE_DIRTY |
+                  test_util::TEST_CACHE_STATE_PERSISTENT,
+                  DriveCache::CACHE_TYPE_PERSISTENT);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Mark the file dirty agian after it's being committed.  Outgoing symlink
+  // should be deleted.
+  num_callback_invocations_ = 0;
+  TestMarkDirty(resource_id, md5, DRIVE_FILE_OK,
+                test_util::TEST_CACHE_STATE_PRESENT |
+                test_util::TEST_CACHE_STATE_DIRTY |
+                test_util::TEST_CACHE_STATE_PERSISTENT,
+                DriveCache::CACHE_TYPE_PERSISTENT);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Commit the file dirty.  Outgoing symlink should be created again.
+  num_callback_invocations_ = 0;
+  TestCommitDirty(resource_id, md5, DRIVE_FILE_OK,
+                  test_util::TEST_CACHE_STATE_PRESENT |
+                  test_util::TEST_CACHE_STATE_DIRTY |
+                  test_util::TEST_CACHE_STATE_PERSISTENT,
+                  DriveCache::CACHE_TYPE_PERSISTENT);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Clear dirty state of the file.
+  num_callback_invocations_ = 0;
+  TestClearDirty(resource_id, md5, DRIVE_FILE_OK,
+                 test_util::TEST_CACHE_STATE_PRESENT,
+                 DriveCache::CACHE_TYPE_TMP);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Again, clear dirty state of the file, which is no longer dirty.
+  num_callback_invocations_ = 0;
+  TestClearDirty(resource_id, md5, DRIVE_FILE_ERROR_INVALID_OPERATION,
+                 test_util::TEST_CACHE_STATE_PRESENT,
+                 DriveCache::CACHE_TYPE_TMP);
+  EXPECT_EQ(1, num_callback_invocations_);
+}
+
+TEST_F(DriveCacheTest, DirtyCacheInvalid) {
+  EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace())
+      .Times(AtLeast(1)).WillRepeatedly(Return(kLotsOfSpace));
+
+  std::string resource_id("pdf:1a2b");
+  std::string md5("abcdef0123456789");
+
+  // Mark a non-existent file dirty.
+  num_callback_invocations_ = 0;
+  TestMarkDirty(resource_id, md5, DRIVE_FILE_ERROR_NOT_FOUND,
+                test_util::TEST_CACHE_STATE_NONE,
+                DriveCache::CACHE_TYPE_TMP);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Commit a non-existent file dirty.
+  num_callback_invocations_ = 0;
+  TestCommitDirty(resource_id, md5, DRIVE_FILE_ERROR_NOT_FOUND,
+                  test_util::TEST_CACHE_STATE_NONE,
+                  DriveCache::CACHE_TYPE_TMP);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Clear dirty state of a non-existent file.
+  num_callback_invocations_ = 0;
+  TestClearDirty(resource_id, md5, DRIVE_FILE_ERROR_NOT_FOUND,
+                 test_util::TEST_CACHE_STATE_NONE,
+                 DriveCache::CACHE_TYPE_TMP);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Store a file to cache.
+  TestStoreToCache(resource_id, md5,
+                   test_util::GetTestFilePath("gdata/root_feed.json"),
+                   DRIVE_FILE_OK, test_util::TEST_CACHE_STATE_PRESENT,
+                   DriveCache::CACHE_TYPE_TMP);
+
+  // Commit a non-dirty existing file dirty.
+  num_callback_invocations_ = 0;
+  TestCommitDirty(resource_id, md5, DRIVE_FILE_ERROR_INVALID_OPERATION,
+                 test_util::TEST_CACHE_STATE_PRESENT,
+                 DriveCache::CACHE_TYPE_TMP);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Clear dirty state of a non-dirty existing file.
+  num_callback_invocations_ = 0;
+  TestClearDirty(resource_id, md5, DRIVE_FILE_ERROR_INVALID_OPERATION,
+                 test_util::TEST_CACHE_STATE_PRESENT,
+                 DriveCache::CACHE_TYPE_TMP);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // 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, DRIVE_FILE_OK,
+                test_util::TEST_CACHE_STATE_PRESENT |
+                test_util::TEST_CACHE_STATE_DIRTY |
+                test_util::TEST_CACHE_STATE_PERSISTENT,
+                DriveCache::CACHE_TYPE_PERSISTENT);
+  num_callback_invocations_ = 0;
+  md5 = "new_md5";
+  TestStoreToCache(resource_id, md5,
+                   test_util::GetTestFilePath("gdata/subdir_feed.json"),
+                   DRIVE_FILE_ERROR_IN_USE,
+                   test_util::TEST_CACHE_STATE_PRESENT |
+                   test_util::TEST_CACHE_STATE_DIRTY |
+                   test_util::TEST_CACHE_STATE_PERSISTENT,
+                   DriveCache::CACHE_TYPE_PERSISTENT);
+  EXPECT_EQ(1, num_callback_invocations_);
+}
+
+TEST_F(DriveCacheTest, RemoveFromDirtyCache) {
+  EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace())
+      .Times(AtLeast(1)).WillRepeatedly(Return(kLotsOfSpace));
+
+  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,
+                   test_util::GetTestFilePath("gdata/root_feed.json"),
+                   DRIVE_FILE_OK, test_util::TEST_CACHE_STATE_PRESENT,
+                   DriveCache::CACHE_TYPE_TMP);
+  TestPin(resource_id, md5, DRIVE_FILE_OK,
+          test_util::TEST_CACHE_STATE_PRESENT |
+          test_util::TEST_CACHE_STATE_PINNED |
+          test_util::TEST_CACHE_STATE_PERSISTENT,
+          DriveCache::CACHE_TYPE_PERSISTENT);
+  TestMarkDirty(resource_id, md5, DRIVE_FILE_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,
+                DriveCache::CACHE_TYPE_PERSISTENT);
+  TestCommitDirty(resource_id, md5, DRIVE_FILE_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,
+                  DriveCache::CACHE_TYPE_PERSISTENT);
+
+  // Try to remove the file.  Since file is dirty, it and the corresponding
+  // pinned and outgoing symlinks should not be removed.
+  num_callback_invocations_ = 0;
+  TestRemoveFromCache(resource_id, DRIVE_FILE_OK);
+  EXPECT_EQ(1, num_callback_invocations_);
+}
+
+TEST_F(DriveCacheTest, MountUnmount) {
+  EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace())
+      .Times(AtLeast(1)).WillRepeatedly(Return(kLotsOfSpace));
+
+  FilePath file_path;
+  std::string resource_id("pdf:1a2b");
+  std::string md5("abcdef0123456789");
+
+  // First store a file to cache in the tmp subdir.
+  TestStoreToCache(resource_id, md5,
+                   test_util::GetTestFilePath("gdata/root_feed.json"),
+                   DRIVE_FILE_OK, test_util::TEST_CACHE_STATE_PRESENT,
+                   DriveCache::CACHE_TYPE_TMP);
+
+  // Mark the file mounted.
+  num_callback_invocations_ = 0;
+  file_path = cache_->GetCacheFilePath(resource_id, md5,
+                                       DriveCache::CACHE_TYPE_TMP,
+                                       DriveCache::CACHED_FILE_FROM_SERVER);
+  TestSetMountedState(resource_id, md5, file_path, true,
+                      DRIVE_FILE_OK,
+                      test_util::TEST_CACHE_STATE_PRESENT |
+                      test_util::TEST_CACHE_STATE_MOUNTED |
+                      test_util::TEST_CACHE_STATE_PERSISTENT,
+                      DriveCache::CACHE_TYPE_PERSISTENT);
+  EXPECT_EQ(1, num_callback_invocations_);
+  EXPECT_TRUE(CacheEntryExists(resource_id, md5));
+
+  // Clear mounted state of the file.
+  num_callback_invocations_ = 0;
+  file_path = cache_->GetCacheFilePath(resource_id,
+                                       md5,
+                                       DriveCache::CACHE_TYPE_PERSISTENT,
+                                       DriveCache::CACHED_FILE_MOUNTED);
+  TestSetMountedState(resource_id, md5, file_path, false,
+                      DRIVE_FILE_OK,
+                      test_util::TEST_CACHE_STATE_PRESENT,
+                      DriveCache::CACHE_TYPE_TMP);
+  EXPECT_EQ(1, num_callback_invocations_);
+  EXPECT_TRUE(CacheEntryExists(resource_id, md5));
+
+  // Try to remove the file.
+  num_callback_invocations_ = 0;
+  TestRemoveFromCache(resource_id, DRIVE_FILE_OK);
+  EXPECT_EQ(1, num_callback_invocations_);
+}
+
+TEST_F(DriveCacheTest, GetResourceIdsOfBacklogOnUIThread) {
+  PrepareForInitCacheTest();
+
+  std::vector<std::string> to_fetch;
+  std::vector<std::string> to_upload;
+  cache_->GetResourceIdsOfBacklogOnUIThread(
+      base::Bind(&OnGetResourceIdsOfBacklog, &to_fetch, &to_upload));
+  test_util::RunBlockingPoolTask();
+
+  sort(to_fetch.begin(), to_fetch.end());
+  ASSERT_EQ(1U, to_fetch.size());
+  EXPECT_EQ("pinned:non-existent", to_fetch[0]);
+
+  sort(to_upload.begin(), to_upload.end());
+  ASSERT_EQ(2U, to_upload.size());
+  EXPECT_EQ("dirty:existing", to_upload[0]);
+  EXPECT_EQ("dirty_and_pinned:existing", to_upload[1]);
+}
+
+TEST_F(DriveCacheTest, GetResourceIdsOfExistingPinnedFilesOnUIThread) {
+  PrepareForInitCacheTest();
+
+  std::vector<std::string> resource_ids;
+  cache_->GetResourceIdsOfExistingPinnedFilesOnUIThread(
+      base::Bind(&OnGetResourceIds, &resource_ids));
+  test_util::RunBlockingPoolTask();
+
+  sort(resource_ids.begin(), resource_ids.end());
+  ASSERT_EQ(2U, resource_ids.size());
+  EXPECT_EQ("dirty_and_pinned:existing", resource_ids[0]);
+  EXPECT_EQ("pinned:existing", resource_ids[1]);
+}
+
+TEST_F(DriveCacheTest, GetResourceIdsOfAllFilesOnUIThread) {
+  PrepareForInitCacheTest();
+
+  std::vector<std::string> resource_ids;
+  cache_->GetResourceIdsOfAllFilesOnUIThread(
+      base::Bind(&OnGetResourceIds, &resource_ids));
+  test_util::RunBlockingPoolTask();
+
+  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]);
+}
+
+
+TEST_F(DriveCacheTest, ClearAllOnUIThread) {
+  PrepareForInitCacheTest();
+
+  EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace())
+      .Times(AtLeast(1)).WillRepeatedly(Return(kLotsOfSpace));
+
+  std::string resource_id("pdf:1a2b");
+  std::string md5("abcdef0123456789");
+
+  // Store an existing file.
+  TestStoreToCache(resource_id, md5,
+                   test_util::GetTestFilePath("gdata/root_feed.json"),
+                   DRIVE_FILE_OK, test_util::TEST_CACHE_STATE_PRESENT,
+                   DriveCache::CACHE_TYPE_TMP);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // Verify that there's only one cached file.
+  EXPECT_EQ(1U, CountCacheFiles(resource_id, md5));
+
+  // Clear cache.
+  DriveFileError error = DRIVE_FILE_OK;
+  FilePath file_path;
+  cache_->ClearAllOnUIThread(base::Bind(&OnClearAll,
+                                        &error,
+                                        &file_path));
+  test_util::RunBlockingPoolTask();
+  EXPECT_EQ(DRIVE_FILE_OK, error);
+
+  // Verify that all the cache is removed.
+  VerifyRemoveFromCache(error, resource_id, md5);
+  EXPECT_EQ(0U, CountCacheFiles(resource_id, md5));
+}
+
+TEST_F(DriveCacheTest, StoreToCacheNoSpace) {
+  EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace())
+      .Times(AtLeast(1)).WillRepeatedly(Return(0));
+
+  std::string resource_id("pdf:1a2b");
+  std::string md5("abcdef0123456789");
+
+  // Try to store an existing file.
+  TestStoreToCache(resource_id, md5,
+                   test_util::GetTestFilePath("gdata/root_feed.json"),
+                   DRIVE_FILE_ERROR_NO_SPACE,
+                   test_util::TEST_CACHE_STATE_NONE,
+                   DriveCache::CACHE_TYPE_TMP);
+  EXPECT_EQ(1, num_callback_invocations_);
+
+  // 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(DriveCacheExtraTest, InitializationFailure) {
+  MessageLoopForUI message_loop;
+  content::TestBrowserThread ui_thread(content::BrowserThread::UI,
+                                       &message_loop);
+
+  scoped_refptr<base::SequencedWorkerPool> pool =
+      content::BrowserThread::GetBlockingPool();
+
+  // Set the cache root to a non existent path, so the initialization fails.
+  DriveCache* cache = DriveCache::CreateDriveCacheOnUIThread(
+      FilePath::FromUTF8Unsafe("/somewhere/nonexistent/blah/blah"),
+      pool->GetSequencedTaskRunner(pool->GetSequenceToken()));
+
+  bool success = false;
+  cache->RequestInitializeOnUIThread(
+      base::Bind(&test_util::CopyResultFromInitializeCacheCallback,
+                 &success));
+  test_util::RunBlockingPoolTask();
+  EXPECT_FALSE(success);
+
+  cache->DestroyOnUIThread();
+  test_util::RunBlockingPoolTask();
+}
+
+}   // namespace gdata