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_v1/drive_file_sync_service.h"
9 #include "base/file_util.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/stl_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/threading/sequenced_worker_pool.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/fake_drive_service_helper.h"
18 #include "chrome/browser/sync_file_system/drive_backend_v1/api_util.h"
19 #include "chrome/browser/sync_file_system/drive_backend_v1/drive_file_sync_util.h"
20 #include "chrome/browser/sync_file_system/drive_backend_v1/drive_metadata_store.h"
21 #include "chrome/browser/sync_file_system/local/canned_syncable_file_system.h"
22 #include "chrome/browser/sync_file_system/local/local_file_sync_context.h"
23 #include "chrome/browser/sync_file_system/local/local_file_sync_service.h"
24 #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h"
25 #include "chrome/browser/sync_file_system/sync_file_system_test_util.h"
26 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
27 #include "chrome/test/base/testing_profile.h"
28 #include "content/public/test/test_browser_thread.h"
29 #include "content/public/test/test_browser_thread_bundle.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
32 #include "third_party/leveldatabase/src/include/leveldb/env.h"
33 #include "webkit/browser/fileapi/file_system_context.h"
35 #define FPL(path) FILE_PATH_LITERAL(path)
37 using content::BrowserThread;
39 using google_apis::GDataErrorCode;
40 using google_apis::ResourceEntry;
42 namespace sync_file_system {
44 using drive_backend::APIUtil;
45 using drive_backend::APIUtilInterface;
46 using drive_backend::FakeDriveServiceHelper;
48 class DriveFileSyncServiceSyncTest : public testing::Test {
50 DriveFileSyncServiceSyncTest()
51 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
53 virtual ~DriveFileSyncServiceSyncTest() {
56 virtual void SetUp() OVERRIDE {
57 // TODO(tzik): Set up TestExtensionSystem to support simulated relaunch.
58 in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
60 RegisterSyncableFileSystem();
61 local_sync_service_ = LocalFileSyncService::CreateForTesting(
62 &profile_, in_memory_env_.get());
64 fake_drive_service_ = new drive::FakeDriveService();
65 fake_drive_service_->Initialize("test_user@gmail.com");
66 ASSERT_TRUE(fake_drive_service_->LoadAccountMetadataForWapi(
67 "sync_file_system/account_metadata.json"));
68 ASSERT_TRUE(fake_drive_service_->LoadResourceListForWapi(
69 "gdata/root_feed.json"));
71 drive_uploader_ = new drive::DriveUploader(
72 fake_drive_service_, base::MessageLoopProxy::current().get());
74 fake_drive_helper_.reset(new FakeDriveServiceHelper(
75 fake_drive_service_, drive_uploader_,
76 APIUtil::GetSyncRootDirectoryName()));
78 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
80 scoped_ptr<DriveMetadataStore> metadata_store(
81 new DriveMetadataStore(fake_drive_helper_->base_dir_path(),
82 base::MessageLoopProxy::current().get()));
83 metadata_store->Initialize(CreateResultReceiver(&status, &created));
85 EXPECT_EQ(SYNC_STATUS_OK, status);
88 scoped_ptr<APIUtil> api_util(APIUtil::CreateForTesting(
89 fake_drive_helper_->base_dir_path().AppendASCII("tmp"),
90 scoped_ptr<drive::DriveServiceInterface>(fake_drive_service_),
91 scoped_ptr<drive::DriveUploaderInterface>(drive_uploader_)));
93 remote_sync_service_ = DriveFileSyncService::CreateForTesting(
95 fake_drive_helper_->base_dir_path(),
96 api_util.PassAs<APIUtilInterface>(),
97 metadata_store.Pass());
99 local_sync_service_->SetLocalChangeProcessor(remote_sync_service_.get());
100 remote_sync_service_->SetRemoteChangeProcessor(local_sync_service_.get());
103 virtual void TearDown() OVERRIDE {
104 drive_uploader_ = NULL;
105 fake_drive_service_ = NULL;
106 remote_sync_service_.reset();
107 local_sync_service_.reset();
110 typedef std::map<GURL, CannedSyncableFileSystem*>::iterator iterator;
111 for (iterator itr = file_systems_.begin();
112 itr != file_systems_.end(); ++itr) {
113 itr->second->TearDown();
116 file_systems_.clear();
119 RevokeSyncableFileSystem();
123 void RegisterOrigin(const GURL& origin) {
124 if (!ContainsKey(file_systems_, origin)) {
125 CannedSyncableFileSystem* file_system = new CannedSyncableFileSystem(
127 in_memory_env_.get(),
128 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get(),
129 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)
132 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
133 file_system->SetUp(CannedSyncableFileSystem::QUOTA_ENABLED);
134 local_sync_service_->MaybeInitializeFileSystemContext(
135 origin, file_system->file_system_context(),
136 CreateResultReceiver(&status));
138 EXPECT_EQ(SYNC_STATUS_OK, status);
140 file_system->backend()->sync_context()->
141 set_mock_notify_changes_duration_in_sec(0);
143 EXPECT_EQ(base::File::FILE_OK, file_system->OpenFileSystem());
144 file_systems_[origin] = file_system;
147 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
148 remote_sync_service_->RegisterOrigin(
149 origin, CreateResultReceiver(&status));
151 EXPECT_EQ(SYNC_STATUS_OK, status);
154 void AddOrUpdateLocalFile(const GURL& origin,
155 const base::FilePath& path,
156 const std::string& content) {
157 fileapi::FileSystemURL url(CreateSyncableFileSystemURL(origin, path));
158 ASSERT_TRUE(ContainsKey(file_systems_, origin));
159 EXPECT_EQ(base::File::FILE_OK, file_systems_[origin]->CreateFile(url));
160 int64 bytes_written = file_systems_[origin]->WriteString(url, content);
161 EXPECT_EQ(static_cast<int64>(content.size()), bytes_written);
165 void UpdateLocalFile(const GURL& origin,
166 const base::FilePath& path,
167 const std::string& content) {
168 ASSERT_TRUE(ContainsKey(file_systems_, origin));
169 int64 bytes_written = file_systems_[origin]->WriteString(
170 CreateSyncableFileSystemURL(origin, path), content);
171 EXPECT_EQ(static_cast<int64>(content.size()), bytes_written);
175 void RemoveLocal(const GURL& origin, const base::FilePath& path) {
176 ASSERT_TRUE(ContainsKey(file_systems_, origin));
177 EXPECT_EQ(base::File::FILE_OK,
178 file_systems_[origin]->Remove(
179 CreateSyncableFileSystemURL(origin, path),
180 true /* recursive */));
184 SyncStatusCode ProcessLocalChange() {
185 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
186 fileapi::FileSystemURL url;
187 local_sync_service_->ProcessLocalChange(
188 CreateResultReceiver(&status, &url));
193 SyncStatusCode ProcessRemoteChange() {
194 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
195 fileapi::FileSystemURL url;
196 remote_sync_service_->ProcessRemoteChange(
197 CreateResultReceiver(&status, &url));
202 SyncStatusCode ProcessChangesUntilDone() {
203 remote_sync_service_->OnNotificationReceived();
206 SyncStatusCode local_sync_status;
207 SyncStatusCode remote_sync_status;
209 local_sync_status = ProcessLocalChange();
210 if (local_sync_status != SYNC_STATUS_OK &&
211 local_sync_status != SYNC_STATUS_NO_CHANGE_TO_SYNC)
212 return local_sync_status;
214 remote_sync_status = ProcessRemoteChange();
215 if (remote_sync_status != SYNC_STATUS_OK &&
216 remote_sync_status != SYNC_STATUS_NO_CHANGE_TO_SYNC)
217 return remote_sync_status;
218 } while (local_sync_status != SYNC_STATUS_NO_CHANGE_TO_SYNC &&
219 remote_sync_status != SYNC_STATUS_NO_CHANGE_TO_SYNC);
220 return SYNC_STATUS_OK;
223 // Verifies local and remote files/folders are consistent.
224 // This function checks:
225 // - Each registered origin has corresponding remote folder.
226 // - Each local file/folder has corresponding remote one.
227 // - Each remote file/folder has corresponding local one.
228 // TODO(tzik): Handle conflict case. i.e. allow remote file has different
229 // file content if the corresponding local file conflicts to it.
230 void VerifyConsistency() {
231 std::string sync_root_folder_id;
232 GDataErrorCode error =
233 fake_drive_helper_->GetSyncRootFolderID(&sync_root_folder_id);
234 if (sync_root_folder_id.empty()) {
235 EXPECT_EQ(google_apis::HTTP_NOT_FOUND, error);
236 EXPECT_TRUE(file_systems_.empty());
239 EXPECT_EQ(google_apis::HTTP_SUCCESS, error);
241 ScopedVector<ResourceEntry> remote_entries;
242 EXPECT_EQ(google_apis::HTTP_SUCCESS,
243 fake_drive_helper_->ListFilesInFolder(
244 sync_root_folder_id, &remote_entries));
245 std::map<std::string, const ResourceEntry*> origin_root_by_title;
246 for (ScopedVector<ResourceEntry>::iterator itr = remote_entries.begin();
247 itr != remote_entries.end();
249 const ResourceEntry& remote_entry = **itr;
250 EXPECT_FALSE(ContainsKey(origin_root_by_title, remote_entry.title()));
251 origin_root_by_title[remote_entry.title()] = *itr;
254 for (std::map<GURL, CannedSyncableFileSystem*>::const_iterator itr =
255 file_systems_.begin();
256 itr != file_systems_.end(); ++itr) {
257 const GURL& origin = itr->first;
258 SCOPED_TRACE(testing::Message() << "Verifying origin: " << origin);
259 CannedSyncableFileSystem* file_system = itr->second;
260 ASSERT_TRUE(ContainsKey(origin_root_by_title, origin.host()));
261 VerifyConsistencyForFolder(
262 origin, base::FilePath(),
263 origin_root_by_title[origin.host()]->resource_id(),
268 void VerifyConsistencyForOrigin(const GURL& origin) {
269 std::string sync_root_folder_id;
270 ASSERT_EQ(google_apis::HTTP_SUCCESS,
271 fake_drive_helper_->GetSyncRootFolderID(&sync_root_folder_id));
272 ASSERT_FALSE(sync_root_folder_id.empty());
274 ScopedVector<ResourceEntry> origin_folder;
275 EXPECT_EQ(google_apis::HTTP_SUCCESS,
276 fake_drive_helper_->SearchByTitle(
277 sync_root_folder_id, origin.host(), &origin_folder));
278 ASSERT_EQ(1u, origin_folder.size());
280 ASSERT_TRUE(ContainsKey(file_systems_, origin));
281 VerifyConsistencyForFolder(
282 origin, base::FilePath(),
283 origin_folder[0]->resource_id(),
284 file_systems_[origin]);
287 void VerifyConsistencyForFolder(const GURL& origin,
288 const base::FilePath& path,
289 const std::string& folder_id,
290 CannedSyncableFileSystem* file_system) {
291 SCOPED_TRACE(testing::Message() << "Verifying path: " << path.value());
293 ScopedVector<ResourceEntry> remote_entries;
294 EXPECT_EQ(google_apis::HTTP_SUCCESS,
295 fake_drive_helper_->ListFilesInFolder(
296 folder_id, &remote_entries));
297 std::map<std::string, const ResourceEntry*> remote_entry_by_title;
298 for (ScopedVector<ResourceEntry>::iterator itr = remote_entries.begin();
299 itr != remote_entries.end();
301 const ResourceEntry& remote_entry = **itr;
302 EXPECT_FALSE(ContainsKey(remote_entry_by_title, remote_entry.title()));
303 remote_entry_by_title[remote_entry.title()] = *itr;
306 fileapi::FileSystemURL url(CreateSyncableFileSystemURL(origin, path));
307 CannedSyncableFileSystem::FileEntryList local_entries;
308 EXPECT_EQ(base::File::FILE_OK,
309 file_system->ReadDirectory(url, &local_entries));
310 for (CannedSyncableFileSystem::FileEntryList::iterator itr =
311 local_entries.begin();
312 itr != local_entries.end();
314 const fileapi::DirectoryEntry& local_entry = *itr;
315 fileapi::FileSystemURL entry_url(
316 CreateSyncableFileSystemURL(origin, path.Append(local_entry.name)));
317 std::string title = DriveFileSyncService::PathToTitle(entry_url.path());
318 ASSERT_TRUE(ContainsKey(remote_entry_by_title, title));
319 const ResourceEntry& remote_entry = *remote_entry_by_title[title];
320 if (local_entry.is_directory) {
321 ASSERT_TRUE(remote_entry.is_folder());
322 VerifyConsistencyForFolder(origin, entry_url.path(),
323 remote_entry.resource_id(),
326 ASSERT_TRUE(remote_entry.is_file());
327 VerifyConsistencyForFile(origin, entry_url.path(),
328 remote_entry.resource_id(),
331 remote_entry_by_title.erase(title);
334 EXPECT_TRUE(remote_entry_by_title.empty());
337 void VerifyConsistencyForFile(const GURL& origin,
338 const base::FilePath& path,
339 const std::string& file_id,
340 CannedSyncableFileSystem* file_system) {
341 fileapi::FileSystemURL url(CreateSyncableFileSystemURL(origin, path));
342 std::string file_content;
343 EXPECT_EQ(google_apis::HTTP_SUCCESS,
344 fake_drive_helper_->ReadFile(file_id, &file_content));
345 EXPECT_EQ(base::File::FILE_OK,
346 file_system->VerifyFile(url, file_content));
349 void FlushMessageLoop() {
350 base::MessageLoop::current()->RunUntilIdle();
351 BrowserThread::GetBlockingPool()->FlushForTesting();
352 base::MessageLoop::current()->RunUntilIdle();
355 void TestInitialization();
356 void TestLocalToRemoteBasic();
357 void TestRemoteToLocalBasic();
358 void TestLocalFileUpdate();
359 void TestRemoteFileUpdate();
360 void TestLocalFileDeletion();
361 void TestRemoteFileDeletion();
363 content::TestBrowserThreadBundle thread_bundle_;
365 scoped_ptr<leveldb::Env> in_memory_env_;
366 TestingProfile profile_;
368 drive::FakeDriveService* fake_drive_service_;
369 drive::DriveUploader* drive_uploader_;
370 scoped_ptr<FakeDriveServiceHelper> fake_drive_helper_;
371 std::map<GURL, CannedSyncableFileSystem*> file_systems_;
373 scoped_ptr<DriveFileSyncService> remote_sync_service_;
374 scoped_ptr<LocalFileSyncService> local_sync_service_;
377 DISALLOW_COPY_AND_ASSIGN(DriveFileSyncServiceSyncTest);
380 void DriveFileSyncServiceSyncTest::TestInitialization() {
381 EXPECT_EQ(SYNC_STATUS_OK, ProcessChangesUntilDone());
385 void DriveFileSyncServiceSyncTest::TestLocalToRemoteBasic() {
386 const GURL kOrigin("chrome-extension://example");
388 RegisterOrigin(kOrigin);
389 AddOrUpdateLocalFile(kOrigin, base::FilePath(FPL("file")), "abcde");
391 EXPECT_EQ(SYNC_STATUS_OK, ProcessChangesUntilDone());
395 void DriveFileSyncServiceSyncTest::TestRemoteToLocalBasic() {
396 const GURL kOrigin("chrome-extension://example");
398 std::string sync_root_folder_id;
399 EXPECT_EQ(google_apis::HTTP_CREATED,
400 fake_drive_helper_->AddOrphanedFolder(
401 APIUtil::GetSyncRootDirectoryName(),
402 &sync_root_folder_id));
404 std::string origin_root_folder_id;
405 EXPECT_EQ(google_apis::HTTP_CREATED,
406 fake_drive_helper_->AddFolder(
407 sync_root_folder_id, kOrigin.host(), &origin_root_folder_id));
409 RegisterOrigin(kOrigin);
412 EXPECT_EQ(google_apis::HTTP_SUCCESS,
413 fake_drive_helper_->AddFile(
414 origin_root_folder_id, "file", "abcde", &file_id));
416 EXPECT_EQ(SYNC_STATUS_OK, ProcessChangesUntilDone());
420 void DriveFileSyncServiceSyncTest::TestLocalFileUpdate() {
421 const GURL kOrigin("chrome-extension://example");
422 const base::FilePath kPath(FPL("file"));
424 RegisterOrigin(kOrigin);
425 AddOrUpdateLocalFile(kOrigin, kPath, "abcde");
427 EXPECT_EQ(SYNC_STATUS_OK, ProcessChangesUntilDone());
428 VerifyConsistencyForOrigin(kOrigin);
430 UpdateLocalFile(kOrigin, kPath, "1234567890");
432 EXPECT_EQ(SYNC_STATUS_OK, ProcessChangesUntilDone());
436 void DriveFileSyncServiceSyncTest::TestRemoteFileUpdate() {
437 const GURL kOrigin("chrome-extension://example");
438 const base::FilePath kPath(FPL("file"));
439 const std::string kTitle(DriveFileSyncService::PathToTitle(kPath));
441 std::string sync_root_folder_id;
442 EXPECT_EQ(google_apis::HTTP_CREATED,
443 fake_drive_helper_->AddOrphanedFolder(
444 APIUtil::GetSyncRootDirectoryName(),
445 &sync_root_folder_id));
447 std::string origin_root_folder_id;
448 EXPECT_EQ(google_apis::HTTP_CREATED,
449 fake_drive_helper_->AddFolder(
450 sync_root_folder_id, kOrigin.host(), &origin_root_folder_id));
452 std::string remote_file_id;
453 EXPECT_EQ(google_apis::HTTP_SUCCESS,
454 fake_drive_helper_->AddFile(
455 origin_root_folder_id, kTitle, "abcde", &remote_file_id));
457 RegisterOrigin(kOrigin);
458 EXPECT_EQ(SYNC_STATUS_OK, ProcessChangesUntilDone());
459 VerifyConsistencyForOrigin(kOrigin);
461 EXPECT_EQ(google_apis::HTTP_SUCCESS,
462 fake_drive_helper_->UpdateFile(remote_file_id, "1234567890"));
464 EXPECT_EQ(SYNC_STATUS_OK, ProcessChangesUntilDone());
468 void DriveFileSyncServiceSyncTest::TestLocalFileDeletion() {
469 const GURL kOrigin("chrome-extension://example");
470 const base::FilePath kPath(FPL("file"));
472 RegisterOrigin(kOrigin);
473 AddOrUpdateLocalFile(kOrigin, kPath, "abcde");
475 EXPECT_EQ(SYNC_STATUS_OK, ProcessChangesUntilDone());
476 VerifyConsistencyForOrigin(kOrigin);
478 RemoveLocal(kOrigin, kPath);
480 EXPECT_EQ(SYNC_STATUS_OK, ProcessChangesUntilDone());
484 void DriveFileSyncServiceSyncTest::TestRemoteFileDeletion() {
485 const GURL kOrigin("chrome-extension://example");
486 const base::FilePath kPath(FPL("file"));
487 const std::string kTitle(DriveFileSyncService::PathToTitle(kPath));
489 std::string sync_root_folder_id;
490 EXPECT_EQ(google_apis::HTTP_CREATED,
491 fake_drive_helper_->AddOrphanedFolder(
492 APIUtil::GetSyncRootDirectoryName(),
493 &sync_root_folder_id));
495 std::string origin_root_folder_id;
496 EXPECT_EQ(google_apis::HTTP_CREATED,
497 fake_drive_helper_->AddFolder(
498 sync_root_folder_id, kOrigin.host(), &origin_root_folder_id));
500 std::string remote_file_id;
501 EXPECT_EQ(google_apis::HTTP_SUCCESS,
502 fake_drive_helper_->AddFile(
503 origin_root_folder_id, kTitle, "abcde", &remote_file_id));
505 RegisterOrigin(kOrigin);
506 EXPECT_EQ(SYNC_STATUS_OK, ProcessChangesUntilDone());
507 VerifyConsistencyForOrigin(kOrigin);
509 EXPECT_EQ(google_apis::HTTP_SUCCESS,
510 fake_drive_helper_->TrashResource(remote_file_id));
512 EXPECT_EQ(SYNC_STATUS_OK, ProcessChangesUntilDone());
516 TEST_F(DriveFileSyncServiceSyncTest, InitializationTest) {
517 ASSERT_FALSE(IsDriveAPIDisabled());
518 TestInitialization();
521 TEST_F(DriveFileSyncServiceSyncTest, InitializationTest_WAPI) {
522 ScopedDisableDriveAPI disable_drive_api;
523 TestInitialization();
526 TEST_F(DriveFileSyncServiceSyncTest, LocalToRemoteBasicTest) {
527 ASSERT_FALSE(IsDriveAPIDisabled());
528 TestLocalToRemoteBasic();
531 TEST_F(DriveFileSyncServiceSyncTest, LocalToRemoteBasicTest_WAPI) {
532 ScopedDisableDriveAPI disable_drive_api;
533 TestLocalToRemoteBasic();
536 TEST_F(DriveFileSyncServiceSyncTest, RemoteToLocalBasicTest) {
537 ASSERT_FALSE(IsDriveAPIDisabled());
538 TestRemoteToLocalBasic();
541 TEST_F(DriveFileSyncServiceSyncTest, RemoteToLocalBasicTest_WAPI) {
542 ScopedDisableDriveAPI disable_drive_api;
543 TestRemoteToLocalBasic();
546 TEST_F(DriveFileSyncServiceSyncTest, LocalFileUpdateTest) {
547 ASSERT_FALSE(IsDriveAPIDisabled());
548 TestLocalFileUpdate();
551 TEST_F(DriveFileSyncServiceSyncTest, LocalFileUpdateTest_WAPI) {
552 ScopedDisableDriveAPI disable_drive_api;
553 TestLocalFileUpdate();
556 TEST_F(DriveFileSyncServiceSyncTest, RemoteFileUpdateTest) {
557 ASSERT_FALSE(IsDriveAPIDisabled());
558 TestRemoteFileUpdate();
561 TEST_F(DriveFileSyncServiceSyncTest, RemoteFileUpdateTest_WAPI) {
562 ScopedDisableDriveAPI disable_drive_api;
563 TestRemoteFileUpdate();
566 TEST_F(DriveFileSyncServiceSyncTest, LocalFileDeletionTest) {
567 ASSERT_FALSE(IsDriveAPIDisabled());
568 TestLocalFileDeletion();
571 TEST_F(DriveFileSyncServiceSyncTest, LocalFileDeletionTest_WAPI) {
572 ScopedDisableDriveAPI disable_drive_api;
573 TestLocalFileDeletion();
576 TEST_F(DriveFileSyncServiceSyncTest, RemoteFileDeletionTest) {
577 ASSERT_FALSE(IsDriveAPIDisabled());
578 TestRemoteFileDeletion();
581 TEST_F(DriveFileSyncServiceSyncTest, RemoteFileDeletionTest_WAPI) {
582 ScopedDisableDriveAPI disable_drive_api;
583 TestRemoteFileDeletion();
586 } // namespace sync_file_system