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_v1/api_util.h"
18 #include "chrome/browser/sync_file_system/drive_backend_v1/drive_file_sync_util.h"
19 #include "chrome/browser/sync_file_system/drive_backend_v1/drive_metadata_store.h"
20 #include "chrome/browser/sync_file_system/drive_backend_v1/fake_drive_service_helper.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/syncable_file_system_util.h"
26 #include "chrome/test/base/testing_profile.h"
27 #include "content/public/test/test_browser_thread.h"
28 #include "content/public/test/test_browser_thread_bundle.h"
29 #include "testing/gtest/include/gtest/gtest.h"
30 #include "webkit/browser/fileapi/file_system_context.h"
32 #define FPL(path) FILE_PATH_LITERAL(path)
34 using content::BrowserThread;
36 using google_apis::GDataErrorCode;
37 using google_apis::ResourceEntry;
39 namespace sync_file_system {
41 using drive_backend::APIUtil;
42 using drive_backend::APIUtilInterface;
43 using drive_backend::FakeDriveServiceHelper;
47 void SyncResultCallback(bool* done,
48 SyncStatusCode* status_out,
49 fileapi::FileSystemURL* url_out,
50 SyncStatusCode status,
51 const fileapi::FileSystemURL& url) {
58 void SyncStatusResultCallback(bool* done,
59 SyncStatusCode* status_out,
60 SyncStatusCode status) {
66 void DatabaseInitResultCallback(bool* done,
67 SyncStatusCode* status_out,
69 SyncStatusCode status,
73 *created_out = created;
79 class DriveFileSyncServiceSyncTest : public testing::Test {
81 DriveFileSyncServiceSyncTest()
82 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
84 virtual ~DriveFileSyncServiceSyncTest() {
87 virtual void SetUp() OVERRIDE {
88 // TODO(tzik): Set up TestExtensionSystem to support simulated relaunch.
90 RegisterSyncableFileSystem();
91 local_sync_service_.reset(new LocalFileSyncService(&profile_));
93 fake_drive_service_ = new drive::FakeDriveService();
94 fake_drive_service_->Initialize("test_user@gmail.com");
95 ASSERT_TRUE(fake_drive_service_->LoadAccountMetadataForWapi(
96 "sync_file_system/account_metadata.json"));
97 ASSERT_TRUE(fake_drive_service_->LoadResourceListForWapi(
98 "gdata/root_feed.json"));
100 drive_uploader_ = new drive::DriveUploader(
101 fake_drive_service_, base::MessageLoopProxy::current().get());
103 fake_drive_helper_.reset(new FakeDriveServiceHelper(
104 fake_drive_service_, drive_uploader_));
107 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
108 bool created = false;
109 scoped_ptr<DriveMetadataStore> metadata_store(
110 new DriveMetadataStore(fake_drive_helper_->base_dir_path(),
111 base::MessageLoopProxy::current().get()));
112 metadata_store->Initialize(
113 base::Bind(&DatabaseInitResultCallback, &done, &status, &created));
116 EXPECT_EQ(SYNC_STATUS_OK, status);
117 EXPECT_TRUE(created);
119 scoped_ptr<APIUtil> api_util(APIUtil::CreateForTesting(
120 fake_drive_helper_->base_dir_path().AppendASCII("tmp"),
121 scoped_ptr<drive::DriveServiceInterface>(fake_drive_service_),
122 scoped_ptr<drive::DriveUploaderInterface>(drive_uploader_)));
124 remote_sync_service_ = DriveFileSyncService::CreateForTesting(
126 fake_drive_helper_->base_dir_path(),
127 api_util.PassAs<APIUtilInterface>(),
128 metadata_store.Pass());
130 local_sync_service_->SetLocalChangeProcessor(remote_sync_service_.get());
131 remote_sync_service_->SetRemoteChangeProcessor(local_sync_service_.get());
134 virtual void TearDown() OVERRIDE {
135 drive_uploader_ = NULL;
136 fake_drive_service_ = NULL;
137 remote_sync_service_.reset();
138 local_sync_service_.reset();
141 typedef std::map<GURL, CannedSyncableFileSystem*>::iterator iterator;
142 for (iterator itr = file_systems_.begin();
143 itr != file_systems_.end(); ++itr) {
144 itr->second->TearDown();
147 file_systems_.clear();
150 RevokeSyncableFileSystem();
154 void RegisterOrigin(const GURL& origin) {
155 if (!ContainsKey(file_systems_, origin)) {
156 CannedSyncableFileSystem* file_system = new CannedSyncableFileSystem(
158 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get(),
159 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)
163 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
164 file_system->SetUp();
165 local_sync_service_->MaybeInitializeFileSystemContext(
166 origin, file_system->file_system_context(),
167 base::Bind(&SyncStatusResultCallback, &done, &status));
170 EXPECT_EQ(SYNC_STATUS_OK, status);
172 file_system->backend()->sync_context()->
173 set_mock_notify_changes_duration_in_sec(0);
175 EXPECT_EQ(base::PLATFORM_FILE_OK, file_system->OpenFileSystem());
176 file_systems_[origin] = file_system;
180 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
181 remote_sync_service_->RegisterOrigin(
182 origin, base::Bind(&SyncStatusResultCallback, &done, &status));
185 EXPECT_EQ(SYNC_STATUS_OK, status);
188 void AddLocalFolder(const GURL& origin,
189 const base::FilePath& path) {
190 ASSERT_TRUE(ContainsKey(file_systems_, origin));
191 EXPECT_EQ(base::PLATFORM_FILE_OK,
192 file_systems_[origin]->CreateDirectory(
193 CreateSyncableFileSystemURL(origin, path)));
196 void AddOrUpdateLocalFile(const GURL& origin,
197 const base::FilePath& path,
198 const std::string& content) {
199 fileapi::FileSystemURL url(CreateSyncableFileSystemURL(origin, path));
200 ASSERT_TRUE(ContainsKey(file_systems_, origin));
201 EXPECT_EQ(base::PLATFORM_FILE_OK, file_systems_[origin]->CreateFile(url));
202 int64 bytes_written = file_systems_[origin]->WriteString(url, content);
203 EXPECT_EQ(static_cast<int64>(content.size()), bytes_written);
207 void UpdateLocalFile(const GURL& origin,
208 const base::FilePath& path,
209 const std::string& content) {
210 ASSERT_TRUE(ContainsKey(file_systems_, origin));
211 int64 bytes_written = file_systems_[origin]->WriteString(
212 CreateSyncableFileSystemURL(origin, path), content);
213 EXPECT_EQ(static_cast<int64>(content.size()), bytes_written);
217 void RemoveLocal(const GURL& origin, const base::FilePath& path) {
218 ASSERT_TRUE(ContainsKey(file_systems_, origin));
219 EXPECT_EQ(base::PLATFORM_FILE_OK,
220 file_systems_[origin]->Remove(
221 CreateSyncableFileSystemURL(origin, path),
222 true /* recursive */));
226 SyncStatusCode ProcessLocalChange() {
228 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
229 fileapi::FileSystemURL url;
230 local_sync_service_->ProcessLocalChange(
231 base::Bind(&SyncResultCallback, &done, &status, &url));
237 SyncStatusCode ProcessRemoteChange() {
239 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
240 fileapi::FileSystemURL url;
241 remote_sync_service_->ProcessRemoteChange(
242 base::Bind(&SyncResultCallback, &done, &status, &url));
248 SyncStatusCode ProcessChangesUntilDone() {
249 remote_sync_service_->OnNotificationReceived();
252 SyncStatusCode local_sync_status;
253 SyncStatusCode remote_sync_status;
255 local_sync_status = ProcessLocalChange();
256 if (local_sync_status != SYNC_STATUS_OK &&
257 local_sync_status != SYNC_STATUS_NO_CHANGE_TO_SYNC)
258 return local_sync_status;
260 remote_sync_status = ProcessRemoteChange();
261 if (remote_sync_status != SYNC_STATUS_OK &&
262 remote_sync_status != SYNC_STATUS_NO_CHANGE_TO_SYNC)
263 return remote_sync_status;
264 } while (local_sync_status != SYNC_STATUS_NO_CHANGE_TO_SYNC &&
265 remote_sync_status != SYNC_STATUS_NO_CHANGE_TO_SYNC);
266 return SYNC_STATUS_OK;
269 // Verifies local and remote files/folders are consistent.
270 // This function checks:
271 // - Each registered origin has corresponding remote folder.
272 // - Each local file/folder has corresponding remote one.
273 // - Each remote file/folder has corresponding local one.
274 // TODO(tzik): Handle conflict case. i.e. allow remote file has different
275 // file content if the corresponding local file conflicts to it.
276 void VerifyConsistency() {
277 std::string sync_root_folder_id;
278 GDataErrorCode error =
279 fake_drive_helper_->GetSyncRootFolderID(&sync_root_folder_id);
280 if (sync_root_folder_id.empty()) {
281 EXPECT_EQ(google_apis::HTTP_NOT_FOUND, error);
282 EXPECT_TRUE(file_systems_.empty());
285 EXPECT_EQ(google_apis::HTTP_SUCCESS, error);
287 ScopedVector<ResourceEntry> remote_entries;
288 EXPECT_EQ(google_apis::HTTP_SUCCESS,
289 fake_drive_helper_->ListFilesInFolder(
290 sync_root_folder_id, &remote_entries));
291 std::map<std::string, const ResourceEntry*> origin_root_by_title;
292 for (ScopedVector<ResourceEntry>::iterator itr = remote_entries.begin();
293 itr != remote_entries.end();
295 const ResourceEntry& remote_entry = **itr;
296 EXPECT_FALSE(ContainsKey(origin_root_by_title, remote_entry.title()));
297 origin_root_by_title[remote_entry.title()] = *itr;
300 for (std::map<GURL, CannedSyncableFileSystem*>::const_iterator itr =
301 file_systems_.begin();
302 itr != file_systems_.end(); ++itr) {
303 const GURL& origin = itr->first;
304 SCOPED_TRACE(testing::Message() << "Verifying origin: " << origin);
305 CannedSyncableFileSystem* file_system = itr->second;
306 ASSERT_TRUE(ContainsKey(origin_root_by_title, origin.host()));
307 VerifyConsistencyForFolder(
308 origin, base::FilePath(),
309 origin_root_by_title[origin.host()]->resource_id(),
314 void VerifyConsistencyForOrigin(const GURL& origin) {
315 std::string sync_root_folder_id;
316 ASSERT_EQ(google_apis::HTTP_SUCCESS,
317 fake_drive_helper_->GetSyncRootFolderID(&sync_root_folder_id));
318 ASSERT_FALSE(sync_root_folder_id.empty());
320 ScopedVector<ResourceEntry> origin_folder;
321 EXPECT_EQ(google_apis::HTTP_SUCCESS,
322 fake_drive_helper_->SearchByTitle(
323 sync_root_folder_id, origin.host(), &origin_folder));
324 ASSERT_EQ(1u, origin_folder.size());
326 ASSERT_TRUE(ContainsKey(file_systems_, origin));
327 VerifyConsistencyForFolder(
328 origin, base::FilePath(),
329 origin_folder[0]->resource_id(),
330 file_systems_[origin]);
333 void VerifyConsistencyForFolder(const GURL& origin,
334 const base::FilePath& path,
335 const std::string& folder_id,
336 CannedSyncableFileSystem* file_system) {
337 SCOPED_TRACE(testing::Message() << "Verifying path: " << path.value());
339 ScopedVector<ResourceEntry> remote_entries;
340 EXPECT_EQ(google_apis::HTTP_SUCCESS,
341 fake_drive_helper_->ListFilesInFolder(
342 folder_id, &remote_entries));
343 std::map<std::string, const ResourceEntry*> remote_entry_by_title;
344 for (ScopedVector<ResourceEntry>::iterator itr = remote_entries.begin();
345 itr != remote_entries.end();
347 const ResourceEntry& remote_entry = **itr;
348 EXPECT_FALSE(ContainsKey(remote_entry_by_title, remote_entry.title()));
349 remote_entry_by_title[remote_entry.title()] = *itr;
352 fileapi::FileSystemURL url(CreateSyncableFileSystemURL(origin, path));
353 CannedSyncableFileSystem::FileEntryList local_entries;
354 EXPECT_EQ(base::PLATFORM_FILE_OK,
355 file_system->ReadDirectory(url, &local_entries));
356 for (CannedSyncableFileSystem::FileEntryList::iterator itr =
357 local_entries.begin();
358 itr != local_entries.end();
360 const fileapi::DirectoryEntry& local_entry = *itr;
361 fileapi::FileSystemURL entry_url(
362 CreateSyncableFileSystemURL(origin, path.Append(local_entry.name)));
363 std::string title = DriveFileSyncService::PathToTitle(entry_url.path());
364 ASSERT_TRUE(ContainsKey(remote_entry_by_title, title));
365 const ResourceEntry& remote_entry = *remote_entry_by_title[title];
366 if (local_entry.is_directory) {
367 ASSERT_TRUE(remote_entry.is_folder());
368 VerifyConsistencyForFolder(origin, entry_url.path(),
369 remote_entry.resource_id(),
372 ASSERT_TRUE(remote_entry.is_file());
373 VerifyConsistencyForFile(origin, entry_url.path(),
374 remote_entry.resource_id(),
377 remote_entry_by_title.erase(title);
380 EXPECT_TRUE(remote_entry_by_title.empty());
383 void VerifyConsistencyForFile(const GURL& origin,
384 const base::FilePath& path,
385 const std::string& file_id,
386 CannedSyncableFileSystem* file_system) {
387 fileapi::FileSystemURL url(CreateSyncableFileSystemURL(origin, path));
388 std::string file_content;
389 EXPECT_EQ(google_apis::HTTP_SUCCESS,
390 fake_drive_helper_->ReadFile(file_id, &file_content));
391 EXPECT_EQ(base::PLATFORM_FILE_OK,
392 file_system->VerifyFile(url, file_content));
395 void FlushMessageLoop() {
396 base::MessageLoop::current()->RunUntilIdle();
397 BrowserThread::GetBlockingPool()->FlushForTesting();
398 base::MessageLoop::current()->RunUntilIdle();
401 void TestInitialization();
402 void TestLocalToRemoteBasic();
403 void TestRemoteToLocalBasic();
404 void TestLocalFileUpdate();
405 void TestRemoteFileUpdate();
406 void TestLocalFileDeletion();
407 void TestRemoteFileDeletion();
409 content::TestBrowserThreadBundle thread_bundle_;
411 TestingProfile profile_;
413 drive::FakeDriveService* fake_drive_service_;
414 drive::DriveUploader* drive_uploader_;
415 scoped_ptr<FakeDriveServiceHelper> fake_drive_helper_;
416 std::map<GURL, CannedSyncableFileSystem*> file_systems_;
418 scoped_ptr<DriveFileSyncService> remote_sync_service_;
419 scoped_ptr<LocalFileSyncService> local_sync_service_;
422 DISALLOW_COPY_AND_ASSIGN(DriveFileSyncServiceSyncTest);
425 void DriveFileSyncServiceSyncTest::TestInitialization() {
426 EXPECT_EQ(SYNC_STATUS_OK, ProcessChangesUntilDone());
430 void DriveFileSyncServiceSyncTest::TestLocalToRemoteBasic() {
431 const GURL kOrigin("chrome-extension://example");
433 RegisterOrigin(kOrigin);
434 AddOrUpdateLocalFile(kOrigin, base::FilePath(FPL("file")), "abcde");
436 EXPECT_EQ(SYNC_STATUS_OK, ProcessChangesUntilDone());
440 void DriveFileSyncServiceSyncTest::TestRemoteToLocalBasic() {
441 const GURL kOrigin("chrome-extension://example");
443 std::string sync_root_folder_id;
444 EXPECT_EQ(google_apis::HTTP_CREATED,
445 fake_drive_helper_->AddOrphanedFolder(
446 APIUtil::GetSyncRootDirectoryName(),
447 &sync_root_folder_id));
449 std::string origin_root_folder_id;
450 EXPECT_EQ(google_apis::HTTP_CREATED,
451 fake_drive_helper_->AddFolder(
452 sync_root_folder_id, kOrigin.host(), &origin_root_folder_id));
454 RegisterOrigin(kOrigin);
457 EXPECT_EQ(google_apis::HTTP_SUCCESS,
458 fake_drive_helper_->AddFile(
459 origin_root_folder_id, "file", "abcde", &file_id));
461 EXPECT_EQ(SYNC_STATUS_OK, ProcessChangesUntilDone());
465 void DriveFileSyncServiceSyncTest::TestLocalFileUpdate() {
466 const GURL kOrigin("chrome-extension://example");
467 const base::FilePath kPath(FPL("file"));
469 RegisterOrigin(kOrigin);
470 AddOrUpdateLocalFile(kOrigin, kPath, "abcde");
472 EXPECT_EQ(SYNC_STATUS_OK, ProcessChangesUntilDone());
473 VerifyConsistencyForOrigin(kOrigin);
475 UpdateLocalFile(kOrigin, kPath, "1234567890");
477 EXPECT_EQ(SYNC_STATUS_OK, ProcessChangesUntilDone());
481 void DriveFileSyncServiceSyncTest::TestRemoteFileUpdate() {
482 const GURL kOrigin("chrome-extension://example");
483 const base::FilePath kPath(FPL("file"));
484 const std::string kTitle(DriveFileSyncService::PathToTitle(kPath));
486 std::string sync_root_folder_id;
487 EXPECT_EQ(google_apis::HTTP_CREATED,
488 fake_drive_helper_->AddOrphanedFolder(
489 APIUtil::GetSyncRootDirectoryName(),
490 &sync_root_folder_id));
492 std::string origin_root_folder_id;
493 EXPECT_EQ(google_apis::HTTP_CREATED,
494 fake_drive_helper_->AddFolder(
495 sync_root_folder_id, kOrigin.host(), &origin_root_folder_id));
497 std::string remote_file_id;
498 EXPECT_EQ(google_apis::HTTP_SUCCESS,
499 fake_drive_helper_->AddFile(
500 origin_root_folder_id, kTitle, "abcde", &remote_file_id));
502 RegisterOrigin(kOrigin);
503 EXPECT_EQ(SYNC_STATUS_OK, ProcessChangesUntilDone());
504 VerifyConsistencyForOrigin(kOrigin);
506 EXPECT_EQ(google_apis::HTTP_SUCCESS,
507 fake_drive_helper_->UpdateFile(remote_file_id, "1234567890"));
509 EXPECT_EQ(SYNC_STATUS_OK, ProcessChangesUntilDone());
513 void DriveFileSyncServiceSyncTest::TestLocalFileDeletion() {
514 const GURL kOrigin("chrome-extension://example");
515 const base::FilePath kPath(FPL("file"));
517 RegisterOrigin(kOrigin);
518 AddOrUpdateLocalFile(kOrigin, kPath, "abcde");
520 EXPECT_EQ(SYNC_STATUS_OK, ProcessChangesUntilDone());
521 VerifyConsistencyForOrigin(kOrigin);
523 RemoveLocal(kOrigin, kPath);
525 EXPECT_EQ(SYNC_STATUS_OK, ProcessChangesUntilDone());
529 void DriveFileSyncServiceSyncTest::TestRemoteFileDeletion() {
530 const GURL kOrigin("chrome-extension://example");
531 const base::FilePath kPath(FPL("file"));
532 const std::string kTitle(DriveFileSyncService::PathToTitle(kPath));
534 std::string sync_root_folder_id;
535 EXPECT_EQ(google_apis::HTTP_CREATED,
536 fake_drive_helper_->AddOrphanedFolder(
537 APIUtil::GetSyncRootDirectoryName(),
538 &sync_root_folder_id));
540 std::string origin_root_folder_id;
541 EXPECT_EQ(google_apis::HTTP_CREATED,
542 fake_drive_helper_->AddFolder(
543 sync_root_folder_id, kOrigin.host(), &origin_root_folder_id));
545 std::string remote_file_id;
546 EXPECT_EQ(google_apis::HTTP_SUCCESS,
547 fake_drive_helper_->AddFile(
548 origin_root_folder_id, kTitle, "abcde", &remote_file_id));
550 RegisterOrigin(kOrigin);
551 EXPECT_EQ(SYNC_STATUS_OK, ProcessChangesUntilDone());
552 VerifyConsistencyForOrigin(kOrigin);
554 EXPECT_EQ(google_apis::HTTP_SUCCESS,
555 fake_drive_helper_->RemoveResource(remote_file_id));
557 EXPECT_EQ(SYNC_STATUS_OK, ProcessChangesUntilDone());
561 TEST_F(DriveFileSyncServiceSyncTest, InitializationTest) {
562 ASSERT_FALSE(IsDriveAPIDisabled());
563 TestInitialization();
566 TEST_F(DriveFileSyncServiceSyncTest, InitializationTest_WAPI) {
567 ScopedDisableDriveAPI disable_drive_api;
568 TestInitialization();
571 TEST_F(DriveFileSyncServiceSyncTest, LocalToRemoteBasicTest) {
572 ASSERT_FALSE(IsDriveAPIDisabled());
573 TestLocalToRemoteBasic();
576 TEST_F(DriveFileSyncServiceSyncTest, LocalToRemoteBasicTest_WAPI) {
577 ScopedDisableDriveAPI disable_drive_api;
578 TestLocalToRemoteBasic();
581 TEST_F(DriveFileSyncServiceSyncTest, RemoteToLocalBasicTest) {
582 ASSERT_FALSE(IsDriveAPIDisabled());
583 TestRemoteToLocalBasic();
586 TEST_F(DriveFileSyncServiceSyncTest, RemoteToLocalBasicTest_WAPI) {
587 ScopedDisableDriveAPI disable_drive_api;
588 TestRemoteToLocalBasic();
591 TEST_F(DriveFileSyncServiceSyncTest, LocalFileUpdateTest) {
592 ASSERT_FALSE(IsDriveAPIDisabled());
593 TestLocalFileUpdate();
596 TEST_F(DriveFileSyncServiceSyncTest, LocalFileUpdateTest_WAPI) {
597 ScopedDisableDriveAPI disable_drive_api;
598 TestLocalFileUpdate();
601 TEST_F(DriveFileSyncServiceSyncTest, RemoteFileUpdateTest) {
602 ASSERT_FALSE(IsDriveAPIDisabled());
603 TestRemoteFileUpdate();
606 TEST_F(DriveFileSyncServiceSyncTest, RemoteFileUpdateTest_WAPI) {
607 ScopedDisableDriveAPI disable_drive_api;
608 TestRemoteFileUpdate();
611 TEST_F(DriveFileSyncServiceSyncTest, LocalFileDeletionTest) {
612 ASSERT_FALSE(IsDriveAPIDisabled());
613 TestLocalFileDeletion();
616 TEST_F(DriveFileSyncServiceSyncTest, LocalFileDeletionTest_WAPI) {
617 ScopedDisableDriveAPI disable_drive_api;
618 TestLocalFileDeletion();
621 TEST_F(DriveFileSyncServiceSyncTest, RemoteFileDeletionTest) {
622 ASSERT_FALSE(IsDriveAPIDisabled());
623 TestRemoteFileDeletion();
626 TEST_F(DriveFileSyncServiceSyncTest, RemoteFileDeletionTest_WAPI) {
627 ScopedDisableDriveAPI disable_drive_api;
628 TestRemoteFileDeletion();
631 } // namespace sync_file_system