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