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 "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/fake_remote_change_processor.h"
24 #include "chrome/browser/sync_file_system/sync_file_system_test_util.h"
25 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
26 #include "content/public/test/test_browser_thread_bundle.h"
27 #include "google_apis/drive/gdata_errorcode.h"
28 #include "testing/gtest/include/gtest/gtest.h"
29 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
30 #include "third_party/leveldatabase/src/include/leveldb/env.h"
32 namespace sync_file_system {
33 namespace drive_backend {
37 fileapi::FileSystemURL URL(const GURL& origin,
38 const std::string& path) {
39 return CreateSyncableFileSystemURL(
40 origin, base::FilePath::FromUTF8Unsafe(path));
45 class RemoteToLocalSyncerTest : public testing::Test,
46 public SyncEngineContext {
48 typedef FakeRemoteChangeProcessor::URLToFileChangesMap URLToFileChangesMap;
50 RemoteToLocalSyncerTest()
51 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
52 virtual ~RemoteToLocalSyncerTest() {}
54 virtual void SetUp() OVERRIDE {
55 ASSERT_TRUE(database_dir_.CreateUniqueTempDir());
56 in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
58 fake_drive_service_.reset(new drive::FakeDriveService);
59 ASSERT_TRUE(fake_drive_service_->LoadAccountMetadataForWapi(
60 "sync_file_system/account_metadata.json"));
61 ASSERT_TRUE(fake_drive_service_->LoadResourceListForWapi(
62 "gdata/empty_feed.json"));
64 drive_uploader_.reset(new drive::DriveUploader(
65 fake_drive_service_.get(), base::MessageLoopProxy::current().get()));
66 fake_drive_helper_.reset(new FakeDriveServiceHelper(
67 fake_drive_service_.get(), drive_uploader_.get(),
68 kSyncRootFolderTitle));
69 fake_remote_change_processor_.reset(new FakeRemoteChangeProcessor);
71 RegisterSyncableFileSystem();
74 virtual void TearDown() OVERRIDE {
75 RevokeSyncableFileSystem();
77 fake_remote_change_processor_.reset();
78 metadata_database_.reset();
79 fake_drive_helper_.reset();
80 drive_uploader_.reset();
81 fake_drive_service_.reset();
82 base::RunLoop().RunUntilIdle();
85 void InitializeMetadataDatabase() {
86 SyncEngineInitializer initializer(this,
87 base::MessageLoopProxy::current(),
88 fake_drive_service_.get(),
90 in_memory_env_.get());
91 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
92 initializer.Run(CreateResultReceiver(&status));
93 base::RunLoop().RunUntilIdle();
94 EXPECT_EQ(SYNC_STATUS_OK, status);
95 metadata_database_ = initializer.PassMetadataDatabase();
98 void RegisterApp(const std::string& app_id,
99 const std::string& app_root_folder_id) {
100 SyncStatusCode status = SYNC_STATUS_FAILED;
101 metadata_database_->RegisterApp(app_id, app_root_folder_id,
102 CreateResultReceiver(&status));
103 base::RunLoop().RunUntilIdle();
104 EXPECT_EQ(SYNC_STATUS_OK, status);
107 virtual drive::DriveServiceInterface* GetDriveService() OVERRIDE {
108 return fake_drive_service_.get();
111 virtual drive::DriveUploaderInterface* GetDriveUploader() OVERRIDE {
112 return drive_uploader_.get();
115 virtual MetadataDatabase* GetMetadataDatabase() OVERRIDE {
116 return metadata_database_.get();
119 virtual RemoteChangeProcessor* GetRemoteChangeProcessor() OVERRIDE {
120 return fake_remote_change_processor_.get();
123 virtual base::SequencedTaskRunner* GetBlockingTaskRunner() OVERRIDE {
124 return base::MessageLoopProxy::current().get();
128 std::string CreateSyncRoot() {
129 std::string sync_root_folder_id;
130 EXPECT_EQ(google_apis::HTTP_CREATED,
131 fake_drive_helper_->AddOrphanedFolder(
132 kSyncRootFolderTitle, &sync_root_folder_id));
133 return sync_root_folder_id;
136 std::string CreateRemoteFolder(const std::string& parent_folder_id,
137 const std::string& title) {
138 std::string folder_id;
139 EXPECT_EQ(google_apis::HTTP_CREATED,
140 fake_drive_helper_->AddFolder(
141 parent_folder_id, title, &folder_id));
145 std::string CreateRemoteFile(const std::string& parent_folder_id,
146 const std::string& title,
147 const std::string& content) {
149 EXPECT_EQ(google_apis::HTTP_SUCCESS,
150 fake_drive_helper_->AddFile(
151 parent_folder_id, title, content, &file_id));
155 void DeleteRemoteFile(const std::string& file_id) {
156 EXPECT_EQ(google_apis::HTTP_NO_CONTENT,
157 fake_drive_helper_->DeleteResource(file_id));
160 void CreateLocalFolder(const fileapi::FileSystemURL& url) {
161 fake_remote_change_processor_->UpdateLocalFileMetadata(
162 url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
163 SYNC_FILE_TYPE_DIRECTORY));
166 void CreateLocalFile(const fileapi::FileSystemURL& url) {
167 fake_remote_change_processor_->UpdateLocalFileMetadata(
168 url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
169 SYNC_FILE_TYPE_FILE));
172 SyncStatusCode RunSyncer() {
173 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
174 scoped_ptr<RemoteToLocalSyncer> syncer(new RemoteToLocalSyncer(this));
175 syncer->Run(CreateResultReceiver(&status));
176 base::RunLoop().RunUntilIdle();
180 void RunSyncerUntilIdle() {
181 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
182 while (status != SYNC_STATUS_NO_CHANGE_TO_SYNC)
183 status = RunSyncer();
186 SyncStatusCode ListChanges() {
187 ListChangesTask list_changes(this);
188 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
189 list_changes.Run(CreateResultReceiver(&status));
190 base::RunLoop().RunUntilIdle();
194 void AppendExpectedChange(const fileapi::FileSystemURL& url,
195 FileChange::ChangeType change_type,
196 SyncFileType file_type) {
197 expected_changes_[url].push_back(FileChange(change_type, file_type));
200 void VerifyConsistency() {
201 fake_remote_change_processor_->VerifyConsistency(expected_changes_);
205 content::TestBrowserThreadBundle thread_bundle_;
206 base::ScopedTempDir database_dir_;
207 scoped_ptr<leveldb::Env> in_memory_env_;
209 scoped_ptr<drive::FakeDriveService> fake_drive_service_;
210 scoped_ptr<drive::DriveUploader> drive_uploader_;
211 scoped_ptr<FakeDriveServiceHelper> fake_drive_helper_;
212 scoped_ptr<MetadataDatabase> metadata_database_;
213 scoped_ptr<FakeRemoteChangeProcessor> fake_remote_change_processor_;
215 URLToFileChangesMap expected_changes_;
217 DISALLOW_COPY_AND_ASSIGN(RemoteToLocalSyncerTest);
220 TEST_F(RemoteToLocalSyncerTest, AddNewFile) {
221 const GURL kOrigin("chrome-extension://example");
222 const std::string sync_root = CreateSyncRoot();
223 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
224 InitializeMetadataDatabase();
225 RegisterApp(kOrigin.host(), app_root);
227 const std::string folder1 = CreateRemoteFolder(app_root, "folder1");
228 const std::string file1 = CreateRemoteFile(app_root, "file1", "data1");
229 const std::string folder2 = CreateRemoteFolder(folder1, "folder2");
230 const std::string file2 = CreateRemoteFile(folder1, "file2", "data2");
232 RunSyncerUntilIdle();
234 // Create expected changes.
235 // TODO(nhiroki): Clean up creating URL part.
236 AppendExpectedChange(URL(kOrigin, "folder1"),
237 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
238 SYNC_FILE_TYPE_DIRECTORY);
239 AppendExpectedChange(URL(kOrigin, "file1"),
240 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
241 SYNC_FILE_TYPE_FILE);
242 AppendExpectedChange(URL(kOrigin, "folder1/folder2"),
243 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
244 SYNC_FILE_TYPE_DIRECTORY);
245 AppendExpectedChange(URL(kOrigin, "folder1/file2"),
246 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
247 SYNC_FILE_TYPE_FILE);
251 EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
252 EXPECT_FALSE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL));
255 TEST_F(RemoteToLocalSyncerTest, DeleteFile) {
256 const GURL kOrigin("chrome-extension://example");
257 const std::string sync_root = CreateSyncRoot();
258 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
259 InitializeMetadataDatabase();
260 RegisterApp(kOrigin.host(), app_root);
262 const std::string folder = CreateRemoteFolder(app_root, "folder");
263 const std::string file = CreateRemoteFile(app_root, "file", "data");
265 AppendExpectedChange(URL(kOrigin, "folder"),
266 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
267 SYNC_FILE_TYPE_DIRECTORY);
268 AppendExpectedChange(URL(kOrigin, "file"),
269 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
270 SYNC_FILE_TYPE_FILE);
272 RunSyncerUntilIdle();
275 DeleteRemoteFile(folder);
276 DeleteRemoteFile(file);
278 AppendExpectedChange(URL(kOrigin, "folder"),
279 FileChange::FILE_CHANGE_DELETE,
280 SYNC_FILE_TYPE_UNKNOWN);
281 AppendExpectedChange(URL(kOrigin, "file"),
282 FileChange::FILE_CHANGE_DELETE,
283 SYNC_FILE_TYPE_UNKNOWN);
285 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
286 RunSyncerUntilIdle();
289 EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
290 EXPECT_FALSE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL));
293 TEST_F(RemoteToLocalSyncerTest, DeleteNestedFiles) {
294 const GURL kOrigin("chrome-extension://example");
295 const std::string sync_root = CreateSyncRoot();
296 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
297 InitializeMetadataDatabase();
298 RegisterApp(kOrigin.host(), app_root);
300 const std::string folder1 = CreateRemoteFolder(app_root, "folder1");
301 const std::string file1 = CreateRemoteFile(app_root, "file1", "data1");
302 const std::string folder2 = CreateRemoteFolder(folder1, "folder2");
303 const std::string file2 = CreateRemoteFile(folder1, "file2", "data2");
305 AppendExpectedChange(URL(kOrigin, "folder1"),
306 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
307 SYNC_FILE_TYPE_DIRECTORY);
308 AppendExpectedChange(URL(kOrigin, "file1"),
309 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
310 SYNC_FILE_TYPE_FILE);
311 AppendExpectedChange(URL(kOrigin, "folder1/folder2"),
312 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
313 SYNC_FILE_TYPE_DIRECTORY);
314 AppendExpectedChange(URL(kOrigin, "folder1/file2"),
315 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
316 SYNC_FILE_TYPE_FILE);
318 RunSyncerUntilIdle();
321 DeleteRemoteFile(folder1);
323 AppendExpectedChange(URL(kOrigin, "folder1"),
324 FileChange::FILE_CHANGE_DELETE,
325 SYNC_FILE_TYPE_UNKNOWN);
326 // Changes for descendant files ("folder2" and "file2") should be ignored.
328 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
329 RunSyncerUntilIdle();
332 EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
333 EXPECT_FALSE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL));
336 TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFileOnFolder) {
337 const GURL kOrigin("chrome-extension://example");
338 const std::string sync_root = CreateSyncRoot();
339 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
340 InitializeMetadataDatabase();
341 RegisterApp(kOrigin.host(), app_root);
343 CreateLocalFolder(URL(kOrigin, "folder"));
344 CreateRemoteFile(app_root, "folder", "data");
346 // Folder-File conflict happens. File creation should be ignored.
348 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
349 RunSyncerUntilIdle();
352 // Tracker for the remote file should be lowered.
353 EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
354 EXPECT_TRUE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL));
357 TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFile) {
358 const GURL kOrigin("chrome-extension://example");
359 const std::string sync_root = CreateSyncRoot();
360 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
361 InitializeMetadataDatabase();
362 RegisterApp(kOrigin.host(), app_root);
364 RunSyncerUntilIdle();
367 CreateLocalFile(URL(kOrigin, "file"));
368 CreateRemoteFolder(app_root, "file");
370 // File-Folder conflict happens. Folder should override the existing file.
371 AppendExpectedChange(URL(kOrigin, "file"),
372 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
373 SYNC_FILE_TYPE_DIRECTORY);
375 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
376 RunSyncerUntilIdle();
379 EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
380 EXPECT_FALSE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL));
383 TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFolder) {
384 const GURL kOrigin("chrome-extension://example");
385 const std::string sync_root = CreateSyncRoot();
386 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
387 InitializeMetadataDatabase();
388 RegisterApp(kOrigin.host(), app_root);
390 CreateLocalFolder(URL(kOrigin, "folder"));
391 CreateRemoteFolder(app_root, "folder");
393 // Folder-Folder conflict happens. Folder creation should be ignored.
395 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
396 RunSyncerUntilIdle();
399 EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
400 EXPECT_FALSE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL));
403 TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFileOnFile) {
404 const GURL kOrigin("chrome-extension://example");
405 const std::string sync_root = CreateSyncRoot();
406 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
407 InitializeMetadataDatabase();
408 RegisterApp(kOrigin.host(), app_root);
410 CreateLocalFile(URL(kOrigin, "file"));
411 CreateRemoteFile(app_root, "file", "data");
413 // File-File conflict happens. File creation should be ignored.
415 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
416 RunSyncerUntilIdle();
419 // Tracker for the remote file should be lowered.
420 EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
421 EXPECT_TRUE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL));
424 TEST_F(RemoteToLocalSyncerTest, Conflict_CreateNestedFolderOnFile) {
425 const GURL kOrigin("chrome-extension://example");
426 const std::string sync_root = CreateSyncRoot();
427 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
428 InitializeMetadataDatabase();
429 RegisterApp(kOrigin.host(), app_root);
431 RunSyncerUntilIdle();
434 const std::string folder = CreateRemoteFolder(app_root, "folder");
435 CreateLocalFile(URL(kOrigin, "/folder"));
436 CreateRemoteFile(folder, "file", "data");
438 // File-Folder conflict happens. Folder should override the existing file.
439 AppendExpectedChange(URL(kOrigin, "/folder"),
440 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
441 SYNC_FILE_TYPE_DIRECTORY);
443 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
444 RunSyncerUntilIdle();
448 TEST_F(RemoteToLocalSyncerTest, AppRootDeletion) {
449 const GURL kOrigin("chrome-extension://example");
450 const std::string sync_root = CreateSyncRoot();
451 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
452 InitializeMetadataDatabase();
453 RegisterApp(kOrigin.host(), app_root);
455 RunSyncerUntilIdle();
458 DeleteRemoteFile(app_root);
460 AppendExpectedChange(URL(kOrigin, "/"),
461 FileChange::FILE_CHANGE_DELETE,
462 SYNC_FILE_TYPE_UNKNOWN);
464 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
465 RunSyncerUntilIdle();
468 // SyncEngine will re-register the app and resurrect the app root later.
471 } // namespace drive_backend
472 } // namespace sync_file_system