Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync_file_system / drive_backend / conflict_resolver_unittest.cc
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/conflict_resolver.h"
6
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/fake_drive_uploader.h"
18 #include "chrome/browser/sync_file_system/drive_backend/list_changes_task.h"
19 #include "chrome/browser/sync_file_system/drive_backend/local_to_remote_syncer.h"
20 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
21 #include "chrome/browser/sync_file_system/drive_backend/remote_to_local_syncer.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/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
33 namespace sync_file_system {
34 namespace drive_backend {
35
36 namespace {
37
38 fileapi::FileSystemURL URL(const GURL& origin,
39                            const std::string& path) {
40   return CreateSyncableFileSystemURL(
41       origin, base::FilePath::FromUTF8Unsafe(path));
42 }
43
44 }  // namespace
45
46 class ConflictResolverTest : public testing::Test,
47                              public SyncEngineContext {
48  public:
49   typedef FakeRemoteChangeProcessor::URLToFileChangesMap URLToFileChangesMap;
50
51   ConflictResolverTest()
52       : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
53   virtual ~ConflictResolverTest() {}
54
55   virtual void SetUp() OVERRIDE {
56     ASSERT_TRUE(database_dir_.CreateUniqueTempDir());
57     in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
58
59     fake_drive_service_.reset(new FakeDriveServiceWrapper);
60     ASSERT_TRUE(fake_drive_service_->LoadAccountMetadataForWapi(
61         "sync_file_system/account_metadata.json"));
62     ASSERT_TRUE(fake_drive_service_->LoadResourceListForWapi(
63         "gdata/empty_feed.json"));
64
65     drive_uploader_.reset(new FakeDriveUploader(fake_drive_service_.get()));
66     fake_drive_helper_.reset(new FakeDriveServiceHelper(
67         fake_drive_service_.get(), drive_uploader_.get(),
68         kSyncRootFolderTitle));
69     fake_remote_change_processor_.reset(new FakeRemoteChangeProcessor);
70
71     RegisterSyncableFileSystem();
72   }
73
74   virtual void TearDown() OVERRIDE {
75     RevokeSyncableFileSystem();
76
77     fake_remote_change_processor_.reset();
78     metadata_database_.reset();
79     fake_drive_helper_.reset();
80     drive_uploader_.reset();
81     fake_drive_service_.reset();
82     base::RunLoop().RunUntilIdle();
83   }
84
85   void InitializeMetadataDatabase() {
86     SyncEngineInitializer initializer(this,
87                                       base::MessageLoopProxy::current(),
88                                       fake_drive_service_.get(),
89                                       database_dir_.path(),
90                                       in_memory_env_.get());
91     SyncStatusCode status = SYNC_STATUS_UNKNOWN;
92     initializer.Run(CreateResultReceiver(&status));
93     base::RunLoop().RunUntilIdle();
94     EXPECT_EQ(SYNC_STATUS_OK, status);
95     metadata_database_ = initializer.PassMetadataDatabase();
96   }
97
98   void RegisterApp(const std::string& app_id,
99                    const std::string& app_root_folder_id) {
100     SyncStatusCode status = SYNC_STATUS_FAILED;
101     metadata_database_->RegisterApp(app_id, app_root_folder_id,
102                                     CreateResultReceiver(&status));
103     base::RunLoop().RunUntilIdle();
104     EXPECT_EQ(SYNC_STATUS_OK, status);
105   }
106
107   virtual drive::DriveServiceInterface* GetDriveService() OVERRIDE {
108     return fake_drive_service_.get();
109   }
110
111   virtual drive::DriveUploaderInterface* GetDriveUploader() OVERRIDE {
112     return drive_uploader_.get();
113   }
114
115   virtual MetadataDatabase* GetMetadataDatabase() OVERRIDE {
116     return metadata_database_.get();
117   }
118
119   virtual RemoteChangeProcessor* GetRemoteChangeProcessor() OVERRIDE {
120     return fake_remote_change_processor_.get();
121   }
122
123   virtual base::SequencedTaskRunner* GetBlockingTaskRunner() OVERRIDE {
124     return base::MessageLoopProxy::current().get();
125   }
126
127  protected:
128   std::string CreateSyncRoot() {
129     std::string sync_root_folder_id;
130     EXPECT_EQ(google_apis::HTTP_CREATED,
131               fake_drive_helper_->AddOrphanedFolder(
132                   kSyncRootFolderTitle, &sync_root_folder_id));
133     return sync_root_folder_id;
134   }
135
136   std::string CreateRemoteFolder(const std::string& parent_folder_id,
137                                  const std::string& title) {
138     std::string folder_id;
139     EXPECT_EQ(google_apis::HTTP_CREATED,
140               fake_drive_helper_->AddFolder(
141                   parent_folder_id, title, &folder_id));
142     return folder_id;
143   }
144
145   std::string CreateRemoteFile(const std::string& parent_folder_id,
146                                const std::string& title,
147                                const std::string& content) {
148     std::string file_id;
149     EXPECT_EQ(google_apis::HTTP_SUCCESS,
150               fake_drive_helper_->AddFile(
151                   parent_folder_id, title, content, &file_id));
152     return file_id;
153   }
154
155   void CreateLocalFile(const fileapi::FileSystemURL& url) {
156     fake_remote_change_processor_->UpdateLocalFileMetadata(
157         url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
158                         SYNC_FILE_TYPE_FILE));
159   }
160
161   google_apis::GDataErrorCode AddFileToFolder(
162       const std::string& parent_folder_id,
163       const std::string& file_id) {
164     google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
165     fake_drive_service_->AddResourceToDirectory(
166         parent_folder_id, file_id,
167         CreateResultReceiver(&error));
168     base::RunLoop().RunUntilIdle();
169     return error;
170   }
171
172   int CountParents(const std::string& file_id) {
173     scoped_ptr<google_apis::ResourceEntry> entry;
174     EXPECT_EQ(google_apis::HTTP_SUCCESS,
175               fake_drive_helper_->GetResourceEntry(file_id, &entry));
176     int count = 0;
177     const ScopedVector<google_apis::Link>& links = entry->links();
178     for (ScopedVector<google_apis::Link>::const_iterator itr = links.begin();
179         itr != links.end(); ++itr) {
180       if ((*itr)->type() == google_apis::Link::LINK_PARENT)
181         ++count;
182     }
183     return count;
184   }
185
186   SyncStatusCode RunRemoteToLocalSyncer() {
187     SyncStatusCode status = SYNC_STATUS_UNKNOWN;
188     scoped_ptr<RemoteToLocalSyncer> syncer(new RemoteToLocalSyncer(this));
189     syncer->Run(CreateResultReceiver(&status));
190     base::RunLoop().RunUntilIdle();
191     return status;
192   }
193
194   SyncStatusCode RunLocalToRemoteSyncer(
195       const fileapi::FileSystemURL& url,
196       const FileChange& file_change) {
197     SyncStatusCode status = SYNC_STATUS_UNKNOWN;
198     base::FilePath local_path = base::FilePath(FILE_PATH_LITERAL("dummy"));
199     if (file_change.IsAddOrUpdate())
200       CreateTemporaryFileInDir(database_dir_.path(), &local_path);
201     scoped_ptr<LocalToRemoteSyncer> syncer(new LocalToRemoteSyncer(
202         this, SyncFileMetadata(file_change.file_type(), 0, base::Time()),
203         file_change, local_path, url));
204     syncer->Run(CreateResultReceiver(&status));
205     base::RunLoop().RunUntilIdle();
206     if (status == SYNC_STATUS_OK)
207       fake_remote_change_processor_->ClearLocalChanges(url);
208     return status;
209   }
210
211   void RunRemoteToLocalSyncerUntilIdle() {
212     SyncStatusCode status = SYNC_STATUS_UNKNOWN;
213     while (status != SYNC_STATUS_NO_CHANGE_TO_SYNC)
214       status = RunRemoteToLocalSyncer();
215   }
216
217   SyncStatusCode RunConflictResolver() {
218     SyncStatusCode status = SYNC_STATUS_UNKNOWN;
219     ConflictResolver resolver(this);
220     resolver.Run(CreateResultReceiver(&status));
221     base::RunLoop().RunUntilIdle();
222     return status;
223   }
224
225   SyncStatusCode ListChanges() {
226     ListChangesTask list_changes(this);
227     SyncStatusCode status = SYNC_STATUS_UNKNOWN;
228     list_changes.Run(CreateResultReceiver(&status));
229     base::RunLoop().RunUntilIdle();
230     return status;
231   }
232
233   ScopedVector<google_apis::ResourceEntry>
234   GetResourceEntriesForParentAndTitle(const std::string& parent_folder_id,
235                                       const std::string& title) {
236     ScopedVector<google_apis::ResourceEntry> entries;
237     EXPECT_EQ(google_apis::HTTP_SUCCESS,
238               fake_drive_helper_->SearchByTitle(
239                   parent_folder_id, title, &entries));
240     return entries.Pass();
241   }
242
243   void VerifyConflictResolution(const std::string& parent_folder_id,
244                                 const std::string& title,
245                                 const std::string& primary_file_id,
246                                 google_apis::DriveEntryKind kind) {
247     ScopedVector<google_apis::ResourceEntry> entries;
248     EXPECT_EQ(google_apis::HTTP_SUCCESS,
249               fake_drive_helper_->SearchByTitle(
250                   parent_folder_id, title, &entries));
251     ASSERT_EQ(1u, entries.size());
252     EXPECT_EQ(primary_file_id, entries[0]->resource_id());
253     EXPECT_EQ(kind, entries[0]->kind());
254   }
255
256   void VerifyLocalChangeConsistency(
257       const URLToFileChangesMap& expected_changes) {
258     fake_remote_change_processor_->VerifyConsistency(expected_changes);
259   }
260
261  private:
262   content::TestBrowserThreadBundle thread_bundle_;
263   base::ScopedTempDir database_dir_;
264   scoped_ptr<leveldb::Env> in_memory_env_;
265
266   scoped_ptr<FakeDriveServiceWrapper> fake_drive_service_;
267   scoped_ptr<FakeDriveUploader> drive_uploader_;
268   scoped_ptr<FakeDriveServiceHelper> fake_drive_helper_;
269   scoped_ptr<MetadataDatabase> metadata_database_;
270   scoped_ptr<FakeRemoteChangeProcessor> fake_remote_change_processor_;
271
272   DISALLOW_COPY_AND_ASSIGN(ConflictResolverTest);
273 };
274
275 TEST_F(ConflictResolverTest, NoFileToBeResolved) {
276   const GURL kOrigin("chrome-extension://example");
277   const std::string sync_root = CreateSyncRoot();
278   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
279   InitializeMetadataDatabase();
280   RegisterApp(kOrigin.host(), app_root);
281   RunRemoteToLocalSyncerUntilIdle();
282
283   EXPECT_EQ(SYNC_STATUS_NO_CONFLICT, RunConflictResolver());
284 }
285
286 TEST_F(ConflictResolverTest, ResolveConflict_Files) {
287   const GURL kOrigin("chrome-extension://example");
288   const std::string sync_root = CreateSyncRoot();
289   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
290   InitializeMetadataDatabase();
291   RegisterApp(kOrigin.host(), app_root);
292   RunRemoteToLocalSyncerUntilIdle();
293
294   const std::string kTitle = "foo";
295   const std::string primary = CreateRemoteFile(app_root, kTitle, "data1");
296   CreateRemoteFile(app_root, kTitle, "data2");
297   CreateRemoteFile(app_root, kTitle, "data3");
298   CreateRemoteFile(app_root, kTitle, "data4");
299   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
300   RunRemoteToLocalSyncerUntilIdle();
301
302   ScopedVector<google_apis::ResourceEntry> entries =
303       GetResourceEntriesForParentAndTitle(app_root, kTitle);
304   ASSERT_EQ(4u, entries.size());
305
306   // Only primary file should survive.
307   EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
308   VerifyConflictResolution(app_root, kTitle, primary,
309                            google_apis::ENTRY_KIND_FILE);
310 }
311
312 TEST_F(ConflictResolverTest, ResolveConflict_Folders) {
313   const GURL kOrigin("chrome-extension://example");
314   const std::string sync_root = CreateSyncRoot();
315   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
316   InitializeMetadataDatabase();
317   RegisterApp(kOrigin.host(), app_root);
318   RunRemoteToLocalSyncerUntilIdle();
319
320   const std::string kTitle = "foo";
321   const std::string primary = CreateRemoteFolder(app_root, kTitle);
322   CreateRemoteFolder(app_root, kTitle);
323   CreateRemoteFolder(app_root, kTitle);
324   CreateRemoteFolder(app_root, kTitle);
325   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
326   RunRemoteToLocalSyncerUntilIdle();
327
328   ScopedVector<google_apis::ResourceEntry> entries =
329       GetResourceEntriesForParentAndTitle(app_root, kTitle);
330   ASSERT_EQ(4u, entries.size());
331
332   // Only primary file should survive.
333   EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
334   VerifyConflictResolution(app_root, kTitle, primary,
335                            google_apis::ENTRY_KIND_FOLDER);
336 }
337
338 TEST_F(ConflictResolverTest, ResolveConflict_FilesAndFolders) {
339   const GURL kOrigin("chrome-extension://example");
340   const std::string sync_root = CreateSyncRoot();
341   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
342   InitializeMetadataDatabase();
343   RegisterApp(kOrigin.host(), app_root);
344   RunRemoteToLocalSyncerUntilIdle();
345
346   const std::string kTitle = "foo";
347   CreateRemoteFile(app_root, kTitle, "data");
348   const std::string primary = CreateRemoteFolder(app_root, kTitle);
349   CreateRemoteFile(app_root, kTitle, "data2");
350   CreateRemoteFolder(app_root, kTitle);
351   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
352   RunRemoteToLocalSyncerUntilIdle();
353
354   ScopedVector<google_apis::ResourceEntry> entries =
355       GetResourceEntriesForParentAndTitle(app_root, kTitle);
356   ASSERT_EQ(4u, entries.size());
357
358   // Only primary file should survive.
359   EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
360   VerifyConflictResolution(app_root, kTitle, primary,
361                            google_apis::ENTRY_KIND_FOLDER);
362 }
363
364 TEST_F(ConflictResolverTest, ResolveConflict_RemoteFolderOnLocalFile) {
365   const GURL kOrigin("chrome-extension://example");
366   const std::string sync_root = CreateSyncRoot();
367   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
368   InitializeMetadataDatabase();
369   RegisterApp(kOrigin.host(), app_root);
370   RunRemoteToLocalSyncerUntilIdle();
371
372   const std::string kTitle = "foo";
373   fileapi::FileSystemURL kURL = URL(kOrigin, kTitle);
374
375   // Create a file on local and sync it.
376   CreateLocalFile(kURL);
377   RunLocalToRemoteSyncer(
378       kURL,
379       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, SYNC_FILE_TYPE_FILE));
380
381   // Create a folder on remote and sync it.
382   const std::string primary = CreateRemoteFolder(app_root, kTitle);
383   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
384   RunRemoteToLocalSyncerUntilIdle();
385
386   ScopedVector<google_apis::ResourceEntry> entries =
387       GetResourceEntriesForParentAndTitle(app_root, kTitle);
388   ASSERT_EQ(2u, entries.size());
389
390   // Run conflict resolver. Only primary file should survive.
391   EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
392   VerifyConflictResolution(app_root, kTitle, primary,
393                            google_apis::ENTRY_KIND_FOLDER);
394
395   // Continue to run remote-to-local sync.
396   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
397   RunRemoteToLocalSyncerUntilIdle();
398
399   // Verify that the local side has been synced to the same state
400   // (i.e. file deletion and folder creation).
401   URLToFileChangesMap expected_changes;
402   expected_changes[kURL].push_back(
403       FileChange(FileChange::FILE_CHANGE_DELETE,
404                  SYNC_FILE_TYPE_UNKNOWN));
405   expected_changes[kURL].push_back(
406       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
407                  SYNC_FILE_TYPE_DIRECTORY));
408   VerifyLocalChangeConsistency(expected_changes);
409 }
410
411 TEST_F(ConflictResolverTest, ResolveConflict_RemoteNestedFolderOnLocalFile) {
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   RunRemoteToLocalSyncerUntilIdle();
418
419   const std::string kTitle = "foo";
420   fileapi::FileSystemURL kURL = URL(kOrigin, kTitle);
421
422   // Create a file on local and sync it.
423   CreateLocalFile(kURL);
424   RunLocalToRemoteSyncer(
425       kURL,
426       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, SYNC_FILE_TYPE_FILE));
427
428   // Create a folder and subfolder in it on remote, and sync it.
429   const std::string primary = CreateRemoteFolder(app_root, kTitle);
430   CreateRemoteFolder(primary, "nested");
431   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
432   RunRemoteToLocalSyncerUntilIdle();
433
434   ScopedVector<google_apis::ResourceEntry> entries =
435       GetResourceEntriesForParentAndTitle(app_root, kTitle);
436   ASSERT_EQ(2u, entries.size());
437
438   // Run conflict resolver. Only primary file should survive.
439   EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
440   VerifyConflictResolution(app_root, kTitle, primary,
441                            google_apis::ENTRY_KIND_FOLDER);
442
443   // Continue to run remote-to-local sync.
444   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
445   RunRemoteToLocalSyncerUntilIdle();
446
447   // Verify that the local side has been synced to the same state
448   // (i.e. file deletion and folders creation).
449   URLToFileChangesMap expected_changes;
450   expected_changes[kURL].push_back(
451       FileChange(FileChange::FILE_CHANGE_DELETE,
452                  SYNC_FILE_TYPE_UNKNOWN));
453   expected_changes[kURL].push_back(
454       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
455                  SYNC_FILE_TYPE_DIRECTORY));
456   expected_changes[URL(kOrigin, "foo/nested")].push_back(
457       FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
458                  SYNC_FILE_TYPE_DIRECTORY));
459   VerifyLocalChangeConsistency(expected_changes);
460 }
461
462 TEST_F(ConflictResolverTest, ResolveMultiParents_File) {
463   const GURL kOrigin("chrome-extension://example");
464   const std::string sync_root = CreateSyncRoot();
465   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
466   InitializeMetadataDatabase();
467   RegisterApp(kOrigin.host(), app_root);
468   RunRemoteToLocalSyncerUntilIdle();
469
470   const std::string primary = CreateRemoteFolder(app_root, "primary");
471   const std::string file = CreateRemoteFile(primary, "file", "data");
472   ASSERT_EQ(google_apis::HTTP_SUCCESS,
473             AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary1"), file));
474   ASSERT_EQ(google_apis::HTTP_SUCCESS,
475             AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary2"), file));
476   ASSERT_EQ(google_apis::HTTP_SUCCESS,
477             AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary3"), file));
478
479   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
480   RunRemoteToLocalSyncerUntilIdle();
481
482   EXPECT_EQ(4, CountParents(file));
483
484   EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
485
486   EXPECT_EQ(1, CountParents(file));
487 }
488
489 TEST_F(ConflictResolverTest, ResolveMultiParents_Folder) {
490   const GURL kOrigin("chrome-extension://example");
491   const std::string sync_root = CreateSyncRoot();
492   const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
493   InitializeMetadataDatabase();
494   RegisterApp(kOrigin.host(), app_root);
495   RunRemoteToLocalSyncerUntilIdle();
496
497   const std::string primary = CreateRemoteFolder(app_root, "primary");
498   const std::string file = CreateRemoteFolder(primary, "folder");
499   ASSERT_EQ(google_apis::HTTP_SUCCESS,
500             AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary1"), file));
501   ASSERT_EQ(google_apis::HTTP_SUCCESS,
502             AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary2"), file));
503   ASSERT_EQ(google_apis::HTTP_SUCCESS,
504             AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary3"), file));
505
506   EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
507   RunRemoteToLocalSyncerUntilIdle();
508
509   EXPECT_EQ(4, CountParents(file));
510
511   EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
512
513   EXPECT_EQ(1, CountParents(file));
514 }
515
516 }  // namespace drive_backend
517 }  // namespace sync_file_system