diff options
Diffstat (limited to 'components/drive/file_system_unittest.cc')
-rw-r--r-- | components/drive/file_system_unittest.cc | 1064 |
1 files changed, 1064 insertions, 0 deletions
diff --git a/components/drive/file_system_unittest.cc b/components/drive/file_system_unittest.cc new file mode 100644 index 0000000..a502a2a --- /dev/null +++ b/components/drive/file_system_unittest.cc @@ -0,0 +1,1064 @@ +// 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 "components/drive/file_system.h" + +#include <string> +#include <vector> + +#include "base/bind.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/memory/scoped_ptr.h" +#include "base/prefs/testing_pref_service.h" +#include "base/run_loop.h" +#include "base/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.h" +#include "components/drive/change_list_loader.h" +#include "components/drive/drive.pb.h" +#include "components/drive/drive_api_util.h" +#include "components/drive/drive_test_util.h" +#include "components/drive/event_logger.h" +#include "components/drive/fake_free_disk_space_getter.h" +#include "components/drive/file_change.h" +#include "components/drive/file_system_core_util.h" +#include "components/drive/file_system_observer.h" +#include "components/drive/job_scheduler.h" +#include "components/drive/service/fake_drive_service.h" +#include "components/drive/service/test_util.h" +#include "components/drive/sync_client.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "google_apis/drive/drive_api_parser.h" +#include "google_apis/drive/test_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace drive { +namespace { + +// Counts the number of invocation, and if it increased up to |expected_counter| +// quits the current message loop by calling |quit|. +void AsyncInitializationCallback( + int* counter, int expected_counter, const base::Closure& quit, + FileError error, scoped_ptr<ResourceEntry> entry) { + if (error != FILE_ERROR_OK || !entry) { + // If we hit an error case, quit the message loop immediately. + // Then the expectation in the test case can find it because the actual + // value of |counter| is different from the expected one. + quit.Run(); + return; + } + + (*counter)++; + if (*counter >= expected_counter) + quit.Run(); +} + +bool CompareHashAndFilePath(const HashAndFilePath& a, + const HashAndFilePath& b) { + const int result = a.hash.compare(b.hash); + if (result < 0) + return true; + if (result > 0) + return false; + return a.path.AsUTF8Unsafe().compare(b.path.AsUTF8Unsafe()) < 0; +} + +// This class is used to record directory changes and examine them later. +class MockDirectoryChangeObserver : public FileSystemObserver { + public: + MockDirectoryChangeObserver() {} + ~MockDirectoryChangeObserver() override {} + + // FileSystemObserver overrides. + void OnDirectoryChanged(const base::FilePath& directory_path) override { + changed_directories_.push_back(directory_path); + } + + void OnFileChanged(const FileChange& new_file_change) override { + changed_files_.Apply(new_file_change); + } + + const std::vector<base::FilePath>& changed_directories() const { + return changed_directories_; + } + + const FileChange& changed_files() const { return changed_files_; } + + private: + std::vector<base::FilePath> changed_directories_; + FileChange changed_files_; + DISALLOW_COPY_AND_ASSIGN(MockDirectoryChangeObserver); +}; + +} // namespace + +class FileSystemTest : public testing::Test { + protected: + void SetUp() override { + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + pref_service_.reset(new TestingPrefServiceSimple); + test_util::RegisterDrivePrefs(pref_service_->registry()); + + logger_.reset(new EventLogger); + fake_drive_service_.reset(new FakeDriveService); + test_util::SetUpTestEntries(fake_drive_service_.get()); + + fake_free_disk_space_getter_.reset(new FakeFreeDiskSpaceGetter); + + scheduler_.reset(new JobScheduler( + pref_service_.get(), + logger_.get(), + fake_drive_service_.get(), + base::ThreadTaskRunnerHandle::Get().get())); + + mock_directory_observer_.reset(new MockDirectoryChangeObserver); + + SetUpResourceMetadataAndFileSystem(); + } + + void SetUpResourceMetadataAndFileSystem() { + const base::FilePath metadata_dir = temp_dir_.path().AppendASCII("meta"); + ASSERT_TRUE(base::CreateDirectory(metadata_dir)); + metadata_storage_.reset(new internal::ResourceMetadataStorage( + metadata_dir, base::ThreadTaskRunnerHandle::Get().get())); + ASSERT_TRUE(metadata_storage_->Initialize()); + + const base::FilePath cache_dir = temp_dir_.path().AppendASCII("files"); + ASSERT_TRUE(base::CreateDirectory(cache_dir)); + cache_.reset(new internal::FileCache( + metadata_storage_.get(), + cache_dir, + base::ThreadTaskRunnerHandle::Get().get(), + fake_free_disk_space_getter_.get())); + ASSERT_TRUE(cache_->Initialize()); + + resource_metadata_.reset(new internal::ResourceMetadata( + metadata_storage_.get(), cache_.get(), + base::ThreadTaskRunnerHandle::Get())); + ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->Initialize()); + + const base::FilePath temp_file_dir = temp_dir_.path().AppendASCII("tmp"); + ASSERT_TRUE(base::CreateDirectory(temp_file_dir)); + file_task_runner_ = content::BrowserThread::GetMessageLoopProxyForThread( + content::BrowserThread::FILE); + file_system_.reset(new FileSystem( + pref_service_.get(), logger_.get(), cache_.get(), scheduler_.get(), + resource_metadata_.get(), base::ThreadTaskRunnerHandle::Get().get(), + file_task_runner_.get(), temp_file_dir)); + file_system_->AddObserver(mock_directory_observer_.get()); + + // Disable delaying so that the sync starts immediately. + file_system_->sync_client_for_testing()->set_delay_for_testing( + base::TimeDelta::FromSeconds(0)); + } + + // Loads the full resource list via FakeDriveService. + bool LoadFullResourceList() { + FileError error = FILE_ERROR_FAILED; + file_system_->change_list_loader_for_testing()->LoadIfNeeded( + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + return error == FILE_ERROR_OK; + } + + // Gets resource entry by path synchronously. + scoped_ptr<ResourceEntry> GetResourceEntrySync( + const base::FilePath& file_path) { + FileError error = FILE_ERROR_FAILED; + scoped_ptr<ResourceEntry> entry; + file_system_->GetResourceEntry( + file_path, + google_apis::test_util::CreateCopyResultCallback(&error, &entry)); + content::RunAllBlockingPoolTasksUntilIdle(); + + return entry.Pass(); + } + + // Gets directory info by path synchronously. + scoped_ptr<ResourceEntryVector> ReadDirectorySync( + const base::FilePath& file_path) { + FileError error = FILE_ERROR_FAILED; + scoped_ptr<ResourceEntryVector> entries(new ResourceEntryVector); + file_system_->ReadDirectory( + file_path, + base::Bind(&AccumulateReadDirectoryResult, entries.get()), + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + if (error != FILE_ERROR_OK) + entries.reset(); + return entries.Pass(); + } + + // Used to implement ReadDirectorySync(). + static void AccumulateReadDirectoryResult( + ResourceEntryVector* out_entries, + scoped_ptr<ResourceEntryVector> entries) { + ASSERT_TRUE(entries); + out_entries->insert(out_entries->end(), entries->begin(), entries->end()); + } + + // Returns true if an entry exists at |file_path|. + bool EntryExists(const base::FilePath& file_path) { + return GetResourceEntrySync(file_path); + } + + // Flag for specifying the timestamp of the test filesystem cache. + enum SetUpTestFileSystemParam { + USE_OLD_TIMESTAMP, + USE_SERVER_TIMESTAMP, + }; + + // Sets up a filesystem with directories: drive/root, drive/root/Dir1, + // drive/root/Dir1/SubDir2 and files drive/root/File1, drive/root/Dir1/File2, + // drive/root/Dir1/SubDir2/File3. If |use_up_to_date_timestamp| is true, sets + // the changestamp to that of FakeDriveService, indicating the cache is + // holding the latest file system info. + void SetUpTestFileSystem(SetUpTestFileSystemParam param) { + // Destroy the existing resource metadata to close DB. + resource_metadata_.reset(); + + const base::FilePath metadata_dir = temp_dir_.path().AppendASCII("meta"); + ASSERT_TRUE(base::CreateDirectory(metadata_dir)); + scoped_ptr<internal::ResourceMetadataStorage, + test_util::DestroyHelperForTests> metadata_storage( + new internal::ResourceMetadataStorage( + metadata_dir, base::ThreadTaskRunnerHandle::Get().get())); + + const base::FilePath cache_dir = temp_dir_.path().AppendASCII("files"); + scoped_ptr<internal::FileCache, test_util::DestroyHelperForTests> cache( + new internal::FileCache(metadata_storage.get(), + cache_dir, + base::ThreadTaskRunnerHandle::Get().get(), + fake_free_disk_space_getter_.get())); + + scoped_ptr<internal::ResourceMetadata, test_util::DestroyHelperForTests> + resource_metadata(new internal::ResourceMetadata( + metadata_storage_.get(), cache.get(), + base::ThreadTaskRunnerHandle::Get())); + + ASSERT_EQ(FILE_ERROR_OK, resource_metadata->Initialize()); + + const int64 changestamp = param == USE_SERVER_TIMESTAMP ? + fake_drive_service_->about_resource().largest_change_id() : 1; + ASSERT_EQ(FILE_ERROR_OK, + resource_metadata->SetLargestChangestamp(changestamp)); + + // drive/root + ResourceEntry root; + ASSERT_EQ(FILE_ERROR_OK, resource_metadata->GetResourceEntryByPath( + util::GetDriveMyDriveRootPath(), &root)); + root.set_resource_id(fake_drive_service_->GetRootResourceId()); + ASSERT_EQ(FILE_ERROR_OK, resource_metadata->RefreshEntry(root)); + + std::string local_id; + + // drive/root/File1 + ResourceEntry file1; + file1.set_title("File1"); + file1.set_resource_id("resource_id:File1"); + file1.set_parent_local_id(root.local_id()); + file1.mutable_file_specific_info()->set_md5("md5#1"); + file1.mutable_file_info()->set_is_directory(false); + file1.mutable_file_info()->set_size(1048576); + ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(file1, &local_id)); + + // drive/root/Dir1 + ResourceEntry dir1; + dir1.set_title("Dir1"); + dir1.set_resource_id("resource_id:Dir1"); + dir1.set_parent_local_id(root.local_id()); + dir1.mutable_file_info()->set_is_directory(true); + ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(dir1, &local_id)); + const std::string dir1_local_id = local_id; + + // drive/root/Dir1/File2 + ResourceEntry file2; + file2.set_title("File2"); + file2.set_resource_id("resource_id:File2"); + file2.set_parent_local_id(dir1_local_id); + file2.mutable_file_specific_info()->set_md5("md5#2"); + file2.mutable_file_info()->set_is_directory(false); + file2.mutable_file_info()->set_size(555); + ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(file2, &local_id)); + + // drive/root/Dir1/SubDir2 + ResourceEntry dir2; + dir2.set_title("SubDir2"); + dir2.set_resource_id("resource_id:SubDir2"); + dir2.set_parent_local_id(dir1_local_id); + dir2.mutable_file_info()->set_is_directory(true); + ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(dir2, &local_id)); + const std::string dir2_local_id = local_id; + + // drive/root/Dir1/SubDir2/File3 + ResourceEntry file3; + file3.set_title("File3"); + file3.set_resource_id("resource_id:File3"); + file3.set_parent_local_id(dir2_local_id); + file3.mutable_file_specific_info()->set_md5("md5#2"); + file3.mutable_file_info()->set_is_directory(false); + file3.mutable_file_info()->set_size(12345); + ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(file3, &local_id)); + + // Recreate resource metadata. + SetUpResourceMetadataAndFileSystem(); + } + + content::TestBrowserThreadBundle thread_bundle_; + base::ScopedTempDir temp_dir_; + // We don't use TestingProfile::GetPrefs() in favor of having less + // dependencies to Profile in general. + scoped_ptr<TestingPrefServiceSimple> pref_service_; + + scoped_ptr<EventLogger> logger_; + scoped_ptr<FakeDriveService> fake_drive_service_; + scoped_ptr<FakeFreeDiskSpaceGetter> fake_free_disk_space_getter_; + scoped_ptr<JobScheduler> scheduler_; + scoped_ptr<MockDirectoryChangeObserver> mock_directory_observer_; + + scoped_ptr<internal::ResourceMetadataStorage, + test_util::DestroyHelperForTests> metadata_storage_; + scoped_ptr<internal::FileCache, test_util::DestroyHelperForTests> cache_; + scoped_ptr<internal::ResourceMetadata, test_util::DestroyHelperForTests> + resource_metadata_; + scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_; + scoped_ptr<FileSystem> file_system_; +}; + +TEST_F(FileSystemTest, SearchByHashes) { + ASSERT_NO_FATAL_FAILURE(SetUpTestFileSystem(USE_SERVER_TIMESTAMP)); + + std::set<std::string> hashes; + FileError error; + std::vector<HashAndFilePath> results; + + hashes.insert("md5#1"); + file_system_->SearchByHashes( + hashes, + google_apis::test_util::CreateCopyResultCallback(&error, &results)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + ASSERT_EQ(1u, results.size()); + EXPECT_EQ(FILE_PATH_LITERAL("drive/root/File1"), results[0].path.value()); + + hashes.clear(); + hashes.insert("md5#2"); + file_system_->SearchByHashes( + hashes, + google_apis::test_util::CreateCopyResultCallback(&error, &results)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + ASSERT_EQ(2u, results.size()); + std::sort(results.begin(), results.end(), &CompareHashAndFilePath); + EXPECT_EQ(FILE_PATH_LITERAL("drive/root/Dir1/File2"), + results[0].path.value()); + EXPECT_EQ(FILE_PATH_LITERAL("drive/root/Dir1/SubDir2/File3"), + results[1].path.value()); + + hashes.clear(); + hashes.insert("md5#1"); + hashes.insert("md5#2"); + file_system_->SearchByHashes( + hashes, + google_apis::test_util::CreateCopyResultCallback(&error, &results)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + ASSERT_EQ(3u, results.size()); + std::sort(results.begin(), results.end(), &CompareHashAndFilePath); + EXPECT_EQ(FILE_PATH_LITERAL("drive/root/File1"), results[0].path.value()); + EXPECT_EQ(FILE_PATH_LITERAL("drive/root/Dir1/File2"), + results[1].path.value()); + EXPECT_EQ(FILE_PATH_LITERAL("drive/root/Dir1/SubDir2/File3"), + results[2].path.value()); +} + +TEST_F(FileSystemTest, Copy) { + base::FilePath src_file_path(FILE_PATH_LITERAL("drive/root/File 1.txt")); + base::FilePath dest_file_path(FILE_PATH_LITERAL("drive/root/Copied.txt")); + EXPECT_TRUE(GetResourceEntrySync(src_file_path)); + EXPECT_FALSE(GetResourceEntrySync(dest_file_path)); + + FileError error = FILE_ERROR_FAILED; + file_system_->Copy(src_file_path, + dest_file_path, + false, // preserve_last_modified, + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // Entry is added on the server. + scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(dest_file_path); + ASSERT_TRUE(entry); + + google_apis::DriveApiErrorCode status = google_apis::DRIVE_OTHER_ERROR; + scoped_ptr<google_apis::FileResource> server_entry; + fake_drive_service_->GetFileResource( + entry->resource_id(), + google_apis::test_util::CreateCopyResultCallback(&status, &server_entry)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(google_apis::HTTP_SUCCESS, status); + ASSERT_TRUE(server_entry); + EXPECT_EQ(entry->title(), server_entry->title()); + EXPECT_FALSE(server_entry->IsDirectory()); +} + +TEST_F(FileSystemTest, Move) { + base::FilePath src_file_path(FILE_PATH_LITERAL("drive/root/File 1.txt")); + base::FilePath dest_file_path( + FILE_PATH_LITERAL("drive/root/Directory 1/Moved.txt")); + EXPECT_TRUE(GetResourceEntrySync(src_file_path)); + EXPECT_FALSE(GetResourceEntrySync(dest_file_path)); + scoped_ptr<ResourceEntry> parent = + GetResourceEntrySync(dest_file_path.DirName()); + ASSERT_TRUE(parent); + + FileError error = FILE_ERROR_FAILED; + file_system_->Move(src_file_path, + dest_file_path, + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // Entry is moved on the server. + scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(dest_file_path); + ASSERT_TRUE(entry); + + google_apis::DriveApiErrorCode status = google_apis::DRIVE_OTHER_ERROR; + scoped_ptr<google_apis::FileResource> server_entry; + fake_drive_service_->GetFileResource( + entry->resource_id(), + google_apis::test_util::CreateCopyResultCallback(&status, &server_entry)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(google_apis::HTTP_SUCCESS, status); + ASSERT_TRUE(server_entry); + EXPECT_EQ(entry->title(), server_entry->title()); + + ASSERT_FALSE(server_entry->parents().empty()); + EXPECT_EQ(parent->resource_id(), server_entry->parents()[0].file_id()); +} + +TEST_F(FileSystemTest, Remove) { + base::FilePath file_path(FILE_PATH_LITERAL("drive/root/File 1.txt")); + scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(file_path); + ASSERT_TRUE(entry); + + FileError error = FILE_ERROR_FAILED; + file_system_->Remove( + file_path, + false, // is_resursive + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // Entry is removed on the server. + google_apis::DriveApiErrorCode status = google_apis::DRIVE_OTHER_ERROR; + scoped_ptr<google_apis::FileResource> server_entry; + fake_drive_service_->GetFileResource( + entry->resource_id(), + google_apis::test_util::CreateCopyResultCallback(&status, &server_entry)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(google_apis::HTTP_SUCCESS, status); + ASSERT_TRUE(server_entry); + EXPECT_TRUE(server_entry->labels().is_trashed()); +} + +TEST_F(FileSystemTest, CreateDirectory) { + base::FilePath directory_path(FILE_PATH_LITERAL("drive/root/New Directory")); + EXPECT_FALSE(GetResourceEntrySync(directory_path)); + + FileError error = FILE_ERROR_FAILED; + file_system_->CreateDirectory( + directory_path, + true, // is_exclusive + false, // is_recursive + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // Directory is created on the server. + scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(directory_path); + ASSERT_TRUE(entry); + + google_apis::DriveApiErrorCode status = google_apis::DRIVE_OTHER_ERROR; + scoped_ptr<google_apis::FileResource> server_entry; + fake_drive_service_->GetFileResource( + entry->resource_id(), + google_apis::test_util::CreateCopyResultCallback(&status, &server_entry)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(google_apis::HTTP_SUCCESS, status); + ASSERT_TRUE(server_entry); + EXPECT_EQ(entry->title(), server_entry->title()); + EXPECT_TRUE(server_entry->IsDirectory()); +} + +TEST_F(FileSystemTest, CreateFile) { + base::FilePath file_path(FILE_PATH_LITERAL("drive/root/New File.txt")); + EXPECT_FALSE(GetResourceEntrySync(file_path)); + + FileError error = FILE_ERROR_FAILED; + file_system_->CreateFile( + file_path, + true, // is_exclusive + "text/plain", + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // File is created on the server. + scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(file_path); + ASSERT_TRUE(entry); + + google_apis::DriveApiErrorCode status = google_apis::DRIVE_OTHER_ERROR; + scoped_ptr<google_apis::FileResource> server_entry; + fake_drive_service_->GetFileResource( + entry->resource_id(), + google_apis::test_util::CreateCopyResultCallback(&status, &server_entry)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(google_apis::HTTP_SUCCESS, status); + ASSERT_TRUE(server_entry); + EXPECT_EQ(entry->title(), server_entry->title()); + EXPECT_FALSE(server_entry->IsDirectory()); +} + +TEST_F(FileSystemTest, TouchFile) { + base::FilePath file_path(FILE_PATH_LITERAL("drive/root/File 1.txt")); + scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(file_path); + ASSERT_TRUE(entry); + + base::Time last_accessed = + base::Time::FromInternalValue(entry->file_info().last_accessed()) + + base::TimeDelta::FromSeconds(1); + base::Time last_modified = + base::Time::FromInternalValue(entry->file_info().last_modified()) + + base::TimeDelta::FromSeconds(1); + + FileError error = FILE_ERROR_FAILED; + file_system_->TouchFile( + file_path, + last_accessed, + last_modified, + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // File is touched on the server. + google_apis::DriveApiErrorCode status = google_apis::DRIVE_OTHER_ERROR; + scoped_ptr<google_apis::FileResource> server_entry; + fake_drive_service_->GetFileResource( + entry->resource_id(), + google_apis::test_util::CreateCopyResultCallback(&status, &server_entry)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(google_apis::HTTP_SUCCESS, status); + ASSERT_TRUE(server_entry); + EXPECT_EQ(last_accessed, server_entry->last_viewed_by_me_date()); + EXPECT_EQ(last_modified, server_entry->modified_date()); +} + +TEST_F(FileSystemTest, TruncateFile) { + base::FilePath file_path(FILE_PATH_LITERAL("drive/root/File 1.txt")); + scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(file_path); + ASSERT_TRUE(entry); + + const int64 kLength = entry->file_info().size() + 100; + + FileError error = FILE_ERROR_FAILED; + file_system_->TruncateFile( + file_path, + kLength, + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // File is touched on the server. + google_apis::DriveApiErrorCode status = google_apis::DRIVE_OTHER_ERROR; + scoped_ptr<google_apis::FileResource> server_entry; + fake_drive_service_->GetFileResource( + entry->resource_id(), + google_apis::test_util::CreateCopyResultCallback(&status, &server_entry)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(google_apis::HTTP_SUCCESS, status); + ASSERT_TRUE(server_entry); + EXPECT_EQ(kLength, server_entry->file_size()); +} + +TEST_F(FileSystemTest, DuplicatedAsyncInitialization) { + base::RunLoop loop; + + int counter = 0; + const GetResourceEntryCallback& callback = base::Bind( + &AsyncInitializationCallback, &counter, 2, loop.QuitClosure()); + + file_system_->GetResourceEntry( + base::FilePath(FILE_PATH_LITERAL("drive/root")), callback); + file_system_->GetResourceEntry( + base::FilePath(FILE_PATH_LITERAL("drive/root")), callback); + loop.Run(); // Wait to get our result + EXPECT_EQ(2, counter); + + EXPECT_EQ(1, fake_drive_service_->file_list_load_count()); +} + +TEST_F(FileSystemTest, GetGrandRootEntry) { + const base::FilePath kFilePath(FILE_PATH_LITERAL("drive")); + scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(kFilePath); + ASSERT_TRUE(entry); + EXPECT_EQ(util::kDriveGrandRootLocalId, entry->local_id()); +} + +TEST_F(FileSystemTest, GetOtherDirEntry) { + const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/other")); + scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(kFilePath); + ASSERT_TRUE(entry); + EXPECT_EQ(util::kDriveOtherDirLocalId, entry->local_id()); +} + +TEST_F(FileSystemTest, GetMyDriveRoot) { + const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root")); + scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(kFilePath); + ASSERT_TRUE(entry); + EXPECT_EQ(fake_drive_service_->GetRootResourceId(), entry->resource_id()); + + // After "fast fetch" is done, full resource list is fetched. + EXPECT_EQ(1, fake_drive_service_->file_list_load_count()); +} + +TEST_F(FileSystemTest, GetExistingFile) { + // Simulate the situation that full feed fetching takes very long time, + // to test the recursive "fast fetch" feature is properly working. + fake_drive_service_->set_never_return_all_file_list(true); + + const base::FilePath kFilePath( + FILE_PATH_LITERAL("drive/root/Directory 1/SubDirectory File 1.txt")); + scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(kFilePath); + ASSERT_TRUE(entry); + EXPECT_EQ("subdirectory_file_1_id", entry->resource_id()); + + EXPECT_EQ(1, fake_drive_service_->about_resource_load_count()); + EXPECT_EQ(2, fake_drive_service_->directory_load_count()); + EXPECT_EQ(1, fake_drive_service_->blocked_file_list_load_count()); +} + +TEST_F(FileSystemTest, GetExistingDocument) { + const base::FilePath kFilePath( + FILE_PATH_LITERAL("drive/root/Document 1 excludeDir-test.gdoc")); + scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(kFilePath); + ASSERT_TRUE(entry); + EXPECT_EQ("5_document_resource_id", entry->resource_id()); +} + +TEST_F(FileSystemTest, GetNonExistingFile) { + const base::FilePath kFilePath( + FILE_PATH_LITERAL("drive/root/nonexisting.file")); + scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(kFilePath); + EXPECT_FALSE(entry); +} + +TEST_F(FileSystemTest, GetInSubSubdir) { + const base::FilePath kFilePath( + FILE_PATH_LITERAL("drive/root/Directory 1/Sub Directory Folder/" + "Sub Sub Directory Folder")); + scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(kFilePath); + ASSERT_TRUE(entry); + ASSERT_EQ("sub_sub_directory_folder_id", entry->resource_id()); +} + +TEST_F(FileSystemTest, GetOrphanFile) { + ASSERT_TRUE(LoadFullResourceList()); + + // Entry without parents are placed under "drive/other". + const base::FilePath kFilePath( + FILE_PATH_LITERAL("drive/other/Orphan File 1.txt")); + scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(kFilePath); + ASSERT_TRUE(entry); + EXPECT_EQ("1_orphanfile_resource_id", entry->resource_id()); +} + +TEST_F(FileSystemTest, ReadDirectory_Root) { + // ReadDirectory() should kick off the resource list loading. + scoped_ptr<ResourceEntryVector> entries( + ReadDirectorySync(base::FilePath::FromUTF8Unsafe("drive"))); + // The root directory should be read correctly. + ASSERT_TRUE(entries); + ASSERT_EQ(3U, entries->size()); + + // The found three directories should be /drive/root, /drive/other and + // /drive/trash. + std::set<base::FilePath> found; + for (size_t i = 0; i < entries->size(); ++i) + found.insert(base::FilePath::FromUTF8Unsafe((*entries)[i].title())); + EXPECT_EQ(3U, found.size()); + EXPECT_EQ(1U, found.count(base::FilePath::FromUTF8Unsafe( + util::kDriveMyDriveRootDirName))); + EXPECT_EQ(1U, found.count( + base::FilePath::FromUTF8Unsafe(util::kDriveOtherDirName))); + EXPECT_EQ(1U, found.count( + base::FilePath::FromUTF8Unsafe(util::kDriveTrashDirName))); +} + +TEST_F(FileSystemTest, ReadDirectory_NonRootDirectory) { + // ReadDirectory() should kick off the resource list loading. + scoped_ptr<ResourceEntryVector> entries( + ReadDirectorySync( + base::FilePath::FromUTF8Unsafe("drive/root/Directory 1"))); + // The non root directory should also be read correctly. + // There was a bug (crbug.com/181487), which broke this behavior. + // Make sure this is fixed. + ASSERT_TRUE(entries); + EXPECT_EQ(3U, entries->size()); +} + +TEST_F(FileSystemTest, LoadFileSystemFromUpToDateCache) { + ASSERT_NO_FATAL_FAILURE(SetUpTestFileSystem(USE_SERVER_TIMESTAMP)); + + // Kicks loading of cached file system and query for server update. + EXPECT_TRUE(ReadDirectorySync(util::GetDriveMyDriveRootPath())); + + // SetUpTestFileSystem and FakeDriveService have the same + // changestamp (i.e. the local metadata is up-to-date), so no request for + // new resource list (i.e., call to GetResourceList) should happen. + EXPECT_EQ(0, fake_drive_service_->file_list_load_count()); + + // Since the file system has verified that it holds the latest snapshot, + // it should change its state to "loaded", which admits periodic refresh. + // To test it, call CheckForUpdates and verify it does try to check updates. + const int about_resource_load_count_before = + fake_drive_service_->about_resource_load_count(); + file_system_->CheckForUpdates(); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_LT(about_resource_load_count_before, + fake_drive_service_->about_resource_load_count()); +} + +TEST_F(FileSystemTest, LoadFileSystemFromCacheWhileOffline) { + ASSERT_NO_FATAL_FAILURE(SetUpTestFileSystem(USE_OLD_TIMESTAMP)); + + // Make GetResourceList fail for simulating offline situation. This will + // leave the file system "loaded from cache, but not synced with server" + // state. + fake_drive_service_->set_offline(true); + + // Load the root. + EXPECT_TRUE(ReadDirectorySync(util::GetDriveGrandRootPath())); + // Loading of about resource should not happen as it's offline. + EXPECT_EQ(0, fake_drive_service_->about_resource_load_count()); + + // Load "My Drive". + EXPECT_TRUE(ReadDirectorySync(util::GetDriveMyDriveRootPath())); + EXPECT_EQ(0, fake_drive_service_->about_resource_load_count()); + + // Tests that cached data can be loaded even if the server is not reachable. + EXPECT_TRUE(EntryExists(base::FilePath( + FILE_PATH_LITERAL("drive/root/File1")))); + EXPECT_TRUE(EntryExists(base::FilePath( + FILE_PATH_LITERAL("drive/root/Dir1")))); + EXPECT_TRUE(EntryExists(base::FilePath( + FILE_PATH_LITERAL("drive/root/Dir1/File2")))); + EXPECT_TRUE(EntryExists(base::FilePath( + FILE_PATH_LITERAL("drive/root/Dir1/SubDir2")))); + EXPECT_TRUE(EntryExists(base::FilePath( + FILE_PATH_LITERAL("drive/root/Dir1/SubDir2/File3")))); + + // Since the file system has at least succeeded to load cached snapshot, + // the file system should be able to start periodic refresh. + // To test it, call CheckForUpdates and verify it does try to check + // updates, which will cause directory changes. + fake_drive_service_->set_offline(false); + + file_system_->CheckForUpdates(); + + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(1, fake_drive_service_->about_resource_load_count()); + EXPECT_EQ(1, fake_drive_service_->change_list_load_count()); + + ASSERT_LE(0u, mock_directory_observer_->changed_directories().size()); + ASSERT_LE(1u, mock_directory_observer_->changed_files().size()); +} + +TEST_F(FileSystemTest, ReadDirectoryWhileRefreshing) { + // Use old timestamp so the fast fetch will be performed. + ASSERT_NO_FATAL_FAILURE(SetUpTestFileSystem(USE_OLD_TIMESTAMP)); + + // The list of resources in "drive/root/Dir1" should be fetched. + EXPECT_TRUE(ReadDirectorySync(base::FilePath( + FILE_PATH_LITERAL("drive/root/Dir1")))); + EXPECT_EQ(1, fake_drive_service_->directory_load_count()); + + ASSERT_LE(1u, mock_directory_observer_->changed_directories().size()); +} + +TEST_F(FileSystemTest, GetResourceEntryNonExistentWhileRefreshing) { + // Use old timestamp so the fast fetch will be performed. + ASSERT_NO_FATAL_FAILURE(SetUpTestFileSystem(USE_OLD_TIMESTAMP)); + + // If an entry is not found, parent directory's resource list is fetched. + EXPECT_FALSE(GetResourceEntrySync(base::FilePath( + FILE_PATH_LITERAL("drive/root/Dir1/NonExistentFile")))); + EXPECT_EQ(1, fake_drive_service_->directory_load_count()); + + ASSERT_LE(1u, mock_directory_observer_->changed_directories().size()); +} + +TEST_F(FileSystemTest, CreateDirectoryByImplicitLoad) { + // Intentionally *not* calling LoadFullResourceList(), for testing that + // CreateDirectory ensures the resource list is loaded before it runs. + + base::FilePath existing_directory( + FILE_PATH_LITERAL("drive/root/Directory 1")); + FileError error = FILE_ERROR_FAILED; + file_system_->CreateDirectory( + existing_directory, + true, // is_exclusive + false, // is_recursive + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + + // It should fail because is_exclusive is set to true. + EXPECT_EQ(FILE_ERROR_EXISTS, error); +} + +TEST_F(FileSystemTest, CreateDirectoryRecursively) { + // Intentionally *not* calling LoadFullResourceList(), for testing that + // CreateDirectory ensures the resource list is loaded before it runs. + + base::FilePath new_directory( + FILE_PATH_LITERAL("drive/root/Directory 1/a/b/c/d")); + FileError error = FILE_ERROR_FAILED; + file_system_->CreateDirectory( + new_directory, + true, // is_exclusive + true, // is_recursive + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + + EXPECT_EQ(FILE_ERROR_OK, error); + + scoped_ptr<ResourceEntry> entry(GetResourceEntrySync(new_directory)); + ASSERT_TRUE(entry); + EXPECT_TRUE(entry->file_info().is_directory()); +} + +TEST_F(FileSystemTest, ReadDirectoryAfterUpdateWhileLoading) { + // Simulate the situation that full feed fetching takes very long time, + // to test the recursive "fast fetch" feature is properly working. + fake_drive_service_->set_never_return_all_file_list(true); + + // On the fake server, create the test directory. + scoped_ptr<google_apis::FileResource> parent; + { + google_apis::DriveApiErrorCode error = google_apis::DRIVE_OTHER_ERROR; + fake_drive_service_->AddNewDirectory( + fake_drive_service_->GetRootResourceId(), "UpdateWhileLoadingTestDir", + AddNewDirectoryOptions(), + google_apis::test_util::CreateCopyResultCallback(&error, &parent)); + base::RunLoop().RunUntilIdle(); + ASSERT_EQ(google_apis::HTTP_CREATED, error); + } + + // Fetch the directory. Currently it is empty. + scoped_ptr<ResourceEntryVector> before = ReadDirectorySync(base::FilePath( + FILE_PATH_LITERAL("drive/root/UpdateWhileLoadingTestDir"))); + ASSERT_TRUE(before); + EXPECT_EQ(0u, before->size()); + + // Create a file in the test directory. + scoped_ptr<google_apis::FileResource> entry; + { + google_apis::DriveApiErrorCode error = google_apis::DRIVE_OTHER_ERROR; + fake_drive_service_->AddNewFile( + "text/plain", + "(dummy data)", + parent->file_id(), + "TestFile", + false, // shared_with_me + google_apis::test_util::CreateCopyResultCallback(&error, &entry)); + base::RunLoop().RunUntilIdle(); + ASSERT_EQ(google_apis::HTTP_CREATED, error); + } + + // Notify the update to the file system. + file_system_->CheckForUpdates(); + + // Read the directory once again. Although the full feed fetching is not yet + // finished, the "fast fetch" of the directory works and the refreshed content + // is returned. + scoped_ptr<ResourceEntryVector> after = ReadDirectorySync(base::FilePath( + FILE_PATH_LITERAL("drive/root/UpdateWhileLoadingTestDir"))); + ASSERT_TRUE(after); + EXPECT_EQ(1u, after->size()); +} + +TEST_F(FileSystemTest, PinAndUnpin) { + ASSERT_TRUE(LoadFullResourceList()); + + base::FilePath file_path(FILE_PATH_LITERAL("drive/root/File 1.txt")); + + // Get the file info. + scoped_ptr<ResourceEntry> entry(GetResourceEntrySync(file_path)); + ASSERT_TRUE(entry); + + // Pin the file. + FileError error = FILE_ERROR_FAILED; + file_system_->Pin(file_path, + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + entry = GetResourceEntrySync(file_path); + ASSERT_TRUE(entry); + EXPECT_TRUE(entry->file_specific_info().cache_state().is_pinned()); + EXPECT_TRUE(entry->file_specific_info().cache_state().is_present()); + + // Unpin the file. + error = FILE_ERROR_FAILED; + file_system_->Unpin(file_path, + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + entry = GetResourceEntrySync(file_path); + ASSERT_TRUE(entry); + EXPECT_FALSE(entry->file_specific_info().cache_state().is_pinned()); + + // Pinned file gets synced and it results in entry state changes. + ASSERT_EQ(0u, mock_directory_observer_->changed_directories().size()); + ASSERT_EQ(1u, mock_directory_observer_->changed_files().size()); + EXPECT_EQ(1u, + mock_directory_observer_->changed_files().CountDirectory( + base::FilePath(FILE_PATH_LITERAL("drive/root")))); +} + +TEST_F(FileSystemTest, PinAndUnpin_NotSynced) { + ASSERT_TRUE(LoadFullResourceList()); + + base::FilePath file_path(FILE_PATH_LITERAL("drive/root/File 1.txt")); + + // Get the file info. + scoped_ptr<ResourceEntry> entry(GetResourceEntrySync(file_path)); + ASSERT_TRUE(entry); + + // Unpin the file just after pinning. File fetch should be cancelled. + FileError error_pin = FILE_ERROR_FAILED; + file_system_->Pin( + file_path, + google_apis::test_util::CreateCopyResultCallback(&error_pin)); + + FileError error_unpin = FILE_ERROR_FAILED; + file_system_->Unpin( + file_path, + google_apis::test_util::CreateCopyResultCallback(&error_unpin)); + + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error_pin); + EXPECT_EQ(FILE_ERROR_OK, error_unpin); + + // No cache file available because the sync was cancelled by Unpin(). + entry = GetResourceEntrySync(file_path); + ASSERT_TRUE(entry); + EXPECT_FALSE(entry->file_specific_info().cache_state().is_present()); +} + +TEST_F(FileSystemTest, GetAvailableSpace) { + FileError error = FILE_ERROR_OK; + int64 bytes_total; + int64 bytes_used; + file_system_->GetAvailableSpace( + google_apis::test_util::CreateCopyResultCallback( + &error, &bytes_total, &bytes_used)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(6789012345LL, bytes_used); + EXPECT_EQ(9876543210LL, bytes_total); +} + +TEST_F(FileSystemTest, MarkCacheFileAsMountedAndUnmounted) { + ASSERT_TRUE(LoadFullResourceList()); + + base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt")); + + // Make the file cached. + FileError error = FILE_ERROR_FAILED; + base::FilePath file_path; + scoped_ptr<ResourceEntry> entry; + file_system_->GetFile( + file_in_root, + google_apis::test_util::CreateCopyResultCallback( + &error, &file_path, &entry)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // Test for mounting. + error = FILE_ERROR_FAILED; + file_path.clear(); + file_system_->MarkCacheFileAsMounted( + file_in_root, + google_apis::test_util::CreateCopyResultCallback(&error, &file_path)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // Cannot remove a cache entry while it's being mounted. + EXPECT_EQ(FILE_ERROR_IN_USE, cache_->Remove(entry->local_id())); + + // Test for unmounting. + error = FILE_ERROR_FAILED; + file_system_->MarkCacheFileAsUnmounted( + file_path, + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // Now able to remove the cache entry. + EXPECT_EQ(FILE_ERROR_OK, cache_->Remove(entry->local_id())); +} + +TEST_F(FileSystemTest, GetShareUrl) { + ASSERT_TRUE(LoadFullResourceList()); + + const base::FilePath kFileInRoot(FILE_PATH_LITERAL("drive/root/File 1.txt")); + const GURL kEmbedOrigin("chrome-extension://test-id"); + + // Try to fetch the URL for the sharing dialog. + FileError error = FILE_ERROR_FAILED; + GURL share_url; + file_system_->GetShareUrl( + kFileInRoot, + kEmbedOrigin, + google_apis::test_util::CreateCopyResultCallback(&error, &share_url)); + content::RunAllBlockingPoolTasksUntilIdle(); + + // Verify the share url to the sharing dialog. + EXPECT_EQ(FILE_ERROR_OK, error); + EXPECT_TRUE(share_url.is_valid()); +} + +TEST_F(FileSystemTest, FreeDiskSpaceIfNeededFor) { + ASSERT_TRUE(LoadFullResourceList()); + + base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt")); + + // Make the file cached. + FileError error = FILE_ERROR_FAILED; + base::FilePath file_path; + scoped_ptr<ResourceEntry> entry; + file_system_->GetFile(file_in_root, + google_apis::test_util::CreateCopyResultCallback( + &error, &file_path, &entry)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + ASSERT_TRUE(entry); + EXPECT_TRUE(entry->file_specific_info().cache_state().is_present()); + + bool available; + file_system_->FreeDiskSpaceIfNeededFor( + 512LL << 40, + google_apis::test_util::CreateCopyResultCallback(&available)); + content::RunAllBlockingPoolTasksUntilIdle(); + ASSERT_FALSE(available); + + entry = GetResourceEntrySync(file_in_root); + ASSERT_TRUE(entry); + EXPECT_FALSE(entry->file_specific_info().cache_state().is_present()); +} + +} // namespace drive |