local_to_remote_syncer_unittest.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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.
4
5#include "chrome/browser/sync_file_system/drive_backend/local_to_remote_syncer.h"
6
7#include "base/bind.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 "chrome/browser/drive/drive_api_util.h"
13#include "chrome/browser/drive/drive_uploader.h"
14#include "chrome/browser/drive/fake_drive_service.h"
15#include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
16#include "chrome/browser/sync_file_system/drive_backend/drive_backend_test_util.h"
17#include "chrome/browser/sync_file_system/drive_backend/fake_drive_service_helper.h"
18#include "chrome/browser/sync_file_system/drive_backend/fake_drive_uploader.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/metadata_database.pb.h"
22#include "chrome/browser/sync_file_system/drive_backend/remote_to_local_syncer.h"
23#include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h"
24#include "chrome/browser/sync_file_system/drive_backend/sync_engine_initializer.h"
25#include "chrome/browser/sync_file_system/drive_backend/sync_task_manager.h"
26#include "chrome/browser/sync_file_system/drive_backend/sync_task_token.h"
27#include "chrome/browser/sync_file_system/fake_remote_change_processor.h"
28#include "chrome/browser/sync_file_system/sync_file_system_test_util.h"
29#include "chrome/browser/sync_file_system/syncable_file_system_util.h"
30#include "content/public/test/test_browser_thread_bundle.h"
31#include "google_apis/drive/drive_api_parser.h"
32#include "google_apis/drive/gdata_errorcode.h"
33#include "testing/gtest/include/gtest/gtest.h"
34#include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
35#include "third_party/leveldatabase/src/include/leveldb/env.h"
36
37namespace sync_file_system {
38namespace drive_backend {
39
40namespace {
41
42fileapi::FileSystemURL URL(const GURL& origin,
43                           const std::string& path) {
44  return CreateSyncableFileSystemURL(
45      origin, base::FilePath::FromUTF8Unsafe(path));
46}
47
48}  // namespace
49
50class LocalToRemoteSyncerTest : public testing::Test {
51 public:
52  LocalToRemoteSyncerTest()
53      : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
54  virtual ~LocalToRemoteSyncerTest() {}
55
56  virtual void SetUp() OVERRIDE {
57    ASSERT_TRUE(database_dir_.CreateUniqueTempDir());
58    in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
59
60    scoped_ptr<FakeDriveServiceWrapper>
61        fake_drive_service(new FakeDriveServiceWrapper);
62    scoped_ptr<drive::DriveUploaderInterface>
63        drive_uploader(new FakeDriveUploader(fake_drive_service.get()));
64    fake_drive_helper_.reset(new FakeDriveServiceHelper(
65        fake_drive_service.get(),
66        drive_uploader.get(),
67        kSyncRootFolderTitle));
68    remote_change_processor_.reset(new FakeRemoteChangeProcessor);
69
70    context_.reset(new SyncEngineContext(
71        fake_drive_service.PassAs<drive::DriveServiceInterface>(),
72        drive_uploader.Pass(),
73        NULL,
74        base::MessageLoopProxy::current(),
75        base::MessageLoopProxy::current(),
76        base::MessageLoopProxy::current()));
77    context_->SetRemoteChangeProcessor(remote_change_processor_.get());
78
79    RegisterSyncableFileSystem();
80
81    sync_task_manager_.reset(new SyncTaskManager(
82        base::WeakPtr<SyncTaskManager::Client>(),
83        10 /* maximum_background_task */));
84    sync_task_manager_->Initialize(SYNC_STATUS_OK);
85  }
86
87  virtual void TearDown() OVERRIDE {
88    sync_task_manager_.reset();
89    RevokeSyncableFileSystem();
90    fake_drive_helper_.reset();
91    context_.reset();
92    base::RunLoop().RunUntilIdle();
93  }
94
95  void InitializeMetadataDatabase() {
96    SyncEngineInitializer* initializer =
97        new SyncEngineInitializer(context_.get(),
98                                  database_dir_.path(),
99                                  in_memory_env_.get());
100    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
101
102    sync_task_manager_->ScheduleSyncTask(
103        FROM_HERE,
104        scoped_ptr<SyncTask>(initializer),
105        SyncTaskManager::PRIORITY_MED,
106        base::Bind(&LocalToRemoteSyncerTest::DidInitializeMetadataDatabase,
107                   base::Unretained(this), initializer, &status));
108
109    base::RunLoop().RunUntilIdle();
110    EXPECT_EQ(SYNC_STATUS_OK, status);
111  }
112
113  void DidInitializeMetadataDatabase(SyncEngineInitializer* initializer,
114                                     SyncStatusCode* status_out,
115                                     SyncStatusCode status) {
116    *status_out = status;
117    context_->SetMetadataDatabase(initializer->PassMetadataDatabase());
118  }
119
120  void RegisterApp(const std::string& app_id,
121                   const std::string& app_root_folder_id) {
122    SyncStatusCode status = SYNC_STATUS_FAILED;
123    context_->GetMetadataDatabase()->RegisterApp(
124        app_id, app_root_folder_id, CreateResultReceiver(&status));
125    base::RunLoop().RunUntilIdle();
126    EXPECT_EQ(SYNC_STATUS_OK, status);
127  }
128
129  MetadataDatabase* GetMetadataDatabase() {
130    return context_->GetMetadataDatabase();
131  }
132
133 protected:
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;
140  }
141
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));
148    return folder_id;
149  }
150
151  std::string CreateRemoteFile(const std::string& parent_folder_id,
152                               const std::string& title,
153                               const std::string& content) {
154    std::string file_id;
155    EXPECT_EQ(google_apis::HTTP_SUCCESS,
156              fake_drive_helper_->AddFile(
157                  parent_folder_id, title, content, &file_id));
158    return file_id;
159  }
160
161  void DeleteResource(const std::string& file_id) {
162    EXPECT_EQ(google_apis::HTTP_NO_CONTENT,
163              fake_drive_helper_->DeleteResource(file_id));
164  }
165
166  SyncStatusCode RunLocalToRemoteSyncer(FileChange file_change,
167                           const fileapi::FileSystemURL& url) {
168    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
169    base::FilePath local_path = base::FilePath::FromUTF8Unsafe("dummy");
170    scoped_ptr<LocalToRemoteSyncer> syncer(new LocalToRemoteSyncer(
171        context_.get(),
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();
177    return status;
178  }
179
180  SyncStatusCode ListChanges() {
181    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
182    sync_task_manager_->ScheduleSyncTask(
183        FROM_HERE,
184        scoped_ptr<SyncTask>(new ListChangesTask(context_.get())),
185        SyncTaskManager::PRIORITY_MED,
186        CreateResultReceiver(&status));
187    base::RunLoop().RunUntilIdle();
188    return status;
189  }
190
191  SyncStatusCode RunRemoteToLocalSyncer() {
192    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
193    scoped_ptr<RemoteToLocalSyncer>
194        syncer(new RemoteToLocalSyncer(context_.get()));
195    syncer->RunExclusive(CreateResultReceiver(&status));
196    base::RunLoop().RunUntilIdle();
197    return status;
198  }
199
200  ScopedVector<google_apis::ResourceEntry>
201  GetResourceEntriesForParentAndTitle(const std::string& parent_folder_id,
202                                      const std::string& title) {
203    ScopedVector<google_apis::ResourceEntry> entries;
204    EXPECT_EQ(google_apis::HTTP_SUCCESS,
205              fake_drive_helper_->SearchByTitle(
206                  parent_folder_id, title, &entries));
207    return entries.Pass();
208  }
209
210  std::string GetFileIDForParentAndTitle(const std::string& parent_folder_id,
211                                         const std::string& title) {
212    ScopedVector<google_apis::ResourceEntry> entries =
213        GetResourceEntriesForParentAndTitle(parent_folder_id, title);
214    if (entries.size() != 1)
215      return std::string();
216    return entries[0]->resource_id();
217  }
218
219  void VerifyTitleUniqueness(const std::string& parent_folder_id,
220                             const std::string& title,
221                             google_apis::DriveEntryKind kind) {
222    ScopedVector<google_apis::ResourceEntry> entries;
223    EXPECT_EQ(google_apis::HTTP_SUCCESS,
224              fake_drive_helper_->SearchByTitle(
225                  parent_folder_id, title, &entries));
226    ASSERT_EQ(1u, entries.size());
227    EXPECT_EQ(kind, entries[0]->kind());
228  }
229
230  void VerifyFileDeletion(const std::string& parent_folder_id,
231                          const std::string& title) {
232    ScopedVector<google_apis::ResourceEntry> entries;
233    EXPECT_EQ(google_apis::HTTP_SUCCESS,
234              fake_drive_helper_->SearchByTitle(
235                  parent_folder_id, title, &entries));
236    EXPECT_TRUE(entries.empty());
237  }
238
239 private:
240  content::TestBrowserThreadBundle thread_bundle_;
241  base::ScopedTempDir database_dir_;
242  scoped_ptr<leveldb::Env> in_memory_env_;
243
244  scoped_ptr<SyncEngineContext> context_;
245  scoped_ptr<FakeDriveServiceHelper> fake_drive_helper_;
246  scoped_ptr<FakeRemoteChangeProcessor> remote_change_processor_;
247  scoped_ptr<MetadataDatabase> metadata_database_;
248  scoped_ptr<SyncTaskManager> sync_task_manager_;
249
250  DISALLOW_COPY_AND_ASSIGN(LocalToRemoteSyncerTest);
251};
252
253TEST_F(LocalToRemoteSyncerTest, CreateFile) {
254  const GURL kOrigin("chrome-extension://example");
255  const std::string sync_root = CreateSyncRoot();
256  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
257  InitializeMetadataDatabase();
258  RegisterApp(kOrigin.host(), app_root);
259
260  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
261      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
262                 SYNC_FILE_TYPE_FILE),
263      URL(kOrigin, "file1")));
264  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
265      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
266                 SYNC_FILE_TYPE_DIRECTORY),
267      URL(kOrigin, "folder")));
268  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
269      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
270                 SYNC_FILE_TYPE_FILE),
271      URL(kOrigin, "folder/file2")));
272
273  std::string folder_id = GetFileIDForParentAndTitle(app_root, "folder");
274  ASSERT_FALSE(folder_id.empty());
275
276  VerifyTitleUniqueness(app_root, "file1", google_apis::ENTRY_KIND_FILE);
277  VerifyTitleUniqueness(app_root, "folder", google_apis::ENTRY_KIND_FOLDER);
278  VerifyTitleUniqueness(folder_id, "file2", google_apis::ENTRY_KIND_FILE);
279}
280
281TEST_F(LocalToRemoteSyncerTest, CreateFileOnMissingPath) {
282  const GURL kOrigin("chrome-extension://example");
283  const std::string sync_root = CreateSyncRoot();
284  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
285  InitializeMetadataDatabase();
286  RegisterApp(kOrigin.host(), app_root);
287
288  // Run the syncer 3 times to create missing folder1 and folder2.
289  EXPECT_EQ(SYNC_STATUS_RETRY, RunLocalToRemoteSyncer(
290      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
291                 SYNC_FILE_TYPE_FILE),
292      URL(kOrigin, "folder1/folder2/file")));
293  EXPECT_EQ(SYNC_STATUS_RETRY, RunLocalToRemoteSyncer(
294      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
295                 SYNC_FILE_TYPE_FILE),
296      URL(kOrigin, "folder1/folder2/file")));
297  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
298      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
299                 SYNC_FILE_TYPE_FILE),
300      URL(kOrigin, "folder1/folder2/file")));
301
302  std::string folder_id1 = GetFileIDForParentAndTitle(app_root, "folder1");
303  ASSERT_FALSE(folder_id1.empty());
304  std::string folder_id2 = GetFileIDForParentAndTitle(folder_id1, "folder2");
305  ASSERT_FALSE(folder_id2.empty());
306
307  VerifyTitleUniqueness(app_root, "folder1", google_apis::ENTRY_KIND_FOLDER);
308  VerifyTitleUniqueness(folder_id1, "folder2", google_apis::ENTRY_KIND_FOLDER);
309  VerifyTitleUniqueness(folder_id2, "file", google_apis::ENTRY_KIND_FILE);
310}
311
312TEST_F(LocalToRemoteSyncerTest, DeleteFile) {
313  const GURL kOrigin("chrome-extension://example");
314  const std::string sync_root = CreateSyncRoot();
315  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
316  InitializeMetadataDatabase();
317  RegisterApp(kOrigin.host(), app_root);
318
319  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
320      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
321                 SYNC_FILE_TYPE_FILE),
322      URL(kOrigin, "file")));
323  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
324      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
325                 SYNC_FILE_TYPE_DIRECTORY),
326      URL(kOrigin, "folder")));
327
328  VerifyTitleUniqueness(app_root, "file", google_apis::ENTRY_KIND_FILE);
329  VerifyTitleUniqueness(app_root, "folder", google_apis::ENTRY_KIND_FOLDER);
330
331  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
332      FileChange(FileChange::FILE_CHANGE_DELETE,
333                 SYNC_FILE_TYPE_FILE),
334      URL(kOrigin, "file")));
335  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
336      FileChange(FileChange::FILE_CHANGE_DELETE,
337                 SYNC_FILE_TYPE_DIRECTORY),
338      URL(kOrigin, "folder")));
339
340  VerifyFileDeletion(app_root, "file");
341  VerifyFileDeletion(app_root, "folder");
342}
343
344TEST_F(LocalToRemoteSyncerTest, Conflict_CreateFileOnFolder) {
345  const GURL kOrigin("chrome-extension://example");
346  const std::string sync_root = CreateSyncRoot();
347  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
348  InitializeMetadataDatabase();
349  RegisterApp(kOrigin.host(), app_root);
350
351  CreateRemoteFolder(app_root, "foo");
352  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
353  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
354      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
355                 SYNC_FILE_TYPE_FILE),
356      URL(kOrigin, "foo")));
357
358  // There should exist both file and folder on remote.
359  ScopedVector<google_apis::ResourceEntry> entries =
360      GetResourceEntriesForParentAndTitle(app_root, "foo");
361  ASSERT_EQ(2u, entries.size());
362  EXPECT_EQ(google_apis::ENTRY_KIND_FOLDER, entries[0]->kind());
363  EXPECT_EQ(google_apis::ENTRY_KIND_FILE, entries[1]->kind());
364}
365
366TEST_F(LocalToRemoteSyncerTest, Conflict_CreateFolderOnFile) {
367  const GURL kOrigin("chrome-extension://example");
368  const std::string sync_root = CreateSyncRoot();
369  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
370  InitializeMetadataDatabase();
371  RegisterApp(kOrigin.host(), app_root);
372
373  CreateRemoteFile(app_root, "foo", "data");
374  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
375
376  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
377      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
378                 SYNC_FILE_TYPE_DIRECTORY),
379      URL(kOrigin, "foo")));
380
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::ENTRY_KIND_FILE, entries[0]->kind());
386  EXPECT_EQ(google_apis::ENTRY_KIND_FOLDER, entries[1]->kind());
387}
388
389TEST_F(LocalToRemoteSyncerTest, Conflict_CreateFileOnFile) {
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);
395
396  CreateRemoteFile(app_root, "foo", "data");
397  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
398
399  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
400      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
401                 SYNC_FILE_TYPE_FILE),
402      URL(kOrigin, "foo")));
403
404  // There should exist both files on remote.
405  ScopedVector<google_apis::ResourceEntry> entries =
406      GetResourceEntriesForParentAndTitle(app_root, "foo");
407  ASSERT_EQ(2u, entries.size());
408  EXPECT_EQ(google_apis::ENTRY_KIND_FILE, entries[0]->kind());
409  EXPECT_EQ(google_apis::ENTRY_KIND_FILE, entries[1]->kind());
410}
411
412TEST_F(LocalToRemoteSyncerTest, Conflict_UpdateDeleteOnFile) {
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);
418
419  const std::string file_id = CreateRemoteFile(app_root, "foo", "data");
420  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
421
422  SyncStatusCode status;
423  do {
424    status = RunRemoteToLocalSyncer();
425    EXPECT_TRUE(status == SYNC_STATUS_OK ||
426                status == SYNC_STATUS_RETRY ||
427                status == SYNC_STATUS_NO_CHANGE_TO_SYNC);
428  } while (status != SYNC_STATUS_NO_CHANGE_TO_SYNC);
429
430  DeleteResource(file_id);
431
432  EXPECT_EQ(SYNC_STATUS_FILE_BUSY, RunLocalToRemoteSyncer(
433      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
434                 SYNC_FILE_TYPE_FILE),
435      URL(kOrigin, "foo")));
436  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
437      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
438                 SYNC_FILE_TYPE_FILE),
439      URL(kOrigin, "foo")));
440
441  ScopedVector<google_apis::ResourceEntry> entries =
442      GetResourceEntriesForParentAndTitle(app_root, "foo");
443  ASSERT_EQ(1u, entries.size());
444  EXPECT_EQ(google_apis::ENTRY_KIND_FILE, entries[0]->kind());
445  EXPECT_TRUE(!entries[0]->deleted());
446  EXPECT_NE(file_id, entries[0]->resource_id());
447}
448
449TEST_F(LocalToRemoteSyncerTest, Conflict_CreateDeleteOnFile) {
450  const GURL kOrigin("chrome-extension://example");
451  const std::string sync_root = CreateSyncRoot();
452  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
453  InitializeMetadataDatabase();
454  RegisterApp(kOrigin.host(), app_root);
455
456  const std::string file_id = CreateRemoteFile(app_root, "foo", "data");
457  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
458  SyncStatusCode status;
459  do {
460    status = RunRemoteToLocalSyncer();
461    EXPECT_TRUE(status == SYNC_STATUS_OK ||
462                status == SYNC_STATUS_RETRY ||
463                status == SYNC_STATUS_NO_CHANGE_TO_SYNC);
464  } while (status != SYNC_STATUS_NO_CHANGE_TO_SYNC);
465
466  DeleteResource(file_id);
467
468  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
469
470  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
471      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
472                 SYNC_FILE_TYPE_FILE),
473      URL(kOrigin, "foo")));
474
475  ScopedVector<google_apis::ResourceEntry> entries =
476      GetResourceEntriesForParentAndTitle(app_root, "foo");
477  ASSERT_EQ(1u, entries.size());
478  EXPECT_EQ(google_apis::ENTRY_KIND_FILE, entries[0]->kind());
479  EXPECT_TRUE(!entries[0]->deleted());
480  EXPECT_NE(file_id, entries[0]->resource_id());
481}
482
483TEST_F(LocalToRemoteSyncerTest, Conflict_CreateFolderOnFolder) {
484  const GURL kOrigin("chrome-extension://example");
485  const std::string sync_root = CreateSyncRoot();
486  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
487  InitializeMetadataDatabase();
488  RegisterApp(kOrigin.host(), app_root);
489
490  const std::string folder_id = CreateRemoteFolder(app_root, "foo");
491
492  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
493      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
494                 SYNC_FILE_TYPE_DIRECTORY),
495      URL(kOrigin, "foo")));
496
497  ScopedVector<google_apis::ResourceEntry> entries =
498      GetResourceEntriesForParentAndTitle(app_root, "foo");
499  ASSERT_EQ(2u, entries.size());
500  EXPECT_EQ(google_apis::ENTRY_KIND_FOLDER, entries[0]->kind());
501  EXPECT_EQ(google_apis::ENTRY_KIND_FOLDER, entries[1]->kind());
502  EXPECT_TRUE(!entries[0]->deleted());
503  EXPECT_TRUE(!entries[1]->deleted());
504  EXPECT_TRUE(folder_id == entries[0]->resource_id() ||
505              folder_id == entries[1]->resource_id());
506
507  TrackerIDSet trackers;
508  EXPECT_TRUE(GetMetadataDatabase()->FindTrackersByFileID(
509      folder_id, &trackers));
510  EXPECT_EQ(1u, trackers.size());
511  ASSERT_TRUE(trackers.has_active());
512}
513
514TEST_F(LocalToRemoteSyncerTest, AppRootDeletion) {
515  const GURL kOrigin("chrome-extension://example");
516  const std::string sync_root = CreateSyncRoot();
517  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
518  InitializeMetadataDatabase();
519  RegisterApp(kOrigin.host(), app_root);
520
521  DeleteResource(app_root);
522  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
523  SyncStatusCode status;
524  do {
525    status = RunRemoteToLocalSyncer();
526    EXPECT_TRUE(status == SYNC_STATUS_OK ||
527                status == SYNC_STATUS_RETRY ||
528                status == SYNC_STATUS_NO_CHANGE_TO_SYNC);
529  } while (status != SYNC_STATUS_NO_CHANGE_TO_SYNC);
530
531  EXPECT_EQ(SYNC_STATUS_UNKNOWN_ORIGIN, RunLocalToRemoteSyncer(
532      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
533                 SYNC_FILE_TYPE_DIRECTORY),
534      URL(kOrigin, "foo")));
535
536  // SyncEngine will re-register the app and resurrect the app root later.
537}
538
539}  // namespace drive_backend
540}  // namespace sync_file_system
541