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