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