remote_to_local_syncer_unittest.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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        base::ThreadTaskRunnerHandle::Get()));
80    context_->SetRemoteChangeProcessor(remote_change_processor_.get());
81
82    RegisterSyncableFileSystem();
83
84    sync_task_manager_.reset(new SyncTaskManager(
85        base::WeakPtr<SyncTaskManager::Client>(),
86        10 /* max_parallel_task */,
87        base::ThreadTaskRunnerHandle::Get()));
88    sync_task_manager_->Initialize(SYNC_STATUS_OK);
89  }
90
91  virtual void TearDown() OVERRIDE {
92    sync_task_manager_.reset();
93    RevokeSyncableFileSystem();
94    fake_drive_helper_.reset();
95    context_.reset();
96    base::RunLoop().RunUntilIdle();
97  }
98
99  void InitializeMetadataDatabase() {
100    SyncEngineInitializer* initializer =
101        new SyncEngineInitializer(context_.get(),
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    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    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->RunPreflight(SyncTaskToken::CreateForTesting(
188        CreateResultReceiver(&status)));
189    base::RunLoop().RunUntilIdle();
190    return status;
191  }
192
193  void RunSyncerUntilIdle() {
194    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
195    while (status != SYNC_STATUS_NO_CHANGE_TO_SYNC)
196      status = RunSyncer();
197  }
198
199  SyncStatusCode ListChanges() {
200    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
201    sync_task_manager_->ScheduleSyncTask(
202        FROM_HERE,
203        scoped_ptr<SyncTask>(new ListChangesTask(context_.get())),
204        SyncTaskManager::PRIORITY_MED,
205        CreateResultReceiver(&status));
206    base::RunLoop().RunUntilIdle();
207    return status;
208  }
209
210  void AppendExpectedChange(const fileapi::FileSystemURL& url,
211                            FileChange::ChangeType change_type,
212                            SyncFileType file_type) {
213    expected_changes_[url].push_back(FileChange(change_type, file_type));
214  }
215
216  void VerifyConsistency() {
217    remote_change_processor_->VerifyConsistency(expected_changes_);
218  }
219
220 private:
221  content::TestBrowserThreadBundle thread_bundle_;
222  base::ScopedTempDir database_dir_;
223  scoped_ptr<leveldb::Env> in_memory_env_;
224
225  scoped_ptr<SyncEngineContext> context_;
226  scoped_ptr<FakeDriveServiceHelper> fake_drive_helper_;
227  scoped_ptr<FakeRemoteChangeProcessor> remote_change_processor_;
228
229  scoped_ptr<SyncTaskManager> sync_task_manager_;
230
231  URLToFileChangesMap expected_changes_;
232
233  DISALLOW_COPY_AND_ASSIGN(RemoteToLocalSyncerTest);
234};
235
236TEST_F(RemoteToLocalSyncerTest, AddNewFile) {
237  const GURL kOrigin("chrome-extension://example");
238  const std::string sync_root = CreateSyncRoot();
239  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
240  InitializeMetadataDatabase();
241  RegisterApp(kOrigin.host(), app_root);
242
243  const std::string folder1 = CreateRemoteFolder(app_root, "folder1");
244  const std::string file1 = CreateRemoteFile(app_root, "file1", "data1");
245  const std::string folder2 = CreateRemoteFolder(folder1, "folder2");
246  const std::string file2 = CreateRemoteFile(folder1, "file2", "data2");
247
248  RunSyncerUntilIdle();
249
250  // Create expected changes.
251  // TODO(nhiroki): Clean up creating URL part.
252  AppendExpectedChange(URL(kOrigin, "folder1"),
253                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
254                       SYNC_FILE_TYPE_DIRECTORY);
255  AppendExpectedChange(URL(kOrigin, "file1"),
256                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
257                       SYNC_FILE_TYPE_FILE);
258  AppendExpectedChange(URL(kOrigin, "folder1/folder2"),
259                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
260                       SYNC_FILE_TYPE_DIRECTORY);
261  AppendExpectedChange(URL(kOrigin, "folder1/file2"),
262                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
263                       SYNC_FILE_TYPE_FILE);
264
265  VerifyConsistency();
266
267  EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
268}
269
270TEST_F(RemoteToLocalSyncerTest, DeleteFile) {
271  const GURL kOrigin("chrome-extension://example");
272  const std::string sync_root = CreateSyncRoot();
273  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
274  InitializeMetadataDatabase();
275  RegisterApp(kOrigin.host(), app_root);
276
277  const std::string folder = CreateRemoteFolder(app_root, "folder");
278  const std::string file = CreateRemoteFile(app_root, "file", "data");
279
280  AppendExpectedChange(URL(kOrigin, "folder"),
281                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
282                       SYNC_FILE_TYPE_DIRECTORY);
283  AppendExpectedChange(URL(kOrigin, "file"),
284                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
285                       SYNC_FILE_TYPE_FILE);
286
287  RunSyncerUntilIdle();
288  VerifyConsistency();
289
290  DeleteRemoteFile(folder);
291  DeleteRemoteFile(file);
292
293  AppendExpectedChange(URL(kOrigin, "folder"),
294                       FileChange::FILE_CHANGE_DELETE,
295                       SYNC_FILE_TYPE_UNKNOWN);
296  AppendExpectedChange(URL(kOrigin, "file"),
297                       FileChange::FILE_CHANGE_DELETE,
298                       SYNC_FILE_TYPE_UNKNOWN);
299
300  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
301  RunSyncerUntilIdle();
302  VerifyConsistency();
303
304  EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
305}
306
307TEST_F(RemoteToLocalSyncerTest, DeleteNestedFiles) {
308  const GURL kOrigin("chrome-extension://example");
309  const std::string sync_root = CreateSyncRoot();
310  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
311  InitializeMetadataDatabase();
312  RegisterApp(kOrigin.host(), app_root);
313
314  const std::string folder1 = CreateRemoteFolder(app_root, "folder1");
315  const std::string file1 = CreateRemoteFile(app_root, "file1", "data1");
316  const std::string folder2 = CreateRemoteFolder(folder1, "folder2");
317  const std::string file2 = CreateRemoteFile(folder1, "file2", "data2");
318
319  AppendExpectedChange(URL(kOrigin, "folder1"),
320                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
321                       SYNC_FILE_TYPE_DIRECTORY);
322  AppendExpectedChange(URL(kOrigin, "file1"),
323                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
324                       SYNC_FILE_TYPE_FILE);
325  AppendExpectedChange(URL(kOrigin, "folder1/folder2"),
326                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
327                       SYNC_FILE_TYPE_DIRECTORY);
328  AppendExpectedChange(URL(kOrigin, "folder1/file2"),
329                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
330                       SYNC_FILE_TYPE_FILE);
331
332  RunSyncerUntilIdle();
333  VerifyConsistency();
334
335  DeleteRemoteFile(folder1);
336
337  AppendExpectedChange(URL(kOrigin, "folder1"),
338                       FileChange::FILE_CHANGE_DELETE,
339                       SYNC_FILE_TYPE_UNKNOWN);
340  // Changes for descendant files ("folder2" and "file2") should be ignored.
341
342  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
343  RunSyncerUntilIdle();
344  VerifyConsistency();
345
346  EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
347}
348
349TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFileOnFolder) {
350  const GURL kOrigin("chrome-extension://example");
351  const std::string sync_root = CreateSyncRoot();
352  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
353  InitializeMetadataDatabase();
354  RegisterApp(kOrigin.host(), app_root);
355
356  CreateLocalFolder(URL(kOrigin, "folder"));
357  CreateRemoteFile(app_root, "folder", "data");
358
359  // Folder-File conflict happens. File creation should be ignored.
360
361  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
362  RunSyncerUntilIdle();
363  VerifyConsistency();
364
365  // Tracker for the remote file should be lowered.
366  EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
367  EXPECT_TRUE(GetMetadataDatabase()->HasLowPriorityDirtyTracker());
368}
369
370TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFile) {
371  const GURL kOrigin("chrome-extension://example");
372  const std::string sync_root = CreateSyncRoot();
373  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
374  InitializeMetadataDatabase();
375  RegisterApp(kOrigin.host(), app_root);
376
377  RunSyncerUntilIdle();
378  VerifyConsistency();
379
380  CreateLocalFile(URL(kOrigin, "file"));
381  CreateRemoteFolder(app_root, "file");
382
383  // File-Folder conflict happens. Folder should override the existing file.
384  AppendExpectedChange(URL(kOrigin, "file"),
385                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
386                       SYNC_FILE_TYPE_DIRECTORY);
387
388  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
389  RunSyncerUntilIdle();
390  VerifyConsistency();
391
392  EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
393}
394
395TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFolder) {
396  const GURL kOrigin("chrome-extension://example");
397  const std::string sync_root = CreateSyncRoot();
398  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
399  InitializeMetadataDatabase();
400  RegisterApp(kOrigin.host(), app_root);
401
402  CreateLocalFolder(URL(kOrigin, "folder"));
403  CreateRemoteFolder(app_root, "folder");
404
405  // Folder-Folder conflict happens. Folder creation should be ignored.
406
407  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
408  RunSyncerUntilIdle();
409  VerifyConsistency();
410
411  EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
412}
413
414TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFileOnFile) {
415  const GURL kOrigin("chrome-extension://example");
416  const std::string sync_root = CreateSyncRoot();
417  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
418  InitializeMetadataDatabase();
419  RegisterApp(kOrigin.host(), app_root);
420
421  CreateLocalFile(URL(kOrigin, "file"));
422  CreateRemoteFile(app_root, "file", "data");
423
424  // File-File conflict happens. File creation should be ignored.
425
426  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
427  RunSyncerUntilIdle();
428  VerifyConsistency();
429
430  // Tracker for the remote file should be lowered.
431  EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
432  EXPECT_TRUE(GetMetadataDatabase()->HasLowPriorityDirtyTracker());
433}
434
435TEST_F(RemoteToLocalSyncerTest, Conflict_CreateNestedFolderOnFile) {
436  const GURL kOrigin("chrome-extension://example");
437  const std::string sync_root = CreateSyncRoot();
438  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
439  InitializeMetadataDatabase();
440  RegisterApp(kOrigin.host(), app_root);
441
442  RunSyncerUntilIdle();
443  VerifyConsistency();
444
445  const std::string folder = CreateRemoteFolder(app_root, "folder");
446  CreateLocalFile(URL(kOrigin, "/folder"));
447  CreateRemoteFile(folder, "file", "data");
448
449  // File-Folder conflict happens. Folder should override the existing file.
450  AppendExpectedChange(URL(kOrigin, "/folder"),
451                       FileChange::FILE_CHANGE_ADD_OR_UPDATE,
452                       SYNC_FILE_TYPE_DIRECTORY);
453
454  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
455  RunSyncerUntilIdle();
456  VerifyConsistency();
457}
458
459TEST_F(RemoteToLocalSyncerTest, AppRootDeletion) {
460  const GURL kOrigin("chrome-extension://example");
461  const std::string sync_root = CreateSyncRoot();
462  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
463  InitializeMetadataDatabase();
464  RegisterApp(kOrigin.host(), app_root);
465
466  RunSyncerUntilIdle();
467  VerifyConsistency();
468
469  DeleteRemoteFile(app_root);
470
471  AppendExpectedChange(URL(kOrigin, "/"),
472                       FileChange::FILE_CHANGE_DELETE,
473                       SYNC_FILE_TYPE_UNKNOWN);
474
475  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
476  RunSyncerUntilIdle();
477  VerifyConsistency();
478
479  // SyncEngine will re-register the app and resurrect the app root later.
480}
481
482}  // namespace drive_backend
483}  // namespace sync_file_system
484