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/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/fake_drive_service_helper.h"
     19 #include "chrome/browser/sync_file_system/drive_backend/list_changes_task.h"
     20 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
     21 #include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h"
     22 #include "chrome/browser/sync_file_system/drive_backend/sync_engine_initializer.h"
     23 #include "chrome/browser/sync_file_system/drive_backend/sync_task_manager.h"
     24 #include "chrome/browser/sync_file_system/fake_remote_change_processor.h"
     25 #include "chrome/browser/sync_file_system/sync_file_system_test_util.h"
     26 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
     27 #include "content/public/test/test_browser_thread_bundle.h"
     28 #include "google_apis/drive/gdata_errorcode.h"
     29 #include "testing/gtest/include/gtest/gtest.h"
     30 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
     31 #include "third_party/leveldatabase/src/include/leveldb/env.h"
     32 
     33 namespace sync_file_system {
     34 namespace drive_backend {
     35 
     36 namespace {
     37 
     38 fileapi::FileSystemURL URL(const GURL& origin,
     39                            const std::string& path) {
     40   return CreateSyncableFileSystemURL(
     41       origin, base::FilePath::FromUTF8Unsafe(path));
     42 }
     43 
     44 }  // namespace
     45 
     46 class RemoteToLocalSyncerTest : public testing::Test {
     47  public:
     48   typedef FakeRemoteChangeProcessor::URLToFileChangesMap URLToFileChangesMap;
     49 
     50   RemoteToLocalSyncerTest()
     51       : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
     52   virtual ~RemoteToLocalSyncerTest() {}
     53 
     54   virtual void SetUp() OVERRIDE {
     55     ASSERT_TRUE(database_dir_.CreateUniqueTempDir());
     56     in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
     57 
     58     scoped_ptr<drive::FakeDriveService>
     59         fake_drive_service(new drive::FakeDriveService);
     60 
     61     scoped_ptr<drive::DriveUploaderInterface>
     62         drive_uploader(new drive::DriveUploader(
     63             fake_drive_service.get(),
     64             base::MessageLoopProxy::current().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 /* max_parallel_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(&RemoteToLocalSyncerTest::DidInitializeMetadataDatabase,
    108                    base::Unretained(this),
    109                    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 
    123   void RegisterApp(const std::string& app_id,
    124                    const std::string& app_root_folder_id) {
    125     SyncStatusCode status = SYNC_STATUS_FAILED;
    126     context_->GetMetadataDatabase()->RegisterApp(app_id, app_root_folder_id,
    127                                                  CreateResultReceiver(&status));
    128     base::RunLoop().RunUntilIdle();
    129     EXPECT_EQ(SYNC_STATUS_OK, status);
    130   }
    131 
    132   MetadataDatabase* GetMetadataDatabase() {
    133     return context_->GetMetadataDatabase();
    134   }
    135 
    136  protected:
    137   std::string CreateSyncRoot() {
    138     std::string sync_root_folder_id;
    139     EXPECT_EQ(google_apis::HTTP_CREATED,
    140               fake_drive_helper_->AddOrphanedFolder(
    141                   kSyncRootFolderTitle, &sync_root_folder_id));
    142     return sync_root_folder_id;
    143   }
    144 
    145   std::string CreateRemoteFolder(const std::string& parent_folder_id,
    146                                  const std::string& title) {
    147     std::string folder_id;
    148     EXPECT_EQ(google_apis::HTTP_CREATED,
    149               fake_drive_helper_->AddFolder(
    150                   parent_folder_id, title, &folder_id));
    151     return folder_id;
    152   }
    153 
    154   std::string CreateRemoteFile(const std::string& parent_folder_id,
    155                                const std::string& title,
    156                                const std::string& content) {
    157     std::string file_id;
    158     EXPECT_EQ(google_apis::HTTP_SUCCESS,
    159               fake_drive_helper_->AddFile(
    160                   parent_folder_id, title, content, &file_id));
    161     return file_id;
    162   }
    163 
    164   void DeleteRemoteFile(const std::string& file_id) {
    165     EXPECT_EQ(google_apis::HTTP_NO_CONTENT,
    166               fake_drive_helper_->DeleteResource(file_id));
    167   }
    168 
    169   void CreateLocalFolder(const fileapi::FileSystemURL& url) {
    170     remote_change_processor_->UpdateLocalFileMetadata(
    171         url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    172                         SYNC_FILE_TYPE_DIRECTORY));
    173   }
    174 
    175   void CreateLocalFile(const fileapi::FileSystemURL& url) {
    176     remote_change_processor_->UpdateLocalFileMetadata(
    177         url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    178                         SYNC_FILE_TYPE_FILE));
    179   }
    180 
    181   SyncStatusCode RunSyncer() {
    182     SyncStatusCode status = SYNC_STATUS_UNKNOWN;
    183     scoped_ptr<RemoteToLocalSyncer>
    184         syncer(new RemoteToLocalSyncer(context_.get()));
    185     syncer->RunExclusive(CreateResultReceiver(&status));
    186     base::RunLoop().RunUntilIdle();
    187     return status;
    188   }
    189 
    190   void RunSyncerUntilIdle() {
    191     SyncStatusCode status = SYNC_STATUS_UNKNOWN;
    192     while (status != SYNC_STATUS_NO_CHANGE_TO_SYNC)
    193       status = RunSyncer();
    194   }
    195 
    196   SyncStatusCode ListChanges() {
    197     SyncStatusCode status = SYNC_STATUS_UNKNOWN;
    198     sync_task_manager_->ScheduleSyncTask(
    199         FROM_HERE,
    200         scoped_ptr<SyncTask>(new ListChangesTask(context_.get())),
    201         SyncTaskManager::PRIORITY_MED,
    202         CreateResultReceiver(&status));
    203     base::RunLoop().RunUntilIdle();
    204     return status;
    205   }
    206 
    207   void AppendExpectedChange(const fileapi::FileSystemURL& url,
    208                             FileChange::ChangeType change_type,
    209                             SyncFileType file_type) {
    210     expected_changes_[url].push_back(FileChange(change_type, file_type));
    211   }
    212 
    213   void VerifyConsistency() {
    214     remote_change_processor_->VerifyConsistency(expected_changes_);
    215   }
    216 
    217  private:
    218   content::TestBrowserThreadBundle thread_bundle_;
    219   base::ScopedTempDir database_dir_;
    220   scoped_ptr<leveldb::Env> in_memory_env_;
    221 
    222   scoped_ptr<SyncEngineContext> context_;
    223   scoped_ptr<FakeDriveServiceHelper> fake_drive_helper_;
    224   scoped_ptr<FakeRemoteChangeProcessor> remote_change_processor_;
    225 
    226   scoped_ptr<SyncTaskManager> sync_task_manager_;
    227 
    228   URLToFileChangesMap expected_changes_;
    229 
    230   DISALLOW_COPY_AND_ASSIGN(RemoteToLocalSyncerTest);
    231 };
    232 
    233 TEST_F(RemoteToLocalSyncerTest, AddNewFile) {
    234   const GURL kOrigin("chrome-extension://example");
    235   const std::string sync_root = CreateSyncRoot();
    236   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
    237   InitializeMetadataDatabase();
    238   RegisterApp(kOrigin.host(), app_root);
    239 
    240   const std::string folder1 = CreateRemoteFolder(app_root, "folder1");
    241   const std::string file1 = CreateRemoteFile(app_root, "file1", "data1");
    242   const std::string folder2 = CreateRemoteFolder(folder1, "folder2");
    243   const std::string file2 = CreateRemoteFile(folder1, "file2", "data2");
    244 
    245   RunSyncerUntilIdle();
    246 
    247   // Create expected changes.
    248   // TODO(nhiroki): Clean up creating URL part.
    249   AppendExpectedChange(URL(kOrigin, "folder1"),
    250                        FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    251                        SYNC_FILE_TYPE_DIRECTORY);
    252   AppendExpectedChange(URL(kOrigin, "file1"),
    253                        FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    254                        SYNC_FILE_TYPE_FILE);
    255   AppendExpectedChange(URL(kOrigin, "folder1/folder2"),
    256                        FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    257                        SYNC_FILE_TYPE_DIRECTORY);
    258   AppendExpectedChange(URL(kOrigin, "folder1/file2"),
    259                        FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    260                        SYNC_FILE_TYPE_FILE);
    261 
    262   VerifyConsistency();
    263 
    264   EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
    265 }
    266 
    267 TEST_F(RemoteToLocalSyncerTest, DeleteFile) {
    268   const GURL kOrigin("chrome-extension://example");
    269   const std::string sync_root = CreateSyncRoot();
    270   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
    271   InitializeMetadataDatabase();
    272   RegisterApp(kOrigin.host(), app_root);
    273 
    274   const std::string folder = CreateRemoteFolder(app_root, "folder");
    275   const std::string file = CreateRemoteFile(app_root, "file", "data");
    276 
    277   AppendExpectedChange(URL(kOrigin, "folder"),
    278                        FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    279                        SYNC_FILE_TYPE_DIRECTORY);
    280   AppendExpectedChange(URL(kOrigin, "file"),
    281                        FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    282                        SYNC_FILE_TYPE_FILE);
    283 
    284   RunSyncerUntilIdle();
    285   VerifyConsistency();
    286 
    287   DeleteRemoteFile(folder);
    288   DeleteRemoteFile(file);
    289 
    290   AppendExpectedChange(URL(kOrigin, "folder"),
    291                        FileChange::FILE_CHANGE_DELETE,
    292                        SYNC_FILE_TYPE_UNKNOWN);
    293   AppendExpectedChange(URL(kOrigin, "file"),
    294                        FileChange::FILE_CHANGE_DELETE,
    295                        SYNC_FILE_TYPE_UNKNOWN);
    296 
    297   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
    298   RunSyncerUntilIdle();
    299   VerifyConsistency();
    300 
    301   EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
    302 }
    303 
    304 TEST_F(RemoteToLocalSyncerTest, DeleteNestedFiles) {
    305   const GURL kOrigin("chrome-extension://example");
    306   const std::string sync_root = CreateSyncRoot();
    307   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
    308   InitializeMetadataDatabase();
    309   RegisterApp(kOrigin.host(), app_root);
    310 
    311   const std::string folder1 = CreateRemoteFolder(app_root, "folder1");
    312   const std::string file1 = CreateRemoteFile(app_root, "file1", "data1");
    313   const std::string folder2 = CreateRemoteFolder(folder1, "folder2");
    314   const std::string file2 = CreateRemoteFile(folder1, "file2", "data2");
    315 
    316   AppendExpectedChange(URL(kOrigin, "folder1"),
    317                        FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    318                        SYNC_FILE_TYPE_DIRECTORY);
    319   AppendExpectedChange(URL(kOrigin, "file1"),
    320                        FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    321                        SYNC_FILE_TYPE_FILE);
    322   AppendExpectedChange(URL(kOrigin, "folder1/folder2"),
    323                        FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    324                        SYNC_FILE_TYPE_DIRECTORY);
    325   AppendExpectedChange(URL(kOrigin, "folder1/file2"),
    326                        FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    327                        SYNC_FILE_TYPE_FILE);
    328 
    329   RunSyncerUntilIdle();
    330   VerifyConsistency();
    331 
    332   DeleteRemoteFile(folder1);
    333 
    334   AppendExpectedChange(URL(kOrigin, "folder1"),
    335                        FileChange::FILE_CHANGE_DELETE,
    336                        SYNC_FILE_TYPE_UNKNOWN);
    337   // Changes for descendant files ("folder2" and "file2") should be ignored.
    338 
    339   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
    340   RunSyncerUntilIdle();
    341   VerifyConsistency();
    342 
    343   EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
    344 }
    345 
    346 TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFileOnFolder) {
    347   const GURL kOrigin("chrome-extension://example");
    348   const std::string sync_root = CreateSyncRoot();
    349   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
    350   InitializeMetadataDatabase();
    351   RegisterApp(kOrigin.host(), app_root);
    352 
    353   CreateLocalFolder(URL(kOrigin, "folder"));
    354   CreateRemoteFile(app_root, "folder", "data");
    355 
    356   // Folder-File conflict happens. File creation should be ignored.
    357 
    358   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
    359   RunSyncerUntilIdle();
    360   VerifyConsistency();
    361 
    362   // Tracker for the remote file should be lowered.
    363   EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
    364   EXPECT_TRUE(GetMetadataDatabase()->HasLowPriorityDirtyTracker());
    365 }
    366 
    367 TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFile) {
    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   RunSyncerUntilIdle();
    375   VerifyConsistency();
    376 
    377   CreateLocalFile(URL(kOrigin, "file"));
    378   CreateRemoteFolder(app_root, "file");
    379 
    380   // File-Folder conflict happens. Folder should override the existing file.
    381   AppendExpectedChange(URL(kOrigin, "file"),
    382                        FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    383                        SYNC_FILE_TYPE_DIRECTORY);
    384 
    385   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
    386   RunSyncerUntilIdle();
    387   VerifyConsistency();
    388 
    389   EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
    390 }
    391 
    392 TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFolder) {
    393   const GURL kOrigin("chrome-extension://example");
    394   const std::string sync_root = CreateSyncRoot();
    395   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
    396   InitializeMetadataDatabase();
    397   RegisterApp(kOrigin.host(), app_root);
    398 
    399   CreateLocalFolder(URL(kOrigin, "folder"));
    400   CreateRemoteFolder(app_root, "folder");
    401 
    402   // Folder-Folder conflict happens. Folder creation should be ignored.
    403 
    404   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
    405   RunSyncerUntilIdle();
    406   VerifyConsistency();
    407 
    408   EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
    409 }
    410 
    411 TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFileOnFile) {
    412   const GURL kOrigin("chrome-extension://example");
    413   const std::string sync_root = CreateSyncRoot();
    414   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
    415   InitializeMetadataDatabase();
    416   RegisterApp(kOrigin.host(), app_root);
    417 
    418   CreateLocalFile(URL(kOrigin, "file"));
    419   CreateRemoteFile(app_root, "file", "data");
    420 
    421   // File-File conflict happens. File creation should be ignored.
    422 
    423   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
    424   RunSyncerUntilIdle();
    425   VerifyConsistency();
    426 
    427   // Tracker for the remote file should be lowered.
    428   EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
    429   EXPECT_TRUE(GetMetadataDatabase()->HasLowPriorityDirtyTracker());
    430 }
    431 
    432 TEST_F(RemoteToLocalSyncerTest, Conflict_CreateNestedFolderOnFile) {
    433   const GURL kOrigin("chrome-extension://example");
    434   const std::string sync_root = CreateSyncRoot();
    435   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
    436   InitializeMetadataDatabase();
    437   RegisterApp(kOrigin.host(), app_root);
    438 
    439   RunSyncerUntilIdle();
    440   VerifyConsistency();
    441 
    442   const std::string folder = CreateRemoteFolder(app_root, "folder");
    443   CreateLocalFile(URL(kOrigin, "/folder"));
    444   CreateRemoteFile(folder, "file", "data");
    445 
    446   // File-Folder conflict happens. Folder should override the existing file.
    447   AppendExpectedChange(URL(kOrigin, "/folder"),
    448                        FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    449                        SYNC_FILE_TYPE_DIRECTORY);
    450 
    451   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
    452   RunSyncerUntilIdle();
    453   VerifyConsistency();
    454 }
    455 
    456 TEST_F(RemoteToLocalSyncerTest, AppRootDeletion) {
    457   const GURL kOrigin("chrome-extension://example");
    458   const std::string sync_root = CreateSyncRoot();
    459   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
    460   InitializeMetadataDatabase();
    461   RegisterApp(kOrigin.host(), app_root);
    462 
    463   RunSyncerUntilIdle();
    464   VerifyConsistency();
    465 
    466   DeleteRemoteFile(app_root);
    467 
    468   AppendExpectedChange(URL(kOrigin, "/"),
    469                        FileChange::FILE_CHANGE_DELETE,
    470                        SYNC_FILE_TYPE_UNKNOWN);
    471 
    472   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
    473   RunSyncerUntilIdle();
    474   VerifyConsistency();
    475 
    476   // SyncEngine will re-register the app and resurrect the app root later.
    477 }
    478 
    479 }  // namespace drive_backend
    480 }  // namespace sync_file_system
    481