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/local_to_remote_syncer.h"
8 #include "base/callback.h"
9 #include "base/files/scoped_temp_dir.h"
10 #include "base/logging.h"
11 #include "base/run_loop.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "chrome/browser/drive/drive_api_util.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/fake_drive_uploader.h"
20 #include "chrome/browser/sync_file_system/drive_backend/list_changes_task.h"
21 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
22 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h"
23 #include "chrome/browser/sync_file_system/drive_backend/remote_to_local_syncer.h"
24 #include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h"
25 #include "chrome/browser/sync_file_system/drive_backend/sync_engine_initializer.h"
26 #include "chrome/browser/sync_file_system/drive_backend/sync_task_manager.h"
27 #include "chrome/browser/sync_file_system/drive_backend/sync_task_token.h"
28 #include "chrome/browser/sync_file_system/fake_remote_change_processor.h"
29 #include "chrome/browser/sync_file_system/sync_file_system_test_util.h"
30 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
31 #include "content/public/test/test_browser_thread_bundle.h"
32 #include "google_apis/drive/drive_api_parser.h"
33 #include "google_apis/drive/gdata_errorcode.h"
34 #include "testing/gtest/include/gtest/gtest.h"
35 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
36 #include "third_party/leveldatabase/src/include/leveldb/env.h"
38 namespace sync_file_system {
39 namespace drive_backend {
43 storage::FileSystemURL URL(const GURL& origin, const std::string& path) {
44 return CreateSyncableFileSystemURL(
45 origin, base::FilePath::FromUTF8Unsafe(path));
48 const int kRetryLimit = 100;
52 class LocalToRemoteSyncerTest : public testing::Test {
54 LocalToRemoteSyncerTest()
55 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
56 virtual ~LocalToRemoteSyncerTest() {}
58 virtual void SetUp() OVERRIDE {
59 ASSERT_TRUE(database_dir_.CreateUniqueTempDir());
60 in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
62 scoped_ptr<FakeDriveServiceWrapper>
63 fake_drive_service(new FakeDriveServiceWrapper);
64 scoped_ptr<drive::DriveUploaderInterface>
65 drive_uploader(new FakeDriveUploader(fake_drive_service.get()));
66 fake_drive_helper_.reset(new FakeDriveServiceHelper(
67 fake_drive_service.get(),
69 kSyncRootFolderTitle));
70 remote_change_processor_.reset(new FakeRemoteChangeProcessor);
72 context_.reset(new SyncEngineContext(
73 fake_drive_service.PassAs<drive::DriveServiceInterface>(),
74 drive_uploader.Pass(),
76 base::ThreadTaskRunnerHandle::Get(),
77 base::ThreadTaskRunnerHandle::Get()));
78 context_->SetRemoteChangeProcessor(remote_change_processor_.get());
80 RegisterSyncableFileSystem();
82 sync_task_manager_.reset(new SyncTaskManager(
83 base::WeakPtr<SyncTaskManager::Client>(),
84 10 /* maximum_background_task */,
85 base::ThreadTaskRunnerHandle::Get()));
86 sync_task_manager_->Initialize(SYNC_STATUS_OK);
89 virtual void TearDown() OVERRIDE {
90 sync_task_manager_.reset();
91 RevokeSyncableFileSystem();
92 fake_drive_helper_.reset();
94 base::RunLoop().RunUntilIdle();
97 void InitializeMetadataDatabase() {
98 SyncEngineInitializer* initializer =
99 new SyncEngineInitializer(context_.get(),
100 database_dir_.path(),
101 in_memory_env_.get());
102 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
104 sync_task_manager_->ScheduleSyncTask(
106 scoped_ptr<SyncTask>(initializer),
107 SyncTaskManager::PRIORITY_MED,
108 base::Bind(&LocalToRemoteSyncerTest::DidInitializeMetadataDatabase,
109 base::Unretained(this), initializer, &status));
111 base::RunLoop().RunUntilIdle();
112 EXPECT_EQ(SYNC_STATUS_OK, status);
115 void DidInitializeMetadataDatabase(SyncEngineInitializer* initializer,
116 SyncStatusCode* status_out,
117 SyncStatusCode status) {
118 *status_out = status;
119 context_->SetMetadataDatabase(initializer->PassMetadataDatabase());
122 void RegisterApp(const std::string& app_id,
123 const std::string& app_root_folder_id) {
124 SyncStatusCode status = context_->GetMetadataDatabase()->RegisterApp(
125 app_id, app_root_folder_id);
126 EXPECT_EQ(SYNC_STATUS_OK, status);
129 MetadataDatabase* GetMetadataDatabase() {
130 return context_->GetMetadataDatabase();
134 std::string CreateSyncRoot() {
135 std::string sync_root_folder_id;
136 EXPECT_EQ(google_apis::HTTP_CREATED,
137 fake_drive_helper_->AddOrphanedFolder(
138 kSyncRootFolderTitle, &sync_root_folder_id));
139 return sync_root_folder_id;
142 std::string CreateRemoteFolder(const std::string& parent_folder_id,
143 const std::string& title) {
144 std::string folder_id;
145 EXPECT_EQ(google_apis::HTTP_CREATED,
146 fake_drive_helper_->AddFolder(
147 parent_folder_id, title, &folder_id));
151 std::string CreateRemoteFile(const std::string& parent_folder_id,
152 const std::string& title,
153 const std::string& content) {
155 EXPECT_EQ(google_apis::HTTP_SUCCESS,
156 fake_drive_helper_->AddFile(
157 parent_folder_id, title, content, &file_id));
161 void DeleteResource(const std::string& file_id) {
162 EXPECT_EQ(google_apis::HTTP_NO_CONTENT,
163 fake_drive_helper_->DeleteResource(file_id));
166 SyncStatusCode RunLocalToRemoteSyncer(FileChange file_change,
167 const storage::FileSystemURL& url) {
168 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
169 base::FilePath local_path = base::FilePath::FromUTF8Unsafe("dummy");
170 scoped_ptr<LocalToRemoteSyncer> syncer(new LocalToRemoteSyncer(
172 SyncFileMetadata(file_change.file_type(), 0, base::Time()),
173 file_change, local_path, url));
174 syncer->RunPreflight(SyncTaskToken::CreateForTesting(
175 CreateResultReceiver(&status)));
176 base::RunLoop().RunUntilIdle();
180 SyncStatusCode ListChanges() {
181 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
182 sync_task_manager_->ScheduleSyncTask(
184 scoped_ptr<SyncTask>(new ListChangesTask(context_.get())),
185 SyncTaskManager::PRIORITY_MED,
186 CreateResultReceiver(&status));
187 base::RunLoop().RunUntilIdle();
191 SyncStatusCode RunRemoteToLocalSyncer() {
192 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
193 scoped_ptr<RemoteToLocalSyncer>
194 syncer(new RemoteToLocalSyncer(context_.get()));
195 syncer->RunPreflight(SyncTaskToken::CreateForTesting(
196 CreateResultReceiver(&status)));
197 base::RunLoop().RunUntilIdle();
201 SyncStatusCode RunRemoteToLocalSyncerUntilIdle() {
202 SyncStatusCode status;
205 if (retry_count++ > kRetryLimit)
207 status = RunRemoteToLocalSyncer();
208 } while (status == SYNC_STATUS_OK ||
209 status == SYNC_STATUS_RETRY ||
210 GetMetadataDatabase()->PromoteDemotedTrackers());
211 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, status);
215 ScopedVector<google_apis::ResourceEntry>
216 GetResourceEntriesForParentAndTitle(const std::string& parent_folder_id,
217 const std::string& title) {
218 ScopedVector<google_apis::ResourceEntry> entries;
219 EXPECT_EQ(google_apis::HTTP_SUCCESS,
220 fake_drive_helper_->SearchByTitle(
221 parent_folder_id, title, &entries));
222 return entries.Pass();
225 std::string GetFileIDForParentAndTitle(const std::string& parent_folder_id,
226 const std::string& title) {
227 ScopedVector<google_apis::ResourceEntry> entries =
228 GetResourceEntriesForParentAndTitle(parent_folder_id, title);
229 if (entries.size() != 1)
230 return std::string();
231 return entries[0]->resource_id();
234 void VerifyTitleUniqueness(
235 const std::string& parent_folder_id,
236 const std::string& title,
237 google_apis::ResourceEntry::ResourceEntryKind kind) {
238 ScopedVector<google_apis::ResourceEntry> entries;
239 EXPECT_EQ(google_apis::HTTP_SUCCESS,
240 fake_drive_helper_->SearchByTitle(
241 parent_folder_id, title, &entries));
242 ASSERT_EQ(1u, entries.size());
243 EXPECT_EQ(kind, entries[0]->kind());
246 void VerifyFileDeletion(const std::string& parent_folder_id,
247 const std::string& title) {
248 ScopedVector<google_apis::ResourceEntry> entries;
249 EXPECT_EQ(google_apis::HTTP_SUCCESS,
250 fake_drive_helper_->SearchByTitle(
251 parent_folder_id, title, &entries));
252 EXPECT_TRUE(entries.empty());
256 content::TestBrowserThreadBundle thread_bundle_;
257 base::ScopedTempDir database_dir_;
258 scoped_ptr<leveldb::Env> in_memory_env_;
260 scoped_ptr<SyncEngineContext> context_;
261 scoped_ptr<FakeDriveServiceHelper> fake_drive_helper_;
262 scoped_ptr<FakeRemoteChangeProcessor> remote_change_processor_;
263 scoped_ptr<SyncTaskManager> sync_task_manager_;
265 DISALLOW_COPY_AND_ASSIGN(LocalToRemoteSyncerTest);
268 TEST_F(LocalToRemoteSyncerTest, CreateFile) {
269 const GURL kOrigin("chrome-extension://example");
270 const std::string sync_root = CreateSyncRoot();
271 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
272 InitializeMetadataDatabase();
273 RegisterApp(kOrigin.host(), app_root);
275 EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
276 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
277 SYNC_FILE_TYPE_FILE),
278 URL(kOrigin, "file1")));
279 EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
280 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
281 SYNC_FILE_TYPE_DIRECTORY),
282 URL(kOrigin, "folder")));
283 EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
284 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
285 SYNC_FILE_TYPE_FILE),
286 URL(kOrigin, "folder/file2")));
288 std::string folder_id = GetFileIDForParentAndTitle(app_root, "folder");
289 ASSERT_FALSE(folder_id.empty());
291 VerifyTitleUniqueness(
292 app_root, "file1", google_apis::ResourceEntry::ENTRY_KIND_FILE);
293 VerifyTitleUniqueness(
294 app_root, "folder", google_apis::ResourceEntry::ENTRY_KIND_FOLDER);
295 VerifyTitleUniqueness(
296 folder_id, "file2", google_apis::ResourceEntry::ENTRY_KIND_FILE);
299 TEST_F(LocalToRemoteSyncerTest, CreateFileOnMissingPath) {
300 const GURL kOrigin("chrome-extension://example");
301 const std::string sync_root = CreateSyncRoot();
302 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
303 InitializeMetadataDatabase();
304 RegisterApp(kOrigin.host(), app_root);
306 // Run the syncer 3 times to create missing folder1 and folder2.
307 EXPECT_EQ(SYNC_STATUS_RETRY, RunLocalToRemoteSyncer(
308 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
309 SYNC_FILE_TYPE_FILE),
310 URL(kOrigin, "folder1/folder2/file")));
311 EXPECT_EQ(SYNC_STATUS_RETRY, RunLocalToRemoteSyncer(
312 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
313 SYNC_FILE_TYPE_FILE),
314 URL(kOrigin, "folder1/folder2/file")));
315 EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
316 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
317 SYNC_FILE_TYPE_FILE),
318 URL(kOrigin, "folder1/folder2/file")));
320 std::string folder_id1 = GetFileIDForParentAndTitle(app_root, "folder1");
321 ASSERT_FALSE(folder_id1.empty());
322 std::string folder_id2 = GetFileIDForParentAndTitle(folder_id1, "folder2");
323 ASSERT_FALSE(folder_id2.empty());
325 VerifyTitleUniqueness(
326 app_root, "folder1", google_apis::ResourceEntry::ENTRY_KIND_FOLDER);
327 VerifyTitleUniqueness(
328 folder_id1, "folder2", google_apis::ResourceEntry::ENTRY_KIND_FOLDER);
329 VerifyTitleUniqueness(
330 folder_id2, "file", google_apis::ResourceEntry::ENTRY_KIND_FILE);
333 TEST_F(LocalToRemoteSyncerTest, DeleteFile) {
334 const GURL kOrigin("chrome-extension://example");
335 const std::string sync_root = CreateSyncRoot();
336 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
337 InitializeMetadataDatabase();
338 RegisterApp(kOrigin.host(), app_root);
340 EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
341 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
342 SYNC_FILE_TYPE_FILE),
343 URL(kOrigin, "file")));
344 EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
345 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
346 SYNC_FILE_TYPE_DIRECTORY),
347 URL(kOrigin, "folder")));
349 VerifyTitleUniqueness(
350 app_root, "file", google_apis::ResourceEntry::ENTRY_KIND_FILE);
351 VerifyTitleUniqueness(
352 app_root, "folder", google_apis::ResourceEntry::ENTRY_KIND_FOLDER);
354 EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
355 FileChange(FileChange::FILE_CHANGE_DELETE,
356 SYNC_FILE_TYPE_FILE),
357 URL(kOrigin, "file")));
358 EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
359 FileChange(FileChange::FILE_CHANGE_DELETE,
360 SYNC_FILE_TYPE_DIRECTORY),
361 URL(kOrigin, "folder")));
363 VerifyFileDeletion(app_root, "file");
364 VerifyFileDeletion(app_root, "folder");
367 TEST_F(LocalToRemoteSyncerTest, Conflict_CreateFileOnFolder) {
368 const GURL kOrigin("chrome-extension://example");
369 const std::string sync_root = CreateSyncRoot();
370 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
371 InitializeMetadataDatabase();
372 RegisterApp(kOrigin.host(), app_root);
374 CreateRemoteFolder(app_root, "foo");
375 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
376 EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
377 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
378 SYNC_FILE_TYPE_FILE),
379 URL(kOrigin, "foo")));
381 // There should exist both file and folder on remote.
382 ScopedVector<google_apis::ResourceEntry> entries =
383 GetResourceEntriesForParentAndTitle(app_root, "foo");
384 ASSERT_EQ(2u, entries.size());
385 EXPECT_EQ(google_apis::ResourceEntry::ENTRY_KIND_FOLDER, entries[0]->kind());
386 EXPECT_EQ(google_apis::ResourceEntry::ENTRY_KIND_FILE, entries[1]->kind());
389 TEST_F(LocalToRemoteSyncerTest, Conflict_CreateFolderOnFile) {
390 const GURL kOrigin("chrome-extension://example");
391 const std::string sync_root = CreateSyncRoot();
392 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
393 InitializeMetadataDatabase();
394 RegisterApp(kOrigin.host(), app_root);
396 CreateRemoteFile(app_root, "foo", "data");
397 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
399 EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
400 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
401 SYNC_FILE_TYPE_DIRECTORY),
402 URL(kOrigin, "foo")));
404 // There should exist both file and folder on remote.
405 ScopedVector<google_apis::ResourceEntry> entries =
406 GetResourceEntriesForParentAndTitle(app_root, "foo");
407 ASSERT_EQ(2u, entries.size());
408 EXPECT_EQ(google_apis::ResourceEntry::ENTRY_KIND_FILE, entries[0]->kind());
409 EXPECT_EQ(google_apis::ResourceEntry::ENTRY_KIND_FOLDER, entries[1]->kind());
412 TEST_F(LocalToRemoteSyncerTest, Conflict_CreateFileOnFile) {
413 const GURL kOrigin("chrome-extension://example");
414 const std::string sync_root = CreateSyncRoot();
415 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
416 InitializeMetadataDatabase();
417 RegisterApp(kOrigin.host(), app_root);
419 CreateRemoteFile(app_root, "foo", "data");
420 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
422 EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
423 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
424 SYNC_FILE_TYPE_FILE),
425 URL(kOrigin, "foo")));
427 // There should exist both files on remote.
428 ScopedVector<google_apis::ResourceEntry> entries =
429 GetResourceEntriesForParentAndTitle(app_root, "foo");
430 ASSERT_EQ(2u, entries.size());
431 EXPECT_EQ(google_apis::ResourceEntry::ENTRY_KIND_FILE, entries[0]->kind());
432 EXPECT_EQ(google_apis::ResourceEntry::ENTRY_KIND_FILE, entries[1]->kind());
435 TEST_F(LocalToRemoteSyncerTest, Conflict_UpdateDeleteOnFile) {
436 const GURL kOrigin("chrome-extension://example");
437 const std::string sync_root = CreateSyncRoot();
438 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
439 InitializeMetadataDatabase();
440 RegisterApp(kOrigin.host(), app_root);
442 const std::string file_id = CreateRemoteFile(app_root, "foo", "data");
443 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
444 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC,
445 RunRemoteToLocalSyncerUntilIdle());
447 DeleteResource(file_id);
449 EXPECT_EQ(SYNC_STATUS_FILE_BUSY, RunLocalToRemoteSyncer(
450 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
451 SYNC_FILE_TYPE_FILE),
452 URL(kOrigin, "foo")));
453 EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
454 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
455 SYNC_FILE_TYPE_FILE),
456 URL(kOrigin, "foo")));
458 ScopedVector<google_apis::ResourceEntry> entries =
459 GetResourceEntriesForParentAndTitle(app_root, "foo");
460 ASSERT_EQ(1u, entries.size());
461 EXPECT_EQ(google_apis::ResourceEntry::ENTRY_KIND_FILE, entries[0]->kind());
462 EXPECT_TRUE(!entries[0]->deleted());
463 EXPECT_NE(file_id, entries[0]->resource_id());
466 TEST_F(LocalToRemoteSyncerTest, Conflict_CreateDeleteOnFile) {
467 const GURL kOrigin("chrome-extension://example");
468 const std::string sync_root = CreateSyncRoot();
469 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
470 InitializeMetadataDatabase();
471 RegisterApp(kOrigin.host(), app_root);
473 const std::string file_id = CreateRemoteFile(app_root, "foo", "data");
474 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
475 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC,
476 RunRemoteToLocalSyncerUntilIdle());
478 DeleteResource(file_id);
480 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
482 EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
483 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
484 SYNC_FILE_TYPE_FILE),
485 URL(kOrigin, "foo")));
487 ScopedVector<google_apis::ResourceEntry> entries =
488 GetResourceEntriesForParentAndTitle(app_root, "foo");
489 ASSERT_EQ(1u, entries.size());
490 EXPECT_EQ(google_apis::ResourceEntry::ENTRY_KIND_FILE, entries[0]->kind());
491 EXPECT_TRUE(!entries[0]->deleted());
492 EXPECT_NE(file_id, entries[0]->resource_id());
495 TEST_F(LocalToRemoteSyncerTest, Conflict_CreateFolderOnFolder) {
496 const GURL kOrigin("chrome-extension://example");
497 const std::string sync_root = CreateSyncRoot();
498 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
499 InitializeMetadataDatabase();
500 RegisterApp(kOrigin.host(), app_root);
502 const std::string folder_id = CreateRemoteFolder(app_root, "foo");
504 EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
505 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
506 SYNC_FILE_TYPE_DIRECTORY),
507 URL(kOrigin, "foo")));
509 ScopedVector<google_apis::ResourceEntry> entries =
510 GetResourceEntriesForParentAndTitle(app_root, "foo");
511 ASSERT_EQ(2u, entries.size());
512 EXPECT_EQ(google_apis::ResourceEntry::ENTRY_KIND_FOLDER, entries[0]->kind());
513 EXPECT_EQ(google_apis::ResourceEntry::ENTRY_KIND_FOLDER, entries[1]->kind());
514 EXPECT_TRUE(!entries[0]->deleted());
515 EXPECT_TRUE(!entries[1]->deleted());
516 EXPECT_TRUE(folder_id == entries[0]->resource_id() ||
517 folder_id == entries[1]->resource_id());
519 TrackerIDSet trackers;
520 EXPECT_TRUE(GetMetadataDatabase()->FindTrackersByFileID(
521 folder_id, &trackers));
522 EXPECT_EQ(1u, trackers.size());
523 ASSERT_TRUE(trackers.has_active());
526 TEST_F(LocalToRemoteSyncerTest, AppRootDeletion) {
527 const GURL kOrigin("chrome-extension://example");
528 const std::string sync_root = CreateSyncRoot();
529 const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
530 InitializeMetadataDatabase();
531 RegisterApp(kOrigin.host(), app_root);
533 DeleteResource(app_root);
534 EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
535 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC,
536 RunRemoteToLocalSyncerUntilIdle());
538 EXPECT_EQ(SYNC_STATUS_UNKNOWN_ORIGIN, RunLocalToRemoteSyncer(
539 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
540 SYNC_FILE_TYPE_DIRECTORY),
541 URL(kOrigin, "foo")));
543 // SyncEngine will re-register the app and resurrect the app root later.
546 } // namespace drive_backend
547 } // namespace sync_file_system