remote_to_local_syncer_unittest.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
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/fake_drive_service_helper.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/sync_task_manager.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
33namespace sync_file_system {
34namespace drive_backend {
35
36namespace {
37
38fileapi::FileSystemURL URL(const GURL& origin,
39                           const std::string& path) {
40  return CreateSyncableFileSystemURL(
41      origin, base::FilePath::FromUTF8Unsafe(path));
42}
43
44}  // namespace
45
46class RemoteToLocalSyncerTest : public testing::Test {
47 public:
48  typedef FakeRemoteChangeProcessor::URLToFileChangesMap URLToFileChangesMap;
49
50  RemoteToLocalSyncerTest()
51      : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
52  virtual ~RemoteToLocalSyncerTest() {}
53
54  virtual void SetUp() OVERRIDE {
55    ASSERT_TRUE(database_dir_.CreateUniqueTempDir());
56    in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
57
58    scoped_ptr<drive::FakeDriveService>
59        fake_drive_service(new drive::FakeDriveService);
60
61    scoped_ptr<drive::DriveUploaderInterface>
62        drive_uploader(new drive::DriveUploader(
63            fake_drive_service.get(),
64            base::MessageLoopProxy::current().get()));
65    fake_drive_helper_.reset(new FakeDriveServiceHelper(
66        fake_drive_service.get(), drive_uploader.get(),
67        kSyncRootFolderTitle));
68    fake_remote_change_processor_.reset(new FakeRemoteChangeProcessor);
69
70    context_.reset(new SyncEngineContext(
71        fake_drive_service.PassAs<drive::DriveServiceInterface>(),
72        drive_uploader.Pass(), base::MessageLoopProxy::current()));
73    context_->SetRemoteChangeProcessor(fake_remote_change_processor_.get());
74
75    RegisterSyncableFileSystem();
76
77    sync_task_manager_.reset(new SyncTaskManager(
78        base::WeakPtr<SyncTaskManager::Client>(),
79        10 /* max_parallel_task */));
80    sync_task_manager_->Initialize(SYNC_STATUS_OK);
81  }
82
83  virtual void TearDown() OVERRIDE {
84    sync_task_manager_.reset();
85
86    RevokeSyncableFileSystem();
87
88    fake_remote_change_processor_.reset();
89    fake_drive_helper_.reset();
90    context_.reset();
91    base::RunLoop().RunUntilIdle();
92  }
93
94  void InitializeMetadataDatabase() {
95    SyncEngineInitializer* initializer =
96        new SyncEngineInitializer(
97            context_.get(),
98            base::MessageLoopProxy::current(),
99            database_dir_.path(),
100            in_memory_env_.get());
101    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
102    sync_task_manager_->ScheduleSyncTask(
103        FROM_HERE,
104        scoped_ptr<SyncTask>(initializer),
105        SyncTaskManager::PRIORITY_MED,
106        base::Bind(&RemoteToLocalSyncerTest::DidInitializeMetadataDatabase,
107                   base::Unretained(this),
108                   initializer, &status));
109
110    base::RunLoop().RunUntilIdle();
111    EXPECT_EQ(SYNC_STATUS_OK, status);
112  }
113
114  void DidInitializeMetadataDatabase(SyncEngineInitializer* initializer,
115                                     SyncStatusCode* status_out,
116                                     SyncStatusCode status) {
117    *status_out = status;
118    context_->SetMetadataDatabase(initializer->PassMetadataDatabase());
119  }
120
121
122  void RegisterApp(const std::string& app_id,
123                   const std::string& app_root_folder_id) {
124    SyncStatusCode status = SYNC_STATUS_FAILED;
125    context_->GetMetadataDatabase()->RegisterApp(app_id, app_root_folder_id,
126                                                 CreateResultReceiver(&status));
127    base::RunLoop().RunUntilIdle();
128    EXPECT_EQ(SYNC_STATUS_OK, status);
129  }
130
131  MetadataDatabase* GetMetadataDatabase() {
132    return context_->GetMetadataDatabase();
133  }
134
135 protected:
136  std::string CreateSyncRoot() {
137    std::string sync_root_folder_id;
138    EXPECT_EQ(google_apis::HTTP_CREATED,
139              fake_drive_helper_->AddOrphanedFolder(
140                  kSyncRootFolderTitle, &sync_root_folder_id));
141    return sync_root_folder_id;
142  }
143
144  std::string CreateRemoteFolder(const std::string& parent_folder_id,
145                                 const std::string& title) {
146    std::string folder_id;
147    EXPECT_EQ(google_apis::HTTP_CREATED,
148              fake_drive_helper_->AddFolder(
149                  parent_folder_id, title, &folder_id));
150    return folder_id;
151  }
152
153  std::string CreateRemoteFile(const std::string& parent_folder_id,
154                               const std::string& title,
155                               const std::string& content) {
156    std::string file_id;
157    EXPECT_EQ(google_apis::HTTP_SUCCESS,
158              fake_drive_helper_->AddFile(
159                  parent_folder_id, title, content, &file_id));
160    return file_id;
161  }
162
163  void DeleteRemoteFile(const std::string& file_id) {
164    EXPECT_EQ(google_apis::HTTP_NO_CONTENT,
165              fake_drive_helper_->DeleteResource(file_id));
166  }
167
168  void CreateLocalFolder(const fileapi::FileSystemURL& url) {
169    fake_remote_change_processor_->UpdateLocalFileMetadata(
170        url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
171                        SYNC_FILE_TYPE_DIRECTORY));
172  }
173
174  void CreateLocalFile(const fileapi::FileSystemURL& url) {
175    fake_remote_change_processor_->UpdateLocalFileMetadata(
176        url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
177                        SYNC_FILE_TYPE_FILE));
178  }
179
180  SyncStatusCode RunSyncer() {
181    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
182    scoped_ptr<RemoteToLocalSyncer>
183        syncer(new RemoteToLocalSyncer(context_.get()));
184    syncer->RunExclusive(CreateResultReceiver(&status));
185    base::RunLoop().RunUntilIdle();
186    return status;
187  }
188
189  void RunSyncerUntilIdle() {
190    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
191    while (status != SYNC_STATUS_NO_CHANGE_TO_SYNC)
192      status = RunSyncer();
193  }
194
195  SyncStatusCode ListChanges() {
196    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
197    sync_task_manager_->ScheduleSyncTask(
198        FROM_HERE,
199        scoped_ptr<SyncTask>(new ListChangesTask(context_.get())),
200        SyncTaskManager::PRIORITY_MED,
201        CreateResultReceiver(&status));
202    base::RunLoop().RunUntilIdle();
203    return status;
204  }
205
206  void AppendExpectedChange(const fileapi::FileSystemURL& url,
207                            FileChange::ChangeType change_type,
208                            SyncFileType file_type) {
209    expected_changes_[url].push_back(FileChange(change_type, file_type));
210  }
211
212  void VerifyConsistency() {
213    fake_remote_change_processor_->VerifyConsistency(expected_changes_);
214  }
215
216 private:
217  content::TestBrowserThreadBundle thread_bundle_;
218  base::ScopedTempDir database_dir_;
219  scoped_ptr<leveldb::Env> in_memory_env_;
220
221  scoped_ptr<SyncEngineContext> context_;
222  scoped_ptr<FakeDriveServiceHelper> fake_drive_helper_;
223  scoped_ptr<FakeRemoteChangeProcessor> fake_remote_change_processor_;
224
225  scoped_ptr<SyncTaskManager> sync_task_manager_;
226
227  URLToFileChangesMap expected_changes_;
228
229  DISALLOW_COPY_AND_ASSIGN(RemoteToLocalSyncerTest);
230};
231
232TEST_F(RemoteToLocalSyncerTest, AddNewFile) {
233  const GURL kOrigin("chrome-extension://example");
234  const std::string sync_root = CreateSyncRoot();
235  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
236  InitializeMetadataDatabase();
237  RegisterApp(kOrigin.host(), app_root);
238
239  const std::string folder1 = CreateRemoteFolder(app_root, "folder1");
240  const std::string file1 = CreateRemoteFile(app_root, "file1", "data1");
241  const std::string folder2 = CreateRemoteFolder(folder1, "folder2");
242  const std::string file2 = CreateRemoteFile(folder1, "file2", "data2");
243
244  RunSyncerUntilIdle();
245
246  // Create expected changes.
247  // TODO(nhiroki): Clean up creating URL part.
248  AppendExpectedChange(URL(kOrigin, "folder1"),
249                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
250                       SYNC_FILE_TYPE_DIRECTORY);
251  AppendExpectedChange(URL(kOrigin, "file1"),
252                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
253                       SYNC_FILE_TYPE_FILE);
254  AppendExpectedChange(URL(kOrigin, "folder1/folder2"),
255                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
256                       SYNC_FILE_TYPE_DIRECTORY);
257  AppendExpectedChange(URL(kOrigin, "folder1/file2"),
258                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
259                       SYNC_FILE_TYPE_FILE);
260
261  VerifyConsistency();
262
263  EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
264}
265
266TEST_F(RemoteToLocalSyncerTest, DeleteFile) {
267  const GURL kOrigin("chrome-extension://example");
268  const std::string sync_root = CreateSyncRoot();
269  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
270  InitializeMetadataDatabase();
271  RegisterApp(kOrigin.host(), app_root);
272
273  const std::string folder = CreateRemoteFolder(app_root, "folder");
274  const std::string file = CreateRemoteFile(app_root, "file", "data");
275
276  AppendExpectedChange(URL(kOrigin, "folder"),
277                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
278                       SYNC_FILE_TYPE_DIRECTORY);
279  AppendExpectedChange(URL(kOrigin, "file"),
280                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
281                       SYNC_FILE_TYPE_FILE);
282
283  RunSyncerUntilIdle();
284  VerifyConsistency();
285
286  DeleteRemoteFile(folder);
287  DeleteRemoteFile(file);
288
289  AppendExpectedChange(URL(kOrigin, "folder"),
290                       FileChange::FILE_CHANGE_DELETE,
291                       SYNC_FILE_TYPE_UNKNOWN);
292  AppendExpectedChange(URL(kOrigin, "file"),
293                       FileChange::FILE_CHANGE_DELETE,
294                       SYNC_FILE_TYPE_UNKNOWN);
295
296  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
297  RunSyncerUntilIdle();
298  VerifyConsistency();
299
300  EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
301}
302
303TEST_F(RemoteToLocalSyncerTest, DeleteNestedFiles) {
304  const GURL kOrigin("chrome-extension://example");
305  const std::string sync_root = CreateSyncRoot();
306  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
307  InitializeMetadataDatabase();
308  RegisterApp(kOrigin.host(), app_root);
309
310  const std::string folder1 = CreateRemoteFolder(app_root, "folder1");
311  const std::string file1 = CreateRemoteFile(app_root, "file1", "data1");
312  const std::string folder2 = CreateRemoteFolder(folder1, "folder2");
313  const std::string file2 = CreateRemoteFile(folder1, "file2", "data2");
314
315  AppendExpectedChange(URL(kOrigin, "folder1"),
316                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
317                       SYNC_FILE_TYPE_DIRECTORY);
318  AppendExpectedChange(URL(kOrigin, "file1"),
319                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
320                       SYNC_FILE_TYPE_FILE);
321  AppendExpectedChange(URL(kOrigin, "folder1/folder2"),
322                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
323                       SYNC_FILE_TYPE_DIRECTORY);
324  AppendExpectedChange(URL(kOrigin, "folder1/file2"),
325                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
326                       SYNC_FILE_TYPE_FILE);
327
328  RunSyncerUntilIdle();
329  VerifyConsistency();
330
331  DeleteRemoteFile(folder1);
332
333  AppendExpectedChange(URL(kOrigin, "folder1"),
334                       FileChange::FILE_CHANGE_DELETE,
335                       SYNC_FILE_TYPE_UNKNOWN);
336  // Changes for descendant files ("folder2" and "file2") should be ignored.
337
338  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
339  RunSyncerUntilIdle();
340  VerifyConsistency();
341
342  EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
343}
344
345TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFileOnFolder) {
346  const GURL kOrigin("chrome-extension://example");
347  const std::string sync_root = CreateSyncRoot();
348  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
349  InitializeMetadataDatabase();
350  RegisterApp(kOrigin.host(), app_root);
351
352  CreateLocalFolder(URL(kOrigin, "folder"));
353  CreateRemoteFile(app_root, "folder", "data");
354
355  // Folder-File conflict happens. File creation should be ignored.
356
357  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
358  RunSyncerUntilIdle();
359  VerifyConsistency();
360
361  // Tracker for the remote file should be lowered.
362  EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
363  EXPECT_TRUE(GetMetadataDatabase()->HasLowPriorityDirtyTracker());
364}
365
366TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFile) {
367  const GURL kOrigin("chrome-extension://example");
368  const std::string sync_root = CreateSyncRoot();
369  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
370  InitializeMetadataDatabase();
371  RegisterApp(kOrigin.host(), app_root);
372
373  RunSyncerUntilIdle();
374  VerifyConsistency();
375
376  CreateLocalFile(URL(kOrigin, "file"));
377  CreateRemoteFolder(app_root, "file");
378
379  // File-Folder conflict happens. Folder should override the existing file.
380  AppendExpectedChange(URL(kOrigin, "file"),
381                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
382                       SYNC_FILE_TYPE_DIRECTORY);
383
384  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
385  RunSyncerUntilIdle();
386  VerifyConsistency();
387
388  EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
389}
390
391TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFolder) {
392  const GURL kOrigin("chrome-extension://example");
393  const std::string sync_root = CreateSyncRoot();
394  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
395  InitializeMetadataDatabase();
396  RegisterApp(kOrigin.host(), app_root);
397
398  CreateLocalFolder(URL(kOrigin, "folder"));
399  CreateRemoteFolder(app_root, "folder");
400
401  // Folder-Folder conflict happens. Folder creation should be ignored.
402
403  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
404  RunSyncerUntilIdle();
405  VerifyConsistency();
406
407  EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
408}
409
410TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFileOnFile) {
411  const GURL kOrigin("chrome-extension://example");
412  const std::string sync_root = CreateSyncRoot();
413  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
414  InitializeMetadataDatabase();
415  RegisterApp(kOrigin.host(), app_root);
416
417  CreateLocalFile(URL(kOrigin, "file"));
418  CreateRemoteFile(app_root, "file", "data");
419
420  // File-File conflict happens. File creation should be ignored.
421
422  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
423  RunSyncerUntilIdle();
424  VerifyConsistency();
425
426  // Tracker for the remote file should be lowered.
427  EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
428  EXPECT_TRUE(GetMetadataDatabase()->HasLowPriorityDirtyTracker());
429}
430
431TEST_F(RemoteToLocalSyncerTest, Conflict_CreateNestedFolderOnFile) {
432  const GURL kOrigin("chrome-extension://example");
433  const std::string sync_root = CreateSyncRoot();
434  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
435  InitializeMetadataDatabase();
436  RegisterApp(kOrigin.host(), app_root);
437
438  RunSyncerUntilIdle();
439  VerifyConsistency();
440
441  const std::string folder = CreateRemoteFolder(app_root, "folder");
442  CreateLocalFile(URL(kOrigin, "/folder"));
443  CreateRemoteFile(folder, "file", "data");
444
445  // File-Folder conflict happens. Folder should override the existing file.
446  AppendExpectedChange(URL(kOrigin, "/folder"),
447                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
448                       SYNC_FILE_TYPE_DIRECTORY);
449
450  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
451  RunSyncerUntilIdle();
452  VerifyConsistency();
453}
454
455TEST_F(RemoteToLocalSyncerTest, AppRootDeletion) {
456  const GURL kOrigin("chrome-extension://example");
457  const std::string sync_root = CreateSyncRoot();
458  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
459  InitializeMetadataDatabase();
460  RegisterApp(kOrigin.host(), app_root);
461
462  RunSyncerUntilIdle();
463  VerifyConsistency();
464
465  DeleteRemoteFile(app_root);
466
467  AppendExpectedChange(URL(kOrigin, "/"),
468                       FileChange::FILE_CHANGE_DELETE,
469                       SYNC_FILE_TYPE_UNKNOWN);
470
471  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
472  RunSyncerUntilIdle();
473  VerifyConsistency();
474
475  // SyncEngine will re-register the app and resurrect the app root later.
476}
477
478}  // namespace drive_backend
479}  // namespace sync_file_system
480