local_to_remote_syncer_unittest.cc revision 6d86b77056ed63eb6871182f42a9fd5f07550f90
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
48const int kRetryLimit = 100;
49
50}  // namespace
51
52class LocalToRemoteSyncerTest : public testing::Test {
53 public:
54  LocalToRemoteSyncerTest()
55      : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
56  virtual ~LocalToRemoteSyncerTest() {}
57
58  virtual void SetUp() OVERRIDE {
59    ASSERT_TRUE(database_dir_.CreateUniqueTempDir());
60    in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
61
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(),
68        drive_uploader.get(),
69        kSyncRootFolderTitle));
70    remote_change_processor_.reset(new FakeRemoteChangeProcessor);
71
72    context_.reset(new SyncEngineContext(
73        fake_drive_service.PassAs<drive::DriveServiceInterface>(),
74        drive_uploader.Pass(),
75        NULL,
76        base::MessageLoopProxy::current(),
77        base::MessageLoopProxy::current(),
78        base::MessageLoopProxy::current()));
79    context_->SetRemoteChangeProcessor(remote_change_processor_.get());
80
81    RegisterSyncableFileSystem();
82
83    sync_task_manager_.reset(new SyncTaskManager(
84        base::WeakPtr<SyncTaskManager::Client>(),
85        10 /* maximum_background_task */,
86        base::MessageLoopProxy::current()));
87    sync_task_manager_->Initialize(SYNC_STATUS_OK);
88  }
89
90  virtual void TearDown() OVERRIDE {
91    sync_task_manager_.reset();
92    RevokeSyncableFileSystem();
93    fake_drive_helper_.reset();
94    context_.reset();
95    base::RunLoop().RunUntilIdle();
96  }
97
98  void InitializeMetadataDatabase() {
99    SyncEngineInitializer* initializer =
100        new SyncEngineInitializer(context_.get(),
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->RunPreflight(SyncTaskToken::CreateForTesting(
178        CreateResultReceiver(&status)));
179    base::RunLoop().RunUntilIdle();
180    return status;
181  }
182
183  SyncStatusCode ListChanges() {
184    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
185    sync_task_manager_->ScheduleSyncTask(
186        FROM_HERE,
187        scoped_ptr<SyncTask>(new ListChangesTask(context_.get())),
188        SyncTaskManager::PRIORITY_MED,
189        CreateResultReceiver(&status));
190    base::RunLoop().RunUntilIdle();
191    return status;
192  }
193
194  SyncStatusCode RunRemoteToLocalSyncer() {
195    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
196    scoped_ptr<RemoteToLocalSyncer>
197        syncer(new RemoteToLocalSyncer(context_.get()));
198    syncer->RunExclusive(CreateResultReceiver(&status));
199    base::RunLoop().RunUntilIdle();
200    return status;
201  }
202
203  ScopedVector<google_apis::ResourceEntry>
204  GetResourceEntriesForParentAndTitle(const std::string& parent_folder_id,
205                                      const std::string& title) {
206    ScopedVector<google_apis::ResourceEntry> entries;
207    EXPECT_EQ(google_apis::HTTP_SUCCESS,
208              fake_drive_helper_->SearchByTitle(
209                  parent_folder_id, title, &entries));
210    return entries.Pass();
211  }
212
213  std::string GetFileIDForParentAndTitle(const std::string& parent_folder_id,
214                                         const std::string& title) {
215    ScopedVector<google_apis::ResourceEntry> entries =
216        GetResourceEntriesForParentAndTitle(parent_folder_id, title);
217    if (entries.size() != 1)
218      return std::string();
219    return entries[0]->resource_id();
220  }
221
222  void VerifyTitleUniqueness(const std::string& parent_folder_id,
223                             const std::string& title,
224                             google_apis::DriveEntryKind kind) {
225    ScopedVector<google_apis::ResourceEntry> entries;
226    EXPECT_EQ(google_apis::HTTP_SUCCESS,
227              fake_drive_helper_->SearchByTitle(
228                  parent_folder_id, title, &entries));
229    ASSERT_EQ(1u, entries.size());
230    EXPECT_EQ(kind, entries[0]->kind());
231  }
232
233  void VerifyFileDeletion(const std::string& parent_folder_id,
234                          const std::string& title) {
235    ScopedVector<google_apis::ResourceEntry> entries;
236    EXPECT_EQ(google_apis::HTTP_SUCCESS,
237              fake_drive_helper_->SearchByTitle(
238                  parent_folder_id, title, &entries));
239    EXPECT_TRUE(entries.empty());
240  }
241
242 private:
243  content::TestBrowserThreadBundle thread_bundle_;
244  base::ScopedTempDir database_dir_;
245  scoped_ptr<leveldb::Env> in_memory_env_;
246
247  scoped_ptr<SyncEngineContext> context_;
248  scoped_ptr<FakeDriveServiceHelper> fake_drive_helper_;
249  scoped_ptr<FakeRemoteChangeProcessor> remote_change_processor_;
250  scoped_ptr<MetadataDatabase> metadata_database_;
251  scoped_ptr<SyncTaskManager> sync_task_manager_;
252
253  DISALLOW_COPY_AND_ASSIGN(LocalToRemoteSyncerTest);
254};
255
256TEST_F(LocalToRemoteSyncerTest, CreateFile) {
257  const GURL kOrigin("chrome-extension://example");
258  const std::string sync_root = CreateSyncRoot();
259  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
260  InitializeMetadataDatabase();
261  RegisterApp(kOrigin.host(), app_root);
262
263  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
264      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
265                 SYNC_FILE_TYPE_FILE),
266      URL(kOrigin, "file1")));
267  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
268      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
269                 SYNC_FILE_TYPE_DIRECTORY),
270      URL(kOrigin, "folder")));
271  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
272      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
273                 SYNC_FILE_TYPE_FILE),
274      URL(kOrigin, "folder/file2")));
275
276  std::string folder_id = GetFileIDForParentAndTitle(app_root, "folder");
277  ASSERT_FALSE(folder_id.empty());
278
279  VerifyTitleUniqueness(app_root, "file1", google_apis::ENTRY_KIND_FILE);
280  VerifyTitleUniqueness(app_root, "folder", google_apis::ENTRY_KIND_FOLDER);
281  VerifyTitleUniqueness(folder_id, "file2", google_apis::ENTRY_KIND_FILE);
282}
283
284TEST_F(LocalToRemoteSyncerTest, CreateFileOnMissingPath) {
285  const GURL kOrigin("chrome-extension://example");
286  const std::string sync_root = CreateSyncRoot();
287  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
288  InitializeMetadataDatabase();
289  RegisterApp(kOrigin.host(), app_root);
290
291  // Run the syncer 3 times to create missing folder1 and folder2.
292  EXPECT_EQ(SYNC_STATUS_RETRY, RunLocalToRemoteSyncer(
293      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
294                 SYNC_FILE_TYPE_FILE),
295      URL(kOrigin, "folder1/folder2/file")));
296  EXPECT_EQ(SYNC_STATUS_RETRY, RunLocalToRemoteSyncer(
297      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
298                 SYNC_FILE_TYPE_FILE),
299      URL(kOrigin, "folder1/folder2/file")));
300  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
301      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
302                 SYNC_FILE_TYPE_FILE),
303      URL(kOrigin, "folder1/folder2/file")));
304
305  std::string folder_id1 = GetFileIDForParentAndTitle(app_root, "folder1");
306  ASSERT_FALSE(folder_id1.empty());
307  std::string folder_id2 = GetFileIDForParentAndTitle(folder_id1, "folder2");
308  ASSERT_FALSE(folder_id2.empty());
309
310  VerifyTitleUniqueness(app_root, "folder1", google_apis::ENTRY_KIND_FOLDER);
311  VerifyTitleUniqueness(folder_id1, "folder2", google_apis::ENTRY_KIND_FOLDER);
312  VerifyTitleUniqueness(folder_id2, "file", google_apis::ENTRY_KIND_FILE);
313}
314
315TEST_F(LocalToRemoteSyncerTest, DeleteFile) {
316  const GURL kOrigin("chrome-extension://example");
317  const std::string sync_root = CreateSyncRoot();
318  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
319  InitializeMetadataDatabase();
320  RegisterApp(kOrigin.host(), app_root);
321
322  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
323      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
324                 SYNC_FILE_TYPE_FILE),
325      URL(kOrigin, "file")));
326  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
327      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
328                 SYNC_FILE_TYPE_DIRECTORY),
329      URL(kOrigin, "folder")));
330
331  VerifyTitleUniqueness(app_root, "file", google_apis::ENTRY_KIND_FILE);
332  VerifyTitleUniqueness(app_root, "folder", google_apis::ENTRY_KIND_FOLDER);
333
334  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
335      FileChange(FileChange::FILE_CHANGE_DELETE,
336                 SYNC_FILE_TYPE_FILE),
337      URL(kOrigin, "file")));
338  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
339      FileChange(FileChange::FILE_CHANGE_DELETE,
340                 SYNC_FILE_TYPE_DIRECTORY),
341      URL(kOrigin, "folder")));
342
343  VerifyFileDeletion(app_root, "file");
344  VerifyFileDeletion(app_root, "folder");
345}
346
347TEST_F(LocalToRemoteSyncerTest, Conflict_CreateFileOnFolder) {
348  const GURL kOrigin("chrome-extension://example");
349  const std::string sync_root = CreateSyncRoot();
350  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
351  InitializeMetadataDatabase();
352  RegisterApp(kOrigin.host(), app_root);
353
354  CreateRemoteFolder(app_root, "foo");
355  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
356  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
357      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
358                 SYNC_FILE_TYPE_FILE),
359      URL(kOrigin, "foo")));
360
361  // There should exist both file and folder on remote.
362  ScopedVector<google_apis::ResourceEntry> entries =
363      GetResourceEntriesForParentAndTitle(app_root, "foo");
364  ASSERT_EQ(2u, entries.size());
365  EXPECT_EQ(google_apis::ENTRY_KIND_FOLDER, entries[0]->kind());
366  EXPECT_EQ(google_apis::ENTRY_KIND_FILE, entries[1]->kind());
367}
368
369TEST_F(LocalToRemoteSyncerTest, Conflict_CreateFolderOnFile) {
370  const GURL kOrigin("chrome-extension://example");
371  const std::string sync_root = CreateSyncRoot();
372  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
373  InitializeMetadataDatabase();
374  RegisterApp(kOrigin.host(), app_root);
375
376  CreateRemoteFile(app_root, "foo", "data");
377  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
378
379  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
380      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
381                 SYNC_FILE_TYPE_DIRECTORY),
382      URL(kOrigin, "foo")));
383
384  // There should exist both file and folder on remote.
385  ScopedVector<google_apis::ResourceEntry> entries =
386      GetResourceEntriesForParentAndTitle(app_root, "foo");
387  ASSERT_EQ(2u, entries.size());
388  EXPECT_EQ(google_apis::ENTRY_KIND_FILE, entries[0]->kind());
389  EXPECT_EQ(google_apis::ENTRY_KIND_FOLDER, entries[1]->kind());
390}
391
392TEST_F(LocalToRemoteSyncerTest, Conflict_CreateFileOnFile) {
393  const GURL kOrigin("chrome-extension://example");
394  const std::string sync_root = CreateSyncRoot();
395  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
396  InitializeMetadataDatabase();
397  RegisterApp(kOrigin.host(), app_root);
398
399  CreateRemoteFile(app_root, "foo", "data");
400  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
401
402  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
403      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
404                 SYNC_FILE_TYPE_FILE),
405      URL(kOrigin, "foo")));
406
407  // There should exist both files on remote.
408  ScopedVector<google_apis::ResourceEntry> entries =
409      GetResourceEntriesForParentAndTitle(app_root, "foo");
410  ASSERT_EQ(2u, entries.size());
411  EXPECT_EQ(google_apis::ENTRY_KIND_FILE, entries[0]->kind());
412  EXPECT_EQ(google_apis::ENTRY_KIND_FILE, entries[1]->kind());
413}
414
415TEST_F(LocalToRemoteSyncerTest, Conflict_UpdateDeleteOnFile) {
416  const GURL kOrigin("chrome-extension://example");
417  const std::string sync_root = CreateSyncRoot();
418  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
419  InitializeMetadataDatabase();
420  RegisterApp(kOrigin.host(), app_root);
421
422  const std::string file_id = CreateRemoteFile(app_root, "foo", "data");
423  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
424
425  SyncStatusCode status;
426  int retry_count = 0;
427  do {
428    if (retry_count++ > kRetryLimit)
429      break;
430    status = RunRemoteToLocalSyncer();
431  } while (status == SYNC_STATUS_OK ||
432           status == SYNC_STATUS_RETRY);
433  EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, status);
434
435  DeleteResource(file_id);
436
437  EXPECT_EQ(SYNC_STATUS_FILE_BUSY, RunLocalToRemoteSyncer(
438      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
439                 SYNC_FILE_TYPE_FILE),
440      URL(kOrigin, "foo")));
441  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
442      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
443                 SYNC_FILE_TYPE_FILE),
444      URL(kOrigin, "foo")));
445
446  ScopedVector<google_apis::ResourceEntry> entries =
447      GetResourceEntriesForParentAndTitle(app_root, "foo");
448  ASSERT_EQ(1u, entries.size());
449  EXPECT_EQ(google_apis::ENTRY_KIND_FILE, entries[0]->kind());
450  EXPECT_TRUE(!entries[0]->deleted());
451  EXPECT_NE(file_id, entries[0]->resource_id());
452}
453
454TEST_F(LocalToRemoteSyncerTest, Conflict_CreateDeleteOnFile) {
455  const GURL kOrigin("chrome-extension://example");
456  const std::string sync_root = CreateSyncRoot();
457  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
458  InitializeMetadataDatabase();
459  RegisterApp(kOrigin.host(), app_root);
460
461  const std::string file_id = CreateRemoteFile(app_root, "foo", "data");
462  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
463  SyncStatusCode status;
464  int retry_count = 0;
465  do {
466    if (retry_count++ > kRetryLimit)
467      break;
468    status = RunRemoteToLocalSyncer();
469  } while (status == SYNC_STATUS_OK ||
470           status == SYNC_STATUS_RETRY);
471  EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, status);
472
473  DeleteResource(file_id);
474
475  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
476
477  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
478      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
479                 SYNC_FILE_TYPE_FILE),
480      URL(kOrigin, "foo")));
481
482  ScopedVector<google_apis::ResourceEntry> entries =
483      GetResourceEntriesForParentAndTitle(app_root, "foo");
484  ASSERT_EQ(1u, entries.size());
485  EXPECT_EQ(google_apis::ENTRY_KIND_FILE, entries[0]->kind());
486  EXPECT_TRUE(!entries[0]->deleted());
487  EXPECT_NE(file_id, entries[0]->resource_id());
488}
489
490TEST_F(LocalToRemoteSyncerTest, Conflict_CreateFolderOnFolder) {
491  const GURL kOrigin("chrome-extension://example");
492  const std::string sync_root = CreateSyncRoot();
493  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
494  InitializeMetadataDatabase();
495  RegisterApp(kOrigin.host(), app_root);
496
497  const std::string folder_id = CreateRemoteFolder(app_root, "foo");
498
499  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
500      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
501                 SYNC_FILE_TYPE_DIRECTORY),
502      URL(kOrigin, "foo")));
503
504  ScopedVector<google_apis::ResourceEntry> entries =
505      GetResourceEntriesForParentAndTitle(app_root, "foo");
506  ASSERT_EQ(2u, entries.size());
507  EXPECT_EQ(google_apis::ENTRY_KIND_FOLDER, entries[0]->kind());
508  EXPECT_EQ(google_apis::ENTRY_KIND_FOLDER, entries[1]->kind());
509  EXPECT_TRUE(!entries[0]->deleted());
510  EXPECT_TRUE(!entries[1]->deleted());
511  EXPECT_TRUE(folder_id == entries[0]->resource_id() ||
512              folder_id == entries[1]->resource_id());
513
514  TrackerIDSet trackers;
515  EXPECT_TRUE(GetMetadataDatabase()->FindTrackersByFileID(
516      folder_id, &trackers));
517  EXPECT_EQ(1u, trackers.size());
518  ASSERT_TRUE(trackers.has_active());
519}
520
521TEST_F(LocalToRemoteSyncerTest, AppRootDeletion) {
522  const GURL kOrigin("chrome-extension://example");
523  const std::string sync_root = CreateSyncRoot();
524  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
525  InitializeMetadataDatabase();
526  RegisterApp(kOrigin.host(), app_root);
527
528  DeleteResource(app_root);
529  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
530  SyncStatusCode status;
531  int retry_count = 0;
532  do {
533    if (retry_count++ > kRetryLimit)
534      break;
535    status = RunRemoteToLocalSyncer();
536  } while (status == SYNC_STATUS_OK ||
537           status == SYNC_STATUS_RETRY);
538  EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC, status);
539
540  EXPECT_EQ(SYNC_STATUS_UNKNOWN_ORIGIN, RunLocalToRemoteSyncer(
541      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
542                 SYNC_FILE_TYPE_DIRECTORY),
543      URL(kOrigin, "foo")));
544
545  // SyncEngine will re-register the app and resurrect the app root later.
546}
547
548}  // namespace drive_backend
549}  // namespace sync_file_system
550