remote_to_local_syncer_unittest.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
1// Copyright 2013 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/sync_file_system/drive_backend/remote_to_local_syncer.h" 6 7#include <map> 8 9#include "base/bind.h" 10#include "base/callback.h" 11#include "base/files/scoped_temp_dir.h" 12#include "base/logging.h" 13#include "base/run_loop.h" 14#include "chrome/browser/drive/drive_uploader.h" 15#include "chrome/browser/drive/fake_drive_service.h" 16#include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h" 17#include "chrome/browser/sync_file_system/drive_backend/drive_backend_test_util.h" 18#include "chrome/browser/sync_file_system/drive_backend/list_changes_task.h" 19#include "chrome/browser/sync_file_system/drive_backend/metadata_database.h" 20#include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h" 21#include "chrome/browser/sync_file_system/drive_backend/sync_engine_initializer.h" 22#include "chrome/browser/sync_file_system/drive_backend_v1/fake_drive_service_helper.h" 23#include "chrome/browser/sync_file_system/fake_remote_change_processor.h" 24#include "chrome/browser/sync_file_system/sync_file_system_test_util.h" 25#include "chrome/browser/sync_file_system/syncable_file_system_util.h" 26#include "content/public/test/test_browser_thread_bundle.h" 27#include "google_apis/drive/gdata_errorcode.h" 28#include "testing/gtest/include/gtest/gtest.h" 29 30namespace sync_file_system { 31namespace drive_backend { 32 33namespace { 34 35fileapi::FileSystemURL URL(const GURL& origin, 36 const std::string& path) { 37 return CreateSyncableFileSystemURL( 38 origin, base::FilePath::FromUTF8Unsafe(path)); 39} 40 41} // namespace 42 43class RemoteToLocalSyncerTest : public testing::Test, 44 public SyncEngineContext { 45 public: 46 typedef FakeRemoteChangeProcessor::URLToFileChangesMap URLToFileChangesMap; 47 48 RemoteToLocalSyncerTest() 49 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {} 50 virtual ~RemoteToLocalSyncerTest() {} 51 52 virtual void SetUp() OVERRIDE { 53 ASSERT_TRUE(database_dir_.CreateUniqueTempDir()); 54 55 fake_drive_service_.reset(new drive::FakeDriveService); 56 ASSERT_TRUE(fake_drive_service_->LoadAccountMetadataForWapi( 57 "sync_file_system/account_metadata.json")); 58 ASSERT_TRUE(fake_drive_service_->LoadResourceListForWapi( 59 "gdata/empty_feed.json")); 60 61 drive_uploader_.reset(new drive::DriveUploader( 62 fake_drive_service_.get(), base::MessageLoopProxy::current().get())); 63 fake_drive_helper_.reset(new FakeDriveServiceHelper( 64 fake_drive_service_.get(), drive_uploader_.get())); 65 fake_remote_change_processor_.reset(new FakeRemoteChangeProcessor); 66 67 RegisterSyncableFileSystem(); 68 } 69 70 virtual void TearDown() OVERRIDE { 71 RevokeSyncableFileSystem(); 72 73 fake_remote_change_processor_.reset(); 74 metadata_database_.reset(); 75 fake_drive_helper_.reset(); 76 drive_uploader_.reset(); 77 fake_drive_service_.reset(); 78 base::RunLoop().RunUntilIdle(); 79 } 80 81 void InitializeMetadataDatabase() { 82 SyncEngineInitializer initializer(this, 83 base::MessageLoopProxy::current(), 84 fake_drive_service_.get(), 85 database_dir_.path()); 86 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 87 initializer.Run(CreateResultReceiver(&status)); 88 base::RunLoop().RunUntilIdle(); 89 EXPECT_EQ(SYNC_STATUS_OK, status); 90 metadata_database_ = initializer.PassMetadataDatabase(); 91 } 92 93 void RegisterApp(const std::string& app_id, 94 const std::string& app_root_folder_id) { 95 SyncStatusCode status = SYNC_STATUS_FAILED; 96 metadata_database_->RegisterApp(app_id, app_root_folder_id, 97 CreateResultReceiver(&status)); 98 base::RunLoop().RunUntilIdle(); 99 EXPECT_EQ(SYNC_STATUS_OK, status); 100 } 101 102 virtual drive::DriveServiceInterface* GetDriveService() OVERRIDE { 103 return fake_drive_service_.get(); 104 } 105 106 virtual drive::DriveUploaderInterface* GetDriveUploader() OVERRIDE { 107 return drive_uploader_.get(); 108 } 109 110 virtual MetadataDatabase* GetMetadataDatabase() OVERRIDE { 111 return metadata_database_.get(); 112 } 113 114 virtual RemoteChangeProcessor* GetRemoteChangeProcessor() OVERRIDE { 115 return fake_remote_change_processor_.get(); 116 } 117 118 virtual base::SequencedTaskRunner* GetBlockingTaskRunner() OVERRIDE { 119 return base::MessageLoopProxy::current().get(); 120 } 121 122 protected: 123 std::string CreateSyncRoot() { 124 std::string sync_root_folder_id; 125 EXPECT_EQ(google_apis::HTTP_CREATED, 126 fake_drive_helper_->AddOrphanedFolder( 127 kSyncRootFolderTitle, &sync_root_folder_id)); 128 return sync_root_folder_id; 129 } 130 131 std::string CreateRemoteFolder(const std::string& parent_folder_id, 132 const std::string& title) { 133 std::string folder_id; 134 EXPECT_EQ(google_apis::HTTP_CREATED, 135 fake_drive_helper_->AddFolder( 136 parent_folder_id, title, &folder_id)); 137 return folder_id; 138 } 139 140 std::string CreateRemoteFile(const std::string& parent_folder_id, 141 const std::string& title, 142 const std::string& content) { 143 std::string file_id; 144 EXPECT_EQ(google_apis::HTTP_SUCCESS, 145 fake_drive_helper_->AddFile( 146 parent_folder_id, title, content, &file_id)); 147 return file_id; 148 } 149 150 void DeleteRemoteFile(const std::string& file_id) { 151 EXPECT_EQ(google_apis::HTTP_SUCCESS, 152 fake_drive_helper_->RemoveResource(file_id)); 153 } 154 155 void CreateLocalFolder(const fileapi::FileSystemURL& url) { 156 fake_remote_change_processor_->UpdateLocalFileMetadata( 157 url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 158 SYNC_FILE_TYPE_DIRECTORY)); 159 } 160 161 void CreateLocalFile(const fileapi::FileSystemURL& url) { 162 fake_remote_change_processor_->UpdateLocalFileMetadata( 163 url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 164 SYNC_FILE_TYPE_FILE)); 165 } 166 167 SyncStatusCode RunSyncer() { 168 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 169 scoped_ptr<RemoteToLocalSyncer> syncer(new RemoteToLocalSyncer(this)); 170 syncer->Run(CreateResultReceiver(&status)); 171 base::RunLoop().RunUntilIdle(); 172 return status; 173 } 174 175 void RunSyncerUntilIdle() { 176 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 177 while (status != SYNC_STATUS_NO_CHANGE_TO_SYNC) 178 status = RunSyncer(); 179 } 180 181 SyncStatusCode ListChanges() { 182 ListChangesTask list_changes(this); 183 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 184 list_changes.Run(CreateResultReceiver(&status)); 185 base::RunLoop().RunUntilIdle(); 186 return status; 187 } 188 189 void AppendExpectedChange(const fileapi::FileSystemURL& url, 190 FileChange::ChangeType change_type, 191 SyncFileType file_type) { 192 expected_changes_[url].push_back(FileChange(change_type, file_type)); 193 } 194 195 void VerifyConsistency() { 196 URLToFileChangesMap applied_changes = 197 fake_remote_change_processor_->GetAppliedRemoteChanges(); 198 EXPECT_EQ(expected_changes_.size(), applied_changes.size()); 199 200 for (URLToFileChangesMap::const_iterator itr = applied_changes.begin(); 201 itr != applied_changes.end(); ++itr) { 202 const fileapi::FileSystemURL& url = itr->first; 203 URLToFileChangesMap::const_iterator found = expected_changes_.find(url); 204 if (found == expected_changes_.end()) { 205 EXPECT_TRUE(found != expected_changes_.end()); 206 continue; 207 } 208 209 if (itr->second.empty() || found->second.empty()) { 210 EXPECT_TRUE(!itr->second.empty()); 211 EXPECT_TRUE(!found->second.empty()); 212 continue; 213 } 214 215 EXPECT_EQ(found->second.back().change(), 216 itr->second.back().change()) << url.DebugString(); 217 EXPECT_EQ(found->second.back().file_type(), 218 itr->second.back().file_type()) << url.DebugString(); 219 } 220 } 221 222 private: 223 content::TestBrowserThreadBundle thread_bundle_; 224 base::ScopedTempDir database_dir_; 225 226 scoped_ptr<drive::FakeDriveService> fake_drive_service_; 227 scoped_ptr<drive::DriveUploader> drive_uploader_; 228 scoped_ptr<FakeDriveServiceHelper> fake_drive_helper_; 229 scoped_ptr<MetadataDatabase> metadata_database_; 230 scoped_ptr<FakeRemoteChangeProcessor> fake_remote_change_processor_; 231 232 URLToFileChangesMap expected_changes_; 233 234 DISALLOW_COPY_AND_ASSIGN(RemoteToLocalSyncerTest); 235}; 236 237TEST_F(RemoteToLocalSyncerTest, AddNewFile) { 238 const GURL kOrigin("chrome-extension://example"); 239 const std::string sync_root = CreateSyncRoot(); 240 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 241 InitializeMetadataDatabase(); 242 RegisterApp(kOrigin.host(), app_root); 243 244 const std::string folder1 = CreateRemoteFolder(app_root, "folder1"); 245 const std::string file1 = CreateRemoteFile(app_root, "file1", "data1"); 246 const std::string folder2 = CreateRemoteFolder(folder1, "folder2"); 247 const std::string file2 = CreateRemoteFile(folder1, "file2", "data2"); 248 249 RunSyncerUntilIdle(); 250 251 // Create expected changes. 252 // TODO(nhiroki): Clean up creating URL part. 253 AppendExpectedChange(URL(kOrigin, ""), 254 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 255 SYNC_FILE_TYPE_DIRECTORY); 256 AppendExpectedChange(URL(kOrigin, "folder1"), 257 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 258 SYNC_FILE_TYPE_DIRECTORY); 259 AppendExpectedChange(URL(kOrigin, "file1"), 260 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 261 SYNC_FILE_TYPE_FILE); 262 AppendExpectedChange(URL(kOrigin, "folder1/folder2"), 263 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 264 SYNC_FILE_TYPE_DIRECTORY); 265 AppendExpectedChange(URL(kOrigin, "folder1/file2"), 266 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 267 SYNC_FILE_TYPE_FILE); 268 269 VerifyConsistency(); 270 271 EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL)); 272 EXPECT_FALSE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL)); 273} 274 275TEST_F(RemoteToLocalSyncerTest, DeleteFile) { 276 const GURL kOrigin("chrome-extension://example"); 277 const std::string sync_root = CreateSyncRoot(); 278 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 279 InitializeMetadataDatabase(); 280 RegisterApp(kOrigin.host(), app_root); 281 282 const std::string folder = CreateRemoteFolder(app_root, "folder"); 283 const std::string file = CreateRemoteFile(app_root, "file", "data"); 284 285 AppendExpectedChange(URL(kOrigin, ""), 286 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 287 SYNC_FILE_TYPE_DIRECTORY); 288 AppendExpectedChange(URL(kOrigin, "folder"), 289 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 290 SYNC_FILE_TYPE_DIRECTORY); 291 AppendExpectedChange(URL(kOrigin, "file"), 292 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 293 SYNC_FILE_TYPE_FILE); 294 295 RunSyncerUntilIdle(); 296 VerifyConsistency(); 297 298 DeleteRemoteFile(folder); 299 DeleteRemoteFile(file); 300 301 AppendExpectedChange(URL(kOrigin, "folder"), 302 FileChange::FILE_CHANGE_DELETE, 303 SYNC_FILE_TYPE_UNKNOWN); 304 AppendExpectedChange(URL(kOrigin, "file"), 305 FileChange::FILE_CHANGE_DELETE, 306 SYNC_FILE_TYPE_UNKNOWN); 307 308 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 309 RunSyncerUntilIdle(); 310 VerifyConsistency(); 311 312 EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL)); 313 EXPECT_FALSE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL)); 314} 315 316TEST_F(RemoteToLocalSyncerTest, DeleteNestedFiles) { 317 const GURL kOrigin("chrome-extension://example"); 318 const std::string sync_root = CreateSyncRoot(); 319 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 320 InitializeMetadataDatabase(); 321 RegisterApp(kOrigin.host(), app_root); 322 323 const std::string folder1 = CreateRemoteFolder(app_root, "folder1"); 324 const std::string file1 = CreateRemoteFile(app_root, "file1", "data1"); 325 const std::string folder2 = CreateRemoteFolder(folder1, "folder2"); 326 const std::string file2 = CreateRemoteFile(folder1, "file2", "data2"); 327 328 AppendExpectedChange(URL(kOrigin, ""), 329 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 330 SYNC_FILE_TYPE_DIRECTORY); 331 AppendExpectedChange(URL(kOrigin, "folder1"), 332 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 333 SYNC_FILE_TYPE_DIRECTORY); 334 AppendExpectedChange(URL(kOrigin, "file1"), 335 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 336 SYNC_FILE_TYPE_FILE); 337 AppendExpectedChange(URL(kOrigin, "folder1/folder2"), 338 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 339 SYNC_FILE_TYPE_DIRECTORY); 340 AppendExpectedChange(URL(kOrigin, "folder1/file2"), 341 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 342 SYNC_FILE_TYPE_FILE); 343 344 RunSyncerUntilIdle(); 345 VerifyConsistency(); 346 347 DeleteRemoteFile(folder1); 348 349 AppendExpectedChange(URL(kOrigin, "folder1"), 350 FileChange::FILE_CHANGE_DELETE, 351 SYNC_FILE_TYPE_UNKNOWN); 352 // Changes for descendant files ("folder2" and "file2") should be ignored. 353 354 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 355 RunSyncerUntilIdle(); 356 VerifyConsistency(); 357 358 EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL)); 359 EXPECT_FALSE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL)); 360} 361 362TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFileOnFolder) { 363 const GURL kOrigin("chrome-extension://example"); 364 const std::string sync_root = CreateSyncRoot(); 365 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 366 InitializeMetadataDatabase(); 367 RegisterApp(kOrigin.host(), app_root); 368 369 AppendExpectedChange(URL(kOrigin, ""), 370 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 371 SYNC_FILE_TYPE_DIRECTORY); 372 373 CreateLocalFolder(URL(kOrigin, "folder")); 374 CreateRemoteFile(app_root, "folder", "data"); 375 376 // Folder-File conflict happens. File creation should be ignored. 377 378 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 379 RunSyncerUntilIdle(); 380 VerifyConsistency(); 381 382 // Tracker for the remote file should be lowered. 383 EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL)); 384 EXPECT_TRUE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL)); 385} 386 387TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFile) { 388 const GURL kOrigin("chrome-extension://example"); 389 const std::string sync_root = CreateSyncRoot(); 390 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 391 InitializeMetadataDatabase(); 392 RegisterApp(kOrigin.host(), app_root); 393 394 AppendExpectedChange(URL(kOrigin, ""), 395 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 396 SYNC_FILE_TYPE_DIRECTORY); 397 398 RunSyncerUntilIdle(); 399 VerifyConsistency(); 400 401 CreateLocalFile(URL(kOrigin, "file")); 402 CreateRemoteFolder(app_root, "file"); 403 404 // File-Folder conflict happens. Folder should override the existing file. 405 AppendExpectedChange(URL(kOrigin, "file"), 406 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 407 SYNC_FILE_TYPE_DIRECTORY); 408 409 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 410 RunSyncerUntilIdle(); 411 VerifyConsistency(); 412 413 EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL)); 414 EXPECT_FALSE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL)); 415} 416 417TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFolder) { 418 const GURL kOrigin("chrome-extension://example"); 419 const std::string sync_root = CreateSyncRoot(); 420 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 421 InitializeMetadataDatabase(); 422 RegisterApp(kOrigin.host(), app_root); 423 424 AppendExpectedChange(URL(kOrigin, ""), 425 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 426 SYNC_FILE_TYPE_DIRECTORY); 427 428 CreateLocalFolder(URL(kOrigin, "folder")); 429 CreateRemoteFolder(app_root, "folder"); 430 431 // Folder-Folder conflict happens. Folder creation should be ignored. 432 433 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 434 RunSyncerUntilIdle(); 435 VerifyConsistency(); 436 437 EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL)); 438 EXPECT_FALSE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL)); 439} 440 441TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFileOnFile) { 442 const GURL kOrigin("chrome-extension://example"); 443 const std::string sync_root = CreateSyncRoot(); 444 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 445 InitializeMetadataDatabase(); 446 RegisterApp(kOrigin.host(), app_root); 447 448 AppendExpectedChange(URL(kOrigin, ""), 449 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 450 SYNC_FILE_TYPE_DIRECTORY); 451 452 CreateLocalFile(URL(kOrigin, "file")); 453 CreateRemoteFile(app_root, "file", "data"); 454 455 // File-File conflict happens. File creation should be ignored. 456 457 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 458 RunSyncerUntilIdle(); 459 VerifyConsistency(); 460 461 // Tracker for the remote file should be lowered. 462 EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL)); 463 EXPECT_TRUE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL)); 464} 465 466TEST_F(RemoteToLocalSyncerTest, Conflict_CreateNestedFolderOnFile) { 467 const GURL kOrigin("chrome-extension://example"); 468 const std::string sync_root = CreateSyncRoot(); 469 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 470 InitializeMetadataDatabase(); 471 RegisterApp(kOrigin.host(), app_root); 472 473 AppendExpectedChange(URL(kOrigin, "/"), 474 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 475 SYNC_FILE_TYPE_DIRECTORY); 476 477 RunSyncerUntilIdle(); 478 VerifyConsistency(); 479 480 const std::string folder = CreateRemoteFolder(app_root, "folder"); 481 482 AppendExpectedChange(URL(kOrigin, "/folder"), 483 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 484 SYNC_FILE_TYPE_DIRECTORY); 485 486 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 487 RunSyncerUntilIdle(); 488 489 CreateLocalFile(URL(kOrigin, "/folder")); 490 CreateRemoteFile(folder, "file", "data"); 491 492 // File-Folder conflict happens. Folder should override the existing file. 493 AppendExpectedChange(URL(kOrigin, "/folder"), 494 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 495 SYNC_FILE_TYPE_DIRECTORY); 496 497 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 498 RunSyncerUntilIdle(); 499 VerifyConsistency(); 500} 501 502TEST_F(RemoteToLocalSyncerTest, AppRootDeletion) { 503 const GURL kOrigin("chrome-extension://example"); 504 const std::string sync_root = CreateSyncRoot(); 505 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 506 InitializeMetadataDatabase(); 507 RegisterApp(kOrigin.host(), app_root); 508 509 AppendExpectedChange(URL(kOrigin, "/"), 510 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 511 SYNC_FILE_TYPE_DIRECTORY); 512 513 RunSyncerUntilIdle(); 514 VerifyConsistency(); 515 516 DeleteRemoteFile(app_root); 517 518 AppendExpectedChange(URL(kOrigin, "/"), 519 FileChange::FILE_CHANGE_DELETE, 520 SYNC_FILE_TYPE_UNKNOWN); 521 522 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 523 RunSyncerUntilIdle(); 524 VerifyConsistency(); 525 526 // SyncEngine will re-register the app and resurrect the app root later. 527} 528 529} // namespace drive_backend 530} // namespace sync_file_system 531