Home | History | Annotate | Download | only in drive_backend
      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/files/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 
     37 namespace sync_file_system {
     38 namespace drive_backend {
     39 
     40 namespace {
     41 
     42 storage::FileSystemURL URL(const GURL& origin, const std::string& path) {
     43   return CreateSyncableFileSystemURL(
     44       origin, base::FilePath::FromUTF8Unsafe(path));
     45 }
     46 
     47 }  // namespace
     48 
     49 class 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 = context_->GetMetadataDatabase()->RegisterApp(
    123         app_id, app_root_folder_id);
    124     EXPECT_EQ(SYNC_STATUS_OK, status);
    125   }
    126 
    127  protected:
    128   std::string CreateSyncRoot() {
    129     std::string sync_root_folder_id;
    130     EXPECT_EQ(google_apis::HTTP_CREATED,
    131               fake_drive_helper_->AddOrphanedFolder(
    132                   kSyncRootFolderTitle, &sync_root_folder_id));
    133     return sync_root_folder_id;
    134   }
    135 
    136   std::string CreateRemoteFolder(const std::string& parent_folder_id,
    137                                  const std::string& title) {
    138     std::string folder_id;
    139     EXPECT_EQ(google_apis::HTTP_CREATED,
    140               fake_drive_helper_->AddFolder(
    141                   parent_folder_id, title, &folder_id));
    142     return folder_id;
    143   }
    144 
    145   std::string CreateRemoteFile(const std::string& parent_folder_id,
    146                                const std::string& title,
    147                                const std::string& content) {
    148     std::string file_id;
    149     EXPECT_EQ(google_apis::HTTP_SUCCESS,
    150               fake_drive_helper_->AddFile(
    151                   parent_folder_id, title, content, &file_id));
    152     return file_id;
    153   }
    154 
    155   void CreateLocalFile(const storage::FileSystemURL& url) {
    156     remote_change_processor_->UpdateLocalFileMetadata(
    157         url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    158                         SYNC_FILE_TYPE_FILE));
    159   }
    160 
    161   google_apis::GDataErrorCode AddFileToFolder(
    162       const std::string& parent_folder_id,
    163       const std::string& file_id) {
    164     google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
    165     context_->GetDriveService()->AddResourceToDirectory(
    166         parent_folder_id, file_id,
    167         CreateResultReceiver(&error));
    168     base::RunLoop().RunUntilIdle();
    169     return error;
    170   }
    171 
    172   int CountParents(const std::string& file_id) {
    173     scoped_ptr<google_apis::FileResource> entry;
    174     EXPECT_EQ(google_apis::HTTP_SUCCESS,
    175               fake_drive_helper_->GetFileResource(file_id, &entry));
    176     return entry->parents().size();
    177   }
    178 
    179   SyncStatusCode RunRemoteToLocalSyncer() {
    180     SyncStatusCode status = SYNC_STATUS_UNKNOWN;
    181     scoped_ptr<RemoteToLocalSyncer> syncer(
    182         new RemoteToLocalSyncer(context_.get()));
    183     syncer->RunPreflight(SyncTaskToken::CreateForTesting(
    184         CreateResultReceiver(&status)));
    185     base::RunLoop().RunUntilIdle();
    186     return status;
    187   }
    188 
    189   SyncStatusCode RunLocalToRemoteSyncer(const storage::FileSystemURL& url,
    190                                         const FileChange& file_change) {
    191     SyncStatusCode status = SYNC_STATUS_UNKNOWN;
    192     base::FilePath local_path = base::FilePath(FILE_PATH_LITERAL("dummy"));
    193     if (file_change.IsAddOrUpdate())
    194       CreateTemporaryFileInDir(database_dir_.path(), &local_path);
    195     scoped_ptr<LocalToRemoteSyncer> syncer(new LocalToRemoteSyncer(
    196         context_.get(),
    197         SyncFileMetadata(file_change.file_type(), 0, base::Time()),
    198         file_change, local_path, url));
    199     syncer->RunPreflight(SyncTaskToken::CreateForTesting(
    200         CreateResultReceiver(&status)));
    201     base::RunLoop().RunUntilIdle();
    202     if (status == SYNC_STATUS_OK)
    203       remote_change_processor_->ClearLocalChanges(url);
    204     return status;
    205   }
    206 
    207   void RunRemoteToLocalSyncerUntilIdle() {
    208     const int kRetryLimit = 100;
    209     SyncStatusCode status;
    210     int retry_count = 0;
    211     MetadataDatabase* metadata_database = context_->GetMetadataDatabase();
    212     do {
    213       if (retry_count++ > kRetryLimit)
    214         break;
    215       status = RunRemoteToLocalSyncer();
    216     } while (status == SYNC_STATUS_OK ||
    217              status == SYNC_STATUS_RETRY ||
    218              metadata_database->PromoteDemotedTrackers());
    219     EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, status);
    220   }
    221 
    222   SyncStatusCode RunConflictResolver() {
    223     SyncStatusCode status = SYNC_STATUS_UNKNOWN;
    224     ConflictResolver resolver(context_.get());
    225     resolver.RunPreflight(SyncTaskToken::CreateForTesting(
    226         CreateResultReceiver(&status)));
    227     base::RunLoop().RunUntilIdle();
    228     return status;
    229   }
    230 
    231   SyncStatusCode ListChanges() {
    232     SyncStatusCode status = SYNC_STATUS_UNKNOWN;
    233     sync_task_manager_->ScheduleSyncTask(
    234         FROM_HERE,
    235         scoped_ptr<SyncTask>(new ListChangesTask(context_.get())),
    236         SyncTaskManager::PRIORITY_MED,
    237         CreateResultReceiver(&status));
    238     base::RunLoop().RunUntilIdle();
    239     return status;
    240   }
    241 
    242   ScopedVector<google_apis::ResourceEntry>
    243   GetResourceEntriesForParentAndTitle(const std::string& parent_folder_id,
    244                                       const std::string& title) {
    245     ScopedVector<google_apis::ResourceEntry> entries;
    246     EXPECT_EQ(google_apis::HTTP_SUCCESS,
    247               fake_drive_helper_->SearchByTitle(
    248                   parent_folder_id, title, &entries));
    249     return entries.Pass();
    250   }
    251 
    252   void VerifyConflictResolution(
    253       const std::string& parent_folder_id,
    254       const std::string& title,
    255       const std::string& primary_file_id,
    256       google_apis::ResourceEntry::ResourceEntryKind kind) {
    257     ScopedVector<google_apis::ResourceEntry> entries;
    258     EXPECT_EQ(google_apis::HTTP_SUCCESS,
    259               fake_drive_helper_->SearchByTitle(
    260                   parent_folder_id, title, &entries));
    261     ASSERT_EQ(1u, entries.size());
    262     EXPECT_EQ(primary_file_id, entries[0]->resource_id());
    263     EXPECT_EQ(kind, entries[0]->kind());
    264   }
    265 
    266   void VerifyLocalChangeConsistency(
    267       const URLToFileChangesMap& expected_changes) {
    268     remote_change_processor_->VerifyConsistency(expected_changes);
    269   }
    270 
    271  private:
    272   content::TestBrowserThreadBundle thread_bundle_;
    273   base::ScopedTempDir database_dir_;
    274   scoped_ptr<leveldb::Env> in_memory_env_;
    275 
    276   scoped_ptr<SyncEngineContext> context_;
    277   scoped_ptr<FakeDriveServiceHelper> fake_drive_helper_;
    278   scoped_ptr<FakeRemoteChangeProcessor> remote_change_processor_;
    279 
    280   scoped_ptr<SyncTaskManager> sync_task_manager_;
    281 
    282   DISALLOW_COPY_AND_ASSIGN(ConflictResolverTest);
    283 };
    284 
    285 TEST_F(ConflictResolverTest, NoFileToBeResolved) {
    286   const GURL kOrigin("chrome-extension://example");
    287   const std::string sync_root = CreateSyncRoot();
    288   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
    289   InitializeMetadataDatabase();
    290   RegisterApp(kOrigin.host(), app_root);
    291   RunRemoteToLocalSyncerUntilIdle();
    292 
    293   EXPECT_EQ(SYNC_STATUS_NO_CONFLICT, RunConflictResolver());
    294 }
    295 
    296 TEST_F(ConflictResolverTest, ResolveConflict_Files) {
    297   const GURL kOrigin("chrome-extension://example");
    298   const std::string sync_root = CreateSyncRoot();
    299   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
    300   InitializeMetadataDatabase();
    301   RegisterApp(kOrigin.host(), app_root);
    302   RunRemoteToLocalSyncerUntilIdle();
    303 
    304   const std::string kTitle = "foo";
    305   const std::string primary = CreateRemoteFile(app_root, kTitle, "data1");
    306   CreateRemoteFile(app_root, kTitle, "data2");
    307   CreateRemoteFile(app_root, kTitle, "data3");
    308   CreateRemoteFile(app_root, kTitle, "data4");
    309   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
    310   RunRemoteToLocalSyncerUntilIdle();
    311 
    312   ScopedVector<google_apis::ResourceEntry> entries =
    313       GetResourceEntriesForParentAndTitle(app_root, kTitle);
    314   ASSERT_EQ(4u, entries.size());
    315 
    316   // Only primary file should survive.
    317   EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
    318   VerifyConflictResolution(app_root, kTitle, primary,
    319                            google_apis::ResourceEntry::ENTRY_KIND_FILE);
    320 }
    321 
    322 TEST_F(ConflictResolverTest, ResolveConflict_Folders) {
    323   const GURL kOrigin("chrome-extension://example");
    324   const std::string sync_root = CreateSyncRoot();
    325   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
    326   InitializeMetadataDatabase();
    327   RegisterApp(kOrigin.host(), app_root);
    328   RunRemoteToLocalSyncerUntilIdle();
    329 
    330   const std::string kTitle = "foo";
    331   const std::string primary = CreateRemoteFolder(app_root, kTitle);
    332   CreateRemoteFolder(app_root, kTitle);
    333   CreateRemoteFolder(app_root, kTitle);
    334   CreateRemoteFolder(app_root, kTitle);
    335   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
    336   RunRemoteToLocalSyncerUntilIdle();
    337 
    338   ScopedVector<google_apis::ResourceEntry> entries =
    339       GetResourceEntriesForParentAndTitle(app_root, kTitle);
    340   ASSERT_EQ(4u, entries.size());
    341 
    342   // Only primary file should survive.
    343   EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
    344   VerifyConflictResolution(app_root, kTitle, primary,
    345                            google_apis::ResourceEntry::ENTRY_KIND_FOLDER);
    346 }
    347 
    348 TEST_F(ConflictResolverTest, ResolveConflict_FilesAndFolders) {
    349   const GURL kOrigin("chrome-extension://example");
    350   const std::string sync_root = CreateSyncRoot();
    351   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
    352   InitializeMetadataDatabase();
    353   RegisterApp(kOrigin.host(), app_root);
    354   RunRemoteToLocalSyncerUntilIdle();
    355 
    356   const std::string kTitle = "foo";
    357   CreateRemoteFile(app_root, kTitle, "data");
    358   const std::string primary = CreateRemoteFolder(app_root, kTitle);
    359   CreateRemoteFile(app_root, kTitle, "data2");
    360   CreateRemoteFolder(app_root, kTitle);
    361   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
    362   RunRemoteToLocalSyncerUntilIdle();
    363 
    364   ScopedVector<google_apis::ResourceEntry> entries =
    365       GetResourceEntriesForParentAndTitle(app_root, kTitle);
    366   ASSERT_EQ(4u, entries.size());
    367 
    368   // Only primary file should survive.
    369   EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
    370   VerifyConflictResolution(app_root, kTitle, primary,
    371                            google_apis::ResourceEntry::ENTRY_KIND_FOLDER);
    372 }
    373 
    374 TEST_F(ConflictResolverTest, ResolveConflict_RemoteFolderOnLocalFile) {
    375   const GURL kOrigin("chrome-extension://example");
    376   const std::string sync_root = CreateSyncRoot();
    377   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
    378   InitializeMetadataDatabase();
    379   RegisterApp(kOrigin.host(), app_root);
    380   RunRemoteToLocalSyncerUntilIdle();
    381 
    382   const std::string kTitle = "foo";
    383   storage::FileSystemURL kURL = URL(kOrigin, kTitle);
    384 
    385   // Create a file on local and sync it.
    386   CreateLocalFile(kURL);
    387   RunLocalToRemoteSyncer(
    388       kURL,
    389       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, SYNC_FILE_TYPE_FILE));
    390 
    391   // Create a folder on remote and sync it.
    392   const std::string primary = CreateRemoteFolder(app_root, kTitle);
    393   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
    394   RunRemoteToLocalSyncerUntilIdle();
    395 
    396   ScopedVector<google_apis::ResourceEntry> entries =
    397       GetResourceEntriesForParentAndTitle(app_root, kTitle);
    398   ASSERT_EQ(2u, entries.size());
    399 
    400   // Run conflict resolver. Only primary file should survive.
    401   EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
    402   VerifyConflictResolution(app_root, kTitle, primary,
    403                            google_apis::ResourceEntry::ENTRY_KIND_FOLDER);
    404 
    405   // Continue to run remote-to-local sync.
    406   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
    407   RunRemoteToLocalSyncerUntilIdle();
    408 
    409   // Verify that the local side has been synced to the same state
    410   // (i.e. file deletion and folder creation).
    411   URLToFileChangesMap expected_changes;
    412   expected_changes[kURL].push_back(
    413       FileChange(FileChange::FILE_CHANGE_DELETE,
    414                  SYNC_FILE_TYPE_UNKNOWN));
    415   expected_changes[kURL].push_back(
    416       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    417                  SYNC_FILE_TYPE_DIRECTORY));
    418   VerifyLocalChangeConsistency(expected_changes);
    419 }
    420 
    421 TEST_F(ConflictResolverTest, ResolveConflict_RemoteNestedFolderOnLocalFile) {
    422   const GURL kOrigin("chrome-extension://example");
    423   const std::string sync_root = CreateSyncRoot();
    424   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
    425   InitializeMetadataDatabase();
    426   RegisterApp(kOrigin.host(), app_root);
    427   RunRemoteToLocalSyncerUntilIdle();
    428 
    429   const std::string kTitle = "foo";
    430   storage::FileSystemURL kURL = URL(kOrigin, kTitle);
    431 
    432   // Create a file on local and sync it.
    433   CreateLocalFile(kURL);
    434   RunLocalToRemoteSyncer(
    435       kURL,
    436       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, SYNC_FILE_TYPE_FILE));
    437 
    438   // Create a folder and subfolder in it on remote, and sync it.
    439   const std::string primary = CreateRemoteFolder(app_root, kTitle);
    440   CreateRemoteFolder(primary, "nested");
    441   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
    442   RunRemoteToLocalSyncerUntilIdle();
    443 
    444   ScopedVector<google_apis::ResourceEntry> entries =
    445       GetResourceEntriesForParentAndTitle(app_root, kTitle);
    446   ASSERT_EQ(2u, entries.size());
    447 
    448   // Run conflict resolver. Only primary file should survive.
    449   EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
    450   VerifyConflictResolution(app_root, kTitle, primary,
    451                            google_apis::ResourceEntry::ENTRY_KIND_FOLDER);
    452 
    453   // Continue to run remote-to-local sync.
    454   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
    455   RunRemoteToLocalSyncerUntilIdle();
    456 
    457   // Verify that the local side has been synced to the same state
    458   // (i.e. file deletion and folders creation).
    459   URLToFileChangesMap expected_changes;
    460   expected_changes[kURL].push_back(
    461       FileChange(FileChange::FILE_CHANGE_DELETE,
    462                  SYNC_FILE_TYPE_UNKNOWN));
    463   expected_changes[kURL].push_back(
    464       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    465                  SYNC_FILE_TYPE_DIRECTORY));
    466   expected_changes[URL(kOrigin, "foo/nested")].push_back(
    467       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    468                  SYNC_FILE_TYPE_DIRECTORY));
    469   VerifyLocalChangeConsistency(expected_changes);
    470 }
    471 
    472 TEST_F(ConflictResolverTest, ResolveMultiParents_File) {
    473   const GURL kOrigin("chrome-extension://example");
    474   const std::string sync_root = CreateSyncRoot();
    475   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
    476   InitializeMetadataDatabase();
    477   RegisterApp(kOrigin.host(), app_root);
    478   RunRemoteToLocalSyncerUntilIdle();
    479 
    480   const std::string primary = CreateRemoteFolder(app_root, "primary");
    481   const std::string file = CreateRemoteFile(primary, "file", "data");
    482   ASSERT_EQ(google_apis::HTTP_SUCCESS,
    483             AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary1"), file));
    484   ASSERT_EQ(google_apis::HTTP_SUCCESS,
    485             AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary2"), file));
    486   ASSERT_EQ(google_apis::HTTP_SUCCESS,
    487             AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary3"), file));
    488 
    489   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
    490   RunRemoteToLocalSyncerUntilIdle();
    491 
    492   EXPECT_EQ(4, CountParents(file));
    493 
    494   EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
    495 
    496   EXPECT_EQ(1, CountParents(file));
    497 }
    498 
    499 TEST_F(ConflictResolverTest, ResolveMultiParents_Folder) {
    500   const GURL kOrigin("chrome-extension://example");
    501   const std::string sync_root = CreateSyncRoot();
    502   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
    503   InitializeMetadataDatabase();
    504   RegisterApp(kOrigin.host(), app_root);
    505   RunRemoteToLocalSyncerUntilIdle();
    506 
    507   const std::string primary = CreateRemoteFolder(app_root, "primary");
    508   const std::string file = CreateRemoteFolder(primary, "folder");
    509   ASSERT_EQ(google_apis::HTTP_SUCCESS,
    510             AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary1"), file));
    511   ASSERT_EQ(google_apis::HTTP_SUCCESS,
    512             AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary2"), file));
    513   ASSERT_EQ(google_apis::HTTP_SUCCESS,
    514             AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary3"), file));
    515 
    516   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
    517   RunRemoteToLocalSyncerUntilIdle();
    518 
    519   EXPECT_EQ(4, CountParents(file));
    520 
    521   EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
    522 
    523   EXPECT_EQ(1, CountParents(file));
    524 }
    525 
    526 }  // namespace drive_backend
    527 }  // namespace sync_file_system
    528