local_to_remote_syncer_unittest.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "chrome/browser/sync_file_system/drive_backend/local_to_remote_syncer.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/callback.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/files/scoped_temp_dir.h"
107dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "base/logging.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/run_loop.h"
1258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "chrome/browser/drive/drive_api_util.h"
13effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "chrome/browser/drive/drive_uploader.h"
14c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/drive/fake_drive_service.h"
1558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/sync_file_system/drive_backend/drive_backend_test_util.h"
17e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch#include "chrome/browser/sync_file_system/drive_backend/fake_drive_service_helper.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/sync_file_system/drive_backend/fake_drive_uploader.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/sync_file_system/drive_backend/list_changes_task.h"
2058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
21effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h"
22effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "chrome/browser/sync_file_system/drive_backend/remote_to_local_syncer.h"
23e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch#include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h"
247dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "chrome/browser/sync_file_system/drive_backend/sync_engine_initializer.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/sync_file_system/fake_remote_change_processor.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/sync_file_system/sync_file_system_test_util.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/sync_file_system/syncable_file_system_util.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/test/test_browser_thread_bundle.h"
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "google_apis/drive/drive_api_parser.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "google_apis/drive/gdata_errorcode.h"
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "testing/gtest/include/gtest/gtest.h"
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/leveldatabase/src/include/leveldb/env.h"
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace sync_file_system {
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace drive_backend {
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace {
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)fileapi::FileSystemURL URL(const GURL& origin,
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           const std::string& path) {
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return CreateSyncableFileSystemURL(
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      origin, base::FilePath::FromUTF8Unsafe(path));
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}  // namespace
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class LocalToRemoteSyncerTest : public testing::Test,
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                public SyncEngineContext {
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public:
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  LocalToRemoteSyncerTest()
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
537dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  virtual ~LocalToRemoteSyncerTest() {}
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  virtual void SetUp() OVERRIDE {
5658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    ASSERT_TRUE(database_dir_.CreateUniqueTempDir());
57effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
58effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
59effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    fake_drive_service_.reset(new FakeDriveServiceWrapper);
6058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    ASSERT_TRUE(fake_drive_service_->LoadAccountMetadataForWapi(
6158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        "sync_file_system/account_metadata.json"));
6258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    ASSERT_TRUE(fake_drive_service_->LoadResourceListForWapi(
6358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        "gdata/empty_feed.json"));
6458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    drive_uploader_.reset(new FakeDriveUploader(fake_drive_service_.get()));
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    fake_drive_helper_.reset(new FakeDriveServiceHelper(
67effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        fake_drive_service_.get(), drive_uploader_.get(),
6858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        kSyncRootFolderTitle));
69e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    fake_remote_change_processor_.reset(new FakeRemoteChangeProcessor);
70e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    RegisterSyncableFileSystem();
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void TearDown() OVERRIDE {
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    RevokeSyncableFileSystem();
767d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
777dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    fake_remote_change_processor_.reset();
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    metadata_database_.reset();
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fake_drive_helper_.reset();
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    drive_uploader_.reset();
8158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    fake_drive_service_.reset();
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::RunLoop().RunUntilIdle();
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
85a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  void InitializeMetadataDatabase() {
8658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    SyncEngineInitializer initializer(this,
8758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                      base::MessageLoopProxy::current(),
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      fake_drive_service_.get(),
89a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                      database_dir_.path(),
90a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                      in_memory_env_.get());
9158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    initializer.RunSequential(CreateResultReceiver(&status));
9358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    base::RunLoop().RunUntilIdle();
94a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    EXPECT_EQ(SYNC_STATUS_OK, status);
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    metadata_database_ = initializer.PassMetadataDatabase();
96a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  void RegisterApp(const std::string& app_id,
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   const std::string& app_root_folder_id) {
1007d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    SyncStatusCode status = SYNC_STATUS_FAILED;
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    metadata_database_->RegisterApp(app_id, app_root_folder_id,
10258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                    CreateResultReceiver(&status));
103010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    base::RunLoop().RunUntilIdle();
104010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    EXPECT_EQ(SYNC_STATUS_OK, status);
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
106
107  virtual drive::DriveServiceInterface* GetDriveService() OVERRIDE {
108    return fake_drive_service_.get();
109  }
110
111  virtual drive::DriveUploaderInterface* GetDriveUploader() OVERRIDE {
112    return drive_uploader_.get();
113  }
114
115  virtual MetadataDatabase* GetMetadataDatabase() OVERRIDE {
116    return metadata_database_.get();
117  }
118
119  virtual RemoteChangeProcessor* GetRemoteChangeProcessor() OVERRIDE {
120    return fake_remote_change_processor_.get();
121  }
122
123  virtual base::SequencedTaskRunner* GetBlockingTaskRunner() OVERRIDE {
124    return base::MessageLoopProxy::current().get();
125  }
126
127 protected:
128  std::string CreateSyncRoot() {
129    std::string sync_root_folder_id;
130    EXPECT_EQ(google_apis::HTTP_CREATED,
131              fake_drive_helper_->AddOrphanedFolder(
132                  kSyncRootFolderTitle, &sync_root_folder_id));
133    return sync_root_folder_id;
134  }
135
136  std::string CreateRemoteFolder(const std::string& parent_folder_id,
137                                 const std::string& title) {
138    std::string folder_id;
139    EXPECT_EQ(google_apis::HTTP_CREATED,
140              fake_drive_helper_->AddFolder(
141                  parent_folder_id, title, &folder_id));
142    return folder_id;
143  }
144
145  std::string CreateRemoteFile(const std::string& parent_folder_id,
146                               const std::string& title,
147                               const std::string& content) {
148    std::string file_id;
149    EXPECT_EQ(google_apis::HTTP_SUCCESS,
150              fake_drive_helper_->AddFile(
151                  parent_folder_id, title, content, &file_id));
152    return file_id;
153  }
154
155  void DeleteResource(const std::string& file_id) {
156    EXPECT_EQ(google_apis::HTTP_NO_CONTENT,
157              fake_drive_helper_->DeleteResource(file_id));
158  }
159
160  SyncStatusCode RunLocalToRemoteSyncer(FileChange file_change,
161                           const fileapi::FileSystemURL& url) {
162    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
163    base::FilePath local_path = base::FilePath::FromUTF8Unsafe("dummy");
164    scoped_ptr<LocalToRemoteSyncer> syncer(new LocalToRemoteSyncer(
165        this, SyncFileMetadata(file_change.file_type(), 0, base::Time()),
166        file_change, local_path, url));
167    syncer->RunSequential(CreateResultReceiver(&status));
168    base::RunLoop().RunUntilIdle();
169    return status;
170  }
171
172  SyncStatusCode ListChanges() {
173    ListChangesTask list_changes(this);
174    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
175    list_changes.RunSequential(CreateResultReceiver(&status));
176    base::RunLoop().RunUntilIdle();
177    return status;
178  }
179
180  SyncStatusCode RunRemoteToLocalSyncer() {
181    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
182    scoped_ptr<RemoteToLocalSyncer> syncer(new RemoteToLocalSyncer(this));
183    syncer->RunSequential(CreateResultReceiver(&status));
184    base::RunLoop().RunUntilIdle();
185    return status;
186  }
187
188  ScopedVector<google_apis::ResourceEntry>
189  GetResourceEntriesForParentAndTitle(const std::string& parent_folder_id,
190                                      const std::string& title) {
191    ScopedVector<google_apis::ResourceEntry> entries;
192    EXPECT_EQ(google_apis::HTTP_SUCCESS,
193              fake_drive_helper_->SearchByTitle(
194                  parent_folder_id, title, &entries));
195    return entries.Pass();
196  }
197
198  std::string GetFileIDForParentAndTitle(const std::string& parent_folder_id,
199                                         const std::string& title) {
200    ScopedVector<google_apis::ResourceEntry> entries =
201        GetResourceEntriesForParentAndTitle(parent_folder_id, title);
202    if (entries.size() != 1)
203      return std::string();
204    return entries[0]->resource_id();
205  }
206
207  void VerifyTitleUniqueness(const std::string& parent_folder_id,
208                             const std::string& title,
209                             google_apis::DriveEntryKind kind) {
210    ScopedVector<google_apis::ResourceEntry> entries;
211    EXPECT_EQ(google_apis::HTTP_SUCCESS,
212              fake_drive_helper_->SearchByTitle(
213                  parent_folder_id, title, &entries));
214    ASSERT_EQ(1u, entries.size());
215    EXPECT_EQ(kind, entries[0]->kind());
216  }
217
218  void VerifyFileDeletion(const std::string& parent_folder_id,
219                          const std::string& title) {
220    ScopedVector<google_apis::ResourceEntry> entries;
221    EXPECT_EQ(google_apis::HTTP_SUCCESS,
222              fake_drive_helper_->SearchByTitle(
223                  parent_folder_id, title, &entries));
224    EXPECT_TRUE(entries.empty());
225  }
226
227 private:
228  content::TestBrowserThreadBundle thread_bundle_;
229  base::ScopedTempDir database_dir_;
230  scoped_ptr<leveldb::Env> in_memory_env_;
231
232  scoped_ptr<FakeDriveServiceWrapper> fake_drive_service_;
233  scoped_ptr<FakeDriveUploader> drive_uploader_;
234  scoped_ptr<FakeDriveServiceHelper> fake_drive_helper_;
235  scoped_ptr<MetadataDatabase> metadata_database_;
236  scoped_ptr<FakeRemoteChangeProcessor> fake_remote_change_processor_;
237
238  DISALLOW_COPY_AND_ASSIGN(LocalToRemoteSyncerTest);
239};
240
241TEST_F(LocalToRemoteSyncerTest, CreateFile) {
242  const GURL kOrigin("chrome-extension://example");
243  const std::string sync_root = CreateSyncRoot();
244  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
245  InitializeMetadataDatabase();
246  RegisterApp(kOrigin.host(), app_root);
247
248  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
249      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
250                 SYNC_FILE_TYPE_FILE),
251      URL(kOrigin, "file1")));
252  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
253      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
254                 SYNC_FILE_TYPE_DIRECTORY),
255      URL(kOrigin, "folder")));
256  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
257      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
258                 SYNC_FILE_TYPE_FILE),
259      URL(kOrigin, "folder/file2")));
260
261  std::string folder_id = GetFileIDForParentAndTitle(app_root, "folder");
262  ASSERT_FALSE(folder_id.empty());
263
264  VerifyTitleUniqueness(app_root, "file1", google_apis::ENTRY_KIND_FILE);
265  VerifyTitleUniqueness(app_root, "folder", google_apis::ENTRY_KIND_FOLDER);
266  VerifyTitleUniqueness(folder_id, "file2", google_apis::ENTRY_KIND_FILE);
267}
268
269TEST_F(LocalToRemoteSyncerTest, CreateFileOnMissingPath) {
270  const GURL kOrigin("chrome-extension://example");
271  const std::string sync_root = CreateSyncRoot();
272  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
273  InitializeMetadataDatabase();
274  RegisterApp(kOrigin.host(), app_root);
275
276  // Run the syncer 3 times to create missing folder1 and folder2.
277  EXPECT_EQ(SYNC_STATUS_RETRY, RunLocalToRemoteSyncer(
278      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
279                 SYNC_FILE_TYPE_FILE),
280      URL(kOrigin, "folder1/folder2/file")));
281  EXPECT_EQ(SYNC_STATUS_RETRY, RunLocalToRemoteSyncer(
282      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
283                 SYNC_FILE_TYPE_FILE),
284      URL(kOrigin, "folder1/folder2/file")));
285  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
286      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
287                 SYNC_FILE_TYPE_FILE),
288      URL(kOrigin, "folder1/folder2/file")));
289
290  std::string folder_id1 = GetFileIDForParentAndTitle(app_root, "folder1");
291  ASSERT_FALSE(folder_id1.empty());
292  std::string folder_id2 = GetFileIDForParentAndTitle(folder_id1, "folder2");
293  ASSERT_FALSE(folder_id2.empty());
294
295  VerifyTitleUniqueness(app_root, "folder1", google_apis::ENTRY_KIND_FOLDER);
296  VerifyTitleUniqueness(folder_id1, "folder2", google_apis::ENTRY_KIND_FOLDER);
297  VerifyTitleUniqueness(folder_id2, "file", google_apis::ENTRY_KIND_FILE);
298}
299
300TEST_F(LocalToRemoteSyncerTest, DeleteFile) {
301  const GURL kOrigin("chrome-extension://example");
302  const std::string sync_root = CreateSyncRoot();
303  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
304  InitializeMetadataDatabase();
305  RegisterApp(kOrigin.host(), app_root);
306
307  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
308      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
309                 SYNC_FILE_TYPE_FILE),
310      URL(kOrigin, "file")));
311  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
312      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
313                 SYNC_FILE_TYPE_DIRECTORY),
314      URL(kOrigin, "folder")));
315
316  VerifyTitleUniqueness(app_root, "file", google_apis::ENTRY_KIND_FILE);
317  VerifyTitleUniqueness(app_root, "folder", google_apis::ENTRY_KIND_FOLDER);
318
319  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
320      FileChange(FileChange::FILE_CHANGE_DELETE,
321                 SYNC_FILE_TYPE_FILE),
322      URL(kOrigin, "file")));
323  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
324      FileChange(FileChange::FILE_CHANGE_DELETE,
325                 SYNC_FILE_TYPE_DIRECTORY),
326      URL(kOrigin, "folder")));
327
328  VerifyFileDeletion(app_root, "file");
329  VerifyFileDeletion(app_root, "folder");
330}
331
332TEST_F(LocalToRemoteSyncerTest, Conflict_CreateFileOnFolder) {
333  const GURL kOrigin("chrome-extension://example");
334  const std::string sync_root = CreateSyncRoot();
335  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
336  InitializeMetadataDatabase();
337  RegisterApp(kOrigin.host(), app_root);
338
339  CreateRemoteFolder(app_root, "foo");
340  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
341  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
342      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
343                 SYNC_FILE_TYPE_FILE),
344      URL(kOrigin, "foo")));
345
346  // There should exist both file and folder on remote.
347  ScopedVector<google_apis::ResourceEntry> entries =
348      GetResourceEntriesForParentAndTitle(app_root, "foo");
349  ASSERT_EQ(2u, entries.size());
350  EXPECT_EQ(google_apis::ENTRY_KIND_FOLDER, entries[0]->kind());
351  EXPECT_EQ(google_apis::ENTRY_KIND_FILE, entries[1]->kind());
352}
353
354TEST_F(LocalToRemoteSyncerTest, Conflict_CreateFolderOnFile) {
355  const GURL kOrigin("chrome-extension://example");
356  const std::string sync_root = CreateSyncRoot();
357  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
358  InitializeMetadataDatabase();
359  RegisterApp(kOrigin.host(), app_root);
360
361  CreateRemoteFile(app_root, "foo", "data");
362  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
363
364  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
365      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
366                 SYNC_FILE_TYPE_DIRECTORY),
367      URL(kOrigin, "foo")));
368
369  // There should exist both file and folder on remote.
370  ScopedVector<google_apis::ResourceEntry> entries =
371      GetResourceEntriesForParentAndTitle(app_root, "foo");
372  ASSERT_EQ(2u, entries.size());
373  EXPECT_EQ(google_apis::ENTRY_KIND_FILE, entries[0]->kind());
374  EXPECT_EQ(google_apis::ENTRY_KIND_FOLDER, entries[1]->kind());
375}
376
377TEST_F(LocalToRemoteSyncerTest, Conflict_CreateFileOnFile) {
378  const GURL kOrigin("chrome-extension://example");
379  const std::string sync_root = CreateSyncRoot();
380  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
381  InitializeMetadataDatabase();
382  RegisterApp(kOrigin.host(), app_root);
383
384  CreateRemoteFile(app_root, "foo", "data");
385  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
386
387  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
388      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
389                 SYNC_FILE_TYPE_FILE),
390      URL(kOrigin, "foo")));
391
392  // There should exist both files on remote.
393  ScopedVector<google_apis::ResourceEntry> entries =
394      GetResourceEntriesForParentAndTitle(app_root, "foo");
395  ASSERT_EQ(2u, entries.size());
396  EXPECT_EQ(google_apis::ENTRY_KIND_FILE, entries[0]->kind());
397  EXPECT_EQ(google_apis::ENTRY_KIND_FILE, entries[1]->kind());
398}
399
400TEST_F(LocalToRemoteSyncerTest, Conflict_UpdateDeleteOnFile) {
401  const GURL kOrigin("chrome-extension://example");
402  const std::string sync_root = CreateSyncRoot();
403  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
404  InitializeMetadataDatabase();
405  RegisterApp(kOrigin.host(), app_root);
406
407  const std::string file_id = CreateRemoteFile(app_root, "foo", "data");
408  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
409
410  SyncStatusCode status;
411  do {
412    status = RunRemoteToLocalSyncer();
413    EXPECT_TRUE(status == SYNC_STATUS_OK ||
414                status == SYNC_STATUS_RETRY ||
415                status == SYNC_STATUS_NO_CHANGE_TO_SYNC);
416  } while (status != SYNC_STATUS_NO_CHANGE_TO_SYNC);
417
418  DeleteResource(file_id);
419
420  EXPECT_EQ(SYNC_STATUS_FILE_BUSY, RunLocalToRemoteSyncer(
421      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
422                 SYNC_FILE_TYPE_FILE),
423      URL(kOrigin, "foo")));
424  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
425      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
426                 SYNC_FILE_TYPE_FILE),
427      URL(kOrigin, "foo")));
428
429  ScopedVector<google_apis::ResourceEntry> entries =
430      GetResourceEntriesForParentAndTitle(app_root, "foo");
431  ASSERT_EQ(1u, entries.size());
432  EXPECT_EQ(google_apis::ENTRY_KIND_FILE, entries[0]->kind());
433  EXPECT_TRUE(!entries[0]->deleted());
434  EXPECT_NE(file_id, entries[0]->resource_id());
435}
436
437TEST_F(LocalToRemoteSyncerTest, Conflict_CreateDeleteOnFile) {
438  const GURL kOrigin("chrome-extension://example");
439  const std::string sync_root = CreateSyncRoot();
440  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
441  InitializeMetadataDatabase();
442  RegisterApp(kOrigin.host(), app_root);
443
444  const std::string file_id = CreateRemoteFile(app_root, "foo", "data");
445  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
446  SyncStatusCode status;
447  do {
448    status = RunRemoteToLocalSyncer();
449    EXPECT_TRUE(status == SYNC_STATUS_OK ||
450                status == SYNC_STATUS_RETRY ||
451                status == SYNC_STATUS_NO_CHANGE_TO_SYNC);
452  } while (status != SYNC_STATUS_NO_CHANGE_TO_SYNC);
453
454  DeleteResource(file_id);
455
456  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
457
458  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
459      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
460                 SYNC_FILE_TYPE_FILE),
461      URL(kOrigin, "foo")));
462
463  ScopedVector<google_apis::ResourceEntry> entries =
464      GetResourceEntriesForParentAndTitle(app_root, "foo");
465  ASSERT_EQ(1u, entries.size());
466  EXPECT_EQ(google_apis::ENTRY_KIND_FILE, entries[0]->kind());
467  EXPECT_TRUE(!entries[0]->deleted());
468  EXPECT_NE(file_id, entries[0]->resource_id());
469}
470
471TEST_F(LocalToRemoteSyncerTest, Conflict_CreateFolderOnFolder) {
472  const GURL kOrigin("chrome-extension://example");
473  const std::string sync_root = CreateSyncRoot();
474  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
475  InitializeMetadataDatabase();
476  RegisterApp(kOrigin.host(), app_root);
477
478  const std::string folder_id = CreateRemoteFolder(app_root, "foo");
479
480  EXPECT_EQ(SYNC_STATUS_OK, RunLocalToRemoteSyncer(
481      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
482                 SYNC_FILE_TYPE_DIRECTORY),
483      URL(kOrigin, "foo")));
484
485  ScopedVector<google_apis::ResourceEntry> entries =
486      GetResourceEntriesForParentAndTitle(app_root, "foo");
487  ASSERT_EQ(2u, entries.size());
488  EXPECT_EQ(google_apis::ENTRY_KIND_FOLDER, entries[0]->kind());
489  EXPECT_EQ(google_apis::ENTRY_KIND_FOLDER, entries[1]->kind());
490  EXPECT_TRUE(!entries[0]->deleted());
491  EXPECT_TRUE(!entries[1]->deleted());
492  EXPECT_TRUE(folder_id == entries[0]->resource_id() ||
493              folder_id == entries[1]->resource_id());
494
495  TrackerIDSet trackers;
496  EXPECT_TRUE(GetMetadataDatabase()->FindTrackersByFileID(
497      folder_id, &trackers));
498  EXPECT_EQ(1u, trackers.size());
499  ASSERT_TRUE(trackers.has_active());
500}
501
502TEST_F(LocalToRemoteSyncerTest, AppRootDeletion) {
503  const GURL kOrigin("chrome-extension://example");
504  const std::string sync_root = CreateSyncRoot();
505  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
506  InitializeMetadataDatabase();
507  RegisterApp(kOrigin.host(), app_root);
508
509  DeleteResource(app_root);
510  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
511  SyncStatusCode status;
512  do {
513    status = RunRemoteToLocalSyncer();
514    EXPECT_TRUE(status == SYNC_STATUS_OK ||
515                status == SYNC_STATUS_RETRY ||
516                status == SYNC_STATUS_NO_CHANGE_TO_SYNC);
517  } while (status != SYNC_STATUS_NO_CHANGE_TO_SYNC);
518
519  EXPECT_EQ(SYNC_STATUS_UNKNOWN_ORIGIN, RunLocalToRemoteSyncer(
520      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
521                 SYNC_FILE_TYPE_DIRECTORY),
522      URL(kOrigin, "foo")));
523
524  // SyncEngine will re-register the app and resurrect the app root later.
525}
526
527}  // namespace drive_backend
528}  // namespace sync_file_system
529