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.
5 #include "chrome/browser/sync_file_system/drive_backend/remote_to_local_syncer.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"
35 namespace sync_file_system {
36 namespace drive_backend {
40 storage::FileSystemURL URL(const GURL& origin, const std::string& path) {
41 return CreateSyncableFileSystemURL(
42 origin, base::FilePath::FromUTF8Unsafe(path));
47 class RemoteToLocalSyncerTest : public testing::Test {
49 typedef FakeRemoteChangeProcessor::URLToFileChangesMap URLToFileChangesMap;
51 RemoteToLocalSyncerTest()
52 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
53 ~RemoteToLocalSyncerTest() override {}
55 void SetUp() override {
56 ASSERT_TRUE(database_dir_.CreateUniqueTempDir());
57 in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
59 scoped_ptr<drive::FakeDriveService>
60 fake_drive_service(new drive::FakeDriveService);
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(),
69 kSyncRootFolderTitle));
70 remote_change_processor_.reset(new FakeRemoteChangeProcessor);
72 context_.reset(new SyncEngineContext(fake_drive_service.Pass(),
73 drive_uploader.Pass(),
75 base::ThreadTaskRunnerHandle::Get(),
76 base::ThreadTaskRunnerHandle::Get()));
77 context_->SetRemoteChangeProcessor(remote_change_processor_.get());
79 RegisterSyncableFileSystem();
81 sync_task_manager_.reset(new SyncTaskManager(
82 base::WeakPtr<SyncTaskManager::Client>(),
83 10 /* max_parallel_task */,
84 base::ThreadTaskRunnerHandle::Get()));
85 sync_task_manager_->Initialize(SYNC_STATUS_OK);
88 void TearDown() override {
89 sync_task_manager_.reset();
90 RevokeSyncableFileSystem();
91 fake_drive_helper_.reset();
93 base::RunLoop().RunUntilIdle();
96 void InitializeMetadataDatabase() {
97 SyncEngineInitializer* initializer =
98 new SyncEngineInitializer(context_.get(),
100 in_memory_env_.get());
101 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
102 sync_task_manager_->ScheduleSyncTask(
104 scoped_ptr<SyncTask>(initializer),
105 SyncTaskManager::PRIORITY_MED,
106 base::Bind(&RemoteToLocalSyncerTest::DidInitializeMetadataDatabase,
107 base::Unretained(this),
108 initializer, &status));
110 base::RunLoop().RunUntilIdle();
111 EXPECT_EQ(SYNC_STATUS_OK, status);
114 void DidInitializeMetadataDatabase(SyncEngineInitializer* initializer,
115 SyncStatusCode* status_out,
116 SyncStatusCode status) {
117 *status_out = status;
118 context_->SetMetadataDatabase(initializer->PassMetadataDatabase());
122 void RegisterApp(const std::string& app_id,
123 const std::string& app_root_folder_id) {
124 SyncStatusCode status = context_->GetMetadataDatabase()->RegisterApp(
125 app_id, app_root_folder_id);
126 EXPECT_EQ(SYNC_STATUS_OK, status);
129 MetadataDatabase* GetMetadataDatabase() {
130 return context_->GetMetadataDatabase();
134 std::string CreateSyncRoot() {
135 std::string sync_root_folder_id;
136 EXPECT_EQ(google_apis::HTTP_CREATED,
137 fake_drive_helper_->AddOrphanedFolder(
138 kSyncRootFolderTitle, &sync_root_folder_id));
139 return sync_root_folder_id;
142 std::string CreateRemoteFolder(const std::string& parent_folder_id,
143 const std::string& title) {
144 std::string folder_id;
145 EXPECT_EQ(google_apis::HTTP_CREATED,
146 fake_drive_helper_->AddFolder(
147 parent_folder_id, title, &folder_id));
151 std::string CreateRemoteFile(const std::string& parent_folder_id,
152 const std::string& title,
153 const std::string& content) {
155 EXPECT_EQ(google_apis::HTTP_SUCCESS,
156 fake_drive_helper_->AddFile(
157 parent_folder_id, title, content, &file_id));
161 void DeleteRemoteFile(const std::string& file_id) {
162 EXPECT_EQ(google_apis::HTTP_NO_CONTENT,
163 fake_drive_helper_->DeleteResource(file_id));
166 void CreateLocalFolder(const storage::FileSystemURL& url) {
167 remote_change_processor_->UpdateLocalFileMetadata(
168 url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
169 SYNC_FILE_TYPE_DIRECTORY));
172 void CreateLocalFile(const storage::FileSystemURL& url) {
173 remote_change_processor_->UpdateLocalFileMetadata(
174 url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
175 SYNC_FILE_TYPE_FILE));
178 SyncStatusCode RunSyncer() {
179 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
180 scoped_ptr<RemoteToLocalSyncer>
181 syncer(new RemoteToLocalSyncer(context_.get()));
182 syncer->RunPreflight(SyncTaskToken::CreateForTesting(
183 CreateResultReceiver(&status)));
184 base::RunLoop().RunUntilIdle();
188 SyncStatusCode RunSyncerUntilIdle() {
189 const int kRetryLimit = 100;
190 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
193 if (count++ > kRetryLimit)
195 status = RunSyncer();
196 } while (status == SYNC_STATUS_OK ||
197 status == SYNC_STATUS_RETRY);
201 SyncStatusCode RunSyncerAndPromoteUntilIdle() {
202 const int kRetryLimit = 100;
203 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
204 MetadataDatabase* metadata_database = context_->GetMetadataDatabase();
207 if (count++ > kRetryLimit)
209 status = RunSyncer();
210 } while (status == SYNC_STATUS_OK ||
211 status == SYNC_STATUS_RETRY ||
212 metadata_database->PromoteDemotedTrackers());
216 SyncStatusCode ListChanges() {
217 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
218 sync_task_manager_->ScheduleSyncTask(
220 scoped_ptr<SyncTask>(new ListChangesTask(context_.get())),
221 SyncTaskManager::PRIORITY_MED,
222 CreateResultReceiver(&status));
223 base::RunLoop().RunUntilIdle();
227 void AppendExpectedChange(const storage::FileSystemURL& url,
228 FileChange::ChangeType change_type,
229 SyncFileType file_type) {
230 expected_changes_[url].push_back(FileChange(change_type, file_type));
233 void VerifyConsistency() {
234 remote_change_processor_->VerifyConsistency(expected_changes_);
238 content::TestBrowserThreadBundle thread_bundle_;
239 base::ScopedTempDir database_dir_;
240 scoped_ptr<leveldb::Env> in_memory_env_;
242 scoped_ptr<SyncEngineContext> context_;
243 scoped_ptr<FakeDriveServiceHelper> fake_drive_helper_;
244 scoped_ptr<FakeRemoteChangeProcessor> remote_change_processor_;
246 scoped_ptr<SyncTaskManager> sync_task_manager_;
248 URLToFileChangesMap expected_changes_;
250 DISALLOW_COPY_AND_ASSIGN(RemoteToLocalSyncerTest);
253 TEST_F(RemoteToLocalSyncerTest, AddNewFile) {
254 const GURL kOrigin("chrome-extension://example");
255 const std::string sync_root = CreateSyncRoot();
256 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
257 InitializeMetadataDatabase();
258 RegisterApp(kOrigin.host(), app_root);
260 const std::string folder1 = CreateRemoteFolder(app_root, "folder1");
261 const std::string file1 = CreateRemoteFile(app_root, "file1", "data1");
262 const std::string folder2 = CreateRemoteFolder(folder1, "folder2");
263 const std::string file2 = CreateRemoteFile(folder1, "file2", "data2");
265 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerAndPromoteUntilIdle());
267 // Create expected changes.
268 // TODO(nhiroki): Clean up creating URL part.
269 AppendExpectedChange(URL(kOrigin, "folder1"),
270 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
271 SYNC_FILE_TYPE_DIRECTORY);
272 AppendExpectedChange(URL(kOrigin, "file1"),
273 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
274 SYNC_FILE_TYPE_FILE);
275 AppendExpectedChange(URL(kOrigin, "folder1/folder2"),
276 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
277 SYNC_FILE_TYPE_DIRECTORY);
278 AppendExpectedChange(URL(kOrigin, "folder1/file2"),
279 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
280 SYNC_FILE_TYPE_FILE);
284 EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
287 TEST_F(RemoteToLocalSyncerTest, DeleteFile) {
288 const GURL kOrigin("chrome-extension://example");
289 const std::string sync_root = CreateSyncRoot();
290 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
291 InitializeMetadataDatabase();
292 RegisterApp(kOrigin.host(), app_root);
294 const std::string folder = CreateRemoteFolder(app_root, "folder");
295 const std::string file = CreateRemoteFile(app_root, "file", "data");
297 AppendExpectedChange(URL(kOrigin, "folder"),
298 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
299 SYNC_FILE_TYPE_DIRECTORY);
300 AppendExpectedChange(URL(kOrigin, "file"),
301 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
302 SYNC_FILE_TYPE_FILE);
304 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerAndPromoteUntilIdle());
307 DeleteRemoteFile(folder);
308 DeleteRemoteFile(file);
310 AppendExpectedChange(URL(kOrigin, "folder"),
311 FileChange::FILE_CHANGE_DELETE,
312 SYNC_FILE_TYPE_UNKNOWN);
313 AppendExpectedChange(URL(kOrigin, "file"),
314 FileChange::FILE_CHANGE_DELETE,
315 SYNC_FILE_TYPE_UNKNOWN);
317 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
318 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle());
321 EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
324 TEST_F(RemoteToLocalSyncerTest, DeleteNestedFiles) {
325 const GURL kOrigin("chrome-extension://example");
326 const std::string sync_root = CreateSyncRoot();
327 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
328 InitializeMetadataDatabase();
329 RegisterApp(kOrigin.host(), app_root);
331 const std::string folder1 = CreateRemoteFolder(app_root, "folder1");
332 const std::string file1 = CreateRemoteFile(app_root, "file1", "data1");
333 const std::string folder2 = CreateRemoteFolder(folder1, "folder2");
334 const std::string file2 = CreateRemoteFile(folder1, "file2", "data2");
336 AppendExpectedChange(URL(kOrigin, "folder1"),
337 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
338 SYNC_FILE_TYPE_DIRECTORY);
339 AppendExpectedChange(URL(kOrigin, "file1"),
340 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
341 SYNC_FILE_TYPE_FILE);
342 AppendExpectedChange(URL(kOrigin, "folder1/folder2"),
343 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
344 SYNC_FILE_TYPE_DIRECTORY);
345 AppendExpectedChange(URL(kOrigin, "folder1/file2"),
346 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
347 SYNC_FILE_TYPE_FILE);
349 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerAndPromoteUntilIdle());
352 DeleteRemoteFile(folder1);
354 AppendExpectedChange(URL(kOrigin, "folder1"),
355 FileChange::FILE_CHANGE_DELETE,
356 SYNC_FILE_TYPE_UNKNOWN);
357 // Changes for descendant files ("folder2" and "file2") should be ignored.
359 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
360 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle());
363 EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
366 TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFileOnFolder) {
367 const GURL kOrigin("chrome-extension://example");
368 const std::string sync_root = CreateSyncRoot();
369 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
370 InitializeMetadataDatabase();
371 RegisterApp(kOrigin.host(), app_root);
373 CreateLocalFolder(URL(kOrigin, "folder"));
374 CreateRemoteFile(app_root, "folder", "data");
376 // Folder-File conflict happens. File creation should be ignored.
378 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
379 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle());
382 // Tracker for the remote file should has low priority.
383 EXPECT_FALSE(GetMetadataDatabase()->GetDirtyTracker(nullptr));
384 EXPECT_TRUE(GetMetadataDatabase()->HasDemotedDirtyTracker());
387 TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFile) {
388 const GURL kOrigin("chrome-extension://example");
389 const std::string sync_root = CreateSyncRoot();
390 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
391 InitializeMetadataDatabase();
392 RegisterApp(kOrigin.host(), app_root);
394 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle());
397 CreateLocalFile(URL(kOrigin, "file"));
398 CreateRemoteFolder(app_root, "file");
400 // File-Folder conflict happens. Folder should override the existing file.
401 AppendExpectedChange(URL(kOrigin, "file"),
402 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
403 SYNC_FILE_TYPE_DIRECTORY);
405 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
406 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle());
409 EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
412 TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFolder) {
413 const GURL kOrigin("chrome-extension://example");
414 const std::string sync_root = CreateSyncRoot();
415 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
416 InitializeMetadataDatabase();
417 RegisterApp(kOrigin.host(), app_root);
419 CreateLocalFolder(URL(kOrigin, "folder"));
420 CreateRemoteFolder(app_root, "folder");
422 // Folder-Folder conflict happens. Folder creation should be ignored.
424 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
425 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle());
428 EXPECT_FALSE(GetMetadataDatabase()->HasDirtyTracker());
431 TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFileOnFile) {
432 const GURL kOrigin("chrome-extension://example");
433 const std::string sync_root = CreateSyncRoot();
434 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
435 InitializeMetadataDatabase();
436 RegisterApp(kOrigin.host(), app_root);
438 CreateLocalFile(URL(kOrigin, "file"));
439 CreateRemoteFile(app_root, "file", "data");
441 // File-File conflict happens. File creation should be ignored.
443 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
444 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle());
447 // Tracker for the remote file should be lowered.
448 EXPECT_FALSE(GetMetadataDatabase()->GetDirtyTracker(nullptr));
449 EXPECT_TRUE(GetMetadataDatabase()->HasDemotedDirtyTracker());
452 TEST_F(RemoteToLocalSyncerTest, Conflict_CreateNestedFolderOnFile) {
453 const GURL kOrigin("chrome-extension://example");
454 const std::string sync_root = CreateSyncRoot();
455 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
456 InitializeMetadataDatabase();
457 RegisterApp(kOrigin.host(), app_root);
459 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle());
462 const std::string folder = CreateRemoteFolder(app_root, "folder");
463 CreateLocalFile(URL(kOrigin, "/folder"));
464 CreateRemoteFile(folder, "file", "data");
466 // File-Folder conflict happens. Folder should override the existing file.
467 AppendExpectedChange(URL(kOrigin, "/folder"),
468 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
469 SYNC_FILE_TYPE_DIRECTORY);
471 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
472 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle());
476 TEST_F(RemoteToLocalSyncerTest, AppRootDeletion) {
477 const GURL kOrigin("chrome-extension://example");
478 const std::string sync_root = CreateSyncRoot();
479 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
480 InitializeMetadataDatabase();
481 RegisterApp(kOrigin.host(), app_root);
483 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle());
486 DeleteRemoteFile(app_root);
488 AppendExpectedChange(URL(kOrigin, "/"),
489 FileChange::FILE_CHANGE_DELETE,
490 SYNC_FILE_TYPE_UNKNOWN);
492 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
493 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, RunSyncerUntilIdle());
496 // SyncEngine will re-register the app and resurrect the app root later.
499 } // namespace drive_backend
500 } // namespace sync_file_system