remote_to_local_syncer_unittest.cc revision f2477e01787aa58f445919b809d89e252beef54f
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/google_apis/gdata_errorcode.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/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_v1/fake_drive_service_helper.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 "testing/gtest/include/gtest/gtest.h" 29 30namespace sync_file_system { 31namespace drive_backend { 32 33namespace { 34 35fileapi::FileSystemURL URL(const GURL& origin, 36 const std::string& path) { 37 return CreateSyncableFileSystemURL( 38 origin, base::FilePath::FromUTF8Unsafe(path)); 39} 40 41} // namespace 42 43class RemoteToLocalSyncerTest : public testing::Test, 44 public SyncEngineContext { 45 public: 46 typedef FakeRemoteChangeProcessor::URLToFileChangesMap URLToFileChangesMap; 47 48 RemoteToLocalSyncerTest() 49 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {} 50 virtual ~RemoteToLocalSyncerTest() {} 51 52 virtual void SetUp() OVERRIDE { 53 ASSERT_TRUE(database_dir_.CreateUniqueTempDir()); 54 55 fake_drive_service_.reset(new drive::FakeDriveService); 56 ASSERT_TRUE(fake_drive_service_->LoadAccountMetadataForWapi( 57 "sync_file_system/account_metadata.json")); 58 ASSERT_TRUE(fake_drive_service_->LoadResourceListForWapi( 59 "gdata/empty_feed.json")); 60 61 drive_uploader_.reset(new drive::DriveUploader( 62 fake_drive_service_.get(), base::MessageLoopProxy::current().get())); 63 fake_drive_helper_.reset(new FakeDriveServiceHelper( 64 fake_drive_service_.get(), drive_uploader_.get())); 65 fake_remote_change_processor_.reset(new FakeRemoteChangeProcessor); 66 67 RegisterSyncableFileSystem(); 68 } 69 70 virtual void TearDown() OVERRIDE { 71 RevokeSyncableFileSystem(); 72 73 fake_remote_change_processor_.reset(); 74 metadata_database_.reset(); 75 fake_drive_helper_.reset(); 76 drive_uploader_.reset(); 77 fake_drive_service_.reset(); 78 base::RunLoop().RunUntilIdle(); 79 } 80 81 void InitializeMetadataDatabase() { 82 SyncEngineInitializer initializer(base::MessageLoopProxy::current(), 83 fake_drive_service_.get(), 84 database_dir_.path()); 85 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 86 initializer.Run(CreateResultReceiver(&status)); 87 base::RunLoop().RunUntilIdle(); 88 EXPECT_EQ(SYNC_STATUS_OK, status); 89 metadata_database_ = initializer.PassMetadataDatabase(); 90 } 91 92 void RegisterApp(const std::string& app_id, 93 const std::string& app_root_folder_id) { 94 SyncStatusCode status = SYNC_STATUS_FAILED; 95 metadata_database_->RegisterApp(app_id, app_root_folder_id, 96 CreateResultReceiver(&status)); 97 base::RunLoop().RunUntilIdle(); 98 EXPECT_EQ(SYNC_STATUS_OK, status); 99 } 100 101 virtual drive::DriveServiceInterface* GetDriveService() OVERRIDE { 102 return fake_drive_service_.get(); 103 } 104 105 virtual drive::DriveUploaderInterface* GetDriveUploader() OVERRIDE { 106 return drive_uploader_.get(); 107 } 108 109 virtual MetadataDatabase* GetMetadataDatabase() OVERRIDE { 110 return metadata_database_.get(); 111 } 112 113 virtual RemoteChangeProcessor* GetRemoteChangeProcessor() OVERRIDE { 114 return fake_remote_change_processor_.get(); 115 } 116 117 virtual base::SequencedTaskRunner* GetBlockingTaskRunner() OVERRIDE { 118 return base::MessageLoopProxy::current().get(); 119 } 120 121 protected: 122 std::string CreateSyncRoot() { 123 std::string sync_root_folder_id; 124 EXPECT_EQ(google_apis::HTTP_CREATED, 125 fake_drive_helper_->AddOrphanedFolder( 126 kSyncRootFolderTitle, &sync_root_folder_id)); 127 return sync_root_folder_id; 128 } 129 130 std::string CreateRemoteFolder(const std::string& parent_folder_id, 131 const std::string& app_id) { 132 std::string folder_id; 133 EXPECT_EQ(google_apis::HTTP_CREATED, 134 fake_drive_helper_->AddFolder( 135 parent_folder_id, app_id, &folder_id)); 136 return folder_id; 137 } 138 139 std::string CreateRemoteFile(const std::string& parent_folder_id, 140 const std::string& title, 141 const std::string& content) { 142 std::string file_id; 143 EXPECT_EQ(google_apis::HTTP_SUCCESS, 144 fake_drive_helper_->AddFile( 145 parent_folder_id, title, content, &file_id)); 146 return file_id; 147 } 148 149 void DeleteRemoteFile(const std::string& file_id) { 150 EXPECT_EQ(google_apis::HTTP_SUCCESS, 151 fake_drive_helper_->RemoveResource(file_id)); 152 } 153 154 void CreateLocalFolder(const fileapi::FileSystemURL& url) { 155 fake_remote_change_processor_->UpdateLocalFileMetadata( 156 url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 157 SYNC_FILE_TYPE_DIRECTORY)); 158 } 159 160 void CreateLocalFile(const fileapi::FileSystemURL& url) { 161 fake_remote_change_processor_->UpdateLocalFileMetadata( 162 url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 163 SYNC_FILE_TYPE_FILE)); 164 } 165 166 SyncStatusCode RunSyncer() { 167 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 168 scoped_ptr<RemoteToLocalSyncer> syncer( 169 new RemoteToLocalSyncer(this, RemoteToLocalSyncer::PRIORITY_NORMAL)); 170 syncer->Run(CreateResultReceiver(&status)); 171 base::RunLoop().RunUntilIdle(); 172 return status; 173 } 174 175 void RunSyncerUntilIdle() { 176 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 177 while (status != SYNC_STATUS_NO_CHANGE_TO_SYNC) 178 status = RunSyncer(); 179 } 180 181 SyncStatusCode ListChanges() { 182 ListChangesTask list_changes(this); 183 SyncStatusCode status = SYNC_STATUS_UNKNOWN; 184 list_changes.Run(CreateResultReceiver(&status)); 185 base::RunLoop().RunUntilIdle(); 186 return status; 187 } 188 189 void AppendExpectedChange(const fileapi::FileSystemURL& url, 190 FileChange::ChangeType change_type, 191 SyncFileType file_type) { 192 expected_changes_[url].push_back(FileChange(change_type, file_type)); 193 } 194 195 void VerifyConsistency() { 196 URLToFileChangesMap applied_changes = 197 fake_remote_change_processor_->GetAppliedRemoteChanges(); 198 EXPECT_EQ(expected_changes_.size(), applied_changes.size()); 199 200 for (URLToFileChangesMap::const_iterator itr = applied_changes.begin(); 201 itr != applied_changes.end(); ++itr) { 202 const fileapi::FileSystemURL& url = itr->first; 203 URLToFileChangesMap::const_iterator found = expected_changes_.find(url); 204 if (found == expected_changes_.end()) { 205 EXPECT_TRUE(found != expected_changes_.end()); 206 continue; 207 } 208 209 if (itr->second.empty() || found->second.empty()) { 210 EXPECT_TRUE(!itr->second.empty()); 211 EXPECT_TRUE(!found->second.empty()); 212 continue; 213 } 214 215 EXPECT_EQ(found->second.back().change(), 216 itr->second.back().change()) << url.DebugString(); 217 EXPECT_EQ(found->second.back().file_type(), 218 itr->second.back().file_type()) << url.DebugString(); 219 } 220 } 221 222 private: 223 content::TestBrowserThreadBundle thread_bundle_; 224 base::ScopedTempDir database_dir_; 225 226 scoped_ptr<drive::FakeDriveService> fake_drive_service_; 227 scoped_ptr<drive::DriveUploader> drive_uploader_; 228 scoped_ptr<FakeDriveServiceHelper> fake_drive_helper_; 229 scoped_ptr<MetadataDatabase> metadata_database_; 230 scoped_ptr<FakeRemoteChangeProcessor> fake_remote_change_processor_; 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, "/"), 254 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 255 SYNC_FILE_TYPE_DIRECTORY); 256 AppendExpectedChange(URL(kOrigin, "/folder1"), 257 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 258 SYNC_FILE_TYPE_DIRECTORY); 259 AppendExpectedChange(URL(kOrigin, "/file1"), 260 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 261 SYNC_FILE_TYPE_FILE); 262 AppendExpectedChange(URL(kOrigin, "/folder1/folder2"), 263 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 264 SYNC_FILE_TYPE_DIRECTORY); 265 AppendExpectedChange(URL(kOrigin, "/folder1/file2"), 266 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 267 SYNC_FILE_TYPE_FILE); 268 269 VerifyConsistency(); 270} 271 272TEST_F(RemoteToLocalSyncerTest, DeleteFile) { 273 const GURL kOrigin("chrome-extension://example"); 274 const std::string sync_root = CreateSyncRoot(); 275 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 276 InitializeMetadataDatabase(); 277 RegisterApp(kOrigin.host(), app_root); 278 279 const std::string folder = CreateRemoteFolder(app_root, "folder"); 280 const std::string file = CreateRemoteFile(app_root, "file", "data"); 281 282 AppendExpectedChange(URL(kOrigin, "/"), 283 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 284 SYNC_FILE_TYPE_DIRECTORY); 285 AppendExpectedChange(URL(kOrigin, "/folder"), 286 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 287 SYNC_FILE_TYPE_DIRECTORY); 288 AppendExpectedChange(URL(kOrigin, "/file"), 289 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 290 SYNC_FILE_TYPE_FILE); 291 292 RunSyncerUntilIdle(); 293 VerifyConsistency(); 294 295 DeleteRemoteFile(folder); 296 DeleteRemoteFile(file); 297 298 AppendExpectedChange(URL(kOrigin, "/folder"), 299 FileChange::FILE_CHANGE_DELETE, 300 SYNC_FILE_TYPE_UNKNOWN); 301 AppendExpectedChange(URL(kOrigin, "/file"), 302 FileChange::FILE_CHANGE_DELETE, 303 SYNC_FILE_TYPE_UNKNOWN); 304 305 ListChanges(); 306 RunSyncerUntilIdle(); 307 VerifyConsistency(); 308} 309 310TEST_F(RemoteToLocalSyncerTest, DeleteNestedFiles) { 311 const GURL kOrigin("chrome-extension://example"); 312 const std::string sync_root = CreateSyncRoot(); 313 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 314 InitializeMetadataDatabase(); 315 RegisterApp(kOrigin.host(), app_root); 316 317 const std::string folder1 = CreateRemoteFolder(app_root, "folder1"); 318 const std::string file1 = CreateRemoteFile(app_root, "file1", "data1"); 319 const std::string folder2 = CreateRemoteFolder(folder1, "folder2"); 320 const std::string file2 = CreateRemoteFile(folder1, "file2", "data2"); 321 322 AppendExpectedChange(URL(kOrigin, "/"), 323 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 324 SYNC_FILE_TYPE_DIRECTORY); 325 AppendExpectedChange(URL(kOrigin, "/folder1"), 326 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 327 SYNC_FILE_TYPE_DIRECTORY); 328 AppendExpectedChange(URL(kOrigin, "/file1"), 329 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 330 SYNC_FILE_TYPE_FILE); 331 AppendExpectedChange(URL(kOrigin, "/folder1/folder2"), 332 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 333 SYNC_FILE_TYPE_DIRECTORY); 334 AppendExpectedChange(URL(kOrigin, "/folder1/file2"), 335 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 336 SYNC_FILE_TYPE_FILE); 337 338 RunSyncerUntilIdle(); 339 VerifyConsistency(); 340 341 DeleteRemoteFile(folder1); 342 343 AppendExpectedChange(URL(kOrigin, "/folder1"), 344 FileChange::FILE_CHANGE_DELETE, 345 SYNC_FILE_TYPE_UNKNOWN); 346 // Changes for descendant files ("folder2" and "file2") should be ignored. 347 348 ListChanges(); 349 RunSyncerUntilIdle(); 350 VerifyConsistency(); 351} 352 353TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFileOnFolder) { 354 const GURL kOrigin("chrome-extension://example"); 355 const std::string sync_root = CreateSyncRoot(); 356 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 357 InitializeMetadataDatabase(); 358 RegisterApp(kOrigin.host(), app_root); 359 360 AppendExpectedChange(URL(kOrigin, "/"), 361 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 362 SYNC_FILE_TYPE_DIRECTORY); 363 364 CreateLocalFolder(URL(kOrigin, "/folder")); 365 CreateRemoteFile(app_root, "folder", "data"); 366 367 // Folder-File conflict happens. File creation should be ignored. 368 369 ListChanges(); 370 RunSyncerUntilIdle(); 371 VerifyConsistency(); 372} 373 374TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFile) { 375 const GURL kOrigin("chrome-extension://example"); 376 const std::string sync_root = CreateSyncRoot(); 377 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 378 InitializeMetadataDatabase(); 379 RegisterApp(kOrigin.host(), app_root); 380 381 AppendExpectedChange(URL(kOrigin, "/"), 382 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 383 SYNC_FILE_TYPE_DIRECTORY); 384 385 RunSyncerUntilIdle(); 386 VerifyConsistency(); 387 388 CreateLocalFile(URL(kOrigin, "/file")); 389 CreateRemoteFolder(app_root, "file"); 390 391 // File-Folder conflict happens. Folder should override the existing file. 392 AppendExpectedChange(URL(kOrigin, "/file"), 393 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 394 SYNC_FILE_TYPE_DIRECTORY); 395 396 ListChanges(); 397 RunSyncerUntilIdle(); 398 VerifyConsistency(); 399} 400 401TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFolder) { 402 const GURL kOrigin("chrome-extension://example"); 403 const std::string sync_root = CreateSyncRoot(); 404 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host()); 405 InitializeMetadataDatabase(); 406 RegisterApp(kOrigin.host(), app_root); 407 408 AppendExpectedChange(URL(kOrigin, "/"), 409 FileChange::FILE_CHANGE_ADD_OR_UPDATE, 410 SYNC_FILE_TYPE_DIRECTORY); 411 412 CreateLocalFolder(URL(kOrigin, "/folder")); 413 CreateRemoteFolder(app_root, "folder"); 414 415 // Folder-Folder conflict happens. Folder creation should be ignored. 416 417 ListChanges(); 418 RunSyncerUntilIdle(); 419 VerifyConsistency(); 420} 421 422// TODO(nhiroki): Add file-file conflict case. 423 424} // namespace drive_backend 425} // namespace sync_file_system 426