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/local_to_remote_syncer.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/callback.h"
      9 #include "base/files/scoped_temp_dir.h"
     10 #include "base/logging.h"
     11 #include "base/run_loop.h"
     12 #include "base/thread_task_runner_handle.h"
     13 #include "chrome/browser/drive/drive_api_util.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/fake_drive_service_helper.h"
     19 #include "chrome/browser/sync_file_system/drive_backend/fake_drive_uploader.h"
     20 #include "chrome/browser/sync_file_system/drive_backend/list_changes_task.h"
     21 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
     22 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h"
     23 #include "chrome/browser/sync_file_system/drive_backend/remote_to_local_syncer.h"
     24 #include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h"
     25 #include "chrome/browser/sync_file_system/drive_backend/sync_engine_initializer.h"
     26 #include "chrome/browser/sync_file_system/drive_backend/sync_task_manager.h"
     27 #include "chrome/browser/sync_file_system/drive_backend/sync_task_token.h"
     28 #include "chrome/browser/sync_file_system/fake_remote_change_processor.h"
     29 #include "chrome/browser/sync_file_system/sync_file_system_test_util.h"
     30 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
     31 #include "content/public/test/test_browser_thread_bundle.h"
     32 #include "google_apis/drive/drive_api_parser.h"
     33 #include "google_apis/drive/gdata_errorcode.h"
     34 #include "testing/gtest/include/gtest/gtest.h"
     35 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
     36 #include "third_party/leveldatabase/src/include/leveldb/env.h"
     37 
     38 namespace sync_file_system {
     39 namespace drive_backend {
     40 
     41 namespace {
     42 
     43 storage::FileSystemURL URL(const GURL& origin, const std::string& path) {
     44   return CreateSyncableFileSystemURL(
     45       origin, base::FilePath::FromUTF8Unsafe(path));
     46 }
     47 
     48 const int kRetryLimit = 100;
     49 
     50 }  // namespace
     51 
     52 class LocalToRemoteSyncerTest : public testing::Test {
     53  public:
     54   LocalToRemoteSyncerTest()
     55       : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
     56   virtual ~LocalToRemoteSyncerTest() {}
     57 
     58   virtual void SetUp() OVERRIDE {
     59     ASSERT_TRUE(database_dir_.CreateUniqueTempDir());
     60     in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
     61 
     62     scoped_ptr<FakeDriveServiceWrapper>
     63         fake_drive_service(new FakeDriveServiceWrapper);
     64     scoped_ptr<drive::DriveUploaderInterface>
     65         drive_uploader(new FakeDriveUploader(fake_drive_service.get()));
     66     fake_drive_helper_.reset(new FakeDriveServiceHelper(
     67         fake_drive_service.get(),
     68         drive_uploader.get(),
     69         kSyncRootFolderTitle));
     70     remote_change_processor_.reset(new FakeRemoteChangeProcessor);
     71 
     72     context_.reset(new SyncEngineContext(
     73         fake_drive_service.PassAs<drive::DriveServiceInterface>(),
     74         drive_uploader.Pass(),
     75         NULL,
     76         base::ThreadTaskRunnerHandle::Get(),
     77         base::ThreadTaskRunnerHandle::Get()));
     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::ThreadTaskRunnerHandle::Get()));
     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 
    104     sync_task_manager_->ScheduleSyncTask(
    105         FROM_HERE,
    106         scoped_ptr<SyncTask>(initializer),
    107         SyncTaskManager::PRIORITY_MED,
    108         base::Bind(&LocalToRemoteSyncerTest::DidInitializeMetadataDatabase,
    109                    base::Unretained(this), initializer, &status));
    110 
    111     base::RunLoop().RunUntilIdle();
    112     EXPECT_EQ(SYNC_STATUS_OK, status);
    113   }
    114 
    115   void DidInitializeMetadataDatabase(SyncEngineInitializer* initializer,
    116                                      SyncStatusCode* status_out,
    117                                      SyncStatusCode status) {
    118     *status_out = status;
    119     context_->SetMetadataDatabase(initializer->PassMetadataDatabase());
    120   }
    121 
    122   void RegisterApp(const std::string& app_id,
    123                    const std::string& app_root_folder_id) {
    124     SyncStatusCode status = context_->GetMetadataDatabase()->RegisterApp(
    125         app_id, app_root_folder_id);
    126     EXPECT_EQ(SYNC_STATUS_OK, status);
    127   }
    128 
    129   MetadataDatabase* GetMetadataDatabase() {
    130     return context_->GetMetadataDatabase();
    131   }
    132 
    133  protected:
    134   std::string CreateSyncRoot() {
    135     std::string sync_root_folder_id;
    136     EXPECT_EQ(google_apis::HTTP_CREATED,
    137               fake_drive_helper_->AddOrphanedFolder(
    138                   kSyncRootFolderTitle, &sync_root_folder_id));
    139     return sync_root_folder_id;
    140   }
    141 
    142   std::string CreateRemoteFolder(const std::string& parent_folder_id,
    143                                  const std::string& title) {
    144     std::string folder_id;
    145     EXPECT_EQ(google_apis::HTTP_CREATED,
    146               fake_drive_helper_->AddFolder(
    147                   parent_folder_id, title, &folder_id));
    148     return folder_id;
    149   }
    150 
    151   std::string CreateRemoteFile(const std::string& parent_folder_id,
    152                                const std::string& title,
    153                                const std::string& content) {
    154     std::string file_id;
    155     EXPECT_EQ(google_apis::HTTP_SUCCESS,
    156               fake_drive_helper_->AddFile(
    157                   parent_folder_id, title, content, &file_id));
    158     return file_id;
    159   }
    160 
    161   void DeleteResource(const std::string& file_id) {
    162     EXPECT_EQ(google_apis::HTTP_NO_CONTENT,
    163               fake_drive_helper_->DeleteResource(file_id));
    164   }
    165 
    166   SyncStatusCode RunLocalToRemoteSyncer(FileChange file_change,
    167                                         const storage::FileSystemURL& url) {
    168     SyncStatusCode status = SYNC_STATUS_UNKNOWN;
    169     base::FilePath local_path = base::FilePath::FromUTF8Unsafe("dummy");
    170     scoped_ptr<LocalToRemoteSyncer> syncer(new LocalToRemoteSyncer(
    171         context_.get(),
    172         SyncFileMetadata(file_change.file_type(), 0, base::Time()),
    173         file_change, local_path, url));
    174     syncer->RunPreflight(SyncTaskToken::CreateForTesting(
    175         CreateResultReceiver(&status)));
    176     base::RunLoop().RunUntilIdle();
    177     return status;
    178   }
    179 
    180   SyncStatusCode ListChanges() {
    181     SyncStatusCode status = SYNC_STATUS_UNKNOWN;
    182     sync_task_manager_->ScheduleSyncTask(
    183         FROM_HERE,
    184         scoped_ptr<SyncTask>(new ListChangesTask(context_.get())),
    185         SyncTaskManager::PRIORITY_MED,
    186         CreateResultReceiver(&status));
    187     base::RunLoop().RunUntilIdle();
    188     return status;
    189   }
    190 
    191   SyncStatusCode RunRemoteToLocalSyncer() {
    192     SyncStatusCode status = SYNC_STATUS_UNKNOWN;
    193     scoped_ptr<RemoteToLocalSyncer>
    194         syncer(new RemoteToLocalSyncer(context_.get()));
    195     syncer->RunPreflight(SyncTaskToken::CreateForTesting(
    196         CreateResultReceiver(&status)));
    197     base::RunLoop().RunUntilIdle();
    198     return status;
    199   }
    200 
    201   SyncStatusCode RunRemoteToLocalSyncerUntilIdle() {
    202     SyncStatusCode status;
    203     int retry_count = 0;
    204     do {
    205       if (retry_count++ > kRetryLimit)
    206         break;
    207       status = RunRemoteToLocalSyncer();
    208     } while (status == SYNC_STATUS_OK ||
    209              status == SYNC_STATUS_RETRY ||
    210              GetMetadataDatabase()->PromoteDemotedTrackers());
    211     EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, status);
    212     return status;
    213   }
    214 
    215   ScopedVector<google_apis::ResourceEntry>
    216   GetResourceEntriesForParentAndTitle(const std::string& parent_folder_id,
    217                                       const std::string& title) {
    218     ScopedVector<google_apis::ResourceEntry> entries;
    219     EXPECT_EQ(google_apis::HTTP_SUCCESS,
    220               fake_drive_helper_->SearchByTitle(
    221                   parent_folder_id, title, &entries));
    222     return entries.Pass();
    223   }
    224 
    225   std::string GetFileIDForParentAndTitle(const std::string& parent_folder_id,
    226                                          const std::string& title) {
    227     ScopedVector<google_apis::ResourceEntry> entries =
    228         GetResourceEntriesForParentAndTitle(parent_folder_id, title);
    229     if (entries.size() != 1)
    230       return std::string();
    231     return entries[0]->resource_id();
    232   }
    233 
    234   void VerifyTitleUniqueness(
    235       const std::string& parent_folder_id,
    236       const std::string& title,
    237       google_apis::ResourceEntry::ResourceEntryKind kind) {
    238     ScopedVector<google_apis::ResourceEntry> entries;
    239     EXPECT_EQ(google_apis::HTTP_SUCCESS,
    240               fake_drive_helper_->SearchByTitle(
    241                   parent_folder_id, title, &entries));
    242     ASSERT_EQ(1u, entries.size());
    243     EXPECT_EQ(kind, entries[0]->kind());
    244   }
    245 
    246   void VerifyFileDeletion(const std::string& parent_folder_id,
    247                           const std::string& title) {
    248     ScopedVector<google_apis::ResourceEntry> entries;
    249     EXPECT_EQ(google_apis::HTTP_SUCCESS,
    250               fake_drive_helper_->SearchByTitle(
    251                   parent_folder_id, title, &entries));
    252     EXPECT_TRUE(entries.empty());
    253   }
    254 
    255  private:
    256   content::TestBrowserThreadBundle thread_bundle_;
    257   base::ScopedTempDir database_dir_;
    258   scoped_ptr<leveldb::Env> in_memory_env_;
    259 
    260   scoped_ptr<SyncEngineContext> context_;
    261   scoped_ptr<FakeDriveServiceHelper> fake_drive_helper_;
    262   scoped_ptr<FakeRemoteChangeProcessor> remote_change_processor_;
    263   scoped_ptr<SyncTaskManager> sync_task_manager_;
    264 
    265   DISALLOW_COPY_AND_ASSIGN(LocalToRemoteSyncerTest);
    266 };
    267 
    268 TEST_F(LocalToRemoteSyncerTest, CreateFile) {
    269   const GURL kOrigin("chrome-extension://example");
    270   const std::string sync_root = CreateSyncRoot();
    271   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
    272   InitializeMetadataDatabase();
    273   RegisterApp(kOrigin.host(), app_root);
    274 
    275   EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
    276       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    277                  SYNC_FILE_TYPE_FILE),
    278       URL(kOrigin, "file1")));
    279   EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
    280       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    281                  SYNC_FILE_TYPE_DIRECTORY),
    282       URL(kOrigin, "folder")));
    283   EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
    284       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    285                  SYNC_FILE_TYPE_FILE),
    286       URL(kOrigin, "folder/file2")));
    287 
    288   std::string folder_id = GetFileIDForParentAndTitle(app_root, "folder");
    289   ASSERT_FALSE(folder_id.empty());
    290 
    291   VerifyTitleUniqueness(
    292       app_root, "file1", google_apis::ResourceEntry::ENTRY_KIND_FILE);
    293   VerifyTitleUniqueness(
    294       app_root, "folder", google_apis::ResourceEntry::ENTRY_KIND_FOLDER);
    295   VerifyTitleUniqueness(
    296       folder_id, "file2", google_apis::ResourceEntry::ENTRY_KIND_FILE);
    297 }
    298 
    299 TEST_F(LocalToRemoteSyncerTest, CreateFileOnMissingPath) {
    300   const GURL kOrigin("chrome-extension://example");
    301   const std::string sync_root = CreateSyncRoot();
    302   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
    303   InitializeMetadataDatabase();
    304   RegisterApp(kOrigin.host(), app_root);
    305 
    306   // Run the syncer 3 times to create missing folder1 and folder2.
    307   EXPECT_EQ(SYNC_STATUS_RETRY, RunLocalToRemoteSyncer(
    308       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    309                  SYNC_FILE_TYPE_FILE),
    310       URL(kOrigin, "folder1/folder2/file")));
    311   EXPECT_EQ(SYNC_STATUS_RETRY, RunLocalToRemoteSyncer(
    312       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    313                  SYNC_FILE_TYPE_FILE),
    314       URL(kOrigin, "folder1/folder2/file")));
    315   EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
    316       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    317                  SYNC_FILE_TYPE_FILE),
    318       URL(kOrigin, "folder1/folder2/file")));
    319 
    320   std::string folder_id1 = GetFileIDForParentAndTitle(app_root, "folder1");
    321   ASSERT_FALSE(folder_id1.empty());
    322   std::string folder_id2 = GetFileIDForParentAndTitle(folder_id1, "folder2");
    323   ASSERT_FALSE(folder_id2.empty());
    324 
    325   VerifyTitleUniqueness(
    326       app_root, "folder1", google_apis::ResourceEntry::ENTRY_KIND_FOLDER);
    327   VerifyTitleUniqueness(
    328       folder_id1, "folder2", google_apis::ResourceEntry::ENTRY_KIND_FOLDER);
    329   VerifyTitleUniqueness(
    330       folder_id2, "file", google_apis::ResourceEntry::ENTRY_KIND_FILE);
    331 }
    332 
    333 TEST_F(LocalToRemoteSyncerTest, DeleteFile) {
    334   const GURL kOrigin("chrome-extension://example");
    335   const std::string sync_root = CreateSyncRoot();
    336   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
    337   InitializeMetadataDatabase();
    338   RegisterApp(kOrigin.host(), app_root);
    339 
    340   EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
    341       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    342                  SYNC_FILE_TYPE_FILE),
    343       URL(kOrigin, "file")));
    344   EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
    345       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    346                  SYNC_FILE_TYPE_DIRECTORY),
    347       URL(kOrigin, "folder")));
    348 
    349   VerifyTitleUniqueness(
    350       app_root, "file", google_apis::ResourceEntry::ENTRY_KIND_FILE);
    351   VerifyTitleUniqueness(
    352       app_root, "folder", google_apis::ResourceEntry::ENTRY_KIND_FOLDER);
    353 
    354   EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
    355       FileChange(FileChange::FILE_CHANGE_DELETE,
    356                  SYNC_FILE_TYPE_FILE),
    357       URL(kOrigin, "file")));
    358   EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
    359       FileChange(FileChange::FILE_CHANGE_DELETE,
    360                  SYNC_FILE_TYPE_DIRECTORY),
    361       URL(kOrigin, "folder")));
    362 
    363   VerifyFileDeletion(app_root, "file");
    364   VerifyFileDeletion(app_root, "folder");
    365 }
    366 
    367 TEST_F(LocalToRemoteSyncerTest, Conflict_CreateFileOnFolder) {
    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 
    374   CreateRemoteFolder(app_root, "foo");
    375   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
    376   EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
    377       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    378                  SYNC_FILE_TYPE_FILE),
    379       URL(kOrigin, "foo")));
    380 
    381   // There should exist both file and folder on remote.
    382   ScopedVector<google_apis::ResourceEntry> entries =
    383       GetResourceEntriesForParentAndTitle(app_root, "foo");
    384   ASSERT_EQ(2u, entries.size());
    385   EXPECT_EQ(google_apis::ResourceEntry::ENTRY_KIND_FOLDER, entries[0]->kind());
    386   EXPECT_EQ(google_apis::ResourceEntry::ENTRY_KIND_FILE, entries[1]->kind());
    387 }
    388 
    389 TEST_F(LocalToRemoteSyncerTest, Conflict_CreateFolderOnFile) {
    390   const GURL kOrigin("chrome-extension://example");
    391   const std::string sync_root = CreateSyncRoot();
    392   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
    393   InitializeMetadataDatabase();
    394   RegisterApp(kOrigin.host(), app_root);
    395 
    396   CreateRemoteFile(app_root, "foo", "data");
    397   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
    398 
    399   EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
    400       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    401                  SYNC_FILE_TYPE_DIRECTORY),
    402       URL(kOrigin, "foo")));
    403 
    404   // There should exist both file and folder on remote.
    405   ScopedVector<google_apis::ResourceEntry> entries =
    406       GetResourceEntriesForParentAndTitle(app_root, "foo");
    407   ASSERT_EQ(2u, entries.size());
    408   EXPECT_EQ(google_apis::ResourceEntry::ENTRY_KIND_FILE, entries[0]->kind());
    409   EXPECT_EQ(google_apis::ResourceEntry::ENTRY_KIND_FOLDER, entries[1]->kind());
    410 }
    411 
    412 TEST_F(LocalToRemoteSyncerTest, Conflict_CreateFileOnFile) {
    413   const GURL kOrigin("chrome-extension://example");
    414   const std::string sync_root = CreateSyncRoot();
    415   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
    416   InitializeMetadataDatabase();
    417   RegisterApp(kOrigin.host(), app_root);
    418 
    419   CreateRemoteFile(app_root, "foo", "data");
    420   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
    421 
    422   EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
    423       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    424                  SYNC_FILE_TYPE_FILE),
    425       URL(kOrigin, "foo")));
    426 
    427   // There should exist both files on remote.
    428   ScopedVector<google_apis::ResourceEntry> entries =
    429       GetResourceEntriesForParentAndTitle(app_root, "foo");
    430   ASSERT_EQ(2u, entries.size());
    431   EXPECT_EQ(google_apis::ResourceEntry::ENTRY_KIND_FILE, entries[0]->kind());
    432   EXPECT_EQ(google_apis::ResourceEntry::ENTRY_KIND_FILE, entries[1]->kind());
    433 }
    434 
    435 TEST_F(LocalToRemoteSyncerTest, Conflict_UpdateDeleteOnFile) {
    436   const GURL kOrigin("chrome-extension://example");
    437   const std::string sync_root = CreateSyncRoot();
    438   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
    439   InitializeMetadataDatabase();
    440   RegisterApp(kOrigin.host(), app_root);
    441 
    442   const std::string file_id = CreateRemoteFile(app_root, "foo", "data");
    443   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
    444   EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC,
    445             RunRemoteToLocalSyncerUntilIdle());
    446 
    447   DeleteResource(file_id);
    448 
    449   EXPECT_EQ(SYNC_STATUS_FILE_BUSY, RunLocalToRemoteSyncer(
    450       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    451                  SYNC_FILE_TYPE_FILE),
    452       URL(kOrigin, "foo")));
    453   EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
    454       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    455                  SYNC_FILE_TYPE_FILE),
    456       URL(kOrigin, "foo")));
    457 
    458   ScopedVector<google_apis::ResourceEntry> entries =
    459       GetResourceEntriesForParentAndTitle(app_root, "foo");
    460   ASSERT_EQ(1u, entries.size());
    461   EXPECT_EQ(google_apis::ResourceEntry::ENTRY_KIND_FILE, entries[0]->kind());
    462   EXPECT_TRUE(!entries[0]->deleted());
    463   EXPECT_NE(file_id, entries[0]->resource_id());
    464 }
    465 
    466 TEST_F(LocalToRemoteSyncerTest, Conflict_CreateDeleteOnFile) {
    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   const std::string file_id = CreateRemoteFile(app_root, "foo", "data");
    474   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
    475   EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC,
    476             RunRemoteToLocalSyncerUntilIdle());
    477 
    478   DeleteResource(file_id);
    479 
    480   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
    481 
    482   EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
    483       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    484                  SYNC_FILE_TYPE_FILE),
    485       URL(kOrigin, "foo")));
    486 
    487   ScopedVector<google_apis::ResourceEntry> entries =
    488       GetResourceEntriesForParentAndTitle(app_root, "foo");
    489   ASSERT_EQ(1u, entries.size());
    490   EXPECT_EQ(google_apis::ResourceEntry::ENTRY_KIND_FILE, entries[0]->kind());
    491   EXPECT_TRUE(!entries[0]->deleted());
    492   EXPECT_NE(file_id, entries[0]->resource_id());
    493 }
    494 
    495 TEST_F(LocalToRemoteSyncerTest, Conflict_CreateFolderOnFolder) {
    496   const GURL kOrigin("chrome-extension://example");
    497   const std::string sync_root = CreateSyncRoot();
    498   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
    499   InitializeMetadataDatabase();
    500   RegisterApp(kOrigin.host(), app_root);
    501 
    502   const std::string folder_id = CreateRemoteFolder(app_root, "foo");
    503 
    504   EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
    505       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    506                  SYNC_FILE_TYPE_DIRECTORY),
    507       URL(kOrigin, "foo")));
    508 
    509   ScopedVector<google_apis::ResourceEntry> entries =
    510       GetResourceEntriesForParentAndTitle(app_root, "foo");
    511   ASSERT_EQ(2u, entries.size());
    512   EXPECT_EQ(google_apis::ResourceEntry::ENTRY_KIND_FOLDER, entries[0]->kind());
    513   EXPECT_EQ(google_apis::ResourceEntry::ENTRY_KIND_FOLDER, entries[1]->kind());
    514   EXPECT_TRUE(!entries[0]->deleted());
    515   EXPECT_TRUE(!entries[1]->deleted());
    516   EXPECT_TRUE(folder_id == entries[0]->resource_id() ||
    517               folder_id == entries[1]->resource_id());
    518 
    519   TrackerIDSet trackers;
    520   EXPECT_TRUE(GetMetadataDatabase()->FindTrackersByFileID(
    521       folder_id, &trackers));
    522   EXPECT_EQ(1u, trackers.size());
    523   ASSERT_TRUE(trackers.has_active());
    524 }
    525 
    526 TEST_F(LocalToRemoteSyncerTest, AppRootDeletion) {
    527   const GURL kOrigin("chrome-extension://example");
    528   const std::string sync_root = CreateSyncRoot();
    529   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
    530   InitializeMetadataDatabase();
    531   RegisterApp(kOrigin.host(), app_root);
    532 
    533   DeleteResource(app_root);
    534   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
    535   EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC,
    536             RunRemoteToLocalSyncerUntilIdle());
    537 
    538   EXPECT_EQ(SYNC_STATUS_UNKNOWN_ORIGIN, RunLocalToRemoteSyncer(
    539       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    540                  SYNC_FILE_TYPE_DIRECTORY),
    541       URL(kOrigin, "foo")));
    542 
    543   // SyncEngine will re-register the app and resurrect the app root later.
    544 }
    545 
    546 }  // namespace drive_backend
    547 }  // namespace sync_file_system
    548