remote_to_local_syncer_unittest.cc revision 6d86b77056ed63eb6871182f42a9fd5f07550f90
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( 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 233TEST_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 267TEST_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 304TEST_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 346TEST_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 367TEST_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 392TEST_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 411TEST_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 432TEST_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 456TEST_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