conflict_resolver_unittest.cc revision 0529e5d033099cbfc42635f6f6183833b09dff6e
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/conflict_resolver.h"
6
7#include "base/bind.h"
8#include "base/callback.h"
9#include "base/file_util.h"
10#include "base/files/scoped_temp_dir.h"
11#include "base/run_loop.h"
12#include "chrome/browser/drive/drive_uploader.h"
13#include "chrome/browser/drive/fake_drive_service.h"
14#include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
15#include "chrome/browser/sync_file_system/drive_backend/drive_backend_test_util.h"
16#include "chrome/browser/sync_file_system/drive_backend/fake_drive_service_helper.h"
17#include "chrome/browser/sync_file_system/drive_backend/fake_drive_uploader.h"
18#include "chrome/browser/sync_file_system/drive_backend/list_changes_task.h"
19#include "chrome/browser/sync_file_system/drive_backend/local_to_remote_syncer.h"
20#include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
21#include "chrome/browser/sync_file_system/drive_backend/remote_to_local_syncer.h"
22#include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h"
23#include "chrome/browser/sync_file_system/drive_backend/sync_engine_initializer.h"
24#include "chrome/browser/sync_file_system/drive_backend/sync_task_manager.h"
25#include "chrome/browser/sync_file_system/fake_remote_change_processor.h"
26#include "chrome/browser/sync_file_system/sync_file_system_test_util.h"
27#include "chrome/browser/sync_file_system/syncable_file_system_util.h"
28#include "content/public/test/test_browser_thread_bundle.h"
29#include "google_apis/drive/gdata_errorcode.h"
30#include "testing/gtest/include/gtest/gtest.h"
31#include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
32#include "third_party/leveldatabase/src/include/leveldb/env.h"
33
34namespace sync_file_system {
35namespace drive_backend {
36
37namespace {
38
39fileapi::FileSystemURL URL(const GURL& origin,
40                           const std::string& path) {
41  return CreateSyncableFileSystemURL(
42      origin, base::FilePath::FromUTF8Unsafe(path));
43}
44
45}  // namespace
46
47class ConflictResolverTest : public testing::Test {
48 public:
49  typedef FakeRemoteChangeProcessor::URLToFileChangesMap URLToFileChangesMap;
50
51  ConflictResolverTest()
52      : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
53  virtual ~ConflictResolverTest() {}
54
55  virtual void SetUp() OVERRIDE {
56    ASSERT_TRUE(database_dir_.CreateUniqueTempDir());
57    in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
58
59    fake_drive_service_.reset(new FakeDriveServiceWrapper);
60
61    drive_uploader_.reset(new FakeDriveUploader(fake_drive_service_.get()));
62    fake_drive_helper_.reset(
63        new FakeDriveServiceHelper(fake_drive_service_.get(),
64                                   drive_uploader_.get(),
65                                   kSyncRootFolderTitle));
66    fake_remote_change_processor_.reset(new FakeRemoteChangeProcessor);
67
68    context_.reset(new SyncEngineContext(
69        fake_drive_service_.get(),
70        drive_uploader_.get(),
71        base::MessageLoopProxy::current(),
72        base::MessageLoopProxy::current()));
73    context_->SetRemoteChangeProcessor(fake_remote_change_processor_.get());
74
75    RegisterSyncableFileSystem();
76
77    sync_task_manager_.reset(new SyncTaskManager(
78        base::WeakPtr<SyncTaskManager::Client>(),
79        10 /* maximum_background_task */));
80    sync_task_manager_->Initialize(SYNC_STATUS_OK);
81  }
82
83  virtual void TearDown() OVERRIDE {
84    sync_task_manager_.reset();
85    fake_drive_service_.reset();
86    drive_uploader_.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    sync_task_manager_->ScheduleSyncTask(
104        FROM_HERE,
105        scoped_ptr<SyncTask>(initializer),
106        SyncTaskManager::PRIORITY_MED,
107        base::Bind(&ConflictResolverTest::DidInitializeMetadataDatabase,
108                   base::Unretained(this), initializer, &status));
109
110    base::RunLoop().RunUntilIdle();
111    EXPECT_EQ(SYNC_STATUS_OK, status);
112  }
113
114  void DidInitializeMetadataDatabase(SyncEngineInitializer* initializer,
115                                     SyncStatusCode* status_out,
116                                     SyncStatusCode status) {
117    context_->SetMetadataDatabase(initializer->PassMetadataDatabase());
118    *status_out = status;
119  }
120
121  void RegisterApp(const std::string& app_id,
122                   const std::string& app_root_folder_id) {
123    SyncStatusCode status = SYNC_STATUS_FAILED;
124    context_->GetMetadataDatabase()->RegisterApp(app_id, app_root_folder_id,
125                                                 CreateResultReceiver(&status));
126    base::RunLoop().RunUntilIdle();
127    EXPECT_EQ(SYNC_STATUS_OK, status);
128  }
129
130 protected:
131  std::string CreateSyncRoot() {
132    std::string sync_root_folder_id;
133    EXPECT_EQ(google_apis::HTTP_CREATED,
134              fake_drive_helper_->AddOrphanedFolder(
135                  kSyncRootFolderTitle, &sync_root_folder_id));
136    return sync_root_folder_id;
137  }
138
139  std::string CreateRemoteFolder(const std::string& parent_folder_id,
140                                 const std::string& title) {
141    std::string folder_id;
142    EXPECT_EQ(google_apis::HTTP_CREATED,
143              fake_drive_helper_->AddFolder(
144                  parent_folder_id, title, &folder_id));
145    return folder_id;
146  }
147
148  std::string CreateRemoteFile(const std::string& parent_folder_id,
149                               const std::string& title,
150                               const std::string& content) {
151    std::string file_id;
152    EXPECT_EQ(google_apis::HTTP_SUCCESS,
153              fake_drive_helper_->AddFile(
154                  parent_folder_id, title, content, &file_id));
155    return file_id;
156  }
157
158  void CreateLocalFile(const fileapi::FileSystemURL& url) {
159    fake_remote_change_processor_->UpdateLocalFileMetadata(
160        url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
161                        SYNC_FILE_TYPE_FILE));
162  }
163
164  google_apis::GDataErrorCode AddFileToFolder(
165      const std::string& parent_folder_id,
166      const std::string& file_id) {
167    google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
168    context_->GetDriveService()->AddResourceToDirectory(
169        parent_folder_id, file_id,
170        CreateResultReceiver(&error));
171    base::RunLoop().RunUntilIdle();
172    return error;
173  }
174
175  int CountParents(const std::string& file_id) {
176    scoped_ptr<google_apis::ResourceEntry> entry;
177    EXPECT_EQ(google_apis::HTTP_SUCCESS,
178              fake_drive_helper_->GetResourceEntry(file_id, &entry));
179    int count = 0;
180    const ScopedVector<google_apis::Link>& links = entry->links();
181    for (ScopedVector<google_apis::Link>::const_iterator itr = links.begin();
182        itr != links.end(); ++itr) {
183      if ((*itr)->type() == google_apis::Link::LINK_PARENT)
184        ++count;
185    }
186    return count;
187  }
188
189  SyncStatusCode RunRemoteToLocalSyncer() {
190    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
191    scoped_ptr<RemoteToLocalSyncer> syncer(
192        new RemoteToLocalSyncer(context_.get()));
193    syncer->RunExclusive(CreateResultReceiver(&status));
194    base::RunLoop().RunUntilIdle();
195    return status;
196  }
197
198  SyncStatusCode RunLocalToRemoteSyncer(
199      const fileapi::FileSystemURL& url,
200      const FileChange& file_change) {
201    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
202    base::FilePath local_path = base::FilePath(FILE_PATH_LITERAL("dummy"));
203    if (file_change.IsAddOrUpdate())
204      CreateTemporaryFileInDir(database_dir_.path(), &local_path);
205    scoped_ptr<LocalToRemoteSyncer> syncer(new LocalToRemoteSyncer(
206        context_.get(),
207        SyncFileMetadata(file_change.file_type(), 0, base::Time()),
208        file_change, local_path, url));
209    syncer->RunExclusive(CreateResultReceiver(&status));
210    base::RunLoop().RunUntilIdle();
211    if (status == SYNC_STATUS_OK)
212      fake_remote_change_processor_->ClearLocalChanges(url);
213    return status;
214  }
215
216  void RunRemoteToLocalSyncerUntilIdle() {
217    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
218    while (status != SYNC_STATUS_NO_CHANGE_TO_SYNC)
219      status = RunRemoteToLocalSyncer();
220  }
221
222  SyncStatusCode RunConflictResolver() {
223    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
224    ConflictResolver resolver(context_.get());
225    resolver.RunExclusive(CreateResultReceiver(&status));
226    base::RunLoop().RunUntilIdle();
227    return status;
228  }
229
230  SyncStatusCode ListChanges() {
231    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
232    sync_task_manager_->ScheduleSyncTask(
233        FROM_HERE,
234        scoped_ptr<SyncTask>(new ListChangesTask(context_.get())),
235        SyncTaskManager::PRIORITY_MED,
236        CreateResultReceiver(&status));
237    base::RunLoop().RunUntilIdle();
238    return status;
239  }
240
241  ScopedVector<google_apis::ResourceEntry>
242  GetResourceEntriesForParentAndTitle(const std::string& parent_folder_id,
243                                      const std::string& title) {
244    ScopedVector<google_apis::ResourceEntry> entries;
245    EXPECT_EQ(google_apis::HTTP_SUCCESS,
246              fake_drive_helper_->SearchByTitle(
247                  parent_folder_id, title, &entries));
248    return entries.Pass();
249  }
250
251  void VerifyConflictResolution(const std::string& parent_folder_id,
252                                const std::string& title,
253                                const std::string& primary_file_id,
254                                google_apis::DriveEntryKind kind) {
255    ScopedVector<google_apis::ResourceEntry> entries;
256    EXPECT_EQ(google_apis::HTTP_SUCCESS,
257              fake_drive_helper_->SearchByTitle(
258                  parent_folder_id, title, &entries));
259    ASSERT_EQ(1u, entries.size());
260    EXPECT_EQ(primary_file_id, entries[0]->resource_id());
261    EXPECT_EQ(kind, entries[0]->kind());
262  }
263
264  void VerifyLocalChangeConsistency(
265      const URLToFileChangesMap& expected_changes) {
266    fake_remote_change_processor_->VerifyConsistency(expected_changes);
267  }
268
269 private:
270  content::TestBrowserThreadBundle thread_bundle_;
271  base::ScopedTempDir database_dir_;
272  scoped_ptr<leveldb::Env> in_memory_env_;
273
274  scoped_ptr<SyncEngineContext> context_;
275  scoped_ptr<FakeDriveServiceWrapper> fake_drive_service_;
276  scoped_ptr<drive::DriveUploaderInterface> drive_uploader_;
277  scoped_ptr<FakeDriveServiceHelper> fake_drive_helper_;
278  scoped_ptr<FakeRemoteChangeProcessor> fake_remote_change_processor_;
279
280  scoped_ptr<SyncTaskManager> sync_task_manager_;
281
282  DISALLOW_COPY_AND_ASSIGN(ConflictResolverTest);
283};
284
285TEST_F(ConflictResolverTest, NoFileToBeResolved) {
286  const GURL kOrigin("chrome-extension://example");
287  const std::string sync_root = CreateSyncRoot();
288  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
289  InitializeMetadataDatabase();
290  RegisterApp(kOrigin.host(), app_root);
291  RunRemoteToLocalSyncerUntilIdle();
292
293  EXPECT_EQ(SYNC_STATUS_NO_CONFLICT, RunConflictResolver());
294}
295
296TEST_F(ConflictResolverTest, ResolveConflict_Files) {
297  const GURL kOrigin("chrome-extension://example");
298  const std::string sync_root = CreateSyncRoot();
299  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
300  InitializeMetadataDatabase();
301  RegisterApp(kOrigin.host(), app_root);
302  RunRemoteToLocalSyncerUntilIdle();
303
304  const std::string kTitle = "foo";
305  const std::string primary = CreateRemoteFile(app_root, kTitle, "data1");
306  CreateRemoteFile(app_root, kTitle, "data2");
307  CreateRemoteFile(app_root, kTitle, "data3");
308  CreateRemoteFile(app_root, kTitle, "data4");
309  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
310  RunRemoteToLocalSyncerUntilIdle();
311
312  ScopedVector<google_apis::ResourceEntry> entries =
313      GetResourceEntriesForParentAndTitle(app_root, kTitle);
314  ASSERT_EQ(4u, entries.size());
315
316  // Only primary file should survive.
317  EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
318  VerifyConflictResolution(app_root, kTitle, primary,
319                           google_apis::ENTRY_KIND_FILE);
320}
321
322TEST_F(ConflictResolverTest, ResolveConflict_Folders) {
323  const GURL kOrigin("chrome-extension://example");
324  const std::string sync_root = CreateSyncRoot();
325  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
326  InitializeMetadataDatabase();
327  RegisterApp(kOrigin.host(), app_root);
328  RunRemoteToLocalSyncerUntilIdle();
329
330  const std::string kTitle = "foo";
331  const std::string primary = CreateRemoteFolder(app_root, kTitle);
332  CreateRemoteFolder(app_root, kTitle);
333  CreateRemoteFolder(app_root, kTitle);
334  CreateRemoteFolder(app_root, kTitle);
335  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
336  RunRemoteToLocalSyncerUntilIdle();
337
338  ScopedVector<google_apis::ResourceEntry> entries =
339      GetResourceEntriesForParentAndTitle(app_root, kTitle);
340  ASSERT_EQ(4u, entries.size());
341
342  // Only primary file should survive.
343  EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
344  VerifyConflictResolution(app_root, kTitle, primary,
345                           google_apis::ENTRY_KIND_FOLDER);
346}
347
348TEST_F(ConflictResolverTest, ResolveConflict_FilesAndFolders) {
349  const GURL kOrigin("chrome-extension://example");
350  const std::string sync_root = CreateSyncRoot();
351  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
352  InitializeMetadataDatabase();
353  RegisterApp(kOrigin.host(), app_root);
354  RunRemoteToLocalSyncerUntilIdle();
355
356  const std::string kTitle = "foo";
357  CreateRemoteFile(app_root, kTitle, "data");
358  const std::string primary = CreateRemoteFolder(app_root, kTitle);
359  CreateRemoteFile(app_root, kTitle, "data2");
360  CreateRemoteFolder(app_root, kTitle);
361  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
362  RunRemoteToLocalSyncerUntilIdle();
363
364  ScopedVector<google_apis::ResourceEntry> entries =
365      GetResourceEntriesForParentAndTitle(app_root, kTitle);
366  ASSERT_EQ(4u, entries.size());
367
368  // Only primary file should survive.
369  EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
370  VerifyConflictResolution(app_root, kTitle, primary,
371                           google_apis::ENTRY_KIND_FOLDER);
372}
373
374TEST_F(ConflictResolverTest, ResolveConflict_RemoteFolderOnLocalFile) {
375  const GURL kOrigin("chrome-extension://example");
376  const std::string sync_root = CreateSyncRoot();
377  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
378  InitializeMetadataDatabase();
379  RegisterApp(kOrigin.host(), app_root);
380  RunRemoteToLocalSyncerUntilIdle();
381
382  const std::string kTitle = "foo";
383  fileapi::FileSystemURL kURL = URL(kOrigin, kTitle);
384
385  // Create a file on local and sync it.
386  CreateLocalFile(kURL);
387  RunLocalToRemoteSyncer(
388      kURL,
389      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, SYNC_FILE_TYPE_FILE));
390
391  // Create a folder on remote and sync it.
392  const std::string primary = CreateRemoteFolder(app_root, kTitle);
393  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
394  RunRemoteToLocalSyncerUntilIdle();
395
396  ScopedVector<google_apis::ResourceEntry> entries =
397      GetResourceEntriesForParentAndTitle(app_root, kTitle);
398  ASSERT_EQ(2u, entries.size());
399
400  // Run conflict resolver. Only primary file should survive.
401  EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
402  VerifyConflictResolution(app_root, kTitle, primary,
403                           google_apis::ENTRY_KIND_FOLDER);
404
405  // Continue to run remote-to-local sync.
406  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
407  RunRemoteToLocalSyncerUntilIdle();
408
409  // Verify that the local side has been synced to the same state
410  // (i.e. file deletion and folder creation).
411  URLToFileChangesMap expected_changes;
412  expected_changes[kURL].push_back(
413      FileChange(FileChange::FILE_CHANGE_DELETE,
414                 SYNC_FILE_TYPE_UNKNOWN));
415  expected_changes[kURL].push_back(
416      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
417                 SYNC_FILE_TYPE_DIRECTORY));
418  VerifyLocalChangeConsistency(expected_changes);
419}
420
421TEST_F(ConflictResolverTest, ResolveConflict_RemoteNestedFolderOnLocalFile) {
422  const GURL kOrigin("chrome-extension://example");
423  const std::string sync_root = CreateSyncRoot();
424  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
425  InitializeMetadataDatabase();
426  RegisterApp(kOrigin.host(), app_root);
427  RunRemoteToLocalSyncerUntilIdle();
428
429  const std::string kTitle = "foo";
430  fileapi::FileSystemURL kURL = URL(kOrigin, kTitle);
431
432  // Create a file on local and sync it.
433  CreateLocalFile(kURL);
434  RunLocalToRemoteSyncer(
435      kURL,
436      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, SYNC_FILE_TYPE_FILE));
437
438  // Create a folder and subfolder in it on remote, and sync it.
439  const std::string primary = CreateRemoteFolder(app_root, kTitle);
440  CreateRemoteFolder(primary, "nested");
441  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
442  RunRemoteToLocalSyncerUntilIdle();
443
444  ScopedVector<google_apis::ResourceEntry> entries =
445      GetResourceEntriesForParentAndTitle(app_root, kTitle);
446  ASSERT_EQ(2u, entries.size());
447
448  // Run conflict resolver. Only primary file should survive.
449  EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
450  VerifyConflictResolution(app_root, kTitle, primary,
451                           google_apis::ENTRY_KIND_FOLDER);
452
453  // Continue to run remote-to-local sync.
454  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
455  RunRemoteToLocalSyncerUntilIdle();
456
457  // Verify that the local side has been synced to the same state
458  // (i.e. file deletion and folders creation).
459  URLToFileChangesMap expected_changes;
460  expected_changes[kURL].push_back(
461      FileChange(FileChange::FILE_CHANGE_DELETE,
462                 SYNC_FILE_TYPE_UNKNOWN));
463  expected_changes[kURL].push_back(
464      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
465                 SYNC_FILE_TYPE_DIRECTORY));
466  expected_changes[URL(kOrigin, "foo/nested")].push_back(
467      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
468                 SYNC_FILE_TYPE_DIRECTORY));
469  VerifyLocalChangeConsistency(expected_changes);
470}
471
472TEST_F(ConflictResolverTest, ResolveMultiParents_File) {
473  const GURL kOrigin("chrome-extension://example");
474  const std::string sync_root = CreateSyncRoot();
475  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
476  InitializeMetadataDatabase();
477  RegisterApp(kOrigin.host(), app_root);
478  RunRemoteToLocalSyncerUntilIdle();
479
480  const std::string primary = CreateRemoteFolder(app_root, "primary");
481  const std::string file = CreateRemoteFile(primary, "file", "data");
482  ASSERT_EQ(google_apis::HTTP_SUCCESS,
483            AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary1"), file));
484  ASSERT_EQ(google_apis::HTTP_SUCCESS,
485            AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary2"), file));
486  ASSERT_EQ(google_apis::HTTP_SUCCESS,
487            AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary3"), file));
488
489  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
490  RunRemoteToLocalSyncerUntilIdle();
491
492  EXPECT_EQ(4, CountParents(file));
493
494  EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
495
496  EXPECT_EQ(1, CountParents(file));
497}
498
499TEST_F(ConflictResolverTest, ResolveMultiParents_Folder) {
500  const GURL kOrigin("chrome-extension://example");
501  const std::string sync_root = CreateSyncRoot();
502  const std::string app_root = CreateRemoteFolder(sync_root, kOrigin.host());
503  InitializeMetadataDatabase();
504  RegisterApp(kOrigin.host(), app_root);
505  RunRemoteToLocalSyncerUntilIdle();
506
507  const std::string primary = CreateRemoteFolder(app_root, "primary");
508  const std::string file = CreateRemoteFolder(primary, "folder");
509  ASSERT_EQ(google_apis::HTTP_SUCCESS,
510            AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary1"), file));
511  ASSERT_EQ(google_apis::HTTP_SUCCESS,
512            AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary2"), file));
513  ASSERT_EQ(google_apis::HTTP_SUCCESS,
514            AddFileToFolder(CreateRemoteFolder(app_root, "nonprimary3"), file));
515
516  EXPECT_EQ(SYNC_STATUS_OK, ListChanges());
517  RunRemoteToLocalSyncerUntilIdle();
518
519  EXPECT_EQ(4, CountParents(file));
520
521  EXPECT_EQ(SYNC_STATUS_OK, RunConflictResolver());
522
523  EXPECT_EQ(1, CountParents(file));
524}
525
526}  // namespace drive_backend
527}  // namespace sync_file_system
528