| // 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 |