remote_to_local_syncer_unittest.cc revision effb81e5f8246d0db0270817048dc992db66e9fb
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 33namespace sync_file_system { 34namespace drive_backend { 35 36namespace { 37 38fileapi::FileSystemURL URL(const GURL& origin, 39 const std::string& path) { 40 return CreateSyncableFileSystemURL( 41 origin, base::FilePath::FromUTF8Unsafe(path)); 42} 43 44} // namespace 45 46class 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 ASSERT_TRUE(fake_drive_service->LoadAccountMetadataForWapi( 61 "sync_file_system/account_metadata.json")); 62 ASSERT_TRUE(fake_drive_service->LoadResourceListForWapi( 63 "gdata/empty_feed.json")); 64 65 scoped_ptr<drive::DriveUploaderInterface> 66 drive_uploader(new drive::DriveUploader( 67 fake_drive_service.get(), 68 base::MessageLoopProxy::current().get())); 69 fake_drive_helper_.reset(new FakeDriveServiceHelper( 70 fake_drive_service.get(), drive_uploader.get(), 71 kSyncRootFolderTitle)); 72 fake_remote_change_processor_.reset(new FakeRemoteChangeProcessor); 73 74 context_.reset(new SyncEngineContext( 75 fake_drive_service.PassAs<drive::DriveServiceInterface>(), 76 drive_uploader.Pass(), base::MessageLoopProxy::current())); 77 context_->SetRemoteChangeProcessor(fake_remote_change_processor_.get()); 78 79 RegisterSyncableFileSystem(); 80 81 sync_task_manager_.reset(new SyncTaskManager( 82 base::WeakPtr<SyncTaskManager::Client>(), 83 10 /* max_parallel_task */)); 84 sync_task_manager_->Initialize(SYNC_STATUS_OK); 85 } 86 87 virtual void TearDown() OVERRIDE { 88 sync_task_manager_.reset(); 89 90 RevokeSyncableFileSystem(); 91 92 fake_remote_change_processor_.reset(); 93 fake_drive_helper_.reset(); 94 context_.reset(); 95 base::RunLoop().RunUntilIdle(); 96 } 97 98 void InitializeMetadataDatabase() { 99 SyncEngineInitializer* initializer = 100 new SyncEngineInitializer( 101 context_.get(), 102 base::MessageLoopProxy::current(), 103 context_->GetDriveService(), 104 database_dir_.path(), 105 in_memory_env_.get()); 106 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 107 sync_task_manager_->ScheduleSyncTask( 108 FROM_HERE, 109 scoped_ptr<SyncTask>(initializer), 110 SyncTaskManager::PRIORITY_MED, 111 base::Bind(&RemoteToLocalSyncerTest::DidInitializeMetadataDatabase, 112 base::Unretained(this), 113 initializer, &status)); 114 115 base::RunLoop().RunUntilIdle(); 116 EXPECT_EQ(SYNC_STATUS_OK, status); 117 } 118 119 void DidInitializeMetadataDatabase(SyncEngineInitializer* initializer, 120 SyncStatusCode* status_out, 121 SyncStatusCode status) { 122 *status_out = status; 123 context_->SetMetadataDatabase(initializer->PassMetadataDatabase()); 124 } 125 126 127 void RegisterApp(const std::string& app_id, 128 const std::string& app_root_folder_id) { 129 SyncStatusCode status = SYNC_STATUS_FAILED; 130 context_->GetMetadataDatabase()->RegisterApp(app_id, app_root_folder_id, 131 CreateResultReceiver(&status)); 132 base::RunLoop().RunUntilIdle(); 133 EXPECT_EQ(SYNC_STATUS_OK, status); 134 } 135 136 MetadataDatabase* GetMetadataDatabase() { 137 return context_->GetMetadataDatabase(); 138 } 139 140 protected: 141 std::string CreateSyncRoot() { 142 std::string sync_root_folder_id; 143 EXPECT_EQ(google_apis::HTTP_CREATED, 144 fake_drive_helper_->AddOrphanedFolder( 145 kSyncRootFolderTitle, &sync_root_folder_id)); 146 return sync_root_folder_id; 147 } 148 149 std::string CreateRemoteFolder(const std::string& parent_folder_id, 150 const std::string& title) { 151 std::string folder_id; 152 EXPECT_EQ(google_apis::HTTP_CREATED, 153 fake_drive_helper_->AddFolder( 154 parent_folder_id, title, &folder_id)); 155 return folder_id; 156 } 157 158 std::string CreateRemoteFile(const std::string& parent_folder_id, 159 const std::string& title, 160 const std::string& content) { 161 std::string file_id; 162 EXPECT_EQ(google_apis::HTTP_SUCCESS, 163 fake_drive_helper_->AddFile( 164 parent_folder_id, title, content, &file_id)); 165 return file_id; 166 } 167 168 void DeleteRemoteFile(const std::string& file_id) { 169 EXPECT_EQ(google_apis::HTTP_NO_CONTENT, 170 fake_drive_helper_->DeleteResource(file_id)); 171 } 172 173 void CreateLocalFolder(const fileapi::FileSystemURL& url) { 174 fake_remote_change_processor_->UpdateLocalFileMetadata( 175 url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 176 SYNC_FILE_TYPE_DIRECTORY)); 177 } 178 179 void CreateLocalFile(const fileapi::FileSystemURL& url) { 180 fake_remote_change_processor_->UpdateLocalFileMetadata( 181 url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 182 SYNC_FILE_TYPE_FILE)); 183 } 184 185 SyncStatusCode RunSyncer() { 186 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 187 scoped_ptr<RemoteToLocalSyncer> 188 syncer(new RemoteToLocalSyncer(context_.get())); 189 syncer->RunSequential(CreateResultReceiver(&status)); 190 base::RunLoop().RunUntilIdle(); 191 return status; 192 } 193 194 void RunSyncerUntilIdle() { 195 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 196 while (status != SYNC_STATUS_NO_CHANGE_TO_SYNC) 197 status = RunSyncer(); 198 } 199 200 SyncStatusCode ListChanges() { 201 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 202 sync_task_manager_->ScheduleSyncTask( 203 FROM_HERE, 204 scoped_ptr<SyncTask>(new ListChangesTask(context_.get())), 205 SyncTaskManager::PRIORITY_MED, 206 CreateResultReceiver(&status)); 207 base::RunLoop().RunUntilIdle(); 208 return status; 209 } 210 211 void AppendExpectedChange(const fileapi::FileSystemURL& url, 212 FileChange::ChangeType change_type, 213 SyncFileType file_type) { 214 expected_changes_[url].push_back(FileChange(change_type, file_type)); 215 } 216 217 void VerifyConsistency() { 218 fake_remote_change_processor_->VerifyConsistency(expected_changes_); 219 } 220 221 private: 222 content::TestBrowserThreadBundle thread_bundle_; 223 base::ScopedTempDir database_dir_; 224 scoped_ptr<leveldb::Env> in_memory_env_; 225 226 scoped_ptr<SyncEngineContext> context_; 227 scoped_ptr<FakeDriveServiceHelper> fake_drive_helper_; 228 scoped_ptr<FakeRemoteChangeProcessor> fake_remote_change_processor_; 229 230 scoped_ptr<SyncTaskManager> sync_task_manager_; 231 232 URLToFileChangesMap expected_changes_; 233 234 DISALLOW_COPY_AND_ASSIGN(RemoteToLocalSyncerTest); 235}; 236 237TEST_F(RemoteToLocalSyncerTest, AddNewFile) { 238 const GURL kOrigin("chrome-extension://example"); 239 const std::string sync_root = CreateSyncRoot(); 240 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 241 InitializeMetadataDatabase(); 242 RegisterApp(kOrigin.host(), app_root); 243 244 const std::string folder1 = CreateRemoteFolder(app_root, "folder1"); 245 const std::string file1 = CreateRemoteFile(app_root, "file1", "data1"); 246 const std::string folder2 = CreateRemoteFolder(folder1, "folder2"); 247 const std::string file2 = CreateRemoteFile(folder1, "file2", "data2"); 248 249 RunSyncerUntilIdle(); 250 251 // Create expected changes. 252 // TODO(nhiroki): Clean up creating URL part. 253 AppendExpectedChange(URL(kOrigin, "folder1"), 254 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 255 SYNC_FILE_TYPE_DIRECTORY); 256 AppendExpectedChange(URL(kOrigin, "file1"), 257 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 258 SYNC_FILE_TYPE_FILE); 259 AppendExpectedChange(URL(kOrigin, "folder1/folder2"), 260 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 261 SYNC_FILE_TYPE_DIRECTORY); 262 AppendExpectedChange(URL(kOrigin, "folder1/file2"), 263 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 264 SYNC_FILE_TYPE_FILE); 265 266 VerifyConsistency(); 267 268 EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker()); 269} 270 271TEST_F(RemoteToLocalSyncerTest, DeleteFile) { 272 const GURL kOrigin("chrome-extension://example"); 273 const std::string sync_root = CreateSyncRoot(); 274 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 275 InitializeMetadataDatabase(); 276 RegisterApp(kOrigin.host(), app_root); 277 278 const std::string folder = CreateRemoteFolder(app_root, "folder"); 279 const std::string file = CreateRemoteFile(app_root, "file", "data"); 280 281 AppendExpectedChange(URL(kOrigin, "folder"), 282 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 283 SYNC_FILE_TYPE_DIRECTORY); 284 AppendExpectedChange(URL(kOrigin, "file"), 285 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 286 SYNC_FILE_TYPE_FILE); 287 288 RunSyncerUntilIdle(); 289 VerifyConsistency(); 290 291 DeleteRemoteFile(folder); 292 DeleteRemoteFile(file); 293 294 AppendExpectedChange(URL(kOrigin, "folder"), 295 FileChange::FILE_CHANGE_DELETE, 296 SYNC_FILE_TYPE_UNKNOWN); 297 AppendExpectedChange(URL(kOrigin, "file"), 298 FileChange::FILE_CHANGE_DELETE, 299 SYNC_FILE_TYPE_UNKNOWN); 300 301 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 302 RunSyncerUntilIdle(); 303 VerifyConsistency(); 304 305 EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker()); 306} 307 308TEST_F(RemoteToLocalSyncerTest, DeleteNestedFiles) { 309 const GURL kOrigin("chrome-extension://example"); 310 const std::string sync_root = CreateSyncRoot(); 311 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 312 InitializeMetadataDatabase(); 313 RegisterApp(kOrigin.host(), app_root); 314 315 const std::string folder1 = CreateRemoteFolder(app_root, "folder1"); 316 const std::string file1 = CreateRemoteFile(app_root, "file1", "data1"); 317 const std::string folder2 = CreateRemoteFolder(folder1, "folder2"); 318 const std::string file2 = CreateRemoteFile(folder1, "file2", "data2"); 319 320 AppendExpectedChange(URL(kOrigin, "folder1"), 321 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 322 SYNC_FILE_TYPE_DIRECTORY); 323 AppendExpectedChange(URL(kOrigin, "file1"), 324 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 325 SYNC_FILE_TYPE_FILE); 326 AppendExpectedChange(URL(kOrigin, "folder1/folder2"), 327 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 328 SYNC_FILE_TYPE_DIRECTORY); 329 AppendExpectedChange(URL(kOrigin, "folder1/file2"), 330 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 331 SYNC_FILE_TYPE_FILE); 332 333 RunSyncerUntilIdle(); 334 VerifyConsistency(); 335 336 DeleteRemoteFile(folder1); 337 338 AppendExpectedChange(URL(kOrigin, "folder1"), 339 FileChange::FILE_CHANGE_DELETE, 340 SYNC_FILE_TYPE_UNKNOWN); 341 // Changes for descendant files ("folder2" and "file2") should be ignored. 342 343 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 344 RunSyncerUntilIdle(); 345 VerifyConsistency(); 346 347 EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker()); 348} 349 350TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFileOnFolder) { 351 const GURL kOrigin("chrome-extension://example"); 352 const std::string sync_root = CreateSyncRoot(); 353 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 354 InitializeMetadataDatabase(); 355 RegisterApp(kOrigin.host(), app_root); 356 357 CreateLocalFolder(URL(kOrigin, "folder")); 358 CreateRemoteFile(app_root, "folder", "data"); 359 360 // Folder-File conflict happens. File creation should be ignored. 361 362 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 363 RunSyncerUntilIdle(); 364 VerifyConsistency(); 365 366 // Tracker for the remote file should be lowered. 367 EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL)); 368 EXPECT_TRUE(GetMetadataDatabase()->HasLowPriorityDirtyTracker()); 369} 370 371TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFile) { 372 const GURL kOrigin("chrome-extension://example"); 373 const std::string sync_root = CreateSyncRoot(); 374 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 375 InitializeMetadataDatabase(); 376 RegisterApp(kOrigin.host(), app_root); 377 378 RunSyncerUntilIdle(); 379 VerifyConsistency(); 380 381 CreateLocalFile(URL(kOrigin, "file")); 382 CreateRemoteFolder(app_root, "file"); 383 384 // File-Folder conflict happens. Folder should override the existing file. 385 AppendExpectedChange(URL(kOrigin, "file"), 386 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 387 SYNC_FILE_TYPE_DIRECTORY); 388 389 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 390 RunSyncerUntilIdle(); 391 VerifyConsistency(); 392 393 EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker()); 394} 395 396TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFolder) { 397 const GURL kOrigin("chrome-extension://example"); 398 const std::string sync_root = CreateSyncRoot(); 399 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 400 InitializeMetadataDatabase(); 401 RegisterApp(kOrigin.host(), app_root); 402 403 CreateLocalFolder(URL(kOrigin, "folder")); 404 CreateRemoteFolder(app_root, "folder"); 405 406 // Folder-Folder conflict happens. Folder creation should be ignored. 407 408 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 409 RunSyncerUntilIdle(); 410 VerifyConsistency(); 411 412 EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker()); 413} 414 415TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFileOnFile) { 416 const GURL kOrigin("chrome-extension://example"); 417 const std::string sync_root = CreateSyncRoot(); 418 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 419 InitializeMetadataDatabase(); 420 RegisterApp(kOrigin.host(), app_root); 421 422 CreateLocalFile(URL(kOrigin, "file")); 423 CreateRemoteFile(app_root, "file", "data"); 424 425 // File-File conflict happens. File creation should be ignored. 426 427 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 428 RunSyncerUntilIdle(); 429 VerifyConsistency(); 430 431 // Tracker for the remote file should be lowered. 432 EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL)); 433 EXPECT_TRUE(GetMetadataDatabase()->HasLowPriorityDirtyTracker()); 434} 435 436TEST_F(RemoteToLocalSyncerTest, Conflict_CreateNestedFolderOnFile) { 437 const GURL kOrigin("chrome-extension://example"); 438 const std::string sync_root = CreateSyncRoot(); 439 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 440 InitializeMetadataDatabase(); 441 RegisterApp(kOrigin.host(), app_root); 442 443 RunSyncerUntilIdle(); 444 VerifyConsistency(); 445 446 const std::string folder = CreateRemoteFolder(app_root, "folder"); 447 CreateLocalFile(URL(kOrigin, "/folder")); 448 CreateRemoteFile(folder, "file", "data"); 449 450 // File-Folder conflict happens. Folder should override the existing file. 451 AppendExpectedChange(URL(kOrigin, "/folder"), 452 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 453 SYNC_FILE_TYPE_DIRECTORY); 454 455 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 456 RunSyncerUntilIdle(); 457 VerifyConsistency(); 458} 459 460TEST_F(RemoteToLocalSyncerTest, AppRootDeletion) { 461 const GURL kOrigin("chrome-extension://example"); 462 const std::string sync_root = CreateSyncRoot(); 463 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 464 InitializeMetadataDatabase(); 465 RegisterApp(kOrigin.host(), app_root); 466 467 RunSyncerUntilIdle(); 468 VerifyConsistency(); 469 470 DeleteRemoteFile(app_root); 471 472 AppendExpectedChange(URL(kOrigin, "/"), 473 FileChange::FILE_CHANGE_DELETE, 474 SYNC_FILE_TYPE_UNKNOWN); 475 476 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 477 RunSyncerUntilIdle(); 478 VerifyConsistency(); 479 480 // SyncEngine will re-register the app and resurrect the app root later. 481} 482 483} // namespace drive_backend 484} // namespace sync_file_system 485