remote_to_local_syncer_unittest.cc revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
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    fake_drive_service_.reset(new drive::FakeDriveService);
59
60    drive_uploader_.reset(
61        new drive::DriveUploader(fake_drive_service_.get(),
62                                 base::MessageLoopProxy::current().get()));
63    fake_drive_helper_.reset(
64        new FakeDriveServiceHelper(fake_drive_service_.get(),
65                                   drive_uploader_.get(),
66                                   kSyncRootFolderTitle));
67    fake_remote_change_processor_.reset(new FakeRemoteChangeProcessor);
68
69    context_.reset(new SyncEngineContext(fake_drive_service_.get(),
70                                         drive_uploader_.get(),
71                                         base::MessageLoopProxy::current(),
72                                         base::MessageLoopProxy::current(),
73                                         base::MessageLoopProxy::current()));
74    context_->SetRemoteChangeProcessor(fake_remote_change_processor_.get());
75
76    RegisterSyncableFileSystem();
77
78    sync_task_manager_.reset(new SyncTaskManager(
79        base::WeakPtr<SyncTaskManager::Client>(),
80        10 /* max_parallel_task */));
81    sync_task_manager_->Initialize(SYNC_STATUS_OK);
82  }
83
84  virtual void TearDown() OVERRIDE {
85    sync_task_manager_.reset();
86
87    RevokeSyncableFileSystem();
88
89    fake_remote_change_processor_.reset();
90    fake_drive_helper_.reset();
91    fake_drive_service_.reset();
92    drive_uploader_.reset();
93    context_.reset();
94    base::RunLoop().RunUntilIdle();
95  }
96
97  void InitializeMetadataDatabase() {
98    SyncEngineInitializer* initializer =
99        new SyncEngineInitializer(
100            context_.get(),
101            base::MessageLoopProxy::current(),
102            database_dir_.path(),
103            in_memory_env_.get());
104    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
105    sync_task_manager_->ScheduleSyncTask(
106        FROM_HERE,
107        scoped_ptr<SyncTask>(initializer),
108        SyncTaskManager::PRIORITY_MED,
109        base::Bind(&RemoteToLocalSyncerTest::DidInitializeMetadataDatabase,
110                   base::Unretained(this),
111                   initializer, &status));
112
113    base::RunLoop().RunUntilIdle();
114    EXPECT_EQ(SYNC_STATUS_OK, status);
115  }
116
117  void DidInitializeMetadataDatabase(SyncEngineInitializer* initializer,
118                                     SyncStatusCode* status_out,
119                                     SyncStatusCode status) {
120    *status_out = status;
121    context_->SetMetadataDatabase(initializer->PassMetadataDatabase());
122  }
123
124
125  void RegisterApp(const std::string& app_id,
126                   const std::string& app_root_folder_id) {
127    SyncStatusCode status = SYNC_STATUS_FAILED;
128    context_->GetMetadataDatabase()->RegisterApp(app_id, app_root_folder_id,
129                                                 CreateResultReceiver(&status));
130    base::RunLoop().RunUntilIdle();
131    EXPECT_EQ(SYNC_STATUS_OK, status);
132  }
133
134  MetadataDatabase* GetMetadataDatabase() {
135    return context_->GetMetadataDatabase();
136  }
137
138 protected:
139  std::string CreateSyncRoot() {
140    std::string sync_root_folder_id;
141    EXPECT_EQ(google_apis::HTTP_CREATED,
142              fake_drive_helper_->AddOrphanedFolder(
143                  kSyncRootFolderTitle, &sync_root_folder_id));
144    return sync_root_folder_id;
145  }
146
147  std::string CreateRemoteFolder(const std::string& parent_folder_id,
148                                 const std::string& title) {
149    std::string folder_id;
150    EXPECT_EQ(google_apis::HTTP_CREATED,
151              fake_drive_helper_->AddFolder(
152                  parent_folder_id, title, &folder_id));
153    return folder_id;
154  }
155
156  std::string CreateRemoteFile(const std::string& parent_folder_id,
157                               const std::string& title,
158                               const std::string& content) {
159    std::string file_id;
160    EXPECT_EQ(google_apis::HTTP_SUCCESS,
161              fake_drive_helper_->AddFile(
162                  parent_folder_id, title, content, &file_id));
163    return file_id;
164  }
165
166  void DeleteRemoteFile(const std::string& file_id) {
167    EXPECT_EQ(google_apis::HTTP_NO_CONTENT,
168              fake_drive_helper_->DeleteResource(file_id));
169  }
170
171  void CreateLocalFolder(const fileapi::FileSystemURL& url) {
172    fake_remote_change_processor_->UpdateLocalFileMetadata(
173        url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
174                        SYNC_FILE_TYPE_DIRECTORY));
175  }
176
177  void CreateLocalFile(const fileapi::FileSystemURL& url) {
178    fake_remote_change_processor_->UpdateLocalFileMetadata(
179        url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
180                        SYNC_FILE_TYPE_FILE));
181  }
182
183  SyncStatusCode RunSyncer() {
184    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
185    scoped_ptr<RemoteToLocalSyncer>
186        syncer(new RemoteToLocalSyncer(context_.get()));
187    syncer->RunExclusive(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    fake_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<drive::FakeDriveService> fake_drive_service_;
226  scoped_ptr<drive::DriveUploaderInterface> drive_uploader_;
227  scoped_ptr<FakeDriveServiceHelper> fake_drive_helper_;
228  scoped_ptr<FakeRemoteChangeProcessor> fake_remote_change_processor_;
229
230  scoped_ptr<SyncTaskManager> sync_task_manager_;
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, "folder1"),
254                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
255                       SYNC_FILE_TYPE_DIRECTORY);
256  AppendExpectedChange(URL(kOrigin, "file1"),
257                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
258                       SYNC_FILE_TYPE_FILE);
259  AppendExpectedChange(URL(kOrigin, "folder1/folder2"),
260                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
261                       SYNC_FILE_TYPE_DIRECTORY);
262  AppendExpectedChange(URL(kOrigin, "folder1/file2"),
263                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
264                       SYNC_FILE_TYPE_FILE);
265
266  VerifyConsistency();
267
268  EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
269}
270
271TEST_F(RemoteToLocalSyncerTest, DeleteFile) {
272  const GURL kOrigin("chrome-extension://example");
273  const std::string sync_root = CreateSyncRoot();
274  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
275  InitializeMetadataDatabase();
276  RegisterApp(kOrigin.host(), app_root);
277
278  const std::string folder = CreateRemoteFolder(app_root, "folder");
279  const std::string file = CreateRemoteFile(app_root, "file", "data");
280
281  AppendExpectedChange(URL(kOrigin, "folder"),
282                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
283                       SYNC_FILE_TYPE_DIRECTORY);
284  AppendExpectedChange(URL(kOrigin, "file"),
285                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
286                       SYNC_FILE_TYPE_FILE);
287
288  RunSyncerUntilIdle();
289  VerifyConsistency();
290
291  DeleteRemoteFile(folder);
292  DeleteRemoteFile(file);
293
294  AppendExpectedChange(URL(kOrigin, "folder"),
295                       FileChange::FILE_CHANGE_DELETE,
296                       SYNC_FILE_TYPE_UNKNOWN);
297  AppendExpectedChange(URL(kOrigin, "file"),
298                       FileChange::FILE_CHANGE_DELETE,
299                       SYNC_FILE_TYPE_UNKNOWN);
300
301  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
302  RunSyncerUntilIdle();
303  VerifyConsistency();
304
305  EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
306}
307
308TEST_F(RemoteToLocalSyncerTest, DeleteNestedFiles) {
309  const GURL kOrigin("chrome-extension://example");
310  const std::string sync_root = CreateSyncRoot();
311  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
312  InitializeMetadataDatabase();
313  RegisterApp(kOrigin.host(), app_root);
314
315  const std::string folder1 = CreateRemoteFolder(app_root, "folder1");
316  const std::string file1 = CreateRemoteFile(app_root, "file1", "data1");
317  const std::string folder2 = CreateRemoteFolder(folder1, "folder2");
318  const std::string file2 = CreateRemoteFile(folder1, "file2", "data2");
319
320  AppendExpectedChange(URL(kOrigin, "folder1"),
321                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
322                       SYNC_FILE_TYPE_DIRECTORY);
323  AppendExpectedChange(URL(kOrigin, "file1"),
324                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
325                       SYNC_FILE_TYPE_FILE);
326  AppendExpectedChange(URL(kOrigin, "folder1/folder2"),
327                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
328                       SYNC_FILE_TYPE_DIRECTORY);
329  AppendExpectedChange(URL(kOrigin, "folder1/file2"),
330                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
331                       SYNC_FILE_TYPE_FILE);
332
333  RunSyncerUntilIdle();
334  VerifyConsistency();
335
336  DeleteRemoteFile(folder1);
337
338  AppendExpectedChange(URL(kOrigin, "folder1"),
339                       FileChange::FILE_CHANGE_DELETE,
340                       SYNC_FILE_TYPE_UNKNOWN);
341  // Changes for descendant files ("folder2" and "file2") should be ignored.
342
343  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
344  RunSyncerUntilIdle();
345  VerifyConsistency();
346
347  EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
348}
349
350TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFileOnFolder) {
351  const GURL kOrigin("chrome-extension://example");
352  const std::string sync_root = CreateSyncRoot();
353  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
354  InitializeMetadataDatabase();
355  RegisterApp(kOrigin.host(), app_root);
356
357  CreateLocalFolder(URL(kOrigin, "folder"));
358  CreateRemoteFile(app_root, "folder", "data");
359
360  // Folder-File conflict happens. File creation should be ignored.
361
362  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
363  RunSyncerUntilIdle();
364  VerifyConsistency();
365
366  // Tracker for the remote file should be lowered.
367  EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
368  EXPECT_TRUE(GetMetadataDatabase()->HasLowPriorityDirtyTracker());
369}
370
371TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFile) {
372  const GURL kOrigin("chrome-extension://example");
373  const std::string sync_root = CreateSyncRoot();
374  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
375  InitializeMetadataDatabase();
376  RegisterApp(kOrigin.host(), app_root);
377
378  RunSyncerUntilIdle();
379  VerifyConsistency();
380
381  CreateLocalFile(URL(kOrigin, "file"));
382  CreateRemoteFolder(app_root, "file");
383
384  // File-Folder conflict happens. Folder should override the existing file.
385  AppendExpectedChange(URL(kOrigin, "file"),
386                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
387                       SYNC_FILE_TYPE_DIRECTORY);
388
389  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
390  RunSyncerUntilIdle();
391  VerifyConsistency();
392
393  EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
394}
395
396TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFolder) {
397  const GURL kOrigin("chrome-extension://example");
398  const std::string sync_root = CreateSyncRoot();
399  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
400  InitializeMetadataDatabase();
401  RegisterApp(kOrigin.host(), app_root);
402
403  CreateLocalFolder(URL(kOrigin, "folder"));
404  CreateRemoteFolder(app_root, "folder");
405
406  // Folder-Folder conflict happens. Folder creation should be ignored.
407
408  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
409  RunSyncerUntilIdle();
410  VerifyConsistency();
411
412  EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
413}
414
415TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFileOnFile) {
416  const GURL kOrigin("chrome-extension://example");
417  const std::string sync_root = CreateSyncRoot();
418  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
419  InitializeMetadataDatabase();
420  RegisterApp(kOrigin.host(), app_root);
421
422  CreateLocalFile(URL(kOrigin, "file"));
423  CreateRemoteFile(app_root, "file", "data");
424
425  // File-File conflict happens. File creation should be ignored.
426
427  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
428  RunSyncerUntilIdle();
429  VerifyConsistency();
430
431  // Tracker for the remote file should be lowered.
432  EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
433  EXPECT_TRUE(GetMetadataDatabase()->HasLowPriorityDirtyTracker());
434}
435
436TEST_F(RemoteToLocalSyncerTest, Conflict_CreateNestedFolderOnFile) {
437  const GURL kOrigin("chrome-extension://example");
438  const std::string sync_root = CreateSyncRoot();
439  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
440  InitializeMetadataDatabase();
441  RegisterApp(kOrigin.host(), app_root);
442
443  RunSyncerUntilIdle();
444  VerifyConsistency();
445
446  const std::string folder = CreateRemoteFolder(app_root, "folder");
447  CreateLocalFile(URL(kOrigin, "/folder"));
448  CreateRemoteFile(folder, "file", "data");
449
450  // File-Folder conflict happens. Folder should override the existing file.
451  AppendExpectedChange(URL(kOrigin, "/folder"),
452                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
453                       SYNC_FILE_TYPE_DIRECTORY);
454
455  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
456  RunSyncerUntilIdle();
457  VerifyConsistency();
458}
459
460TEST_F(RemoteToLocalSyncerTest, AppRootDeletion) {
461  const GURL kOrigin("chrome-extension://example");
462  const std::string sync_root = CreateSyncRoot();
463  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
464  InitializeMetadataDatabase();
465  RegisterApp(kOrigin.host(), app_root);
466
467  RunSyncerUntilIdle();
468  VerifyConsistency();
469
470  DeleteRemoteFile(app_root);
471
472  AppendExpectedChange(URL(kOrigin, "/"),
473                       FileChange::FILE_CHANGE_DELETE,
474                       SYNC_FILE_TYPE_UNKNOWN);
475
476  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
477  RunSyncerUntilIdle();
478  VerifyConsistency();
479
480  // SyncEngine will re-register the app and resurrect the app root later.
481}
482
483}  // namespace drive_backend
484}  // namespace sync_file_system
485