1 // Copyright 2014 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 "sync/engine/directory_update_handler.h"
7 #include "base/compiler_specific.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/stl_util.h"
10 #include "sync/engine/syncer_proto_util.h"
11 #include "sync/internal_api/public/base/attachment_id_proto.h"
12 #include "sync/internal_api/public/base/model_type.h"
13 #include "sync/internal_api/public/test/test_entry_factory.h"
14 #include "sync/protocol/sync.pb.h"
15 #include "sync/sessions/directory_type_debug_info_emitter.h"
16 #include "sync/sessions/status_controller.h"
17 #include "sync/syncable/directory.h"
18 #include "sync/syncable/entry.h"
19 #include "sync/syncable/mutable_entry.h"
20 #include "sync/syncable/syncable_model_neutral_write_transaction.h"
21 #include "sync/syncable/syncable_proto_util.h"
22 #include "sync/syncable/syncable_read_transaction.h"
23 #include "sync/syncable/syncable_write_transaction.h"
24 #include "sync/test/engine/fake_model_worker.h"
25 #include "sync/test/engine/test_directory_setter_upper.h"
26 #include "sync/test/engine/test_id_factory.h"
27 #include "sync/test/engine/test_syncable_utils.h"
28 #include "testing/gtest/include/gtest/gtest.h"
32 using syncable::UNITTEST
;
34 static const int64 kDefaultVersion
= 1000;
36 // A test harness for tests that focus on processing updates.
38 // Update processing is what occurs when we first download updates. It converts
39 // the received protobuf message into information in the syncable::Directory.
40 // Any invalid or redundant updates will be dropped at this point.
41 class DirectoryUpdateHandlerProcessUpdateTest
: public ::testing::Test
{
43 DirectoryUpdateHandlerProcessUpdateTest()
44 : ui_worker_(new FakeModelWorker(GROUP_UI
)) {
47 virtual ~DirectoryUpdateHandlerProcessUpdateTest() {}
49 virtual void SetUp() OVERRIDE
{
53 virtual void TearDown() OVERRIDE
{
54 dir_maker_
.TearDown();
57 syncable::Directory
* dir() {
58 return dir_maker_
.directory();
62 scoped_ptr
<sync_pb::SyncEntity
> CreateUpdate(
63 const std::string
& id
,
64 const std::string
& parent
,
65 const ModelType
& type
);
67 // This exists mostly to give tests access to the protected member function.
68 // Warning: This takes the syncable directory lock.
69 void UpdateSyncEntities(
70 DirectoryUpdateHandler
* handler
,
71 const SyncEntityList
& applicable_updates
,
72 sessions::StatusController
* status
);
74 // Another function to access private member functions.
75 void UpdateProgressMarkers(
76 DirectoryUpdateHandler
* handler
,
77 const sync_pb::DataTypeProgressMarker
& progress
);
79 scoped_refptr
<FakeModelWorker
> ui_worker() {
83 bool EntryExists(const std::string
& id
) {
84 syncable::ReadTransaction
trans(FROM_HERE
, dir());
85 syncable::Entry
e(&trans
, syncable::GET_BY_ID
,
86 syncable::Id::CreateFromServerId(id
));
87 return e
.good() && !e
.GetIsDel();
91 // Used in the construction of DirectoryTypeDebugInfoEmitters.
92 ObserverList
<TypeDebugInfoObserver
> type_observers_
;
95 base::MessageLoop loop_
; // Needed to initialize the directory.
96 TestDirectorySetterUpper dir_maker_
;
97 scoped_refptr
<FakeModelWorker
> ui_worker_
;
100 scoped_ptr
<sync_pb::SyncEntity
>
101 DirectoryUpdateHandlerProcessUpdateTest::CreateUpdate(
102 const std::string
& id
,
103 const std::string
& parent
,
104 const ModelType
& type
) {
105 scoped_ptr
<sync_pb::SyncEntity
> e(new sync_pb::SyncEntity());
106 e
->set_id_string(id
);
107 e
->set_parent_id_string(parent
);
108 e
->set_non_unique_name(id
);
110 e
->set_version(kDefaultVersion
);
111 AddDefaultFieldValue(type
, e
->mutable_specifics());
115 void DirectoryUpdateHandlerProcessUpdateTest::UpdateSyncEntities(
116 DirectoryUpdateHandler
* handler
,
117 const SyncEntityList
& applicable_updates
,
118 sessions::StatusController
* status
) {
119 syncable::ModelNeutralWriteTransaction
trans(FROM_HERE
, UNITTEST
, dir());
120 handler
->UpdateSyncEntities(&trans
, applicable_updates
, status
);
123 void DirectoryUpdateHandlerProcessUpdateTest::UpdateProgressMarkers(
124 DirectoryUpdateHandler
* handler
,
125 const sync_pb::DataTypeProgressMarker
& progress
) {
126 handler
->UpdateProgressMarker(progress
);
129 static const char kCacheGuid
[] = "IrcjZ2jyzHDV9Io4+zKcXQ==";
131 // Test that the bookmark tag is set on newly downloaded items.
132 TEST_F(DirectoryUpdateHandlerProcessUpdateTest
, NewBookmarkTag
) {
133 DirectoryTypeDebugInfoEmitter
emitter(BOOKMARKS
, &type_observers_
);
134 DirectoryUpdateHandler
handler(dir(), BOOKMARKS
, ui_worker(), &emitter
);
135 sync_pb::GetUpdatesResponse gu_response
;
136 sessions::StatusController status
;
138 // Add a bookmark item to the update message.
139 std::string root
= syncable::GetNullId().GetServerId();
140 syncable::Id server_id
= syncable::Id::CreateFromServerId("b1");
141 scoped_ptr
<sync_pb::SyncEntity
> e
=
142 CreateUpdate(SyncableIdToProto(server_id
), root
, BOOKMARKS
);
143 e
->set_originator_cache_guid(
144 std::string(kCacheGuid
, arraysize(kCacheGuid
)-1));
145 syncable::Id client_id
= syncable::Id::CreateFromClientString("-2");
146 e
->set_originator_client_item_id(client_id
.GetServerId());
147 e
->set_position_in_parent(0);
149 // Add it to the applicable updates list.
150 SyncEntityList bookmark_updates
;
151 bookmark_updates
.push_back(e
.get());
153 // Process the update.
154 UpdateSyncEntities(&handler
, bookmark_updates
, &status
);
156 syncable::ReadTransaction
trans(FROM_HERE
, dir());
157 syncable::Entry
entry(&trans
, syncable::GET_BY_ID
, server_id
);
158 ASSERT_TRUE(entry
.good());
159 EXPECT_TRUE(UniquePosition::IsValidSuffix(entry
.GetUniqueBookmarkTag()));
160 EXPECT_TRUE(entry
.GetServerUniquePosition().IsValid());
162 // If this assertion fails, that might indicate that the algorithm used to
163 // generate bookmark tags has been modified. This could have implications for
164 // bookmark ordering. Please make sure you know what you're doing if you
165 // intend to make such a change.
166 EXPECT_EQ("6wHRAb3kbnXV5GHrejp4/c1y5tw=", entry
.GetUniqueBookmarkTag());
169 // Test the receipt of a type root node.
170 TEST_F(DirectoryUpdateHandlerProcessUpdateTest
,
171 ReceiveServerCreatedBookmarkFolders
) {
172 DirectoryTypeDebugInfoEmitter
emitter(BOOKMARKS
, &type_observers_
);
173 DirectoryUpdateHandler
handler(dir(), BOOKMARKS
, ui_worker(), &emitter
);
174 sync_pb::GetUpdatesResponse gu_response
;
175 sessions::StatusController status
;
177 // Create an update that mimics the bookmark root.
178 syncable::Id server_id
= syncable::Id::CreateFromServerId("xyz");
179 std::string root
= syncable::GetNullId().GetServerId();
180 scoped_ptr
<sync_pb::SyncEntity
> e
=
181 CreateUpdate(SyncableIdToProto(server_id
), root
, BOOKMARKS
);
182 e
->set_server_defined_unique_tag("google_chrome_bookmarks");
185 // Add it to the applicable updates list.
186 SyncEntityList bookmark_updates
;
187 bookmark_updates
.push_back(e
.get());
189 EXPECT_FALSE(SyncerProtoUtil::ShouldMaintainPosition(*e
));
192 UpdateSyncEntities(&handler
, bookmark_updates
, &status
);
194 // Verify the results.
195 syncable::ReadTransaction
trans(FROM_HERE
, dir());
196 syncable::Entry
entry(&trans
, syncable::GET_BY_ID
, server_id
);
197 ASSERT_TRUE(entry
.good());
199 EXPECT_FALSE(entry
.ShouldMaintainPosition());
200 EXPECT_FALSE(entry
.GetUniquePosition().IsValid());
201 EXPECT_FALSE(entry
.GetServerUniquePosition().IsValid());
202 EXPECT_TRUE(entry
.GetUniqueBookmarkTag().empty());
205 // Test the receipt of a non-bookmark item.
206 TEST_F(DirectoryUpdateHandlerProcessUpdateTest
, ReceiveNonBookmarkItem
) {
207 DirectoryTypeDebugInfoEmitter
emitter(AUTOFILL
, &type_observers_
);
208 DirectoryUpdateHandler
handler(dir(), AUTOFILL
, ui_worker(), &emitter
);
209 sync_pb::GetUpdatesResponse gu_response
;
210 sessions::StatusController status
;
212 std::string root
= syncable::GetNullId().GetServerId();
213 syncable::Id server_id
= syncable::Id::CreateFromServerId("xyz");
214 scoped_ptr
<sync_pb::SyncEntity
> e
=
215 CreateUpdate(SyncableIdToProto(server_id
), root
, AUTOFILL
);
216 e
->set_server_defined_unique_tag("9PGRuKdX5sHyGMB17CvYTXuC43I=");
218 // Add it to the applicable updates list.
219 SyncEntityList autofill_updates
;
220 autofill_updates
.push_back(e
.get());
222 EXPECT_FALSE(SyncerProtoUtil::ShouldMaintainPosition(*e
));
225 UpdateSyncEntities(&handler
, autofill_updates
, &status
);
227 syncable::ReadTransaction
trans(FROM_HERE
, dir());
228 syncable::Entry
entry(&trans
, syncable::GET_BY_ID
, server_id
);
229 ASSERT_TRUE(entry
.good());
231 EXPECT_FALSE(entry
.ShouldMaintainPosition());
232 EXPECT_FALSE(entry
.GetUniquePosition().IsValid());
233 EXPECT_FALSE(entry
.GetServerUniquePosition().IsValid());
234 EXPECT_TRUE(entry
.GetUniqueBookmarkTag().empty());
237 // Tests the setting of progress markers.
238 TEST_F(DirectoryUpdateHandlerProcessUpdateTest
, ProcessNewProgressMarkers
) {
239 DirectoryTypeDebugInfoEmitter
emitter(BOOKMARKS
, &type_observers_
);
240 DirectoryUpdateHandler
handler(dir(), BOOKMARKS
, ui_worker(), &emitter
);
242 sync_pb::DataTypeProgressMarker progress
;
243 progress
.set_data_type_id(GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
244 progress
.set_token("token");
246 UpdateProgressMarkers(&handler
, progress
);
248 sync_pb::DataTypeProgressMarker saved
;
249 dir()->GetDownloadProgress(BOOKMARKS
, &saved
);
251 EXPECT_EQ(progress
.token(), saved
.token());
252 EXPECT_EQ(progress
.data_type_id(), saved
.data_type_id());
255 TEST_F(DirectoryUpdateHandlerProcessUpdateTest
, GarbageCollectionByVersion
) {
256 DirectoryTypeDebugInfoEmitter
emitter(SYNCED_NOTIFICATIONS
, &type_observers_
);
257 DirectoryUpdateHandler
handler(dir(), SYNCED_NOTIFICATIONS
,
258 ui_worker(), &emitter
);
259 sessions::StatusController status
;
261 sync_pb::DataTypeProgressMarker progress
;
262 progress
.set_data_type_id(
263 GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS
));
264 progress
.set_token("token");
265 progress
.mutable_gc_directive()->set_version_watermark(kDefaultVersion
+ 10);
267 sync_pb::DataTypeContext context
;
268 context
.set_data_type_id(
269 GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS
));
270 context
.set_context("context");
271 context
.set_version(1);
273 scoped_ptr
<sync_pb::SyncEntity
> type_root
=
274 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("root")),
275 syncable::GetNullId().GetServerId(),
276 SYNCED_NOTIFICATIONS
);
277 type_root
->set_server_defined_unique_tag(
278 ModelTypeToRootTag(SYNCED_NOTIFICATIONS
));
279 type_root
->set_folder(true);
281 scoped_ptr
<sync_pb::SyncEntity
> e1
=
282 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("e1")),
283 type_root
->id_string(),
284 SYNCED_NOTIFICATIONS
);
286 scoped_ptr
<sync_pb::SyncEntity
> e2
=
287 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("e2")),
288 type_root
->id_string(),
289 SYNCED_NOTIFICATIONS
);
290 e2
->set_version(kDefaultVersion
+ 100);
292 // Add to the applicable updates list.
293 SyncEntityList updates
;
294 updates
.push_back(type_root
.get());
295 updates
.push_back(e1
.get());
296 updates
.push_back(e2
.get());
298 // Process and apply updates.
301 handler
.ProcessGetUpdatesResponse(progress
, context
, updates
, &status
));
302 handler
.ApplyUpdates(&status
);
304 // Verify none is deleted because they are unapplied during GC.
305 EXPECT_TRUE(EntryExists(type_root
->id_string()));
306 EXPECT_TRUE(EntryExists(e1
->id_string()));
307 EXPECT_TRUE(EntryExists(e2
->id_string()));
309 // Process and apply again. Old entry is deleted but not root.
310 progress
.mutable_gc_directive()->set_version_watermark(kDefaultVersion
+ 20);
312 handler
.ProcessGetUpdatesResponse(
313 progress
, context
, SyncEntityList(), &status
));
314 handler
.ApplyUpdates(&status
);
315 EXPECT_TRUE(EntryExists(type_root
->id_string()));
316 EXPECT_FALSE(EntryExists(e1
->id_string()));
317 EXPECT_TRUE(EntryExists(e2
->id_string()));
320 TEST_F(DirectoryUpdateHandlerProcessUpdateTest
, ContextVersion
) {
321 DirectoryTypeDebugInfoEmitter
emitter(SYNCED_NOTIFICATIONS
, &type_observers_
);
322 DirectoryUpdateHandler
handler(dir(), SYNCED_NOTIFICATIONS
,
323 ui_worker(), &emitter
);
324 sessions::StatusController status
;
325 int field_number
= GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS
);
327 sync_pb::DataTypeProgressMarker progress
;
328 progress
.set_data_type_id(
329 GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS
));
330 progress
.set_token("token");
332 sync_pb::DataTypeContext old_context
;
333 old_context
.set_version(1);
334 old_context
.set_context("data");
335 old_context
.set_data_type_id(field_number
);
337 scoped_ptr
<sync_pb::SyncEntity
> type_root
=
338 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("root")),
339 syncable::GetNullId().GetServerId(),
340 SYNCED_NOTIFICATIONS
);
341 type_root
->set_server_defined_unique_tag(
342 ModelTypeToRootTag(SYNCED_NOTIFICATIONS
));
343 type_root
->set_folder(true);
344 scoped_ptr
<sync_pb::SyncEntity
> e1
=
345 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("e1")),
346 type_root
->id_string(),
347 SYNCED_NOTIFICATIONS
);
349 SyncEntityList updates
;
350 updates
.push_back(type_root
.get());
351 updates
.push_back(e1
.get());
353 // The first response should be processed fine.
355 handler
.ProcessGetUpdatesResponse(
356 progress
, old_context
, updates
, &status
));
357 handler
.ApplyUpdates(&status
);
359 EXPECT_TRUE(EntryExists(type_root
->id_string()));
360 EXPECT_TRUE(EntryExists(e1
->id_string()));
363 sync_pb::DataTypeContext dir_context
;
364 syncable::ReadTransaction
trans(FROM_HERE
, dir());
365 trans
.directory()->GetDataTypeContext(
366 &trans
, SYNCED_NOTIFICATIONS
, &dir_context
);
367 EXPECT_EQ(old_context
.SerializeAsString(), dir_context
.SerializeAsString());
370 sync_pb::DataTypeContext new_context
;
371 new_context
.set_version(0);
372 new_context
.set_context("old");
373 new_context
.set_data_type_id(field_number
);
375 scoped_ptr
<sync_pb::SyncEntity
> e2
=
376 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("e2")),
377 type_root
->id_string(),
378 SYNCED_NOTIFICATIONS
);
380 updates
.push_back(e2
.get());
382 // The second response, with an old context version, should result in an
383 // error and the updates should be dropped.
384 EXPECT_EQ(DATATYPE_TRIGGERED_RETRY
,
385 handler
.ProcessGetUpdatesResponse(
386 progress
, new_context
, updates
, &status
));
387 handler
.ApplyUpdates(&status
);
389 EXPECT_FALSE(EntryExists(e2
->id_string()));
392 sync_pb::DataTypeContext dir_context
;
393 syncable::ReadTransaction
trans(FROM_HERE
, dir());
394 trans
.directory()->GetDataTypeContext(
395 &trans
, SYNCED_NOTIFICATIONS
, &dir_context
);
396 EXPECT_EQ(old_context
.SerializeAsString(), dir_context
.SerializeAsString());
400 // See that updates containing attachment metadata are applied
401 // (i.e. server_attachment_metadata is copied to attachment_metadata).
402 TEST_F(DirectoryUpdateHandlerProcessUpdateTest
,
403 ProcessAndApplyUpdatesWithAttachments
) {
404 DirectoryTypeDebugInfoEmitter
emitter(ARTICLES
, &type_observers_
);
405 DirectoryUpdateHandler
handler(dir(), ARTICLES
, ui_worker(), &emitter
);
406 sessions::StatusController status
;
408 sync_pb::DataTypeProgressMarker progress
;
409 progress
.set_data_type_id(GetSpecificsFieldNumberFromModelType(ARTICLES
));
410 progress
.set_token("token");
411 progress
.mutable_gc_directive()->set_version_watermark(kDefaultVersion
+ 10);
413 sync_pb::DataTypeContext context
;
414 context
.set_data_type_id(GetSpecificsFieldNumberFromModelType(ARTICLES
));
415 context
.set_context("context");
416 context
.set_version(1);
418 scoped_ptr
<sync_pb::SyncEntity
> type_root
=
419 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("root")),
420 syncable::GetNullId().GetServerId(),
422 type_root
->set_server_defined_unique_tag(ModelTypeToRootTag(ARTICLES
));
423 type_root
->set_folder(true);
425 scoped_ptr
<sync_pb::SyncEntity
> e1
=
426 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("e1")),
427 type_root
->id_string(),
429 sync_pb::AttachmentIdProto
* attachment_id
= e1
->add_attachment_id();
430 *attachment_id
= CreateAttachmentIdProto();
432 SyncEntityList updates
;
433 updates
.push_back(type_root
.get());
434 updates
.push_back(e1
.get());
436 // Process and apply updates.
439 handler
.ProcessGetUpdatesResponse(progress
, context
, updates
, &status
));
440 handler
.ApplyUpdates(&status
);
442 ASSERT_TRUE(EntryExists(type_root
->id_string()));
443 ASSERT_TRUE(EntryExists(e1
->id_string()));
445 syncable::ReadTransaction
trans(FROM_HERE
, dir());
446 syncable::Entry
e(&trans
,
448 syncable::Id::CreateFromServerId(e1
->id_string()));
450 // See that the attachment_metadata is correct.
451 sync_pb::AttachmentMetadata attachment_metadata
= e
.GetAttachmentMetadata();
452 ASSERT_EQ(1, attachment_metadata
.record_size());
453 ASSERT_EQ(attachment_id
->SerializeAsString(),
454 attachment_metadata
.record(0).id().SerializeAsString());
455 ASSERT_TRUE(attachment_metadata
.record(0).is_on_server());
457 // See that attachment_metadata and server_attachment_metadata are equal.
458 ASSERT_EQ(attachment_metadata
.SerializeAsString(),
459 e
.GetServerAttachmentMetadata().SerializeAsString());
463 // A test harness for tests that focus on applying updates.
465 // Update application is performed when we want to take updates that were
466 // previously downloaded, processed, and stored in our syncable::Directory
467 // and use them to update our local state (both the Directory's local state
468 // and the model's local state, though these tests focus only on the Directory's
471 // This is kept separate from the update processing test in part for historical
472 // reasons, and in part because these tests may require a bit more infrastrcture
473 // in the future. Update application should happen on a different thread a lot
474 // of the time so these tests may end up requiring more infrastructure than the
475 // update processing tests. Currently, we're bypassing most of those issues by
476 // using FakeModelWorkers, so there's not much difference between the two test
478 class DirectoryUpdateHandlerApplyUpdateTest
: public ::testing::Test
{
480 DirectoryUpdateHandlerApplyUpdateTest()
481 : ui_worker_(new FakeModelWorker(GROUP_UI
)),
482 password_worker_(new FakeModelWorker(GROUP_PASSWORD
)),
483 passive_worker_(new FakeModelWorker(GROUP_PASSIVE
)),
484 bookmarks_emitter_(BOOKMARKS
, &type_observers_
),
485 passwords_emitter_(PASSWORDS
, &type_observers_
),
486 articles_emitter_(ARTICLES
, &type_observers_
),
487 update_handler_map_deleter_(&update_handler_map_
) {}
489 virtual void SetUp() OVERRIDE
{
491 entry_factory_
.reset(new TestEntryFactory(directory()));
493 update_handler_map_
.insert(std::make_pair(
495 new DirectoryUpdateHandler(directory(), BOOKMARKS
,
496 ui_worker_
, &bookmarks_emitter_
)));
497 update_handler_map_
.insert(std::make_pair(
499 new DirectoryUpdateHandler(directory(),
502 &passwords_emitter_
)));
503 update_handler_map_
.insert(std::make_pair(
505 new DirectoryUpdateHandler(
506 directory(), ARTICLES
, ui_worker_
, &articles_emitter_
)));
509 virtual void TearDown() OVERRIDE
{
510 dir_maker_
.TearDown();
513 const UpdateCounters
& GetBookmarksUpdateCounters() {
514 return bookmarks_emitter_
.GetUpdateCounters();
517 const UpdateCounters
& GetPasswordsUpdateCounters() {
518 return passwords_emitter_
.GetUpdateCounters();
521 const UpdateCounters
& GetArticlesUpdateCounters() {
522 return articles_emitter_
.GetUpdateCounters();
526 void ApplyBookmarkUpdates(sessions::StatusController
* status
) {
527 update_handler_map_
[BOOKMARKS
]->ApplyUpdates(status
);
530 void ApplyPasswordUpdates(sessions::StatusController
* status
) {
531 update_handler_map_
[PASSWORDS
]->ApplyUpdates(status
);
534 void ApplyArticlesUpdates(sessions::StatusController
* status
) {
535 update_handler_map_
[ARTICLES
]->ApplyUpdates(status
);
538 TestEntryFactory
* entry_factory() {
539 return entry_factory_
.get();
542 syncable::Directory
* directory() {
543 return dir_maker_
.directory();
547 typedef std::map
<ModelType
, UpdateHandler
*> UpdateHandlerMap
;
549 base::MessageLoop loop_
; // Needed to initialize the directory.
550 TestDirectorySetterUpper dir_maker_
;
551 scoped_ptr
<TestEntryFactory
> entry_factory_
;
553 scoped_refptr
<FakeModelWorker
> ui_worker_
;
554 scoped_refptr
<FakeModelWorker
> password_worker_
;
555 scoped_refptr
<FakeModelWorker
> passive_worker_
;
557 ObserverList
<TypeDebugInfoObserver
> type_observers_
;
558 DirectoryTypeDebugInfoEmitter bookmarks_emitter_
;
559 DirectoryTypeDebugInfoEmitter passwords_emitter_
;
560 DirectoryTypeDebugInfoEmitter articles_emitter_
;
562 UpdateHandlerMap update_handler_map_
;
563 STLValueDeleter
<UpdateHandlerMap
> update_handler_map_deleter_
;
567 sync_pb::EntitySpecifics
DefaultBookmarkSpecifics() {
568 sync_pb::EntitySpecifics result
;
569 AddDefaultFieldValue(BOOKMARKS
, &result
);
574 // Test update application for a few bookmark items.
575 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
, SimpleBookmark
) {
576 sessions::StatusController status
;
578 std::string root_server_id
= syncable::GetNullId().GetServerId();
579 int64 parent_handle
=
580 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
581 "parent", DefaultBookmarkSpecifics(), root_server_id
);
583 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
584 "child", DefaultBookmarkSpecifics(), "parent");
586 ApplyBookmarkUpdates(&status
);
588 const UpdateCounters
& counter
= GetBookmarksUpdateCounters();
589 EXPECT_EQ(0, counter
.num_encryption_conflict_application_failures
)
590 << "Simple update shouldn't result in conflicts";
591 EXPECT_EQ(0, counter
.num_hierarchy_conflict_application_failures
)
592 << "Simple update shouldn't result in conflicts";
593 EXPECT_EQ(2, counter
.num_updates_applied
)
594 << "All items should have been successfully applied";
597 syncable::ReadTransaction
trans(FROM_HERE
, directory());
599 syncable::Entry
parent(&trans
, syncable::GET_BY_HANDLE
, parent_handle
);
600 syncable::Entry
child(&trans
, syncable::GET_BY_HANDLE
, child_handle
);
602 ASSERT_TRUE(parent
.good());
603 ASSERT_TRUE(child
.good());
605 EXPECT_FALSE(parent
.GetIsUnsynced());
606 EXPECT_FALSE(parent
.GetIsUnappliedUpdate());
607 EXPECT_FALSE(child
.GetIsUnsynced());
608 EXPECT_FALSE(child
.GetIsUnappliedUpdate());
612 // Test that the applicator can handle updates delivered out of order.
613 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
,
614 BookmarkChildrenBeforeParent
) {
615 // Start with some bookmarks whose parents are unknown.
616 std::string root_server_id
= syncable::GetNullId().GetServerId();
617 int64 a_handle
= entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
618 "a_child_created_first", DefaultBookmarkSpecifics(), "parent");
619 int64 x_handle
= entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
620 "x_child_created_first", DefaultBookmarkSpecifics(), "parent");
622 // Update application will fail.
623 sessions::StatusController status1
;
624 ApplyBookmarkUpdates(&status1
);
625 EXPECT_EQ(0, status1
.num_updates_applied());
626 EXPECT_EQ(2, status1
.num_hierarchy_conflicts());
629 syncable::ReadTransaction
trans(FROM_HERE
, directory());
631 syncable::Entry
a(&trans
, syncable::GET_BY_HANDLE
, a_handle
);
632 syncable::Entry
x(&trans
, syncable::GET_BY_HANDLE
, x_handle
);
634 ASSERT_TRUE(a
.good());
635 ASSERT_TRUE(x
.good());
637 EXPECT_TRUE(a
.GetIsUnappliedUpdate());
638 EXPECT_TRUE(x
.GetIsUnappliedUpdate());
641 // Now add their parent and a few siblings.
642 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
643 "parent", DefaultBookmarkSpecifics(), root_server_id
);
644 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
645 "a_child_created_second", DefaultBookmarkSpecifics(), "parent");
646 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
647 "x_child_created_second", DefaultBookmarkSpecifics(), "parent");
649 // Update application will succeed.
650 sessions::StatusController status2
;
651 ApplyBookmarkUpdates(&status2
);
652 EXPECT_EQ(5, status2
.num_updates_applied())
653 << "All updates should have been successfully applied";
656 syncable::ReadTransaction
trans(FROM_HERE
, directory());
658 syncable::Entry
a(&trans
, syncable::GET_BY_HANDLE
, a_handle
);
659 syncable::Entry
x(&trans
, syncable::GET_BY_HANDLE
, x_handle
);
661 ASSERT_TRUE(a
.good());
662 ASSERT_TRUE(x
.good());
664 EXPECT_FALSE(a
.GetIsUnappliedUpdate());
665 EXPECT_FALSE(x
.GetIsUnappliedUpdate());
669 // Try to apply changes on an item that is both IS_UNSYNCED and
670 // IS_UNAPPLIED_UPDATE. Conflict resolution should be performed.
671 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
, SimpleBookmarkConflict
) {
672 int64 handle
= entry_factory()->CreateUnappliedAndUnsyncedBookmarkItem("x");
674 int original_server_version
= -10;
676 syncable::ReadTransaction
trans(FROM_HERE
, directory());
677 syncable::Entry
e(&trans
, syncable::GET_BY_HANDLE
, handle
);
678 original_server_version
= e
.GetServerVersion();
679 ASSERT_NE(original_server_version
, e
.GetBaseVersion());
680 EXPECT_TRUE(e
.GetIsUnsynced());
683 sessions::StatusController status
;
684 ApplyBookmarkUpdates(&status
);
686 const UpdateCounters
& counters
= GetBookmarksUpdateCounters();
687 EXPECT_EQ(1, counters
.num_server_overwrites
)
688 << "Unsynced and unapplied item conflict should be resolved";
689 EXPECT_EQ(0, counters
.num_updates_applied
)
690 << "Update should not be applied; we should override the server.";
693 syncable::ReadTransaction
trans(FROM_HERE
, directory());
694 syncable::Entry
e(&trans
, syncable::GET_BY_HANDLE
, handle
);
695 ASSERT_TRUE(e
.good());
696 EXPECT_EQ(original_server_version
, e
.GetServerVersion());
697 EXPECT_EQ(original_server_version
, e
.GetBaseVersion());
698 EXPECT_FALSE(e
.GetIsUnappliedUpdate());
700 // The unsynced flag will remain set until we successfully commit the item.
701 EXPECT_TRUE(e
.GetIsUnsynced());
705 // Create a simple conflict that is also a hierarchy conflict. If we were to
706 // follow the normal "server wins" logic, we'd end up violating hierarchy
707 // constraints. The hierarchy conflict must take precedence. We can not allow
708 // the update to be applied. The item must remain in the conflict state.
709 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
, HierarchyAndSimpleConflict
) {
710 // Create a simply-conflicting item. It will start with valid parent ids.
711 int64 handle
= entry_factory()->CreateUnappliedAndUnsyncedBookmarkItem(
712 "orphaned_by_server");
714 // Manually set the SERVER_PARENT_ID to bad value.
715 // A bad parent indicates a hierarchy conflict.
716 syncable::WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
717 syncable::MutableEntry
entry(&trans
, syncable::GET_BY_HANDLE
, handle
);
718 ASSERT_TRUE(entry
.good());
720 entry
.PutServerParentId(TestIdFactory::MakeServer("bogus_parent"));
723 sessions::StatusController status
;
724 ApplyBookmarkUpdates(&status
);
726 const UpdateCounters
& counters
= GetBookmarksUpdateCounters();
727 EXPECT_EQ(0, counters
.num_updates_applied
);
728 EXPECT_EQ(0, counters
.num_server_overwrites
);
729 EXPECT_EQ(1, counters
.num_hierarchy_conflict_application_failures
);
732 syncable::ReadTransaction
trans(FROM_HERE
, directory());
733 syncable::Entry
e(&trans
, syncable::GET_BY_HANDLE
, handle
);
734 ASSERT_TRUE(e
.good());
735 EXPECT_TRUE(e
.GetIsUnappliedUpdate());
736 EXPECT_TRUE(e
.GetIsUnsynced());
740 // Attempt to apply an udpate that would create a bookmark folder loop. This
741 // application should fail.
742 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
, BookmarkFolderLoop
) {
743 // Item 'X' locally has parent of 'root'. Server is updating it to have
746 // Create it as a child of root node.
747 int64 handle
= entry_factory()->CreateSyncedItem("X", BOOKMARKS
, true);
750 syncable::WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
751 syncable::MutableEntry
entry(&trans
, syncable::GET_BY_HANDLE
, handle
);
752 ASSERT_TRUE(entry
.good());
754 // Re-parent from root to "Y"
755 entry
.PutServerVersion(entry_factory()->GetNextRevision());
756 entry
.PutIsUnappliedUpdate(true);
757 entry
.PutServerParentId(TestIdFactory::MakeServer("Y"));
760 // Item 'Y' is child of 'X'.
761 entry_factory()->CreateUnsyncedItem(
762 TestIdFactory::MakeServer("Y"), TestIdFactory::MakeServer("X"), "Y", true,
765 // If the server's update were applied, we would have X be a child of Y, and Y
766 // as a child of X. That's a directory loop. The UpdateApplicator should
767 // prevent the update from being applied and note that this is a hierarchy
770 sessions::StatusController status
;
771 ApplyBookmarkUpdates(&status
);
773 // This should count as a hierarchy conflict.
774 const UpdateCounters
& counters
= GetBookmarksUpdateCounters();
775 EXPECT_EQ(1, counters
.num_hierarchy_conflict_application_failures
);
778 syncable::ReadTransaction
trans(FROM_HERE
, directory());
779 syncable::Entry
e(&trans
, syncable::GET_BY_HANDLE
, handle
);
780 ASSERT_TRUE(e
.good());
781 EXPECT_TRUE(e
.GetIsUnappliedUpdate());
782 EXPECT_FALSE(e
.GetIsUnsynced());
786 // Test update application where the update has been orphaned by a local folder
787 // deletion. The update application attempt should fail.
788 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
,
789 HierarchyConflictDeletedParent
) {
790 // Create a locally deleted parent item.
792 entry_factory()->CreateUnsyncedItem(
793 syncable::Id::CreateFromServerId("parent"), TestIdFactory::root(),
794 "parent", true, BOOKMARKS
, &parent_handle
);
796 syncable::WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
797 syncable::MutableEntry
entry(&trans
,
798 syncable::GET_BY_HANDLE
,
800 entry
.PutIsDel(true);
803 // Create an incoming child from the server.
804 int64 child_handle
= entry_factory()->CreateUnappliedNewItemWithParent(
805 "child", DefaultBookmarkSpecifics(), "parent");
807 // The server's update may seem valid to some other client, but on this client
808 // that new item's parent no longer exists. The update should not be applied
809 // and the update applicator should indicate this is a hierarchy conflict.
811 sessions::StatusController status
;
812 ApplyBookmarkUpdates(&status
);
813 const UpdateCounters
& counters
= GetBookmarksUpdateCounters();
814 EXPECT_EQ(1, counters
.num_hierarchy_conflict_application_failures
);
817 syncable::ReadTransaction
trans(FROM_HERE
, directory());
818 syncable::Entry
child(&trans
, syncable::GET_BY_HANDLE
, child_handle
);
819 ASSERT_TRUE(child
.good());
820 EXPECT_TRUE(child
.GetIsUnappliedUpdate());
821 EXPECT_FALSE(child
.GetIsUnsynced());
825 // Attempt to apply an update that deletes a folder where the folder has
826 // locally-created children. The update application should fail.
827 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
,
828 HierarchyConflictDeleteNonEmptyDirectory
) {
829 // Create a server-deleted folder as a child of root node.
830 int64 parent_handle
=
831 entry_factory()->CreateSyncedItem("parent", BOOKMARKS
, true);
833 syncable::WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
834 syncable::MutableEntry
entry(&trans
,
835 syncable::GET_BY_HANDLE
,
837 ASSERT_TRUE(entry
.good());
839 // Delete it on the server.
840 entry
.PutServerVersion(entry_factory()->GetNextRevision());
841 entry
.PutIsUnappliedUpdate(true);
842 entry
.PutServerParentId(TestIdFactory::root());
843 entry
.PutServerIsDel(true);
846 // Create a local child of the server-deleted directory.
847 entry_factory()->CreateUnsyncedItem(
848 TestIdFactory::MakeServer("child"), TestIdFactory::MakeServer("parent"),
849 "child", false, BOOKMARKS
, NULL
);
851 // The server's request to delete the directory must be ignored, otherwise our
852 // unsynced new child would be orphaned. This is a hierarchy conflict.
854 sessions::StatusController status
;
855 ApplyBookmarkUpdates(&status
);
857 // This should count as a hierarchy conflict.
858 const UpdateCounters
& counters
= GetBookmarksUpdateCounters();
859 EXPECT_EQ(1, counters
.num_hierarchy_conflict_application_failures
);
862 syncable::ReadTransaction
trans(FROM_HERE
, directory());
863 syncable::Entry
parent(&trans
, syncable::GET_BY_HANDLE
, parent_handle
);
864 ASSERT_TRUE(parent
.good());
865 EXPECT_TRUE(parent
.GetIsUnappliedUpdate());
866 EXPECT_FALSE(parent
.GetIsUnsynced());
870 // Attempt to apply updates where the updated item's parent is not known to this
871 // client. The update application attempt should fail.
872 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
,
873 HierarchyConflictUnknownParent
) {
874 // We shouldn't be able to do anything with either of these items.
875 int64 x_handle
= entry_factory()->CreateUnappliedNewItemWithParent(
876 "some_item", DefaultBookmarkSpecifics(), "unknown_parent");
877 int64 y_handle
= entry_factory()->CreateUnappliedNewItemWithParent(
878 "some_other_item", DefaultBookmarkSpecifics(), "some_item");
880 sessions::StatusController status
;
881 ApplyBookmarkUpdates(&status
);
883 const UpdateCounters
& counters
= GetBookmarksUpdateCounters();
884 EXPECT_EQ(2, counters
.num_hierarchy_conflict_application_failures
)
885 << "All updates with an unknown ancestors should be in conflict";
886 EXPECT_EQ(0, counters
.num_updates_applied
)
887 << "No item with an unknown ancestor should be applied";
890 syncable::ReadTransaction
trans(FROM_HERE
, directory());
891 syncable::Entry
x(&trans
, syncable::GET_BY_HANDLE
, x_handle
);
892 syncable::Entry
y(&trans
, syncable::GET_BY_HANDLE
, y_handle
);
893 ASSERT_TRUE(x
.good());
894 ASSERT_TRUE(y
.good());
895 EXPECT_TRUE(x
.GetIsUnappliedUpdate());
896 EXPECT_TRUE(y
.GetIsUnappliedUpdate());
897 EXPECT_FALSE(x
.GetIsUnsynced());
898 EXPECT_FALSE(y
.GetIsUnsynced());
902 // Attempt application of a mix of items. Some update application attempts will
903 // fail due to hierarchy conflicts. Others should succeed.
904 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
, ItemsBothKnownAndUnknown
) {
905 // See what happens when there's a mixture of good and bad updates.
906 std::string root_server_id
= syncable::GetNullId().GetServerId();
907 int64 u1_handle
= entry_factory()->CreateUnappliedNewItemWithParent(
908 "first_unknown_item", DefaultBookmarkSpecifics(), "unknown_parent");
909 int64 k1_handle
= entry_factory()->CreateUnappliedNewItemWithParent(
910 "first_known_item", DefaultBookmarkSpecifics(), root_server_id
);
911 int64 u2_handle
= entry_factory()->CreateUnappliedNewItemWithParent(
912 "second_unknown_item", DefaultBookmarkSpecifics(), "unknown_parent");
913 int64 k2_handle
= entry_factory()->CreateUnappliedNewItemWithParent(
914 "second_known_item", DefaultBookmarkSpecifics(), "first_known_item");
915 int64 k3_handle
= entry_factory()->CreateUnappliedNewItemWithParent(
916 "third_known_item", DefaultBookmarkSpecifics(), "fourth_known_item");
917 int64 k4_handle
= entry_factory()->CreateUnappliedNewItemWithParent(
918 "fourth_known_item", DefaultBookmarkSpecifics(), root_server_id
);
920 sessions::StatusController status
;
921 ApplyBookmarkUpdates(&status
);
923 const UpdateCounters
& counters
= GetBookmarksUpdateCounters();
924 EXPECT_EQ(2, counters
.num_hierarchy_conflict_application_failures
)
925 << "The updates with unknown ancestors should be in conflict";
926 EXPECT_EQ(4, counters
.num_updates_applied
)
927 << "The updates with known ancestors should be successfully applied";
930 syncable::ReadTransaction
trans(FROM_HERE
, directory());
931 syncable::Entry
u1(&trans
, syncable::GET_BY_HANDLE
, u1_handle
);
932 syncable::Entry
u2(&trans
, syncable::GET_BY_HANDLE
, u2_handle
);
933 syncable::Entry
k1(&trans
, syncable::GET_BY_HANDLE
, k1_handle
);
934 syncable::Entry
k2(&trans
, syncable::GET_BY_HANDLE
, k2_handle
);
935 syncable::Entry
k3(&trans
, syncable::GET_BY_HANDLE
, k3_handle
);
936 syncable::Entry
k4(&trans
, syncable::GET_BY_HANDLE
, k4_handle
);
937 ASSERT_TRUE(u1
.good());
938 ASSERT_TRUE(u2
.good());
939 ASSERT_TRUE(k1
.good());
940 ASSERT_TRUE(k2
.good());
941 ASSERT_TRUE(k3
.good());
942 ASSERT_TRUE(k4
.good());
943 EXPECT_TRUE(u1
.GetIsUnappliedUpdate());
944 EXPECT_TRUE(u2
.GetIsUnappliedUpdate());
945 EXPECT_FALSE(k1
.GetIsUnappliedUpdate());
946 EXPECT_FALSE(k2
.GetIsUnappliedUpdate());
947 EXPECT_FALSE(k3
.GetIsUnappliedUpdate());
948 EXPECT_FALSE(k4
.GetIsUnappliedUpdate());
952 // Attempt application of password upates where the passphrase is known.
953 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
, DecryptablePassword
) {
954 // Decryptable password updates should be applied.
955 Cryptographer
* cryptographer
;
957 // Storing the cryptographer separately is bad, but for this test we
959 syncable::ReadTransaction
trans(FROM_HERE
, directory());
960 cryptographer
= directory()->GetCryptographer(&trans
);
963 KeyParams params
= {"localhost", "dummy", "foobar"};
964 cryptographer
->AddKey(params
);
966 sync_pb::EntitySpecifics specifics
;
967 sync_pb::PasswordSpecificsData data
;
968 data
.set_origin("http://example.com");
970 cryptographer
->Encrypt(data
,
971 specifics
.mutable_password()->mutable_encrypted());
973 entry_factory()->CreateUnappliedNewItem("item", specifics
, false);
975 sessions::StatusController status
;
976 ApplyPasswordUpdates(&status
);
978 const UpdateCounters
& counters
= GetPasswordsUpdateCounters();
979 EXPECT_EQ(1, counters
.num_updates_applied
)
980 << "The updates that can be decrypted should be applied";
983 syncable::ReadTransaction
trans(FROM_HERE
, directory());
984 syncable::Entry
e(&trans
, syncable::GET_BY_HANDLE
, handle
);
985 ASSERT_TRUE(e
.good());
986 EXPECT_FALSE(e
.GetIsUnappliedUpdate());
987 EXPECT_FALSE(e
.GetIsUnsynced());
991 // Attempt application of encrypted items when the passphrase is not known.
992 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
, UndecryptableData
) {
993 // Undecryptable updates should not be applied.
994 sync_pb::EntitySpecifics encrypted_bookmark
;
995 encrypted_bookmark
.mutable_encrypted();
996 AddDefaultFieldValue(BOOKMARKS
, &encrypted_bookmark
);
997 std::string root_server_id
= syncable::GetNullId().GetServerId();
998 int64 folder_handle
= entry_factory()->CreateUnappliedNewItemWithParent(
1002 int64 bookmark_handle
= entry_factory()->CreateUnappliedNewItem(
1006 sync_pb::EntitySpecifics encrypted_password
;
1007 encrypted_password
.mutable_password();
1008 int64 password_handle
= entry_factory()->CreateUnappliedNewItem(
1013 sessions::StatusController status
;
1014 ApplyBookmarkUpdates(&status
);
1015 ApplyPasswordUpdates(&status
);
1017 const UpdateCounters
& bm_counters
= GetBookmarksUpdateCounters();
1018 EXPECT_EQ(2, bm_counters
.num_encryption_conflict_application_failures
)
1019 << "Updates that can't be decrypted should be in encryption conflict";
1020 EXPECT_EQ(0, bm_counters
.num_updates_applied
)
1021 << "No update that can't be decrypted should be applied";
1023 const UpdateCounters
& pw_counters
= GetPasswordsUpdateCounters();
1024 EXPECT_EQ(1, pw_counters
.num_encryption_conflict_application_failures
)
1025 << "Updates that can't be decrypted should be in encryption conflict";
1026 EXPECT_EQ(0, pw_counters
.num_updates_applied
)
1027 << "No update that can't be decrypted should be applied";
1030 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1031 syncable::Entry
folder(&trans
, syncable::GET_BY_HANDLE
, folder_handle
);
1032 syncable::Entry
bm(&trans
, syncable::GET_BY_HANDLE
, bookmark_handle
);
1033 syncable::Entry
pw(&trans
, syncable::GET_BY_HANDLE
, password_handle
);
1034 ASSERT_TRUE(folder
.good());
1035 ASSERT_TRUE(bm
.good());
1036 ASSERT_TRUE(pw
.good());
1037 EXPECT_TRUE(folder
.GetIsUnappliedUpdate());
1038 EXPECT_TRUE(bm
.GetIsUnappliedUpdate());
1039 EXPECT_TRUE(pw
.GetIsUnappliedUpdate());
1043 // Test a mix of decryptable and undecryptable updates.
1044 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
, SomeUndecryptablePassword
) {
1045 Cryptographer
* cryptographer
;
1047 int64 decryptable_handle
= -1;
1048 int64 undecryptable_handle
= -1;
1050 // Only decryptable password updates should be applied.
1052 sync_pb::EntitySpecifics specifics
;
1053 sync_pb::PasswordSpecificsData data
;
1054 data
.set_origin("http://example.com/1");
1056 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1057 cryptographer
= directory()->GetCryptographer(&trans
);
1059 KeyParams params
= {"localhost", "dummy", "foobar"};
1060 cryptographer
->AddKey(params
);
1062 cryptographer
->Encrypt(data
,
1063 specifics
.mutable_password()->mutable_encrypted());
1065 decryptable_handle
=
1066 entry_factory()->CreateUnappliedNewItem("item1", specifics
, false);
1069 // Create a new cryptographer, independent of the one in the session.
1070 Cryptographer
other_cryptographer(cryptographer
->encryptor());
1071 KeyParams params
= {"localhost", "dummy", "bazqux"};
1072 other_cryptographer
.AddKey(params
);
1074 sync_pb::EntitySpecifics specifics
;
1075 sync_pb::PasswordSpecificsData data
;
1076 data
.set_origin("http://example.com/2");
1078 other_cryptographer
.Encrypt(data
,
1079 specifics
.mutable_password()->mutable_encrypted());
1080 undecryptable_handle
=
1081 entry_factory()->CreateUnappliedNewItem("item2", specifics
, false);
1084 sessions::StatusController status
;
1085 ApplyPasswordUpdates(&status
);
1087 const UpdateCounters
& counters
= GetPasswordsUpdateCounters();
1088 EXPECT_EQ(1, counters
.num_encryption_conflict_application_failures
)
1089 << "The updates that can't be decrypted should be in encryption "
1091 EXPECT_EQ(1, counters
.num_updates_applied
)
1092 << "The undecryptable password update shouldn't be applied";
1095 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1096 syncable::Entry
e1(&trans
, syncable::GET_BY_HANDLE
, decryptable_handle
);
1097 syncable::Entry
e2(&trans
, syncable::GET_BY_HANDLE
, undecryptable_handle
);
1098 ASSERT_TRUE(e1
.good());
1099 ASSERT_TRUE(e2
.good());
1100 EXPECT_FALSE(e1
.GetIsUnappliedUpdate());
1101 EXPECT_TRUE(e2
.GetIsUnappliedUpdate());
1105 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
,
1106 SimpleConflictDifferentAttachmentMetadata
) {
1107 const bool is_folder
= false;
1108 sync_pb::EntitySpecifics specifics
;
1109 *specifics
.mutable_article() = sync_pb::ArticleSpecifics();
1110 int64 handle
= entry_factory()->CreateSyncedItem("art1", ARTICLES
, is_folder
);
1112 sync_pb::AttachmentIdProto local_attachment_id
= CreateAttachmentIdProto();
1113 sync_pb::AttachmentIdProto server_attachment_id
= CreateAttachmentIdProto();
1115 // Add an attachment to the local attachment metadata.
1116 sync_pb::AttachmentMetadata local_metadata
;
1117 sync_pb::AttachmentMetadataRecord
* local_record
= local_metadata
.add_record();
1118 *local_record
->mutable_id() = local_attachment_id
;
1119 local_record
->set_is_on_server(true);
1120 entry_factory()->SetLocalAttachmentMetadataForItem(handle
, local_metadata
);
1122 // Add a different attachment to the server attachment metadata.
1123 sync_pb::AttachmentMetadata server_metadata
;
1124 sync_pb::AttachmentMetadataRecord
* server_record
=
1125 server_metadata
.add_record();
1126 *server_record
->mutable_id() = server_attachment_id
;
1127 server_record
->set_is_on_server(true);
1128 entry_factory()->SetServerAttachmentMetadataForItem(handle
, server_metadata
);
1130 // At this point we have a simple conflict. The server says art1 should have
1131 // server_attachment_id, but the local sync engine says it should have
1132 // local_attachment_id.
1134 sessions::StatusController status
;
1135 ApplyArticlesUpdates(&status
);
1137 // See that the server won.
1138 const UpdateCounters
& counters
= GetArticlesUpdateCounters();
1139 EXPECT_EQ(1, counters
.num_updates_applied
);
1140 EXPECT_EQ(1, counters
.num_local_overwrites
);
1141 EXPECT_EQ(0, counters
.num_server_overwrites
);
1142 local_metadata
= entry_factory()->GetLocalAttachmentMetadataForItem(handle
);
1143 EXPECT_EQ(server_metadata
.SerializeAsString(),
1144 local_metadata
.SerializeAsString());
1147 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
,
1148 SimpleConflictSameAttachmentMetadataDifferentOrder
) {
1149 const bool is_folder
= false;
1150 sync_pb::EntitySpecifics specifics
;
1151 *specifics
.mutable_article() = sync_pb::ArticleSpecifics();
1152 int64 handle
= entry_factory()->CreateSyncedItem("art1", ARTICLES
, is_folder
);
1154 sync_pb::AttachmentIdProto id1
= CreateAttachmentIdProto();
1155 sync_pb::AttachmentIdProto id2
= CreateAttachmentIdProto();
1157 // Add id1, then id2 to the local attachment metadata.
1158 sync_pb::AttachmentMetadata local_metadata
;
1159 sync_pb::AttachmentMetadataRecord
* record
= local_metadata
.add_record();
1160 *record
->mutable_id() = id1
;
1161 record
->set_is_on_server(true);
1162 record
= local_metadata
.add_record();
1163 *record
->mutable_id() = id2
;
1164 record
->set_is_on_server(true);
1165 entry_factory()->SetLocalAttachmentMetadataForItem(handle
, local_metadata
);
1167 // Add id1 and id2 to the server attachment metadata, but in reverse order.
1168 sync_pb::AttachmentMetadata server_metadata
;
1169 record
= server_metadata
.add_record();
1170 *record
->mutable_id() = id2
;
1171 record
->set_is_on_server(true);
1172 record
= local_metadata
.add_record();
1173 *record
->mutable_id() = id1
;
1174 record
->set_is_on_server(true);
1175 entry_factory()->SetServerAttachmentMetadataForItem(handle
, server_metadata
);
1177 // At this point we have a (false) conflict.
1179 sessions::StatusController status
;
1180 ApplyArticlesUpdates(&status
);
1182 // See that the server won.
1183 const UpdateCounters
& counters
= GetArticlesUpdateCounters();
1184 EXPECT_EQ(1, counters
.num_updates_applied
);
1185 EXPECT_EQ(1, counters
.num_local_overwrites
);
1186 EXPECT_EQ(0, counters
.num_server_overwrites
);
1187 local_metadata
= entry_factory()->GetLocalAttachmentMetadataForItem(handle
);
1188 EXPECT_EQ(server_metadata
.SerializeAsString(),
1189 local_metadata
.SerializeAsString());
1192 } // namespace syncer