Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / sync_file_system / drive_backend / conflict_resolver_unittest.cc
blobf68bb0383a56a17d4dd5deaa15e16a7120641e88
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.
5 #include "chrome/browser/sync_file_system/drive_backend/conflict_resolver.h"
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/file_util.h"
10 #include "base/files/scoped_temp_dir.h"
11 #include "base/run_loop.h"
12 #include "chrome/browser/drive/drive_uploader.h"
13 #include "chrome/browser/drive/fake_drive_service.h"
14 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
15 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_test_util.h"
16 #include "chrome/browser/sync_file_system/drive_backend/fake_drive_service_helper.h"
17 #include "chrome/browser/sync_file_system/drive_backend/list_changes_task.h"
18 #include "chrome/browser/sync_file_system/drive_backend/local_to_remote_syncer.h"
19 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
20 #include "chrome/browser/sync_file_system/drive_backend/remote_to_local_syncer.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_uploader.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"
31 namespace sync_file_system {
32 namespace drive_backend {
34 namespace {
36 fileapi::FileSystemURL URL(const GURL& origin,
37 const std::string& path) {
38 return CreateSyncableFileSystemURL(
39 origin, base::FilePath::FromUTF8Unsafe(path));
42 } // namespace
44 class ConflictResolverTest : public testing::Test,
45 public SyncEngineContext {
46 public:
47 typedef FakeRemoteChangeProcessor::URLToFileChangesMap URLToFileChangesMap;
49 ConflictResolverTest()
50 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
51 virtual ~ConflictResolverTest() {}
53 virtual void SetUp() OVERRIDE {
54 ASSERT_TRUE(database_dir_.CreateUniqueTempDir());
56 fake_drive_service_.reset(new FakeDriveServiceWrapper);
57 ASSERT_TRUE(fake_drive_service_->LoadAccountMetadataForWapi(
58 "sync_file_system/account_metadata.json"));
59 ASSERT_TRUE(fake_drive_service_->LoadResourceListForWapi(
60 "gdata/empty_feed.json"));
62 drive_uploader_.reset(new FakeDriveUploader(fake_drive_service_.get()));
63 fake_drive_helper_.reset(new FakeDriveServiceHelper(
64 fake_drive_service_.get(), drive_uploader_.get(),
65 kSyncRootFolderTitle));
66 fake_remote_change_processor_.reset(new FakeRemoteChangeProcessor);
68 RegisterSyncableFileSystem();
71 virtual void TearDown() OVERRIDE {
72 RevokeSyncableFileSystem();
74 fake_remote_change_processor_.reset();
75 metadata_database_.reset();
76 fake_drive_helper_.reset();
77 drive_uploader_.reset();
78 fake_drive_service_.reset();
79 base::RunLoop().RunUntilIdle();
82 void InitializeMetadataDatabase() {
83 SyncEngineInitializer initializer(this,
84 base::MessageLoopProxy::current(),
85 fake_drive_service_.get(),
86 database_dir_.path());
87 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
88 initializer.Run(CreateResultReceiver(&status));
89 base::RunLoop().RunUntilIdle();
90 EXPECT_EQ(SYNC_STATUS_OK, status);
91 metadata_database_ = initializer.PassMetadataDatabase();
94 void RegisterApp(const std::string& app_id,
95 const std::string& app_root_folder_id) {
96 SyncStatusCode status = SYNC_STATUS_FAILED;
97 metadata_database_->RegisterApp(app_id, app_root_folder_id,
98 CreateResultReceiver(&status));
99 base::RunLoop().RunUntilIdle();
100 EXPECT_EQ(SYNC_STATUS_OK, status);
103 virtual drive::DriveServiceInterface* GetDriveService() OVERRIDE {
104 return fake_drive_service_.get();
107 virtual drive::DriveUploaderInterface* GetDriveUploader() OVERRIDE {
108 return drive_uploader_.get();
111 virtual MetadataDatabase* GetMetadataDatabase() OVERRIDE {
112 return metadata_database_.get();
115 virtual RemoteChangeProcessor* GetRemoteChangeProcessor() OVERRIDE {
116 return fake_remote_change_processor_.get();
119 virtual base::SequencedTaskRunner* GetBlockingTaskRunner() OVERRIDE {
120 return base::MessageLoopProxy::current().get();
123 protected:
124 std::string CreateSyncRoot() {
125 std::string sync_root_folder_id;
126 EXPECT_EQ(google_apis::HTTP_CREATED,
127 fake_drive_helper_->AddOrphanedFolder(
128 kSyncRootFolderTitle, &sync_root_folder_id));
129 return sync_root_folder_id;
132 std::string CreateRemoteFolder(const std::string& parent_folder_id,
133 const std::string& title) {
134 std::string folder_id;
135 EXPECT_EQ(google_apis::HTTP_CREATED,
136 fake_drive_helper_->AddFolder(
137 parent_folder_id, title, &folder_id));
138 return folder_id;
141 std::string CreateRemoteFile(const std::string& parent_folder_id,
142 const std::string& title,
143 const std::string& content) {
144 std::string file_id;
145 EXPECT_EQ(google_apis::HTTP_SUCCESS,
146 fake_drive_helper_->AddFile(
147 parent_folder_id, title, content, &file_id));
148 return file_id;
151 void CreateLocalFile(const fileapi::FileSystemURL& url) {
152 fake_remote_change_processor_->UpdateLocalFileMetadata(
153 url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
154 SYNC_FILE_TYPE_FILE));
157 google_apis::GDataErrorCode AddFileToFolder(
158 const std::string& parent_folder_id,
159 const std::string& file_id) {
160 google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
161 fake_drive_service_->AddResourceToDirectory(
162 parent_folder_id, file_id,
163 CreateResultReceiver(&error));
164 base::RunLoop().RunUntilIdle();
165 return error;
168 int CountParents(const std::string& file_id) {
169 scoped_ptr<google_apis::ResourceEntry> entry;
170 EXPECT_EQ(google_apis::HTTP_SUCCESS,
171 fake_drive_helper_->GetResourceEntry(file_id, &entry));
172 int count = 0;
173 const ScopedVector<google_apis::Link>& links = entry->links();
174 for (ScopedVector<google_apis::Link>::const_iterator itr = links.begin();
175 itr != links.end(); ++itr) {
176 if ((*itr)->type() == google_apis::Link::LINK_PARENT)
177 ++count;
179 return count;
182 SyncStatusCode RunRemoteToLocalSyncer() {
183 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
184 scoped_ptr<RemoteToLocalSyncer> syncer(new RemoteToLocalSyncer(this));
185 syncer->Run(CreateResultReceiver(&status));
186 base::RunLoop().RunUntilIdle();
187 return status;
190 SyncStatusCode RunLocalToRemoteSyncer(
191 const fileapi::FileSystemURL& url,
192 const FileChange& file_change) {
193 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
194 base::FilePath local_path = base::FilePath(FILE_PATH_LITERAL("dummy"));
195 if (file_change.IsAddOrUpdate())
196 CreateTemporaryFileInDir(database_dir_.path(), &local_path);
197 scoped_ptr<LocalToRemoteSyncer> syncer(new LocalToRemoteSyncer(
198 this, SyncFileMetadata(file_change.file_type(), 0, base::Time()),
199 file_change, local_path, url));
200 syncer->Run(CreateResultReceiver(&status));
201 base::RunLoop().RunUntilIdle();
202 if (status == SYNC_STATUS_OK)
203 fake_remote_change_processor_->ClearLocalChanges(url);
204 return status;
207 void RunRemoteToLocalSyncerUntilIdle() {
208 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
209 while (status != SYNC_STATUS_NO_CHANGE_TO_SYNC)
210 status = RunRemoteToLocalSyncer();
213 SyncStatusCode RunConflictResolver() {
214 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
215 ConflictResolver resolver(this);
216 resolver.Run(CreateResultReceiver(&status));
217 base::RunLoop().RunUntilIdle();
218 return status;
221 SyncStatusCode ListChanges() {
222 ListChangesTask list_changes(this);
223 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
224 list_changes.Run(CreateResultReceiver(&status));
225 base::RunLoop().RunUntilIdle();
226 return status;
229 ScopedVector<google_apis::ResourceEntry>
230 GetResourceEntriesForParentAndTitle(const std::string& parent_folder_id,
231 const std::string& title) {
232 ScopedVector<google_apis::ResourceEntry> entries;
233 EXPECT_EQ(google_apis::HTTP_SUCCESS,
234 fake_drive_helper_->SearchByTitle(
235 parent_folder_id, title, &entries));
236 return entries.Pass();
239 void VerifyConflictResolution(const std::string& parent_folder_id,
240 const std::string& title,
241 const std::string& primary_file_id,
242 google_apis::DriveEntryKind kind) {
243 ScopedVector<google_apis::ResourceEntry> entries;
244 EXPECT_EQ(google_apis::HTTP_SUCCESS,
245 fake_drive_helper_->SearchByTitle(
246 parent_folder_id, title, &entries));
247 ASSERT_EQ(1u, entries.size());
248 EXPECT_EQ(primary_file_id, entries[0]->resource_id());
249 EXPECT_EQ(kind, entries[0]->kind());
252 void VerifyLocalChangeConsistency(
253 const URLToFileChangesMap& expected_changes) {
254 fake_remote_change_processor_->VerifyConsistency(expected_changes);
257 private:
258 content::TestBrowserThreadBundle thread_bundle_;
259 base::ScopedTempDir database_dir_;
261 scoped_ptr<FakeDriveServiceWrapper> fake_drive_service_;
262 scoped_ptr<FakeDriveUploader> drive_uploader_;
263 scoped_ptr<FakeDriveServiceHelper> fake_drive_helper_;
264 scoped_ptr<MetadataDatabase> metadata_database_;
265 scoped_ptr<FakeRemoteChangeProcessor> fake_remote_change_processor_;
267 DISALLOW_COPY_AND_ASSIGN(ConflictResolverTest);
270 TEST_F(ConflictResolverTest, NoFileToBeResolved) {
271 const GURL kOrigin("chrome-extension://example");
272 const std::string sync_root = CreateSyncRoot();
273 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
274 InitializeMetadataDatabase();
275 RegisterApp(kOrigin.host(), app_root);
276 RunRemoteToLocalSyncerUntilIdle();
278 EXPECT_EQ(SYNC_STATUS_NO_CONFLICT, RunConflictResolver());
281 TEST_F(ConflictResolverTest, ResolveConflict_Files) {
282 const GURL kOrigin("chrome-extension://example");
283 const std::string sync_root = CreateSyncRoot();
284 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
285 InitializeMetadataDatabase();
286 RegisterApp(kOrigin.host(), app_root);
287 RunRemoteToLocalSyncerUntilIdle();
289 const std::string kTitle = "foo";
290 const std::string primary = CreateRemoteFile(app_root, kTitle, "data1");
291 CreateRemoteFile(app_root, kTitle, "data2");
292 CreateRemoteFile(app_root, kTitle, "data3");
293 CreateRemoteFile(app_root, kTitle, "data4");
294 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
295 RunRemoteToLocalSyncerUntilIdle();
297 ScopedVector<google_apis::ResourceEntry> entries =
298 GetResourceEntriesForParentAndTitle(app_root, kTitle);
299 ASSERT_EQ(4u, entries.size());
301 // Only primary file should survive.
302 EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
303 VerifyConflictResolution(app_root, kTitle, primary,
304 google_apis::ENTRY_KIND_FILE);
307 TEST_F(ConflictResolverTest, ResolveConflict_Folders) {
308 const GURL kOrigin("chrome-extension://example");
309 const std::string sync_root = CreateSyncRoot();
310 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
311 InitializeMetadataDatabase();
312 RegisterApp(kOrigin.host(), app_root);
313 RunRemoteToLocalSyncerUntilIdle();
315 const std::string kTitle = "foo";
316 const std::string primary = CreateRemoteFolder(app_root, kTitle);
317 CreateRemoteFolder(app_root, kTitle);
318 CreateRemoteFolder(app_root, kTitle);
319 CreateRemoteFolder(app_root, kTitle);
320 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
321 RunRemoteToLocalSyncerUntilIdle();
323 ScopedVector<google_apis::ResourceEntry> entries =
324 GetResourceEntriesForParentAndTitle(app_root, kTitle);
325 ASSERT_EQ(4u, entries.size());
327 // Only primary file should survive.
328 EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
329 VerifyConflictResolution(app_root, kTitle, primary,
330 google_apis::ENTRY_KIND_FOLDER);
333 TEST_F(ConflictResolverTest, ResolveConflict_FilesAndFolders) {
334 const GURL kOrigin("chrome-extension://example");
335 const std::string sync_root = CreateSyncRoot();
336 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
337 InitializeMetadataDatabase();
338 RegisterApp(kOrigin.host(), app_root);
339 RunRemoteToLocalSyncerUntilIdle();
341 const std::string kTitle = "foo";
342 CreateRemoteFile(app_root, kTitle, "data");
343 const std::string primary = CreateRemoteFolder(app_root, kTitle);
344 CreateRemoteFile(app_root, kTitle, "data2");
345 CreateRemoteFolder(app_root, kTitle);
346 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
347 RunRemoteToLocalSyncerUntilIdle();
349 ScopedVector<google_apis::ResourceEntry> entries =
350 GetResourceEntriesForParentAndTitle(app_root, kTitle);
351 ASSERT_EQ(4u, entries.size());
353 // Only primary file should survive.
354 EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
355 VerifyConflictResolution(app_root, kTitle, primary,
356 google_apis::ENTRY_KIND_FOLDER);
359 TEST_F(ConflictResolverTest, ResolveConflict_RemoteFolderOnLocalFile) {
360 const GURL kOrigin("chrome-extension://example");
361 const std::string sync_root = CreateSyncRoot();
362 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
363 InitializeMetadataDatabase();
364 RegisterApp(kOrigin.host(), app_root);
365 RunRemoteToLocalSyncerUntilIdle();
367 const std::string kTitle = "foo";
368 fileapi::FileSystemURL kURL = URL(kOrigin, kTitle);
370 // Create a file on local and sync it.
371 CreateLocalFile(kURL);
372 RunLocalToRemoteSyncer(
373 kURL,
374 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, SYNC_FILE_TYPE_FILE));
376 // Create a folder on remote and sync it.
377 const std::string primary = CreateRemoteFolder(app_root, kTitle);
378 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
379 RunRemoteToLocalSyncerUntilIdle();
381 ScopedVector<google_apis::ResourceEntry> entries =
382 GetResourceEntriesForParentAndTitle(app_root, kTitle);
383 ASSERT_EQ(2u, entries.size());
385 // Run conflict resolver. Only primary file should survive.
386 EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
387 VerifyConflictResolution(app_root, kTitle, primary,
388 google_apis::ENTRY_KIND_FOLDER);
390 // Continue to run remote-to-local sync.
391 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
392 RunRemoteToLocalSyncerUntilIdle();
394 // Verify that the local side has been synced to the same state
395 // (i.e. file deletion and folder creation).
396 URLToFileChangesMap expected_changes;
397 expected_changes[kURL].push_back(
398 FileChange(FileChange::FILE_CHANGE_DELETE,
399 SYNC_FILE_TYPE_UNKNOWN));
400 expected_changes[kURL].push_back(
401 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
402 SYNC_FILE_TYPE_DIRECTORY));
403 VerifyLocalChangeConsistency(expected_changes);
406 TEST_F(ConflictResolverTest, ResolveConflict_RemoteNestedFolderOnLocalFile) {
407 const GURL kOrigin("chrome-extension://example");
408 const std::string sync_root = CreateSyncRoot();
409 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
410 InitializeMetadataDatabase();
411 RegisterApp(kOrigin.host(), app_root);
412 RunRemoteToLocalSyncerUntilIdle();
414 const std::string kTitle = "foo";
415 fileapi::FileSystemURL kURL = URL(kOrigin, kTitle);
417 // Create a file on local and sync it.
418 CreateLocalFile(kURL);
419 RunLocalToRemoteSyncer(
420 kURL,
421 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, SYNC_FILE_TYPE_FILE));
423 // Create a folder and subfolder in it on remote, and sync it.
424 const std::string primary = CreateRemoteFolder(app_root, kTitle);
425 CreateRemoteFolder(primary, "nested");
426 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
427 RunRemoteToLocalSyncerUntilIdle();
429 ScopedVector<google_apis::ResourceEntry> entries =
430 GetResourceEntriesForParentAndTitle(app_root, kTitle);
431 ASSERT_EQ(2u, entries.size());
433 // Run conflict resolver. Only primary file should survive.
434 EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
435 VerifyConflictResolution(app_root, kTitle, primary,
436 google_apis::ENTRY_KIND_FOLDER);
438 // Continue to run remote-to-local sync.
439 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
440 RunRemoteToLocalSyncerUntilIdle();
442 // Verify that the local side has been synced to the same state
443 // (i.e. file deletion and folders creation).
444 URLToFileChangesMap expected_changes;
445 expected_changes[kURL].push_back(
446 FileChange(FileChange::FILE_CHANGE_DELETE,
447 SYNC_FILE_TYPE_UNKNOWN));
448 expected_changes[kURL].push_back(
449 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
450 SYNC_FILE_TYPE_DIRECTORY));
451 expected_changes[URL(kOrigin, "foo/nested")].push_back(
452 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
453 SYNC_FILE_TYPE_DIRECTORY));
454 VerifyLocalChangeConsistency(expected_changes);
457 TEST_F(ConflictResolverTest, ResolveMultiParents_File) {
458 const GURL kOrigin("chrome-extension://example");
459 const std::string sync_root = CreateSyncRoot();
460 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
461 InitializeMetadataDatabase();
462 RegisterApp(kOrigin.host(), app_root);
463 RunRemoteToLocalSyncerUntilIdle();
465 const std::string primary = CreateRemoteFolder(app_root, "primary");
466 const std::string file = CreateRemoteFile(primary, "file", "data");
467 ASSERT_EQ(google_apis::HTTP_SUCCESS,
468 AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary1"), file));
469 ASSERT_EQ(google_apis::HTTP_SUCCESS,
470 AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary2"), file));
471 ASSERT_EQ(google_apis::HTTP_SUCCESS,
472 AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary3"), file));
474 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
475 RunRemoteToLocalSyncerUntilIdle();
477 EXPECT_EQ(4, CountParents(file));
479 EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
481 EXPECT_EQ(1, CountParents(file));
484 TEST_F(ConflictResolverTest, ResolveMultiParents_Folder) {
485 const GURL kOrigin("chrome-extension://example");
486 const std::string sync_root = CreateSyncRoot();
487 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
488 InitializeMetadataDatabase();
489 RegisterApp(kOrigin.host(), app_root);
490 RunRemoteToLocalSyncerUntilIdle();
492 const std::string primary = CreateRemoteFolder(app_root, "primary");
493 const std::string file = CreateRemoteFolder(primary, "folder");
494 ASSERT_EQ(google_apis::HTTP_SUCCESS,
495 AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary1"), file));
496 ASSERT_EQ(google_apis::HTTP_SUCCESS,
497 AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary2"), file));
498 ASSERT_EQ(google_apis::HTTP_SUCCESS,
499 AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary3"), file));
501 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
502 RunRemoteToLocalSyncerUntilIdle();
504 EXPECT_EQ(4, CountParents(file));
506 EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
508 EXPECT_EQ(1, CountParents(file));
511 } // namespace drive_backend
512 } // namespace sync_file_system