remote_to_local_syncer_unittest.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
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 "base/thread_task_runner_handle.h" 15#include "chrome/browser/drive/drive_uploader.h" 16#include "chrome/browser/drive/fake_drive_service.h" 17#include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h" 18#include "chrome/browser/sync_file_system/drive_backend/drive_backend_test_util.h" 19#include "chrome/browser/sync_file_system/drive_backend/fake_drive_service_helper.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/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/gdata_errorcode.h" 31#include "testing/gtest/include/gtest/gtest.h" 32#include "third_party/leveldatabase/src/helpers/memenv/memenv.h" 33#include "third_party/leveldatabase/src/include/leveldb/env.h" 34 35namespace sync_file_system { 36namespace drive_backend { 37 38namespace { 39 40storage::FileSystemURL URL(const GURL& origin, const std::string& path) { 41 return CreateSyncableFileSystemURL( 42 origin, base::FilePath::FromUTF8Unsafe(path)); 43} 44 45} // namespace 46 47class RemoteToLocalSyncerTest : public testing::Test { 48 public: 49 typedef FakeRemoteChangeProcessor::URLToFileChangesMap URLToFileChangesMap; 50 51 RemoteToLocalSyncerTest() 52 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {} 53 virtual ~RemoteToLocalSyncerTest() {} 54 55 virtual void SetUp() OVERRIDE { 56 ASSERT_TRUE(database_dir_.CreateUniqueTempDir()); 57 in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default())); 58 59 scoped_ptr<drive::FakeDriveService> 60 fake_drive_service(new drive::FakeDriveService); 61 62 scoped_ptr<drive::DriveUploaderInterface> 63 drive_uploader(new drive::DriveUploader( 64 fake_drive_service.get(), 65 base::ThreadTaskRunnerHandle::Get().get())); 66 fake_drive_helper_.reset( 67 new FakeDriveServiceHelper(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 /* max_parallel_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 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 storage::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 storage::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->RunPreflight(SyncTaskToken::CreateForTesting( 186 CreateResultReceiver(&status))); 187 base::RunLoop().RunUntilIdle(); 188 return status; 189 } 190 191 SyncStatusCode RunSyncerUntilIdle() { 192 const int kRetryLimit = 100; 193 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 194 int count = 0; 195 do { 196 if (count++ > kRetryLimit) 197 return status; 198 status = RunSyncer(); 199 } while (status == SYNC_STATUS_OK || 200 status == SYNC_STATUS_RETRY); 201 return status; 202 } 203 204 SyncStatusCode RunSyncerAndPromoteUntilIdle() { 205 const int kRetryLimit = 100; 206 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 207 MetadataDatabase* metadata_database = context_->GetMetadataDatabase(); 208 int count = 0; 209 do { 210 if (count++ > kRetryLimit) 211 return status; 212 status = RunSyncer(); 213 } while (status == SYNC_STATUS_OK || 214 status == SYNC_STATUS_RETRY || 215 metadata_database->PromoteLowerPriorityTrackersToNormal()); 216 return status; 217 } 218 219 SyncStatusCode ListChanges() { 220 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 221 sync_task_manager_->ScheduleSyncTask( 222 FROM_HERE, 223 scoped_ptr<SyncTask>(new ListChangesTask(context_.get())), 224 SyncTaskManager::PRIORITY_MED, 225 CreateResultReceiver(&status)); 226 base::RunLoop().RunUntilIdle(); 227 return status; 228 } 229 230 void AppendExpectedChange(const storage::FileSystemURL& url, 231 FileChange::ChangeType change_type, 232 SyncFileType file_type) { 233 expected_changes_[url].push_back(FileChange(change_type, file_type)); 234 } 235 236 void VerifyConsistency() { 237 remote_change_processor_->VerifyConsistency(expected_changes_); 238 } 239 240 private: 241 content::TestBrowserThreadBundle thread_bundle_; 242 base::ScopedTempDir database_dir_; 243 scoped_ptr<leveldb::Env> in_memory_env_; 244 245 scoped_ptr<SyncEngineContext> context_; 246 scoped_ptr<FakeDriveServiceHelper> fake_drive_helper_; 247 scoped_ptr<FakeRemoteChangeProcessor> remote_change_processor_; 248 249 scoped_ptr<SyncTaskManager> sync_task_manager_; 250 251 URLToFileChangesMap expected_changes_; 252 253 DISALLOW_COPY_AND_ASSIGN(RemoteToLocalSyncerTest); 254}; 255 256TEST_F(RemoteToLocalSyncerTest, AddNewFile) { 257 const GURL kOrigin("chrome-extension://example"); 258 const std::string sync_root = CreateSyncRoot(); 259 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 260 InitializeMetadataDatabase(); 261 RegisterApp(kOrigin.host(), app_root); 262 263 const std::string folder1 = CreateRemoteFolder(app_root, "folder1"); 264 const std::string file1 = CreateRemoteFile(app_root, "file1", "data1"); 265 const std::string folder2 = CreateRemoteFolder(folder1, "folder2"); 266 const std::string file2 = CreateRemoteFile(folder1, "file2", "data2"); 267 268 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerAndPromoteUntilIdle()); 269 270 // Create expected changes. 271 // TODO(nhiroki): Clean up creating URL part. 272 AppendExpectedChange(URL(kOrigin, "folder1"), 273 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 274 SYNC_FILE_TYPE_DIRECTORY); 275 AppendExpectedChange(URL(kOrigin, "file1"), 276 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 277 SYNC_FILE_TYPE_FILE); 278 AppendExpectedChange(URL(kOrigin, "folder1/folder2"), 279 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 280 SYNC_FILE_TYPE_DIRECTORY); 281 AppendExpectedChange(URL(kOrigin, "folder1/file2"), 282 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 283 SYNC_FILE_TYPE_FILE); 284 285 VerifyConsistency(); 286 287 EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker()); 288} 289 290TEST_F(RemoteToLocalSyncerTest, DeleteFile) { 291 const GURL kOrigin("chrome-extension://example"); 292 const std::string sync_root = CreateSyncRoot(); 293 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 294 InitializeMetadataDatabase(); 295 RegisterApp(kOrigin.host(), app_root); 296 297 const std::string folder = CreateRemoteFolder(app_root, "folder"); 298 const std::string file = CreateRemoteFile(app_root, "file", "data"); 299 300 AppendExpectedChange(URL(kOrigin, "folder"), 301 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 302 SYNC_FILE_TYPE_DIRECTORY); 303 AppendExpectedChange(URL(kOrigin, "file"), 304 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 305 SYNC_FILE_TYPE_FILE); 306 307 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerAndPromoteUntilIdle()); 308 VerifyConsistency(); 309 310 DeleteRemoteFile(folder); 311 DeleteRemoteFile(file); 312 313 AppendExpectedChange(URL(kOrigin, "folder"), 314 FileChange::FILE_CHANGE_DELETE, 315 SYNC_FILE_TYPE_UNKNOWN); 316 AppendExpectedChange(URL(kOrigin, "file"), 317 FileChange::FILE_CHANGE_DELETE, 318 SYNC_FILE_TYPE_UNKNOWN); 319 320 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 321 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle()); 322 VerifyConsistency(); 323 324 EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker()); 325} 326 327TEST_F(RemoteToLocalSyncerTest, DeleteNestedFiles) { 328 const GURL kOrigin("chrome-extension://example"); 329 const std::string sync_root = CreateSyncRoot(); 330 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 331 InitializeMetadataDatabase(); 332 RegisterApp(kOrigin.host(), app_root); 333 334 const std::string folder1 = CreateRemoteFolder(app_root, "folder1"); 335 const std::string file1 = CreateRemoteFile(app_root, "file1", "data1"); 336 const std::string folder2 = CreateRemoteFolder(folder1, "folder2"); 337 const std::string file2 = CreateRemoteFile(folder1, "file2", "data2"); 338 339 AppendExpectedChange(URL(kOrigin, "folder1"), 340 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 341 SYNC_FILE_TYPE_DIRECTORY); 342 AppendExpectedChange(URL(kOrigin, "file1"), 343 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 344 SYNC_FILE_TYPE_FILE); 345 AppendExpectedChange(URL(kOrigin, "folder1/folder2"), 346 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 347 SYNC_FILE_TYPE_DIRECTORY); 348 AppendExpectedChange(URL(kOrigin, "folder1/file2"), 349 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 350 SYNC_FILE_TYPE_FILE); 351 352 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerAndPromoteUntilIdle()); 353 VerifyConsistency(); 354 355 DeleteRemoteFile(folder1); 356 357 AppendExpectedChange(URL(kOrigin, "folder1"), 358 FileChange::FILE_CHANGE_DELETE, 359 SYNC_FILE_TYPE_UNKNOWN); 360 // Changes for descendant files ("folder2" and "file2") should be ignored. 361 362 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 363 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle()); 364 VerifyConsistency(); 365 366 EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker()); 367} 368 369TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFileOnFolder) { 370 const GURL kOrigin("chrome-extension://example"); 371 const std::string sync_root = CreateSyncRoot(); 372 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 373 InitializeMetadataDatabase(); 374 RegisterApp(kOrigin.host(), app_root); 375 376 CreateLocalFolder(URL(kOrigin, "folder")); 377 CreateRemoteFile(app_root, "folder", "data"); 378 379 // Folder-File conflict happens. File creation should be ignored. 380 381 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 382 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle()); 383 VerifyConsistency(); 384 385 // Tracker for the remote file should has low priority. 386 EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL)); 387 EXPECT_TRUE(GetMetadataDatabase()->HasLowPriorityDirtyTracker()); 388} 389 390TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFile) { 391 const GURL kOrigin("chrome-extension://example"); 392 const std::string sync_root = CreateSyncRoot(); 393 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 394 InitializeMetadataDatabase(); 395 RegisterApp(kOrigin.host(), app_root); 396 397 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle()); 398 VerifyConsistency(); 399 400 CreateLocalFile(URL(kOrigin, "file")); 401 CreateRemoteFolder(app_root, "file"); 402 403 // File-Folder conflict happens. Folder should override the existing file. 404 AppendExpectedChange(URL(kOrigin, "file"), 405 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 406 SYNC_FILE_TYPE_DIRECTORY); 407 408 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 409 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle()); 410 VerifyConsistency(); 411 412 EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker()); 413} 414 415TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFolder) { 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 CreateLocalFolder(URL(kOrigin, "folder")); 423 CreateRemoteFolder(app_root, "folder"); 424 425 // Folder-Folder conflict happens. Folder creation should be ignored. 426 427 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 428 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle()); 429 VerifyConsistency(); 430 431 EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker()); 432} 433 434TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFileOnFile) { 435 const GURL kOrigin("chrome-extension://example"); 436 const std::string sync_root = CreateSyncRoot(); 437 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 438 InitializeMetadataDatabase(); 439 RegisterApp(kOrigin.host(), app_root); 440 441 CreateLocalFile(URL(kOrigin, "file")); 442 CreateRemoteFile(app_root, "file", "data"); 443 444 // File-File conflict happens. File creation should be ignored. 445 446 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 447 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle()); 448 VerifyConsistency(); 449 450 // Tracker for the remote file should be lowered. 451 EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL)); 452 EXPECT_TRUE(GetMetadataDatabase()->HasLowPriorityDirtyTracker()); 453} 454 455TEST_F(RemoteToLocalSyncerTest, Conflict_CreateNestedFolderOnFile) { 456 const GURL kOrigin("chrome-extension://example"); 457 const std::string sync_root = CreateSyncRoot(); 458 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 459 InitializeMetadataDatabase(); 460 RegisterApp(kOrigin.host(), app_root); 461 462 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle()); 463 VerifyConsistency(); 464 465 const std::string folder = CreateRemoteFolder(app_root, "folder"); 466 CreateLocalFile(URL(kOrigin, "/folder")); 467 CreateRemoteFile(folder, "file", "data"); 468 469 // File-Folder conflict happens. Folder should override the existing file. 470 AppendExpectedChange(URL(kOrigin, "/folder"), 471 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 472 SYNC_FILE_TYPE_DIRECTORY); 473 474 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 475 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle()); 476 VerifyConsistency(); 477} 478 479TEST_F(RemoteToLocalSyncerTest, AppRootDeletion) { 480 const GURL kOrigin("chrome-extension://example"); 481 const std::string sync_root = CreateSyncRoot(); 482 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 483 InitializeMetadataDatabase(); 484 RegisterApp(kOrigin.host(), app_root); 485 486 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle()); 487 VerifyConsistency(); 488 489 DeleteRemoteFile(app_root); 490 491 AppendExpectedChange(URL(kOrigin, "/"), 492 FileChange::FILE_CHANGE_DELETE, 493 SYNC_FILE_TYPE_UNKNOWN); 494 495 EXPECT_EQ(SYNC_STATUS_OK, ListChanges()); 496 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle()); 497 VerifyConsistency(); 498 499 // SyncEngine will re-register the app and resurrect the app root later. 500} 501 502} // namespace drive_backend 503} // namespace sync_file_system 504