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