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