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