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