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