remote_to_local_syncer_unittest.cc revision effb81e5f8246d0db0270817048dc992db66e9fb
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    ASSERT_TRUE(fake_drive_service->LoadAccountMetadataForWapi(
61        "sync_file_system/account_metadata.json"));
62    ASSERT_TRUE(fake_drive_service->LoadResourceListForWapi(
63        "gdata/empty_feed.json"));
64
65    scoped_ptr<drive::DriveUploaderInterface>
66        drive_uploader(new drive::DriveUploader(
67            fake_drive_service.get(),
68            base::MessageLoopProxy::current().get()));
69    fake_drive_helper_.reset(new FakeDriveServiceHelper(
70        fake_drive_service.get(), drive_uploader.get(),
71        kSyncRootFolderTitle));
72    fake_remote_change_processor_.reset(new FakeRemoteChangeProcessor);
73
74    context_.reset(new SyncEngineContext(
75        fake_drive_service.PassAs<drive::DriveServiceInterface>(),
76        drive_uploader.Pass(), base::MessageLoopProxy::current()));
77    context_->SetRemoteChangeProcessor(fake_remote_change_processor_.get());
78
79    RegisterSyncableFileSystem();
80
81    sync_task_manager_.reset(new SyncTaskManager(
82        base::WeakPtr<SyncTaskManager::Client>(),
83        10 /* max_parallel_task */));
84    sync_task_manager_->Initialize(SYNC_STATUS_OK);
85  }
86
87  virtual void TearDown() OVERRIDE {
88    sync_task_manager_.reset();
89
90    RevokeSyncableFileSystem();
91
92    fake_remote_change_processor_.reset();
93    fake_drive_helper_.reset();
94    context_.reset();
95    base::RunLoop().RunUntilIdle();
96  }
97
98  void InitializeMetadataDatabase() {
99    SyncEngineInitializer* initializer =
100        new SyncEngineInitializer(
101            context_.get(),
102            base::MessageLoopProxy::current(),
103            context_->GetDriveService(),
104            database_dir_.path(),
105            in_memory_env_.get());
106    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
107    sync_task_manager_->ScheduleSyncTask(
108        FROM_HERE,
109        scoped_ptr<SyncTask>(initializer),
110        SyncTaskManager::PRIORITY_MED,
111        base::Bind(&RemoteToLocalSyncerTest::DidInitializeMetadataDatabase,
112                   base::Unretained(this),
113                   initializer, &status));
114
115    base::RunLoop().RunUntilIdle();
116    EXPECT_EQ(SYNC_STATUS_OK, status);
117  }
118
119  void DidInitializeMetadataDatabase(SyncEngineInitializer* initializer,
120                                     SyncStatusCode* status_out,
121                                     SyncStatusCode status) {
122    *status_out = status;
123    context_->SetMetadataDatabase(initializer->PassMetadataDatabase());
124  }
125
126
127  void RegisterApp(const std::string& app_id,
128                   const std::string& app_root_folder_id) {
129    SyncStatusCode status = SYNC_STATUS_FAILED;
130    context_->GetMetadataDatabase()->RegisterApp(app_id, app_root_folder_id,
131                                                 CreateResultReceiver(&status));
132    base::RunLoop().RunUntilIdle();
133    EXPECT_EQ(SYNC_STATUS_OK, status);
134  }
135
136  MetadataDatabase* GetMetadataDatabase() {
137    return context_->GetMetadataDatabase();
138  }
139
140 protected:
141  std::string CreateSyncRoot() {
142    std::string sync_root_folder_id;
143    EXPECT_EQ(google_apis::HTTP_CREATED,
144              fake_drive_helper_->AddOrphanedFolder(
145                  kSyncRootFolderTitle, &sync_root_folder_id));
146    return sync_root_folder_id;
147  }
148
149  std::string CreateRemoteFolder(const std::string& parent_folder_id,
150                                 const std::string& title) {
151    std::string folder_id;
152    EXPECT_EQ(google_apis::HTTP_CREATED,
153              fake_drive_helper_->AddFolder(
154                  parent_folder_id, title, &folder_id));
155    return folder_id;
156  }
157
158  std::string CreateRemoteFile(const std::string& parent_folder_id,
159                               const std::string& title,
160                               const std::string& content) {
161    std::string file_id;
162    EXPECT_EQ(google_apis::HTTP_SUCCESS,
163              fake_drive_helper_->AddFile(
164                  parent_folder_id, title, content, &file_id));
165    return file_id;
166  }
167
168  void DeleteRemoteFile(const std::string& file_id) {
169    EXPECT_EQ(google_apis::HTTP_NO_CONTENT,
170              fake_drive_helper_->DeleteResource(file_id));
171  }
172
173  void CreateLocalFolder(const fileapi::FileSystemURL& url) {
174    fake_remote_change_processor_->UpdateLocalFileMetadata(
175        url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
176                        SYNC_FILE_TYPE_DIRECTORY));
177  }
178
179  void CreateLocalFile(const fileapi::FileSystemURL& url) {
180    fake_remote_change_processor_->UpdateLocalFileMetadata(
181        url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
182                        SYNC_FILE_TYPE_FILE));
183  }
184
185  SyncStatusCode RunSyncer() {
186    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
187    scoped_ptr<RemoteToLocalSyncer>
188        syncer(new RemoteToLocalSyncer(context_.get()));
189    syncer->RunSequential(CreateResultReceiver(&status));
190    base::RunLoop().RunUntilIdle();
191    return status;
192  }
193
194  void RunSyncerUntilIdle() {
195    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
196    while (status != SYNC_STATUS_NO_CHANGE_TO_SYNC)
197      status = RunSyncer();
198  }
199
200  SyncStatusCode ListChanges() {
201    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
202    sync_task_manager_->ScheduleSyncTask(
203        FROM_HERE,
204        scoped_ptr<SyncTask>(new ListChangesTask(context_.get())),
205        SyncTaskManager::PRIORITY_MED,
206        CreateResultReceiver(&status));
207    base::RunLoop().RunUntilIdle();
208    return status;
209  }
210
211  void AppendExpectedChange(const fileapi::FileSystemURL& url,
212                            FileChange::ChangeType change_type,
213                            SyncFileType file_type) {
214    expected_changes_[url].push_back(FileChange(change_type, file_type));
215  }
216
217  void VerifyConsistency() {
218    fake_remote_change_processor_->VerifyConsistency(expected_changes_);
219  }
220
221 private:
222  content::TestBrowserThreadBundle thread_bundle_;
223  base::ScopedTempDir database_dir_;
224  scoped_ptr<leveldb::Env> in_memory_env_;
225
226  scoped_ptr<SyncEngineContext> context_;
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