conflict_resolver_unittest.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
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/conflict_resolver.h" 6 7#include "base/bind.h" 8#include "base/callback.h" 9#include "base/file_util.h" 10#include "base/files/scoped_temp_dir.h" 11#include "base/run_loop.h" 12#include "base/thread_task_runner_handle.h" 13#include "chrome/browser/drive/drive_uploader.h" 14#include "chrome/browser/drive/fake_drive_service.h" 15#include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h" 16#include "chrome/browser/sync_file_system/drive_backend/drive_backend_test_util.h" 17#include "chrome/browser/sync_file_system/drive_backend/fake_drive_service_helper.h" 18#include "chrome/browser/sync_file_system/drive_backend/fake_drive_uploader.h" 19#include "chrome/browser/sync_file_system/drive_backend/list_changes_task.h" 20#include "chrome/browser/sync_file_system/drive_backend/local_to_remote_syncer.h" 21#include "chrome/browser/sync_file_system/drive_backend/metadata_database.h" 22#include "chrome/browser/sync_file_system/drive_backend/remote_to_local_syncer.h" 23#include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h" 24#include "chrome/browser/sync_file_system/drive_backend/sync_engine_initializer.h" 25#include "chrome/browser/sync_file_system/drive_backend/sync_task_manager.h" 26#include "chrome/browser/sync_file_system/drive_backend/sync_task_token.h" 27#include "chrome/browser/sync_file_system/fake_remote_change_processor.h" 28#include "chrome/browser/sync_file_system/sync_file_system_test_util.h" 29#include "chrome/browser/sync_file_system/syncable_file_system_util.h" 30#include "content/public/test/test_browser_thread_bundle.h" 31#include "google_apis/drive/drive_api_parser.h" 32#include "google_apis/drive/gdata_errorcode.h" 33#include "testing/gtest/include/gtest/gtest.h" 34#include "third_party/leveldatabase/src/helpers/memenv/memenv.h" 35#include "third_party/leveldatabase/src/include/leveldb/env.h" 36 37namespace sync_file_system { 38namespace drive_backend { 39 40namespace { 41 42storage::FileSystemURL URL(const GURL& origin, const std::string& path) { 43 return CreateSyncableFileSystemURL( 44 origin, base::FilePath::FromUTF8Unsafe(path)); 45} 46 47} // namespace 48 49class ConflictResolverTest : public testing::Test { 50 public: 51 typedef FakeRemoteChangeProcessor::URLToFileChangesMap URLToFileChangesMap; 52 53 ConflictResolverTest() 54 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {} 55 virtual ~ConflictResolverTest() {} 56 57 virtual void SetUp() OVERRIDE { 58 ASSERT_TRUE(database_dir_.CreateUniqueTempDir()); 59 in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default())); 60 61 scoped_ptr<FakeDriveServiceWrapper> 62 fake_drive_service(new FakeDriveServiceWrapper); 63 scoped_ptr<drive::DriveUploaderInterface> 64 drive_uploader(new FakeDriveUploader(fake_drive_service.get())); 65 fake_drive_helper_.reset( 66 new FakeDriveServiceHelper(fake_drive_service.get(), 67 drive_uploader.get(), 68 kSyncRootFolderTitle)); 69 remote_change_processor_.reset(new FakeRemoteChangeProcessor); 70 71 context_.reset(new SyncEngineContext( 72 fake_drive_service.PassAs<drive::DriveServiceInterface>(), 73 drive_uploader.Pass(), 74 NULL, 75 base::ThreadTaskRunnerHandle::Get(), 76 base::ThreadTaskRunnerHandle::Get())); 77 context_->SetRemoteChangeProcessor(remote_change_processor_.get()); 78 79 RegisterSyncableFileSystem(); 80 81 sync_task_manager_.reset(new SyncTaskManager( 82 base::WeakPtr<SyncTaskManager::Client>(), 83 10 /* maximum_background_task */, 84 base::ThreadTaskRunnerHandle::Get())); 85 sync_task_manager_->Initialize(SYNC_STATUS_OK); 86 } 87 88 virtual void TearDown() OVERRIDE { 89 sync_task_manager_.reset(); 90 RevokeSyncableFileSystem(); 91 fake_drive_helper_.reset(); 92 context_.reset(); 93 base::RunLoop().RunUntilIdle(); 94 } 95 96 void InitializeMetadataDatabase() { 97 SyncEngineInitializer* initializer = 98 new SyncEngineInitializer(context_.get(), 99 database_dir_.path(), 100 in_memory_env_.get()); 101 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 102 sync_task_manager_->ScheduleSyncTask( 103 FROM_HERE, 104 scoped_ptr<SyncTask>(initializer), 105 SyncTaskManager::PRIORITY_MED, 106 base::Bind(&ConflictResolverTest::DidInitializeMetadataDatabase, 107 base::Unretained(this), initializer, &status)); 108 109 base::RunLoop().RunUntilIdle(); 110 EXPECT_EQ(SYNC_STATUS_OK, status); 111 } 112 113 void DidInitializeMetadataDatabase(SyncEngineInitializer* initializer, 114 SyncStatusCode* status_out, 115 SyncStatusCode status) { 116 context_->SetMetadataDatabase(initializer->PassMetadataDatabase()); 117 *status_out = status; 118 } 119 120 void RegisterApp(const std::string& app_id, 121 const std::string& app_root_folder_id) { 122 SyncStatusCode status = SYNC_STATUS_FAILED; 123 context_->GetMetadataDatabase()->RegisterApp(app_id, app_root_folder_id, 124 CreateResultReceiver(&status)); 125 base::RunLoop().RunUntilIdle(); 126 EXPECT_EQ(SYNC_STATUS_OK, status); 127 } 128 129 protected: 130 std::string CreateSyncRoot() { 131 std::string sync_root_folder_id; 132 EXPECT_EQ(google_apis::HTTP_CREATED, 133 fake_drive_helper_->AddOrphanedFolder( 134 kSyncRootFolderTitle, &sync_root_folder_id)); 135 return sync_root_folder_id; 136 } 137 138 std::string CreateRemoteFolder(const std::string& parent_folder_id, 139 const std::string& title) { 140 std::string folder_id; 141 EXPECT_EQ(google_apis::HTTP_CREATED, 142 fake_drive_helper_->AddFolder( 143 parent_folder_id, title, &folder_id)); 144 return folder_id; 145 } 146 147 std::string CreateRemoteFile(const std::string& parent_folder_id, 148 const std::string& title, 149 const std::string& content) { 150 std::string file_id; 151 EXPECT_EQ(google_apis::HTTP_SUCCESS, 152 fake_drive_helper_->AddFile( 153 parent_folder_id, title, content, &file_id)); 154 return file_id; 155 } 156 157 void CreateLocalFile(const storage::FileSystemURL& url) { 158 remote_change_processor_->UpdateLocalFileMetadata( 159 url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 160 SYNC_FILE_TYPE_FILE)); 161 } 162 163 google_apis::GDataErrorCode AddFileToFolder( 164 const std::string& parent_folder_id, 165 const std::string& file_id) { 166 google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR; 167 context_->GetDriveService()->AddResourceToDirectory( 168 parent_folder_id, file_id, 169 CreateResultReceiver(&error)); 170 base::RunLoop().RunUntilIdle(); 171 return error; 172 } 173 174 int CountParents(const std::string& file_id) { 175 scoped_ptr<google_apis::FileResource> entry; 176 EXPECT_EQ(google_apis::HTTP_SUCCESS, 177 fake_drive_helper_->GetFileResource(file_id, &entry)); 178 return entry->parents().size(); 179 } 180 181 SyncStatusCode RunRemoteToLocalSyncer() { 182 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 183 scoped_ptr<RemoteToLocalSyncer> syncer( 184 new RemoteToLocalSyncer(context_.get())); 185 syncer->RunPreflight(SyncTaskToken::CreateForTesting( 186 CreateResultReceiver(&status))); 187 base::RunLoop().RunUntilIdle(); 188 return status; 189 } 190 191 SyncStatusCode RunLocalToRemoteSyncer(const storage::FileSystemURL& url, 192 const FileChange& file_change) { 193 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 194 base::FilePath local_path = base::FilePath(FILE_PATH_LITERAL("dummy")); 195 if (file_change.IsAddOrUpdate()) 196 CreateTemporaryFileInDir(database_dir_.path(), &local_path); 197 scoped_ptr<LocalToRemoteSyncer> syncer(new LocalToRemoteSyncer( 198 context_.get(), 199 SyncFileMetadata(file_change.file_type(), 0, base::Time()), 200 file_change, local_path, url)); 201 syncer->RunPreflight(SyncTaskToken::CreateForTesting( 202 CreateResultReceiver(&status))); 203 base::RunLoop().RunUntilIdle(); 204 if (status == SYNC_STATUS_OK) 205 remote_change_processor_->ClearLocalChanges(url); 206 return status; 207 } 208 209 void RunRemoteToLocalSyncerUntilIdle() { 210 const int kRetryLimit = 100; 211 SyncStatusCode status; 212 int retry_count = 0; 213 MetadataDatabase* metadata_database = context_->GetMetadataDatabase(); 214 do { 215 if (retry_count++ > kRetryLimit) 216 break; 217 status = RunRemoteToLocalSyncer(); 218 } while (status == SYNC_STATUS_OK || 219 status == SYNC_STATUS_RETRY || 220 metadata_database->PromoteLowerPriorityTrackersToNormal()); 221 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, status); 222 } 223 224 SyncStatusCode RunConflictResolver() { 225 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 226 ConflictResolver resolver(context_.get()); 227 resolver.RunPreflight(SyncTaskToken::CreateForTesting( 228 CreateResultReceiver(&status))); 229 base::RunLoop().RunUntilIdle(); 230 return status; 231 } 232 233 SyncStatusCode ListChanges() { 234 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 235 sync_task_manager_->ScheduleSyncTask( 236 FROM_HERE, 237 scoped_ptr<SyncTask>(new ListChangesTask(context_.get())), 238 SyncTaskManager::PRIORITY_MED, 239 CreateResultReceiver(&status)); 240 base::RunLoop().RunUntilIdle(); 241 return status; 242 } 243 244 ScopedVector<google_apis::ResourceEntry> 245 GetResourceEntriesForParentAndTitle(const std::string& parent_folder_id, 246 const std::string& title) { 247 ScopedVector<google_apis::ResourceEntry> entries; 248 EXPECT_EQ(google_apis::HTTP_SUCCESS, 249 fake_drive_helper_->SearchByTitle( 250 parent_folder_id, title, &entries)); 251 return entries.Pass(); 252 } 253 254 void VerifyConflictResolution( 255 const std::string& parent_folder_id, 256 const std::string& title, 257 const std::string& primary_file_id, 258 google_apis::ResourceEntry::ResourceEntryKind kind) { 259 ScopedVector<google_apis::ResourceEntry> entries; 260 EXPECT_EQ(google_apis::HTTP_SUCCESS, 261 fake_drive_helper_->SearchByTitle( 262 parent_folder_id, title, &entries)); 263 ASSERT_EQ(1u, entries.size()); 264 EXPECT_EQ(primary_file_id, entries[0]->resource_id()); 265 EXPECT_EQ(kind, entries[0]->kind()); 266 } 267 268 void VerifyLocalChangeConsistency( 269 const URLToFileChangesMap& expected_changes) { 270 remote_change_processor_->VerifyConsistency(expected_changes); 271 } 272 273 private: 274 content::TestBrowserThreadBundle thread_bundle_; 275 base::ScopedTempDir database_dir_; 276 scoped_ptr<leveldb::Env> in_memory_env_; 277 278 scoped_ptr<SyncEngineContext> context_; 279 scoped_ptr<FakeDriveServiceHelper> fake_drive_helper_; 280 scoped_ptr<FakeRemoteChangeProcessor> remote_change_processor_; 281 282 scoped_ptr<SyncTaskManager> sync_task_manager_; 283 284 DISALLOW_COPY_AND_ASSIGN(ConflictResolverTest); 285}; 286 287TEST_F(ConflictResolverTest, NoFileToBeResolved) { 288 const GURL kOrigin("chrome-extension://example"); 289 const std::string sync_root = CreateSyncRoot(); 290 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 291 InitializeMetadataDatabase(); 292 RegisterApp(kOrigin.host(), app_root); 293 RunRemoteToLocalSyncerUntilIdle(); 294 295 EXPECT_EQ(SYNC_STATUS_NO_CONFLICT, RunConflictResolver()); 296} 297 298TEST_F(ConflictResolverTest, ResolveConflict_Files) { 299 const GURL kOrigin("chrome-extension://example"); 300 const std::string sync_root = CreateSyncRoot(); 301 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 302 InitializeMetadataDatabase(); 303 RegisterApp(kOrigin.host(), app_root); 304 RunRemoteToLocalSyncerUntilIdle(); 305 306 const std::string kTitle = "foo"; 307 const std::string primary = CreateRemoteFile(app_root, kTitle, "data1"); 308 CreateRemoteFile(app_root, kTitle, "data2"); 309 CreateRemoteFile(app_root, kTitle, "data3"); 310 CreateRemoteFile(app_root, kTitle, "data4"); 311 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 312 RunRemoteToLocalSyncerUntilIdle(); 313 314 ScopedVector<google_apis::ResourceEntry> entries = 315 GetResourceEntriesForParentAndTitle(app_root, kTitle); 316 ASSERT_EQ(4u, entries.size()); 317 318 // Only primary file should survive. 319 EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver()); 320 VerifyConflictResolution(app_root, kTitle, primary, 321 google_apis::ResourceEntry::ENTRY_KIND_FILE); 322} 323 324TEST_F(ConflictResolverTest, ResolveConflict_Folders) { 325 const GURL kOrigin("chrome-extension://example"); 326 const std::string sync_root = CreateSyncRoot(); 327 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 328 InitializeMetadataDatabase(); 329 RegisterApp(kOrigin.host(), app_root); 330 RunRemoteToLocalSyncerUntilIdle(); 331 332 const std::string kTitle = "foo"; 333 const std::string primary = CreateRemoteFolder(app_root, kTitle); 334 CreateRemoteFolder(app_root, kTitle); 335 CreateRemoteFolder(app_root, kTitle); 336 CreateRemoteFolder(app_root, kTitle); 337 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 338 RunRemoteToLocalSyncerUntilIdle(); 339 340 ScopedVector<google_apis::ResourceEntry> entries = 341 GetResourceEntriesForParentAndTitle(app_root, kTitle); 342 ASSERT_EQ(4u, entries.size()); 343 344 // Only primary file should survive. 345 EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver()); 346 VerifyConflictResolution(app_root, kTitle, primary, 347 google_apis::ResourceEntry::ENTRY_KIND_FOLDER); 348} 349 350TEST_F(ConflictResolverTest, ResolveConflict_FilesAndFolders) { 351 const GURL kOrigin("chrome-extension://example"); 352 const std::string sync_root = CreateSyncRoot(); 353 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 354 InitializeMetadataDatabase(); 355 RegisterApp(kOrigin.host(), app_root); 356 RunRemoteToLocalSyncerUntilIdle(); 357 358 const std::string kTitle = "foo"; 359 CreateRemoteFile(app_root, kTitle, "data"); 360 const std::string primary = CreateRemoteFolder(app_root, kTitle); 361 CreateRemoteFile(app_root, kTitle, "data2"); 362 CreateRemoteFolder(app_root, kTitle); 363 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 364 RunRemoteToLocalSyncerUntilIdle(); 365 366 ScopedVector<google_apis::ResourceEntry> entries = 367 GetResourceEntriesForParentAndTitle(app_root, kTitle); 368 ASSERT_EQ(4u, entries.size()); 369 370 // Only primary file should survive. 371 EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver()); 372 VerifyConflictResolution(app_root, kTitle, primary, 373 google_apis::ResourceEntry::ENTRY_KIND_FOLDER); 374} 375 376TEST_F(ConflictResolverTest, ResolveConflict_RemoteFolderOnLocalFile) { 377 const GURL kOrigin("chrome-extension://example"); 378 const std::string sync_root = CreateSyncRoot(); 379 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 380 InitializeMetadataDatabase(); 381 RegisterApp(kOrigin.host(), app_root); 382 RunRemoteToLocalSyncerUntilIdle(); 383 384 const std::string kTitle = "foo"; 385 storage::FileSystemURL kURL = URL(kOrigin, kTitle); 386 387 // Create a file on local and sync it. 388 CreateLocalFile(kURL); 389 RunLocalToRemoteSyncer( 390 kURL, 391 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, SYNC_FILE_TYPE_FILE)); 392 393 // Create a folder on remote and sync it. 394 const std::string primary = CreateRemoteFolder(app_root, kTitle); 395 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 396 RunRemoteToLocalSyncerUntilIdle(); 397 398 ScopedVector<google_apis::ResourceEntry> entries = 399 GetResourceEntriesForParentAndTitle(app_root, kTitle); 400 ASSERT_EQ(2u, entries.size()); 401 402 // Run conflict resolver. Only primary file should survive. 403 EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver()); 404 VerifyConflictResolution(app_root, kTitle, primary, 405 google_apis::ResourceEntry::ENTRY_KIND_FOLDER); 406 407 // Continue to run remote-to-local sync. 408 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 409 RunRemoteToLocalSyncerUntilIdle(); 410 411 // Verify that the local side has been synced to the same state 412 // (i.e. file deletion and folder creation). 413 URLToFileChangesMap expected_changes; 414 expected_changes[kURL].push_back( 415 FileChange(FileChange::FILE_CHANGE_DELETE, 416 SYNC_FILE_TYPE_UNKNOWN)); 417 expected_changes[kURL].push_back( 418 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 419 SYNC_FILE_TYPE_DIRECTORY)); 420 VerifyLocalChangeConsistency(expected_changes); 421} 422 423TEST_F(ConflictResolverTest, ResolveConflict_RemoteNestedFolderOnLocalFile) { 424 const GURL kOrigin("chrome-extension://example"); 425 const std::string sync_root = CreateSyncRoot(); 426 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 427 InitializeMetadataDatabase(); 428 RegisterApp(kOrigin.host(), app_root); 429 RunRemoteToLocalSyncerUntilIdle(); 430 431 const std::string kTitle = "foo"; 432 storage::FileSystemURL kURL = URL(kOrigin, kTitle); 433 434 // Create a file on local and sync it. 435 CreateLocalFile(kURL); 436 RunLocalToRemoteSyncer( 437 kURL, 438 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, SYNC_FILE_TYPE_FILE)); 439 440 // Create a folder and subfolder in it on remote, and sync it. 441 const std::string primary = CreateRemoteFolder(app_root, kTitle); 442 CreateRemoteFolder(primary, "nested"); 443 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 444 RunRemoteToLocalSyncerUntilIdle(); 445 446 ScopedVector<google_apis::ResourceEntry> entries = 447 GetResourceEntriesForParentAndTitle(app_root, kTitle); 448 ASSERT_EQ(2u, entries.size()); 449 450 // Run conflict resolver. Only primary file should survive. 451 EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver()); 452 VerifyConflictResolution(app_root, kTitle, primary, 453 google_apis::ResourceEntry::ENTRY_KIND_FOLDER); 454 455 // Continue to run remote-to-local sync. 456 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 457 RunRemoteToLocalSyncerUntilIdle(); 458 459 // Verify that the local side has been synced to the same state 460 // (i.e. file deletion and folders creation). 461 URLToFileChangesMap expected_changes; 462 expected_changes[kURL].push_back( 463 FileChange(FileChange::FILE_CHANGE_DELETE, 464 SYNC_FILE_TYPE_UNKNOWN)); 465 expected_changes[kURL].push_back( 466 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 467 SYNC_FILE_TYPE_DIRECTORY)); 468 expected_changes[URL(kOrigin, "foo/nested")].push_back( 469 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 470 SYNC_FILE_TYPE_DIRECTORY)); 471 VerifyLocalChangeConsistency(expected_changes); 472} 473 474TEST_F(ConflictResolverTest, ResolveMultiParents_File) { 475 const GURL kOrigin("chrome-extension://example"); 476 const std::string sync_root = CreateSyncRoot(); 477 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 478 InitializeMetadataDatabase(); 479 RegisterApp(kOrigin.host(), app_root); 480 RunRemoteToLocalSyncerUntilIdle(); 481 482 const std::string primary = CreateRemoteFolder(app_root, "primary"); 483 const std::string file = CreateRemoteFile(primary, "file", "data"); 484 ASSERT_EQ(google_apis::HTTP_SUCCESS, 485 AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary1"), file)); 486 ASSERT_EQ(google_apis::HTTP_SUCCESS, 487 AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary2"), file)); 488 ASSERT_EQ(google_apis::HTTP_SUCCESS, 489 AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary3"), file)); 490 491 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 492 RunRemoteToLocalSyncerUntilIdle(); 493 494 EXPECT_EQ(4, CountParents(file)); 495 496 EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver()); 497 498 EXPECT_EQ(1, CountParents(file)); 499} 500 501TEST_F(ConflictResolverTest, ResolveMultiParents_Folder) { 502 const GURL kOrigin("chrome-extension://example"); 503 const std::string sync_root = CreateSyncRoot(); 504 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 505 InitializeMetadataDatabase(); 506 RegisterApp(kOrigin.host(), app_root); 507 RunRemoteToLocalSyncerUntilIdle(); 508 509 const std::string primary = CreateRemoteFolder(app_root, "primary"); 510 const std::string file = CreateRemoteFolder(primary, "folder"); 511 ASSERT_EQ(google_apis::HTTP_SUCCESS, 512 AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary1"), file)); 513 ASSERT_EQ(google_apis::HTTP_SUCCESS, 514 AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary2"), file)); 515 ASSERT_EQ(google_apis::HTTP_SUCCESS, 516 AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary3"), file)); 517 518 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 519 RunRemoteToLocalSyncerUntilIdle(); 520 521 EXPECT_EQ(4, CountParents(file)); 522 523 EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver()); 524 525 EXPECT_EQ(1, CountParents(file)); 526} 527 528} // namespace drive_backend 529} // namespace sync_file_system 530