| // 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/gdata/drive_file_system.h" | 
 |  | 
 | #include <string> | 
 | #include <vector> | 
 |  | 
 | #include "base/bind.h" | 
 | #include "base/file_path.h" | 
 | #include "base/file_util.h" | 
 | #include "base/json/json_file_value_serializer.h" | 
 | #include "base/memory/scoped_ptr.h" | 
 | #include "base/message_loop.h" | 
 | #include "base/path_service.h" | 
 | #include "base/scoped_temp_dir.h" | 
 | #include "base/stringprintf.h" | 
 | #include "base/threading/sequenced_worker_pool.h" | 
 | #include "base/values.h" | 
 | #include "chrome/browser/chromeos/gdata/drive.pb.h" | 
 | #include "chrome/browser/chromeos/gdata/drive_file_system_util.h" | 
 | #include "chrome/browser/chromeos/gdata/drive_function_remove.h" | 
 | #include "chrome/browser/chromeos/gdata/drive_test_util.h" | 
 | #include "chrome/browser/chromeos/gdata/drive_uploader.h" | 
 | #include "chrome/browser/chromeos/gdata/drive_webapps_registry.h" | 
 | #include "chrome/browser/chromeos/gdata/mock_directory_change_observer.h" | 
 | #include "chrome/browser/chromeos/gdata/mock_drive_cache_observer.h" | 
 | #include "chrome/browser/chromeos/gdata/mock_drive_service.h" | 
 | #include "chrome/browser/chromeos/gdata/mock_drive_uploader.h" | 
 | #include "chrome/browser/chromeos/gdata/mock_drive_web_apps_registry.h" | 
 | #include "chrome/browser/chromeos/gdata/mock_free_disk_space_getter.h" | 
 | #include "chrome/browser/google_apis/drive_api_parser.h" | 
 | #include "chrome/common/chrome_paths.h" | 
 | #include "chrome/test/base/testing_profile.h" | 
 | #include "content/public/browser/browser_thread.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::AnyNumber; | 
 | using ::testing::AtLeast; | 
 | using ::testing::Eq; | 
 | using ::testing::NotNull; | 
 | using ::testing::Return; | 
 | using ::testing::StrictMock; | 
 | using ::testing::_; | 
 |  | 
 | namespace gdata { | 
 | namespace { | 
 |  | 
 | const char kSymLinkToDevNull[] = "/dev/null"; | 
 |  | 
 | const int64 kLotsOfSpace = kMinFreeSpace * 10; | 
 |  | 
 | struct SearchResultPair { | 
 |   const char* path; | 
 |   const bool is_directory; | 
 | }; | 
 |  | 
 | // Callback to DriveFileSystem::Search used in ContentSearch tests. | 
 | // Verifies returned vector of results. | 
 | void DriveSearchCallback( | 
 |     MessageLoop* message_loop, | 
 |     const SearchResultPair* expected_results, | 
 |     size_t expected_results_size, | 
 |     DriveFileError error, | 
 |     const GURL& next_feed, | 
 |     scoped_ptr<std::vector<SearchResultInfo> > results) { | 
 |   ASSERT_TRUE(results.get()); | 
 |   ASSERT_EQ(expected_results_size, results->size()); | 
 |  | 
 |   for (size_t i = 0; i < results->size(); i++) { | 
 |     EXPECT_EQ(FilePath(expected_results[i].path), | 
 |               results->at(i).path); | 
 |     EXPECT_EQ(expected_results[i].is_directory, | 
 |               results->at(i).is_directory); | 
 |   } | 
 |  | 
 |   message_loop->Quit(); | 
 | } | 
 |  | 
 | // Action used to set mock expectations for | 
 | // DriveServiceInterface::GetDocumentEntry(). | 
 | ACTION_P2(MockGetDocumentEntry, status, value) { | 
 |   base::MessageLoopProxy::current()->PostTask(FROM_HERE, | 
 |       base::Bind(arg1, status, base::Passed(value))); | 
 | } | 
 |  | 
 | // Action used to set mock expectations for | 
 | // DriveUploaderInterface::UploadExistingFile(). | 
 | ACTION_P4(MockUploadExistingFile, | 
 |           error, drive_path, local_file_path, document_entry) { | 
 |   scoped_ptr<DocumentEntry> scoped_document_entry(document_entry); | 
 |   base::MessageLoopProxy::current()->PostTask(FROM_HERE, | 
 |       base::Bind(arg5, | 
 |                  error, | 
 |                  drive_path, | 
 |                  local_file_path, | 
 |                  base::Passed(&scoped_document_entry))); | 
 |  | 
 |   const int kUploadId = 123; | 
 |   return kUploadId; | 
 | } | 
 |  | 
 | // Action used to set mock expectations for | 
 | // DriveUploaderInterface::UploadNewFile(). | 
 | ACTION(MockUploadNewFile) { | 
 |   scoped_ptr<base::Value> value = | 
 |       test_util::LoadJSONFile("gdata/uploaded_file.json"); | 
 |   scoped_ptr<DocumentEntry> document_entry( | 
 |       DocumentEntry::ExtractAndParse(*value)); | 
 |  | 
 |   base::MessageLoopProxy::current()->PostTask(FROM_HERE, | 
 |       base::Bind(arg7, | 
 |                  DRIVE_FILE_OK, | 
 |                  arg1, | 
 |                  arg2, | 
 |                  base::Passed(&document_entry))); | 
 |  | 
 |   const int kUploadId = 123; | 
 |   return kUploadId; | 
 | } | 
 |  | 
 | // Action used to set mock expectations for | 
 | // DriveFileSystem::CopyDocument(). | 
 | ACTION_P2(MockCopyDocument, status, value) { | 
 |   base::MessageLoopProxy::current()->PostTask( | 
 |       FROM_HERE, | 
 |       base::Bind(arg2, status, base::Passed(value))); | 
 | } | 
 |  | 
 | // Counts the number of files (not directories) in |entries|. | 
 | int CountFiles(const DriveEntryProtoVector& entries) { | 
 |   int num_files = 0; | 
 |   for (size_t i = 0; i < entries.size(); ++i) { | 
 |     if (!entries[i].file_info().is_directory()) | 
 |       ++num_files; | 
 |   } | 
 |   return num_files; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | class DriveFileSystemTest : public testing::Test { | 
 |  protected: | 
 |   DriveFileSystemTest() | 
 |       : ui_thread_(content::BrowserThread::UI, &message_loop_), | 
 |         io_thread_(content::BrowserThread::IO), | 
 |         cache_(NULL), | 
 |         file_system_(NULL), | 
 |         mock_drive_service_(NULL), | 
 |         mock_webapps_registry_(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); | 
 |  | 
 |     callback_helper_ = new CallbackHelper; | 
 |  | 
 |     // Allocate and keep a pointer to the mock, and inject it into the | 
 |     // DriveFileSystem object, which will own the mock object. | 
 |     mock_drive_service_ = new StrictMock<MockDriveService>; | 
 |  | 
 |     EXPECT_CALL(*mock_drive_service_, Initialize(profile_.get())).Times(1); | 
 |  | 
 |     // Likewise, this will be owned by DriveFileSystem. | 
 |     mock_free_disk_space_checker_ = new StrictMock<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_uploader_.reset(new StrictMock<MockDriveUploader>); | 
 |     mock_webapps_registry_.reset(new StrictMock<MockDriveWebAppsRegistry>); | 
 |  | 
 |     ASSERT_FALSE(file_system_); | 
 |     file_system_ = new DriveFileSystem(profile_.get(), | 
 |                                        cache_, | 
 |                                        mock_drive_service_, | 
 |                                        mock_uploader_.get(), | 
 |                                        mock_webapps_registry_.get(), | 
 |                                        blocking_task_runner_); | 
 |  | 
 |     mock_cache_observer_.reset(new StrictMock<MockDriveCacheObserver>); | 
 |     cache_->AddObserver(mock_cache_observer_.get()); | 
 |  | 
 |     mock_directory_observer_.reset(new StrictMock<MockDirectoryChangeObserver>); | 
 |     file_system_->AddObserver(mock_directory_observer_.get()); | 
 |  | 
 |     file_system_->Initialize(); | 
 |     cache_->RequestInitializeOnUIThreadForTesting(); | 
 |     test_util::RunBlockingPoolTask(); | 
 |   } | 
 |  | 
 |   virtual void TearDown() OVERRIDE { | 
 |     ASSERT_TRUE(file_system_); | 
 |     EXPECT_CALL(*mock_drive_service_, CancelAll()).Times(1); | 
 |     delete file_system_; | 
 |     file_system_ = NULL; | 
 |     delete mock_drive_service_; | 
 |     mock_drive_service_ = NULL; | 
 |     SetFreeDiskSpaceGetterForTesting(NULL); | 
 |     cache_->DestroyOnUIThread(); | 
 |     // The cache destruction requires to post a task to the blocking pool. | 
 |     test_util::RunBlockingPoolTask(); | 
 |  | 
 |     profile_.reset(NULL); | 
 |   } | 
 |  | 
 |   // Loads test json file as root ("/drive") element. | 
 |   void LoadRootFeedDocument(const std::string& filename) { | 
 |     LoadChangeFeed(filename, 0); | 
 |   } | 
 |  | 
 |   void LoadChangeFeed(const std::string& filename, | 
 |                       int largest_changestamp) { | 
 |     test_util::LoadChangeFeed(filename, | 
 |                               file_system_, | 
 |                               largest_changestamp, | 
 |                               root_feed_changestamp_); | 
 |     root_feed_changestamp_++; | 
 |   } | 
 |  | 
 |   void AddDirectoryFromFile(const FilePath& directory_path, | 
 |                             const std::string& filename) { | 
 |     scoped_ptr<Value> atom = test_util::LoadJSONFile(filename); | 
 |     ASSERT_TRUE(atom.get()); | 
 |     ASSERT_TRUE(atom->GetType() == Value::TYPE_DICTIONARY); | 
 |  | 
 |     DictionaryValue* dict_value = NULL; | 
 |     Value* entry_value = NULL; | 
 |     ASSERT_TRUE(atom->GetAsDictionary(&dict_value)); | 
 |     ASSERT_TRUE(dict_value->Get("entry", &entry_value)); | 
 |  | 
 |     DictionaryValue* entry_dict = NULL; | 
 |     ASSERT_TRUE(entry_value->GetAsDictionary(&entry_dict)); | 
 |  | 
 |     // Tweak entry title to match the last segment of the directory path | 
 |     // (new directory name). | 
 |     std::vector<FilePath::StringType> dir_parts; | 
 |     directory_path.GetComponents(&dir_parts); | 
 |     entry_dict->SetString("title.$t", dir_parts[dir_parts.size() - 1]); | 
 |  | 
 |     DriveFileError error; | 
 |     DriveFileSystem::CreateDirectoryParams params(directory_path, | 
 |                                                   directory_path, | 
 |                                                   false,  // is_exclusive | 
 |                                                   false,  // is_recursive | 
 |         base::Bind(&test_util::CopyErrorCodeFromFileOperationCallback, &error)); | 
 |     file_system_->AddNewDirectory(params, HTTP_SUCCESS, atom.Pass()); | 
 |     test_util::RunBlockingPoolTask(); | 
 |     EXPECT_EQ(DRIVE_FILE_OK, error); | 
 |   } | 
 |  | 
 |   bool RemoveEntry(const FilePath& file_path) { | 
 |     DriveFileError error; | 
 |     EXPECT_CALL(*mock_drive_service_, DeleteDocument(_, _)).Times(AnyNumber()); | 
 |     file_system_->remove_function_->Remove( | 
 |         file_path, false, | 
 |         base::Bind(&test_util::CopyErrorCodeFromFileOperationCallback, &error)); | 
 |  | 
 |     test_util::RunBlockingPoolTask(); | 
 |     return error == DRIVE_FILE_OK; | 
 |   } | 
 |  | 
 |   FilePath GetCachePathForFile(const std::string& resource_id, | 
 |                                const std::string& md5) { | 
 |     return cache_->GetCacheFilePath(resource_id, | 
 |                                     md5, | 
 |                                     DriveCache::CACHE_TYPE_TMP, | 
 |                                     DriveCache::CACHED_FILE_FROM_SERVER); | 
 |   } | 
 |  | 
 |   // Gets entry info by path synchronously. | 
 |   scoped_ptr<DriveEntryProto> GetEntryInfoByPathSync( | 
 |       const FilePath& file_path) { | 
 |     file_system_->GetEntryInfoByPath( | 
 |         file_path, | 
 |         base::Bind(&CallbackHelper::GetEntryInfoCallback, | 
 |                    callback_helper_.get())); | 
 |     test_util::RunBlockingPoolTask(); | 
 |  | 
 |     return callback_helper_->entry_proto_.Pass(); | 
 |   } | 
 |  | 
 |   // Gets directory info by path synchronously. | 
 |   scoped_ptr<DriveEntryProtoVector> ReadDirectoryByPathSync( | 
 |       const FilePath& file_path) { | 
 |     file_system_->ReadDirectoryByPath( | 
 |         file_path, | 
 |         base::Bind(&CallbackHelper::ReadDirectoryCallback, | 
 |                    callback_helper_.get())); | 
 |     test_util::RunBlockingPoolTask(); | 
 |  | 
 |     return callback_helper_->directory_entries_.Pass(); | 
 |   } | 
 |  | 
 |   // Returns true if an entry exists at |file_path|. | 
 |   bool EntryExists(const FilePath& file_path) { | 
 |     return GetEntryInfoByPathSync(file_path).get(); | 
 |   } | 
 |  | 
 |  | 
 |   // Gets the resource ID of |file_path|. Returns an empty string if not found. | 
 |   std::string GetResourceIdByPath(const FilePath& file_path) { | 
 |     scoped_ptr<DriveEntryProto> entry_proto = | 
 |         GetEntryInfoByPathSync(file_path); | 
 |     if (entry_proto.get()) | 
 |       return entry_proto->resource_id(); | 
 |     else | 
 |       return ""; | 
 |   } | 
 |  | 
 |   // 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(&DriveFileSystemTest::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); | 
 |   } | 
 |  | 
 |   // Returns true if the cache file exists for the given resource ID and MD5. | 
 |   bool CacheFileExists(const std::string& resource_id, | 
 |                        const std::string& md5) { | 
 |     const FilePath file_path = cache_->GetCacheFilePath( | 
 |         resource_id, | 
 |         md5, | 
 |         DriveCache::CACHE_TYPE_TMP, | 
 |         DriveCache::CACHED_FILE_FROM_SERVER); | 
 |     return file_util::PathExists(file_path); | 
 |   } | 
 |  | 
 |   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(&DriveFileSystemTest::VerifyCacheFileState, | 
 |                    base::Unretained(this))); | 
 |  | 
 |     test_util::RunBlockingPoolTask(); | 
 |   } | 
 |  | 
 |   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(&DriveFileSystemTest::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(&DriveFileSystemTest::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(&DriveFileSystemTest::VerifyCacheFileState, | 
 |                    base::Unretained(this))); | 
 |  | 
 |     test_util::RunBlockingPoolTask(); | 
 |   } | 
 |  | 
 |   // Verify the file identified by |resource_id| and |md5| is in the expected | 
 |   // cache state after |OpenFile|, that is, marked dirty and has no outgoing | 
 |   // symlink, etc. | 
 |   void VerifyCacheStateAfterOpenFile(DriveFileError error, | 
 |                                      const std::string& resource_id, | 
 |                                      const std::string& md5, | 
 |                                      const FilePath& cache_file_path) { | 
 |     expected_error_ = DRIVE_FILE_OK; | 
 |     expected_cache_state_ = (test_util::TEST_CACHE_STATE_PRESENT | | 
 |                              test_util::TEST_CACHE_STATE_DIRTY | | 
 |                              test_util::TEST_CACHE_STATE_PERSISTENT); | 
 |     expected_sub_dir_type_ = DriveCache::CACHE_TYPE_PERSISTENT; | 
 |     expect_outgoing_symlink_ = false; | 
 |     VerifyMarkDirty(resource_id, md5, error, cache_file_path); | 
 |   } | 
 |  | 
 |   // Verify the file identified by |resource_id| and |md5| is in the expected | 
 |   // cache state after |CloseFile|, that is, marked dirty and has an outgoing | 
 |   // symlink, etc. | 
 |   void VerifyCacheStateAfterCloseFile(DriveFileError error, | 
 |                                       const std::string& resource_id, | 
 |                                       const std::string& md5) { | 
 |     expected_error_ = DRIVE_FILE_OK; | 
 |     expected_cache_state_ = (test_util::TEST_CACHE_STATE_PRESENT | | 
 |                              test_util::TEST_CACHE_STATE_DIRTY | | 
 |                              test_util::TEST_CACHE_STATE_PERSISTENT); | 
 |     expected_sub_dir_type_ = DriveCache::CACHE_TYPE_PERSISTENT; | 
 |     expect_outgoing_symlink_ = true; | 
 |     VerifyCacheFileState(error, resource_id, md5); | 
 |   } | 
 |  | 
 |   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 dereferencing 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 dereferencing 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); | 
 |     } | 
 |   } | 
 |  | 
 |   void SetExpectationsForGetDocumentEntry(scoped_ptr<base::Value>* document, | 
 |                                           const std::string& resource_id) { | 
 |     EXPECT_CALL(*mock_drive_service_, GetDocumentEntry(resource_id, _)) | 
 |         .WillOnce(MockGetDocumentEntry(gdata::HTTP_SUCCESS, document)); | 
 |   } | 
 |  | 
 |   // Loads serialized proto file from GCache, and makes sure the root | 
 |   // filesystem has a root at 'drive' | 
 |   void TestLoadMetadataFromCache() { | 
 |     file_system_->LoadRootFeedFromCacheForTesting(); | 
 |     test_util::RunBlockingPoolTask(); | 
 |   } | 
 |  | 
 |   // Creates a proto file representing a filesystem with directories: | 
 |   // drive, drive/Dir1, drive/Dir1/SubDir2 | 
 |   // and files | 
 |   // drive/File1, drive/Dir1/File2, drive/Dir1/SubDir2/File3. | 
 |   // Sets the changestamp to 654321, equal to that of "account_metadata.json" | 
 |   // test data, indicating the cache is holding the latest file system info. | 
 |   void SaveTestFileSystem() { | 
 |     DriveRootDirectoryProto root; | 
 |     root.set_version(kProtoVersion); | 
 |     root.set_largest_changestamp(654321); | 
 |     DriveDirectoryProto* root_dir = root.mutable_drive_directory(); | 
 |     DriveEntryProto* dir_base = root_dir->mutable_drive_entry(); | 
 |     PlatformFileInfoProto* platform_info = dir_base->mutable_file_info(); | 
 |     dir_base->set_title("drive"); | 
 |     dir_base->set_resource_id(kDriveRootDirectoryResourceId); | 
 |     dir_base->set_upload_url("http://resumable-create-media/1"); | 
 |     platform_info->set_is_directory(true); | 
 |  | 
 |     // drive/File1 | 
 |     DriveEntryProto* file = root_dir->add_child_files(); | 
 |     file->set_title("File1"); | 
 |     file->set_resource_id("resource_id:File1"); | 
 |     file->set_upload_url("http://resumable-edit-media/1"); | 
 |     file->mutable_file_specific_info()->set_file_md5("md5"); | 
 |     platform_info = file->mutable_file_info(); | 
 |     platform_info->set_is_directory(false); | 
 |     platform_info->set_size(1048576); | 
 |  | 
 |     // drive/Dir1 | 
 |     DriveDirectoryProto* dir1 = root_dir->add_child_directories(); | 
 |     dir_base = dir1->mutable_drive_entry(); | 
 |     dir_base->set_title("Dir1"); | 
 |     dir_base->set_resource_id("resource_id:Dir1"); | 
 |     dir_base->set_upload_url("http://resumable-create-media/2"); | 
 |     platform_info = dir_base->mutable_file_info(); | 
 |     platform_info->set_is_directory(true); | 
 |  | 
 |     // drive/Dir1/File2 | 
 |     file = dir1->add_child_files(); | 
 |     file->set_title("File2"); | 
 |     file->set_resource_id("resource_id:File2"); | 
 |     file->set_upload_url("http://resumable-edit-media/2"); | 
 |     file->mutable_file_specific_info()->set_file_md5("md5"); | 
 |     platform_info = file->mutable_file_info(); | 
 |     platform_info->set_is_directory(false); | 
 |     platform_info->set_size(555); | 
 |  | 
 |     // drive/Dir1/SubDir2 | 
 |     DriveDirectoryProto* dir2 = dir1->add_child_directories(); | 
 |     dir_base = dir2->mutable_drive_entry(); | 
 |     dir_base->set_title("SubDir2"); | 
 |     dir_base->set_resource_id("resource_id:SubDir2"); | 
 |     dir_base->set_upload_url("http://resumable-create-media/3"); | 
 |     platform_info = dir_base->mutable_file_info(); | 
 |     platform_info->set_is_directory(true); | 
 |  | 
 |     // drive/Dir1/SubDir2/File3 | 
 |     file = dir2->add_child_files(); | 
 |     file->set_title("File3"); | 
 |     file->set_resource_id("resource_id:File3"); | 
 |     file->set_upload_url("http://resumable-edit-media/3"); | 
 |     file->mutable_file_specific_info()->set_file_md5("md5"); | 
 |     platform_info = file->mutable_file_info(); | 
 |     platform_info->set_is_directory(false); | 
 |     platform_info->set_size(12345); | 
 |  | 
 |     // Write this proto out to GCache/vi/meta/file_system.pb | 
 |     std::string serialized_proto; | 
 |     ASSERT_TRUE(root.SerializeToString(&serialized_proto)); | 
 |     ASSERT_TRUE(!serialized_proto.empty()); | 
 |  | 
 |     FilePath cache_dir_path = profile_->GetPath().Append( | 
 |         FILE_PATH_LITERAL("GCache/v1/meta/")); | 
 |     ASSERT_TRUE(file_util::CreateDirectory(cache_dir_path)); | 
 |     const int file_size = static_cast<int>(serialized_proto.length()); | 
 |     ASSERT_EQ(file_util::WriteFile(cache_dir_path.Append("file_system.pb"), | 
 |         serialized_proto.data(), file_size), file_size); | 
 |   } | 
 |  | 
 |   // Verifies that |file_path| is a valid JSON file for the hosted document | 
 |   // associated with |entry| (i.e. |url| and |resource_id| match). | 
 |   void VerifyHostedDocumentJSONFile(const DriveEntryProto& entry_proto, | 
 |                                     const FilePath& file_path) { | 
 |     std::string error; | 
 |     JSONFileValueSerializer serializer(file_path); | 
 |     scoped_ptr<Value> value(serializer.Deserialize(NULL, &error)); | 
 |     ASSERT_TRUE(value.get()) << "Parse error " << file_path.value() | 
 |                              << ": " << error; | 
 |     DictionaryValue* dict_value = NULL; | 
 |     ASSERT_TRUE(value->GetAsDictionary(&dict_value)); | 
 |  | 
 |     std::string edit_url, resource_id; | 
 |     EXPECT_TRUE(dict_value->GetString("url", &edit_url)); | 
 |     EXPECT_TRUE(dict_value->GetString("resource_id", &resource_id)); | 
 |  | 
 |     EXPECT_EQ(entry_proto.file_specific_info().alternate_url(), | 
 |               edit_url); | 
 |     EXPECT_EQ(entry_proto.resource_id(), resource_id); | 
 |   } | 
 |  | 
 |   // This is used as a helper for registering callbacks that need to be | 
 |   // RefCountedThreadSafe, and a place where we can fetch results from various | 
 |   // operations. | 
 |   class CallbackHelper | 
 |     : public base::RefCountedThreadSafe<CallbackHelper> { | 
 |    public: | 
 |     CallbackHelper() | 
 |         : last_error_(DRIVE_FILE_OK), | 
 |           quota_bytes_total_(0), | 
 |           quota_bytes_used_(0), | 
 |           entry_proto_(NULL) {} | 
 |  | 
 |     virtual void GetFileCallback(DriveFileError error, | 
 |                                  const FilePath& file_path, | 
 |                                  const std::string& mime_type, | 
 |                                  DriveFileType file_type) { | 
 |       last_error_ = error; | 
 |       download_path_ = file_path; | 
 |       mime_type_ = mime_type; | 
 |       file_type_ = file_type; | 
 |     } | 
 |  | 
 |     virtual void FileOperationCallback(DriveFileError error) { | 
 |       DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 
 |  | 
 |       last_error_ = error; | 
 |     } | 
 |  | 
 |     virtual void GetAvailableSpaceCallback(DriveFileError error, | 
 |                                            int64 bytes_total, | 
 |                                            int64 bytes_used) { | 
 |       last_error_ = error; | 
 |       quota_bytes_total_ = bytes_total; | 
 |       quota_bytes_used_ = bytes_used; | 
 |     } | 
 |  | 
 |     virtual void OpenFileCallback(DriveFileError error, | 
 |                                   const FilePath& file_path) { | 
 |       last_error_ = error; | 
 |       opened_file_path_ = file_path; | 
 |       MessageLoop::current()->Quit(); | 
 |     } | 
 |  | 
 |     virtual void CloseFileCallback(DriveFileError error) { | 
 |       last_error_ = error; | 
 |       MessageLoop::current()->Quit(); | 
 |     } | 
 |  | 
 |     virtual void GetEntryInfoCallback( | 
 |         DriveFileError error, | 
 |         scoped_ptr<DriveEntryProto> entry_proto) { | 
 |       last_error_ = error; | 
 |       entry_proto_ = entry_proto.Pass(); | 
 |     } | 
 |  | 
 |     virtual void ReadDirectoryCallback( | 
 |         DriveFileError error, | 
 |         bool /* hide_hosted_documents */, | 
 |         scoped_ptr<DriveEntryProtoVector> entries) { | 
 |       last_error_ = error; | 
 |       directory_entries_ = entries.Pass(); | 
 |     } | 
 |  | 
 |     DriveFileError last_error_; | 
 |     FilePath download_path_; | 
 |     FilePath opened_file_path_; | 
 |     std::string mime_type_; | 
 |     DriveFileType file_type_; | 
 |     int64 quota_bytes_total_; | 
 |     int64 quota_bytes_used_; | 
 |     scoped_ptr<DriveEntryProto> entry_proto_; | 
 |     scoped_ptr<DriveEntryProtoVector> directory_entries_; | 
 |  | 
 |    protected: | 
 |     virtual ~CallbackHelper() {} | 
 |  | 
 |    private: | 
 |     friend class base::RefCountedThreadSafe<CallbackHelper>; | 
 |   }; | 
 |  | 
 |   // Copy the result from FindFirstMissingParentDirectory(). | 
 |   static void CopyResultFromFindFirstMissingParentDirectory( | 
 |       DriveFileSystem::FindFirstMissingParentDirectoryResult* out_result, | 
 |       const DriveFileSystem::FindFirstMissingParentDirectoryResult& result) { | 
 |     DCHECK(out_result); | 
 |     *out_result = result; | 
 |   } | 
 |  | 
 |   MessageLoopForUI message_loop_; | 
 |   // The order of the test threads is important, do not change the order. | 
 |   // See also content/browser/browser_thread_impl.cc. | 
 |   content::TestBrowserThread ui_thread_; | 
 |   content::TestBrowserThread io_thread_; | 
 |   scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_; | 
 |   scoped_ptr<TestingProfile> profile_; | 
 |   scoped_refptr<CallbackHelper> callback_helper_; | 
 |   DriveCache* cache_; | 
 |   scoped_ptr<StrictMock<MockDriveUploader> > mock_uploader_; | 
 |   DriveFileSystem* file_system_; | 
 |   StrictMock<MockDriveService>* mock_drive_service_; | 
 |   scoped_ptr<StrictMock<MockDriveWebAppsRegistry> > mock_webapps_registry_; | 
 |   StrictMock<MockFreeDiskSpaceGetter>* mock_free_disk_space_checker_; | 
 |   scoped_ptr<StrictMock<MockDriveCacheObserver> > mock_cache_observer_; | 
 |   scoped_ptr<StrictMock<MockDirectoryChangeObserver> > mock_directory_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_; | 
 | }; | 
 |  | 
 | void AsyncInitializationCallback( | 
 |     int* counter, | 
 |     int expected_counter, | 
 |     const FilePath& expected_file_path, | 
 |     MessageLoop* message_loop, | 
 |     DriveFileError error, | 
 |     scoped_ptr<DriveEntryProto> entry_proto) { | 
 |   ASSERT_EQ(DRIVE_FILE_OK, error); | 
 |   ASSERT_TRUE(entry_proto.get()); | 
 |   ASSERT_TRUE(entry_proto->file_info().is_directory()); | 
 |   EXPECT_EQ(expected_file_path.value(), entry_proto->base_name()); | 
 |  | 
 |   (*counter)++; | 
 |   if (*counter >= expected_counter) | 
 |     message_loop->Quit(); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, DuplicatedAsyncInitialization) { | 
 |   int counter = 0; | 
 |   GetEntryInfoCallback callback = base::Bind( | 
 |       &AsyncInitializationCallback, | 
 |       &counter, | 
 |       2, | 
 |       FilePath(FILE_PATH_LITERAL("drive")), | 
 |       &message_loop_); | 
 |  | 
 |   EXPECT_CALL(*mock_drive_service_, GetAccountMetadata(_)).Times(1); | 
 |   EXPECT_CALL(*mock_drive_service_, | 
 |               GetDocuments(Eq(GURL()), _, _, _, _)).Times(1); | 
 |  | 
 |   EXPECT_CALL(*mock_webapps_registry_, UpdateFromFeed(_)).Times(1); | 
 |  | 
 |   file_system_->GetEntryInfoByPath( | 
 |       FilePath(FILE_PATH_LITERAL("drive")), callback); | 
 |   file_system_->GetEntryInfoByPath( | 
 |       FilePath(FILE_PATH_LITERAL("drive")), callback); | 
 |   message_loop_.Run();  // Wait to get our result | 
 |   EXPECT_EQ(2, counter); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, SearchRootDirectory) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   const FilePath kFilePath = FilePath(FILE_PATH_LITERAL("drive")); | 
 |   scoped_ptr<DriveEntryProto> entry = GetEntryInfoByPathSync( | 
 |       FilePath(FILE_PATH_LITERAL(kFilePath))); | 
 |   ASSERT_TRUE(entry.get()); | 
 |   EXPECT_EQ(kDriveRootDirectoryResourceId, entry->resource_id()); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, SearchExistingFile) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   const FilePath kFilePath = FilePath( | 
 |       FILE_PATH_LITERAL("drive/File 1.txt")); | 
 |   scoped_ptr<DriveEntryProto> entry = GetEntryInfoByPathSync(kFilePath); | 
 |   ASSERT_TRUE(entry.get()); | 
 |   EXPECT_EQ("file:2_file_resource_id", entry->resource_id()); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, SearchExistingDocument) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   const FilePath kFilePath = FilePath( | 
 |       FILE_PATH_LITERAL("drive/Document 1.gdoc")); | 
 |   scoped_ptr<DriveEntryProto> entry = GetEntryInfoByPathSync(kFilePath); | 
 |   ASSERT_TRUE(entry.get()); | 
 |   EXPECT_EQ("document:5_document_resource_id", entry->resource_id()); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, SearchNonExistingFile) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   const FilePath kFilePath = FilePath( | 
 |       FILE_PATH_LITERAL("drive/nonexisting.file")); | 
 |   scoped_ptr<DriveEntryProto> entry = GetEntryInfoByPathSync(kFilePath); | 
 |   ASSERT_FALSE(entry.get()); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, SearchEncodedFileNames) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   const FilePath kFilePath1 = FilePath( | 
 |       FILE_PATH_LITERAL("drive/Slash / in file 1.txt")); | 
 |   scoped_ptr<DriveEntryProto> entry = GetEntryInfoByPathSync(kFilePath1); | 
 |   ASSERT_FALSE(entry.get()); | 
 |  | 
 |   const FilePath kFilePath2 = FilePath::FromUTF8Unsafe( | 
 |       "drive/Slash \xE2\x88\x95 in file 1.txt"); | 
 |   entry = GetEntryInfoByPathSync(kFilePath2); | 
 |   ASSERT_TRUE(entry.get()); | 
 |   EXPECT_EQ("file:slash_file_resource_id", entry->resource_id()); | 
 |  | 
 |   const FilePath kFilePath3 = FilePath::FromUTF8Unsafe( | 
 |       "drive/Slash \xE2\x88\x95 in directory/Slash SubDir File.txt"); | 
 |   entry = GetEntryInfoByPathSync(kFilePath3); | 
 |   ASSERT_TRUE(entry.get()); | 
 |   EXPECT_EQ("file:slash_subdir_file", entry->resource_id()); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, SearchEncodedFileNamesLoadingRoot) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   const FilePath kFilePath1 = FilePath( | 
 |       FILE_PATH_LITERAL("drive/Slash / in file 1.txt")); | 
 |   scoped_ptr<DriveEntryProto> entry = GetEntryInfoByPathSync(kFilePath1); | 
 |   ASSERT_FALSE(entry.get()); | 
 |  | 
 |   const FilePath kFilePath2 = FilePath::FromUTF8Unsafe( | 
 |       "drive/Slash \xE2\x88\x95 in file 1.txt"); | 
 |   entry = GetEntryInfoByPathSync(kFilePath2); | 
 |   ASSERT_TRUE(entry.get()); | 
 |   EXPECT_EQ("file:slash_file_resource_id", entry->resource_id()); | 
 |  | 
 |   const FilePath kFilePath3 = FilePath::FromUTF8Unsafe( | 
 |       "drive/Slash \xE2\x88\x95 in directory/Slash SubDir File.txt"); | 
 |   entry = GetEntryInfoByPathSync(kFilePath3); | 
 |   ASSERT_TRUE(entry.get()); | 
 |   EXPECT_EQ("file:slash_subdir_file", entry->resource_id()); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, SearchDuplicateNames) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   const FilePath kFilePath1 = FilePath( | 
 |       FILE_PATH_LITERAL("drive/Duplicate Name.txt")); | 
 |   scoped_ptr<DriveEntryProto> entry = GetEntryInfoByPathSync(kFilePath1); | 
 |   ASSERT_TRUE(entry.get()); | 
 |   EXPECT_EQ("file:3_file_resource_id", entry->resource_id()); | 
 |  | 
 |   const FilePath kFilePath2 = FilePath( | 
 |       FILE_PATH_LITERAL("drive/Duplicate Name (2).txt")); | 
 |   entry = GetEntryInfoByPathSync(kFilePath2); | 
 |   ASSERT_TRUE(entry.get()); | 
 |   EXPECT_EQ("file:4_file_resource_id", entry->resource_id()); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, SearchExistingDirectory) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   const FilePath kFilePath = FilePath( | 
 |       FILE_PATH_LITERAL("drive/Directory 1")); | 
 |   scoped_ptr<DriveEntryProto> entry = GetEntryInfoByPathSync(kFilePath); | 
 |   ASSERT_TRUE(entry.get()); | 
 |   ASSERT_EQ("folder:1_folder_resource_id", entry->resource_id()); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, SearchInSubdir) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   const FilePath kFilePath = FilePath( | 
 |       FILE_PATH_LITERAL("drive/Directory 1/SubDirectory File 1.txt")); | 
 |   scoped_ptr<DriveEntryProto> entry = GetEntryInfoByPathSync(kFilePath); | 
 |   ASSERT_TRUE(entry.get()); | 
 |   ASSERT_EQ("file:subdirectory_file_1_id", entry->resource_id()); | 
 | } | 
 |  | 
 | // Check the reconstruction of the directory structure from only the root feed. | 
 | TEST_F(DriveFileSystemTest, SearchInSubSubdir) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   const FilePath kFilePath = FilePath( | 
 |       FILE_PATH_LITERAL("drive/Directory 1/Sub Directory Folder/" | 
 |                         "Sub Sub Directory Folder")); | 
 |   scoped_ptr<DriveEntryProto> entry = GetEntryInfoByPathSync(kFilePath); | 
 |   ASSERT_TRUE(entry.get()); | 
 |   ASSERT_EQ("folder:sub_sub_directory_folder_id", entry->resource_id()); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, FilePathTests) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL("drive/File 1.txt")))); | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL("drive/Directory 1")))); | 
 |   EXPECT_TRUE(EntryExists( | 
 |       FilePath( | 
 |           FILE_PATH_LITERAL("drive/Directory 1/SubDirectory File 1.txt")))); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, ChangeFeed_AddAndDeleteFileInRoot) { | 
 |   int latest_changelog = 0; | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive"))))).Times(2); | 
 |  | 
 |   LoadChangeFeed("gdata/delta_file_added_in_root.json", ++latest_changelog); | 
 |   EXPECT_TRUE( | 
 |       EntryExists(FilePath(FILE_PATH_LITERAL("drive/Added file.gdoc")))); | 
 |  | 
 |   LoadChangeFeed("gdata/delta_file_deleted_in_root.json", ++latest_changelog); | 
 |   EXPECT_FALSE( | 
 |       EntryExists(FilePath(FILE_PATH_LITERAL("drive/Added file.gdoc")))); | 
 | } | 
 |  | 
 |  | 
 | TEST_F(DriveFileSystemTest, ChangeFeed_AddAndDeleteFileFromExistingDirectory) { | 
 |   int latest_changelog = 0; | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   EXPECT_TRUE(EntryExists(FilePath( | 
 |       FILE_PATH_LITERAL("drive/Directory 1")))); | 
 |  | 
 |   // Add file to an existing directory. | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive"))))).Times(1); | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive/Directory 1"))))).Times(1); | 
 |   LoadChangeFeed("gdata/delta_file_added_in_directory.json", | 
 |                  ++latest_changelog); | 
 |   EXPECT_TRUE(EntryExists(FilePath( | 
 |       FILE_PATH_LITERAL("drive/Directory 1/Added file.gdoc")))); | 
 |  | 
 |   // Remove that file from the directory. | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive/Directory 1"))))).Times(1); | 
 |   LoadChangeFeed("gdata/delta_file_deleted_in_directory.json", | 
 |                  ++latest_changelog); | 
 |   EXPECT_TRUE(EntryExists(FilePath( | 
 |       FILE_PATH_LITERAL("drive/Directory 1")))); | 
 |   EXPECT_FALSE(EntryExists(FilePath( | 
 |       FILE_PATH_LITERAL("drive/Directory 1/Added file.gdoc")))); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, ChangeFeed_AddFileToNewDirectory) { | 
 |   int latest_changelog = 0; | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |   // Add file to a new directory. | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive"))))).Times(1); | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive/New Directory"))))).Times(1); | 
 |  | 
 |   LoadChangeFeed("gdata/delta_file_added_in_new_directory.json", | 
 |                  ++latest_changelog); | 
 |  | 
 |   EXPECT_TRUE(EntryExists(FilePath( | 
 |       FILE_PATH_LITERAL("drive/New Directory")))); | 
 |   EXPECT_TRUE(EntryExists(FilePath( | 
 |       FILE_PATH_LITERAL("drive/New Directory/File in new dir.gdoc")))); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, ChangeFeed_AddFileToNewButDeletedDirectory) { | 
 |   int latest_changelog = 0; | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   // This feed contains thw following updates: | 
 |   // 1) A new PDF file is added to a new directory | 
 |   // 2) but the new directory is marked "deleted" (i.e. moved to Trash) | 
 |   // Hence, the PDF file should be just ignored. | 
 |   LoadChangeFeed("gdata/delta_file_added_in_new_but_deleted_directory.json", | 
 |                  ++latest_changelog); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, ChangeFeed_DirectoryMovedFromRootToDirectory) { | 
 |   int latest_changelog = 0; | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL( | 
 |       "drive/Directory 2")))); | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL( | 
 |       "drive/Directory 1")))); | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL( | 
 |       "drive/Directory 1/SubDirectory File 1.txt")))); | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL( | 
 |       "drive/Directory 1/Sub Directory Folder")))); | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL( | 
 |       "drive/Directory 1/Sub Directory Folder/Sub Sub Directory Folder")))); | 
 |  | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive"))))).Times(1); | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive/Directory 2"))))).Times(1); | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive/Directory 2/Directory 1"))))) | 
 |       .Times(1); | 
 |   LoadChangeFeed("gdata/delta_dir_moved_from_root_to_directory.json", | 
 |                  ++latest_changelog); | 
 |  | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL( | 
 |       "drive/Directory 2")))); | 
 |   EXPECT_FALSE(EntryExists(FilePath(FILE_PATH_LITERAL( | 
 |       "drive/Directory 1")))); | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL( | 
 |       "drive/Directory 2/Directory 1")))); | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL( | 
 |       "drive/Directory 2/Directory 1/SubDirectory File 1.txt")))); | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL( | 
 |       "drive/Directory 2/Directory 1/Sub Directory Folder")))); | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL( | 
 |       "drive/Directory 2/Directory 1/Sub Directory Folder/" | 
 |       "Sub Sub Directory Folder")))); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, ChangeFeed_FileMovedFromDirectoryToRoot) { | 
 |   int latest_changelog = 0; | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL( | 
 |       "drive/Directory 1")))); | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL( | 
 |       "drive/Directory 1/Sub Directory Folder")))); | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL( | 
 |       "drive/Directory 1/Sub Directory Folder/Sub Sub Directory Folder")))); | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL( | 
 |       "drive/Directory 1/SubDirectory File 1.txt")))); | 
 |  | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive"))))).Times(1); | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive/Directory 1"))))).Times(1); | 
 |   LoadChangeFeed("gdata/delta_file_moved_from_directory_to_root.json", | 
 |                  ++latest_changelog); | 
 |  | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL( | 
 |       "drive/Directory 1")))); | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL( | 
 |       "drive/Directory 1/Sub Directory Folder")))); | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL( | 
 |       "drive/Directory 1/Sub Directory Folder/Sub Sub Directory Folder")))); | 
 |   EXPECT_FALSE(EntryExists(FilePath(FILE_PATH_LITERAL( | 
 |       "drive/Directory 1/SubDirectory File 1.txt")))); | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL( | 
 |       "drive/SubDirectory File 1.txt")))); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, ChangeFeed_FileRenamedInDirectory) { | 
 |   int latest_changelog = 0; | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL( | 
 |       "drive/Directory 1")))); | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL( | 
 |       "drive/Directory 1/SubDirectory File 1.txt")))); | 
 |  | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive"))))).Times(1); | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive/Directory 1"))))).Times(1); | 
 |   LoadChangeFeed("gdata/delta_file_renamed_in_directory.json", | 
 |                  ++latest_changelog); | 
 |  | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL( | 
 |       "drive/Directory 1")))); | 
 |   EXPECT_FALSE(EntryExists(FilePath(FILE_PATH_LITERAL( | 
 |       "drive/Directory 1/SubDirectory File 1.txt")))); | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL( | 
 |       "drive/Directory 1/New SubDirectory File 1.txt")))); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, CachedFeedLoading) { | 
 |   SaveTestFileSystem(); | 
 |   TestLoadMetadataFromCache(); | 
 |  | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL("drive/File1")))); | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL("drive/Dir1")))); | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL("drive/Dir1/File2")))); | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL("drive/Dir1/SubDir2")))); | 
 |   EXPECT_TRUE(EntryExists( | 
 |       FilePath(FILE_PATH_LITERAL("drive/Dir1/SubDir2/File3")))); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, CachedFeadLoadingThenServerFeedLoading) { | 
 |   SaveTestFileSystem(); | 
 |  | 
 |   // SaveTestFileSystem and "account_metadata.json" have the same changestamp, | 
 |   // so no request for new feeds (i.e., call to GetDocuments) should happen. | 
 |   mock_drive_service_->set_account_metadata( | 
 |       test_util::LoadJSONFile("gdata/account_metadata.json").release()); | 
 |   EXPECT_CALL(*mock_drive_service_, GetAccountMetadata(_)).Times(1); | 
 |   EXPECT_CALL(*mock_webapps_registry_, UpdateFromFeed(_)).Times(1); | 
 |   EXPECT_CALL(*mock_drive_service_, GetDocuments(_, _, _, _, _)).Times(0); | 
 |  | 
 |   // Kicks loading of cached file system and query for server update. | 
 |   EXPECT_TRUE(EntryExists(FilePath(FILE_PATH_LITERAL("drive/File1")))); | 
 |  | 
 |   // Since the file system has verified that it holds the latest snapshot, | 
 |   // it should change its state to FROM_SERVER, which admits periodic refresh. | 
 |   // To test it, call CheckForUpdates and verify it does try to check updates. | 
 |   mock_drive_service_->set_account_metadata( | 
 |       test_util::LoadJSONFile("gdata/account_metadata.json").release()); | 
 |   EXPECT_CALL(*mock_drive_service_, GetAccountMetadata(_)).Times(1); | 
 |   EXPECT_CALL(*mock_webapps_registry_, UpdateFromFeed(_)).Times(1); | 
 |  | 
 |   file_system_->CheckForUpdates(); | 
 |   test_util::RunBlockingPoolTask(); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, TransferFileFromLocalToRemote_RegularFile) { | 
 |   EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace()) | 
 |       .Times(AtLeast(1)).WillRepeatedly(Return(kLotsOfSpace)); | 
 |  | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   // We'll add a file to the Drive root directory. | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive"))))).Times(1); | 
 |  | 
 |   FileOperationCallback callback = | 
 |       base::Bind(&CallbackHelper::FileOperationCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   // Prepare a local file. | 
 |   ScopedTempDir temp_dir; | 
 |   ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | 
 |   const FilePath local_src_file_path = temp_dir.path().Append("local.txt"); | 
 |   const std::string kContent = "hello"; | 
 |   file_util::WriteFile(local_src_file_path, kContent.data(), kContent.size()); | 
 |  | 
 |   // Confirm that the remote file does not exist. | 
 |   const FilePath remote_dest_file_path(FILE_PATH_LITERAL("drive/remote.txt")); | 
 |   EXPECT_FALSE(EntryExists(remote_dest_file_path)); | 
 |  | 
 |   scoped_ptr<base::Value> value = | 
 |       test_util::LoadJSONFile("gdata/document_to_download.json"); | 
 |   scoped_ptr<DocumentEntry> document_entry( | 
 |       DocumentEntry::ExtractAndParse(*value)); | 
 |  | 
 |   EXPECT_CALL(*mock_uploader_, UploadNewFile(_, _, _, _, _, _, _, _, _)) | 
 |       .WillOnce(MockUploadNewFile()); | 
 |  | 
 |   // Transfer the local file to Drive. | 
 |   file_system_->TransferFileFromLocalToRemote( | 
 |       local_src_file_path, remote_dest_file_path, callback); | 
 |   test_util::RunBlockingPoolTask(); | 
 |  | 
 |   EXPECT_EQ(DRIVE_FILE_OK, callback_helper_->last_error_); | 
 |  | 
 |   // Now the remote file should exist. | 
 |   EXPECT_TRUE(EntryExists(remote_dest_file_path)); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, TransferFileFromLocalToRemote_HostedDocument) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   // Prepare a local file, which is a json file of a hosted document, which | 
 |   // matches "Document 1" in root_feed.json. | 
 |   ScopedTempDir temp_dir; | 
 |   ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | 
 |   const FilePath local_src_file_path = temp_dir.path().Append("local.gdoc"); | 
 |   const std::string kEditUrl = | 
 |       "https://3_document_self_link/document:5_document_resource_id"; | 
 |   const std::string kResourceId = "document:5_document_resource_id"; | 
 |   const std::string kContent = | 
 |       base::StringPrintf("{\"url\": \"%s\", \"resource_id\": \"%s\"}", | 
 |                          kEditUrl.c_str(), kResourceId.c_str()); | 
 |   file_util::WriteFile(local_src_file_path, kContent.data(), kContent.size()); | 
 |  | 
 |   // Confirm that the remote file does not exist. | 
 |   const FilePath remote_dest_file_path( | 
 |       FILE_PATH_LITERAL("drive/Directory 1/Document 1.gdoc")); | 
 |   EXPECT_FALSE(EntryExists(remote_dest_file_path)); | 
 |  | 
 |   // We'll add a file to "Directory 1" directory on Drive. | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive/Directory 1"))))).Times(1); | 
 |  | 
 |   // We'll copy a hosted document using CopyDocument. | 
 |   // ".gdoc" suffix should be stripped when copying. | 
 |   scoped_ptr<base::Value> document = | 
 |       test_util::LoadJSONFile("gdata/uploaded_document.json"); | 
 |   EXPECT_CALL(*mock_drive_service_, | 
 |               CopyDocument(kResourceId, | 
 |                            FILE_PATH_LITERAL("Document 1"), | 
 |                            _)) | 
 |       .WillOnce(MockCopyDocument(gdata::HTTP_SUCCESS, &document)); | 
 |   // We'll then add the hosted document to the destination directory. | 
 |   EXPECT_CALL(*mock_drive_service_, | 
 |               AddResourceToDirectory(_, _, _)).Times(1); | 
 |  | 
 |   FileOperationCallback callback = | 
 |       base::Bind(&CallbackHelper::FileOperationCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   // Transfer the local file to Drive. | 
 |   file_system_->TransferFileFromLocalToRemote( | 
 |       local_src_file_path, remote_dest_file_path, callback); | 
 |   test_util::RunBlockingPoolTask(); | 
 |  | 
 |   EXPECT_EQ(DRIVE_FILE_OK, callback_helper_->last_error_); | 
 |  | 
 |   // Now the remote file should exist. | 
 |   EXPECT_TRUE(EntryExists(remote_dest_file_path)); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, TransferFileFromRemoteToLocal_RegularFile) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   FileOperationCallback callback = | 
 |       base::Bind(&CallbackHelper::FileOperationCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   ScopedTempDir temp_dir; | 
 |   ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | 
 |   FilePath local_dest_file_path = temp_dir.path().Append("local_copy.txt"); | 
 |  | 
 |   FilePath remote_src_file_path(FILE_PATH_LITERAL("drive/File 1.txt")); | 
 |   scoped_ptr<DriveEntryProto> file = GetEntryInfoByPathSync( | 
 |       remote_src_file_path); | 
 |   FilePath cache_file = GetCachePathForFile( | 
 |       file->resource_id(), | 
 |       file->file_specific_info().file_md5()); | 
 |   const int64 file_size = file->file_info().size(); | 
 |  | 
 |   // Pretend we have enough space. | 
 |   EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace()) | 
 |       .Times(2).WillRepeatedly(Return(file_size + kMinFreeSpace)); | 
 |  | 
 |   const std::string remote_src_file_data = "Test file data"; | 
 |   mock_drive_service_->set_file_data(new std::string(remote_src_file_data)); | 
 |  | 
 |   // Before Download starts metadata from server will be fetched. | 
 |   // We will read content url from the result. | 
 |   scoped_ptr<base::Value> document = | 
 |       test_util::LoadJSONFile("gdata/document_to_download.json"); | 
 |   SetExpectationsForGetDocumentEntry(&document, "file:2_file_resource_id"); | 
 |  | 
 |   // The file is obtained with the mock DriveService. | 
 |   EXPECT_CALL(*mock_drive_service_, | 
 |               DownloadFile(remote_src_file_path, | 
 |                            cache_file, | 
 |                            GURL("https://file_content_url_changed/"), | 
 |                            _, _)) | 
 |       .Times(1); | 
 |  | 
 |   file_system_->TransferFileFromRemoteToLocal( | 
 |       remote_src_file_path, local_dest_file_path, callback); | 
 |   test_util::RunBlockingPoolTask(); | 
 |  | 
 |   EXPECT_EQ(DRIVE_FILE_OK, callback_helper_->last_error_); | 
 |  | 
 |   std::string cache_file_data; | 
 |   EXPECT_TRUE(file_util::ReadFileToString(cache_file, &cache_file_data)); | 
 |   EXPECT_EQ(remote_src_file_data, cache_file_data); | 
 |  | 
 |   std::string local_dest_file_data; | 
 |   EXPECT_TRUE(file_util::ReadFileToString(local_dest_file_path, | 
 |                                           &local_dest_file_data)); | 
 |   EXPECT_EQ(remote_src_file_data, local_dest_file_data); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, TransferFileFromRemoteToLocal_HostedDocument) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   FileOperationCallback callback = | 
 |       base::Bind(&CallbackHelper::FileOperationCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   ScopedTempDir temp_dir; | 
 |   ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | 
 |   FilePath local_dest_file_path = temp_dir.path().Append("local_copy.txt"); | 
 |   FilePath remote_src_file_path(FILE_PATH_LITERAL("drive/Document 1.gdoc")); | 
 |   file_system_->TransferFileFromRemoteToLocal( | 
 |       remote_src_file_path, local_dest_file_path, callback); | 
 |   test_util::RunBlockingPoolTask(); | 
 |  | 
 |   EXPECT_EQ(DRIVE_FILE_OK, callback_helper_->last_error_); | 
 |  | 
 |   scoped_ptr<DriveEntryProto> entry_proto = GetEntryInfoByPathSync( | 
 |       remote_src_file_path); | 
 |   ASSERT_TRUE(entry_proto.get()); | 
 |   VerifyHostedDocumentJSONFile(*entry_proto, local_dest_file_path); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, CopyNotExistingFile) { | 
 |   FilePath src_file_path(FILE_PATH_LITERAL("drive/Dummy file.txt")); | 
 |   FilePath dest_file_path(FILE_PATH_LITERAL("drive/Test.log")); | 
 |  | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   EXPECT_FALSE(EntryExists(src_file_path)); | 
 |  | 
 |   FileOperationCallback callback = | 
 |       base::Bind(&CallbackHelper::FileOperationCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   file_system_->Copy(src_file_path, dest_file_path, callback); | 
 |   test_util::RunBlockingPoolTask(); | 
 |   EXPECT_EQ(DRIVE_FILE_ERROR_NOT_FOUND, callback_helper_->last_error_); | 
 |  | 
 |   EXPECT_FALSE(EntryExists(src_file_path)); | 
 |   EXPECT_FALSE(EntryExists(dest_file_path)); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, CopyFileToNonExistingDirectory) { | 
 |   FilePath src_file_path(FILE_PATH_LITERAL("drive/File 1.txt")); | 
 |   FilePath dest_parent_path(FILE_PATH_LITERAL("drive/Dummy")); | 
 |   FilePath dest_file_path(FILE_PATH_LITERAL("drive/Dummy/Test.log")); | 
 |  | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   ASSERT_TRUE(EntryExists(src_file_path)); | 
 |   scoped_ptr<DriveEntryProto> src_entry_proto = GetEntryInfoByPathSync( | 
 |       src_file_path); | 
 |   ASSERT_TRUE(src_entry_proto.get()); | 
 |   std::string src_file_path_resource_id = | 
 |       src_entry_proto->resource_id(); | 
 |   EXPECT_FALSE(src_entry_proto->edit_url().empty()); | 
 |  | 
 |   EXPECT_FALSE(EntryExists(dest_parent_path)); | 
 |  | 
 |   FileOperationCallback callback = | 
 |       base::Bind(&CallbackHelper::FileOperationCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   file_system_->Move(src_file_path, dest_file_path, callback); | 
 |   test_util::RunBlockingPoolTask(); | 
 |   EXPECT_EQ(DRIVE_FILE_ERROR_NOT_FOUND, callback_helper_->last_error_); | 
 |  | 
 |   EXPECT_TRUE(EntryExists(src_file_path)); | 
 |   EXPECT_FALSE(EntryExists(dest_parent_path)); | 
 |   EXPECT_FALSE(EntryExists(dest_file_path)); | 
 | } | 
 |  | 
 | // Test the case where the parent of |dest_file_path| is an existing file, | 
 | // not a directory. | 
 | TEST_F(DriveFileSystemTest, CopyFileToInvalidPath) { | 
 |   FilePath src_file_path(FILE_PATH_LITERAL("drive/Document 1.gdoc")); | 
 |   FilePath dest_parent_path(FILE_PATH_LITERAL("drive/Duplicate Name.txt")); | 
 |   FilePath dest_file_path(FILE_PATH_LITERAL( | 
 |       "drive/Duplicate Name.txt/Document 1.gdoc")); | 
 |  | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   ASSERT_TRUE(EntryExists(src_file_path)); | 
 |   scoped_ptr<DriveEntryProto> src_entry_proto = GetEntryInfoByPathSync( | 
 |       src_file_path); | 
 |   ASSERT_TRUE(src_entry_proto.get()); | 
 |   std::string src_file_resource_id = | 
 |       src_entry_proto->resource_id(); | 
 |   EXPECT_FALSE(src_entry_proto->edit_url().empty()); | 
 |  | 
 |   ASSERT_TRUE(EntryExists(dest_parent_path)); | 
 |   scoped_ptr<DriveEntryProto> dest_entry_proto = GetEntryInfoByPathSync( | 
 |       dest_parent_path); | 
 |   ASSERT_TRUE(dest_entry_proto.get()); | 
 |  | 
 |   FileOperationCallback callback = | 
 |       base::Bind(&CallbackHelper::FileOperationCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   file_system_->Copy(src_file_path, dest_file_path, callback); | 
 |   test_util::RunBlockingPoolTask(); | 
 |   EXPECT_EQ(DRIVE_FILE_ERROR_NOT_A_DIRECTORY, | 
 |             callback_helper_->last_error_); | 
 |  | 
 |   EXPECT_TRUE(EntryExists(src_file_path)); | 
 |   EXPECT_TRUE(EntryExists(src_file_path)); | 
 |   EXPECT_TRUE(EntryExists(dest_parent_path)); | 
 |  | 
 |   EXPECT_FALSE(EntryExists(dest_file_path)); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, RenameFile) { | 
 |   const FilePath src_file_path( | 
 |       FILE_PATH_LITERAL("drive/Directory 1/SubDirectory File 1.txt")); | 
 |   const FilePath src_parent_path(FILE_PATH_LITERAL("drive/Directory 1")); | 
 |   const FilePath dest_file_path( | 
 |       FILE_PATH_LITERAL("drive/Directory 1/Test.log")); | 
 |  | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   ASSERT_TRUE(EntryExists(src_file_path)); | 
 |   scoped_ptr<DriveEntryProto> src_entry_proto = GetEntryInfoByPathSync( | 
 |       src_file_path); | 
 |   ASSERT_TRUE(src_entry_proto.get()); | 
 |   std::string src_file_resource_id = | 
 |       src_entry_proto->resource_id(); | 
 |  | 
 |   EXPECT_CALL(*mock_drive_service_, | 
 |               RenameResource(GURL(src_entry_proto->edit_url()), | 
 |                              FILE_PATH_LITERAL("Test.log"), _)); | 
 |  | 
 |   FileOperationCallback callback = | 
 |       base::Bind(&CallbackHelper::FileOperationCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive/Directory 1"))))).Times(1); | 
 |  | 
 |   file_system_->Move(src_file_path, dest_file_path, callback); | 
 |   test_util::RunBlockingPoolTask(); | 
 |   EXPECT_EQ(DRIVE_FILE_OK, callback_helper_->last_error_); | 
 |  | 
 |   EXPECT_FALSE(EntryExists(src_file_path)); | 
 |   EXPECT_TRUE(EntryExists(dest_file_path)); | 
 |   EXPECT_EQ(src_file_resource_id, GetResourceIdByPath(dest_file_path)); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, MoveFileFromRootToSubDirectory) { | 
 |   FilePath src_file_path(FILE_PATH_LITERAL("drive/File 1.txt")); | 
 |   FilePath dest_parent_path(FILE_PATH_LITERAL("drive/Directory 1")); | 
 |   FilePath dest_file_path(FILE_PATH_LITERAL("drive/Directory 1/Test.log")); | 
 |  | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   ASSERT_TRUE(EntryExists(src_file_path)); | 
 |   scoped_ptr<DriveEntryProto> src_entry_proto = GetEntryInfoByPathSync( | 
 |       src_file_path); | 
 |   ASSERT_TRUE(src_entry_proto.get()); | 
 |   std::string src_file_resource_id = | 
 |       src_entry_proto->resource_id(); | 
 |   EXPECT_FALSE(src_entry_proto->edit_url().empty()); | 
 |  | 
 |   ASSERT_TRUE(EntryExists(dest_parent_path)); | 
 |   scoped_ptr<DriveEntryProto> dest_parent_proto = GetEntryInfoByPathSync( | 
 |       dest_parent_path); | 
 |   ASSERT_TRUE(dest_parent_proto.get()); | 
 |   ASSERT_TRUE(dest_parent_proto->file_info().is_directory()); | 
 |   EXPECT_FALSE(dest_parent_proto->content_url().empty()); | 
 |  | 
 |   EXPECT_CALL(*mock_drive_service_, | 
 |               RenameResource(GURL(src_entry_proto->edit_url()), | 
 |                              FILE_PATH_LITERAL("Test.log"), _)); | 
 |   EXPECT_CALL(*mock_drive_service_, | 
 |               AddResourceToDirectory( | 
 |                   GURL(dest_parent_proto->content_url()), | 
 |                   GURL(src_entry_proto->edit_url()), _)); | 
 |  | 
 |   FileOperationCallback callback = | 
 |       base::Bind(&CallbackHelper::FileOperationCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   // Expect notification for both source and destination directories. | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive"))))).Times(1); | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive/Directory 1"))))).Times(1); | 
 |  | 
 |   file_system_->Move(src_file_path, dest_file_path, callback); | 
 |   test_util::RunBlockingPoolTask(); | 
 |   EXPECT_EQ(DRIVE_FILE_OK, callback_helper_->last_error_); | 
 |  | 
 |   EXPECT_FALSE(EntryExists(src_file_path)); | 
 |   EXPECT_TRUE(EntryExists(dest_file_path)); | 
 |   EXPECT_EQ(src_file_resource_id, GetResourceIdByPath(dest_file_path)); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, MoveFileFromSubDirectoryToRoot) { | 
 |   FilePath src_parent_path(FILE_PATH_LITERAL("drive/Directory 1")); | 
 |   FilePath src_file_path( | 
 |       FILE_PATH_LITERAL("drive/Directory 1/SubDirectory File 1.txt")); | 
 |   FilePath dest_file_path(FILE_PATH_LITERAL("drive/Test.log")); | 
 |  | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   ASSERT_TRUE(EntryExists(src_file_path)); | 
 |   scoped_ptr<DriveEntryProto> src_entry_proto = GetEntryInfoByPathSync( | 
 |       src_file_path); | 
 |   ASSERT_TRUE(src_entry_proto.get()); | 
 |   std::string src_file_resource_id = | 
 |       src_entry_proto->resource_id(); | 
 |   EXPECT_FALSE(src_entry_proto->edit_url().empty()); | 
 |  | 
 |   ASSERT_TRUE(EntryExists(src_parent_path)); | 
 |   scoped_ptr<DriveEntryProto> src_parent_proto = GetEntryInfoByPathSync( | 
 |       src_parent_path); | 
 |   ASSERT_TRUE(src_parent_proto.get()); | 
 |   ASSERT_TRUE(src_parent_proto->file_info().is_directory()); | 
 |   EXPECT_FALSE(src_parent_proto->content_url().empty()); | 
 |  | 
 |   EXPECT_CALL(*mock_drive_service_, | 
 |               RenameResource(GURL(src_entry_proto->edit_url()), | 
 |                              FILE_PATH_LITERAL("Test.log"), _)); | 
 |   EXPECT_CALL(*mock_drive_service_, | 
 |               RemoveResourceFromDirectory( | 
 |                   GURL(src_parent_proto->content_url()), | 
 |                   GURL(src_entry_proto->edit_url()), | 
 |                   src_file_resource_id, _)); | 
 |  | 
 |   FileOperationCallback callback = | 
 |       base::Bind(&CallbackHelper::FileOperationCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   // Expect notification for both source and destination directories. | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive"))))).Times(1); | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive/Directory 1"))))).Times(1); | 
 |  | 
 |   file_system_->Move(src_file_path, dest_file_path, callback); | 
 |   test_util::RunBlockingPoolTask(); | 
 |   EXPECT_EQ(DRIVE_FILE_OK, callback_helper_->last_error_); | 
 |  | 
 |   EXPECT_FALSE(EntryExists(src_file_path)); | 
 |   ASSERT_TRUE(EntryExists(dest_file_path)); | 
 |   EXPECT_EQ(src_file_resource_id, GetResourceIdByPath(dest_file_path)); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, MoveFileBetweenSubDirectories) { | 
 |   FilePath src_parent_path(FILE_PATH_LITERAL("drive/Directory 1")); | 
 |   FilePath src_file_path( | 
 |       FILE_PATH_LITERAL("drive/Directory 1/SubDirectory File 1.txt")); | 
 |   FilePath dest_parent_path(FILE_PATH_LITERAL("drive/New Folder 1")); | 
 |   FilePath dest_file_path(FILE_PATH_LITERAL("drive/New Folder 1/Test.log")); | 
 |   FilePath interim_file_path(FILE_PATH_LITERAL("drive/Test.log")); | 
 |  | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive"))))).Times(1); | 
 |  | 
 |   AddDirectoryFromFile(dest_parent_path, "gdata/directory_entry_atom.json"); | 
 |  | 
 |   ASSERT_TRUE(EntryExists(src_file_path)); | 
 |   scoped_ptr<DriveEntryProto> src_entry_proto = GetEntryInfoByPathSync( | 
 |       src_file_path); | 
 |   ASSERT_TRUE(src_entry_proto.get()); | 
 |   std::string src_file_resource_id = | 
 |       src_entry_proto->resource_id(); | 
 |   EXPECT_FALSE(src_entry_proto->edit_url().empty()); | 
 |  | 
 |   ASSERT_TRUE(EntryExists(src_parent_path)); | 
 |   scoped_ptr<DriveEntryProto> src_parent_proto = GetEntryInfoByPathSync( | 
 |       src_parent_path); | 
 |   ASSERT_TRUE(src_parent_proto.get()); | 
 |   ASSERT_TRUE(src_parent_proto->file_info().is_directory()); | 
 |   EXPECT_FALSE(src_parent_proto->content_url().empty()); | 
 |  | 
 |   ASSERT_TRUE(EntryExists(dest_parent_path)); | 
 |   scoped_ptr<DriveEntryProto> dest_parent_proto = GetEntryInfoByPathSync( | 
 |       dest_parent_path); | 
 |   ASSERT_TRUE(dest_parent_proto.get()); | 
 |   ASSERT_TRUE(dest_parent_proto->file_info().is_directory()); | 
 |   EXPECT_FALSE(dest_parent_proto->content_url().empty()); | 
 |  | 
 |   EXPECT_FALSE(EntryExists(interim_file_path)); | 
 |  | 
 |   EXPECT_CALL(*mock_drive_service_, | 
 |               RenameResource(GURL(src_entry_proto->edit_url()), | 
 |                              FILE_PATH_LITERAL("Test.log"), _)); | 
 |   EXPECT_CALL(*mock_drive_service_, | 
 |               RemoveResourceFromDirectory( | 
 |                   GURL(src_parent_proto->content_url()), | 
 |                   GURL(src_entry_proto->edit_url()), | 
 |                   src_file_resource_id, _)); | 
 |   EXPECT_CALL(*mock_drive_service_, | 
 |               AddResourceToDirectory( | 
 |                   GURL(dest_parent_proto->content_url()), | 
 |                   GURL(src_entry_proto->edit_url()), | 
 |                   _)); | 
 |  | 
 |   FileOperationCallback callback = | 
 |       base::Bind(&CallbackHelper::FileOperationCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   // Expect notification for both source and destination directories plus | 
 |   // interim file path. | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive/Directory 1"))))).Times(1); | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive"))))).Times(1); | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive/New Folder 1"))))).Times(1); | 
 |  | 
 |   file_system_->Move(src_file_path, dest_file_path, callback); | 
 |   test_util::RunBlockingPoolTask(); | 
 |   EXPECT_EQ(DRIVE_FILE_OK, callback_helper_->last_error_); | 
 |  | 
 |   EXPECT_FALSE(EntryExists(src_file_path)); | 
 |   EXPECT_FALSE(EntryExists(interim_file_path)); | 
 |  | 
 |   EXPECT_FALSE(EntryExists(src_file_path)); | 
 |   EXPECT_TRUE(EntryExists(dest_file_path)); | 
 |   EXPECT_EQ(src_file_resource_id, GetResourceIdByPath(dest_file_path)); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, MoveNotExistingFile) { | 
 |   FilePath src_file_path(FILE_PATH_LITERAL("drive/Dummy file.txt")); | 
 |   FilePath dest_file_path(FILE_PATH_LITERAL("drive/Test.log")); | 
 |  | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   EXPECT_FALSE(EntryExists(src_file_path)); | 
 |  | 
 |   FileOperationCallback callback = | 
 |       base::Bind(&CallbackHelper::FileOperationCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   file_system_->Move(src_file_path, dest_file_path, callback); | 
 |   test_util::RunBlockingPoolTask(); | 
 |   EXPECT_EQ(DRIVE_FILE_ERROR_NOT_FOUND, callback_helper_->last_error_); | 
 |  | 
 |   EXPECT_FALSE(EntryExists(src_file_path)); | 
 |   EXPECT_FALSE(EntryExists(dest_file_path)); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, MoveFileToNonExistingDirectory) { | 
 |   FilePath src_file_path(FILE_PATH_LITERAL("drive/File 1.txt")); | 
 |   FilePath dest_parent_path(FILE_PATH_LITERAL("drive/Dummy")); | 
 |   FilePath dest_file_path(FILE_PATH_LITERAL("drive/Dummy/Test.log")); | 
 |  | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   ASSERT_TRUE(EntryExists(src_file_path)); | 
 |   scoped_ptr<DriveEntryProto> src_entry_proto = GetEntryInfoByPathSync( | 
 |       src_file_path); | 
 |   ASSERT_TRUE(src_entry_proto.get()); | 
 |   std::string src_file_resource_id = | 
 |       src_entry_proto->resource_id(); | 
 |   EXPECT_FALSE(src_entry_proto->edit_url().empty()); | 
 |  | 
 |   EXPECT_FALSE(EntryExists(dest_parent_path)); | 
 |  | 
 |   FileOperationCallback callback = | 
 |       base::Bind(&CallbackHelper::FileOperationCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   file_system_->Move(src_file_path, dest_file_path, callback); | 
 |   test_util::RunBlockingPoolTask(); | 
 |   EXPECT_EQ(DRIVE_FILE_ERROR_NOT_FOUND, callback_helper_->last_error_); | 
 |  | 
 |  | 
 |   EXPECT_FALSE(EntryExists(dest_parent_path)); | 
 |   EXPECT_FALSE(EntryExists(dest_file_path)); | 
 | } | 
 |  | 
 | // Test the case where the parent of |dest_file_path| is a existing file, | 
 | // not a directory. | 
 | TEST_F(DriveFileSystemTest, MoveFileToInvalidPath) { | 
 |   FilePath src_file_path(FILE_PATH_LITERAL("drive/File 1.txt")); | 
 |   FilePath dest_parent_path(FILE_PATH_LITERAL("drive/Duplicate Name.txt")); | 
 |   FilePath dest_file_path(FILE_PATH_LITERAL( | 
 |       "drive/Duplicate Name.txt/Test.log")); | 
 |  | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   ASSERT_TRUE(EntryExists(src_file_path)); | 
 |   scoped_ptr<DriveEntryProto> src_entry_proto = GetEntryInfoByPathSync( | 
 |       src_file_path); | 
 |   ASSERT_TRUE(src_entry_proto.get()); | 
 |   std::string src_file_resource_id = | 
 |       src_entry_proto->resource_id(); | 
 |   EXPECT_FALSE(src_entry_proto->edit_url().empty()); | 
 |  | 
 |   ASSERT_TRUE(EntryExists(dest_parent_path)); | 
 |   scoped_ptr<DriveEntryProto> dest_parent_proto = GetEntryInfoByPathSync( | 
 |       dest_parent_path); | 
 |   ASSERT_TRUE(dest_parent_proto.get()); | 
 |  | 
 |   FileOperationCallback callback = | 
 |       base::Bind(&CallbackHelper::FileOperationCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   file_system_->Move(src_file_path, dest_file_path, callback); | 
 |   test_util::RunBlockingPoolTask(); | 
 |   EXPECT_EQ(DRIVE_FILE_ERROR_NOT_A_DIRECTORY, | 
 |             callback_helper_->last_error_); | 
 |  | 
 |   EXPECT_TRUE(EntryExists(src_file_path)); | 
 |   EXPECT_TRUE(EntryExists(dest_parent_path)); | 
 |   EXPECT_FALSE(EntryExists(dest_file_path)); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, RemoveEntries) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   FilePath nonexisting_file(FILE_PATH_LITERAL("drive/Dummy file.txt")); | 
 |   FilePath file_in_root(FILE_PATH_LITERAL("drive/File 1.txt")); | 
 |   FilePath dir_in_root(FILE_PATH_LITERAL("drive/Directory 1")); | 
 |   FilePath file_in_subdir( | 
 |       FILE_PATH_LITERAL("drive/Directory 1/SubDirectory File 1.txt")); | 
 |  | 
 |   ASSERT_TRUE(EntryExists(file_in_root)); | 
 |   scoped_ptr<DriveEntryProto> file_in_root_proto = GetEntryInfoByPathSync( | 
 |       file_in_root); | 
 |   ASSERT_TRUE(file_in_root_proto.get()); | 
 |  | 
 |   ASSERT_TRUE(EntryExists(dir_in_root)); | 
 |   scoped_ptr<DriveEntryProto> dir_in_root_proto = GetEntryInfoByPathSync( | 
 |       dir_in_root); | 
 |   ASSERT_TRUE(dir_in_root_proto.get()); | 
 |   ASSERT_TRUE(dir_in_root_proto->file_info().is_directory()); | 
 |  | 
 |   ASSERT_TRUE(EntryExists(file_in_subdir)); | 
 |   scoped_ptr<DriveEntryProto> file_in_subdir_proto = GetEntryInfoByPathSync( | 
 |       file_in_subdir); | 
 |   ASSERT_TRUE(file_in_subdir_proto.get()); | 
 |  | 
 |   // Once for file in root and once for file... | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive"))))).Times(2); | 
 |  | 
 |   // Remove first file in root. | 
 |   EXPECT_TRUE(RemoveEntry(file_in_root)); | 
 |   EXPECT_FALSE(EntryExists(file_in_root)); | 
 |   EXPECT_TRUE(EntryExists(dir_in_root)); | 
 |   EXPECT_TRUE(EntryExists(file_in_subdir)); | 
 |  | 
 |   // Remove directory. | 
 |   EXPECT_TRUE(RemoveEntry(dir_in_root)); | 
 |   EXPECT_FALSE(EntryExists(file_in_root)); | 
 |   EXPECT_FALSE(EntryExists(dir_in_root)); | 
 |   EXPECT_FALSE(EntryExists(file_in_subdir)); | 
 |  | 
 |   // Try removing file in already removed subdirectory. | 
 |   EXPECT_FALSE(RemoveEntry(file_in_subdir)); | 
 |  | 
 |   // Try removing non-existing file. | 
 |   EXPECT_FALSE(RemoveEntry(nonexisting_file)); | 
 |  | 
 |   // Try removing root file element. | 
 |   EXPECT_FALSE(RemoveEntry(FilePath(FILE_PATH_LITERAL("drive")))); | 
 |  | 
 |   // Need this to ensure OnDirectoryChanged() is run. | 
 |   test_util::RunBlockingPoolTask(); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, CreateDirectory) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive"))))).Times(1); | 
 |  | 
 |   // Create directory in root. | 
 |   FilePath dir_path(FILE_PATH_LITERAL("drive/New Folder 1")); | 
 |   EXPECT_FALSE(EntryExists(dir_path)); | 
 |   AddDirectoryFromFile(dir_path, "gdata/directory_entry_atom.json"); | 
 |   EXPECT_TRUE(EntryExists(dir_path)); | 
 |  | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive/New Folder 1"))))).Times(1); | 
 |  | 
 |   // Create directory in a sub directory. | 
 |   FilePath subdir_path(FILE_PATH_LITERAL("drive/New Folder 1/New Folder 2")); | 
 |   EXPECT_FALSE(EntryExists(subdir_path)); | 
 |   AddDirectoryFromFile(subdir_path, "gdata/directory_entry_atom2.json"); | 
 |   EXPECT_TRUE(EntryExists(subdir_path)); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, FindFirstMissingParentDirectory) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   DriveFileSystem::FindFirstMissingParentDirectoryResult result; | 
 |  | 
 |   // Create directory in root. | 
 |   FilePath dir_path(FILE_PATH_LITERAL("drive/New Folder 1")); | 
 |   file_system_->FindFirstMissingParentDirectory( | 
 |       dir_path, | 
 |       base::Bind(&CopyResultFromFindFirstMissingParentDirectory, | 
 |                  &result)); | 
 |   test_util::RunBlockingPoolTask(); | 
 |   EXPECT_EQ(DriveFileSystem::FIND_FIRST_FOUND_MISSING, result.error); | 
 |   EXPECT_EQ(FilePath(FILE_PATH_LITERAL("drive/New Folder 1")), | 
 |             result.first_missing_parent_path); | 
 |   EXPECT_TRUE(result.last_dir_content_url.is_empty());  // root directory. | 
 |  | 
 |   // Missing folders in subdir of an existing folder. | 
 |   FilePath dir_path2(FILE_PATH_LITERAL("drive/Directory 1/New Folder 2")); | 
 |   file_system_->FindFirstMissingParentDirectory( | 
 |       dir_path2, | 
 |       base::Bind(&CopyResultFromFindFirstMissingParentDirectory, | 
 |                  &result)); | 
 |   test_util::RunBlockingPoolTask(); | 
 |   EXPECT_EQ(DriveFileSystem::FIND_FIRST_FOUND_MISSING, result.error); | 
 |   EXPECT_EQ(FilePath(FILE_PATH_LITERAL("drive/Directory 1/New Folder 2")), | 
 |             result.first_missing_parent_path); | 
 |   EXPECT_FALSE(result.last_dir_content_url.is_empty());  // non-root dir. | 
 |  | 
 |   // Missing two folders on the path. | 
 |   FilePath dir_path3 = dir_path2.Append(FILE_PATH_LITERAL("Another Folder")); | 
 |   file_system_->FindFirstMissingParentDirectory( | 
 |       dir_path3, | 
 |       base::Bind(&CopyResultFromFindFirstMissingParentDirectory, | 
 |                  &result)); | 
 |   test_util::RunBlockingPoolTask(); | 
 |   EXPECT_EQ(DriveFileSystem::FIND_FIRST_FOUND_MISSING, result.error); | 
 |   EXPECT_EQ(FilePath(FILE_PATH_LITERAL("drive/Directory 1/New Folder 2")), | 
 |             result.first_missing_parent_path); | 
 |   EXPECT_FALSE(result.last_dir_content_url.is_empty());  // non-root dir. | 
 |  | 
 |   // Folders on top of an existing file. | 
 |   file_system_->FindFirstMissingParentDirectory( | 
 |       FilePath(FILE_PATH_LITERAL("drive/File 1.txt/BadDir")), | 
 |       base::Bind(&CopyResultFromFindFirstMissingParentDirectory, | 
 |                  &result)); | 
 |   test_util::RunBlockingPoolTask(); | 
 |   EXPECT_EQ(DriveFileSystem::FIND_FIRST_FOUND_INVALID, result.error); | 
 |  | 
 |   // Existing folder. | 
 |   file_system_->FindFirstMissingParentDirectory( | 
 |       FilePath(FILE_PATH_LITERAL("drive/Directory 1")), | 
 |       base::Bind(&CopyResultFromFindFirstMissingParentDirectory, | 
 |                  &result)); | 
 |   test_util::RunBlockingPoolTask(); | 
 |   EXPECT_EQ(DriveFileSystem::FIND_FIRST_DIRECTORY_ALREADY_PRESENT, | 
 |             result.error); | 
 | } | 
 |  | 
 | // Create a directory through the document service | 
 | TEST_F(DriveFileSystemTest, CreateDirectoryWithService) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |   EXPECT_CALL(*mock_drive_service_, | 
 |               CreateDirectory(_, "Sample Directory Title", _)).Times(1); | 
 |   EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged( | 
 |       Eq(FilePath(FILE_PATH_LITERAL("drive"))))).Times(1); | 
 |  | 
 |   // Set last error so it's not a valid error code. | 
 |   callback_helper_->last_error_ = static_cast<DriveFileError>(1); | 
 |   file_system_->CreateDirectory( | 
 |       FilePath(FILE_PATH_LITERAL("drive/Sample Directory Title")), | 
 |       false,  // is_exclusive | 
 |       true,  // is_recursive | 
 |       base::Bind(&CallbackHelper::FileOperationCallback, | 
 |                  callback_helper_.get())); | 
 |   test_util::RunBlockingPoolTask(); | 
 |   // TODO(gspencer): Uncomment this when we get a blob that | 
 |   // works that can be returned from the mock. | 
 |   // EXPECT_EQ(DRIVE_FILE_OK, callback_helper_->last_error_); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, GetFileByPath_FromGData_EnoughSpace) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   GetFileCallback callback = | 
 |       base::Bind(&CallbackHelper::GetFileCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   FilePath file_in_root(FILE_PATH_LITERAL("drive/File 1.txt")); | 
 |   scoped_ptr<DriveEntryProto> entry_proto(GetEntryInfoByPathSync(file_in_root)); | 
 |   FilePath downloaded_file = GetCachePathForFile( | 
 |       entry_proto->resource_id(), | 
 |       entry_proto->file_specific_info().file_md5()); | 
 |   const int64 file_size = entry_proto->file_info().size(); | 
 |  | 
 |   // Pretend we have enough space. | 
 |   EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace()) | 
 |       .Times(2).WillRepeatedly(Return(file_size + kMinFreeSpace)); | 
 |  | 
 |   // Before Download starts metadata from server will be fetched. | 
 |   // We will read content url from the result. | 
 |   scoped_ptr<base::Value> document = | 
 |       test_util::LoadJSONFile("gdata/document_to_download.json"); | 
 |   SetExpectationsForGetDocumentEntry(&document, "file:2_file_resource_id"); | 
 |  | 
 |   // The file is obtained with the mock DriveService. | 
 |   EXPECT_CALL(*mock_drive_service_, | 
 |               DownloadFile(file_in_root, | 
 |                            downloaded_file, | 
 |                            GURL("https://file_content_url_changed/"), | 
 |                            _, _)) | 
 |       .Times(1); | 
 |  | 
 |   file_system_->GetFileByPath(file_in_root, callback, | 
 |                               GetContentCallback()); | 
 |   test_util::RunBlockingPoolTask(); | 
 |  | 
 |   EXPECT_EQ(DRIVE_FILE_OK, callback_helper_->last_error_); | 
 |   EXPECT_EQ(REGULAR_FILE, callback_helper_->file_type_); | 
 |   EXPECT_EQ(downloaded_file.value(), | 
 |             callback_helper_->download_path_.value()); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, GetFileByPath_FromGData_NoSpaceAtAll) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   GetFileCallback callback = | 
 |       base::Bind(&CallbackHelper::GetFileCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   FilePath file_in_root(FILE_PATH_LITERAL("drive/File 1.txt")); | 
 |   scoped_ptr<DriveEntryProto> entry_proto(GetEntryInfoByPathSync(file_in_root)); | 
 |   FilePath downloaded_file = GetCachePathForFile( | 
 |       entry_proto->resource_id(), | 
 |       entry_proto->file_specific_info().file_md5()); | 
 |  | 
 |   // Pretend we have no space at all. | 
 |   EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace()) | 
 |       .Times(2).WillRepeatedly(Return(0)); | 
 |  | 
 |   // Before Download starts metadata from server will be fetched. | 
 |   // We will read content url from the result. | 
 |   scoped_ptr<base::Value> document = | 
 |       test_util::LoadJSONFile("gdata/document_to_download.json"); | 
 |   SetExpectationsForGetDocumentEntry(&document, "file:2_file_resource_id"); | 
 |  | 
 |   // The file is not obtained with the mock DriveService, because of no space. | 
 |   EXPECT_CALL(*mock_drive_service_, | 
 |               DownloadFile(file_in_root, | 
 |                            downloaded_file, | 
 |                            GURL("https://file_content_url_changed/"), | 
 |                            _, _)) | 
 |       .Times(0); | 
 |  | 
 |   file_system_->GetFileByPath(file_in_root, callback, | 
 |                               GetContentCallback()); | 
 |   test_util::RunBlockingPoolTask(); | 
 |  | 
 |   EXPECT_EQ(DRIVE_FILE_ERROR_NO_SPACE, | 
 |             callback_helper_->last_error_); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, GetFileByPath_FromGData_NoEnoughSpaceButCanFreeUp) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   GetFileCallback callback = | 
 |       base::Bind(&CallbackHelper::GetFileCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   FilePath file_in_root(FILE_PATH_LITERAL("drive/File 1.txt")); | 
 |   scoped_ptr<DriveEntryProto> entry_proto(GetEntryInfoByPathSync(file_in_root)); | 
 |   FilePath downloaded_file = GetCachePathForFile( | 
 |       entry_proto->resource_id(), | 
 |       entry_proto->file_specific_info().file_md5()); | 
 |   const int64 file_size = entry_proto->file_info().size(); | 
 |  | 
 |   // Pretend we have no space first (checked before downloading a file), | 
 |   // but then start reporting we have space. This is to emulate that | 
 |   // the disk space was freed up by removing temporary files. | 
 |   EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace()) | 
 |       .WillOnce(Return(file_size + kMinFreeSpace)) | 
 |       .WillOnce(Return(0)) | 
 |       .WillOnce(Return(file_size + kMinFreeSpace)) | 
 |       .WillOnce(Return(file_size + kMinFreeSpace)); | 
 |  | 
 |   // Store something in the temporary cache directory. | 
 |   TestStoreToCache("<resource_id>", | 
 |                    "<md5>", | 
 |                    test_util::GetTestFilePath("gdata/root_feed.json"), | 
 |                    DRIVE_FILE_OK, | 
 |                    test_util::TEST_CACHE_STATE_PRESENT, | 
 |                    DriveCache::CACHE_TYPE_TMP); | 
 |   ASSERT_TRUE(CacheEntryExists("<resource_id>", "<md5>")); | 
 |   ASSERT_TRUE(CacheFileExists("<resource_id>", "<md5>")); | 
 |  | 
 |   // Before Download starts metadata from server will be fetched. | 
 |   // We will read content url from the result. | 
 |   scoped_ptr<base::Value> document = | 
 |       test_util::LoadJSONFile("gdata/document_to_download.json"); | 
 |   SetExpectationsForGetDocumentEntry(&document, "file:2_file_resource_id"); | 
 |  | 
 |   // The file is obtained with the mock DriveService, because of we freed up the | 
 |   // space. | 
 |   EXPECT_CALL(*mock_drive_service_, | 
 |               DownloadFile(file_in_root, | 
 |                            downloaded_file, | 
 |                            GURL("https://file_content_url_changed/"), | 
 |                            _, _)) | 
 |       .Times(1); | 
 |  | 
 |   file_system_->GetFileByPath(file_in_root, callback, | 
 |                               GetContentCallback()); | 
 |   test_util::RunBlockingPoolTask(); | 
 |  | 
 |   EXPECT_EQ(DRIVE_FILE_OK, callback_helper_->last_error_); | 
 |   EXPECT_EQ(REGULAR_FILE, callback_helper_->file_type_); | 
 |   EXPECT_EQ(downloaded_file.value(), | 
 |             callback_helper_->download_path_.value()); | 
 |  | 
 |   // The file should be removed in order to free up space, and the cache | 
 |   // entry should also be removed. | 
 |   ASSERT_FALSE(CacheEntryExists("<resource_id>", "<md5>")); | 
 |   ASSERT_FALSE(CacheFileExists("<resource_id>", "<md5>")); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, GetFileByPath_FromGData_EnoughSpaceButBecomeFull) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   GetFileCallback callback = | 
 |       base::Bind(&CallbackHelper::GetFileCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   FilePath file_in_root(FILE_PATH_LITERAL("drive/File 1.txt")); | 
 |   scoped_ptr<DriveEntryProto> entry_proto(GetEntryInfoByPathSync(file_in_root)); | 
 |   FilePath downloaded_file = GetCachePathForFile( | 
 |       entry_proto->resource_id(), | 
 |       entry_proto->file_specific_info().file_md5()); | 
 |   const int64 file_size = entry_proto->file_info().size(); | 
 |  | 
 |   // Pretend we have enough space first (checked before downloading a file), | 
 |   // but then start reporting we have not enough space. This is to emulate that | 
 |   // the disk space becomes full after the file is downloaded for some reason | 
 |   // (ex. the actual file was larger than the expected size). | 
 |   EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace()) | 
 |       .WillOnce(Return(file_size + kMinFreeSpace)) | 
 |       .WillOnce(Return(kMinFreeSpace - 1)) | 
 |       .WillOnce(Return(kMinFreeSpace - 1)); | 
 |  | 
 |   // Before Download starts metadata from server will be fetched. | 
 |   // We will read content url from the result. | 
 |   scoped_ptr<base::Value> document = | 
 |       test_util::LoadJSONFile("gdata/document_to_download.json"); | 
 |   SetExpectationsForGetDocumentEntry(&document, "file:2_file_resource_id"); | 
 |  | 
 |   // The file is obtained with the mock DriveService. | 
 |   EXPECT_CALL(*mock_drive_service_, | 
 |               DownloadFile(file_in_root, | 
 |                            downloaded_file, | 
 |                            GURL("https://file_content_url_changed/"), | 
 |                            _, _)) | 
 |       .Times(1); | 
 |  | 
 |   file_system_->GetFileByPath(file_in_root, callback, | 
 |                               GetContentCallback()); | 
 |   test_util::RunBlockingPoolTask(); | 
 |  | 
 |   EXPECT_EQ(DRIVE_FILE_ERROR_NO_SPACE, | 
 |             callback_helper_->last_error_); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, GetFileByPath_FromCache) { | 
 |   EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace()) | 
 |       .Times(AtLeast(1)).WillRepeatedly(Return(kLotsOfSpace)); | 
 |  | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   GetFileCallback callback = | 
 |       base::Bind(&CallbackHelper::GetFileCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   FilePath file_in_root(FILE_PATH_LITERAL("drive/File 1.txt")); | 
 |   scoped_ptr<DriveEntryProto> entry_proto(GetEntryInfoByPathSync(file_in_root)); | 
 |   FilePath downloaded_file = GetCachePathForFile( | 
 |       entry_proto->resource_id(), | 
 |       entry_proto->file_specific_info().file_md5()); | 
 |  | 
 |   // Store something as cached version of this file. | 
 |   TestStoreToCache(entry_proto->resource_id(), | 
 |                    entry_proto->file_specific_info().file_md5(), | 
 |                    test_util::GetTestFilePath("gdata/root_feed.json"), | 
 |                    DRIVE_FILE_OK, | 
 |                    test_util::TEST_CACHE_STATE_PRESENT, | 
 |                    DriveCache::CACHE_TYPE_TMP); | 
 |  | 
 |   // Make sure we don't fetch metadata for downloading file. | 
 |   EXPECT_CALL(*mock_drive_service_, GetDocumentEntry(_, _)).Times(0); | 
 |  | 
 |   // Make sure we don't call downloads at all. | 
 |   EXPECT_CALL(*mock_drive_service_, | 
 |               DownloadFile(file_in_root, | 
 |                            downloaded_file, | 
 |                            GURL("https://file_content_url_changed/"), | 
 |                            _, _)) | 
 |       .Times(0); | 
 |  | 
 |   file_system_->GetFileByPath(file_in_root, callback, | 
 |                               GetContentCallback()); | 
 |   test_util::RunBlockingPoolTask(); | 
 |  | 
 |   EXPECT_EQ(REGULAR_FILE, callback_helper_->file_type_); | 
 |   EXPECT_EQ(downloaded_file.value(), | 
 |             callback_helper_->download_path_.value()); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, GetFileByPath_HostedDocument) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   GetFileCallback callback = | 
 |       base::Bind(&CallbackHelper::GetFileCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   FilePath file_in_root(FILE_PATH_LITERAL("drive/Document 1.gdoc")); | 
 |   scoped_ptr<DriveEntryProto> src_entry_proto = | 
 |       GetEntryInfoByPathSync(file_in_root); | 
 |   ASSERT_TRUE(src_entry_proto.get()); | 
 |  | 
 |   file_system_->GetFileByPath(file_in_root, callback, | 
 |                               GetContentCallback()); | 
 |   test_util::RunBlockingPoolTask(); | 
 |  | 
 |   EXPECT_EQ(HOSTED_DOCUMENT, callback_helper_->file_type_); | 
 |   EXPECT_FALSE(callback_helper_->download_path_.empty()); | 
 |  | 
 |   ASSERT_TRUE(src_entry_proto.get()); | 
 |   VerifyHostedDocumentJSONFile(*src_entry_proto, | 
 |                                callback_helper_->download_path_); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, GetFileByResourceId) { | 
 |   EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace()) | 
 |       .Times(AtLeast(1)).WillRepeatedly(Return(kLotsOfSpace)); | 
 |  | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   GetFileCallback callback = | 
 |       base::Bind(&CallbackHelper::GetFileCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   FilePath file_in_root(FILE_PATH_LITERAL("drive/File 1.txt")); | 
 |   scoped_ptr<DriveEntryProto> entry_proto(GetEntryInfoByPathSync(file_in_root)); | 
 |   FilePath downloaded_file = GetCachePathForFile( | 
 |       entry_proto->resource_id(), | 
 |       entry_proto->file_specific_info().file_md5()); | 
 |  | 
 |   // Before Download starts metadata from server will be fetched. | 
 |   // We will read content url from the result. | 
 |   scoped_ptr<base::Value> document = | 
 |       test_util::LoadJSONFile("gdata/document_to_download.json"); | 
 |   SetExpectationsForGetDocumentEntry(&document, "file:2_file_resource_id"); | 
 |  | 
 |   // The file is obtained with the mock DriveService, because it's not stored in | 
 |   // the cache. | 
 |   EXPECT_CALL(*mock_drive_service_, | 
 |               DownloadFile(file_in_root, | 
 |                            downloaded_file, | 
 |                            GURL("https://file_content_url_changed/"), | 
 |                            _, _)) | 
 |       .Times(1); | 
 |  | 
 |   file_system_->GetFileByResourceId(entry_proto->resource_id(), | 
 |                                     callback, | 
 |                                     GetContentCallback()); | 
 |   test_util::RunBlockingPoolTask(); | 
 |  | 
 |   EXPECT_EQ(REGULAR_FILE, callback_helper_->file_type_); | 
 |   EXPECT_EQ(downloaded_file.value(), | 
 |             callback_helper_->download_path_.value()); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, GetFileByResourceId_FromCache) { | 
 |   EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace()) | 
 |       .Times(AtLeast(1)).WillRepeatedly(Return(kLotsOfSpace)); | 
 |  | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   GetFileCallback callback = | 
 |       base::Bind(&CallbackHelper::GetFileCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   FilePath file_in_root(FILE_PATH_LITERAL("drive/File 1.txt")); | 
 |   scoped_ptr<DriveEntryProto> entry_proto(GetEntryInfoByPathSync(file_in_root)); | 
 |   FilePath downloaded_file = GetCachePathForFile( | 
 |       entry_proto->resource_id(), | 
 |       entry_proto->file_specific_info().file_md5()); | 
 |  | 
 |   // Store something as cached version of this file. | 
 |   TestStoreToCache(entry_proto->resource_id(), | 
 |                    entry_proto->file_specific_info().file_md5(), | 
 |                    test_util::GetTestFilePath("gdata/root_feed.json"), | 
 |                    DRIVE_FILE_OK, | 
 |                    test_util::TEST_CACHE_STATE_PRESENT, | 
 |                    DriveCache::CACHE_TYPE_TMP); | 
 |  | 
 |   // The file is obtained from the cache. | 
 |   // Make sure we don't call downloads at all. | 
 |   EXPECT_CALL(*mock_drive_service_, DownloadFile(_, _, _, _, _)) | 
 |       .Times(0); | 
 |  | 
 |   file_system_->GetFileByResourceId(entry_proto->resource_id(), | 
 |                                     callback, | 
 |                                     GetContentCallback()); | 
 |   test_util::RunBlockingPoolTask(); | 
 |  | 
 |   EXPECT_EQ(REGULAR_FILE, callback_helper_->file_type_); | 
 |   EXPECT_EQ(downloaded_file.value(), | 
 |             callback_helper_->download_path_.value()); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, UpdateFileByResourceId_PersistentFile) { | 
 |   EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace()) | 
 |       .Times(AtLeast(1)).WillRepeatedly(Return(kLotsOfSpace)); | 
 |  | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   // This is a file defined in root_feed.json. | 
 |   const FilePath kFilePath(FILE_PATH_LITERAL("drive/File 1.txt")); | 
 |   const std::string kResourceId("file:2_file_resource_id"); | 
 |   const std::string kMd5("3b4382ebefec6e743578c76bbd0575ce"); | 
 |  | 
 |   // Pin the file so it'll be store in "persistent" directory. | 
 |   EXPECT_CALL(*mock_cache_observer_, OnCachePinned(kResourceId, kMd5)).Times(1); | 
 |   TestPin(kResourceId, | 
 |           kMd5, | 
 |           DRIVE_FILE_OK, | 
 |           test_util::TEST_CACHE_STATE_PINNED, | 
 |           DriveCache::CACHE_TYPE_TMP); | 
 |  | 
 |   // First store a file to cache. A cache file will be created at: | 
 |   // GCache/v1/persistent/<kResourceId>.<kMd5> | 
 |   const FilePath original_cache_file_path = | 
 |       DriveCache::GetCacheRootPath(profile_.get()) | 
 |       .AppendASCII("persistent") | 
 |       .AppendASCII(kResourceId + "." + kMd5); | 
 |   TestStoreToCache(kResourceId, | 
 |                    kMd5, | 
 |                    // Anything works. | 
 |                    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); | 
 |   ASSERT_TRUE(file_util::PathExists(original_cache_file_path)); | 
 |  | 
 |   // Add the dirty bit. The cache file will be renamed to | 
 |   // GCache/v1/persistent/<kResourceId>.local | 
 |   TestMarkDirty(kResourceId, | 
 |                 kMd5, | 
 |                 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); | 
 |   const FilePath dirty_cache_file_path = | 
 |       DriveCache::GetCacheRootPath(profile_.get()) | 
 |       .AppendASCII("persistent") | 
 |       .AppendASCII(kResourceId + ".local"); | 
 |   ASSERT_FALSE(file_util::PathExists(original_cache_file_path)); | 
 |   ASSERT_TRUE(file_util::PathExists(dirty_cache_file_path)); | 
 |  | 
 |   // Modify the cached file. | 
 |   const std::string kDummyCacheContent("modification to the cache"); | 
 |   ASSERT_TRUE(file_util::WriteFile(dirty_cache_file_path, | 
 |                                    kDummyCacheContent.c_str(), | 
 |                                    kDummyCacheContent.size())); | 
 |  | 
 |   // Commit the dirty bit. The cache file name remains the same | 
 |   // but a symlink will be created at: | 
 |   // GCache/v1/outgoing/<kResourceId> | 
 |   EXPECT_CALL(*mock_cache_observer_, OnCacheCommitted(kResourceId)).Times(1); | 
 |   TestCommitDirty(kResourceId, | 
 |                   kMd5, | 
 |                   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); | 
 |   const FilePath outgoing_symlink_path = | 
 |       DriveCache::GetCacheRootPath(profile_.get()) | 
 |       .AppendASCII("outgoing") | 
 |       .AppendASCII(kResourceId); | 
 |   ASSERT_TRUE(file_util::PathExists(dirty_cache_file_path)); | 
 |   ASSERT_TRUE(file_util::PathExists(outgoing_symlink_path)); | 
 |  | 
 |   // Create a DocumentEntry, which is needed to mock | 
 |   // DriveUploaderInterface::UploadExistingFile(). | 
 |   // TODO(satorux): This should be cleaned up. crbug.com/134240. | 
 |   DocumentEntry* document_entry = NULL; | 
 |   scoped_ptr<base::Value> value = | 
 |       test_util::LoadJSONFile("gdata/root_feed.json"); | 
 |   ASSERT_TRUE(value.get()); | 
 |   base::DictionaryValue* as_dict = NULL; | 
 |   base::ListValue* entry_list = NULL; | 
 |   if (value->GetAsDictionary(&as_dict) && | 
 |       as_dict->GetList("feed.entry", &entry_list)) { | 
 |     for (size_t i = 0; i < entry_list->GetSize(); ++i) { | 
 |       base::DictionaryValue* entry = NULL; | 
 |       std::string resource_id; | 
 |       if (entry_list->GetDictionary(i, &entry) && | 
 |           entry->GetString("gd$resourceId.$t", &resource_id) && | 
 |           resource_id == kResourceId) { | 
 |         // This will be deleted by UploadExistingFile(). | 
 |         document_entry = DocumentEntry::CreateFrom(*entry); | 
 |       } | 
 |     } | 
 |   } | 
 |   ASSERT_TRUE(document_entry); | 
 |  | 
 |   // The mock uploader will be used to simulate a file upload. | 
 |   EXPECT_CALL(*mock_uploader_, UploadExistingFile( | 
 |       GURL("https://file_link_resumable_edit_media/"), | 
 |       kFilePath, | 
 |       dirty_cache_file_path, | 
 |       "audio/mpeg", | 
 |       kDummyCacheContent.size(),  // The size after modification must be used. | 
 |       _,   // Completion callback. | 
 |       _))  // Ready callback. | 
 |       .WillOnce(MockUploadExistingFile( | 
 |           DRIVE_FILE_OK, | 
 |           FilePath::FromUTF8Unsafe("drive/File1"), | 
 |           dirty_cache_file_path, | 
 |           document_entry)); | 
 |  | 
 |   // We'll notify the directory change to the observer upon completion. | 
 |   EXPECT_CALL(*mock_directory_observer_, | 
 |               OnDirectoryChanged(Eq(FilePath(kDriveRootDirectory)))).Times(1); | 
 |  | 
 |   // The callback will be called upon completion of | 
 |   // UpdateFileByResourceId(). | 
 |   FileOperationCallback callback = | 
 |       base::Bind(&CallbackHelper::FileOperationCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   // Check the number of files in the root directory. We'll compare the | 
 |   // number after updating a file. | 
 |   scoped_ptr<DriveEntryProtoVector> root_directory_entries( | 
 |       ReadDirectoryByPathSync(FilePath::FromUTF8Unsafe("drive"))); | 
 |   ASSERT_TRUE(root_directory_entries.get()); | 
 |   const int num_files_in_root = CountFiles(*root_directory_entries); | 
 |  | 
 |   file_system_->UpdateFileByResourceId(kResourceId, callback); | 
 |   test_util::RunBlockingPoolTask(); | 
 |  | 
 |   EXPECT_EQ(DRIVE_FILE_OK, callback_helper_->last_error_); | 
 |   // Make sure that the number of files did not change (i.e. we updated an | 
 |   // existing file, rather than adding a new file. The number of files | 
 |   // increases if we don't handle the file update right). | 
 |   EXPECT_EQ(num_files_in_root, CountFiles(*root_directory_entries)); | 
 |   // After the file is updated, the dirty bit is cleared, hence the symlink | 
 |   // should be gone. | 
 |   ASSERT_FALSE(file_util::PathExists(outgoing_symlink_path)); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, UpdateFileByResourceId_NonexistentFile) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   // This is nonexistent in root_feed.json. | 
 |   const FilePath kFilePath(FILE_PATH_LITERAL("drive/Nonexistent.txt")); | 
 |   const std::string kResourceId("file:nonexistent_resource_id"); | 
 |   const std::string kMd5("nonexistent_md5"); | 
 |  | 
 |   // The callback will be called upon completion of | 
 |   // UpdateFileByResourceId(). | 
 |   FileOperationCallback callback = | 
 |       base::Bind(&CallbackHelper::FileOperationCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   file_system_->UpdateFileByResourceId(kResourceId, callback); | 
 |   test_util::RunBlockingPoolTask(); | 
 |   EXPECT_EQ(DRIVE_FILE_ERROR_NOT_FOUND, callback_helper_->last_error_); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, ContentSearch) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   mock_drive_service_->set_search_result("gdata/search_result_feed.json"); | 
 |  | 
 |   EXPECT_CALL(*mock_drive_service_, GetDocuments(Eq(GURL()), _, "foo", _, _)) | 
 |       .Times(1); | 
 |  | 
 |   const SearchResultPair kExpectedResults[] = { | 
 |     { "drive/Directory 1/SubDirectory File 1.txt", false }, | 
 |     { "drive/Directory 1", true } | 
 |   }; | 
 |  | 
 |   SearchCallback callback = base::Bind(&DriveSearchCallback, | 
 |       &message_loop_, kExpectedResults, ARRAYSIZE_UNSAFE(kExpectedResults)); | 
 |  | 
 |   file_system_->Search("foo", GURL(), callback); | 
 |   message_loop_.Run();  // Wait to get our result. | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, ContentSearchWithNewEntry) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   // Search result returning two entries "Directory 1/" and | 
 |   // "Directory 1/SubDirectory Newly Added File.txt". The latter is not | 
 |   // contained in the root feed. | 
 |   mock_drive_service_->set_search_result( | 
 |       "gdata/search_result_with_new_entry_feed.json"); | 
 |  | 
 |   EXPECT_CALL(*mock_drive_service_, GetDocuments(Eq(GURL()), _, "foo", _, _)) | 
 |       .Times(1); | 
 |  | 
 |   // As the result of the first Search(), only entries in the current file | 
 |   // system snapshot are expected to be returned. | 
 |   const SearchResultPair kExpectedResults[] = { | 
 |     { "drive/Directory 1", true } | 
 |   }; | 
 |  | 
 |   // At the same time, unknown entry should trigger delta feed request. | 
 |   // This will cause notification to observers (e.g., File Browser) so that | 
 |   // they can request search again. | 
 |   EXPECT_CALL(*mock_drive_service_, GetAccountMetadata(_)).Times(1); | 
 |   EXPECT_CALL(*mock_drive_service_, GetDocuments(Eq(GURL()), _, "", _, _)) | 
 |       .Times(1); | 
 |   EXPECT_CALL(*mock_webapps_registry_, UpdateFromFeed(_)).Times(1); | 
 |  | 
 |   SearchCallback callback = base::Bind(&DriveSearchCallback, | 
 |       &message_loop_, kExpectedResults, ARRAYSIZE_UNSAFE(kExpectedResults)); | 
 |  | 
 |   file_system_->Search("foo", GURL(), callback); | 
 |   message_loop_.Run();  // Wait to get our result. | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, ContentSearchEmptyResult) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   mock_drive_service_->set_search_result("gdata/empty_feed.json"); | 
 |  | 
 |   EXPECT_CALL(*mock_drive_service_, GetDocuments(Eq(GURL()), _, "foo", _, _)) | 
 |       .Times(1); | 
 |  | 
 |   const SearchResultPair* expected_results = NULL; | 
 |  | 
 |   SearchCallback callback = base::Bind(&DriveSearchCallback, | 
 |       &message_loop_, expected_results, 0u); | 
 |  | 
 |   file_system_->Search("foo", GURL(), callback); | 
 |   message_loop_.Run();  // Wait to get our result. | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, GetAvailableSpace) { | 
 |   GetAvailableSpaceCallback callback = | 
 |       base::Bind(&CallbackHelper::GetAvailableSpaceCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   EXPECT_CALL(*mock_drive_service_, GetAccountMetadata(_)); | 
 |  | 
 |   file_system_->GetAvailableSpace(callback); | 
 |   test_util::RunBlockingPoolTask(); | 
 |   EXPECT_EQ(GG_LONGLONG(6789012345), callback_helper_->quota_bytes_used_); | 
 |   EXPECT_EQ(GG_LONGLONG(9876543210), callback_helper_->quota_bytes_total_); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, RequestDirectoryRefresh) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   // We'll fetch documents in the root directory with its resource ID. | 
 |   EXPECT_CALL(*mock_drive_service_, | 
 |               GetDocuments(Eq(GURL()), _, _, kDriveRootDirectoryResourceId, _)) | 
 |       .Times(1); | 
 |   // We'll notify the directory change to the observer. | 
 |   EXPECT_CALL(*mock_directory_observer_, | 
 |               OnDirectoryChanged(Eq(FilePath(kDriveRootDirectory)))).Times(1); | 
 |  | 
 |   file_system_->RequestDirectoryRefresh(FilePath(kDriveRootDirectory)); | 
 |   test_util::RunBlockingPoolTask(); | 
 | } | 
 |  | 
 | TEST_F(DriveFileSystemTest, OpenAndCloseFile) { | 
 |   LoadRootFeedDocument("gdata/root_feed.json"); | 
 |  | 
 |   OpenFileCallback callback = | 
 |       base::Bind(&CallbackHelper::OpenFileCallback, | 
 |                  callback_helper_.get()); | 
 |   FileOperationCallback close_file_callback = | 
 |       base::Bind(&CallbackHelper::CloseFileCallback, | 
 |                  callback_helper_.get()); | 
 |  | 
 |   const FilePath kFileInRoot(FILE_PATH_LITERAL("drive/File 1.txt")); | 
 |   scoped_ptr<DriveEntryProto> entry_proto(GetEntryInfoByPathSync(kFileInRoot)); | 
 |   FilePath downloaded_file = GetCachePathForFile( | 
 |       entry_proto->resource_id(), | 
 |       entry_proto->file_specific_info().file_md5()); | 
 |   const int64 file_size = entry_proto->file_info().size(); | 
 |   const std::string& file_resource_id = | 
 |       entry_proto->resource_id(); | 
 |   const std::string& file_md5 = entry_proto->file_specific_info().file_md5(); | 
 |  | 
 |   // A dirty file is created on close. | 
 |   EXPECT_CALL(*mock_cache_observer_, OnCacheCommitted(file_resource_id)) | 
 |       .Times(1); | 
 |  | 
 |   // Pretend we have enough space. | 
 |   EXPECT_CALL(*mock_free_disk_space_checker_, AmountOfFreeDiskSpace()) | 
 |       .Times(2).WillRepeatedly(Return(file_size + kMinFreeSpace)); | 
 |  | 
 |   const std::string kExpectedFileData = "test file data"; | 
 |   mock_drive_service_->set_file_data(new std::string(kExpectedFileData)); | 
 |  | 
 |   // Before Download starts metadata from server will be fetched. | 
 |   // We will read content url from the result. | 
 |   scoped_ptr<base::Value> document = | 
 |       test_util::LoadJSONFile("gdata/document_to_download.json"); | 
 |   SetExpectationsForGetDocumentEntry(&document, "file:2_file_resource_id"); | 
 |  | 
 |   // The file is obtained with the mock DriveService. | 
 |   EXPECT_CALL(*mock_drive_service_, | 
 |               DownloadFile(kFileInRoot, | 
 |                            downloaded_file, | 
 |                            GURL("https://file_content_url_changed/"), | 
 |                            _, _)) | 
 |       .Times(1); | 
 |  | 
 |   // Open kFileInRoot ("drive/File 1.txt"). | 
 |   file_system_->OpenFile(kFileInRoot, callback); | 
 |   message_loop_.Run(); | 
 |   const FilePath opened_file_path = callback_helper_->opened_file_path_; | 
 |  | 
 |   // Verify that the file was properly opened. | 
 |   EXPECT_EQ(DRIVE_FILE_OK, callback_helper_->last_error_); | 
 |  | 
 |   // Try to open the already opened file. | 
 |   file_system_->OpenFile(kFileInRoot, callback); | 
 |   message_loop_.Run(); | 
 |  | 
 |   // It must fail. | 
 |   EXPECT_EQ(DRIVE_FILE_ERROR_IN_USE, callback_helper_->last_error_); | 
 |  | 
 |   // Verify that the file contents match the expected contents. | 
 |   std::string cache_file_data; | 
 |   EXPECT_TRUE(file_util::ReadFileToString(opened_file_path, &cache_file_data)); | 
 |   EXPECT_EQ(kExpectedFileData, cache_file_data); | 
 |  | 
 |   // Verify that the cache state was changed as expected. | 
 |   VerifyCacheStateAfterOpenFile(DRIVE_FILE_OK, | 
 |                                 file_resource_id, | 
 |                                 file_md5, | 
 |                                 opened_file_path); | 
 |  | 
 |   // Close kFileInRoot ("drive/File 1.txt"). | 
 |   file_system_->CloseFile(kFileInRoot, close_file_callback); | 
 |   message_loop_.Run(); | 
 |  | 
 |   // Verify that the file was properly closed. | 
 |   EXPECT_EQ(DRIVE_FILE_OK, callback_helper_->last_error_); | 
 |  | 
 |   // Verify that the cache state was changed as expected. | 
 |   VerifyCacheStateAfterCloseFile(DRIVE_FILE_OK, | 
 |                                  file_resource_id, | 
 |                                  file_md5); | 
 |  | 
 |   // Try to close the same file twice. | 
 |   file_system_->CloseFile(kFileInRoot, close_file_callback); | 
 |   message_loop_.Run(); | 
 |  | 
 |   // It must fail. | 
 |   EXPECT_EQ(DRIVE_FILE_ERROR_NOT_FOUND, callback_helper_->last_error_); | 
 | } | 
 |  | 
 | }   // namespace gdata |