1 // Copyright 2014 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.
7 #include "base/file_util.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/run_loop.h"
10 #include "chrome/browser/drive/drive_uploader.h"
11 #include "chrome/browser/drive/fake_drive_service.h"
12 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
13 #include "chrome/browser/sync_file_system/drive_backend/fake_drive_service_helper.h"
14 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
15 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h"
16 #include "chrome/browser/sync_file_system/drive_backend/sync_engine.h"
17 #include "chrome/browser/sync_file_system/local/canned_syncable_file_system.h"
18 #include "chrome/browser/sync_file_system/local/local_file_sync_context.h"
19 #include "chrome/browser/sync_file_system/local/local_file_sync_service.h"
20 #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h"
21 #include "chrome/browser/sync_file_system/sync_file_system_test_util.h"
22 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
23 #include "chrome/test/base/testing_profile.h"
24 #include "content/public/test/test_browser_thread.h"
25 #include "content/public/test/test_browser_thread_bundle.h"
26 #include "extensions/common/extension.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28 #include "webkit/browser/fileapi/file_system_context.h"
30 #define FPL(a) FILE_PATH_LITERAL(a)
32 namespace sync_file_system
{
33 namespace drive_backend
{
35 class DriveBackendSyncTest
: public testing::Test
{
37 DriveBackendSyncTest()
38 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP
) {}
39 virtual ~DriveBackendSyncTest() {}
41 virtual void SetUp() OVERRIDE
{
42 ASSERT_TRUE(base_dir_
.CreateUniqueTempDir());
44 io_task_runner_
= content::BrowserThread::GetMessageLoopProxyForThread(
45 content::BrowserThread::IO
);
46 file_task_runner_
= content::BrowserThread::GetMessageLoopProxyForThread(
47 content::BrowserThread::FILE);
49 RegisterSyncableFileSystem();
50 local_sync_service_
.reset(new LocalFileSyncService(&profile_
));
52 scoped_ptr
<drive::FakeDriveService
> drive_service(
53 new drive::FakeDriveService());
54 drive_service
->Initialize("test@example.com");
55 ASSERT_TRUE(drive_service
->LoadAccountMetadataForWapi(
56 "sync_file_system/account_metadata.json"));
57 ASSERT_TRUE(drive_service
->LoadResourceListForWapi(
58 "gdata/root_feed.json"));
60 scoped_ptr
<drive::DriveUploaderInterface
> uploader(
61 new drive::DriveUploader(drive_service
.get(),
62 file_task_runner_
.get()));
64 remote_sync_service_
.reset(new SyncEngine(
66 file_task_runner_
.get(),
67 drive_service
.PassAs
<drive::DriveServiceInterface
>(),
70 remote_sync_service_
->Initialize();
72 fake_drive_service_helper_
.reset(new FakeDriveServiceHelper(
73 fake_drive_service(), drive_uploader(),
74 kSyncRootFolderTitle
));
76 local_sync_service_
->SetLocalChangeProcessor(remote_sync_service_
.get());
77 remote_sync_service_
->SetRemoteChangeProcessor(local_sync_service_
.get());
80 virtual void TearDown() OVERRIDE
{
81 typedef std::map
<std::string
, CannedSyncableFileSystem
*>::iterator iterator
;
82 for (iterator itr
= file_systems_
.begin();
83 itr
!= file_systems_
.end(); ++itr
) {
84 itr
->second
->TearDown();
87 file_systems_
.clear();
89 fake_drive_service_helper_
.reset();
90 remote_sync_service_
.reset();
92 base::RunLoop().RunUntilIdle();
93 RevokeSyncableFileSystem();
97 fileapi::FileSystemURL
CreateURL(const std::string
& app_id
,
98 const base::FilePath::StringType
& path
) {
99 return CreateURL(app_id
, base::FilePath(path
));
102 fileapi::FileSystemURL
CreateURL(const std::string
& app_id
,
103 const base::FilePath
& path
) {
104 GURL origin
= extensions::Extension::GetBaseURLFromExtensionId(app_id
);
105 return CreateSyncableFileSystemURL(origin
, path
);
108 bool GetAppRootFolderID(const std::string
& app_id
,
109 std::string
* folder_id
) {
111 if (!metadata_database()->FindAppRootTracker(app_id
, &tracker
))
113 *folder_id
= tracker
.file_id();
117 bool GetFileIDByPath(const std::string
& app_id
,
118 const base::FilePath
& path
,
119 std::string
* file_id
) {
121 base::FilePath result_path
;
122 if (!metadata_database()->FindNearestActiveAncestor(
123 app_id
, path
, &tracker
, &result_path
) ||
126 *file_id
= tracker
.file_id();
130 SyncStatusCode
RegisterApp(const std::string
& app_id
) {
131 GURL origin
= extensions::Extension::GetBaseURLFromExtensionId(app_id
);
132 if (!ContainsKey(file_systems_
, app_id
)) {
133 CannedSyncableFileSystem
* file_system
= new CannedSyncableFileSystem(
134 origin
, io_task_runner_
.get(), file_task_runner_
.get());
135 file_system
->SetUp();
137 SyncStatusCode status
= SYNC_STATUS_UNKNOWN
;
138 local_sync_service_
->MaybeInitializeFileSystemContext(
139 origin
, file_system
->file_system_context(),
140 CreateResultReceiver(&status
));
141 base::RunLoop().RunUntilIdle();
142 EXPECT_EQ(SYNC_STATUS_OK
, status
);
144 file_system
->backend()->sync_context()->
145 set_mock_notify_changes_duration_in_sec(0);
147 EXPECT_EQ(base::PLATFORM_FILE_OK
, file_system
->OpenFileSystem());
148 file_systems_
[app_id
] = file_system
;
151 SyncStatusCode status
= SYNC_STATUS_UNKNOWN
;
152 remote_sync_service_
->RegisterOrigin(origin
, CreateResultReceiver(&status
));
153 base::RunLoop().RunUntilIdle();
157 void AddLocalFolder(const std::string
& app_id
,
158 const base::FilePath::StringType
& path
) {
159 ASSERT_TRUE(ContainsKey(file_systems_
, app_id
));
160 EXPECT_EQ(base::PLATFORM_FILE_OK
,
161 file_systems_
[app_id
]->CreateDirectory(
162 CreateURL(app_id
, path
)));
165 void AddOrUpdateLocalFile(const std::string
& app_id
,
166 const base::FilePath::StringType
& path
,
167 const std::string
& content
) {
168 fileapi::FileSystemURL
url(CreateURL(app_id
, path
));
169 ASSERT_TRUE(ContainsKey(file_systems_
, app_id
));
170 EXPECT_EQ(base::PLATFORM_FILE_OK
, file_systems_
[app_id
]->CreateFile(url
));
171 int64 bytes_written
= file_systems_
[app_id
]->WriteString(url
, content
);
172 EXPECT_EQ(static_cast<int64
>(content
.size()), bytes_written
);
173 base::RunLoop().RunUntilIdle();
176 void UpdateLocalFile(const std::string
& app_id
,
177 const base::FilePath::StringType
& path
,
178 const std::string
& content
) {
179 ASSERT_TRUE(ContainsKey(file_systems_
, app_id
));
180 int64 bytes_written
= file_systems_
[app_id
]->WriteString(
181 CreateURL(app_id
, path
), content
);
182 EXPECT_EQ(static_cast<int64
>(content
.size()), bytes_written
);
183 base::RunLoop().RunUntilIdle();
186 void RemoveLocal(const std::string
& app_id
,
187 const base::FilePath::StringType
& path
) {
188 ASSERT_TRUE(ContainsKey(file_systems_
, app_id
));
189 EXPECT_EQ(base::PLATFORM_FILE_OK
,
190 file_systems_
[app_id
]->Remove(
191 CreateURL(app_id
, path
),
192 true /* recursive */));
193 base::RunLoop().RunUntilIdle();
196 SyncStatusCode
ProcessLocalChange() {
197 SyncStatusCode status
= SYNC_STATUS_UNKNOWN
;
198 fileapi::FileSystemURL url
;
199 local_sync_service_
->ProcessLocalChange(
200 CreateResultReceiver(&status
, &url
));
201 base::RunLoop().RunUntilIdle();
205 SyncStatusCode
ProcessRemoteChange() {
206 SyncStatusCode status
= SYNC_STATUS_UNKNOWN
;
207 fileapi::FileSystemURL url
;
208 remote_sync_service_
->ProcessRemoteChange(
209 CreateResultReceiver(&status
, &url
));
210 base::RunLoop().RunUntilIdle();
214 SyncStatusCode
ProcessChangesUntilDone() {
215 SyncStatusCode local_sync_status
;
216 SyncStatusCode remote_sync_status
;
218 remote_sync_service_
->OnNotificationReceived();
219 base::RunLoop().RunUntilIdle();
221 local_sync_status
= ProcessLocalChange();
222 if (local_sync_status
!= SYNC_STATUS_OK
&&
223 local_sync_status
!= SYNC_STATUS_NO_CHANGE_TO_SYNC
)
224 return local_sync_status
;
226 remote_sync_status
= ProcessRemoteChange();
227 if (remote_sync_status
!= SYNC_STATUS_OK
&&
228 remote_sync_status
!= SYNC_STATUS_NO_CHANGE_TO_SYNC
)
229 return remote_sync_status
;
230 } while (local_sync_status
!= SYNC_STATUS_NO_CHANGE_TO_SYNC
||
231 remote_sync_status
!= SYNC_STATUS_NO_CHANGE_TO_SYNC
);
232 return SYNC_STATUS_OK
;
235 // Verifies local and remote files/folders are consistent.
236 // This function checks:
237 // - Each registered origin has corresponding remote folder.
238 // - Each local file/folder has corresponding remote one.
239 // - Each remote file/folder has corresponding local one.
240 // TODO(tzik): Handle conflict case. i.e. allow remote file has different
241 // file content if the corresponding local file conflicts to it.
242 void VerifyConsistency() {
243 std::string sync_root_folder_id
;
244 google_apis::GDataErrorCode error
=
245 fake_drive_service_helper_
->GetSyncRootFolderID(&sync_root_folder_id
);
246 if (sync_root_folder_id
.empty()) {
247 EXPECT_EQ(google_apis::HTTP_NOT_FOUND
, error
);
248 EXPECT_TRUE(file_systems_
.empty());
251 EXPECT_EQ(google_apis::HTTP_SUCCESS
, error
);
253 ScopedVector
<google_apis::ResourceEntry
> remote_entries
;
254 EXPECT_EQ(google_apis::HTTP_SUCCESS
,
255 fake_drive_service_helper_
->ListFilesInFolder(
256 sync_root_folder_id
, &remote_entries
));
257 std::map
<std::string
, const google_apis::ResourceEntry
*> app_root_by_title
;
258 for (ScopedVector
<google_apis::ResourceEntry
>::iterator itr
=
259 remote_entries
.begin();
260 itr
!= remote_entries
.end();
262 const google_apis::ResourceEntry
& remote_entry
= **itr
;
263 EXPECT_FALSE(ContainsKey(app_root_by_title
, remote_entry
.title()));
264 app_root_by_title
[remote_entry
.title()] = *itr
;
267 for (std::map
<std::string
, CannedSyncableFileSystem
*>::const_iterator itr
=
268 file_systems_
.begin();
269 itr
!= file_systems_
.end(); ++itr
) {
270 const std::string
& app_id
= itr
->first
;
271 SCOPED_TRACE(testing::Message() << "Verifying app: " << app_id
);
272 CannedSyncableFileSystem
* file_system
= itr
->second
;
273 ASSERT_TRUE(ContainsKey(app_root_by_title
, app_id
));
274 VerifyConsistencyForFolder(
275 app_id
, base::FilePath(),
276 app_root_by_title
[app_id
]->resource_id(),
281 void VerifyConsistencyForFolder(const std::string
& app_id
,
282 const base::FilePath
& path
,
283 const std::string
& folder_id
,
284 CannedSyncableFileSystem
* file_system
) {
285 SCOPED_TRACE(testing::Message() << "Verifying folder: " << path
.value());
287 ScopedVector
<google_apis::ResourceEntry
> remote_entries
;
288 EXPECT_EQ(google_apis::HTTP_SUCCESS
,
289 fake_drive_service_helper_
->ListFilesInFolder(
290 folder_id
, &remote_entries
));
291 std::map
<std::string
, const google_apis::ResourceEntry
*>
292 remote_entry_by_title
;
293 for (ScopedVector
<google_apis::ResourceEntry
>::iterator itr
=
294 remote_entries
.begin();
295 itr
!= remote_entries
.end();
297 const google_apis::ResourceEntry
& remote_entry
= **itr
;
298 EXPECT_FALSE(ContainsKey(remote_entry_by_title
, remote_entry
.title()));
299 remote_entry_by_title
[remote_entry
.title()] = *itr
;
302 fileapi::FileSystemURL
url(CreateURL(app_id
, path
));
303 CannedSyncableFileSystem::FileEntryList local_entries
;
304 EXPECT_EQ(base::PLATFORM_FILE_OK
,
305 file_system
->ReadDirectory(url
, &local_entries
));
306 for (CannedSyncableFileSystem::FileEntryList::iterator itr
=
307 local_entries
.begin();
308 itr
!= local_entries
.end();
310 const fileapi::DirectoryEntry
& local_entry
= *itr
;
311 fileapi::FileSystemURL
entry_url(
312 CreateURL(app_id
, path
.Append(local_entry
.name
)));
313 std::string title
= entry_url
.path().AsUTF8Unsafe();
314 ASSERT_TRUE(ContainsKey(remote_entry_by_title
, title
));
315 const google_apis::ResourceEntry
& remote_entry
=
316 *remote_entry_by_title
[title
];
317 if (local_entry
.is_directory
) {
318 ASSERT_TRUE(remote_entry
.is_folder());
319 VerifyConsistencyForFolder(app_id
, entry_url
.path(),
320 remote_entry
.resource_id(),
323 ASSERT_TRUE(remote_entry
.is_file());
324 VerifyConsistencyForFile(app_id
, entry_url
.path(),
325 remote_entry
.resource_id(),
328 remote_entry_by_title
.erase(title
);
331 EXPECT_TRUE(remote_entry_by_title
.empty());
334 void VerifyConsistencyForFile(const std::string
& app_id
,
335 const base::FilePath
& path
,
336 const std::string
& file_id
,
337 CannedSyncableFileSystem
* file_system
) {
338 fileapi::FileSystemURL
url(CreateURL(app_id
, path
));
339 std::string file_content
;
340 EXPECT_EQ(google_apis::HTTP_SUCCESS
,
341 fake_drive_service_helper_
->ReadFile(file_id
, &file_content
));
342 EXPECT_EQ(base::PLATFORM_FILE_OK
,
343 file_system
->VerifyFile(url
, file_content
));
346 drive::FakeDriveService
* fake_drive_service() {
347 return static_cast<drive::FakeDriveService
*>(
348 remote_sync_service_
->GetDriveService());
351 drive::DriveUploaderInterface
* drive_uploader() {
352 return remote_sync_service_
->GetDriveUploader();
355 FakeDriveServiceHelper
* fake_drive_service_helper() {
356 return fake_drive_service_helper_
.get();
359 MetadataDatabase
* metadata_database() {
360 return remote_sync_service_
->GetMetadataDatabase();
364 content::TestBrowserThreadBundle thread_bundle_
;
366 base::ScopedTempDir base_dir_
;
367 TestingProfile profile_
;
369 scoped_ptr
<SyncEngine
> remote_sync_service_
;
370 scoped_ptr
<LocalFileSyncService
> local_sync_service_
;
372 scoped_ptr
<FakeDriveServiceHelper
> fake_drive_service_helper_
;
373 std::map
<std::string
, CannedSyncableFileSystem
*> file_systems_
;
376 scoped_refptr
<base::SingleThreadTaskRunner
> io_task_runner_
;
377 scoped_refptr
<base::SingleThreadTaskRunner
> file_task_runner_
;
379 DISALLOW_COPY_AND_ASSIGN(DriveBackendSyncTest
);
382 TEST_F(DriveBackendSyncTest
, LocalToRemoteBasicTest
) {
383 std::string app_id
= "example";
386 AddOrUpdateLocalFile(app_id
, FPL("file"), "abcde");
388 EXPECT_EQ(SYNC_STATUS_OK
, ProcessChangesUntilDone());
392 TEST_F(DriveBackendSyncTest
, RemoteToLocalBasicTest
) {
393 std::string app_id
= "example";
396 std::string app_root_folder_id
;
397 EXPECT_TRUE(GetAppRootFolderID(app_id
, &app_root_folder_id
));
400 EXPECT_EQ(google_apis::HTTP_SUCCESS
,
401 fake_drive_service_helper()->AddFile(
402 app_root_folder_id
, "file", "abcde", &file_id
));
404 EXPECT_EQ(SYNC_STATUS_OK
, ProcessChangesUntilDone());
408 TEST_F(DriveBackendSyncTest
, LocalFileUpdateTest
) {
409 std::string app_id
= "example";
410 const base::FilePath::StringType
kPath(FPL("file"));
413 AddOrUpdateLocalFile(app_id
, kPath
, "abcde");
415 EXPECT_EQ(SYNC_STATUS_OK
, ProcessChangesUntilDone());
418 UpdateLocalFile(app_id
, kPath
, "1234567890");
420 EXPECT_EQ(SYNC_STATUS_OK
, ProcessChangesUntilDone());
424 TEST_F(DriveBackendSyncTest
, RemoteFileUpdateTest
) {
425 std::string app_id
= "example";
428 std::string remote_file_id
;
429 std::string app_root_folder_id
;
430 EXPECT_TRUE(GetAppRootFolderID(app_id
, &app_root_folder_id
));
431 EXPECT_EQ(google_apis::HTTP_SUCCESS
,
432 fake_drive_service_helper()->AddFile(
433 app_root_folder_id
, "file", "abcde", &remote_file_id
));
435 EXPECT_EQ(SYNC_STATUS_OK
, ProcessChangesUntilDone());
438 EXPECT_EQ(google_apis::HTTP_SUCCESS
,
439 fake_drive_service_helper()->UpdateFile(
440 remote_file_id
, "1234567890"));
442 EXPECT_EQ(SYNC_STATUS_OK
, ProcessChangesUntilDone());
446 TEST_F(DriveBackendSyncTest
, LocalFileDeletionTest
) {
447 std::string app_id
= "example";
448 const base::FilePath::StringType
path(FPL("file"));
451 AddOrUpdateLocalFile(app_id
, path
, "abcde");
453 EXPECT_EQ(SYNC_STATUS_OK
, ProcessChangesUntilDone());
456 RemoveLocal(app_id
, path
);
458 EXPECT_EQ(SYNC_STATUS_OK
, ProcessChangesUntilDone());
462 TEST_F(DriveBackendSyncTest
, RemoteFileDeletionTest
) {
463 std::string app_id
= "example";
464 const base::FilePath::StringType
path(FPL("file"));
467 AddOrUpdateLocalFile(app_id
, path
, "abcde");
469 EXPECT_EQ(SYNC_STATUS_OK
, ProcessChangesUntilDone());
473 EXPECT_TRUE(GetFileIDByPath(app_id
, base::FilePath(path
), &file_id
));
474 EXPECT_EQ(google_apis::HTTP_NO_CONTENT
,
475 fake_drive_service_helper()->DeleteResource(file_id
));
477 EXPECT_EQ(SYNC_STATUS_OK
, ProcessChangesUntilDone());
481 } // namespace drive_backend
482 } // namespace sync_file_system