sync_client_unittest.cc revision 116680a4aac90f2aa7413d9095a592090648e557
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/chromeos/drive/sync_client.h" 6 7#include "base/file_util.h" 8#include "base/files/file_path.h" 9#include "base/files/scoped_temp_dir.h" 10#include "base/memory/scoped_ptr.h" 11#include "base/prefs/testing_pref_service.h" 12#include "base/run_loop.h" 13#include "base/test/test_timeouts.h" 14#include "chrome/browser/chromeos/drive/change_list_loader.h" 15#include "chrome/browser/chromeos/drive/drive.pb.h" 16#include "chrome/browser/chromeos/drive/fake_free_disk_space_getter.h" 17#include "chrome/browser/chromeos/drive/file_cache.h" 18#include "chrome/browser/chromeos/drive/file_change.h" 19#include "chrome/browser/chromeos/drive/file_system/move_operation.h" 20#include "chrome/browser/chromeos/drive/file_system/operation_observer.h" 21#include "chrome/browser/chromeos/drive/file_system/remove_operation.h" 22#include "chrome/browser/chromeos/drive/file_system_util.h" 23#include "chrome/browser/chromeos/drive/job_scheduler.h" 24#include "chrome/browser/chromeos/drive/resource_entry_conversion.h" 25#include "chrome/browser/chromeos/drive/resource_metadata.h" 26#include "chrome/browser/chromeos/drive/test_util.h" 27#include "chrome/browser/drive/event_logger.h" 28#include "chrome/browser/drive/fake_drive_service.h" 29#include "content/public/test/test_browser_thread_bundle.h" 30#include "google_apis/drive/drive_api_parser.h" 31#include "google_apis/drive/test_util.h" 32#include "testing/gtest/include/gtest/gtest.h" 33 34namespace drive { 35namespace internal { 36 37namespace { 38 39// The content of files initially stored in the cache. 40const char kLocalContent[] = "Hello!"; 41 42// The content of files stored in the service. 43const char kRemoteContent[] = "World!"; 44 45// SyncClientTestDriveService will return GDATA_CANCELLED when a request is 46// made with the specified resource ID. 47class SyncClientTestDriveService : public ::drive::FakeDriveService { 48 public: 49 SyncClientTestDriveService() : download_file_count_(0) {} 50 51 // FakeDriveService override: 52 virtual google_apis::CancelCallback DownloadFile( 53 const base::FilePath& local_cache_path, 54 const std::string& resource_id, 55 const google_apis::DownloadActionCallback& download_action_callback, 56 const google_apis::GetContentCallback& get_content_callback, 57 const google_apis::ProgressCallback& progress_callback) OVERRIDE { 58 ++download_file_count_; 59 if (resource_id == resource_id_to_be_cancelled_) { 60 base::MessageLoopProxy::current()->PostTask( 61 FROM_HERE, 62 base::Bind(download_action_callback, 63 google_apis::GDATA_CANCELLED, 64 base::FilePath())); 65 return google_apis::CancelCallback(); 66 } 67 if (resource_id == resource_id_to_be_paused_) { 68 paused_action_ = base::Bind(download_action_callback, 69 google_apis::GDATA_OTHER_ERROR, 70 base::FilePath()); 71 return google_apis::CancelCallback(); 72 } 73 return FakeDriveService::DownloadFile(local_cache_path, 74 resource_id, 75 download_action_callback, 76 get_content_callback, 77 progress_callback); 78 } 79 80 int download_file_count() const { return download_file_count_; } 81 82 void set_resource_id_to_be_cancelled(const std::string& resource_id) { 83 resource_id_to_be_cancelled_ = resource_id; 84 } 85 86 void set_resource_id_to_be_paused(const std::string& resource_id) { 87 resource_id_to_be_paused_ = resource_id; 88 } 89 90 const base::Closure& paused_action() const { return paused_action_; } 91 92 private: 93 int download_file_count_; 94 std::string resource_id_to_be_cancelled_; 95 std::string resource_id_to_be_paused_; 96 base::Closure paused_action_; 97}; 98 99class DummyOperationObserver : public file_system::OperationObserver { 100 // OperationObserver override: 101 virtual void OnFileChangedByOperation( 102 const FileChange& changed_files) OVERRIDE {} 103}; 104 105} // namespace 106 107class SyncClientTest : public testing::Test { 108 public: 109 virtual void SetUp() OVERRIDE { 110 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 111 112 pref_service_.reset(new TestingPrefServiceSimple); 113 test_util::RegisterDrivePrefs(pref_service_->registry()); 114 115 fake_network_change_notifier_.reset( 116 new test_util::FakeNetworkChangeNotifier); 117 118 logger_.reset(new EventLogger); 119 120 drive_service_.reset(new SyncClientTestDriveService); 121 122 scheduler_.reset(new JobScheduler(pref_service_.get(), 123 logger_.get(), 124 drive_service_.get(), 125 base::MessageLoopProxy::current().get())); 126 127 metadata_storage_.reset(new ResourceMetadataStorage( 128 temp_dir_.path(), base::MessageLoopProxy::current().get())); 129 ASSERT_TRUE(metadata_storage_->Initialize()); 130 131 cache_.reset(new FileCache(metadata_storage_.get(), 132 temp_dir_.path(), 133 base::MessageLoopProxy::current().get(), 134 NULL /* free_disk_space_getter */)); 135 ASSERT_TRUE(cache_->Initialize()); 136 137 metadata_.reset(new internal::ResourceMetadata( 138 metadata_storage_.get(), cache_.get(), 139 base::MessageLoopProxy::current())); 140 ASSERT_EQ(FILE_ERROR_OK, metadata_->Initialize()); 141 142 about_resource_loader_.reset(new AboutResourceLoader(scheduler_.get())); 143 loader_controller_.reset(new LoaderController); 144 change_list_loader_.reset(new ChangeListLoader( 145 logger_.get(), 146 base::MessageLoopProxy::current().get(), 147 metadata_.get(), 148 scheduler_.get(), 149 about_resource_loader_.get(), 150 loader_controller_.get())); 151 ASSERT_NO_FATAL_FAILURE(SetUpTestData()); 152 153 sync_client_.reset(new SyncClient(base::MessageLoopProxy::current().get(), 154 &observer_, 155 scheduler_.get(), 156 metadata_.get(), 157 cache_.get(), 158 loader_controller_.get(), 159 temp_dir_.path())); 160 161 // Disable delaying so that DoSyncLoop() starts immediately. 162 sync_client_->set_delay_for_testing(base::TimeDelta::FromSeconds(0)); 163 } 164 165 // Adds a file to the service root and |resource_ids_|. 166 void AddFileEntry(const std::string& title) { 167 google_apis::GDataErrorCode error = google_apis::GDATA_FILE_ERROR; 168 scoped_ptr<google_apis::FileResource> entry; 169 drive_service_->AddNewFile( 170 "text/plain", 171 kRemoteContent, 172 drive_service_->GetRootResourceId(), 173 title, 174 false, // shared_with_me 175 google_apis::test_util::CreateCopyResultCallback(&error, &entry)); 176 base::RunLoop().RunUntilIdle(); 177 ASSERT_EQ(google_apis::HTTP_CREATED, error); 178 ASSERT_TRUE(entry); 179 resource_ids_[title] = entry->file_id(); 180 } 181 182 // Sets up data for tests. 183 void SetUpTestData() { 184 // Prepare a temp file. 185 base::FilePath temp_file; 186 EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &temp_file)); 187 ASSERT_TRUE(google_apis::test_util::WriteStringToFile(temp_file, 188 kLocalContent)); 189 190 // Add file entries to the service. 191 ASSERT_NO_FATAL_FAILURE(AddFileEntry("foo")); 192 ASSERT_NO_FATAL_FAILURE(AddFileEntry("bar")); 193 ASSERT_NO_FATAL_FAILURE(AddFileEntry("baz")); 194 ASSERT_NO_FATAL_FAILURE(AddFileEntry("fetched")); 195 ASSERT_NO_FATAL_FAILURE(AddFileEntry("dirty")); 196 ASSERT_NO_FATAL_FAILURE(AddFileEntry("removed")); 197 ASSERT_NO_FATAL_FAILURE(AddFileEntry("moved")); 198 199 // Load data from the service to the metadata. 200 FileError error = FILE_ERROR_FAILED; 201 change_list_loader_->LoadIfNeeded( 202 google_apis::test_util::CreateCopyResultCallback(&error)); 203 base::RunLoop().RunUntilIdle(); 204 EXPECT_EQ(FILE_ERROR_OK, error); 205 206 // Prepare 3 pinned-but-not-present files. 207 EXPECT_EQ(FILE_ERROR_OK, cache_->Pin(GetLocalId("foo"))); 208 EXPECT_EQ(FILE_ERROR_OK, cache_->Pin(GetLocalId("bar"))); 209 EXPECT_EQ(FILE_ERROR_OK, cache_->Pin(GetLocalId("baz"))); 210 211 // Prepare a pinned-and-fetched file. 212 const std::string md5_fetched = "md5"; 213 EXPECT_EQ(FILE_ERROR_OK, 214 cache_->Store(GetLocalId("fetched"), md5_fetched, 215 temp_file, FileCache::FILE_OPERATION_COPY)); 216 EXPECT_EQ(FILE_ERROR_OK, cache_->Pin(GetLocalId("fetched"))); 217 218 // Prepare a pinned-and-fetched-and-dirty file. 219 EXPECT_EQ(FILE_ERROR_OK, 220 cache_->Store(GetLocalId("dirty"), std::string(), 221 temp_file, FileCache::FILE_OPERATION_COPY)); 222 EXPECT_EQ(FILE_ERROR_OK, cache_->Pin(GetLocalId("dirty"))); 223 224 // Prepare a removed file. 225 file_system::RemoveOperation remove_operation( 226 base::MessageLoopProxy::current().get(), &observer_, metadata_.get(), 227 cache_.get()); 228 remove_operation.Remove( 229 util::GetDriveMyDriveRootPath().AppendASCII("removed"), 230 false, // is_recursive 231 google_apis::test_util::CreateCopyResultCallback(&error)); 232 base::RunLoop().RunUntilIdle(); 233 EXPECT_EQ(FILE_ERROR_OK, error); 234 235 // Prepare a moved file. 236 file_system::MoveOperation move_operation( 237 base::MessageLoopProxy::current().get(), &observer_, metadata_.get()); 238 move_operation.Move( 239 util::GetDriveMyDriveRootPath().AppendASCII("moved"), 240 util::GetDriveMyDriveRootPath().AppendASCII("moved_new_title"), 241 google_apis::test_util::CreateCopyResultCallback(&error)); 242 base::RunLoop().RunUntilIdle(); 243 EXPECT_EQ(FILE_ERROR_OK, error); 244 } 245 246 protected: 247 std::string GetLocalId(const std::string& title) { 248 EXPECT_EQ(1U, resource_ids_.count(title)); 249 std::string local_id; 250 EXPECT_EQ(FILE_ERROR_OK, 251 metadata_->GetIdByResourceId(resource_ids_[title], &local_id)); 252 return local_id; 253 } 254 255 content::TestBrowserThreadBundle thread_bundle_; 256 base::ScopedTempDir temp_dir_; 257 scoped_ptr<TestingPrefServiceSimple> pref_service_; 258 scoped_ptr<test_util::FakeNetworkChangeNotifier> 259 fake_network_change_notifier_; 260 scoped_ptr<EventLogger> logger_; 261 scoped_ptr<SyncClientTestDriveService> drive_service_; 262 DummyOperationObserver observer_; 263 scoped_ptr<JobScheduler> scheduler_; 264 scoped_ptr<ResourceMetadataStorage, 265 test_util::DestroyHelperForTests> metadata_storage_; 266 scoped_ptr<FileCache, test_util::DestroyHelperForTests> cache_; 267 scoped_ptr<ResourceMetadata, test_util::DestroyHelperForTests> metadata_; 268 scoped_ptr<AboutResourceLoader> about_resource_loader_; 269 scoped_ptr<LoaderController> loader_controller_; 270 scoped_ptr<ChangeListLoader> change_list_loader_; 271 scoped_ptr<SyncClient> sync_client_; 272 273 std::map<std::string, std::string> resource_ids_; // Name-to-id map. 274}; 275 276TEST_F(SyncClientTest, StartProcessingBacklog) { 277 sync_client_->StartProcessingBacklog(); 278 base::RunLoop().RunUntilIdle(); 279 280 ResourceEntry entry; 281 // Pinned files get downloaded. 282 EXPECT_EQ(FILE_ERROR_OK, 283 metadata_->GetResourceEntryById(GetLocalId("foo"), &entry)); 284 EXPECT_TRUE(entry.file_specific_info().cache_state().is_present()); 285 286 EXPECT_EQ(FILE_ERROR_OK, 287 metadata_->GetResourceEntryById(GetLocalId("bar"), &entry)); 288 EXPECT_TRUE(entry.file_specific_info().cache_state().is_present()); 289 290 EXPECT_EQ(FILE_ERROR_OK, 291 metadata_->GetResourceEntryById(GetLocalId("baz"), &entry)); 292 EXPECT_TRUE(entry.file_specific_info().cache_state().is_present()); 293 294 // Dirty file gets uploaded. 295 EXPECT_EQ(FILE_ERROR_OK, 296 metadata_->GetResourceEntryById(GetLocalId("dirty"), &entry)); 297 EXPECT_FALSE(entry.file_specific_info().cache_state().is_dirty()); 298 299 // Removed entry is not found. 300 google_apis::GDataErrorCode status = google_apis::GDATA_OTHER_ERROR; 301 scoped_ptr<google_apis::FileResource> server_entry; 302 drive_service_->GetFileResource( 303 resource_ids_["removed"], 304 google_apis::test_util::CreateCopyResultCallback(&status, &server_entry)); 305 base::RunLoop().RunUntilIdle(); 306 EXPECT_EQ(google_apis::HTTP_SUCCESS, status); 307 ASSERT_TRUE(server_entry); 308 EXPECT_TRUE(server_entry->labels().is_trashed()); 309 310 // Moved entry was moved. 311 status = google_apis::GDATA_OTHER_ERROR; 312 drive_service_->GetFileResource( 313 resource_ids_["moved"], 314 google_apis::test_util::CreateCopyResultCallback(&status, &server_entry)); 315 base::RunLoop().RunUntilIdle(); 316 EXPECT_EQ(google_apis::HTTP_SUCCESS, status); 317 ASSERT_TRUE(server_entry); 318 EXPECT_EQ("moved_new_title", server_entry->title()); 319} 320 321TEST_F(SyncClientTest, AddFetchTask) { 322 sync_client_->AddFetchTask(GetLocalId("foo")); 323 base::RunLoop().RunUntilIdle(); 324 325 ResourceEntry entry; 326 EXPECT_EQ(FILE_ERROR_OK, 327 metadata_->GetResourceEntryById(GetLocalId("foo"), &entry)); 328 EXPECT_TRUE(entry.file_specific_info().cache_state().is_present()); 329} 330 331TEST_F(SyncClientTest, AddFetchTaskAndCancelled) { 332 // Trigger fetching of a file which results in cancellation. 333 drive_service_->set_resource_id_to_be_cancelled(resource_ids_["foo"]); 334 sync_client_->AddFetchTask(GetLocalId("foo")); 335 base::RunLoop().RunUntilIdle(); 336 337 // The file should be unpinned if the user wants the download to be cancelled. 338 ResourceEntry entry; 339 EXPECT_EQ(FILE_ERROR_OK, 340 metadata_->GetResourceEntryById(GetLocalId("foo"), &entry)); 341 EXPECT_FALSE(entry.file_specific_info().cache_state().is_pinned()); 342} 343 344TEST_F(SyncClientTest, RemoveFetchTask) { 345 sync_client_->AddFetchTask(GetLocalId("foo")); 346 sync_client_->AddFetchTask(GetLocalId("bar")); 347 sync_client_->AddFetchTask(GetLocalId("baz")); 348 349 sync_client_->RemoveFetchTask(GetLocalId("foo")); 350 sync_client_->RemoveFetchTask(GetLocalId("baz")); 351 base::RunLoop().RunUntilIdle(); 352 353 // Only "bar" should be fetched. 354 ResourceEntry entry; 355 EXPECT_EQ(FILE_ERROR_OK, 356 metadata_->GetResourceEntryById(GetLocalId("foo"), &entry)); 357 EXPECT_FALSE(entry.file_specific_info().cache_state().is_present()); 358 359 EXPECT_EQ(FILE_ERROR_OK, 360 metadata_->GetResourceEntryById(GetLocalId("bar"), &entry)); 361 EXPECT_TRUE(entry.file_specific_info().cache_state().is_present()); 362 363 EXPECT_EQ(FILE_ERROR_OK, 364 metadata_->GetResourceEntryById(GetLocalId("baz"), &entry)); 365 EXPECT_FALSE(entry.file_specific_info().cache_state().is_present()); 366 367} 368 369TEST_F(SyncClientTest, ExistingPinnedFiles) { 370 // Start checking the existing pinned files. This will collect the resource 371 // IDs of pinned files, with stale local cache files. 372 sync_client_->StartCheckingExistingPinnedFiles(); 373 base::RunLoop().RunUntilIdle(); 374 375 // "fetched" and "dirty" are the existing pinned files. 376 // The non-dirty one should be synced, but the dirty one should not. 377 base::FilePath cache_file; 378 std::string content; 379 EXPECT_EQ(FILE_ERROR_OK, cache_->GetFile(GetLocalId("fetched"), &cache_file)); 380 EXPECT_TRUE(base::ReadFileToString(cache_file, &content)); 381 EXPECT_EQ(kRemoteContent, content); 382 content.clear(); 383 384 EXPECT_EQ(FILE_ERROR_OK, cache_->GetFile(GetLocalId("dirty"), &cache_file)); 385 EXPECT_TRUE(base::ReadFileToString(cache_file, &content)); 386 EXPECT_EQ(kLocalContent, content); 387} 388 389TEST_F(SyncClientTest, RetryOnDisconnection) { 390 // Let the service go down. 391 drive_service_->set_offline(true); 392 // Change the network connection state after some delay, to test that 393 // FILE_ERROR_NO_CONNECTION is handled by SyncClient correctly. 394 // Without this delay, JobScheduler will keep the jobs unrun and SyncClient 395 // will receive no error. 396 base::MessageLoopProxy::current()->PostDelayedTask( 397 FROM_HERE, 398 base::Bind(&test_util::FakeNetworkChangeNotifier::SetConnectionType, 399 base::Unretained(fake_network_change_notifier_.get()), 400 net::NetworkChangeNotifier::CONNECTION_NONE), 401 TestTimeouts::tiny_timeout()); 402 403 // Try fetch and upload. 404 sync_client_->AddFetchTask(GetLocalId("foo")); 405 sync_client_->AddUpdateTask(ClientContext(USER_INITIATED), 406 GetLocalId("dirty")); 407 base::RunLoop().RunUntilIdle(); 408 409 // Not yet fetched nor uploaded. 410 ResourceEntry entry; 411 EXPECT_EQ(FILE_ERROR_OK, 412 metadata_->GetResourceEntryById(GetLocalId("foo"), &entry)); 413 EXPECT_FALSE(entry.file_specific_info().cache_state().is_present()); 414 EXPECT_EQ(FILE_ERROR_OK, 415 metadata_->GetResourceEntryById(GetLocalId("dirty"), &entry)); 416 EXPECT_TRUE(entry.file_specific_info().cache_state().is_dirty()); 417 418 // Switch to online. 419 fake_network_change_notifier_->SetConnectionType( 420 net::NetworkChangeNotifier::CONNECTION_WIFI); 421 drive_service_->set_offline(false); 422 base::RunLoop().RunUntilIdle(); 423 424 // Fetched and uploaded. 425 EXPECT_EQ(FILE_ERROR_OK, 426 metadata_->GetResourceEntryById(GetLocalId("foo"), &entry)); 427 EXPECT_TRUE(entry.file_specific_info().cache_state().is_present()); 428 EXPECT_EQ(FILE_ERROR_OK, 429 metadata_->GetResourceEntryById(GetLocalId("dirty"), &entry)); 430 EXPECT_FALSE(entry.file_specific_info().cache_state().is_dirty()); 431} 432 433TEST_F(SyncClientTest, ScheduleRerun) { 434 // Add a fetch task for "foo", this should result in being paused. 435 drive_service_->set_resource_id_to_be_paused(resource_ids_["foo"]); 436 sync_client_->AddFetchTask(GetLocalId("foo")); 437 base::RunLoop().RunUntilIdle(); 438 439 // While the first task is paused, add a task again. 440 // This results in scheduling rerun of the task. 441 sync_client_->AddFetchTask(GetLocalId("foo")); 442 base::RunLoop().RunUntilIdle(); 443 444 // Resume the paused task. 445 drive_service_->set_resource_id_to_be_paused(std::string()); 446 ASSERT_FALSE(drive_service_->paused_action().is_null()); 447 drive_service_->paused_action().Run(); 448 base::RunLoop().RunUntilIdle(); 449 450 // Task should be run twice. 451 EXPECT_EQ(2, drive_service_->download_file_count()); 452} 453 454TEST_F(SyncClientTest, Dependencies) { 455 // Create directories locally. 456 const base::FilePath kPath1(FILE_PATH_LITERAL("drive/root/dir1")); 457 const base::FilePath kPath2 = kPath1.AppendASCII("dir2"); 458 459 ResourceEntry parent; 460 EXPECT_EQ(FILE_ERROR_OK, 461 metadata_->GetResourceEntryByPath(kPath1.DirName(), &parent)); 462 463 ResourceEntry entry1; 464 entry1.set_parent_local_id(parent.local_id()); 465 entry1.set_title(kPath1.BaseName().AsUTF8Unsafe()); 466 entry1.mutable_file_info()->set_is_directory(true); 467 entry1.set_metadata_edit_state(ResourceEntry::DIRTY); 468 std::string local_id1; 469 EXPECT_EQ(FILE_ERROR_OK, metadata_->AddEntry(entry1, &local_id1)); 470 471 ResourceEntry entry2; 472 entry2.set_parent_local_id(local_id1); 473 entry2.set_title(kPath2.BaseName().AsUTF8Unsafe()); 474 entry2.mutable_file_info()->set_is_directory(true); 475 entry2.set_metadata_edit_state(ResourceEntry::DIRTY); 476 std::string local_id2; 477 EXPECT_EQ(FILE_ERROR_OK, metadata_->AddEntry(entry2, &local_id2)); 478 479 // Start syncing the child first. 480 sync_client_->AddUpdateTask(ClientContext(USER_INITIATED), local_id2); 481 // Start syncing the parent later. 482 sync_client_->AddUpdateTask(ClientContext(USER_INITIATED), local_id1); 483 base::RunLoop().RunUntilIdle(); 484 485 // Both entries are synced. 486 EXPECT_EQ(FILE_ERROR_OK, metadata_->GetResourceEntryById(local_id1, &entry1)); 487 EXPECT_EQ(ResourceEntry::CLEAN, entry1.metadata_edit_state()); 488 EXPECT_EQ(FILE_ERROR_OK, metadata_->GetResourceEntryById(local_id2, &entry2)); 489 EXPECT_EQ(ResourceEntry::CLEAN, entry2.metadata_edit_state()); 490} 491 492} // namespace internal 493} // namespace drive 494