remote_to_local_syncer_unittest.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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 = context_->GetMetadataDatabase()->RegisterApp(
126        app_id, app_root_folder_id);
127    EXPECT_EQ(SYNC_STATUS_OK, status);
128  }
129
130  MetadataDatabase* GetMetadataDatabase() {
131    return context_->GetMetadataDatabase();
132  }
133
134 protected:
135  std::string CreateSyncRoot() {
136    std::string sync_root_folder_id;
137    EXPECT_EQ(google_apis::HTTP_CREATED,
138              fake_drive_helper_->AddOrphanedFolder(
139                  kSyncRootFolderTitle, &sync_root_folder_id));
140    return sync_root_folder_id;
141  }
142
143  std::string CreateRemoteFolder(const std::string& parent_folder_id,
144                                 const std::string& title) {
145    std::string folder_id;
146    EXPECT_EQ(google_apis::HTTP_CREATED,
147              fake_drive_helper_->AddFolder(
148                  parent_folder_id, title, &folder_id));
149    return folder_id;
150  }
151
152  std::string CreateRemoteFile(const std::string& parent_folder_id,
153                               const std::string& title,
154                               const std::string& content) {
155    std::string file_id;
156    EXPECT_EQ(google_apis::HTTP_SUCCESS,
157              fake_drive_helper_->AddFile(
158                  parent_folder_id, title, content, &file_id));
159    return file_id;
160  }
161
162  void DeleteRemoteFile(const std::string& file_id) {
163    EXPECT_EQ(google_apis::HTTP_NO_CONTENT,
164              fake_drive_helper_->DeleteResource(file_id));
165  }
166
167  void CreateLocalFolder(const storage::FileSystemURL& url) {
168    remote_change_processor_->UpdateLocalFileMetadata(
169        url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
170                        SYNC_FILE_TYPE_DIRECTORY));
171  }
172
173  void CreateLocalFile(const storage::FileSystemURL& url) {
174    remote_change_processor_->UpdateLocalFileMetadata(
175        url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
176                        SYNC_FILE_TYPE_FILE));
177  }
178
179  SyncStatusCode RunSyncer() {
180    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
181    scoped_ptr<RemoteToLocalSyncer>
182        syncer(new RemoteToLocalSyncer(context_.get()));
183    syncer->RunPreflight(SyncTaskToken::CreateForTesting(
184        CreateResultReceiver(&status)));
185    base::RunLoop().RunUntilIdle();
186    return status;
187  }
188
189  SyncStatusCode RunSyncerUntilIdle() {
190    const int kRetryLimit = 100;
191    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
192    int count = 0;
193    do {
194      if (count++ > kRetryLimit)
195        return status;
196      status = RunSyncer();
197    } while (status == SYNC_STATUS_OK ||
198             status == SYNC_STATUS_RETRY);
199    return status;
200  }
201
202  SyncStatusCode RunSyncerAndPromoteUntilIdle() {
203    const int kRetryLimit = 100;
204    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
205    MetadataDatabase* metadata_database = context_->GetMetadataDatabase();
206    int count = 0;
207    do {
208      if (count++ > kRetryLimit)
209        return status;
210      status = RunSyncer();
211    } while (status == SYNC_STATUS_OK ||
212             status == SYNC_STATUS_RETRY ||
213             metadata_database->PromoteDemotedTrackers());
214    return status;
215  }
216
217  SyncStatusCode ListChanges() {
218    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
219    sync_task_manager_->ScheduleSyncTask(
220        FROM_HERE,
221        scoped_ptr<SyncTask>(new ListChangesTask(context_.get())),
222        SyncTaskManager::PRIORITY_MED,
223        CreateResultReceiver(&status));
224    base::RunLoop().RunUntilIdle();
225    return status;
226  }
227
228  void AppendExpectedChange(const storage::FileSystemURL& url,
229                            FileChange::ChangeType change_type,
230                            SyncFileType file_type) {
231    expected_changes_[url].push_back(FileChange(change_type, file_type));
232  }
233
234  void VerifyConsistency() {
235    remote_change_processor_->VerifyConsistency(expected_changes_);
236  }
237
238 private:
239  content::TestBrowserThreadBundle thread_bundle_;
240  base::ScopedTempDir database_dir_;
241  scoped_ptr<leveldb::Env> in_memory_env_;
242
243  scoped_ptr<SyncEngineContext> context_;
244  scoped_ptr<FakeDriveServiceHelper> fake_drive_helper_;
245  scoped_ptr<FakeRemoteChangeProcessor> remote_change_processor_;
246
247  scoped_ptr<SyncTaskManager> sync_task_manager_;
248
249  URLToFileChangesMap expected_changes_;
250
251  DISALLOW_COPY_AND_ASSIGN(RemoteToLocalSyncerTest);
252};
253
254TEST_F(RemoteToLocalSyncerTest, AddNewFile) {
255  const GURL kOrigin("chrome-extension://example");
256  const std::string sync_root = CreateSyncRoot();
257  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
258  InitializeMetadataDatabase();
259  RegisterApp(kOrigin.host(), app_root);
260
261  const std::string folder1 = CreateRemoteFolder(app_root, "folder1");
262  const std::string file1 = CreateRemoteFile(app_root, "file1", "data1");
263  const std::string folder2 = CreateRemoteFolder(folder1, "folder2");
264  const std::string file2 = CreateRemoteFile(folder1, "file2", "data2");
265
266  EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerAndPromoteUntilIdle());
267
268  // Create expected changes.
269  // TODO(nhiroki): Clean up creating URL part.
270  AppendExpectedChange(URL(kOrigin, "folder1"),
271                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
272                       SYNC_FILE_TYPE_DIRECTORY);
273  AppendExpectedChange(URL(kOrigin, "file1"),
274                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
275                       SYNC_FILE_TYPE_FILE);
276  AppendExpectedChange(URL(kOrigin, "folder1/folder2"),
277                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
278                       SYNC_FILE_TYPE_DIRECTORY);
279  AppendExpectedChange(URL(kOrigin, "folder1/file2"),
280                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
281                       SYNC_FILE_TYPE_FILE);
282
283  VerifyConsistency();
284
285  EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
286}
287
288TEST_F(RemoteToLocalSyncerTest, DeleteFile) {
289  const GURL kOrigin("chrome-extension://example");
290  const std::string sync_root = CreateSyncRoot();
291  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
292  InitializeMetadataDatabase();
293  RegisterApp(kOrigin.host(), app_root);
294
295  const std::string folder = CreateRemoteFolder(app_root, "folder");
296  const std::string file = CreateRemoteFile(app_root, "file", "data");
297
298  AppendExpectedChange(URL(kOrigin, "folder"),
299                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
300                       SYNC_FILE_TYPE_DIRECTORY);
301  AppendExpectedChange(URL(kOrigin, "file"),
302                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
303                       SYNC_FILE_TYPE_FILE);
304
305  EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerAndPromoteUntilIdle());
306  VerifyConsistency();
307
308  DeleteRemoteFile(folder);
309  DeleteRemoteFile(file);
310
311  AppendExpectedChange(URL(kOrigin, "folder"),
312                       FileChange::FILE_CHANGE_DELETE,
313                       SYNC_FILE_TYPE_UNKNOWN);
314  AppendExpectedChange(URL(kOrigin, "file"),
315                       FileChange::FILE_CHANGE_DELETE,
316                       SYNC_FILE_TYPE_UNKNOWN);
317
318  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
319  EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle());
320  VerifyConsistency();
321
322  EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
323}
324
325TEST_F(RemoteToLocalSyncerTest, DeleteNestedFiles) {
326  const GURL kOrigin("chrome-extension://example");
327  const std::string sync_root = CreateSyncRoot();
328  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
329  InitializeMetadataDatabase();
330  RegisterApp(kOrigin.host(), app_root);
331
332  const std::string folder1 = CreateRemoteFolder(app_root, "folder1");
333  const std::string file1 = CreateRemoteFile(app_root, "file1", "data1");
334  const std::string folder2 = CreateRemoteFolder(folder1, "folder2");
335  const std::string file2 = CreateRemoteFile(folder1, "file2", "data2");
336
337  AppendExpectedChange(URL(kOrigin, "folder1"),
338                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
339                       SYNC_FILE_TYPE_DIRECTORY);
340  AppendExpectedChange(URL(kOrigin, "file1"),
341                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
342                       SYNC_FILE_TYPE_FILE);
343  AppendExpectedChange(URL(kOrigin, "folder1/folder2"),
344                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
345                       SYNC_FILE_TYPE_DIRECTORY);
346  AppendExpectedChange(URL(kOrigin, "folder1/file2"),
347                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
348                       SYNC_FILE_TYPE_FILE);
349
350  EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerAndPromoteUntilIdle());
351  VerifyConsistency();
352
353  DeleteRemoteFile(folder1);
354
355  AppendExpectedChange(URL(kOrigin, "folder1"),
356                       FileChange::FILE_CHANGE_DELETE,
357                       SYNC_FILE_TYPE_UNKNOWN);
358  // Changes for descendant files ("folder2" and "file2") should be ignored.
359
360  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
361  EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle());
362  VerifyConsistency();
363
364  EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
365}
366
367TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFileOnFolder) {
368  const GURL kOrigin("chrome-extension://example");
369  const std::string sync_root = CreateSyncRoot();
370  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
371  InitializeMetadataDatabase();
372  RegisterApp(kOrigin.host(), app_root);
373
374  CreateLocalFolder(URL(kOrigin, "folder"));
375  CreateRemoteFile(app_root, "folder", "data");
376
377  // Folder-File conflict happens. File creation should be ignored.
378
379  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
380  EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle());
381  VerifyConsistency();
382
383  // Tracker for the remote file should has low priority.
384  EXPECT_FALSE(GetMetadataDatabase()->GetDirtyTracker(NULL));
385  EXPECT_TRUE(GetMetadataDatabase()->HasDemotedDirtyTracker());
386}
387
388TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFile) {
389  const GURL kOrigin("chrome-extension://example");
390  const std::string sync_root = CreateSyncRoot();
391  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
392  InitializeMetadataDatabase();
393  RegisterApp(kOrigin.host(), app_root);
394
395  EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle());
396  VerifyConsistency();
397
398  CreateLocalFile(URL(kOrigin, "file"));
399  CreateRemoteFolder(app_root, "file");
400
401  // File-Folder conflict happens. Folder should override the existing file.
402  AppendExpectedChange(URL(kOrigin, "file"),
403                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
404                       SYNC_FILE_TYPE_DIRECTORY);
405
406  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
407  EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle());
408  VerifyConsistency();
409
410  EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
411}
412
413TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFolder) {
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  CreateLocalFolder(URL(kOrigin, "folder"));
421  CreateRemoteFolder(app_root, "folder");
422
423  // Folder-Folder conflict happens. Folder creation should be ignored.
424
425  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
426  EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle());
427  VerifyConsistency();
428
429  EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
430}
431
432TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFileOnFile) {
433  const GURL kOrigin("chrome-extension://example");
434  const std::string sync_root = CreateSyncRoot();
435  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
436  InitializeMetadataDatabase();
437  RegisterApp(kOrigin.host(), app_root);
438
439  CreateLocalFile(URL(kOrigin, "file"));
440  CreateRemoteFile(app_root, "file", "data");
441
442  // File-File conflict happens. File creation should be ignored.
443
444  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
445  EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle());
446  VerifyConsistency();
447
448  // Tracker for the remote file should be lowered.
449  EXPECT_FALSE(GetMetadataDatabase()->GetDirtyTracker(NULL));
450  EXPECT_TRUE(GetMetadataDatabase()->HasDemotedDirtyTracker());
451}
452
453TEST_F(RemoteToLocalSyncerTest, Conflict_CreateNestedFolderOnFile) {
454  const GURL kOrigin("chrome-extension://example");
455  const std::string sync_root = CreateSyncRoot();
456  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
457  InitializeMetadataDatabase();
458  RegisterApp(kOrigin.host(), app_root);
459
460  EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle());
461  VerifyConsistency();
462
463  const std::string folder = CreateRemoteFolder(app_root, "folder");
464  CreateLocalFile(URL(kOrigin, "/folder"));
465  CreateRemoteFile(folder, "file", "data");
466
467  // File-Folder conflict happens. Folder should override the existing file.
468  AppendExpectedChange(URL(kOrigin, "/folder"),
469                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
470                       SYNC_FILE_TYPE_DIRECTORY);
471
472  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
473  EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle());
474  VerifyConsistency();
475}
476
477TEST_F(RemoteToLocalSyncerTest, AppRootDeletion) {
478  const GURL kOrigin("chrome-extension://example");
479  const std::string sync_root = CreateSyncRoot();
480  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
481  InitializeMetadataDatabase();
482  RegisterApp(kOrigin.host(), app_root);
483
484  EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle());
485  VerifyConsistency();
486
487  DeleteRemoteFile(app_root);
488
489  AppendExpectedChange(URL(kOrigin, "/"),
490                       FileChange::FILE_CHANGE_DELETE,
491                       SYNC_FILE_TYPE_UNKNOWN);
492
493  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
494  EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle());
495  VerifyConsistency();
496
497  // SyncEngine will re-register the app and resurrect the app root later.
498}
499
500}  // namespace drive_backend
501}  // namespace sync_file_system
502