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.
5 #include "chrome/browser/sync_file_system/drive_backend/conflict_resolver.h"
8 #include "base/callback.h"
9 #include "base/files/file_util.h"
10 #include "base/files/scoped_temp_dir.h"
11 #include "base/run_loop.h"
12 #include "base/thread_task_runner_handle.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/local_to_remote_syncer.h"
21 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.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"
37 namespace sync_file_system
{
38 namespace drive_backend
{
42 storage::FileSystemURL
URL(const GURL
& origin
, const std::string
& path
) {
43 return CreateSyncableFileSystemURL(
44 origin
, base::FilePath::FromUTF8Unsafe(path
));
49 class ConflictResolverTest
: public testing::Test
{
51 typedef FakeRemoteChangeProcessor::URLToFileChangesMap URLToFileChangesMap
;
53 ConflictResolverTest()
54 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP
) {}
55 ~ConflictResolverTest() override
{}
57 void SetUp() override
{
58 ASSERT_TRUE(database_dir_
.CreateUniqueTempDir());
59 in_memory_env_
.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
61 scoped_ptr
<FakeDriveServiceWrapper
>
62 fake_drive_service(new FakeDriveServiceWrapper
);
63 scoped_ptr
<drive::DriveUploaderInterface
>
64 drive_uploader(new FakeDriveUploader(fake_drive_service
.get()));
65 fake_drive_helper_
.reset(
66 new FakeDriveServiceHelper(fake_drive_service
.get(),
68 kSyncRootFolderTitle
));
69 remote_change_processor_
.reset(new FakeRemoteChangeProcessor
);
71 context_
.reset(new SyncEngineContext(fake_drive_service
.Pass(),
72 drive_uploader
.Pass(),
74 base::ThreadTaskRunnerHandle::Get(),
75 base::ThreadTaskRunnerHandle::Get()));
76 context_
->SetRemoteChangeProcessor(remote_change_processor_
.get());
78 RegisterSyncableFileSystem();
80 sync_task_manager_
.reset(new SyncTaskManager(
81 base::WeakPtr
<SyncTaskManager::Client
>(),
82 10 /* maximum_background_task */,
83 base::ThreadTaskRunnerHandle::Get()));
84 sync_task_manager_
->Initialize(SYNC_STATUS_OK
);
87 void TearDown() override
{
88 sync_task_manager_
.reset();
89 RevokeSyncableFileSystem();
90 fake_drive_helper_
.reset();
92 base::RunLoop().RunUntilIdle();
95 void InitializeMetadataDatabase() {
96 SyncEngineInitializer
* initializer
=
97 new SyncEngineInitializer(context_
.get(),
99 in_memory_env_
.get());
100 SyncStatusCode status
= SYNC_STATUS_UNKNOWN
;
101 sync_task_manager_
->ScheduleSyncTask(
103 scoped_ptr
<SyncTask
>(initializer
),
104 SyncTaskManager::PRIORITY_MED
,
105 base::Bind(&ConflictResolverTest::DidInitializeMetadataDatabase
,
106 base::Unretained(this), initializer
, &status
));
108 base::RunLoop().RunUntilIdle();
109 EXPECT_EQ(SYNC_STATUS_OK
, status
);
112 void DidInitializeMetadataDatabase(SyncEngineInitializer
* initializer
,
113 SyncStatusCode
* status_out
,
114 SyncStatusCode status
) {
115 context_
->SetMetadataDatabase(initializer
->PassMetadataDatabase());
116 *status_out
= status
;
119 void RegisterApp(const std::string
& app_id
,
120 const std::string
& app_root_folder_id
) {
121 SyncStatusCode status
= context_
->GetMetadataDatabase()->RegisterApp(
122 app_id
, app_root_folder_id
);
123 EXPECT_EQ(SYNC_STATUS_OK
, status
);
127 std::string
CreateSyncRoot() {
128 std::string sync_root_folder_id
;
129 EXPECT_EQ(google_apis::HTTP_CREATED
,
130 fake_drive_helper_
->AddOrphanedFolder(
131 kSyncRootFolderTitle
, &sync_root_folder_id
));
132 return sync_root_folder_id
;
135 std::string
CreateRemoteFolder(const std::string
& parent_folder_id
,
136 const std::string
& title
) {
137 std::string folder_id
;
138 EXPECT_EQ(google_apis::HTTP_CREATED
,
139 fake_drive_helper_
->AddFolder(
140 parent_folder_id
, title
, &folder_id
));
144 std::string
CreateRemoteFile(const std::string
& parent_folder_id
,
145 const std::string
& title
,
146 const std::string
& content
) {
148 EXPECT_EQ(google_apis::HTTP_SUCCESS
,
149 fake_drive_helper_
->AddFile(
150 parent_folder_id
, title
, content
, &file_id
));
154 void CreateLocalFile(const storage::FileSystemURL
& url
) {
155 remote_change_processor_
->UpdateLocalFileMetadata(
156 url
, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE
,
157 SYNC_FILE_TYPE_FILE
));
160 google_apis::GDataErrorCode
AddFileToFolder(
161 const std::string
& parent_folder_id
,
162 const std::string
& file_id
) {
163 google_apis::GDataErrorCode error
= google_apis::GDATA_OTHER_ERROR
;
164 context_
->GetDriveService()->AddResourceToDirectory(
165 parent_folder_id
, file_id
,
166 CreateResultReceiver(&error
));
167 base::RunLoop().RunUntilIdle();
171 int CountParents(const std::string
& file_id
) {
172 scoped_ptr
<google_apis::FileResource
> entry
;
173 EXPECT_EQ(google_apis::HTTP_SUCCESS
,
174 fake_drive_helper_
->GetFileResource(file_id
, &entry
));
175 return entry
->parents().size();
178 SyncStatusCode
RunRemoteToLocalSyncer() {
179 SyncStatusCode status
= SYNC_STATUS_UNKNOWN
;
180 scoped_ptr
<RemoteToLocalSyncer
> syncer(
181 new RemoteToLocalSyncer(context_
.get()));
182 syncer
->RunPreflight(SyncTaskToken::CreateForTesting(
183 CreateResultReceiver(&status
)));
184 base::RunLoop().RunUntilIdle();
188 SyncStatusCode
RunLocalToRemoteSyncer(const storage::FileSystemURL
& url
,
189 const FileChange
& file_change
) {
190 SyncStatusCode status
= SYNC_STATUS_UNKNOWN
;
191 base::FilePath local_path
= base::FilePath(FILE_PATH_LITERAL("dummy"));
192 if (file_change
.IsAddOrUpdate())
193 CreateTemporaryFileInDir(database_dir_
.path(), &local_path
);
194 scoped_ptr
<LocalToRemoteSyncer
> syncer(new LocalToRemoteSyncer(
196 SyncFileMetadata(file_change
.file_type(), 0, base::Time()),
197 file_change
, local_path
, url
));
198 syncer
->RunPreflight(SyncTaskToken::CreateForTesting(
199 CreateResultReceiver(&status
)));
200 base::RunLoop().RunUntilIdle();
201 if (status
== SYNC_STATUS_OK
)
202 remote_change_processor_
->ClearLocalChanges(url
);
206 void RunRemoteToLocalSyncerUntilIdle() {
207 const int kRetryLimit
= 100;
208 SyncStatusCode status
;
210 MetadataDatabase
* metadata_database
= context_
->GetMetadataDatabase();
212 if (retry_count
++ > kRetryLimit
)
214 status
= RunRemoteToLocalSyncer();
215 } while (status
== SYNC_STATUS_OK
||
216 status
== SYNC_STATUS_RETRY
||
217 metadata_database
->PromoteDemotedTrackers());
218 EXPECT_EQ(SYNC_STATUS_NO_CHANGE_TO_SYNC
, status
);
221 SyncStatusCode
RunConflictResolver() {
222 SyncStatusCode status
= SYNC_STATUS_UNKNOWN
;
223 ConflictResolver
resolver(context_
.get());
224 resolver
.RunPreflight(SyncTaskToken::CreateForTesting(
225 CreateResultReceiver(&status
)));
226 base::RunLoop().RunUntilIdle();
230 SyncStatusCode
ListChanges() {
231 SyncStatusCode status
= SYNC_STATUS_UNKNOWN
;
232 sync_task_manager_
->ScheduleSyncTask(
234 scoped_ptr
<SyncTask
>(new ListChangesTask(context_
.get())),
235 SyncTaskManager::PRIORITY_MED
,
236 CreateResultReceiver(&status
));
237 base::RunLoop().RunUntilIdle();
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();
251 void VerifyConflictResolution(
252 const std::string
& parent_folder_id
,
253 const std::string
& title
,
254 const std::string
& primary_file_id
,
255 google_apis::ResourceEntry::ResourceEntryKind kind
) {
256 ScopedVector
<google_apis::ResourceEntry
> entries
;
257 EXPECT_EQ(google_apis::HTTP_SUCCESS
,
258 fake_drive_helper_
->SearchByTitle(
259 parent_folder_id
, title
, &entries
));
260 ASSERT_EQ(1u, entries
.size());
261 EXPECT_EQ(primary_file_id
, entries
[0]->resource_id());
262 EXPECT_EQ(kind
, entries
[0]->kind());
265 void VerifyLocalChangeConsistency(
266 const URLToFileChangesMap
& expected_changes
) {
267 remote_change_processor_
->VerifyConsistency(expected_changes
);
271 content::TestBrowserThreadBundle thread_bundle_
;
272 base::ScopedTempDir database_dir_
;
273 scoped_ptr
<leveldb::Env
> in_memory_env_
;
275 scoped_ptr
<SyncEngineContext
> context_
;
276 scoped_ptr
<FakeDriveServiceHelper
> fake_drive_helper_
;
277 scoped_ptr
<FakeRemoteChangeProcessor
> remote_change_processor_
;
279 scoped_ptr
<SyncTaskManager
> sync_task_manager_
;
281 DISALLOW_COPY_AND_ASSIGN(ConflictResolverTest
);
284 TEST_F(ConflictResolverTest
, NoFileToBeResolved
) {
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 RunRemoteToLocalSyncerUntilIdle();
292 EXPECT_EQ(SYNC_STATUS_NO_CONFLICT
, RunConflictResolver());
295 TEST_F(ConflictResolverTest
, ResolveConflict_Files
) {
296 const GURL
kOrigin("chrome-extension://example");
297 const std::string sync_root
= CreateSyncRoot();
298 const std::string app_root
= CreateRemoteFolder(sync_root
, kOrigin
.host());
299 InitializeMetadataDatabase();
300 RegisterApp(kOrigin
.host(), app_root
);
301 RunRemoteToLocalSyncerUntilIdle();
303 const std::string kTitle
= "foo";
304 const std::string primary
= CreateRemoteFile(app_root
, kTitle
, "data1");
305 CreateRemoteFile(app_root
, kTitle
, "data2");
306 CreateRemoteFile(app_root
, kTitle
, "data3");
307 CreateRemoteFile(app_root
, kTitle
, "data4");
308 EXPECT_EQ(SYNC_STATUS_OK
, ListChanges());
309 RunRemoteToLocalSyncerUntilIdle();
311 ScopedVector
<google_apis::ResourceEntry
> entries
=
312 GetResourceEntriesForParentAndTitle(app_root
, kTitle
);
313 ASSERT_EQ(4u, entries
.size());
315 // Only primary file should survive.
316 EXPECT_EQ(SYNC_STATUS_OK
, RunConflictResolver());
317 VerifyConflictResolution(app_root
, kTitle
, primary
,
318 google_apis::ResourceEntry::ENTRY_KIND_FILE
);
321 TEST_F(ConflictResolverTest
, ResolveConflict_Folders
) {
322 const GURL
kOrigin("chrome-extension://example");
323 const std::string sync_root
= CreateSyncRoot();
324 const std::string app_root
= CreateRemoteFolder(sync_root
, kOrigin
.host());
325 InitializeMetadataDatabase();
326 RegisterApp(kOrigin
.host(), app_root
);
327 RunRemoteToLocalSyncerUntilIdle();
329 const std::string kTitle
= "foo";
330 const std::string primary
= CreateRemoteFolder(app_root
, kTitle
);
331 CreateRemoteFolder(app_root
, kTitle
);
332 CreateRemoteFolder(app_root
, kTitle
);
333 CreateRemoteFolder(app_root
, kTitle
);
334 EXPECT_EQ(SYNC_STATUS_OK
, ListChanges());
335 RunRemoteToLocalSyncerUntilIdle();
337 ScopedVector
<google_apis::ResourceEntry
> entries
=
338 GetResourceEntriesForParentAndTitle(app_root
, kTitle
);
339 ASSERT_EQ(4u, entries
.size());
341 // Only primary file should survive.
342 EXPECT_EQ(SYNC_STATUS_OK
, RunConflictResolver());
343 VerifyConflictResolution(app_root
, kTitle
, primary
,
344 google_apis::ResourceEntry::ENTRY_KIND_FOLDER
);
347 TEST_F(ConflictResolverTest
, ResolveConflict_FilesAndFolders
) {
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 RunRemoteToLocalSyncerUntilIdle();
355 const std::string kTitle
= "foo";
356 CreateRemoteFile(app_root
, kTitle
, "data");
357 const std::string primary
= CreateRemoteFolder(app_root
, kTitle
);
358 CreateRemoteFile(app_root
, kTitle
, "data2");
359 CreateRemoteFolder(app_root
, kTitle
);
360 EXPECT_EQ(SYNC_STATUS_OK
, ListChanges());
361 RunRemoteToLocalSyncerUntilIdle();
363 ScopedVector
<google_apis::ResourceEntry
> entries
=
364 GetResourceEntriesForParentAndTitle(app_root
, kTitle
);
365 ASSERT_EQ(4u, entries
.size());
367 // Only primary file should survive.
368 EXPECT_EQ(SYNC_STATUS_OK
, RunConflictResolver());
369 VerifyConflictResolution(app_root
, kTitle
, primary
,
370 google_apis::ResourceEntry::ENTRY_KIND_FOLDER
);
373 TEST_F(ConflictResolverTest
, ResolveConflict_RemoteFolderOnLocalFile
) {
374 const GURL
kOrigin("chrome-extension://example");
375 const std::string sync_root
= CreateSyncRoot();
376 const std::string app_root
= CreateRemoteFolder(sync_root
, kOrigin
.host());
377 InitializeMetadataDatabase();
378 RegisterApp(kOrigin
.host(), app_root
);
379 RunRemoteToLocalSyncerUntilIdle();
381 const std::string kTitle
= "foo";
382 storage::FileSystemURL kURL
= URL(kOrigin
, kTitle
);
384 // Create a file on local and sync it.
385 CreateLocalFile(kURL
);
386 RunLocalToRemoteSyncer(
388 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE
, SYNC_FILE_TYPE_FILE
));
390 // Create a folder on remote and sync it.
391 const std::string primary
= CreateRemoteFolder(app_root
, kTitle
);
392 EXPECT_EQ(SYNC_STATUS_OK
, ListChanges());
393 RunRemoteToLocalSyncerUntilIdle();
395 ScopedVector
<google_apis::ResourceEntry
> entries
=
396 GetResourceEntriesForParentAndTitle(app_root
, kTitle
);
397 ASSERT_EQ(2u, entries
.size());
399 // Run conflict resolver. Only primary file should survive.
400 EXPECT_EQ(SYNC_STATUS_OK
, RunConflictResolver());
401 VerifyConflictResolution(app_root
, kTitle
, primary
,
402 google_apis::ResourceEntry::ENTRY_KIND_FOLDER
);
404 // Continue to run remote-to-local sync.
405 EXPECT_EQ(SYNC_STATUS_OK
, ListChanges());
406 RunRemoteToLocalSyncerUntilIdle();
408 // Verify that the local side has been synced to the same state
409 // (i.e. file deletion and folder creation).
410 URLToFileChangesMap expected_changes
;
411 expected_changes
[kURL
].push_back(
412 FileChange(FileChange::FILE_CHANGE_DELETE
,
413 SYNC_FILE_TYPE_UNKNOWN
));
414 expected_changes
[kURL
].push_back(
415 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE
,
416 SYNC_FILE_TYPE_DIRECTORY
));
417 VerifyLocalChangeConsistency(expected_changes
);
420 TEST_F(ConflictResolverTest
, ResolveConflict_RemoteNestedFolderOnLocalFile
) {
421 const GURL
kOrigin("chrome-extension://example");
422 const std::string sync_root
= CreateSyncRoot();
423 const std::string app_root
= CreateRemoteFolder(sync_root
, kOrigin
.host());
424 InitializeMetadataDatabase();
425 RegisterApp(kOrigin
.host(), app_root
);
426 RunRemoteToLocalSyncerUntilIdle();
428 const std::string kTitle
= "foo";
429 storage::FileSystemURL kURL
= URL(kOrigin
, kTitle
);
431 // Create a file on local and sync it.
432 CreateLocalFile(kURL
);
433 RunLocalToRemoteSyncer(
435 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE
, SYNC_FILE_TYPE_FILE
));
437 // Create a folder and subfolder in it on remote, and sync it.
438 const std::string primary
= CreateRemoteFolder(app_root
, kTitle
);
439 CreateRemoteFolder(primary
, "nested");
440 EXPECT_EQ(SYNC_STATUS_OK
, ListChanges());
441 RunRemoteToLocalSyncerUntilIdle();
443 ScopedVector
<google_apis::ResourceEntry
> entries
=
444 GetResourceEntriesForParentAndTitle(app_root
, kTitle
);
445 ASSERT_EQ(2u, entries
.size());
447 // Run conflict resolver. Only primary file should survive.
448 EXPECT_EQ(SYNC_STATUS_OK
, RunConflictResolver());
449 VerifyConflictResolution(app_root
, kTitle
, primary
,
450 google_apis::ResourceEntry::ENTRY_KIND_FOLDER
);
452 // Continue to run remote-to-local sync.
453 EXPECT_EQ(SYNC_STATUS_OK
, ListChanges());
454 RunRemoteToLocalSyncerUntilIdle();
456 // Verify that the local side has been synced to the same state
457 // (i.e. file deletion and folders creation).
458 URLToFileChangesMap expected_changes
;
459 expected_changes
[kURL
].push_back(
460 FileChange(FileChange::FILE_CHANGE_DELETE
,
461 SYNC_FILE_TYPE_UNKNOWN
));
462 expected_changes
[kURL
].push_back(
463 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE
,
464 SYNC_FILE_TYPE_DIRECTORY
));
465 expected_changes
[URL(kOrigin
, "foo/nested")].push_back(
466 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE
,
467 SYNC_FILE_TYPE_DIRECTORY
));
468 VerifyLocalChangeConsistency(expected_changes
);
471 TEST_F(ConflictResolverTest
, ResolveMultiParents_File
) {
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 RunRemoteToLocalSyncerUntilIdle();
479 const std::string primary
= CreateRemoteFolder(app_root
, "primary");
480 const std::string file
= CreateRemoteFile(primary
, "file", "data");
481 ASSERT_EQ(google_apis::HTTP_SUCCESS
,
482 AddFileToFolder(CreateRemoteFolder(app_root
, "nonprimary1"), file
));
483 ASSERT_EQ(google_apis::HTTP_SUCCESS
,
484 AddFileToFolder(CreateRemoteFolder(app_root
, "nonprimary2"), file
));
485 ASSERT_EQ(google_apis::HTTP_SUCCESS
,
486 AddFileToFolder(CreateRemoteFolder(app_root
, "nonprimary3"), file
));
488 EXPECT_EQ(SYNC_STATUS_OK
, ListChanges());
489 RunRemoteToLocalSyncerUntilIdle();
491 EXPECT_EQ(4, CountParents(file
));
493 EXPECT_EQ(SYNC_STATUS_OK
, RunConflictResolver());
495 EXPECT_EQ(1, CountParents(file
));
498 TEST_F(ConflictResolverTest
, ResolveMultiParents_Folder
) {
499 const GURL
kOrigin("chrome-extension://example");
500 const std::string sync_root
= CreateSyncRoot();
501 const std::string app_root
= CreateRemoteFolder(sync_root
, kOrigin
.host());
502 InitializeMetadataDatabase();
503 RegisterApp(kOrigin
.host(), app_root
);
504 RunRemoteToLocalSyncerUntilIdle();
506 const std::string primary
= CreateRemoteFolder(app_root
, "primary");
507 const std::string file
= CreateRemoteFolder(primary
, "folder");
508 ASSERT_EQ(google_apis::HTTP_SUCCESS
,
509 AddFileToFolder(CreateRemoteFolder(app_root
, "nonprimary1"), file
));
510 ASSERT_EQ(google_apis::HTTP_SUCCESS
,
511 AddFileToFolder(CreateRemoteFolder(app_root
, "nonprimary2"), file
));
512 ASSERT_EQ(google_apis::HTTP_SUCCESS
,
513 AddFileToFolder(CreateRemoteFolder(app_root
, "nonprimary3"), file
));
515 EXPECT_EQ(SYNC_STATUS_OK
, ListChanges());
516 RunRemoteToLocalSyncerUntilIdle();
518 EXPECT_EQ(4, CountParents(file
));
520 EXPECT_EQ(SYNC_STATUS_OK
, RunConflictResolver());
522 EXPECT_EQ(1, CountParents(file
));
525 } // namespace drive_backend
526 } // namespace sync_file_system