remote_to_local_syncer_unittest.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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/list_changes_task.h"
19#include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
20#include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h"
21#include "chrome/browser/sync_file_system/drive_backend/sync_engine_initializer.h"
22#include "chrome/browser/sync_file_system/drive_backend_v1/fake_drive_service_helper.h"
23#include "chrome/browser/sync_file_system/fake_remote_change_processor.h"
24#include "chrome/browser/sync_file_system/sync_file_system_test_util.h"
25#include "chrome/browser/sync_file_system/syncable_file_system_util.h"
26#include "content/public/test/test_browser_thread_bundle.h"
27#include "google_apis/drive/gdata_errorcode.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(this,
83                                      base::MessageLoopProxy::current(),
84                                      fake_drive_service_.get(),
85                                      database_dir_.path());
86    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
87    initializer.Run(CreateResultReceiver(&status));
88    base::RunLoop().RunUntilIdle();
89    EXPECT_EQ(SYNC_STATUS_OK, status);
90    metadata_database_ = initializer.PassMetadataDatabase();
91  }
92
93  void RegisterApp(const std::string& app_id,
94                   const std::string& app_root_folder_id) {
95    SyncStatusCode status = SYNC_STATUS_FAILED;
96    metadata_database_->RegisterApp(app_id, app_root_folder_id,
97                                    CreateResultReceiver(&status));
98    base::RunLoop().RunUntilIdle();
99    EXPECT_EQ(SYNC_STATUS_OK, status);
100  }
101
102  virtual drive::DriveServiceInterface* GetDriveService() OVERRIDE {
103    return fake_drive_service_.get();
104  }
105
106  virtual drive::DriveUploaderInterface* GetDriveUploader() OVERRIDE {
107    return drive_uploader_.get();
108  }
109
110  virtual MetadataDatabase* GetMetadataDatabase() OVERRIDE {
111    return metadata_database_.get();
112  }
113
114  virtual RemoteChangeProcessor* GetRemoteChangeProcessor() OVERRIDE {
115    return fake_remote_change_processor_.get();
116  }
117
118  virtual base::SequencedTaskRunner* GetBlockingTaskRunner() OVERRIDE {
119    return base::MessageLoopProxy::current().get();
120  }
121
122 protected:
123  std::string CreateSyncRoot() {
124    std::string sync_root_folder_id;
125    EXPECT_EQ(google_apis::HTTP_CREATED,
126              fake_drive_helper_->AddOrphanedFolder(
127                  kSyncRootFolderTitle, &sync_root_folder_id));
128    return sync_root_folder_id;
129  }
130
131  std::string CreateRemoteFolder(const std::string& parent_folder_id,
132                                 const std::string& title) {
133    std::string folder_id;
134    EXPECT_EQ(google_apis::HTTP_CREATED,
135              fake_drive_helper_->AddFolder(
136                  parent_folder_id, title, &folder_id));
137    return folder_id;
138  }
139
140  std::string CreateRemoteFile(const std::string& parent_folder_id,
141                               const std::string& title,
142                               const std::string& content) {
143    std::string file_id;
144    EXPECT_EQ(google_apis::HTTP_SUCCESS,
145              fake_drive_helper_->AddFile(
146                  parent_folder_id, title, content, &file_id));
147    return file_id;
148  }
149
150  void DeleteRemoteFile(const std::string& file_id) {
151    EXPECT_EQ(google_apis::HTTP_SUCCESS,
152              fake_drive_helper_->RemoveResource(file_id));
153  }
154
155  void CreateLocalFolder(const fileapi::FileSystemURL& url) {
156    fake_remote_change_processor_->UpdateLocalFileMetadata(
157        url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
158                        SYNC_FILE_TYPE_DIRECTORY));
159  }
160
161  void CreateLocalFile(const fileapi::FileSystemURL& url) {
162    fake_remote_change_processor_->UpdateLocalFileMetadata(
163        url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
164                        SYNC_FILE_TYPE_FILE));
165  }
166
167  SyncStatusCode RunSyncer() {
168    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
169    scoped_ptr<RemoteToLocalSyncer> syncer(new RemoteToLocalSyncer(this));
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  EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
272  EXPECT_FALSE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL));
273}
274
275TEST_F(RemoteToLocalSyncerTest, DeleteFile) {
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
282  const std::string folder = CreateRemoteFolder(app_root, "folder");
283  const std::string file = CreateRemoteFile(app_root, "file", "data");
284
285  AppendExpectedChange(URL(kOrigin, ""),
286                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
287                       SYNC_FILE_TYPE_DIRECTORY);
288  AppendExpectedChange(URL(kOrigin, "folder"),
289                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
290                       SYNC_FILE_TYPE_DIRECTORY);
291  AppendExpectedChange(URL(kOrigin, "file"),
292                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
293                       SYNC_FILE_TYPE_FILE);
294
295  RunSyncerUntilIdle();
296  VerifyConsistency();
297
298  DeleteRemoteFile(folder);
299  DeleteRemoteFile(file);
300
301  AppendExpectedChange(URL(kOrigin, "folder"),
302                       FileChange::FILE_CHANGE_DELETE,
303                       SYNC_FILE_TYPE_UNKNOWN);
304  AppendExpectedChange(URL(kOrigin, "file"),
305                       FileChange::FILE_CHANGE_DELETE,
306                       SYNC_FILE_TYPE_UNKNOWN);
307
308  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
309  RunSyncerUntilIdle();
310  VerifyConsistency();
311
312  EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
313  EXPECT_FALSE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL));
314}
315
316TEST_F(RemoteToLocalSyncerTest, DeleteNestedFiles) {
317  const GURL kOrigin("chrome-extension://example");
318  const std::string sync_root = CreateSyncRoot();
319  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
320  InitializeMetadataDatabase();
321  RegisterApp(kOrigin.host(), app_root);
322
323  const std::string folder1 = CreateRemoteFolder(app_root, "folder1");
324  const std::string file1 = CreateRemoteFile(app_root, "file1", "data1");
325  const std::string folder2 = CreateRemoteFolder(folder1, "folder2");
326  const std::string file2 = CreateRemoteFile(folder1, "file2", "data2");
327
328  AppendExpectedChange(URL(kOrigin, ""),
329                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
330                       SYNC_FILE_TYPE_DIRECTORY);
331  AppendExpectedChange(URL(kOrigin, "folder1"),
332                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
333                       SYNC_FILE_TYPE_DIRECTORY);
334  AppendExpectedChange(URL(kOrigin, "file1"),
335                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
336                       SYNC_FILE_TYPE_FILE);
337  AppendExpectedChange(URL(kOrigin, "folder1/folder2"),
338                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
339                       SYNC_FILE_TYPE_DIRECTORY);
340  AppendExpectedChange(URL(kOrigin, "folder1/file2"),
341                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
342                       SYNC_FILE_TYPE_FILE);
343
344  RunSyncerUntilIdle();
345  VerifyConsistency();
346
347  DeleteRemoteFile(folder1);
348
349  AppendExpectedChange(URL(kOrigin, "folder1"),
350                       FileChange::FILE_CHANGE_DELETE,
351                       SYNC_FILE_TYPE_UNKNOWN);
352  // Changes for descendant files ("folder2" and "file2") should be ignored.
353
354  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
355  RunSyncerUntilIdle();
356  VerifyConsistency();
357
358  EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
359  EXPECT_FALSE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL));
360}
361
362TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFileOnFolder) {
363  const GURL kOrigin("chrome-extension://example");
364  const std::string sync_root = CreateSyncRoot();
365  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
366  InitializeMetadataDatabase();
367  RegisterApp(kOrigin.host(), app_root);
368
369  AppendExpectedChange(URL(kOrigin, ""),
370                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
371                       SYNC_FILE_TYPE_DIRECTORY);
372
373  CreateLocalFolder(URL(kOrigin, "folder"));
374  CreateRemoteFile(app_root, "folder", "data");
375
376  // Folder-File conflict happens. File creation should be ignored.
377
378  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
379  RunSyncerUntilIdle();
380  VerifyConsistency();
381
382  // Tracker for the remote file should be lowered.
383  EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
384  EXPECT_TRUE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL));
385}
386
387TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFile) {
388  const GURL kOrigin("chrome-extension://example");
389  const std::string sync_root = CreateSyncRoot();
390  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
391  InitializeMetadataDatabase();
392  RegisterApp(kOrigin.host(), app_root);
393
394  AppendExpectedChange(URL(kOrigin, ""),
395                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
396                       SYNC_FILE_TYPE_DIRECTORY);
397
398  RunSyncerUntilIdle();
399  VerifyConsistency();
400
401  CreateLocalFile(URL(kOrigin, "file"));
402  CreateRemoteFolder(app_root, "file");
403
404  // File-Folder conflict happens. Folder should override the existing file.
405  AppendExpectedChange(URL(kOrigin, "file"),
406                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
407                       SYNC_FILE_TYPE_DIRECTORY);
408
409  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
410  RunSyncerUntilIdle();
411  VerifyConsistency();
412
413  EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
414  EXPECT_FALSE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL));
415}
416
417TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFolder) {
418  const GURL kOrigin("chrome-extension://example");
419  const std::string sync_root = CreateSyncRoot();
420  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
421  InitializeMetadataDatabase();
422  RegisterApp(kOrigin.host(), app_root);
423
424  AppendExpectedChange(URL(kOrigin, ""),
425                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
426                       SYNC_FILE_TYPE_DIRECTORY);
427
428  CreateLocalFolder(URL(kOrigin, "folder"));
429  CreateRemoteFolder(app_root, "folder");
430
431  // Folder-Folder conflict happens. Folder creation should be ignored.
432
433  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
434  RunSyncerUntilIdle();
435  VerifyConsistency();
436
437  EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
438  EXPECT_FALSE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL));
439}
440
441TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFileOnFile) {
442  const GURL kOrigin("chrome-extension://example");
443  const std::string sync_root = CreateSyncRoot();
444  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
445  InitializeMetadataDatabase();
446  RegisterApp(kOrigin.host(), app_root);
447
448  AppendExpectedChange(URL(kOrigin, ""),
449                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
450                       SYNC_FILE_TYPE_DIRECTORY);
451
452  CreateLocalFile(URL(kOrigin, "file"));
453  CreateRemoteFile(app_root, "file", "data");
454
455  // File-File conflict happens. File creation should be ignored.
456
457  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
458  RunSyncerUntilIdle();
459  VerifyConsistency();
460
461  // Tracker for the remote file should be lowered.
462  EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
463  EXPECT_TRUE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL));
464}
465
466TEST_F(RemoteToLocalSyncerTest, Conflict_CreateNestedFolderOnFile) {
467  const GURL kOrigin("chrome-extension://example");
468  const std::string sync_root = CreateSyncRoot();
469  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
470  InitializeMetadataDatabase();
471  RegisterApp(kOrigin.host(), app_root);
472
473  AppendExpectedChange(URL(kOrigin, "/"),
474                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
475                       SYNC_FILE_TYPE_DIRECTORY);
476
477  RunSyncerUntilIdle();
478  VerifyConsistency();
479
480  const std::string folder = CreateRemoteFolder(app_root, "folder");
481
482  AppendExpectedChange(URL(kOrigin, "/folder"),
483                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
484                       SYNC_FILE_TYPE_DIRECTORY);
485
486  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
487  RunSyncerUntilIdle();
488
489  CreateLocalFile(URL(kOrigin, "/folder"));
490  CreateRemoteFile(folder, "file", "data");
491
492  // File-Folder conflict happens. Folder should override the existing file.
493  AppendExpectedChange(URL(kOrigin, "/folder"),
494                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
495                       SYNC_FILE_TYPE_DIRECTORY);
496
497  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
498  RunSyncerUntilIdle();
499  VerifyConsistency();
500}
501
502TEST_F(RemoteToLocalSyncerTest, AppRootDeletion) {
503  const GURL kOrigin("chrome-extension://example");
504  const std::string sync_root = CreateSyncRoot();
505  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
506  InitializeMetadataDatabase();
507  RegisterApp(kOrigin.host(), app_root);
508
509  AppendExpectedChange(URL(kOrigin, "/"),
510                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
511                       SYNC_FILE_TYPE_DIRECTORY);
512
513  RunSyncerUntilIdle();
514  VerifyConsistency();
515
516  DeleteRemoteFile(app_root);
517
518  AppendExpectedChange(URL(kOrigin, "/"),
519                       FileChange::FILE_CHANGE_DELETE,
520                       SYNC_FILE_TYPE_UNKNOWN);
521
522  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
523  RunSyncerUntilIdle();
524  VerifyConsistency();
525
526  // SyncEngine will re-register the app and resurrect the app root later.
527}
528
529}  // namespace drive_backend
530}  // namespace sync_file_system
531