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"
30 namespace sync_file_system {
31 namespace drive_backend {
35 fileapi::FileSystemURL URL(const GURL& origin,
36 const std::string& path) {
37 return CreateSyncableFileSystemURL(
38 origin, base::FilePath::FromUTF8Unsafe(path));
43 class RemoteToLocalSyncerTest : public testing::Test,
44 public SyncEngineContext {
46 typedef FakeRemoteChangeProcessor::URLToFileChangesMap URLToFileChangesMap;
48 RemoteToLocalSyncerTest()
49 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
50 virtual ~RemoteToLocalSyncerTest() {}
52 virtual void SetUp() OVERRIDE {
53 ASSERT_TRUE(database_dir_.CreateUniqueTempDir());
55 fake_drive_service_.reset(new drive::FakeDriveService);
56 ASSERT_TRUE(fake_drive_service_->LoadAccountMetadataForWapi(
57 "sync_file_system/account_metadata.json"));
58 ASSERT_TRUE(fake_drive_service_->LoadResourceListForWapi(
59 "gdata/empty_feed.json"));
61 drive_uploader_.reset(new drive::DriveUploader(
62 fake_drive_service_.get(), base::MessageLoopProxy::current().get()));
63 fake_drive_helper_.reset(new FakeDriveServiceHelper(
64 fake_drive_service_.get(), drive_uploader_.get(),
65 kSyncRootFolderTitle));
66 fake_remote_change_processor_.reset(new FakeRemoteChangeProcessor);
68 RegisterSyncableFileSystem();
71 virtual void TearDown() OVERRIDE {
72 RevokeSyncableFileSystem();
74 fake_remote_change_processor_.reset();
75 metadata_database_.reset();
76 fake_drive_helper_.reset();
77 drive_uploader_.reset();
78 fake_drive_service_.reset();
79 base::RunLoop().RunUntilIdle();
82 void InitializeMetadataDatabase() {
83 SyncEngineInitializer initializer(this,
84 base::MessageLoopProxy::current(),
85 fake_drive_service_.get(),
86 database_dir_.path());
87 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
88 initializer.Run(CreateResultReceiver(&status));
89 base::RunLoop().RunUntilIdle();
90 EXPECT_EQ(SYNC_STATUS_OK, status);
91 metadata_database_ = initializer.PassMetadataDatabase();
94 void RegisterApp(const std::string& app_id,
95 const std::string& app_root_folder_id) {
96 SyncStatusCode status = SYNC_STATUS_FAILED;
97 metadata_database_->RegisterApp(app_id, app_root_folder_id,
98 CreateResultReceiver(&status));
99 base::RunLoop().RunUntilIdle();
100 EXPECT_EQ(SYNC_STATUS_OK, status);
103 virtual drive::DriveServiceInterface* GetDriveService() OVERRIDE {
104 return fake_drive_service_.get();
107 virtual drive::DriveUploaderInterface* GetDriveUploader() OVERRIDE {
108 return drive_uploader_.get();
111 virtual MetadataDatabase* GetMetadataDatabase() OVERRIDE {
112 return metadata_database_.get();
115 virtual RemoteChangeProcessor* GetRemoteChangeProcessor() OVERRIDE {
116 return fake_remote_change_processor_.get();
119 virtual base::SequencedTaskRunner* GetBlockingTaskRunner() OVERRIDE {
120 return base::MessageLoopProxy::current().get();
124 std::string CreateSyncRoot() {
125 std::string sync_root_folder_id;
126 EXPECT_EQ(google_apis::HTTP_CREATED,
127 fake_drive_helper_->AddOrphanedFolder(
128 kSyncRootFolderTitle, &sync_root_folder_id));
129 return sync_root_folder_id;
132 std::string CreateRemoteFolder(const std::string& parent_folder_id,
133 const std::string& title) {
134 std::string folder_id;
135 EXPECT_EQ(google_apis::HTTP_CREATED,
136 fake_drive_helper_->AddFolder(
137 parent_folder_id, title, &folder_id));
141 std::string CreateRemoteFile(const std::string& parent_folder_id,
142 const std::string& title,
143 const std::string& content) {
145 EXPECT_EQ(google_apis::HTTP_SUCCESS,
146 fake_drive_helper_->AddFile(
147 parent_folder_id, title, content, &file_id));
151 void DeleteRemoteFile(const std::string& file_id) {
152 EXPECT_EQ(google_apis::HTTP_NO_CONTENT,
153 fake_drive_helper_->DeleteResource(file_id));
156 void CreateLocalFolder(const fileapi::FileSystemURL& url) {
157 fake_remote_change_processor_->UpdateLocalFileMetadata(
158 url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
159 SYNC_FILE_TYPE_DIRECTORY));
162 void CreateLocalFile(const fileapi::FileSystemURL& url) {
163 fake_remote_change_processor_->UpdateLocalFileMetadata(
164 url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
165 SYNC_FILE_TYPE_FILE));
168 SyncStatusCode RunSyncer() {
169 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
170 scoped_ptr<RemoteToLocalSyncer> syncer(new RemoteToLocalSyncer(this));
171 syncer->Run(CreateResultReceiver(&status));
172 base::RunLoop().RunUntilIdle();
176 void RunSyncerUntilIdle() {
177 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
178 while (status != SYNC_STATUS_NO_CHANGE_TO_SYNC)
179 status = RunSyncer();
182 SyncStatusCode ListChanges() {
183 ListChangesTask list_changes(this);
184 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
185 list_changes.Run(CreateResultReceiver(&status));
186 base::RunLoop().RunUntilIdle();
190 void AppendExpectedChange(const fileapi::FileSystemURL& url,
191 FileChange::ChangeType change_type,
192 SyncFileType file_type) {
193 expected_changes_[url].push_back(FileChange(change_type, file_type));
196 void VerifyConsistency() {
197 fake_remote_change_processor_->VerifyConsistency(expected_changes_);
201 content::TestBrowserThreadBundle thread_bundle_;
202 base::ScopedTempDir database_dir_;
204 scoped_ptr<drive::FakeDriveService> fake_drive_service_;
205 scoped_ptr<drive::DriveUploader> drive_uploader_;
206 scoped_ptr<FakeDriveServiceHelper> fake_drive_helper_;
207 scoped_ptr<MetadataDatabase> metadata_database_;
208 scoped_ptr<FakeRemoteChangeProcessor> fake_remote_change_processor_;
210 URLToFileChangesMap expected_changes_;
212 DISALLOW_COPY_AND_ASSIGN(RemoteToLocalSyncerTest);
215 TEST_F(RemoteToLocalSyncerTest, AddNewFile) {
216 const GURL kOrigin("chrome-extension://example");
217 const std::string sync_root = CreateSyncRoot();
218 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
219 InitializeMetadataDatabase();
220 RegisterApp(kOrigin.host(), app_root);
222 const std::string folder1 = CreateRemoteFolder(app_root, "folder1");
223 const std::string file1 = CreateRemoteFile(app_root, "file1", "data1");
224 const std::string folder2 = CreateRemoteFolder(folder1, "folder2");
225 const std::string file2 = CreateRemoteFile(folder1, "file2", "data2");
227 RunSyncerUntilIdle();
229 // Create expected changes.
230 // TODO(nhiroki): Clean up creating URL part.
231 AppendExpectedChange(URL(kOrigin, "folder1"),
232 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
233 SYNC_FILE_TYPE_DIRECTORY);
234 AppendExpectedChange(URL(kOrigin, "file1"),
235 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
236 SYNC_FILE_TYPE_FILE);
237 AppendExpectedChange(URL(kOrigin, "folder1/folder2"),
238 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
239 SYNC_FILE_TYPE_DIRECTORY);
240 AppendExpectedChange(URL(kOrigin, "folder1/file2"),
241 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
242 SYNC_FILE_TYPE_FILE);
246 EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
247 EXPECT_FALSE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL));
250 TEST_F(RemoteToLocalSyncerTest, DeleteFile) {
251 const GURL kOrigin("chrome-extension://example");
252 const std::string sync_root = CreateSyncRoot();
253 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
254 InitializeMetadataDatabase();
255 RegisterApp(kOrigin.host(), app_root);
257 const std::string folder = CreateRemoteFolder(app_root, "folder");
258 const std::string file = CreateRemoteFile(app_root, "file", "data");
260 AppendExpectedChange(URL(kOrigin, "folder"),
261 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
262 SYNC_FILE_TYPE_DIRECTORY);
263 AppendExpectedChange(URL(kOrigin, "file"),
264 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
265 SYNC_FILE_TYPE_FILE);
267 RunSyncerUntilIdle();
270 DeleteRemoteFile(folder);
271 DeleteRemoteFile(file);
273 AppendExpectedChange(URL(kOrigin, "folder"),
274 FileChange::FILE_CHANGE_DELETE,
275 SYNC_FILE_TYPE_UNKNOWN);
276 AppendExpectedChange(URL(kOrigin, "file"),
277 FileChange::FILE_CHANGE_DELETE,
278 SYNC_FILE_TYPE_UNKNOWN);
280 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
281 RunSyncerUntilIdle();
284 EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
285 EXPECT_FALSE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL));
288 TEST_F(RemoteToLocalSyncerTest, DeleteNestedFiles) {
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);
295 const std::string folder1 = CreateRemoteFolder(app_root, "folder1");
296 const std::string file1 = CreateRemoteFile(app_root, "file1", "data1");
297 const std::string folder2 = CreateRemoteFolder(folder1, "folder2");
298 const std::string file2 = CreateRemoteFile(folder1, "file2", "data2");
300 AppendExpectedChange(URL(kOrigin, "folder1"),
301 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
302 SYNC_FILE_TYPE_DIRECTORY);
303 AppendExpectedChange(URL(kOrigin, "file1"),
304 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
305 SYNC_FILE_TYPE_FILE);
306 AppendExpectedChange(URL(kOrigin, "folder1/folder2"),
307 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
308 SYNC_FILE_TYPE_DIRECTORY);
309 AppendExpectedChange(URL(kOrigin, "folder1/file2"),
310 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
311 SYNC_FILE_TYPE_FILE);
313 RunSyncerUntilIdle();
316 DeleteRemoteFile(folder1);
318 AppendExpectedChange(URL(kOrigin, "folder1"),
319 FileChange::FILE_CHANGE_DELETE,
320 SYNC_FILE_TYPE_UNKNOWN);
321 // Changes for descendant files ("folder2" and "file2") should be ignored.
323 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
324 RunSyncerUntilIdle();
327 EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
328 EXPECT_FALSE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL));
331 TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFileOnFolder) {
332 const GURL kOrigin("chrome-extension://example");
333 const std::string sync_root = CreateSyncRoot();
334 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
335 InitializeMetadataDatabase();
336 RegisterApp(kOrigin.host(), app_root);
338 CreateLocalFolder(URL(kOrigin, "folder"));
339 CreateRemoteFile(app_root, "folder", "data");
341 // Folder-File conflict happens. File creation should be ignored.
343 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
344 RunSyncerUntilIdle();
347 // Tracker for the remote file should be lowered.
348 EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
349 EXPECT_TRUE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL));
352 TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFile) {
353 const GURL kOrigin("chrome-extension://example");
354 const std::string sync_root = CreateSyncRoot();
355 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
356 InitializeMetadataDatabase();
357 RegisterApp(kOrigin.host(), app_root);
359 RunSyncerUntilIdle();
362 CreateLocalFile(URL(kOrigin, "file"));
363 CreateRemoteFolder(app_root, "file");
365 // File-Folder conflict happens. Folder should override the existing file.
366 AppendExpectedChange(URL(kOrigin, "file"),
367 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
368 SYNC_FILE_TYPE_DIRECTORY);
370 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
371 RunSyncerUntilIdle();
374 EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
375 EXPECT_FALSE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL));
378 TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFolderOnFolder) {
379 const GURL kOrigin("chrome-extension://example");
380 const std::string sync_root = CreateSyncRoot();
381 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
382 InitializeMetadataDatabase();
383 RegisterApp(kOrigin.host(), app_root);
385 CreateLocalFolder(URL(kOrigin, "folder"));
386 CreateRemoteFolder(app_root, "folder");
388 // Folder-Folder conflict happens. Folder creation should be ignored.
390 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
391 RunSyncerUntilIdle();
394 EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
395 EXPECT_FALSE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL));
398 TEST_F(RemoteToLocalSyncerTest, Conflict_CreateFileOnFile) {
399 const GURL kOrigin("chrome-extension://example");
400 const std::string sync_root = CreateSyncRoot();
401 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
402 InitializeMetadataDatabase();
403 RegisterApp(kOrigin.host(), app_root);
405 CreateLocalFile(URL(kOrigin, "file"));
406 CreateRemoteFile(app_root, "file", "data");
408 // File-File conflict happens. File creation should be ignored.
410 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
411 RunSyncerUntilIdle();
414 // Tracker for the remote file should be lowered.
415 EXPECT_FALSE(GetMetadataDatabase()->GetNormalPriorityDirtyTracker(NULL));
416 EXPECT_TRUE(GetMetadataDatabase()->GetLowPriorityDirtyTracker(NULL));
419 TEST_F(RemoteToLocalSyncerTest, Conflict_CreateNestedFolderOnFile) {
420 const GURL kOrigin("chrome-extension://example");
421 const std::string sync_root = CreateSyncRoot();
422 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
423 InitializeMetadataDatabase();
424 RegisterApp(kOrigin.host(), app_root);
426 RunSyncerUntilIdle();
429 const std::string folder = CreateRemoteFolder(app_root, "folder");
430 CreateLocalFile(URL(kOrigin, "/folder"));
431 CreateRemoteFile(folder, "file", "data");
433 // File-Folder conflict happens. Folder should override the existing file.
434 AppendExpectedChange(URL(kOrigin, "/folder"),
435 FileChange::FILE_CHANGE_ADD_OR_UPDATE,
436 SYNC_FILE_TYPE_DIRECTORY);
438 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
439 RunSyncerUntilIdle();
443 TEST_F(RemoteToLocalSyncerTest, AppRootDeletion) {
444 const GURL kOrigin("chrome-extension://example");
445 const std::string sync_root = CreateSyncRoot();
446 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
447 InitializeMetadataDatabase();
448 RegisterApp(kOrigin.host(), app_root);
450 RunSyncerUntilIdle();
453 DeleteRemoteFile(app_root);
455 AppendExpectedChange(URL(kOrigin, "/"),
456 FileChange::FILE_CHANGE_DELETE,
457 SYNC_FILE_TYPE_UNKNOWN);
459 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
460 RunSyncerUntilIdle();
463 // SyncEngine will re-register the app and resurrect the app root later.
466 } // namespace drive_backend
467 } // namespace sync_file_system