1 // Copyright 2012 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 // Syncer unit tests. Unfortunately a lot of these tests
6 // are outdated and need to be reworked and updated.
15 #include "base/bind.h"
16 #include "base/bind_helpers.h"
17 #include "base/callback.h"
18 #include "base/compiler_specific.h"
19 #include "base/location.h"
20 #include "base/memory/scoped_ptr.h"
21 #include "base/message_loop/message_loop.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/stringprintf.h"
24 #include "base/time/time.h"
25 #include "build/build_config.h"
26 #include "sync/engine/backoff_delay_provider.h"
27 #include "sync/engine/get_commit_ids.h"
28 #include "sync/engine/net/server_connection_manager.h"
29 #include "sync/engine/sync_scheduler_impl.h"
30 #include "sync/engine/syncer.h"
31 #include "sync/engine/syncer_proto_util.h"
32 #include "sync/internal_api/public/base/cancelation_signal.h"
33 #include "sync/internal_api/public/base/model_type.h"
34 #include "sync/internal_api/public/engine/model_safe_worker.h"
35 #include "sync/internal_api/public/sessions/commit_counters.h"
36 #include "sync/internal_api/public/sessions/status_counters.h"
37 #include "sync/internal_api/public/sessions/update_counters.h"
38 #include "sync/protocol/bookmark_specifics.pb.h"
39 #include "sync/protocol/nigori_specifics.pb.h"
40 #include "sync/protocol/preference_specifics.pb.h"
41 #include "sync/protocol/sync.pb.h"
42 #include "sync/sessions/sync_session_context.h"
43 #include "sync/syncable/mutable_entry.h"
44 #include "sync/syncable/nigori_util.h"
45 #include "sync/syncable/syncable_delete_journal.h"
46 #include "sync/syncable/syncable_read_transaction.h"
47 #include "sync/syncable/syncable_util.h"
48 #include "sync/syncable/syncable_write_transaction.h"
49 #include "sync/test/engine/fake_model_worker.h"
50 #include "sync/test/engine/mock_connection_manager.h"
51 #include "sync/test/engine/mock_nudge_handler.h"
52 #include "sync/test/engine/test_directory_setter_upper.h"
53 #include "sync/test/engine/test_id_factory.h"
54 #include "sync/test/engine/test_syncable_utils.h"
55 #include "sync/test/fake_encryptor.h"
56 #include "sync/test/fake_sync_encryption_handler.h"
57 #include "sync/test/sessions/mock_debug_info_getter.h"
58 #include "sync/util/cryptographer.h"
59 #include "sync/util/extensions_activity.h"
60 #include "sync/util/time.h"
61 #include "testing/gtest/include/gtest/gtest.h"
63 using base::TimeDelta
;
74 using syncable::BaseTransaction
;
76 using syncable::CountEntriesWithName
;
77 using syncable::Directory
;
78 using syncable::Entry
;
79 using syncable::GetFirstEntryWithName
;
80 using syncable::GetOnlyEntryWithName
;
82 using syncable::kEncryptedString
;
83 using syncable::MutableEntry
;
84 using syncable::WriteTransaction
;
86 using syncable::CREATE
;
87 using syncable::GET_BY_HANDLE
;
88 using syncable::GET_BY_ID
;
89 using syncable::GET_BY_CLIENT_TAG
;
90 using syncable::GET_BY_SERVER_TAG
;
91 using syncable::GET_TYPE_ROOT
;
92 using syncable::UNITTEST
;
94 using sessions::MockDebugInfoGetter
;
95 using sessions::StatusController
;
96 using sessions::SyncSessionContext
;
97 using sessions::SyncSession
;
101 // A helper to hold on to the counters emitted by the sync engine.
102 class TypeDebugInfoCache
: public TypeDebugInfoObserver
{
104 TypeDebugInfoCache();
105 ~TypeDebugInfoCache() override
;
107 CommitCounters
GetLatestCommitCounters(ModelType type
) const;
108 UpdateCounters
GetLatestUpdateCounters(ModelType type
) const;
109 StatusCounters
GetLatestStatusCounters(ModelType type
) const;
111 // TypeDebugInfoObserver implementation.
112 void OnCommitCountersUpdated(syncer::ModelType type
,
113 const CommitCounters
& counters
) override
;
114 void OnUpdateCountersUpdated(syncer::ModelType type
,
115 const UpdateCounters
& counters
) override
;
116 void OnStatusCountersUpdated(syncer::ModelType type
,
117 const StatusCounters
& counters
) override
;
120 std::map
<ModelType
, CommitCounters
> commit_counters_map_
;
121 std::map
<ModelType
, UpdateCounters
> update_counters_map_
;
122 std::map
<ModelType
, StatusCounters
> status_counters_map_
;
125 TypeDebugInfoCache::TypeDebugInfoCache() {}
127 TypeDebugInfoCache::~TypeDebugInfoCache() {}
129 CommitCounters
TypeDebugInfoCache::GetLatestCommitCounters(
130 ModelType type
) const {
131 std::map
<ModelType
, CommitCounters
>::const_iterator it
=
132 commit_counters_map_
.find(type
);
133 if (it
== commit_counters_map_
.end()) {
134 return CommitCounters();
140 UpdateCounters
TypeDebugInfoCache::GetLatestUpdateCounters(
141 ModelType type
) const {
142 std::map
<ModelType
, UpdateCounters
>::const_iterator it
=
143 update_counters_map_
.find(type
);
144 if (it
== update_counters_map_
.end()) {
145 return UpdateCounters();
151 StatusCounters
TypeDebugInfoCache::GetLatestStatusCounters(
152 ModelType type
) const {
153 std::map
<ModelType
, StatusCounters
>::const_iterator it
=
154 status_counters_map_
.find(type
);
155 if (it
== status_counters_map_
.end()) {
156 return StatusCounters();
162 void TypeDebugInfoCache::OnCommitCountersUpdated(
163 syncer::ModelType type
,
164 const CommitCounters
& counters
) {
165 commit_counters_map_
[type
] = counters
;
168 void TypeDebugInfoCache::OnUpdateCountersUpdated(
169 syncer::ModelType type
,
170 const UpdateCounters
& counters
) {
171 update_counters_map_
[type
] = counters
;
174 void TypeDebugInfoCache::OnStatusCountersUpdated(
175 syncer::ModelType type
,
176 const StatusCounters
& counters
) {
177 status_counters_map_
[type
] = counters
;
182 class SyncerTest
: public testing::Test
,
183 public SyncSession::Delegate
,
184 public SyncEngineEventListener
{
187 : extensions_activity_(new ExtensionsActivity
),
189 saw_syncer_event_(false),
190 last_client_invalidation_hint_buffer_size_(10) {
193 // SyncSession::Delegate implementation.
194 void OnThrottled(const base::TimeDelta
& throttle_duration
) override
{
195 FAIL() << "Should not get silenced.";
197 void OnTypesThrottled(ModelTypeSet types
,
198 const base::TimeDelta
& throttle_duration
) override
{
199 scheduler_
->OnTypesThrottled(types
, throttle_duration
);
201 bool IsCurrentlyThrottled() override
{ return false; }
202 void OnReceivedLongPollIntervalUpdate(
203 const base::TimeDelta
& new_interval
) override
{
204 last_long_poll_interval_received_
= new_interval
;
206 void OnReceivedShortPollIntervalUpdate(
207 const base::TimeDelta
& new_interval
) override
{
208 last_short_poll_interval_received_
= new_interval
;
210 void OnReceivedCustomNudgeDelays(
211 const std::map
<ModelType
, base::TimeDelta
>& delay_map
) override
{
212 std::map
<ModelType
, base::TimeDelta
>::const_iterator iter
=
213 delay_map
.find(SESSIONS
);
214 if (iter
!= delay_map
.end() && iter
->second
> base::TimeDelta())
215 last_sessions_commit_delay_
= iter
->second
;
216 iter
= delay_map
.find(BOOKMARKS
);
217 if (iter
!= delay_map
.end() && iter
->second
> base::TimeDelta())
218 last_bookmarks_commit_delay_
= iter
->second
;
220 void OnReceivedClientInvalidationHintBufferSize(int size
) override
{
221 last_client_invalidation_hint_buffer_size_
= size
;
223 void OnReceivedGuRetryDelay(const base::TimeDelta
& delay
) override
{}
224 void OnReceivedMigrationRequest(ModelTypeSet types
) override
{}
225 void OnProtocolEvent(const ProtocolEvent
& event
) override
{}
226 void OnSyncProtocolError(const SyncProtocolError
& error
) override
{}
228 void GetModelSafeRoutingInfo(ModelSafeRoutingInfo
* out
) {
229 // We're just testing the sync engine here, so we shunt everything to
230 // the SyncerThread. Datatypes which aren't enabled aren't in the map.
231 for (ModelTypeSet::Iterator it
= enabled_datatypes_
.First();
232 it
.Good(); it
.Inc()) {
233 (*out
)[it
.Get()] = GROUP_PASSIVE
;
237 void OnSyncCycleEvent(const SyncCycleEvent
& event
) override
{
238 DVLOG(1) << "HandleSyncEngineEvent in unittest " << event
.what_happened
;
239 // we only test for entry-specific events, not status changed ones.
240 switch (event
.what_happened
) {
241 case SyncCycleEvent::SYNC_CYCLE_BEGIN
: // Fall through.
242 case SyncCycleEvent::STATUS_CHANGED
:
243 case SyncCycleEvent::SYNC_CYCLE_ENDED
:
246 CHECK(false) << "Handling unknown error type in unit tests!!";
248 saw_syncer_event_
= true;
251 void OnActionableError(const SyncProtocolError
& error
) override
{}
252 void OnRetryTimeChanged(base::Time retry_time
) override
{}
253 void OnThrottledTypesChanged(ModelTypeSet throttled_types
) override
{}
254 void OnMigrationRequested(ModelTypeSet types
) override
{}
256 void ResetSession() {
257 session_
.reset(SyncSession::Build(context_
.get(), this));
260 void SyncShareNudge() {
263 // Pretend we've seen a local change, to make the nudge_tracker look normal.
264 nudge_tracker_
.RecordLocalChange(ModelTypeSet(BOOKMARKS
));
266 EXPECT_TRUE(syncer_
->NormalSyncShare(context_
->GetEnabledTypes(),
267 &nudge_tracker_
, session_
.get()));
270 void SyncShareConfigure() {
272 EXPECT_TRUE(syncer_
->ConfigureSyncShare(
273 context_
->GetEnabledTypes(),
274 sync_pb::GetUpdatesCallerInfo::RECONFIGURATION
,
278 void SetUp() override
{
280 mock_server_
.reset(new MockConnectionManager(directory(),
281 &cancelation_signal_
));
282 debug_info_getter_
.reset(new MockDebugInfoGetter
);
283 EnableDatatype(BOOKMARKS
);
284 EnableDatatype(NIGORI
);
285 EnableDatatype(PREFERENCES
);
286 EnableDatatype(NIGORI
);
287 workers_
.push_back(scoped_refptr
<ModelSafeWorker
>(
288 new FakeModelWorker(GROUP_PASSIVE
)));
289 std::vector
<SyncEngineEventListener
*> listeners
;
290 listeners
.push_back(this);
292 ModelSafeRoutingInfo routing_info
;
293 GetModelSafeRoutingInfo(&routing_info
);
295 model_type_registry_
.reset(
296 new ModelTypeRegistry(workers_
, directory(), &mock_nudge_handler_
));
297 model_type_registry_
->RegisterDirectoryTypeDebugInfoObserver(
300 context_
.reset(new SyncSessionContext(
303 extensions_activity_
.get(),
305 debug_info_getter_
.get(),
306 model_type_registry_
.get(),
307 true, // enable keystore encryption
308 false, // force enable pre-commit GU avoidance experiment
309 "fake_invalidator_client_id"));
310 context_
->SetRoutingInfo(routing_info
);
311 syncer_
= new Syncer(&cancelation_signal_
);
312 scheduler_
.reset(new SyncSchedulerImpl(
314 BackoffDelayProvider::FromDefaults(),
316 // scheduler_ owned syncer_ now and will manage the memory of syncer_
319 syncable::ReadTransaction
trans(FROM_HERE
, directory());
320 syncable::Directory::Metahandles children
;
321 directory()->GetChildHandlesById(&trans
, trans
.root_id(), &children
);
322 ASSERT_EQ(0u, children
.size());
323 saw_syncer_event_
= false;
324 root_id_
= TestIdFactory::root();
325 parent_id_
= ids_
.MakeServer("parent id");
326 child_id_
= ids_
.MakeServer("child id");
327 directory()->set_store_birthday(mock_server_
->store_birthday());
328 mock_server_
->SetKeystoreKey("encryption_key");
331 void TearDown() override
{
332 model_type_registry_
->UnregisterDirectoryTypeDebugInfoObserver(
334 mock_server_
.reset();
336 dir_maker_
.TearDown();
339 void WriteTestDataToEntry(WriteTransaction
* trans
, MutableEntry
* entry
) {
340 EXPECT_FALSE(entry
->GetIsDir());
341 EXPECT_FALSE(entry
->GetIsDel());
342 sync_pb::EntitySpecifics specifics
;
343 specifics
.mutable_bookmark()->set_url("http://demo/");
344 specifics
.mutable_bookmark()->set_favicon("PNG");
345 entry
->PutSpecifics(specifics
);
346 entry
->PutIsUnsynced(true);
348 void VerifyTestDataInEntry(BaseTransaction
* trans
, Entry
* entry
) {
349 EXPECT_FALSE(entry
->GetIsDir());
350 EXPECT_FALSE(entry
->GetIsDel());
351 VerifyTestBookmarkDataInEntry(entry
);
353 void VerifyTestBookmarkDataInEntry(Entry
* entry
) {
354 const sync_pb::EntitySpecifics
& specifics
= entry
->GetSpecifics();
355 EXPECT_TRUE(specifics
.has_bookmark());
356 EXPECT_EQ("PNG", specifics
.bookmark().favicon());
357 EXPECT_EQ("http://demo/", specifics
.bookmark().url());
360 void VerifyHierarchyConflictsReported(
361 const sync_pb::ClientToServerMessage
& message
) {
362 // Our request should have included a warning about hierarchy conflicts.
363 const sync_pb::ClientStatus
& client_status
= message
.client_status();
364 EXPECT_TRUE(client_status
.has_hierarchy_conflict_detected());
365 EXPECT_TRUE(client_status
.hierarchy_conflict_detected());
368 void VerifyNoHierarchyConflictsReported(
369 const sync_pb::ClientToServerMessage
& message
) {
370 // Our request should have reported no hierarchy conflicts detected.
371 const sync_pb::ClientStatus
& client_status
= message
.client_status();
372 EXPECT_TRUE(client_status
.has_hierarchy_conflict_detected());
373 EXPECT_FALSE(client_status
.hierarchy_conflict_detected());
376 void VerifyHierarchyConflictsUnspecified(
377 const sync_pb::ClientToServerMessage
& message
) {
378 // Our request should have neither confirmed nor denied hierarchy conflicts.
379 const sync_pb::ClientStatus
& client_status
= message
.client_status();
380 EXPECT_FALSE(client_status
.has_hierarchy_conflict_detected());
383 sync_pb::EntitySpecifics
DefaultBookmarkSpecifics() {
384 sync_pb::EntitySpecifics result
;
385 AddDefaultFieldValue(BOOKMARKS
, &result
);
389 sync_pb::EntitySpecifics
DefaultPreferencesSpecifics() {
390 sync_pb::EntitySpecifics result
;
391 AddDefaultFieldValue(PREFERENCES
, &result
);
394 // Enumeration of alterations to entries for commit ordering tests.
396 LIST_END
= 0, // Denotes the end of the list of features from below.
397 SYNCED
, // Items are unsynced by default
403 struct CommitOrderingTest
{
404 // expected commit index.
406 // Details about the item
408 syncable::Id parent_id
;
409 EntryFeature features
[10];
411 static CommitOrderingTest
MakeLastCommitItem() {
412 CommitOrderingTest last_commit_item
;
413 last_commit_item
.commit_index
= -1;
414 last_commit_item
.id
= TestIdFactory::root();
415 return last_commit_item
;
419 void RunCommitOrderingTest(CommitOrderingTest
* test
) {
420 map
<int, syncable::Id
> expected_positions
;
421 { // Transaction scope.
422 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
423 while (!test
->id
.IsRoot()) {
424 if (test
->commit_index
>= 0) {
425 map
<int, syncable::Id
>::value_type
entry(test
->commit_index
,
427 bool double_position
= !expected_positions
.insert(entry
).second
;
428 ASSERT_FALSE(double_position
) << "Two id's expected at one position";
430 string utf8_name
= test
->id
.GetServerId();
431 string
name(utf8_name
.begin(), utf8_name
.end());
432 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, test
->parent_id
, name
);
434 entry
.PutId(test
->id
);
435 if (test
->id
.ServerKnows()) {
436 entry
.PutBaseVersion(5);
437 entry
.PutServerVersion(5);
438 entry
.PutServerParentId(test
->parent_id
);
440 entry
.PutIsDir(true);
441 entry
.PutIsUnsynced(true);
442 entry
.PutSpecifics(DefaultBookmarkSpecifics());
443 // Set the time to 30 seconds in the future to reduce the chance of
445 const base::Time
& now_plus_30s
=
446 base::Time::Now() + base::TimeDelta::FromSeconds(30);
447 const base::Time
& now_minus_2h
=
448 base::Time::Now() - base::TimeDelta::FromHours(2);
449 entry
.PutMtime(now_plus_30s
);
450 for (size_t i
= 0 ; i
< arraysize(test
->features
) ; ++i
) {
451 switch (test
->features
[i
]) {
455 entry
.PutIsUnsynced(false);
458 entry
.PutIsDel(true);
461 entry
.PutMtime(now_minus_2h
);
463 case MOVED_FROM_ROOT
:
464 entry
.PutServerParentId(trans
.root_id());
467 FAIL() << "Bad value in CommitOrderingTest list";
474 ASSERT_TRUE(expected_positions
.size() ==
475 mock_server_
->committed_ids().size());
476 // If this test starts failing, be aware other sort orders could be valid.
477 for (size_t i
= 0; i
< expected_positions
.size(); ++i
) {
479 EXPECT_EQ(1u, expected_positions
.count(i
));
480 EXPECT_EQ(expected_positions
[i
], mock_server_
->committed_ids()[i
]);
484 CommitCounters
GetCommitCounters(ModelType type
) {
485 return debug_info_cache_
.GetLatestCommitCounters(type
);
488 UpdateCounters
GetUpdateCounters(ModelType type
) {
489 return debug_info_cache_
.GetLatestUpdateCounters(type
);
492 StatusCounters
GetStatusCounters(ModelType type
) {
493 return debug_info_cache_
.GetLatestStatusCounters(type
);
496 Directory
* directory() {
497 return dir_maker_
.directory();
500 const std::string
local_cache_guid() {
501 return directory()->cache_guid();
504 const std::string
foreign_cache_guid() {
505 return "kqyg7097kro6GSUod+GSg==";
508 int64
CreateUnsyncedDirectory(const string
& entry_name
,
509 const string
& idstring
) {
510 return CreateUnsyncedDirectory(entry_name
,
511 syncable::Id::CreateFromServerId(idstring
));
514 int64
CreateUnsyncedDirectory(const string
& entry_name
,
515 const syncable::Id
& id
) {
516 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
518 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), entry_name
);
519 EXPECT_TRUE(entry
.good());
520 entry
.PutIsUnsynced(true);
521 entry
.PutIsDir(true);
522 entry
.PutSpecifics(DefaultBookmarkSpecifics());
523 entry
.PutBaseVersion(id
.ServerKnows() ? 1 : 0);
525 return entry
.GetMetahandle();
528 void EnableDatatype(ModelType model_type
) {
529 enabled_datatypes_
.Put(model_type
);
531 ModelSafeRoutingInfo routing_info
;
532 GetModelSafeRoutingInfo(&routing_info
);
535 context_
->SetRoutingInfo(routing_info
);
538 mock_server_
->ExpectGetUpdatesRequestTypes(enabled_datatypes_
);
541 void DisableDatatype(ModelType model_type
) {
542 enabled_datatypes_
.Remove(model_type
);
544 ModelSafeRoutingInfo routing_info
;
545 GetModelSafeRoutingInfo(&routing_info
);
548 context_
->SetRoutingInfo(routing_info
);
551 mock_server_
->ExpectGetUpdatesRequestTypes(enabled_datatypes_
);
554 Cryptographer
* GetCryptographer(syncable::BaseTransaction
* trans
) {
555 return directory()->GetCryptographer(trans
);
558 // Configures SyncSessionContext and NudgeTracker so Syncer won't call
559 // GetUpdates prior to Commit. This method can be used to ensure a Commit is
560 // not preceeded by GetUpdates.
561 void ConfigureNoGetUpdatesRequired() {
562 context_
->set_server_enabled_pre_commit_update_avoidance(true);
563 nudge_tracker_
.OnInvalidationsEnabled();
564 nudge_tracker_
.RecordSuccessfulSyncCycle();
566 ASSERT_FALSE(context_
->ShouldFetchUpdatesBeforeCommit());
567 ASSERT_FALSE(nudge_tracker_
.IsGetUpdatesRequired());
570 base::MessageLoop message_loop_
;
572 // Some ids to aid tests. Only the root one's value is specific. The rest
573 // are named for test clarity.
574 // TODO(chron): Get rid of these inbuilt IDs. They only make it
576 syncable::Id root_id_
;
577 syncable::Id parent_id_
;
578 syncable::Id child_id_
;
582 TestDirectorySetterUpper dir_maker_
;
583 FakeEncryptor encryptor_
;
584 scoped_refptr
<ExtensionsActivity
> extensions_activity_
;
585 scoped_ptr
<MockConnectionManager
> mock_server_
;
586 CancelationSignal cancelation_signal_
;
590 scoped_ptr
<SyncSession
> session_
;
591 TypeDebugInfoCache debug_info_cache_
;
592 MockNudgeHandler mock_nudge_handler_
;
593 scoped_ptr
<ModelTypeRegistry
> model_type_registry_
;
594 scoped_ptr
<SyncSchedulerImpl
> scheduler_
;
595 scoped_ptr
<SyncSessionContext
> context_
;
596 bool saw_syncer_event_
;
597 base::TimeDelta last_short_poll_interval_received_
;
598 base::TimeDelta last_long_poll_interval_received_
;
599 base::TimeDelta last_sessions_commit_delay_
;
600 base::TimeDelta last_bookmarks_commit_delay_
;
601 int last_client_invalidation_hint_buffer_size_
;
602 std::vector
<scoped_refptr
<ModelSafeWorker
> > workers_
;
604 ModelTypeSet enabled_datatypes_
;
605 sessions::NudgeTracker nudge_tracker_
;
606 scoped_ptr
<MockDebugInfoGetter
> debug_info_getter_
;
608 DISALLOW_COPY_AND_ASSIGN(SyncerTest
);
611 TEST_F(SyncerTest
, TestCallGatherUnsyncedEntries
) {
613 Syncer::UnsyncedMetaHandles handles
;
615 syncable::ReadTransaction
trans(FROM_HERE
, directory());
616 GetUnsyncedEntries(&trans
, &handles
);
618 ASSERT_EQ(0u, handles
.size());
620 // TODO(sync): When we can dynamically connect and disconnect the mock
621 // ServerConnectionManager test disconnected GetUnsyncedEntries here. It's a
622 // regression for a very old bug.
625 TEST_F(SyncerTest
, GetCommitIdsFiltersThrottledEntries
) {
626 const ModelTypeSet
throttled_types(BOOKMARKS
);
627 sync_pb::EntitySpecifics bookmark_data
;
628 AddDefaultFieldValue(BOOKMARKS
, &bookmark_data
);
630 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
631 foreign_cache_guid(), "-1");
635 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
636 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
637 ASSERT_TRUE(A
.good());
638 A
.PutIsUnsynced(true);
639 A
.PutSpecifics(bookmark_data
);
640 A
.PutNonUniqueName("bookmark");
643 // Now sync without enabling bookmarks.
644 mock_server_
->ExpectGetUpdatesRequestTypes(
645 Difference(context_
->GetEnabledTypes(), ModelTypeSet(BOOKMARKS
)));
647 syncer_
->NormalSyncShare(
648 Difference(context_
->GetEnabledTypes(), ModelTypeSet(BOOKMARKS
)),
649 &nudge_tracker_
, session_
.get());
652 // Nothing should have been committed as bookmarks is throttled.
653 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
654 Entry
entryA(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
655 ASSERT_TRUE(entryA
.good());
656 EXPECT_TRUE(entryA
.GetIsUnsynced());
659 // Sync again with bookmarks enabled.
660 mock_server_
->ExpectGetUpdatesRequestTypes(context_
->GetEnabledTypes());
663 // It should have been committed.
664 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
665 Entry
entryA(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
666 ASSERT_TRUE(entryA
.good());
667 EXPECT_FALSE(entryA
.GetIsUnsynced());
671 // We use a macro so we can preserve the error location.
672 #define VERIFY_ENTRY(id, is_unapplied, is_unsynced, prev_initialized, \
673 parent_id, version, server_version, id_fac, rtrans) \
675 Entry entryA(rtrans, syncable::GET_BY_ID, id_fac.FromNumber(id)); \
676 ASSERT_TRUE(entryA.good()); \
677 /* We don't use EXPECT_EQ here because when the left side param is false,
678 gcc 4.6 warns about converting 'false' to pointer type for argument 1. */ \
679 EXPECT_TRUE(is_unsynced == entryA.GetIsUnsynced()); \
680 EXPECT_TRUE(is_unapplied == entryA.GetIsUnappliedUpdate()); \
681 EXPECT_TRUE(prev_initialized == \
682 IsRealDataType(GetModelTypeFromSpecifics( \
683 entryA.GetBaseServerSpecifics()))); \
684 EXPECT_TRUE(parent_id == -1 || \
685 entryA.GetParentId()== id_fac.FromNumber(parent_id)); \
686 EXPECT_EQ(version, entryA.GetBaseVersion()); \
687 EXPECT_EQ(server_version, entryA.GetServerVersion()); \
690 TEST_F(SyncerTest
, GetCommitIdsFiltersUnreadyEntries
) {
691 KeyParams key_params
= {"localhost", "dummy", "foobar"};
692 KeyParams other_params
= {"localhost", "dummy", "foobar2"};
693 sync_pb::EntitySpecifics bookmark
, encrypted_bookmark
;
694 bookmark
.mutable_bookmark()->set_url("url");
695 bookmark
.mutable_bookmark()->set_title("title");
696 AddDefaultFieldValue(BOOKMARKS
, &encrypted_bookmark
);
697 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
698 foreign_cache_guid(), "-1");
699 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
700 foreign_cache_guid(), "-2");
701 mock_server_
->AddUpdateDirectory(3, 0, "C", 10, 10,
702 foreign_cache_guid(), "-3");
703 mock_server_
->AddUpdateDirectory(4, 0, "D", 10, 10,
704 foreign_cache_guid(), "-4");
706 // Server side change will put A in conflict.
707 mock_server_
->AddUpdateDirectory(1, 0, "A", 20, 20,
708 foreign_cache_guid(), "-1");
710 // Mark bookmarks as encrypted and set the cryptographer to have pending
712 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
713 Cryptographer
other_cryptographer(&encryptor_
);
714 other_cryptographer
.AddKey(other_params
);
715 sync_pb::EntitySpecifics specifics
;
716 sync_pb::NigoriSpecifics
* nigori
= specifics
.mutable_nigori();
717 other_cryptographer
.GetKeys(nigori
->mutable_encryption_keybag());
718 dir_maker_
.encryption_handler()->EnableEncryptEverything();
719 // Set up with an old passphrase, but have pending keys
720 GetCryptographer(&wtrans
)->AddKey(key_params
);
721 GetCryptographer(&wtrans
)->Encrypt(bookmark
,
722 encrypted_bookmark
.mutable_encrypted());
723 GetCryptographer(&wtrans
)->SetPendingKeys(nigori
->encryption_keybag());
725 // In conflict but properly encrypted.
726 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
727 ASSERT_TRUE(A
.good());
728 A
.PutIsUnsynced(true);
729 A
.PutSpecifics(encrypted_bookmark
);
730 A
.PutNonUniqueName(kEncryptedString
);
731 // Not in conflict and properly encrypted.
732 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
733 ASSERT_TRUE(B
.good());
734 B
.PutIsUnsynced(true);
735 B
.PutSpecifics(encrypted_bookmark
);
736 B
.PutNonUniqueName(kEncryptedString
);
737 // Unencrypted specifics.
738 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
739 ASSERT_TRUE(C
.good());
740 C
.PutIsUnsynced(true);
741 C
.PutNonUniqueName(kEncryptedString
);
742 // Unencrypted non_unique_name.
743 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
744 ASSERT_TRUE(D
.good());
745 D
.PutIsUnsynced(true);
746 D
.PutSpecifics(encrypted_bookmark
);
747 D
.PutNonUniqueName("not encrypted");
751 // Nothing should have commited due to bookmarks being encrypted and
752 // the cryptographer having pending keys. A would have been resolved
753 // as a simple conflict, but still be unsynced until the next sync cycle.
754 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
755 VERIFY_ENTRY(1, false, true, false, 0, 20, 20, ids_
, &rtrans
);
756 VERIFY_ENTRY(2, false, true, false, 0, 10, 10, ids_
, &rtrans
);
757 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_
, &rtrans
);
758 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_
, &rtrans
);
760 // Resolve the pending keys.
761 GetCryptographer(&rtrans
)->DecryptPendingKeys(other_params
);
765 // All properly encrypted and non-conflicting items should commit. "A" was
766 // conflicting, but last sync cycle resolved it as simple conflict, so on
767 // this sync cycle it committed succesfullly.
768 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
769 // Committed successfully.
770 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_
, &rtrans
);
771 // Committed successfully.
772 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_
, &rtrans
);
773 // Was not properly encrypted.
774 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_
, &rtrans
);
775 // Was not properly encrypted.
776 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_
, &rtrans
);
779 // Fix the remaining items.
780 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
781 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
782 ASSERT_TRUE(C
.good());
783 C
.PutSpecifics(encrypted_bookmark
);
784 C
.PutNonUniqueName(kEncryptedString
);
785 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
786 ASSERT_TRUE(D
.good());
787 D
.PutSpecifics(encrypted_bookmark
);
788 D
.PutNonUniqueName(kEncryptedString
);
792 const StatusController
& status_controller
= session_
->status_controller();
794 EXPECT_EQ(status_controller
.model_neutral_state().commit_result
, SYNCER_OK
);
795 // None should be unsynced anymore.
796 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
797 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_
, &rtrans
);
798 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_
, &rtrans
);
799 VERIFY_ENTRY(3, false, false, false, 0, 11, 11, ids_
, &rtrans
);
800 VERIFY_ENTRY(4, false, false, false, 0, 11, 11, ids_
, &rtrans
);
804 TEST_F(SyncerTest
, GetUpdatesPartialThrottled
) {
805 sync_pb::EntitySpecifics bookmark
, pref
;
806 bookmark
.mutable_bookmark()->set_title("title");
807 pref
.mutable_preference()->set_name("name");
808 AddDefaultFieldValue(BOOKMARKS
, &bookmark
);
809 AddDefaultFieldValue(PREFERENCES
, &pref
);
811 // Normal sync, all the data types should get synced.
812 mock_server_
->AddUpdateSpecifics(1, 0, "A", 10, 10, true, 0, bookmark
,
813 foreign_cache_guid(), "-1");
814 mock_server_
->AddUpdateSpecifics(2, 1, "B", 10, 10, false, 2, bookmark
,
815 foreign_cache_guid(), "-2");
816 mock_server_
->AddUpdateSpecifics(3, 1, "C", 10, 10, false, 1, bookmark
,
817 foreign_cache_guid(), "-3");
818 mock_server_
->AddUpdateSpecifics(4, 0, "D", 10, 10, false, 0, pref
);
822 // Initial state. Everything is normal.
823 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
824 VERIFY_ENTRY(1, false, false, false, 0, 10, 10, ids_
, &rtrans
);
825 VERIFY_ENTRY(2, false, false, false, 1, 10, 10, ids_
, &rtrans
);
826 VERIFY_ENTRY(3, false, false, false, 1, 10, 10, ids_
, &rtrans
);
827 VERIFY_ENTRY(4, false, false, false, 0, 10, 10, ids_
, &rtrans
);
830 // Set BOOKMARKS throttled but PREFERENCES not,
831 // then BOOKMARKS should not get synced but PREFERENCES should.
832 ModelTypeSet
throttled_types(BOOKMARKS
);
833 mock_server_
->set_partial_throttling(true);
834 mock_server_
->SetThrottledTypes(throttled_types
);
836 mock_server_
->AddUpdateSpecifics(1, 0, "E", 20, 20, true, 0, bookmark
,
837 foreign_cache_guid(), "-1");
838 mock_server_
->AddUpdateSpecifics(2, 1, "F", 20, 20, false, 2, bookmark
,
839 foreign_cache_guid(), "-2");
840 mock_server_
->AddUpdateSpecifics(3, 1, "G", 20, 20, false, 1, bookmark
,
841 foreign_cache_guid(), "-3");
842 mock_server_
->AddUpdateSpecifics(4, 0, "H", 20, 20, false, 0, pref
);
844 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
845 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
846 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
847 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
848 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
849 A
.PutIsUnsynced(true);
850 B
.PutIsUnsynced(true);
851 C
.PutIsUnsynced(true);
852 D
.PutIsUnsynced(true);
856 // BOOKMARKS throttled.
857 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
858 VERIFY_ENTRY(1, false, true, false, 0, 10, 10, ids_
, &rtrans
);
859 VERIFY_ENTRY(2, false, true, false, 1, 10, 10, ids_
, &rtrans
);
860 VERIFY_ENTRY(3, false, true, false, 1, 10, 10, ids_
, &rtrans
);
861 VERIFY_ENTRY(4, false, false, false, 0, 21, 21, ids_
, &rtrans
);
864 // Unthrottled BOOKMARKS, then BOOKMARKS should get synced now.
865 mock_server_
->set_partial_throttling(false);
867 mock_server_
->AddUpdateSpecifics(1, 0, "E", 30, 30, true, 0, bookmark
,
868 foreign_cache_guid(), "-1");
869 mock_server_
->AddUpdateSpecifics(2, 1, "F", 30, 30, false, 2, bookmark
,
870 foreign_cache_guid(), "-2");
871 mock_server_
->AddUpdateSpecifics(3, 1, "G", 30, 30, false, 1, bookmark
,
872 foreign_cache_guid(), "-3");
873 mock_server_
->AddUpdateSpecifics(4, 0, "H", 30, 30, false, 0, pref
);
876 // BOOKMARKS unthrottled.
877 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
878 VERIFY_ENTRY(1, false, false, false, 0, 31, 31, ids_
, &rtrans
);
879 VERIFY_ENTRY(2, false, false, false, 1, 31, 31, ids_
, &rtrans
);
880 VERIFY_ENTRY(3, false, false, false, 1, 31, 31, ids_
, &rtrans
);
881 VERIFY_ENTRY(4, false, false, false, 0, 30, 30, ids_
, &rtrans
);
885 // This test uses internal knowledge of the directory to test correctness of
886 // GetCommitIds. In almost every other test, the hierarchy is created from
887 // parent to child order, and so parents always have metahandles that are
888 // smaller than those of their children. This makes it very difficult to test
889 // some GetCommitIds edge cases, since it uses metahandle ordering as
891 TEST_F(SyncerTest
, GetCommitIds_VerifyDeletionCommitOrder
) {
893 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
895 // Create four bookmarks folders at the root node.
896 for (int i
= 1; i
< 5; ++i
) {
897 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "");
898 entry
.PutId(ids_
.FromNumber(i
));
899 entry
.PutIsDir(true);
900 entry
.PutBaseVersion(5);
901 entry
.PutServerVersion(5);
902 entry
.PutServerParentId(trans
.root_id());
903 entry
.PutServerIsDir(true);
904 entry
.PutIsUnsynced(true);
905 entry
.PutSpecifics(DefaultBookmarkSpecifics());
908 // Now iterate in reverse order make a hierarchy of them.
909 // While we're at it, also mark them as deleted.
910 syncable::Id parent_id
= trans
.root_id();
911 for (int i
= 4; i
> 0; --i
) {
912 MutableEntry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(i
));
913 entry
.PutParentId(parent_id
);
914 entry
.PutServerParentId(parent_id
);
915 entry
.PutIsDel(true);
916 parent_id
= ids_
.FromNumber(i
);
921 // Run GetCommitIds, the function being tested.
922 syncable::Directory::Metahandles result_handles
;
923 syncable::ReadTransaction
trans(FROM_HERE
, directory());
924 GetCommitIdsForType(&trans
, BOOKMARKS
, 100, &result_handles
);
926 // Now verify the output. We expect four results in child to parent order.
927 ASSERT_EQ(4U, result_handles
.size());
929 Entry
entry0(&trans
, GET_BY_HANDLE
, result_handles
[0]);
930 EXPECT_EQ(ids_
.FromNumber(1), entry0
.GetId());
932 Entry
entry1(&trans
, GET_BY_HANDLE
, result_handles
[1]);
933 EXPECT_EQ(ids_
.FromNumber(2), entry1
.GetId());
935 Entry
entry2(&trans
, GET_BY_HANDLE
, result_handles
[2]);
936 EXPECT_EQ(ids_
.FromNumber(3), entry2
.GetId());
938 Entry
entry3(&trans
, GET_BY_HANDLE
, result_handles
[3]);
939 EXPECT_EQ(ids_
.FromNumber(4), entry3
.GetId());
943 // Verify that if there are more deleted items than the maximum number of
944 // entries, child to parent order is still preserved.
945 TEST_F(SyncerTest
, GetCommitIds_VerifyDeletionCommitOrderMaxEntries
) {
947 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
949 // Create a bookmark tree with one root, two second level, and three third
950 // level bookmarks, all folders.
951 for (int i
= 1; i
<= 6; ++i
) {
952 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "");
953 entry
.PutId(ids_
.FromNumber(i
));
954 entry
.PutIsDir(true);
955 entry
.PutBaseVersion(5);
956 entry
.PutServerVersion(5);
957 entry
.PutParentId(ids_
.FromNumber(i
/2));
958 entry
.PutServerParentId(ids_
.FromNumber(i
/2));
959 entry
.PutServerIsDir(true);
960 entry
.PutIsUnsynced(true);
961 entry
.PutSpecifics(DefaultBookmarkSpecifics());
962 entry
.PutIsDel(true);
967 // Run GetCommitIds with a limit of 2 entries to commit.
968 syncable::Directory::Metahandles result_handles
;
969 syncable::ReadTransaction
trans(FROM_HERE
, directory());
970 GetCommitIdsForType(&trans
, BOOKMARKS
, 2, &result_handles
);
972 // Now verify the output. We expect two results in child to parent order
973 // (descending id order).
974 ASSERT_EQ(2U, result_handles
.size());
976 Entry
entry0(&trans
, GET_BY_HANDLE
, result_handles
[0]);
977 EXPECT_EQ(ids_
.FromNumber(6), entry0
.GetId());
979 Entry
entry1(&trans
, GET_BY_HANDLE
, result_handles
[1]);
980 EXPECT_EQ(ids_
.FromNumber(5), entry1
.GetId());
984 TEST_F(SyncerTest
, EncryptionAwareConflicts
) {
985 KeyParams key_params
= {"localhost", "dummy", "foobar"};
986 Cryptographer
other_cryptographer(&encryptor_
);
987 other_cryptographer
.AddKey(key_params
);
988 sync_pb::EntitySpecifics bookmark
, encrypted_bookmark
, modified_bookmark
;
989 bookmark
.mutable_bookmark()->set_title("title");
990 other_cryptographer
.Encrypt(bookmark
,
991 encrypted_bookmark
.mutable_encrypted());
992 AddDefaultFieldValue(BOOKMARKS
, &encrypted_bookmark
);
993 modified_bookmark
.mutable_bookmark()->set_title("title2");
994 other_cryptographer
.Encrypt(modified_bookmark
,
995 modified_bookmark
.mutable_encrypted());
996 sync_pb::EntitySpecifics pref
, encrypted_pref
, modified_pref
;
997 pref
.mutable_preference()->set_name("name");
998 AddDefaultFieldValue(PREFERENCES
, &encrypted_pref
);
999 other_cryptographer
.Encrypt(pref
,
1000 encrypted_pref
.mutable_encrypted());
1001 modified_pref
.mutable_preference()->set_name("name2");
1002 other_cryptographer
.Encrypt(modified_pref
,
1003 modified_pref
.mutable_encrypted());
1005 // Mark bookmarks and preferences as encrypted and set the cryptographer to
1006 // have pending keys.
1007 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1008 sync_pb::EntitySpecifics specifics
;
1009 sync_pb::NigoriSpecifics
* nigori
= specifics
.mutable_nigori();
1010 other_cryptographer
.GetKeys(nigori
->mutable_encryption_keybag());
1011 dir_maker_
.encryption_handler()->EnableEncryptEverything();
1012 GetCryptographer(&wtrans
)->SetPendingKeys(nigori
->encryption_keybag());
1013 EXPECT_TRUE(GetCryptographer(&wtrans
)->has_pending_keys());
1016 // We need to remember the exact position of our local items, so we can
1017 // make updates that do not modify those positions.
1018 UniquePosition pos1
;
1019 UniquePosition pos2
;
1020 UniquePosition pos3
;
1022 mock_server_
->AddUpdateSpecifics(1, 0, "A", 10, 10, true, 0, bookmark
,
1023 foreign_cache_guid(), "-1");
1024 mock_server_
->AddUpdateSpecifics(2, 1, "B", 10, 10, false, 2, bookmark
,
1025 foreign_cache_guid(), "-2");
1026 mock_server_
->AddUpdateSpecifics(3, 1, "C", 10, 10, false, 1, bookmark
,
1027 foreign_cache_guid(), "-3");
1028 mock_server_
->AddUpdateSpecifics(4, 0, "D", 10, 10, false, 0, pref
);
1031 // Initial state. Everything is normal.
1032 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1033 VERIFY_ENTRY(1, false, false, false, 0, 10, 10, ids_
, &rtrans
);
1034 VERIFY_ENTRY(2, false, false, false, 1, 10, 10, ids_
, &rtrans
);
1035 VERIFY_ENTRY(3, false, false, false, 1, 10, 10, ids_
, &rtrans
);
1036 VERIFY_ENTRY(4, false, false, false, 0, 10, 10, ids_
, &rtrans
);
1038 Entry
entry1(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
1039 ASSERT_TRUE(entry1
.GetUniquePosition().Equals(
1040 entry1
.GetServerUniquePosition()));
1041 pos1
= entry1
.GetUniquePosition();
1042 Entry
entry2(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(2));
1043 pos2
= entry2
.GetUniquePosition();
1044 Entry
entry3(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(3));
1045 pos3
= entry3
.GetUniquePosition();
1048 // Server side encryption will not be applied due to undecryptable data.
1049 // At this point, BASE_SERVER_SPECIFICS should be filled for all four items.
1050 mock_server_
->AddUpdateSpecifics(1, 0, kEncryptedString
, 20, 20, true, 0,
1052 foreign_cache_guid(), "-1");
1053 mock_server_
->AddUpdateSpecifics(2, 1, kEncryptedString
, 20, 20, false, 2,
1055 foreign_cache_guid(), "-2");
1056 mock_server_
->AddUpdateSpecifics(3, 1, kEncryptedString
, 20, 20, false, 1,
1058 foreign_cache_guid(), "-3");
1059 mock_server_
->AddUpdateSpecifics(4, 0, kEncryptedString
, 20, 20, false, 0,
1061 foreign_cache_guid(), "-4");
1064 // All should be unapplied due to being undecryptable and have a valid
1065 // BASE_SERVER_SPECIFICS.
1066 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1067 VERIFY_ENTRY(1, true, false, true, 0, 10, 20, ids_
, &rtrans
);
1068 VERIFY_ENTRY(2, true, false, true, 1, 10, 20, ids_
, &rtrans
);
1069 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_
, &rtrans
);
1070 VERIFY_ENTRY(4, true, false, true, 0, 10, 20, ids_
, &rtrans
);
1073 // Server side change that don't modify anything should not affect
1074 // BASE_SERVER_SPECIFICS (such as name changes and mtime changes).
1075 mock_server_
->AddUpdateSpecifics(1, 0, kEncryptedString
, 30, 30, true, 0,
1077 foreign_cache_guid(), "-1");
1078 mock_server_
->AddUpdateSpecifics(2, 1, kEncryptedString
, 30, 30, false, 2,
1080 foreign_cache_guid(), "-2");
1081 // Item 3 doesn't change.
1082 mock_server_
->AddUpdateSpecifics(4, 0, kEncryptedString
, 30, 30, false, 0,
1084 foreign_cache_guid(), "-4");
1087 // Items 1, 2, and 4 should have newer server versions, 3 remains the same.
1088 // All should remain unapplied due to be undecryptable.
1089 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1090 VERIFY_ENTRY(1, true, false, true, 0, 10, 30, ids_
, &rtrans
);
1091 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_
, &rtrans
);
1092 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_
, &rtrans
);
1093 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_
, &rtrans
);
1096 // Positional changes, parent changes, and specifics changes should reset
1097 // BASE_SERVER_SPECIFICS.
1098 // Became unencrypted.
1099 mock_server_
->AddUpdateSpecifics(1, 0, "A", 40, 40, true, 0, bookmark
,
1100 foreign_cache_guid(), "-1");
1101 // Reordered to after item 2.
1102 mock_server_
->AddUpdateSpecifics(3, 1, kEncryptedString
, 30, 30, false, 3,
1104 foreign_cache_guid(), "-3");
1107 // Items 2 and 4 should be the only ones with BASE_SERVER_SPECIFICS set.
1108 // Items 1 is now unencrypted, so should have applied normally.
1109 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1110 VERIFY_ENTRY(1, false, false, false, 0, 40, 40, ids_
, &rtrans
);
1111 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_
, &rtrans
);
1112 VERIFY_ENTRY(3, true, false, false, 1, 10, 30, ids_
, &rtrans
);
1113 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_
, &rtrans
);
1116 // Make local changes, which should remain unsynced for items 2, 3, 4.
1118 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1119 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
1120 ASSERT_TRUE(A
.good());
1121 A
.PutSpecifics(modified_bookmark
);
1122 A
.PutNonUniqueName(kEncryptedString
);
1123 A
.PutIsUnsynced(true);
1124 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
1125 ASSERT_TRUE(B
.good());
1126 B
.PutSpecifics(modified_bookmark
);
1127 B
.PutNonUniqueName(kEncryptedString
);
1128 B
.PutIsUnsynced(true);
1129 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
1130 ASSERT_TRUE(C
.good());
1131 C
.PutSpecifics(modified_bookmark
);
1132 C
.PutNonUniqueName(kEncryptedString
);
1133 C
.PutIsUnsynced(true);
1134 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
1135 ASSERT_TRUE(D
.good());
1136 D
.PutSpecifics(modified_pref
);
1137 D
.PutNonUniqueName(kEncryptedString
);
1138 D
.PutIsUnsynced(true);
1142 // Item 1 remains unsynced due to there being pending keys.
1143 // Items 2, 3, 4 should remain unsynced since they were not up to date.
1144 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1145 VERIFY_ENTRY(1, false, true, false, 0, 40, 40, ids_
, &rtrans
);
1146 VERIFY_ENTRY(2, true, true, true, 1, 10, 30, ids_
, &rtrans
);
1147 VERIFY_ENTRY(3, true, true, false, 1, 10, 30, ids_
, &rtrans
);
1148 VERIFY_ENTRY(4, true, true, true, 0, 10, 30, ids_
, &rtrans
);
1152 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1153 // Resolve the pending keys.
1154 GetCryptographer(&rtrans
)->DecryptPendingKeys(key_params
);
1156 // First cycle resolves conflicts, second cycle commits changes.
1158 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_server_overwrites
);
1159 EXPECT_EQ(1, GetUpdateCounters(PREFERENCES
).num_server_overwrites
);
1160 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_local_overwrites
);
1162 // We successfully commited item(s).
1163 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS
).num_commits_attempted
);
1164 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS
).num_commits_success
);
1165 EXPECT_EQ(1, GetCommitCounters(PREFERENCES
).num_commits_attempted
);
1166 EXPECT_EQ(1, GetCommitCounters(PREFERENCES
).num_commits_success
);
1170 // Everything should be resolved now. The local changes should have
1171 // overwritten the server changes for 2 and 4, while the server changes
1172 // overwrote the local for entry 3.
1174 // Expect there will be no new overwrites.
1175 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_server_overwrites
);
1176 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_local_overwrites
);
1178 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS
).num_commits_success
);
1179 EXPECT_EQ(1, GetCommitCounters(PREFERENCES
).num_commits_success
);
1181 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1182 VERIFY_ENTRY(1, false, false, false, 0, 41, 41, ids_
, &rtrans
);
1183 VERIFY_ENTRY(2, false, false, false, 1, 31, 31, ids_
, &rtrans
);
1184 VERIFY_ENTRY(3, false, false, false, 1, 30, 30, ids_
, &rtrans
);
1185 VERIFY_ENTRY(4, false, false, false, 0, 31, 31, ids_
, &rtrans
);
1190 TEST_F(SyncerTest
, TestGetUnsyncedAndSimpleCommit
) {
1192 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1193 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1194 ASSERT_TRUE(parent
.good());
1195 parent
.PutIsUnsynced(true);
1196 parent
.PutIsDir(true);
1197 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1198 parent
.PutBaseVersion(1);
1199 parent
.PutId(parent_id_
);
1200 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete");
1201 ASSERT_TRUE(child
.good());
1202 child
.PutId(child_id_
);
1203 child
.PutBaseVersion(1);
1204 WriteTestDataToEntry(&wtrans
, &child
);
1208 ASSERT_EQ(2u, mock_server_
->committed_ids().size());
1209 // If this test starts failing, be aware other sort orders could be valid.
1210 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1211 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1213 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1214 Entry
entry(&rt
, syncable::GET_BY_ID
, child_id_
);
1215 ASSERT_TRUE(entry
.good());
1216 VerifyTestDataInEntry(&rt
, &entry
);
1220 TEST_F(SyncerTest
, TestPurgeWhileUnsynced
) {
1221 // Similar to above, but throw a purge operation into the mix. Bug 49278.
1222 syncable::Id pref_node_id
= TestIdFactory::MakeServer("Tim");
1224 directory()->SetDownloadProgress(BOOKMARKS
,
1225 syncable::BuildProgress(BOOKMARKS
));
1226 directory()->SetDownloadProgress(PREFERENCES
,
1227 syncable::BuildProgress(PREFERENCES
));
1228 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1229 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1230 ASSERT_TRUE(parent
.good());
1231 parent
.PutIsUnsynced(true);
1232 parent
.PutIsDir(true);
1233 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1234 parent
.PutBaseVersion(1);
1235 parent
.PutId(parent_id_
);
1236 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete");
1237 ASSERT_TRUE(child
.good());
1238 child
.PutId(child_id_
);
1239 child
.PutBaseVersion(1);
1240 WriteTestDataToEntry(&wtrans
, &child
);
1242 MutableEntry
parent2(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Tim");
1243 ASSERT_TRUE(parent2
.good());
1244 parent2
.PutIsUnsynced(true);
1245 parent2
.PutIsDir(true);
1246 parent2
.PutSpecifics(DefaultPreferencesSpecifics());
1247 parent2
.PutBaseVersion(1);
1248 parent2
.PutId(pref_node_id
);
1251 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES
),
1256 ASSERT_EQ(2U, mock_server_
->committed_ids().size());
1257 // If this test starts failing, be aware other sort orders could be valid.
1258 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1259 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1261 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1262 Entry
entry(&rt
, syncable::GET_BY_ID
, child_id_
);
1263 ASSERT_TRUE(entry
.good());
1264 VerifyTestDataInEntry(&rt
, &entry
);
1266 directory()->SaveChanges();
1268 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1269 Entry
entry(&rt
, syncable::GET_BY_ID
, pref_node_id
);
1270 ASSERT_FALSE(entry
.good());
1274 TEST_F(SyncerTest
, TestPurgeWhileUnapplied
) {
1275 // Similar to above, but for unapplied items. Bug 49278.
1277 directory()->SetDownloadProgress(BOOKMARKS
,
1278 syncable::BuildProgress(BOOKMARKS
));
1279 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1280 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1281 ASSERT_TRUE(parent
.good());
1282 parent
.PutIsUnappliedUpdate(true);
1283 parent
.PutIsDir(true);
1284 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1285 parent
.PutBaseVersion(1);
1286 parent
.PutId(parent_id_
);
1289 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(BOOKMARKS
),
1294 directory()->SaveChanges();
1296 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1297 Entry
entry(&rt
, syncable::GET_BY_ID
, parent_id_
);
1298 ASSERT_FALSE(entry
.good());
1302 TEST_F(SyncerTest
, TestPurgeWithJournal
) {
1304 directory()->SetDownloadProgress(BOOKMARKS
,
1305 syncable::BuildProgress(BOOKMARKS
));
1306 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1307 MutableEntry
parent(&wtrans
, syncable::CREATE
, BOOKMARKS
, wtrans
.root_id(),
1309 ASSERT_TRUE(parent
.good());
1310 parent
.PutIsDir(true);
1311 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1312 parent
.PutBaseVersion(1);
1313 parent
.PutId(parent_id_
);
1314 MutableEntry
child(&wtrans
, syncable::CREATE
, BOOKMARKS
, parent_id_
,
1316 ASSERT_TRUE(child
.good());
1317 child
.PutId(child_id_
);
1318 child
.PutBaseVersion(1);
1319 WriteTestDataToEntry(&wtrans
, &child
);
1321 MutableEntry
parent2(&wtrans
, syncable::CREATE
, PREFERENCES
,
1322 wtrans
.root_id(), "Tim");
1323 ASSERT_TRUE(parent2
.good());
1324 parent2
.PutIsDir(true);
1325 parent2
.PutSpecifics(DefaultPreferencesSpecifics());
1326 parent2
.PutBaseVersion(1);
1327 parent2
.PutId(TestIdFactory::MakeServer("Tim"));
1330 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES
, BOOKMARKS
),
1331 ModelTypeSet(BOOKMARKS
),
1334 // Verify bookmark nodes are saved in delete journal but not preference
1336 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1337 syncable::DeleteJournal
* delete_journal
= directory()->delete_journal();
1338 EXPECT_EQ(2u, delete_journal
->GetDeleteJournalSize(&rt
));
1339 syncable::EntryKernelSet journal_entries
;
1340 directory()->delete_journal()->GetDeleteJournals(&rt
, BOOKMARKS
,
1342 EXPECT_EQ(parent_id_
, (*journal_entries
.begin())->ref(syncable::ID
));
1343 EXPECT_EQ(child_id_
, (*journal_entries
.rbegin())->ref(syncable::ID
));
1347 TEST_F(SyncerTest
, ResetVersions
) {
1348 // Download some pref items.
1349 mock_server_
->AddUpdatePref("id1", "", "tag1", 20, 20);
1350 mock_server_
->AddUpdatePref("id2", "", "tag2", 30, 30);
1351 mock_server_
->AddUpdatePref("id3", "", "tag3", 40, 40);
1355 // Modify one of the preferences locally, mark another one as unapplied,
1356 // and create another unsynced preference.
1357 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1358 MutableEntry
entry(&wtrans
, GET_BY_CLIENT_TAG
, "tag1");
1359 entry
.PutIsUnsynced(true);
1361 MutableEntry
entry2(&wtrans
, GET_BY_CLIENT_TAG
, "tag2");
1362 entry2
.PutIsUnappliedUpdate(true);
1364 MutableEntry
entry4(&wtrans
, CREATE
, PREFERENCES
, "name");
1365 entry4
.PutUniqueClientTag("tag4");
1366 entry4
.PutIsUnsynced(true);
1370 // Reset the versions.
1371 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1372 ASSERT_TRUE(directory()->ResetVersionsForType(&wtrans
, PREFERENCES
));
1376 // Verify the synced items are all with version 1 now, with
1377 // unsynced/unapplied state preserved.
1378 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1379 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, "tag1");
1380 EXPECT_EQ(1, entry
.GetBaseVersion());
1381 EXPECT_EQ(1, entry
.GetServerVersion());
1382 EXPECT_TRUE(entry
.GetIsUnsynced());
1383 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
1384 Entry
entry2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
1385 EXPECT_EQ(1, entry2
.GetBaseVersion());
1386 EXPECT_EQ(1, entry2
.GetServerVersion());
1387 EXPECT_FALSE(entry2
.GetIsUnsynced());
1388 EXPECT_TRUE(entry2
.GetIsUnappliedUpdate());
1389 Entry
entry3(&trans
, GET_BY_CLIENT_TAG
, "tag3");
1390 EXPECT_EQ(1, entry3
.GetBaseVersion());
1391 EXPECT_EQ(1, entry3
.GetServerVersion());
1392 EXPECT_FALSE(entry3
.GetIsUnsynced());
1393 EXPECT_FALSE(entry3
.GetIsUnappliedUpdate());
1395 // Entry 4 (the locally created one) should remain the same.
1396 Entry
entry4(&trans
, GET_BY_CLIENT_TAG
, "tag4");
1397 EXPECT_EQ(-1, entry4
.GetBaseVersion());
1398 EXPECT_EQ(0, entry4
.GetServerVersion());
1399 EXPECT_TRUE(entry4
.GetIsUnsynced());
1400 EXPECT_FALSE(entry4
.GetIsUnappliedUpdate());
1404 TEST_F(SyncerTest
, TestCommitListOrderingTwoItemsTall
) {
1405 CommitOrderingTest items
[] = {
1406 {1, ids_
.FromNumber(-1001), ids_
.FromNumber(-1000)},
1407 {0, ids_
.FromNumber(-1000), ids_
.FromNumber(0)},
1408 CommitOrderingTest::MakeLastCommitItem(),
1410 RunCommitOrderingTest(items
);
1413 TEST_F(SyncerTest
, TestCommitListOrderingThreeItemsTall
) {
1414 CommitOrderingTest items
[] = {
1415 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1416 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1417 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1418 CommitOrderingTest::MakeLastCommitItem(),
1420 RunCommitOrderingTest(items
);
1423 TEST_F(SyncerTest
, TestCommitListOrderingFourItemsTall
) {
1424 CommitOrderingTest items
[] = {
1425 {3, ids_
.FromNumber(-2003), ids_
.FromNumber(-2002)},
1426 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1427 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1428 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1429 CommitOrderingTest::MakeLastCommitItem(),
1431 RunCommitOrderingTest(items
);
1434 TEST_F(SyncerTest
, TestCommitListOrderingThreeItemsTallLimitedSize
) {
1435 context_
->set_max_commit_batch_size(2);
1436 CommitOrderingTest items
[] = {
1437 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1438 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1439 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1440 CommitOrderingTest::MakeLastCommitItem(),
1442 RunCommitOrderingTest(items
);
1445 TEST_F(SyncerTest
, TestCommitListOrderingSingleDeletedItem
) {
1446 CommitOrderingTest items
[] = {
1447 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1448 CommitOrderingTest::MakeLastCommitItem(),
1450 RunCommitOrderingTest(items
);
1453 TEST_F(SyncerTest
, TestCommitListOrderingSingleUncommittedDeletedItem
) {
1454 CommitOrderingTest items
[] = {
1455 {-1, ids_
.FromNumber(-1000), ids_
.FromNumber(0), {DELETED
}},
1456 CommitOrderingTest::MakeLastCommitItem(),
1458 RunCommitOrderingTest(items
);
1461 TEST_F(SyncerTest
, TestCommitListOrderingSingleDeletedItemWithUnroll
) {
1462 CommitOrderingTest items
[] = {
1463 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1464 CommitOrderingTest::MakeLastCommitItem(),
1466 RunCommitOrderingTest(items
);
1470 TestCommitListOrderingSingleLongDeletedItemWithUnroll
) {
1471 CommitOrderingTest items
[] = {
1472 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1473 CommitOrderingTest::MakeLastCommitItem(),
1475 RunCommitOrderingTest(items
);
1478 TEST_F(SyncerTest
, TestCommitListOrderingTwoLongDeletedItemWithUnroll
) {
1479 CommitOrderingTest items
[] = {
1480 {1, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1481 {0, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
, OLD_MTIME
}},
1482 CommitOrderingTest::MakeLastCommitItem(),
1484 RunCommitOrderingTest(items
);
1487 TEST_F(SyncerTest
, TestCommitListOrdering3LongDeletedItemsWithSizeLimit
) {
1488 context_
->set_max_commit_batch_size(2);
1489 CommitOrderingTest items
[] = {
1490 {2, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1491 {1, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
, OLD_MTIME
}},
1492 {0, ids_
.FromNumber(1002), ids_
.FromNumber(1001), {DELETED
, OLD_MTIME
}},
1493 CommitOrderingTest::MakeLastCommitItem(),
1495 RunCommitOrderingTest(items
);
1498 TEST_F(SyncerTest
, TestCommitListOrderingTwoDeletedItemsWithUnroll
) {
1499 CommitOrderingTest items
[] = {
1500 {1, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1501 {0, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
}},
1502 CommitOrderingTest::MakeLastCommitItem(),
1504 RunCommitOrderingTest(items
);
1507 TEST_F(SyncerTest
, TestCommitListOrderingComplexDeletionScenario
) {
1508 CommitOrderingTest items
[] = {
1509 {2, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1510 {-1, ids_
.FromNumber(1001), ids_
.FromNumber(0), {SYNCED
}},
1511 {1, ids_
.FromNumber(1002), ids_
.FromNumber(1001), {DELETED
, OLD_MTIME
}},
1512 {-1, ids_
.FromNumber(1003), ids_
.FromNumber(1001), {SYNCED
}},
1513 {0, ids_
.FromNumber(1004), ids_
.FromNumber(1003), {DELETED
}},
1514 CommitOrderingTest::MakeLastCommitItem(),
1516 RunCommitOrderingTest(items
);
1520 TestCommitListOrderingComplexDeletionScenarioWith2RecentDeletes
) {
1521 CommitOrderingTest items
[] = {
1522 {3, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1523 {-1, ids_
.FromNumber(1001), ids_
.FromNumber(0), {SYNCED
}},
1524 {2, ids_
.FromNumber(1002), ids_
.FromNumber(1001), {DELETED
, OLD_MTIME
}},
1525 {-1, ids_
.FromNumber(1003), ids_
.FromNumber(1001), {SYNCED
}},
1526 {1, ids_
.FromNumber(1004), ids_
.FromNumber(1003), {DELETED
}},
1527 {0, ids_
.FromNumber(1005), ids_
.FromNumber(1003), {DELETED
}},
1528 CommitOrderingTest::MakeLastCommitItem(),
1530 RunCommitOrderingTest(items
);
1533 TEST_F(SyncerTest
, TestCommitListOrderingDeleteMovedItems
) {
1534 CommitOrderingTest items
[] = {
1535 {1, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1536 {0, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
, OLD_MTIME
,
1538 CommitOrderingTest::MakeLastCommitItem(),
1540 RunCommitOrderingTest(items
);
1543 TEST_F(SyncerTest
, TestCommitListOrderingWithNesting
) {
1544 const base::Time
& now_minus_2h
=
1545 base::Time::Now() - base::TimeDelta::FromHours(2);
1547 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1549 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bob");
1550 ASSERT_TRUE(parent
.good());
1551 parent
.PutIsUnsynced(true);
1552 parent
.PutIsDir(true);
1553 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1554 parent
.PutId(ids_
.FromNumber(100));
1555 parent
.PutBaseVersion(1);
1557 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(100), "Bob");
1558 ASSERT_TRUE(child
.good());
1559 child
.PutIsUnsynced(true);
1560 child
.PutIsDir(true);
1561 child
.PutSpecifics(DefaultBookmarkSpecifics());
1562 child
.PutId(ids_
.FromNumber(101));
1563 child
.PutBaseVersion(1);
1564 MutableEntry
grandchild(
1565 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(101), "Bob");
1566 ASSERT_TRUE(grandchild
.good());
1567 grandchild
.PutId(ids_
.FromNumber(102));
1568 grandchild
.PutIsUnsynced(true);
1569 grandchild
.PutSpecifics(DefaultBookmarkSpecifics());
1570 grandchild
.PutBaseVersion(1);
1573 // Create three deleted items which deletions we expect to be sent to the
1575 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1576 ASSERT_TRUE(parent
.good());
1577 parent
.PutId(ids_
.FromNumber(103));
1578 parent
.PutIsUnsynced(true);
1579 parent
.PutIsDir(true);
1580 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1581 parent
.PutIsDel(true);
1582 parent
.PutBaseVersion(1);
1583 parent
.PutMtime(now_minus_2h
);
1585 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(103), "Pete");
1586 ASSERT_TRUE(child
.good());
1587 child
.PutId(ids_
.FromNumber(104));
1588 child
.PutIsUnsynced(true);
1589 child
.PutIsDir(true);
1590 child
.PutSpecifics(DefaultBookmarkSpecifics());
1591 child
.PutIsDel(true);
1592 child
.PutBaseVersion(1);
1593 child
.PutMtime(now_minus_2h
);
1594 MutableEntry
grandchild(
1595 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(104), "Pete");
1596 ASSERT_TRUE(grandchild
.good());
1597 grandchild
.PutId(ids_
.FromNumber(105));
1598 grandchild
.PutIsUnsynced(true);
1599 grandchild
.PutIsDel(true);
1600 grandchild
.PutIsDir(false);
1601 grandchild
.PutSpecifics(DefaultBookmarkSpecifics());
1602 grandchild
.PutBaseVersion(1);
1603 grandchild
.PutMtime(now_minus_2h
);
1608 ASSERT_EQ(6u, mock_server_
->committed_ids().size());
1609 // This test will NOT unroll deletes because SERVER_PARENT_ID is not set.
1610 // It will treat these like moves.
1611 vector
<syncable::Id
> commit_ids(mock_server_
->committed_ids());
1612 EXPECT_TRUE(ids_
.FromNumber(100) == commit_ids
[0]);
1613 EXPECT_TRUE(ids_
.FromNumber(101) == commit_ids
[1]);
1614 EXPECT_TRUE(ids_
.FromNumber(102) == commit_ids
[2]);
1615 // We don't guarantee the delete orders in this test, only that they occur
1617 std::sort(commit_ids
.begin() + 3, commit_ids
.end());
1618 EXPECT_TRUE(ids_
.FromNumber(103) == commit_ids
[3]);
1619 EXPECT_TRUE(ids_
.FromNumber(104) == commit_ids
[4]);
1620 EXPECT_TRUE(ids_
.FromNumber(105) == commit_ids
[5]);
1623 TEST_F(SyncerTest
, TestCommitListOrderingWithNewItems
) {
1624 syncable::Id parent1_id
= ids_
.MakeServer("p1");
1625 syncable::Id parent2_id
= ids_
.MakeServer("p2");
1628 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1629 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "1");
1630 ASSERT_TRUE(parent
.good());
1631 parent
.PutIsUnsynced(true);
1632 parent
.PutIsDir(true);
1633 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1634 parent
.PutId(parent1_id
);
1635 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "2");
1636 ASSERT_TRUE(child
.good());
1637 child
.PutIsUnsynced(true);
1638 child
.PutIsDir(true);
1639 child
.PutSpecifics(DefaultBookmarkSpecifics());
1640 child
.PutId(parent2_id
);
1641 parent
.PutBaseVersion(1);
1642 child
.PutBaseVersion(1);
1645 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1646 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, parent1_id
, "A");
1647 ASSERT_TRUE(parent
.good());
1648 parent
.PutIsUnsynced(true);
1649 parent
.PutIsDir(true);
1650 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1651 parent
.PutId(ids_
.FromNumber(102));
1652 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent1_id
, "B");
1653 ASSERT_TRUE(child
.good());
1654 child
.PutIsUnsynced(true);
1655 child
.PutIsDir(true);
1656 child
.PutSpecifics(DefaultBookmarkSpecifics());
1657 child
.PutId(ids_
.FromNumber(-103));
1658 parent
.PutBaseVersion(1);
1661 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1662 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, parent2_id
, "A");
1663 ASSERT_TRUE(parent
.good());
1664 parent
.PutIsUnsynced(true);
1665 parent
.PutIsDir(true);
1666 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1667 parent
.PutId(ids_
.FromNumber(-104));
1668 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent2_id
, "B");
1669 ASSERT_TRUE(child
.good());
1670 child
.PutIsUnsynced(true);
1671 child
.PutIsDir(true);
1672 child
.PutSpecifics(DefaultBookmarkSpecifics());
1673 child
.PutId(ids_
.FromNumber(105));
1674 child
.PutBaseVersion(1);
1678 ASSERT_EQ(6u, mock_server_
->committed_ids().size());
1680 // This strange iteration and std::count() usage is to allow the order to
1681 // vary. All we really care about is that parent1_id and parent2_id are the
1682 // first two IDs, and that the children make up the next four. Other than
1683 // that, ordering doesn't matter.
1685 vector
<syncable::Id
>::const_iterator i
=
1686 mock_server_
->committed_ids().begin();
1687 vector
<syncable::Id
>::const_iterator parents_begin
= i
;
1690 vector
<syncable::Id
>::const_iterator parents_end
= i
;
1691 vector
<syncable::Id
>::const_iterator children_begin
= i
;
1692 vector
<syncable::Id
>::const_iterator children_end
=
1693 mock_server_
->committed_ids().end();
1695 EXPECT_EQ(1, count(parents_begin
, parents_end
, parent1_id
));
1696 EXPECT_EQ(1, count(parents_begin
, parents_end
, parent2_id
));
1698 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(-103)));
1699 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(102)));
1700 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(105)));
1701 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(-104)));
1704 TEST_F(SyncerTest
, TestCommitListOrderingCounterexample
) {
1705 syncable::Id child2_id
= ids_
.NewServerId();
1708 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1709 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "P");
1710 ASSERT_TRUE(parent
.good());
1711 parent
.PutIsUnsynced(true);
1712 parent
.PutIsDir(true);
1713 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1714 parent
.PutId(parent_id_
);
1715 MutableEntry
child1(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "1");
1716 ASSERT_TRUE(child1
.good());
1717 child1
.PutIsUnsynced(true);
1718 child1
.PutId(child_id_
);
1719 child1
.PutSpecifics(DefaultBookmarkSpecifics());
1720 MutableEntry
child2(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "2");
1721 ASSERT_TRUE(child2
.good());
1722 child2
.PutIsUnsynced(true);
1723 child2
.PutSpecifics(DefaultBookmarkSpecifics());
1724 child2
.PutId(child2_id
);
1726 parent
.PutBaseVersion(1);
1727 child1
.PutBaseVersion(1);
1728 child2
.PutBaseVersion(1);
1732 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1733 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1734 // There are two possible valid orderings.
1735 if (child2_id
== mock_server_
->committed_ids()[1]) {
1736 EXPECT_TRUE(child2_id
== mock_server_
->committed_ids()[1]);
1737 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[2]);
1739 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1740 EXPECT_TRUE(child2_id
== mock_server_
->committed_ids()[2]);
1744 TEST_F(SyncerTest
, TestCommitListOrderingAndNewParent
) {
1745 string parent1_name
= "1";
1746 string parent2_name
= "A";
1747 string child_name
= "B";
1750 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1751 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(),
1753 ASSERT_TRUE(parent
.good());
1754 parent
.PutIsUnsynced(true);
1755 parent
.PutIsDir(true);
1756 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1757 parent
.PutId(parent_id_
);
1758 parent
.PutBaseVersion(1);
1761 syncable::Id parent2_id
= ids_
.NewLocalId();
1762 syncable::Id child_id
= ids_
.NewServerId();
1764 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1765 MutableEntry
parent2(
1766 &wtrans
, CREATE
, BOOKMARKS
, parent_id_
, parent2_name
);
1767 ASSERT_TRUE(parent2
.good());
1768 parent2
.PutIsUnsynced(true);
1769 parent2
.PutIsDir(true);
1770 parent2
.PutSpecifics(DefaultBookmarkSpecifics());
1771 parent2
.PutId(parent2_id
);
1774 &wtrans
, CREATE
, BOOKMARKS
, parent2_id
, child_name
);
1775 ASSERT_TRUE(child
.good());
1776 child
.PutIsUnsynced(true);
1777 child
.PutIsDir(true);
1778 child
.PutSpecifics(DefaultBookmarkSpecifics());
1779 child
.PutId(child_id
);
1780 child
.PutBaseVersion(1);
1784 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1785 // If this test starts failing, be aware other sort orders could be valid.
1786 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1787 EXPECT_TRUE(parent2_id
== mock_server_
->committed_ids()[1]);
1788 EXPECT_TRUE(child_id
== mock_server_
->committed_ids()[2]);
1790 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1791 // Check that things committed correctly.
1792 Entry
entry_1(&rtrans
, syncable::GET_BY_ID
, parent_id_
);
1793 EXPECT_EQ(entry_1
.GetNonUniqueName(), parent1_name
);
1794 // Check that parent2 is a subfolder of parent1.
1795 EXPECT_EQ(1, CountEntriesWithName(&rtrans
,
1799 // Parent2 was a local ID and thus should have changed on commit!
1800 Entry
pre_commit_entry_parent2(&rtrans
, syncable::GET_BY_ID
, parent2_id
);
1801 ASSERT_FALSE(pre_commit_entry_parent2
.good());
1803 // Look up the new ID.
1804 Id parent2_committed_id
=
1805 GetOnlyEntryWithName(&rtrans
, parent_id_
, parent2_name
);
1806 EXPECT_TRUE(parent2_committed_id
.ServerKnows());
1808 Entry
child(&rtrans
, syncable::GET_BY_ID
, child_id
);
1809 EXPECT_EQ(parent2_committed_id
, child
.GetParentId());
1813 TEST_F(SyncerTest
, TestCommitListOrderingAndNewParentAndChild
) {
1814 string parent_name
= "1";
1815 string parent2_name
= "A";
1816 string child_name
= "B";
1819 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1820 MutableEntry
parent(&wtrans
,
1824 ASSERT_TRUE(parent
.good());
1825 parent
.PutIsUnsynced(true);
1826 parent
.PutIsDir(true);
1827 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1828 parent
.PutId(parent_id_
);
1829 parent
.PutBaseVersion(1);
1832 int64 meta_handle_b
;
1833 const Id parent2_local_id
= ids_
.NewLocalId();
1834 const Id child_local_id
= ids_
.NewLocalId();
1836 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1837 MutableEntry
parent2(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, parent2_name
);
1838 ASSERT_TRUE(parent2
.good());
1839 parent2
.PutIsUnsynced(true);
1840 parent2
.PutIsDir(true);
1841 parent2
.PutSpecifics(DefaultBookmarkSpecifics());
1843 parent2
.PutId(parent2_local_id
);
1845 &wtrans
, CREATE
, BOOKMARKS
, parent2_local_id
, child_name
);
1846 ASSERT_TRUE(child
.good());
1847 child
.PutIsUnsynced(true);
1848 child
.PutIsDir(true);
1849 child
.PutSpecifics(DefaultBookmarkSpecifics());
1850 child
.PutId(child_local_id
);
1851 meta_handle_b
= child
.GetMetahandle();
1855 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1856 // If this test starts failing, be aware other sort orders could be valid.
1857 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1858 EXPECT_TRUE(parent2_local_id
== mock_server_
->committed_ids()[1]);
1859 EXPECT_TRUE(child_local_id
== mock_server_
->committed_ids()[2]);
1861 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1863 Entry
parent(&rtrans
, syncable::GET_BY_ID
,
1864 GetOnlyEntryWithName(&rtrans
, rtrans
.root_id(), parent_name
));
1865 ASSERT_TRUE(parent
.good());
1866 EXPECT_TRUE(parent
.GetId().ServerKnows());
1868 Entry
parent2(&rtrans
, syncable::GET_BY_ID
,
1869 GetOnlyEntryWithName(&rtrans
, parent
.GetId(), parent2_name
));
1870 ASSERT_TRUE(parent2
.good());
1871 EXPECT_TRUE(parent2
.GetId().ServerKnows());
1873 // Id changed on commit, so this should fail.
1874 Entry
local_parent2_id_entry(&rtrans
,
1875 syncable::GET_BY_ID
,
1877 ASSERT_FALSE(local_parent2_id_entry
.good());
1879 Entry
entry_b(&rtrans
, syncable::GET_BY_HANDLE
, meta_handle_b
);
1880 EXPECT_TRUE(entry_b
.GetId().ServerKnows());
1881 EXPECT_TRUE(parent2
.GetId()== entry_b
.GetParentId());
1885 TEST_F(SyncerTest
, UpdateWithZeroLengthName
) {
1886 // One illegal update
1887 mock_server_
->AddUpdateDirectory(
1888 1, 0, std::string(), 1, 10, foreign_cache_guid(), "-1");
1889 // And one legal one that we're going to delete.
1890 mock_server_
->AddUpdateDirectory(2, 0, "FOO", 1, 10,
1891 foreign_cache_guid(), "-2");
1893 // Delete the legal one. The new update has a null name.
1894 mock_server_
->AddUpdateDirectory(
1895 2, 0, std::string(), 2, 20, foreign_cache_guid(), "-2");
1896 mock_server_
->SetLastUpdateDeleted();
1900 TEST_F(SyncerTest
, TestBasicUpdate
) {
1901 string id
= "some_id";
1902 string parent_id
= "0";
1903 string name
= "in_root";
1905 int64 timestamp
= 10;
1906 mock_server_
->AddUpdateDirectory(id
, parent_id
, name
, version
, timestamp
,
1907 foreign_cache_guid(), "-1");
1911 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1912 Entry
entry(&trans
, GET_BY_ID
,
1913 syncable::Id::CreateFromServerId("some_id"));
1914 ASSERT_TRUE(entry
.good());
1915 EXPECT_TRUE(entry
.GetIsDir());
1916 EXPECT_TRUE(entry
.GetServerVersion()== version
);
1917 EXPECT_TRUE(entry
.GetBaseVersion()== version
);
1918 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
1919 EXPECT_FALSE(entry
.GetIsUnsynced());
1920 EXPECT_FALSE(entry
.GetServerIsDel());
1921 EXPECT_FALSE(entry
.GetIsDel());
1925 TEST_F(SyncerTest
, IllegalAndLegalUpdates
) {
1926 Id root
= TestIdFactory::root();
1927 // Should apply just fine.
1928 mock_server_
->AddUpdateDirectory(1, 0, "in_root", 10, 10,
1929 foreign_cache_guid(), "-1");
1931 // Same name. But this SHOULD work.
1932 mock_server_
->AddUpdateDirectory(2, 0, "in_root", 10, 10,
1933 foreign_cache_guid(), "-2");
1935 // Unknown parent: should never be applied. "-80" is a legal server ID,
1936 // because any string sent by the server is a legal server ID in the sync
1937 // protocol, but it's not the ID of any item known to the client. This
1938 // update should succeed validation, but be stuck in the unapplied state
1939 // until an item with the server ID "-80" arrives.
1940 mock_server_
->AddUpdateDirectory(3, -80, "bad_parent", 10, 10,
1941 foreign_cache_guid(), "-3");
1945 // Id 3 should be in conflict now.
1948 GetUpdateCounters(BOOKMARKS
).num_hierarchy_conflict_application_failures
);
1950 // The only request in that loop should have been a GetUpdate.
1951 // At that point, we didn't know whether or not we had conflicts.
1952 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
1953 VerifyHierarchyConflictsUnspecified(mock_server_
->last_request());
1955 // These entries will be used in the second set of updates.
1956 mock_server_
->AddUpdateDirectory(4, 0, "newer_version", 20, 10,
1957 foreign_cache_guid(), "-4");
1958 mock_server_
->AddUpdateDirectory(5, 0, "circular1", 10, 10,
1959 foreign_cache_guid(), "-5");
1960 mock_server_
->AddUpdateDirectory(6, 5, "circular2", 10, 10,
1961 foreign_cache_guid(), "-6");
1962 mock_server_
->AddUpdateDirectory(9, 3, "bad_parent_child", 10, 10,
1963 foreign_cache_guid(), "-9");
1964 mock_server_
->AddUpdateDirectory(100, 9, "bad_parent_child2", 10, 10,
1965 foreign_cache_guid(), "-100");
1966 mock_server_
->AddUpdateDirectory(10, 0, "dir_to_bookmark", 10, 10,
1967 foreign_cache_guid(), "-10");
1970 // The three items with an unresolved parent should be unapplied (3, 9, 100).
1971 // The name clash should also still be in conflict.
1974 GetUpdateCounters(BOOKMARKS
).num_hierarchy_conflict_application_failures
);
1976 // This time around, we knew that there were conflicts.
1977 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
1978 VerifyHierarchyConflictsReported(mock_server_
->last_request());
1981 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1982 // Even though it has the same name, it should work.
1983 Entry
name_clash(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
1984 ASSERT_TRUE(name_clash
.good());
1985 EXPECT_FALSE(name_clash
.GetIsUnappliedUpdate())
1986 << "Duplicate name SHOULD be OK.";
1988 Entry
bad_parent(&trans
, GET_BY_ID
, ids_
.FromNumber(3));
1989 ASSERT_TRUE(bad_parent
.good());
1990 EXPECT_TRUE(bad_parent
.GetIsUnappliedUpdate())
1991 << "child of unknown parent should be in conflict";
1993 Entry
bad_parent_child(&trans
, GET_BY_ID
, ids_
.FromNumber(9));
1994 ASSERT_TRUE(bad_parent_child
.good());
1995 EXPECT_TRUE(bad_parent_child
.GetIsUnappliedUpdate())
1996 << "grandchild of unknown parent should be in conflict";
1998 Entry
bad_parent_child2(&trans
, GET_BY_ID
, ids_
.FromNumber(100));
1999 ASSERT_TRUE(bad_parent_child2
.good());
2000 EXPECT_TRUE(bad_parent_child2
.GetIsUnappliedUpdate())
2001 << "great-grandchild of unknown parent should be in conflict";
2004 // Updating 1 should not affect item 2 of the same name.
2005 mock_server_
->AddUpdateDirectory(1, 0, "new_name", 20, 20,
2006 foreign_cache_guid(), "-1");
2008 // Moving 5 under 6 will create a cycle: a conflict.
2009 mock_server_
->AddUpdateDirectory(5, 6, "circular3", 20, 20,
2010 foreign_cache_guid(), "-5");
2012 // Flip the is_dir bit: should fail verify & be dropped.
2013 mock_server_
->AddUpdateBookmark(10, 0, "dir_to_bookmark", 20, 20,
2014 foreign_cache_guid(), "-10");
2017 // Version number older than last known: should fail verify & be dropped.
2018 mock_server_
->AddUpdateDirectory(4, 0, "old_version", 10, 10,
2019 foreign_cache_guid(), "-4");
2022 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2024 Entry
still_a_dir(&trans
, GET_BY_ID
, ids_
.FromNumber(10));
2025 ASSERT_TRUE(still_a_dir
.good());
2026 EXPECT_FALSE(still_a_dir
.GetIsUnappliedUpdate());
2027 EXPECT_EQ(10u, still_a_dir
.GetBaseVersion());
2028 EXPECT_EQ(10u, still_a_dir
.GetServerVersion());
2029 EXPECT_TRUE(still_a_dir
.GetIsDir());
2031 Entry
rename(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2032 ASSERT_TRUE(rename
.good());
2033 EXPECT_EQ(root
, rename
.GetParentId());
2034 EXPECT_EQ("new_name", rename
.GetNonUniqueName());
2035 EXPECT_FALSE(rename
.GetIsUnappliedUpdate());
2036 EXPECT_TRUE(ids_
.FromNumber(1) == rename
.GetId());
2037 EXPECT_EQ(20u, rename
.GetBaseVersion());
2039 Entry
name_clash(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
2040 ASSERT_TRUE(name_clash
.good());
2041 EXPECT_EQ(root
, name_clash
.GetParentId());
2042 EXPECT_TRUE(ids_
.FromNumber(2) == name_clash
.GetId());
2043 EXPECT_EQ(10u, name_clash
.GetBaseVersion());
2044 EXPECT_EQ("in_root", name_clash
.GetNonUniqueName());
2046 Entry
ignored_old_version(&trans
, GET_BY_ID
, ids_
.FromNumber(4));
2047 ASSERT_TRUE(ignored_old_version
.good());
2049 ignored_old_version
.GetNonUniqueName()== "newer_version");
2050 EXPECT_FALSE(ignored_old_version
.GetIsUnappliedUpdate());
2051 EXPECT_EQ(20u, ignored_old_version
.GetBaseVersion());
2053 Entry
circular_parent_issue(&trans
, GET_BY_ID
, ids_
.FromNumber(5));
2054 ASSERT_TRUE(circular_parent_issue
.good());
2055 EXPECT_TRUE(circular_parent_issue
.GetIsUnappliedUpdate())
2056 << "circular move should be in conflict";
2057 EXPECT_TRUE(circular_parent_issue
.GetParentId()== root_id_
);
2058 EXPECT_TRUE(circular_parent_issue
.GetServerParentId()==
2059 ids_
.FromNumber(6));
2060 EXPECT_EQ(10u, circular_parent_issue
.GetBaseVersion());
2062 Entry
circular_parent_target(&trans
, GET_BY_ID
, ids_
.FromNumber(6));
2063 ASSERT_TRUE(circular_parent_target
.good());
2064 EXPECT_FALSE(circular_parent_target
.GetIsUnappliedUpdate());
2065 EXPECT_TRUE(circular_parent_issue
.GetId()==
2066 circular_parent_target
.GetParentId());
2067 EXPECT_EQ(10u, circular_parent_target
.GetBaseVersion());
2070 EXPECT_FALSE(saw_syncer_event_
);
2073 GetUpdateCounters(BOOKMARKS
).num_hierarchy_conflict_application_failures
);
2076 // A commit with a lost response produces an update that has to be reunited with
2078 TEST_F(SyncerTest
, CommitReuniteUpdateAdjustsChildren
) {
2079 // Create a folder in the root.
2080 int64 metahandle_folder
;
2082 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2084 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_folder");
2085 ASSERT_TRUE(entry
.good());
2086 entry
.PutIsDir(true);
2087 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2088 entry
.PutIsUnsynced(true);
2089 metahandle_folder
= entry
.GetMetahandle();
2092 // Verify it and pull the ID out of the folder.
2093 syncable::Id folder_id
;
2094 int64 metahandle_entry
;
2096 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2097 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_folder
);
2098 ASSERT_TRUE(entry
.good());
2099 folder_id
= entry
.GetId();
2100 ASSERT_TRUE(!folder_id
.ServerKnows());
2103 // Create an entry in the newly created folder.
2105 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2106 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, folder_id
, "new_entry");
2107 ASSERT_TRUE(entry
.good());
2108 metahandle_entry
= entry
.GetMetahandle();
2109 WriteTestDataToEntry(&trans
, &entry
);
2112 // Verify it and pull the ID out of the entry.
2113 syncable::Id entry_id
;
2115 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2116 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, metahandle_entry
);
2117 ASSERT_TRUE(entry
.good());
2118 EXPECT_EQ(folder_id
, entry
.GetParentId());
2119 EXPECT_EQ("new_entry", entry
.GetNonUniqueName());
2120 entry_id
= entry
.GetId();
2121 EXPECT_TRUE(!entry_id
.ServerKnows());
2122 VerifyTestDataInEntry(&trans
, &entry
);
2125 // Now, to emulate a commit response failure, we just don't commit it.
2126 int64 new_version
= 150; // any larger value.
2127 int64 timestamp
= 20; // arbitrary value.
2128 syncable::Id new_folder_id
=
2129 syncable::Id::CreateFromServerId("folder_server_id");
2131 // The following update should cause the folder to both apply the update, as
2132 // well as reassociate the id.
2133 mock_server_
->AddUpdateDirectory(new_folder_id
, root_id_
,
2134 "new_folder", new_version
, timestamp
,
2135 local_cache_guid(), folder_id
.GetServerId());
2137 // We don't want it accidentally committed, just the update applied.
2138 mock_server_
->set_conflict_all_commits(true);
2140 // Alright! Apply that update!
2143 // The folder's ID should have been updated.
2144 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2145 Entry
folder(&trans
, GET_BY_HANDLE
, metahandle_folder
);
2146 ASSERT_TRUE(folder
.good());
2147 EXPECT_EQ("new_folder", folder
.GetNonUniqueName());
2148 EXPECT_TRUE(new_version
== folder
.GetBaseVersion());
2149 EXPECT_TRUE(new_folder_id
== folder
.GetId());
2150 EXPECT_TRUE(folder
.GetId().ServerKnows());
2151 EXPECT_EQ(trans
.root_id(), folder
.GetParentId());
2153 // Since it was updated, the old folder should not exist.
2154 Entry
old_dead_folder(&trans
, GET_BY_ID
, folder_id
);
2155 EXPECT_FALSE(old_dead_folder
.good());
2157 // The child's parent should have changed.
2158 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, metahandle_entry
);
2159 ASSERT_TRUE(entry
.good());
2160 EXPECT_EQ("new_entry", entry
.GetNonUniqueName());
2161 EXPECT_EQ(new_folder_id
, entry
.GetParentId());
2162 EXPECT_TRUE(!entry
.GetId().ServerKnows());
2163 VerifyTestDataInEntry(&trans
, &entry
);
2167 // A commit with a lost response produces an update that has to be reunited with
2169 TEST_F(SyncerTest
, CommitReuniteUpdate
) {
2170 // Create an entry in the root.
2171 int64 entry_metahandle
;
2173 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2174 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_entry");
2175 ASSERT_TRUE(entry
.good());
2176 entry_metahandle
= entry
.GetMetahandle();
2177 WriteTestDataToEntry(&trans
, &entry
);
2180 // Verify it and pull the ID out.
2181 syncable::Id entry_id
;
2183 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2185 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2186 ASSERT_TRUE(entry
.good());
2187 entry_id
= entry
.GetId();
2188 EXPECT_TRUE(!entry_id
.ServerKnows());
2189 VerifyTestDataInEntry(&trans
, &entry
);
2192 // Now, to emulate a commit response failure, we just don't commit it.
2193 int64 new_version
= 150; // any larger value.
2194 int64 timestamp
= 20; // arbitrary value.
2195 syncable::Id new_entry_id
= syncable::Id::CreateFromServerId("server_id");
2197 // Generate an update from the server with a relevant ID reassignment.
2198 mock_server_
->AddUpdateBookmark(new_entry_id
, root_id_
,
2199 "new_entry", new_version
, timestamp
,
2200 local_cache_guid(), entry_id
.GetServerId());
2202 // We don't want it accidentally committed, just the update applied.
2203 mock_server_
->set_conflict_all_commits(true);
2205 // Alright! Apply that update!
2208 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2209 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2210 ASSERT_TRUE(entry
.good());
2211 EXPECT_TRUE(new_version
== entry
.GetBaseVersion());
2212 EXPECT_TRUE(new_entry_id
== entry
.GetId());
2213 EXPECT_EQ("new_entry", entry
.GetNonUniqueName());
2217 // A commit with a lost response must work even if the local entry was deleted
2218 // before the update is applied. We should not duplicate the local entry in
2219 // this case, but just create another one alongside. We may wish to examine
2220 // this behavior in the future as it can create hanging uploads that never
2221 // finish, that must be cleaned up on the server side after some time.
2222 TEST_F(SyncerTest
, CommitReuniteUpdateDoesNotChokeOnDeletedLocalEntry
) {
2223 // Create a entry in the root.
2224 int64 entry_metahandle
;
2226 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2227 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_entry");
2228 ASSERT_TRUE(entry
.good());
2229 entry_metahandle
= entry
.GetMetahandle();
2230 WriteTestDataToEntry(&trans
, &entry
);
2232 // Verify it and pull the ID out.
2233 syncable::Id entry_id
;
2235 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2236 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2237 ASSERT_TRUE(entry
.good());
2238 entry_id
= entry
.GetId();
2239 EXPECT_TRUE(!entry_id
.ServerKnows());
2240 VerifyTestDataInEntry(&trans
, &entry
);
2243 // Now, to emulate a commit response failure, we just don't commit it.
2244 int64 new_version
= 150; // any larger value.
2245 int64 timestamp
= 20; // arbitrary value.
2246 syncable::Id new_entry_id
= syncable::Id::CreateFromServerId("server_id");
2248 // Generate an update from the server with a relevant ID reassignment.
2249 mock_server_
->AddUpdateBookmark(new_entry_id
, root_id_
,
2250 "new_entry", new_version
, timestamp
,
2251 local_cache_guid(), entry_id
.GetServerId());
2253 // We don't want it accidentally committed, just the update applied.
2254 mock_server_
->set_conflict_all_commits(true);
2256 // Purposefully delete the entry now before the update application finishes.
2258 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2259 Id new_entry_id
= GetOnlyEntryWithName(
2260 &trans
, trans
.root_id(), "new_entry");
2261 MutableEntry
entry(&trans
, GET_BY_ID
, new_entry_id
);
2262 ASSERT_TRUE(entry
.good());
2263 entry
.PutIsDel(true);
2266 // Just don't CHECK fail in sync, have the update split.
2269 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2270 Id new_entry_id
= GetOnlyEntryWithName(
2271 &trans
, trans
.root_id(), "new_entry");
2272 Entry
entry(&trans
, GET_BY_ID
, new_entry_id
);
2273 ASSERT_TRUE(entry
.good());
2274 EXPECT_FALSE(entry
.GetIsDel());
2276 Entry
old_entry(&trans
, GET_BY_ID
, entry_id
);
2277 ASSERT_TRUE(old_entry
.good());
2278 EXPECT_TRUE(old_entry
.GetIsDel());
2282 // TODO(chron): Add more unsanitized name tests.
2283 TEST_F(SyncerTest
, ConflictMatchingEntryHandlesUnsanitizedNames
) {
2284 mock_server_
->AddUpdateDirectory(1, 0, "A/A", 10, 10,
2285 foreign_cache_guid(), "-1");
2286 mock_server_
->AddUpdateDirectory(2, 0, "B/B", 10, 10,
2287 foreign_cache_guid(), "-2");
2288 mock_server_
->set_conflict_all_commits(true);
2291 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2293 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2294 ASSERT_TRUE(A
.good());
2295 A
.PutIsUnsynced(true);
2296 A
.PutIsUnappliedUpdate(true);
2297 A
.PutServerVersion(20);
2299 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
2300 ASSERT_TRUE(B
.good());
2301 B
.PutIsUnappliedUpdate(true);
2302 B
.PutServerVersion(20);
2305 saw_syncer_event_
= false;
2306 mock_server_
->set_conflict_all_commits(false);
2309 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2311 Entry
A(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2312 ASSERT_TRUE(A
.good());
2313 EXPECT_TRUE(A
.GetIsUnsynced()== false);
2314 EXPECT_TRUE(A
.GetIsUnappliedUpdate()== false);
2315 EXPECT_TRUE(A
.GetServerVersion()== 20);
2317 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
2318 ASSERT_TRUE(B
.good());
2319 EXPECT_TRUE(B
.GetIsUnsynced()== false);
2320 EXPECT_TRUE(B
.GetIsUnappliedUpdate()== false);
2321 EXPECT_TRUE(B
.GetServerVersion()== 20);
2325 TEST_F(SyncerTest
, ConflictMatchingEntryHandlesNormalNames
) {
2326 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
2327 foreign_cache_guid(), "-1");
2328 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
2329 foreign_cache_guid(), "-2");
2330 mock_server_
->set_conflict_all_commits(true);
2333 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2335 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2336 ASSERT_TRUE(A
.good());
2337 A
.PutIsUnsynced(true);
2338 A
.PutIsUnappliedUpdate(true);
2339 A
.PutServerVersion(20);
2341 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
2342 ASSERT_TRUE(B
.good());
2343 B
.PutIsUnappliedUpdate(true);
2344 B
.PutServerVersion(20);
2347 saw_syncer_event_
= false;
2348 mock_server_
->set_conflict_all_commits(false);
2351 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2353 Entry
A(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2354 ASSERT_TRUE(A
.good());
2355 EXPECT_TRUE(A
.GetIsUnsynced()== false);
2356 EXPECT_TRUE(A
.GetIsUnappliedUpdate()== false);
2357 EXPECT_TRUE(A
.GetServerVersion()== 20);
2359 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
2360 ASSERT_TRUE(B
.good());
2361 EXPECT_TRUE(B
.GetIsUnsynced()== false);
2362 EXPECT_TRUE(B
.GetIsUnappliedUpdate()== false);
2363 EXPECT_TRUE(B
.GetServerVersion()== 20);
2367 TEST_F(SyncerTest
, ReverseFolderOrderingTest
) {
2368 mock_server_
->AddUpdateDirectory(4, 3, "ggchild", 10, 10,
2369 foreign_cache_guid(), "-4");
2370 mock_server_
->AddUpdateDirectory(3, 2, "gchild", 10, 10,
2371 foreign_cache_guid(), "-3");
2372 mock_server_
->AddUpdateDirectory(5, 4, "gggchild", 10, 10,
2373 foreign_cache_guid(), "-5");
2374 mock_server_
->AddUpdateDirectory(2, 1, "child", 10, 10,
2375 foreign_cache_guid(), "-2");
2376 mock_server_
->AddUpdateDirectory(1, 0, "parent", 10, 10,
2377 foreign_cache_guid(), "-1");
2379 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2381 Id child_id
= GetOnlyEntryWithName(
2382 &trans
, ids_
.FromNumber(4), "gggchild");
2383 Entry
child(&trans
, GET_BY_ID
, child_id
);
2384 ASSERT_TRUE(child
.good());
2387 class EntryCreatedInNewFolderTest
: public SyncerTest
{
2389 void CreateFolderInBob() {
2390 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2391 MutableEntry
bob(&trans
,
2392 syncable::GET_BY_ID
,
2393 GetOnlyEntryWithName(&trans
,
2394 TestIdFactory::root(),
2398 MutableEntry
entry2(
2399 &trans
, CREATE
, BOOKMARKS
, bob
.GetId(), "bob");
2400 CHECK(entry2
.good());
2401 entry2
.PutIsDir(true);
2402 entry2
.PutIsUnsynced(true);
2403 entry2
.PutSpecifics(DefaultBookmarkSpecifics());
2407 TEST_F(EntryCreatedInNewFolderTest
, EntryCreatedInNewFolderMidSync
) {
2409 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2410 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2411 ASSERT_TRUE(entry
.good());
2412 entry
.PutIsDir(true);
2413 entry
.PutIsUnsynced(true);
2414 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2417 mock_server_
->SetMidCommitCallback(
2418 base::Bind(&EntryCreatedInNewFolderTest::CreateFolderInBob
,
2419 base::Unretained(this)));
2421 // We loop until no unsynced handles remain, so we will commit both ids.
2422 EXPECT_EQ(2u, mock_server_
->committed_ids().size());
2424 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2425 Entry
parent_entry(&trans
, syncable::GET_BY_ID
,
2426 GetOnlyEntryWithName(&trans
, TestIdFactory::root(), "bob"));
2427 ASSERT_TRUE(parent_entry
.good());
2430 GetOnlyEntryWithName(&trans
, parent_entry
.GetId(), "bob");
2431 Entry
child(&trans
, syncable::GET_BY_ID
, child_id
);
2432 ASSERT_TRUE(child
.good());
2433 EXPECT_EQ(parent_entry
.GetId(), child
.GetParentId());
2437 TEST_F(SyncerTest
, NegativeIDInUpdate
) {
2438 mock_server_
->AddUpdateBookmark(-10, 0, "bad", 40, 40,
2439 foreign_cache_guid(), "-100");
2441 // The negative id would make us CHECK!
2444 TEST_F(SyncerTest
, UnappliedUpdateOnCreatedItemItemDoesNotCrash
) {
2445 int64 metahandle_fred
;
2446 syncable::Id orig_id
;
2449 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2450 MutableEntry
fred_match(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(),
2452 ASSERT_TRUE(fred_match
.good());
2453 metahandle_fred
= fred_match
.GetMetahandle();
2454 orig_id
= fred_match
.GetId();
2455 WriteTestDataToEntry(&trans
, &fred_match
);
2459 EXPECT_EQ(1u, mock_server_
->committed_ids().size());
2460 mock_server_
->set_conflict_all_commits(true);
2461 syncable::Id fred_match_id
;
2463 // Now receive a change from outside.
2464 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2465 MutableEntry
fred_match(&trans
, GET_BY_HANDLE
, metahandle_fred
);
2466 ASSERT_TRUE(fred_match
.good());
2467 EXPECT_TRUE(fred_match
.GetId().ServerKnows());
2468 fred_match_id
= fred_match
.GetId();
2469 mock_server_
->AddUpdateBookmark(fred_match_id
, trans
.root_id(),
2470 "fred_match", 40, 40, local_cache_guid(), orig_id
.GetServerId());
2473 for (int i
= 0 ; i
< 30 ; ++i
) {
2479 * In the event that we have a double changed entry, that is changed on both
2480 * the client and the server, the conflict resolver should just drop one of
2481 * them and accept the other.
2484 TEST_F(SyncerTest
, DoublyChangedWithResolver
) {
2485 syncable::Id local_id
;
2487 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2488 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Folder");
2489 ASSERT_TRUE(parent
.good());
2490 parent
.PutIsDir(true);
2491 parent
.PutId(parent_id_
);
2492 parent
.PutBaseVersion(5);
2493 parent
.PutSpecifics(DefaultBookmarkSpecifics());
2494 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete.htm");
2495 ASSERT_TRUE(child
.good());
2496 local_id
= child
.GetId();
2497 child
.PutId(child_id_
);
2498 child
.PutBaseVersion(10);
2499 WriteTestDataToEntry(&wtrans
, &child
);
2501 mock_server_
->AddUpdateBookmark(child_id_
, parent_id_
, "Pete2.htm", 11, 10,
2502 local_cache_guid(), local_id
.GetServerId());
2503 mock_server_
->set_conflict_all_commits(true);
2505 syncable::Directory::Metahandles children
;
2507 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2508 directory()->GetChildHandlesById(&trans
, parent_id_
, &children
);
2509 // We expect the conflict resolver to preserve the local entry.
2510 Entry
child(&trans
, syncable::GET_BY_ID
, child_id_
);
2511 ASSERT_TRUE(child
.good());
2512 EXPECT_TRUE(child
.GetIsUnsynced());
2513 EXPECT_FALSE(child
.GetIsUnappliedUpdate());
2514 EXPECT_TRUE(child
.GetSpecifics().has_bookmark());
2515 EXPECT_EQ("Pete.htm", child
.GetNonUniqueName());
2516 VerifyTestBookmarkDataInEntry(&child
);
2519 // Only one entry, since we just overwrite one.
2520 EXPECT_EQ(1u, children
.size());
2521 saw_syncer_event_
= false;
2524 // We got this repro case when someone was editing bookmarks while sync was
2525 // occuring. The entry had changed out underneath the user.
2526 TEST_F(SyncerTest
, CommitsUpdateDoesntAlterEntry
) {
2527 const base::Time
& test_time
= ProtoTimeToTime(123456);
2528 syncable::Id local_id
;
2529 int64 entry_metahandle
;
2531 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2532 MutableEntry
entry(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Pete");
2533 ASSERT_TRUE(entry
.good());
2534 EXPECT_FALSE(entry
.GetId().ServerKnows());
2535 local_id
= entry
.GetId();
2536 entry
.PutIsDir(true);
2537 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2538 entry
.PutIsUnsynced(true);
2539 entry
.PutMtime(test_time
);
2540 entry_metahandle
= entry
.GetMetahandle();
2546 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2547 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, entry_metahandle
);
2548 ASSERT_TRUE(entry
.good());
2550 EXPECT_TRUE(id
.ServerKnows());
2551 version
= entry
.GetBaseVersion();
2553 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
2554 update
->set_originator_cache_guid(local_cache_guid());
2555 update
->set_originator_client_item_id(local_id
.GetServerId());
2556 EXPECT_EQ("Pete", update
->name());
2557 EXPECT_EQ(id
.GetServerId(), update
->id_string());
2558 EXPECT_EQ(root_id_
.GetServerId(), update
->parent_id_string());
2559 EXPECT_EQ(version
, update
->version());
2562 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2563 Entry
entry(&trans
, syncable::GET_BY_ID
, id
);
2564 ASSERT_TRUE(entry
.good());
2565 EXPECT_TRUE(entry
.GetMtime()== test_time
);
2569 TEST_F(SyncerTest
, ParentAndChildBothMatch
) {
2570 // Disable PREFERENCES which is enabled at the setup step to avoid
2572 // PREFERENCES root folder and failing the test below that verifies the number
2573 // of children at the root.
2574 DisableDatatype(PREFERENCES
);
2576 const FullModelTypeSet all_types
= FullModelTypeSet::All();
2577 syncable::Id parent_id
= ids_
.NewServerId();
2578 syncable::Id child_id
= ids_
.NewServerId();
2579 syncable::Id parent_local_id
;
2580 syncable::Id child_local_id
;
2583 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2584 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Folder");
2585 ASSERT_TRUE(parent
.good());
2586 parent_local_id
= parent
.GetId();
2587 parent
.PutIsDir(true);
2588 parent
.PutIsUnsynced(true);
2589 parent
.PutId(parent_id
);
2590 parent
.PutBaseVersion(1);
2591 parent
.PutSpecifics(DefaultBookmarkSpecifics());
2593 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent
.GetId(), "test.htm");
2594 ASSERT_TRUE(child
.good());
2595 child_local_id
= child
.GetId();
2596 child
.PutId(child_id
);
2597 child
.PutBaseVersion(1);
2598 child
.PutSpecifics(DefaultBookmarkSpecifics());
2599 WriteTestDataToEntry(&wtrans
, &child
);
2601 mock_server_
->AddUpdateDirectory(parent_id
, root_id_
, "Folder", 10, 10,
2603 parent_local_id
.GetServerId());
2604 mock_server_
->AddUpdateBookmark(child_id
, parent_id
, "test.htm", 10, 10,
2606 child_local_id
.GetServerId());
2607 mock_server_
->set_conflict_all_commits(true);
2612 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2613 Directory::Metahandles children
;
2614 directory()->GetChildHandlesById(&trans
, root_id_
, &children
);
2615 EXPECT_EQ(1u, children
.size());
2616 directory()->GetChildHandlesById(&trans
, parent_id
, &children
);
2617 EXPECT_EQ(1u, children
.size());
2618 std::vector
<int64
> unapplied
;
2619 directory()->GetUnappliedUpdateMetaHandles(&trans
, all_types
, &unapplied
);
2620 EXPECT_EQ(0u, unapplied
.size());
2621 syncable::Directory::Metahandles unsynced
;
2622 directory()->GetUnsyncedMetaHandles(&trans
, &unsynced
);
2623 EXPECT_EQ(0u, unsynced
.size());
2624 saw_syncer_event_
= false;
2628 TEST_F(SyncerTest
, CommittingNewDeleted
) {
2630 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2631 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2632 entry
.PutIsUnsynced(true);
2633 entry
.PutIsDel(true);
2636 EXPECT_EQ(0u, mock_server_
->committed_ids().size());
2639 // Original problem synopsis:
2640 // Check failed: entry->GetBaseVersion()<= entry->GetServerVersion()
2641 // Client creates entry, client finishes committing entry. Between
2642 // commit and getting update back, we delete the entry.
2643 // We get the update for the entry, but the local one was modified
2644 // so we store the entry but don't apply it. IS_UNAPPLIED_UPDATE is set.
2645 // We commit deletion and get a new version number.
2646 // We apply unapplied updates again before we get the update about the deletion.
2647 // This means we have an unapplied update where server_version < base_version.
2648 TEST_F(SyncerTest
, UnappliedUpdateDuringCommit
) {
2649 // This test is a little fake.
2651 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2652 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2653 entry
.PutId(ids_
.FromNumber(20));
2654 entry
.PutBaseVersion(1);
2655 entry
.PutServerVersion(1);
2656 entry
.PutServerParentId(ids_
.FromNumber(9999)); // Bad parent.
2657 entry
.PutIsUnsynced(true);
2658 entry
.PutIsUnappliedUpdate(true);
2659 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2660 entry
.PutServerSpecifics(DefaultBookmarkSpecifics());
2661 entry
.PutIsDel(false);
2664 EXPECT_EQ(1, session_
->status_controller().TotalNumConflictingItems());
2665 saw_syncer_event_
= false;
2668 // Original problem synopsis:
2670 // Unexpected error during sync if we:
2671 // make a new folder bob
2673 // make a new folder fred
2674 // move bob into fred
2677 // if no syncing occured midway, bob will have an illegal parent
2678 TEST_F(SyncerTest
, DeletingEntryInFolder
) {
2679 // This test is a little fake.
2680 int64 existing_metahandle
;
2682 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2683 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "existing");
2684 ASSERT_TRUE(entry
.good());
2685 entry
.PutIsDir(true);
2686 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2687 entry
.PutIsUnsynced(true);
2688 existing_metahandle
= entry
.GetMetahandle();
2692 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2693 MutableEntry
newfolder(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new");
2694 ASSERT_TRUE(newfolder
.good());
2695 newfolder
.PutIsDir(true);
2696 newfolder
.PutSpecifics(DefaultBookmarkSpecifics());
2697 newfolder
.PutIsUnsynced(true);
2699 MutableEntry
existing(&trans
, GET_BY_HANDLE
, existing_metahandle
);
2700 ASSERT_TRUE(existing
.good());
2701 existing
.PutParentId(newfolder
.GetId());
2702 existing
.PutIsUnsynced(true);
2703 EXPECT_TRUE(existing
.GetId().ServerKnows());
2705 newfolder
.PutIsDel(true);
2706 existing
.PutIsDel(true);
2709 EXPECT_EQ(0, GetCommitCounters(BOOKMARKS
).num_commits_conflict
);
2712 TEST_F(SyncerTest
, DeletingEntryWithLocalEdits
) {
2713 int64 newfolder_metahandle
;
2715 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
2716 foreign_cache_guid(), "-1");
2719 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2720 MutableEntry
newfolder(
2721 &trans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(1), "local");
2722 ASSERT_TRUE(newfolder
.good());
2723 newfolder
.PutIsUnsynced(true);
2724 newfolder
.PutIsDir(true);
2725 newfolder
.PutSpecifics(DefaultBookmarkSpecifics());
2726 newfolder_metahandle
= newfolder
.GetMetahandle();
2728 mock_server_
->AddUpdateDirectory(1, 0, "bob", 2, 20,
2729 foreign_cache_guid(), "-1");
2730 mock_server_
->SetLastUpdateDeleted();
2731 SyncShareConfigure();
2733 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2734 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, newfolder_metahandle
);
2735 ASSERT_TRUE(entry
.good());
2739 TEST_F(SyncerTest
, FolderSwapUpdate
) {
2740 mock_server_
->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2741 foreign_cache_guid(), "-7801");
2742 mock_server_
->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2743 foreign_cache_guid(), "-1024");
2745 mock_server_
->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2746 foreign_cache_guid(), "-1024");
2747 mock_server_
->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2748 foreign_cache_guid(), "-7801");
2751 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2752 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2753 ASSERT_TRUE(id1
.good());
2754 EXPECT_TRUE("fred" == id1
.GetNonUniqueName());
2755 EXPECT_TRUE(root_id_
== id1
.GetParentId());
2756 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2757 ASSERT_TRUE(id2
.good());
2758 EXPECT_TRUE("bob" == id2
.GetNonUniqueName());
2759 EXPECT_TRUE(root_id_
== id2
.GetParentId());
2761 saw_syncer_event_
= false;
2764 TEST_F(SyncerTest
, NameCollidingFolderSwapWorksFine
) {
2765 mock_server_
->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2766 foreign_cache_guid(), "-7801");
2767 mock_server_
->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2768 foreign_cache_guid(), "-1024");
2769 mock_server_
->AddUpdateDirectory(4096, 0, "alice", 1, 10,
2770 foreign_cache_guid(), "-4096");
2773 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2774 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2775 ASSERT_TRUE(id1
.good());
2776 EXPECT_TRUE("bob" == id1
.GetNonUniqueName());
2777 EXPECT_TRUE(root_id_
== id1
.GetParentId());
2778 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2779 ASSERT_TRUE(id2
.good());
2780 EXPECT_TRUE("fred" == id2
.GetNonUniqueName());
2781 EXPECT_TRUE(root_id_
== id2
.GetParentId());
2782 Entry
id3(&trans
, GET_BY_ID
, ids_
.FromNumber(4096));
2783 ASSERT_TRUE(id3
.good());
2784 EXPECT_TRUE("alice" == id3
.GetNonUniqueName());
2785 EXPECT_TRUE(root_id_
== id3
.GetParentId());
2787 mock_server_
->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2788 foreign_cache_guid(), "-1024");
2789 mock_server_
->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2790 foreign_cache_guid(), "-7801");
2791 mock_server_
->AddUpdateDirectory(4096, 0, "bob", 2, 20,
2792 foreign_cache_guid(), "-4096");
2795 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2796 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2797 ASSERT_TRUE(id1
.good());
2798 EXPECT_TRUE("fred" == id1
.GetNonUniqueName());
2799 EXPECT_TRUE(root_id_
== id1
.GetParentId());
2800 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2801 ASSERT_TRUE(id2
.good());
2802 EXPECT_TRUE("bob" == id2
.GetNonUniqueName());
2803 EXPECT_TRUE(root_id_
== id2
.GetParentId());
2804 Entry
id3(&trans
, GET_BY_ID
, ids_
.FromNumber(4096));
2805 ASSERT_TRUE(id3
.good());
2806 EXPECT_TRUE("bob" == id3
.GetNonUniqueName());
2807 EXPECT_TRUE(root_id_
== id3
.GetParentId());
2809 saw_syncer_event_
= false;
2812 // Committing more than kDefaultMaxCommitBatchSize items requires that
2813 // we post more than one commit command to the server. This test makes
2814 // sure that scenario works as expected.
2815 TEST_F(SyncerTest
, CommitManyItemsInOneGo_Success
) {
2816 uint32 num_batches
= 3;
2817 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2819 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2820 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2821 string nameutf8
= base::StringPrintf("%d", i
);
2822 string
name(nameutf8
.begin(), nameutf8
.end());
2823 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2824 e
.PutIsUnsynced(true);
2826 e
.PutSpecifics(DefaultBookmarkSpecifics());
2829 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2832 EXPECT_EQ(num_batches
, mock_server_
->commit_messages().size());
2833 EXPECT_EQ(0, directory()->unsynced_entity_count());
2836 // Test that a single failure to contact the server will cause us to exit the
2837 // commit loop immediately.
2838 TEST_F(SyncerTest
, CommitManyItemsInOneGo_PostBufferFail
) {
2839 uint32 num_batches
= 3;
2840 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2842 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2843 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2844 string nameutf8
= base::StringPrintf("%d", i
);
2845 string
name(nameutf8
.begin(), nameutf8
.end());
2846 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2847 e
.PutIsUnsynced(true);
2849 e
.PutSpecifics(DefaultBookmarkSpecifics());
2852 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2854 // The second commit should fail. It will be preceded by one successful
2855 // GetUpdate and one succesful commit.
2856 mock_server_
->FailNthPostBufferToPathCall(3);
2859 EXPECT_EQ(1U, mock_server_
->commit_messages().size());
2860 EXPECT_EQ(SYNC_SERVER_ERROR
,
2861 session_
->status_controller().model_neutral_state().commit_result
);
2862 EXPECT_EQ(items_to_commit
- kDefaultMaxCommitBatchSize
,
2863 directory()->unsynced_entity_count());
2866 // Test that a single conflict response from the server will cause us to exit
2867 // the commit loop immediately.
2868 TEST_F(SyncerTest
, CommitManyItemsInOneGo_CommitConflict
) {
2869 uint32 num_batches
= 2;
2870 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2872 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2873 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2874 string nameutf8
= base::StringPrintf("%d", i
);
2875 string
name(nameutf8
.begin(), nameutf8
.end());
2876 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2877 e
.PutIsUnsynced(true);
2879 e
.PutSpecifics(DefaultBookmarkSpecifics());
2882 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2884 // Return a CONFLICT response for the first item.
2885 mock_server_
->set_conflict_n_commits(1);
2888 // We should stop looping at the first sign of trouble.
2889 EXPECT_EQ(1U, mock_server_
->commit_messages().size());
2890 EXPECT_EQ(items_to_commit
- (kDefaultMaxCommitBatchSize
- 1),
2891 directory()->unsynced_entity_count());
2894 // Tests that sending debug info events works.
2895 TEST_F(SyncerTest
, SendDebugInfoEventsOnGetUpdates_HappyCase
) {
2896 debug_info_getter_
->AddDebugEvent();
2897 debug_info_getter_
->AddDebugEvent();
2901 // Verify we received one GetUpdates request with two debug info events.
2902 EXPECT_EQ(1U, mock_server_
->requests().size());
2903 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2904 EXPECT_EQ(2, mock_server_
->last_request().debug_info().events_size());
2908 // See that we received another GetUpdates request, but that it contains no
2909 // debug info events.
2910 EXPECT_EQ(2U, mock_server_
->requests().size());
2911 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2912 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
2914 debug_info_getter_
->AddDebugEvent();
2918 // See that we received another GetUpdates request and it contains one debug
2920 EXPECT_EQ(3U, mock_server_
->requests().size());
2921 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2922 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
2925 // Tests that debug info events are dropped on server error.
2926 TEST_F(SyncerTest
, SendDebugInfoEventsOnGetUpdates_PostFailsDontDrop
) {
2927 debug_info_getter_
->AddDebugEvent();
2928 debug_info_getter_
->AddDebugEvent();
2930 mock_server_
->FailNextPostBufferToPathCall();
2933 // Verify we attempted to send one GetUpdates request with two debug info
2935 EXPECT_EQ(1U, mock_server_
->requests().size());
2936 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2937 EXPECT_EQ(2, mock_server_
->last_request().debug_info().events_size());
2941 // See that the client resent the two debug info events.
2942 EXPECT_EQ(2U, mock_server_
->requests().size());
2943 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2944 EXPECT_EQ(2, mock_server_
->last_request().debug_info().events_size());
2946 // The previous send was successful so this next one shouldn't generate any
2947 // debug info events.
2949 EXPECT_EQ(3U, mock_server_
->requests().size());
2950 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2951 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
2954 // Tests that commit failure with conflict will trigger GetUpdates for next
2956 TEST_F(SyncerTest
, CommitFailureWithConflict
) {
2957 ConfigureNoGetUpdatesRequired();
2958 CreateUnsyncedDirectory("X", "id_X");
2959 EXPECT_FALSE(nudge_tracker_
.IsGetUpdatesRequired());
2962 EXPECT_FALSE(nudge_tracker_
.IsGetUpdatesRequired());
2964 CreateUnsyncedDirectory("Y", "id_Y");
2965 mock_server_
->set_conflict_n_commits(1);
2967 EXPECT_TRUE(nudge_tracker_
.IsGetUpdatesRequired());
2969 nudge_tracker_
.RecordSuccessfulSyncCycle();
2970 EXPECT_FALSE(nudge_tracker_
.IsGetUpdatesRequired());
2973 // Tests that sending debug info events on Commit works.
2974 TEST_F(SyncerTest
, SendDebugInfoEventsOnCommit_HappyCase
) {
2975 // Make sure GetUpdate isn't call as it would "steal" debug info events before
2976 // Commit has a chance to send them.
2977 ConfigureNoGetUpdatesRequired();
2979 // Generate a debug info event and trigger a commit.
2980 debug_info_getter_
->AddDebugEvent();
2981 CreateUnsyncedDirectory("X", "id_X");
2984 // Verify that the last request received is a Commit and that it contains a
2985 // debug info event.
2986 EXPECT_EQ(1U, mock_server_
->requests().size());
2987 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2988 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
2990 // Generate another commit, but no debug info event.
2991 CreateUnsyncedDirectory("Y", "id_Y");
2994 // See that it was received and contains no debug info events.
2995 EXPECT_EQ(2U, mock_server_
->requests().size());
2996 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2997 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
3000 // Tests that debug info events are not dropped on server error.
3001 TEST_F(SyncerTest
, SendDebugInfoEventsOnCommit_PostFailsDontDrop
) {
3002 // Make sure GetUpdate isn't call as it would "steal" debug info events before
3003 // Commit has a chance to send them.
3004 ConfigureNoGetUpdatesRequired();
3006 mock_server_
->FailNextPostBufferToPathCall();
3008 // Generate a debug info event and trigger a commit.
3009 debug_info_getter_
->AddDebugEvent();
3010 CreateUnsyncedDirectory("X", "id_X");
3013 // Verify that the last request sent is a Commit and that it contains a debug
3015 EXPECT_EQ(1U, mock_server_
->requests().size());
3016 ASSERT_TRUE(mock_server_
->last_request().has_commit());
3017 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
3022 // Verify that we've received another Commit and that it contains a debug info
3023 // event (just like the previous one).
3024 EXPECT_EQ(2U, mock_server_
->requests().size());
3025 ASSERT_TRUE(mock_server_
->last_request().has_commit());
3026 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
3028 // Generate another commit and try again.
3029 CreateUnsyncedDirectory("Y", "id_Y");
3032 // See that it was received and contains no debug info events.
3033 EXPECT_EQ(3U, mock_server_
->requests().size());
3034 ASSERT_TRUE(mock_server_
->last_request().has_commit());
3035 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
3038 TEST_F(SyncerTest
, HugeConflict
) {
3039 int item_count
= 300; // We should be able to do 300 or 3000 w/o issue.
3041 syncable::Id parent_id
= ids_
.NewServerId();
3042 syncable::Id last_id
= parent_id
;
3043 vector
<syncable::Id
> tree_ids
;
3045 // Create a lot of updates for which the parent does not exist yet.
3046 // Generate a huge deep tree which should all fail to apply at first.
3048 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3049 for (int i
= 0; i
< item_count
; i
++) {
3050 syncable::Id next_id
= ids_
.NewServerId();
3051 syncable::Id local_id
= ids_
.NewLocalId();
3052 tree_ids
.push_back(next_id
);
3053 mock_server_
->AddUpdateDirectory(next_id
, last_id
, "BOB", 2, 20,
3054 foreign_cache_guid(),
3055 local_id
.GetServerId());
3061 // Check they're in the expected conflict state.
3063 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3064 for (int i
= 0; i
< item_count
; i
++) {
3065 Entry
e(&trans
, GET_BY_ID
, tree_ids
[i
]);
3066 // They should all exist but none should be applied.
3067 ASSERT_TRUE(e
.good());
3068 EXPECT_TRUE(e
.GetIsDel());
3069 EXPECT_TRUE(e
.GetIsUnappliedUpdate());
3073 // Add the missing parent directory.
3074 mock_server_
->AddUpdateDirectory(parent_id
, TestIdFactory::root(),
3075 "BOB", 2, 20, foreign_cache_guid(), "-3500");
3078 // Now they should all be OK.
3080 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3081 for (int i
= 0; i
< item_count
; i
++) {
3082 Entry
e(&trans
, GET_BY_ID
, tree_ids
[i
]);
3083 ASSERT_TRUE(e
.good());
3084 EXPECT_FALSE(e
.GetIsDel());
3085 EXPECT_FALSE(e
.GetIsUnappliedUpdate());
3090 TEST_F(SyncerTest
, DontCrashOnCaseChange
) {
3091 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
3092 foreign_cache_guid(), "-1");
3095 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3096 MutableEntry
e(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3097 ASSERT_TRUE(e
.good());
3098 e
.PutIsUnsynced(true);
3100 mock_server_
->set_conflict_all_commits(true);
3101 mock_server_
->AddUpdateDirectory(1, 0, "BOB", 2, 20,
3102 foreign_cache_guid(), "-1");
3103 SyncShareNudge(); // USED TO CAUSE AN ASSERT
3104 saw_syncer_event_
= false;
3107 TEST_F(SyncerTest
, UnsyncedItemAndUpdate
) {
3108 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
3109 foreign_cache_guid(), "-1");
3111 mock_server_
->set_conflict_all_commits(true);
3112 mock_server_
->AddUpdateDirectory(2, 0, "bob", 2, 20,
3113 foreign_cache_guid(), "-2");
3114 SyncShareNudge(); // USED TO CAUSE AN ASSERT
3115 saw_syncer_event_
= false;
3118 TEST_F(SyncerTest
, NewEntryAndAlteredServerEntrySharePath
) {
3119 mock_server_
->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
3120 foreign_cache_guid(), "-1");
3122 int64 local_folder_handle
;
3123 syncable::Id local_folder_id
;
3125 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3126 MutableEntry
new_entry(
3127 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bar.htm");
3128 ASSERT_TRUE(new_entry
.good());
3129 local_folder_id
= new_entry
.GetId();
3130 local_folder_handle
= new_entry
.GetMetahandle();
3131 new_entry
.PutIsUnsynced(true);
3132 new_entry
.PutSpecifics(DefaultBookmarkSpecifics());
3133 MutableEntry
old(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3134 ASSERT_TRUE(old
.good());
3135 WriteTestDataToEntry(&wtrans
, &old
);
3137 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
3138 foreign_cache_guid(), "-1");
3139 mock_server_
->set_conflict_all_commits(true);
3141 saw_syncer_event_
= false;
3143 // Update #20 should have been dropped in favor of the local version.
3144 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3145 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3146 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3147 ASSERT_TRUE(server
.good());
3148 ASSERT_TRUE(local
.good());
3149 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3150 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3151 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3152 EXPECT_TRUE(server
.GetIsUnsynced());
3153 EXPECT_TRUE(local
.GetIsUnsynced());
3154 EXPECT_EQ("Foo.htm", server
.GetNonUniqueName());
3155 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3157 // Allow local changes to commit.
3158 mock_server_
->set_conflict_all_commits(false);
3160 saw_syncer_event_
= false;
3162 // Now add a server change to make the two names equal. There should
3163 // be no conflict with that, since names are not unique.
3164 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
3165 foreign_cache_guid(), "-1");
3167 saw_syncer_event_
= false;
3169 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3170 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3171 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3172 ASSERT_TRUE(server
.good());
3173 ASSERT_TRUE(local
.good());
3174 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3175 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3176 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3177 EXPECT_FALSE(server
.GetIsUnsynced());
3178 EXPECT_FALSE(local
.GetIsUnsynced());
3179 EXPECT_EQ("Bar.htm", server
.GetNonUniqueName());
3180 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3181 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
3182 server
.GetSpecifics().bookmark().url());
3186 // Same as NewEntryAnddServerEntrySharePath, but using the old-style protocol.
3187 TEST_F(SyncerTest
, NewEntryAndAlteredServerEntrySharePath_OldBookmarksProto
) {
3188 mock_server_
->set_use_legacy_bookmarks_protocol(true);
3189 mock_server_
->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
3190 foreign_cache_guid(), "-1");
3192 int64 local_folder_handle
;
3193 syncable::Id local_folder_id
;
3195 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3196 MutableEntry
new_entry(
3197 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bar.htm");
3198 ASSERT_TRUE(new_entry
.good());
3199 local_folder_id
= new_entry
.GetId();
3200 local_folder_handle
= new_entry
.GetMetahandle();
3201 new_entry
.PutIsUnsynced(true);
3202 new_entry
.PutSpecifics(DefaultBookmarkSpecifics());
3203 MutableEntry
old(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3204 ASSERT_TRUE(old
.good());
3205 WriteTestDataToEntry(&wtrans
, &old
);
3207 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
3208 foreign_cache_guid(), "-1");
3209 mock_server_
->set_conflict_all_commits(true);
3211 saw_syncer_event_
= false;
3213 // Update #20 should have been dropped in favor of the local version.
3214 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3215 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3216 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3217 ASSERT_TRUE(server
.good());
3218 ASSERT_TRUE(local
.good());
3219 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3220 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3221 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3222 EXPECT_TRUE(server
.GetIsUnsynced());
3223 EXPECT_TRUE(local
.GetIsUnsynced());
3224 EXPECT_EQ("Foo.htm", server
.GetNonUniqueName());
3225 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3227 // Allow local changes to commit.
3228 mock_server_
->set_conflict_all_commits(false);
3230 saw_syncer_event_
= false;
3232 // Now add a server change to make the two names equal. There should
3233 // be no conflict with that, since names are not unique.
3234 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
3235 foreign_cache_guid(), "-1");
3237 saw_syncer_event_
= false;
3239 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3240 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3241 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3242 ASSERT_TRUE(server
.good());
3243 ASSERT_TRUE(local
.good());
3244 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3245 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3246 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3247 EXPECT_FALSE(server
.GetIsUnsynced());
3248 EXPECT_FALSE(local
.GetIsUnsynced());
3249 EXPECT_EQ("Bar.htm", server
.GetNonUniqueName());
3250 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3251 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
3252 server
.GetSpecifics().bookmark().url());
3256 // Circular links should be resolved by the server.
3257 TEST_F(SyncerTest
, SiblingDirectoriesBecomeCircular
) {
3258 // we don't currently resolve this. This test ensures we don't.
3259 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
3260 foreign_cache_guid(), "-1");
3261 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
3262 foreign_cache_guid(), "-2");
3265 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3266 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3267 ASSERT_TRUE(A
.good());
3268 A
.PutIsUnsynced(true);
3269 A
.PutParentId(ids_
.FromNumber(2));
3270 A
.PutNonUniqueName("B");
3272 mock_server_
->AddUpdateDirectory(2, 1, "A", 20, 20,
3273 foreign_cache_guid(), "-2");
3274 mock_server_
->set_conflict_all_commits(true);
3276 saw_syncer_event_
= false;
3278 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3279 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3280 ASSERT_TRUE(A
.good());
3281 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
3282 ASSERT_TRUE(B
.good());
3283 EXPECT_TRUE(A
.GetNonUniqueName()== "B");
3284 EXPECT_TRUE(B
.GetNonUniqueName()== "B");
3288 TEST_F(SyncerTest
, SwapEntryNames
) {
3289 // Simple transaction test.
3290 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
3291 foreign_cache_guid(), "-1");
3292 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
3293 foreign_cache_guid(), "-2");
3294 mock_server_
->set_conflict_all_commits(true);
3297 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3298 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3299 ASSERT_TRUE(A
.good());
3300 A
.PutIsUnsynced(true);
3301 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
3302 ASSERT_TRUE(B
.good());
3303 B
.PutIsUnsynced(true);
3304 A
.PutNonUniqueName("C");
3305 B
.PutNonUniqueName("A");
3306 A
.PutNonUniqueName("B");
3309 saw_syncer_event_
= false;
3312 TEST_F(SyncerTest
, DualDeletionWithNewItemNameClash
) {
3313 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
3314 foreign_cache_guid(), "-1");
3315 mock_server_
->AddUpdateBookmark(2, 0, "B", 10, 10,
3316 foreign_cache_guid(), "-2");
3317 mock_server_
->set_conflict_all_commits(true);
3320 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3321 MutableEntry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3322 ASSERT_TRUE(B
.good());
3323 WriteTestDataToEntry(&trans
, &B
);
3326 mock_server_
->AddUpdateBookmark(2, 0, "A", 11, 11,
3327 foreign_cache_guid(), "-2");
3328 mock_server_
->SetLastUpdateDeleted();
3331 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3332 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3333 ASSERT_TRUE(B
.good());
3334 EXPECT_FALSE(B
.GetIsUnsynced());
3335 EXPECT_FALSE(B
.GetIsUnappliedUpdate());
3337 saw_syncer_event_
= false;
3340 // When we undelete an entity as a result of conflict resolution, we reuse the
3341 // existing server id and preserve the old version, simply updating the server
3342 // version with the new non-deleted entity.
3343 TEST_F(SyncerTest
, ResolveWeWroteTheyDeleted
) {
3344 int64 bob_metahandle
;
3346 mock_server_
->AddUpdateBookmark(1, 0, "bob", 1, 10,
3347 foreign_cache_guid(), "-1");
3350 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3351 MutableEntry
bob(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3352 ASSERT_TRUE(bob
.good());
3353 bob_metahandle
= bob
.GetMetahandle();
3354 WriteTestDataToEntry(&trans
, &bob
);
3356 mock_server_
->AddUpdateBookmark(1, 0, "bob", 2, 10,
3357 foreign_cache_guid(), "-1");
3358 mock_server_
->SetLastUpdateDeleted();
3359 mock_server_
->set_conflict_all_commits(true);
3363 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3364 Entry
bob(&trans
, GET_BY_HANDLE
, bob_metahandle
);
3365 ASSERT_TRUE(bob
.good());
3366 EXPECT_TRUE(bob
.GetIsUnsynced());
3367 EXPECT_TRUE(bob
.GetId().ServerKnows());
3368 EXPECT_FALSE(bob
.GetIsUnappliedUpdate());
3369 EXPECT_FALSE(bob
.GetIsDel());
3370 EXPECT_EQ(2, bob
.GetServerVersion());
3371 EXPECT_EQ(2, bob
.GetBaseVersion());
3373 saw_syncer_event_
= false;
3376 // This test is to reproduce a check failure. Sometimes we would get a bad ID
3377 // back when creating an entry.
3378 TEST_F(SyncerTest
, DuplicateIDReturn
) {
3380 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3381 MutableEntry
folder(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
3382 ASSERT_TRUE(folder
.good());
3383 folder
.PutIsUnsynced(true);
3384 folder
.PutIsDir(true);
3385 folder
.PutSpecifics(DefaultBookmarkSpecifics());
3386 MutableEntry
folder2(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "fred");
3387 ASSERT_TRUE(folder2
.good());
3388 folder2
.PutIsUnsynced(false);
3389 folder2
.PutIsDir(true);
3390 folder2
.PutSpecifics(DefaultBookmarkSpecifics());
3391 folder2
.PutBaseVersion(3);
3392 folder2
.PutId(syncable::Id::CreateFromServerId("mock_server:10000"));
3394 mock_server_
->set_next_new_id(10000);
3395 EXPECT_EQ(1u, directory()->unsynced_entity_count());
3396 // we get back a bad id in here (should never happen).
3398 EXPECT_EQ(1u, directory()->unsynced_entity_count());
3399 SyncShareNudge(); // another bad id in here.
3400 EXPECT_EQ(0u, directory()->unsynced_entity_count());
3401 saw_syncer_event_
= false;
3404 TEST_F(SyncerTest
, DeletedEntryWithBadParentInLoopCalculation
) {
3405 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
3406 foreign_cache_guid(), "-1");
3409 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3410 MutableEntry
bob(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3411 ASSERT_TRUE(bob
.good());
3412 // This is valid, because the parent could have gone away a long time ago.
3413 bob
.PutParentId(ids_
.FromNumber(54));
3415 bob
.PutIsUnsynced(true);
3417 mock_server_
->AddUpdateDirectory(2, 1, "fred", 1, 10,
3418 foreign_cache_guid(), "-2");
3423 TEST_F(SyncerTest
, ConflictResolverMergesLocalDeleteAndServerUpdate
) {
3424 syncable::Id local_id
;
3426 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3428 MutableEntry
local_deleted(
3429 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "name");
3430 local_id
= local_deleted
.GetId();
3431 local_deleted
.PutId(ids_
.FromNumber(1));
3432 local_deleted
.PutBaseVersion(1);
3433 local_deleted
.PutIsDel(true);
3434 local_deleted
.PutIsDir(false);
3435 local_deleted
.PutIsUnsynced(true);
3436 local_deleted
.PutSpecifics(DefaultBookmarkSpecifics());
3439 mock_server_
->AddUpdateBookmark(ids_
.FromNumber(1), root_id_
, "name", 10, 10,
3441 local_id
.GetServerId());
3443 // We don't care about actually committing, just the resolution.
3444 mock_server_
->set_conflict_all_commits(true);
3448 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3449 Entry
local_deleted(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3450 EXPECT_TRUE(local_deleted
.GetBaseVersion()== 10);
3451 EXPECT_TRUE(local_deleted
.GetIsUnappliedUpdate()== false);
3452 EXPECT_TRUE(local_deleted
.GetIsUnsynced()== true);
3453 EXPECT_TRUE(local_deleted
.GetIsDel()== true);
3454 EXPECT_TRUE(local_deleted
.GetIsDir()== false);
3458 // See what happens if the IS_DIR bit gets flipped. This can cause us
3459 // all kinds of disasters.
3460 TEST_F(SyncerTest
, UpdateFlipsTheFolderBit
) {
3461 // Local object: a deleted directory (container), revision 1, unsynced.
3463 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3465 MutableEntry
local_deleted(
3466 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "name");
3467 local_deleted
.PutId(ids_
.FromNumber(1));
3468 local_deleted
.PutBaseVersion(1);
3469 local_deleted
.PutIsDel(true);
3470 local_deleted
.PutIsDir(true);
3471 local_deleted
.PutIsUnsynced(true);
3472 local_deleted
.PutSpecifics(DefaultBookmarkSpecifics());
3475 // Server update: entry-type object (not a container), revision 10.
3476 mock_server_
->AddUpdateBookmark(ids_
.FromNumber(1), root_id_
, "name", 10, 10,
3478 ids_
.FromNumber(1).GetServerId());
3480 // Don't attempt to commit.
3481 mock_server_
->set_conflict_all_commits(true);
3483 // The syncer should not attempt to apply the invalid update.
3487 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3488 Entry
local_deleted(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3489 EXPECT_TRUE(local_deleted
.GetBaseVersion()== 1);
3490 EXPECT_TRUE(local_deleted
.GetIsUnappliedUpdate()== false);
3491 EXPECT_TRUE(local_deleted
.GetIsUnsynced()== true);
3492 EXPECT_TRUE(local_deleted
.GetIsDel()== true);
3493 EXPECT_TRUE(local_deleted
.GetIsDir()== true);
3498 // Merge conflict resolution will merge a new local entry with another entry
3499 // that needs updates, resulting in CHECK.
3500 TEST_F(SyncerTest
, MergingExistingItems
) {
3501 mock_server_
->set_conflict_all_commits(true);
3502 mock_server_
->AddUpdateBookmark(1, 0, "base", 10, 10,
3503 local_cache_guid(), "-1");
3506 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3508 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "Copy of base");
3509 WriteTestDataToEntry(&trans
, &entry
);
3511 mock_server_
->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
3512 local_cache_guid(), "-1");
3516 // In this test a long changelog contains a child at the start of the changelog
3517 // and a parent at the end. While these updates are in progress the client would
3519 TEST_F(SyncerTest
, LongChangelistWithApplicationConflict
) {
3520 const int depth
= 400;
3521 syncable::Id folder_id
= ids_
.FromNumber(1);
3523 // First we an item in a folder in the root. However the folder won't come
3525 syncable::Id stuck_entry_id
= TestIdFactory::FromNumber(99999);
3526 mock_server_
->AddUpdateDirectory(stuck_entry_id
,
3527 folder_id
, "stuck", 1, 1,
3528 foreign_cache_guid(), "-99999");
3529 mock_server_
->SetChangesRemaining(depth
- 1);
3532 // Buffer up a very long series of downloads.
3533 // We should never be stuck (conflict resolution shouldn't
3534 // kick in so long as we're making forward progress).
3535 for (int i
= 0; i
< depth
; i
++) {
3536 mock_server_
->NextUpdateBatch();
3537 mock_server_
->SetNewTimestamp(i
+ 1);
3538 mock_server_
->SetChangesRemaining(depth
- i
);
3543 // Ensure our folder hasn't somehow applied.
3545 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3546 Entry
child(&trans
, GET_BY_ID
, stuck_entry_id
);
3547 EXPECT_TRUE(child
.good());
3548 EXPECT_TRUE(child
.GetIsUnappliedUpdate());
3549 EXPECT_TRUE(child
.GetIsDel());
3550 EXPECT_FALSE(child
.GetIsUnsynced());
3553 // And finally the folder.
3554 mock_server_
->AddUpdateDirectory(folder_id
,
3555 TestIdFactory::root(), "folder", 1, 1,
3556 foreign_cache_guid(), "-1");
3557 mock_server_
->SetChangesRemaining(0);
3560 // Check that everything is as expected after the commit.
3562 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3563 Entry
entry(&trans
, GET_BY_ID
, folder_id
);
3564 ASSERT_TRUE(entry
.good());
3565 Entry
child(&trans
, GET_BY_ID
, stuck_entry_id
);
3566 EXPECT_EQ(entry
.GetId(), child
.GetParentId());
3567 EXPECT_EQ("stuck", child
.GetNonUniqueName());
3568 EXPECT_TRUE(child
.good());
3572 TEST_F(SyncerTest
, DontMergeTwoExistingItems
) {
3573 mock_server_
->set_conflict_all_commits(true);
3574 mock_server_
->AddUpdateBookmark(1, 0, "base", 10, 10,
3575 foreign_cache_guid(), "-1");
3576 mock_server_
->AddUpdateBookmark(2, 0, "base2", 10, 10,
3577 foreign_cache_guid(), "-2");
3580 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3581 MutableEntry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3582 ASSERT_TRUE(entry
.good());
3583 entry
.PutNonUniqueName("Copy of base");
3584 entry
.PutIsUnsynced(true);
3586 mock_server_
->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
3587 foreign_cache_guid(), "-1");
3590 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3591 Entry
entry1(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3592 EXPECT_FALSE(entry1
.GetIsUnappliedUpdate());
3593 EXPECT_FALSE(entry1
.GetIsUnsynced());
3594 EXPECT_FALSE(entry1
.GetIsDel());
3595 Entry
entry2(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3596 EXPECT_FALSE(entry2
.GetIsUnappliedUpdate());
3597 EXPECT_TRUE(entry2
.GetIsUnsynced());
3598 EXPECT_FALSE(entry2
.GetIsDel());
3599 EXPECT_EQ(entry1
.GetNonUniqueName(), entry2
.GetNonUniqueName());
3603 TEST_F(SyncerTest
, TestUndeleteUpdate
) {
3604 mock_server_
->set_conflict_all_commits(true);
3605 mock_server_
->AddUpdateDirectory(1, 0, "foo", 1, 1,
3606 foreign_cache_guid(), "-1");
3607 mock_server_
->AddUpdateDirectory(2, 1, "bar", 1, 2,
3608 foreign_cache_guid(), "-2");
3610 mock_server_
->AddUpdateDirectory(2, 1, "bar", 2, 3,
3611 foreign_cache_guid(), "-2");
3612 mock_server_
->SetLastUpdateDeleted();
3617 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3618 Entry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3619 ASSERT_TRUE(entry
.good());
3620 EXPECT_TRUE(entry
.GetIsDel());
3621 metahandle
= entry
.GetMetahandle();
3623 mock_server_
->AddUpdateDirectory(1, 0, "foo", 2, 4,
3624 foreign_cache_guid(), "-1");
3625 mock_server_
->SetLastUpdateDeleted();
3627 // This used to be rejected as it's an undeletion. Now, it results in moving
3628 // the delete path aside.
3629 mock_server_
->AddUpdateDirectory(2, 1, "bar", 3, 5,
3630 foreign_cache_guid(), "-2");
3633 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3634 Entry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3635 ASSERT_TRUE(entry
.good());
3636 EXPECT_TRUE(entry
.GetIsDel());
3637 EXPECT_FALSE(entry
.GetServerIsDel());
3638 EXPECT_TRUE(entry
.GetIsUnappliedUpdate());
3639 EXPECT_NE(entry
.GetMetahandle(), metahandle
);
3643 TEST_F(SyncerTest
, TestMoveSanitizedNamedFolder
) {
3644 mock_server_
->AddUpdateDirectory(1, 0, "foo", 1, 1,
3645 foreign_cache_guid(), "-1");
3646 mock_server_
->AddUpdateDirectory(2, 0, ":::", 1, 2,
3647 foreign_cache_guid(), "-2");
3650 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3651 MutableEntry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3652 ASSERT_TRUE(entry
.good());
3653 entry
.PutParentId(ids_
.FromNumber(1));
3654 EXPECT_TRUE(entry
.PutIsUnsynced(true));
3657 // We use the same sync ts as before so our times match up.
3658 mock_server_
->AddUpdateDirectory(2, 1, ":::", 2, 2,
3659 foreign_cache_guid(), "-2");
3663 // Don't crash when this occurs.
3664 TEST_F(SyncerTest
, UpdateWhereParentIsNotAFolder
) {
3665 mock_server_
->AddUpdateBookmark(1, 0, "B", 10, 10,
3666 foreign_cache_guid(), "-1");
3667 mock_server_
->AddUpdateDirectory(2, 1, "BookmarkParent", 10, 10,
3668 foreign_cache_guid(), "-2");
3669 // Used to cause a CHECK
3672 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
3673 Entry
good_entry(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
3674 ASSERT_TRUE(good_entry
.good());
3675 EXPECT_FALSE(good_entry
.GetIsUnappliedUpdate());
3676 Entry
bad_parent(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(2));
3677 ASSERT_TRUE(bad_parent
.good());
3678 EXPECT_TRUE(bad_parent
.GetIsUnappliedUpdate());
3682 TEST_F(SyncerTest
, DirectoryUpdateTest
) {
3683 Id in_root_id
= ids_
.NewServerId();
3684 Id in_in_root_id
= ids_
.NewServerId();
3686 mock_server_
->AddUpdateDirectory(in_root_id
, TestIdFactory::root(),
3687 "in_root_name", 2, 2,
3688 foreign_cache_guid(), "-1");
3689 mock_server_
->AddUpdateDirectory(in_in_root_id
, in_root_id
,
3690 "in_in_root_name", 3, 3,
3691 foreign_cache_guid(), "-2");
3694 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3695 Entry
in_root(&trans
, GET_BY_ID
, in_root_id
);
3696 ASSERT_TRUE(in_root
.good());
3697 EXPECT_EQ("in_root_name", in_root
.GetNonUniqueName());
3698 EXPECT_EQ(TestIdFactory::root(), in_root
.GetParentId());
3700 Entry
in_in_root(&trans
, GET_BY_ID
, in_in_root_id
);
3701 ASSERT_TRUE(in_in_root
.good());
3702 EXPECT_EQ("in_in_root_name", in_in_root
.GetNonUniqueName());
3703 EXPECT_EQ(in_root_id
, in_in_root
.GetParentId());
3707 TEST_F(SyncerTest
, DirectoryCommitTest
) {
3708 syncable::Id in_root_id
, in_dir_id
;
3709 int64 foo_metahandle
;
3710 int64 bar_metahandle
;
3713 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3714 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "foo");
3715 ASSERT_TRUE(parent
.good());
3716 parent
.PutIsUnsynced(true);
3717 parent
.PutIsDir(true);
3718 parent
.PutSpecifics(DefaultBookmarkSpecifics());
3719 in_root_id
= parent
.GetId();
3720 foo_metahandle
= parent
.GetMetahandle();
3722 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent
.GetId(), "bar");
3723 ASSERT_TRUE(child
.good());
3724 child
.PutIsUnsynced(true);
3725 child
.PutIsDir(true);
3726 child
.PutSpecifics(DefaultBookmarkSpecifics());
3727 bar_metahandle
= child
.GetMetahandle();
3728 in_dir_id
= parent
.GetId();
3732 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3733 Entry
fail_by_old_id_entry(&trans
, GET_BY_ID
, in_root_id
);
3734 ASSERT_FALSE(fail_by_old_id_entry
.good());
3736 Entry
foo_entry(&trans
, GET_BY_HANDLE
, foo_metahandle
);
3737 ASSERT_TRUE(foo_entry
.good());
3738 EXPECT_EQ("foo", foo_entry
.GetNonUniqueName());
3739 EXPECT_NE(foo_entry
.GetId(), in_root_id
);
3741 Entry
bar_entry(&trans
, GET_BY_HANDLE
, bar_metahandle
);
3742 ASSERT_TRUE(bar_entry
.good());
3743 EXPECT_EQ("bar", bar_entry
.GetNonUniqueName());
3744 EXPECT_NE(bar_entry
.GetId(), in_dir_id
);
3745 EXPECT_EQ(foo_entry
.GetId(), bar_entry
.GetParentId());
3749 TEST_F(SyncerTest
, TestClientCommandDuringUpdate
) {
3750 using sync_pb::ClientCommand
;
3752 ClientCommand
* command
= new ClientCommand();
3753 command
->set_set_sync_poll_interval(8);
3754 command
->set_set_sync_long_poll_interval(800);
3755 command
->set_sessions_commit_delay_seconds(3141);
3756 sync_pb::CustomNudgeDelay
* bookmark_delay
=
3757 command
->add_custom_nudge_delays();
3758 bookmark_delay
->set_datatype_id(
3759 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3760 bookmark_delay
->set_delay_ms(950);
3761 command
->set_client_invalidation_hint_buffer_size(11);
3762 mock_server_
->AddUpdateDirectory(1, 0, "in_root", 1, 1,
3763 foreign_cache_guid(), "-1");
3764 mock_server_
->SetGUClientCommand(command
);
3767 EXPECT_EQ(TimeDelta::FromSeconds(8), last_short_poll_interval_received_
);
3768 EXPECT_EQ(TimeDelta::FromSeconds(800), last_long_poll_interval_received_
);
3769 EXPECT_EQ(TimeDelta::FromSeconds(3141), last_sessions_commit_delay_
);
3770 EXPECT_EQ(TimeDelta::FromMilliseconds(950), last_bookmarks_commit_delay_
);
3771 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_
);
3773 command
= new ClientCommand();
3774 command
->set_set_sync_poll_interval(180);
3775 command
->set_set_sync_long_poll_interval(190);
3776 command
->set_sessions_commit_delay_seconds(2718);
3777 bookmark_delay
= command
->add_custom_nudge_delays();
3778 bookmark_delay
->set_datatype_id(
3779 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3780 bookmark_delay
->set_delay_ms(1050);
3781 command
->set_client_invalidation_hint_buffer_size(9);
3782 mock_server_
->AddUpdateDirectory(
3783 1, 0, "in_root", 1, 1, foreign_cache_guid(), "-1");
3784 mock_server_
->SetGUClientCommand(command
);
3787 EXPECT_EQ(TimeDelta::FromSeconds(180), last_short_poll_interval_received_
);
3788 EXPECT_EQ(TimeDelta::FromSeconds(190), last_long_poll_interval_received_
);
3789 EXPECT_EQ(TimeDelta::FromSeconds(2718), last_sessions_commit_delay_
);
3790 EXPECT_EQ(TimeDelta::FromMilliseconds(1050), last_bookmarks_commit_delay_
);
3791 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_
);
3794 TEST_F(SyncerTest
, TestClientCommandDuringCommit
) {
3795 using sync_pb::ClientCommand
;
3797 ClientCommand
* command
= new ClientCommand();
3798 command
->set_set_sync_poll_interval(8);
3799 command
->set_set_sync_long_poll_interval(800);
3800 command
->set_sessions_commit_delay_seconds(3141);
3801 sync_pb::CustomNudgeDelay
* bookmark_delay
=
3802 command
->add_custom_nudge_delays();
3803 bookmark_delay
->set_datatype_id(
3804 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3805 bookmark_delay
->set_delay_ms(950);
3806 command
->set_client_invalidation_hint_buffer_size(11);
3807 CreateUnsyncedDirectory("X", "id_X");
3808 mock_server_
->SetCommitClientCommand(command
);
3811 EXPECT_EQ(TimeDelta::FromSeconds(8), last_short_poll_interval_received_
);
3812 EXPECT_EQ(TimeDelta::FromSeconds(800), last_long_poll_interval_received_
);
3813 EXPECT_EQ(TimeDelta::FromSeconds(3141), last_sessions_commit_delay_
);
3814 EXPECT_EQ(TimeDelta::FromMilliseconds(950), last_bookmarks_commit_delay_
);
3815 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_
);
3817 command
= new ClientCommand();
3818 command
->set_set_sync_poll_interval(180);
3819 command
->set_set_sync_long_poll_interval(190);
3820 command
->set_sessions_commit_delay_seconds(2718);
3821 bookmark_delay
= command
->add_custom_nudge_delays();
3822 bookmark_delay
->set_datatype_id(
3823 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3824 bookmark_delay
->set_delay_ms(1050);
3825 command
->set_client_invalidation_hint_buffer_size(9);
3826 CreateUnsyncedDirectory("Y", "id_Y");
3827 mock_server_
->SetCommitClientCommand(command
);
3830 EXPECT_EQ(TimeDelta::FromSeconds(180), last_short_poll_interval_received_
);
3831 EXPECT_EQ(TimeDelta::FromSeconds(190), last_long_poll_interval_received_
);
3832 EXPECT_EQ(TimeDelta::FromSeconds(2718), last_sessions_commit_delay_
);
3833 EXPECT_EQ(TimeDelta::FromMilliseconds(1050), last_bookmarks_commit_delay_
);
3834 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_
);
3837 TEST_F(SyncerTest
, EnsureWeSendUpOldParent
) {
3838 syncable::Id folder_one_id
= ids_
.FromNumber(1);
3839 syncable::Id folder_two_id
= ids_
.FromNumber(2);
3841 mock_server_
->AddUpdateDirectory(folder_one_id
, TestIdFactory::root(),
3842 "folder_one", 1, 1, foreign_cache_guid(), "-1");
3843 mock_server_
->AddUpdateDirectory(folder_two_id
, TestIdFactory::root(),
3844 "folder_two", 1, 1, foreign_cache_guid(), "-2");
3847 // A moved entry should send an "old parent."
3848 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3849 MutableEntry
entry(&trans
, GET_BY_ID
, folder_one_id
);
3850 ASSERT_TRUE(entry
.good());
3851 entry
.PutParentId(folder_two_id
);
3852 entry
.PutIsUnsynced(true);
3853 // A new entry should send no "old parent."
3854 MutableEntry
create(
3855 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_folder");
3856 create
.PutIsUnsynced(true);
3857 create
.PutSpecifics(DefaultBookmarkSpecifics());
3860 const sync_pb::CommitMessage
& commit
= mock_server_
->last_sent_commit();
3861 ASSERT_EQ(2, commit
.entries_size());
3862 EXPECT_TRUE(commit
.entries(0).parent_id_string() == "2");
3863 EXPECT_TRUE(commit
.entries(0).old_parent_id() == "0");
3864 EXPECT_FALSE(commit
.entries(1).has_old_parent_id());
3867 TEST_F(SyncerTest
, Test64BitVersionSupport
) {
3868 int64 really_big_int
= std::numeric_limits
<int64
>::max() - 12;
3869 const string
name("ringo's dang orang ran rings around my o-ring");
3870 int64 item_metahandle
;
3872 // Try writing max int64 to the version fields of a meta entry.
3874 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3875 MutableEntry
entry(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), name
);
3876 ASSERT_TRUE(entry
.good());
3877 entry
.PutBaseVersion(really_big_int
);
3878 entry
.PutServerVersion(really_big_int
);
3879 entry
.PutId(ids_
.NewServerId());
3880 item_metahandle
= entry
.GetMetahandle();
3882 // Now read it back out and make sure the value is max int64.
3883 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
3884 Entry
entry(&rtrans
, syncable::GET_BY_HANDLE
, item_metahandle
);
3885 ASSERT_TRUE(entry
.good());
3886 EXPECT_TRUE(really_big_int
== entry
.GetBaseVersion());
3889 TEST_F(SyncerTest
, TestSimpleUndelete
) {
3890 Id id
= ids_
.MakeServer("undeletion item"), root
= TestIdFactory::root();
3891 mock_server_
->set_conflict_all_commits(true);
3892 // Let there be an entry from the server.
3893 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 1, 10,
3894 foreign_cache_guid(), "-1");
3896 // Check it out and delete it.
3898 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3899 MutableEntry
entry(&wtrans
, GET_BY_ID
, id
);
3900 ASSERT_TRUE(entry
.good());
3901 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3902 EXPECT_FALSE(entry
.GetIsUnsynced());
3903 EXPECT_FALSE(entry
.GetIsDel());
3904 // Delete it locally.
3905 entry
.PutIsDel(true);
3908 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3910 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3911 Entry
entry(&trans
, GET_BY_ID
, id
);
3912 ASSERT_TRUE(entry
.good());
3913 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3914 EXPECT_FALSE(entry
.GetIsUnsynced());
3915 EXPECT_TRUE(entry
.GetIsDel());
3916 EXPECT_FALSE(entry
.GetServerIsDel());
3919 // Update from server confirming deletion.
3920 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 11,
3921 foreign_cache_guid(), "-1");
3922 mock_server_
->SetLastUpdateDeleted();
3924 // IS_DEL AND SERVER_IS_DEL now both true.
3926 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3927 Entry
entry(&trans
, GET_BY_ID
, id
);
3928 ASSERT_TRUE(entry
.good());
3929 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3930 EXPECT_FALSE(entry
.GetIsUnsynced());
3931 EXPECT_TRUE(entry
.GetIsDel());
3932 EXPECT_TRUE(entry
.GetServerIsDel());
3934 // Undelete from server.
3935 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 12,
3936 foreign_cache_guid(), "-1");
3938 // IS_DEL and SERVER_IS_DEL now both false.
3940 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3941 Entry
entry(&trans
, GET_BY_ID
, id
);
3942 ASSERT_TRUE(entry
.good());
3943 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3944 EXPECT_FALSE(entry
.GetIsUnsynced());
3945 EXPECT_FALSE(entry
.GetIsDel());
3946 EXPECT_FALSE(entry
.GetServerIsDel());
3950 TEST_F(SyncerTest
, TestUndeleteWithMissingDeleteUpdate
) {
3951 Id id
= ids_
.MakeServer("undeletion item"), root
= TestIdFactory::root();
3952 // Let there be a entry, from the server.
3953 mock_server_
->set_conflict_all_commits(true);
3954 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 1, 10,
3955 foreign_cache_guid(), "-1");
3957 // Check it out and delete it.
3959 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3960 MutableEntry
entry(&wtrans
, GET_BY_ID
, id
);
3961 ASSERT_TRUE(entry
.good());
3962 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3963 EXPECT_FALSE(entry
.GetIsUnsynced());
3964 EXPECT_FALSE(entry
.GetIsDel());
3965 // Delete it locally.
3966 entry
.PutIsDel(true);
3969 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3971 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3972 Entry
entry(&trans
, GET_BY_ID
, id
);
3973 ASSERT_TRUE(entry
.good());
3974 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3975 EXPECT_FALSE(entry
.GetIsUnsynced());
3976 EXPECT_TRUE(entry
.GetIsDel());
3977 EXPECT_FALSE(entry
.GetServerIsDel());
3980 // Say we do not get an update from server confirming deletion. Undelete
3982 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 12,
3983 foreign_cache_guid(), "-1");
3985 // IS_DEL and SERVER_IS_DEL now both false.
3987 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3988 Entry
entry(&trans
, GET_BY_ID
, id
);
3989 ASSERT_TRUE(entry
.good());
3990 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3991 EXPECT_FALSE(entry
.GetIsUnsynced());
3992 EXPECT_FALSE(entry
.GetIsDel());
3993 EXPECT_FALSE(entry
.GetServerIsDel());
3997 TEST_F(SyncerTest
, TestUndeleteIgnoreCorrectlyUnappliedUpdate
) {
3998 Id id1
= ids_
.MakeServer("first"), id2
= ids_
.MakeServer("second");
3999 Id root
= TestIdFactory::root();
4000 // Duplicate! expect path clashing!
4001 mock_server_
->set_conflict_all_commits(true);
4002 mock_server_
->AddUpdateBookmark(id1
, root
, "foo", 1, 10,
4003 foreign_cache_guid(), "-1");
4004 mock_server_
->AddUpdateBookmark(id2
, root
, "foo", 1, 10,
4005 foreign_cache_guid(), "-2");
4007 mock_server_
->AddUpdateBookmark(id2
, root
, "foo2", 2, 20,
4008 foreign_cache_guid(), "-2");
4009 SyncShareNudge(); // Now just don't explode.
4012 TEST_F(SyncerTest
, ClientTagServerCreatedUpdatesWork
) {
4013 mock_server_
->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
4014 foreign_cache_guid(), "-1");
4015 mock_server_
->SetLastUpdateClientTag("permfolder");
4020 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4021 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
4022 ASSERT_TRUE(perm_folder
.good());
4023 EXPECT_FALSE(perm_folder
.GetIsDel());
4024 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
4025 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
4026 EXPECT_EQ(perm_folder
.GetUniqueClientTag(), "permfolder");
4027 EXPECT_EQ(perm_folder
.GetNonUniqueName(), "permitem1");
4030 mock_server_
->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
4031 foreign_cache_guid(), "-1");
4032 mock_server_
->SetLastUpdateClientTag("permfolder");
4036 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4038 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
4039 ASSERT_TRUE(perm_folder
.good());
4040 EXPECT_FALSE(perm_folder
.GetIsDel());
4041 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
4042 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
4043 EXPECT_EQ(perm_folder
.GetUniqueClientTag(), "permfolder");
4044 EXPECT_EQ(perm_folder
.GetNonUniqueName(), "permitem_renamed");
4048 TEST_F(SyncerTest
, ClientTagIllegalUpdateIgnored
) {
4049 mock_server_
->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
4050 foreign_cache_guid(), "-1");
4051 mock_server_
->SetLastUpdateClientTag("permfolder");
4056 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4057 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
4058 ASSERT_TRUE(perm_folder
.good());
4059 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
4060 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
4061 EXPECT_EQ(perm_folder
.GetUniqueClientTag(), "permfolder");
4062 EXPECT_TRUE(perm_folder
.GetNonUniqueName()== "permitem1");
4063 EXPECT_TRUE(perm_folder
.GetId().ServerKnows());
4066 mock_server_
->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
4067 foreign_cache_guid(), "-1");
4068 mock_server_
->SetLastUpdateClientTag("wrongtag");
4072 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4074 // This update is rejected because it has the same ID, but a
4075 // different tag than one that is already on the client.
4076 // The client has a ServerKnows ID, which cannot be overwritten.
4077 Entry
rejected_update(&trans
, GET_BY_CLIENT_TAG
, "wrongtag");
4078 EXPECT_FALSE(rejected_update
.good());
4080 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
4081 ASSERT_TRUE(perm_folder
.good());
4082 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
4083 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
4084 EXPECT_EQ(perm_folder
.GetNonUniqueName(), "permitem1");
4088 TEST_F(SyncerTest
, ClientTagUncommittedTagMatchesUpdate
) {
4089 int64 original_metahandle
= 0;
4092 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4094 &trans
, CREATE
, PREFERENCES
, ids_
.root(), "name");
4095 ASSERT_TRUE(pref
.good());
4096 pref
.PutUniqueClientTag("tag");
4097 pref
.PutIsUnsynced(true);
4098 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
4099 EXPECT_FALSE(pref
.GetId().ServerKnows());
4100 original_metahandle
= pref
.GetMetahandle();
4103 syncable::Id server_id
= TestIdFactory::MakeServer("id");
4104 mock_server_
->AddUpdatePref(server_id
.GetServerId(),
4105 ids_
.root().GetServerId(),
4107 mock_server_
->set_conflict_all_commits(true);
4110 // This should cause client tag reunion, preserving the metahandle.
4112 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4114 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
4115 ASSERT_TRUE(pref
.good());
4116 EXPECT_FALSE(pref
.GetIsDel());
4117 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
4118 EXPECT_TRUE(pref
.GetIsUnsynced());
4119 EXPECT_EQ(10, pref
.GetBaseVersion());
4120 // Entry should have been given the new ID while preserving the
4121 // metahandle; client should have won the conflict resolution.
4122 EXPECT_EQ(original_metahandle
, pref
.GetMetahandle());
4123 EXPECT_EQ("tag", pref
.GetUniqueClientTag());
4124 EXPECT_TRUE(pref
.GetId().ServerKnows());
4127 mock_server_
->set_conflict_all_commits(false);
4130 // The resolved entry ought to commit cleanly.
4132 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4134 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
4135 ASSERT_TRUE(pref
.good());
4136 EXPECT_FALSE(pref
.GetIsDel());
4137 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
4138 EXPECT_FALSE(pref
.GetIsUnsynced());
4139 EXPECT_TRUE(10 < pref
.GetBaseVersion());
4140 // Entry should have been given the new ID while preserving the
4141 // metahandle; client should have won the conflict resolution.
4142 EXPECT_EQ(original_metahandle
, pref
.GetMetahandle());
4143 EXPECT_EQ("tag", pref
.GetUniqueClientTag());
4144 EXPECT_TRUE(pref
.GetId().ServerKnows());
4148 TEST_F(SyncerTest
, ClientTagConflictWithDeletedLocalEntry
) {
4150 // Create a deleted local entry with a unique client tag.
4151 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4153 &trans
, CREATE
, PREFERENCES
, ids_
.root(), "name");
4154 ASSERT_TRUE(pref
.good());
4155 ASSERT_FALSE(pref
.GetId().ServerKnows());
4156 pref
.PutUniqueClientTag("tag");
4157 pref
.PutIsUnsynced(true);
4159 // Note: IS_DEL && !ServerKnows() will clear the UNSYNCED bit.
4160 // (We never attempt to commit server-unknown deleted items, so this
4161 // helps us clean up those entries).
4162 pref
.PutIsDel(true);
4165 // Prepare an update with the same unique client tag.
4166 syncable::Id server_id
= TestIdFactory::MakeServer("id");
4167 mock_server_
->AddUpdatePref(server_id
.GetServerId(),
4168 ids_
.root().GetServerId(),
4172 // The local entry will be overwritten.
4174 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4176 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
4177 ASSERT_TRUE(pref
.good());
4178 ASSERT_TRUE(pref
.GetId().ServerKnows());
4179 EXPECT_FALSE(pref
.GetIsDel());
4180 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
4181 EXPECT_FALSE(pref
.GetIsUnsynced());
4182 EXPECT_EQ(pref
.GetBaseVersion(), 10);
4183 EXPECT_EQ(pref
.GetUniqueClientTag(), "tag");
4187 TEST_F(SyncerTest
, ClientTagUpdateClashesWithLocalEntry
) {
4188 // This test is written assuming that ID comparison
4189 // will work out in a particular way.
4190 EXPECT_TRUE(ids_
.FromNumber(1) < ids_
.FromNumber(2));
4191 EXPECT_TRUE(ids_
.FromNumber(3) < ids_
.FromNumber(4));
4193 syncable::Id id1
= TestIdFactory::MakeServer("1");
4194 mock_server_
->AddUpdatePref(id1
.GetServerId(), "", "tag1", 10, 100);
4196 syncable::Id id4
= TestIdFactory::MakeServer("4");
4197 mock_server_
->AddUpdatePref(id4
.GetServerId(), "", "tag2", 11, 110);
4199 mock_server_
->set_conflict_all_commits(true);
4202 int64 tag1_metahandle
= syncable::kInvalidMetaHandle
;
4203 int64 tag2_metahandle
= syncable::kInvalidMetaHandle
;
4204 // This should cause client tag overwrite.
4206 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4208 Entry
tag1(&trans
, GET_BY_CLIENT_TAG
, "tag1");
4209 ASSERT_TRUE(tag1
.good());
4210 ASSERT_TRUE(tag1
.GetId().ServerKnows());
4211 ASSERT_TRUE(id1
== tag1
.GetId());
4212 EXPECT_FALSE(tag1
.GetIsDel());
4213 EXPECT_FALSE(tag1
.GetIsUnappliedUpdate());
4214 EXPECT_FALSE(tag1
.GetIsUnsynced());
4215 EXPECT_EQ(10, tag1
.GetBaseVersion());
4216 EXPECT_EQ("tag1", tag1
.GetUniqueClientTag());
4217 tag1_metahandle
= tag1
.GetMetahandle();
4219 Entry
tag2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
4220 ASSERT_TRUE(tag2
.good());
4221 ASSERT_TRUE(tag2
.GetId().ServerKnows());
4222 ASSERT_TRUE(id4
== tag2
.GetId());
4223 EXPECT_FALSE(tag2
.GetIsDel());
4224 EXPECT_FALSE(tag2
.GetIsUnappliedUpdate());
4225 EXPECT_FALSE(tag2
.GetIsUnsynced());
4226 EXPECT_EQ(11, tag2
.GetBaseVersion());
4227 EXPECT_EQ("tag2", tag2
.GetUniqueClientTag());
4228 tag2_metahandle
= tag2
.GetMetahandle();
4230 // Preferences type root should have been created by the updates above.
4231 Entry
pref_root(&trans
, GET_TYPE_ROOT
, PREFERENCES
);
4232 ASSERT_TRUE(pref_root
.good());
4234 syncable::Directory::Metahandles children
;
4235 directory()->GetChildHandlesById(&trans
, pref_root
.GetId(), &children
);
4236 ASSERT_EQ(2U, children
.size());
4239 syncable::Id id2
= TestIdFactory::MakeServer("2");
4240 mock_server_
->AddUpdatePref(id2
.GetServerId(), "", "tag1", 12, 120);
4241 syncable::Id id3
= TestIdFactory::MakeServer("3");
4242 mock_server_
->AddUpdatePref(id3
.GetServerId(), "", "tag2", 13, 130);
4246 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4248 Entry
tag1(&trans
, GET_BY_CLIENT_TAG
, "tag1");
4249 ASSERT_TRUE(tag1
.good());
4250 ASSERT_TRUE(tag1
.GetId().ServerKnows());
4251 ASSERT_EQ(id1
, tag1
.GetId())
4252 << "ID 1 should be kept, since it was less than ID 2.";
4253 EXPECT_FALSE(tag1
.GetIsDel());
4254 EXPECT_FALSE(tag1
.GetIsUnappliedUpdate());
4255 EXPECT_FALSE(tag1
.GetIsUnsynced());
4256 EXPECT_EQ(10, tag1
.GetBaseVersion());
4257 EXPECT_EQ("tag1", tag1
.GetUniqueClientTag());
4258 EXPECT_EQ(tag1_metahandle
, tag1
.GetMetahandle());
4260 Entry
tag2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
4261 ASSERT_TRUE(tag2
.good());
4262 ASSERT_TRUE(tag2
.GetId().ServerKnows());
4263 ASSERT_EQ(id3
, tag2
.GetId())
4264 << "ID 3 should be kept, since it was less than ID 4.";
4265 EXPECT_FALSE(tag2
.GetIsDel());
4266 EXPECT_FALSE(tag2
.GetIsUnappliedUpdate());
4267 EXPECT_FALSE(tag2
.GetIsUnsynced());
4268 EXPECT_EQ(13, tag2
.GetBaseVersion());
4269 EXPECT_EQ("tag2", tag2
.GetUniqueClientTag());
4270 EXPECT_EQ(tag2_metahandle
, tag2
.GetMetahandle());
4272 // Preferences type root should have been created by the updates above.
4273 Entry
pref_root(&trans
, GET_TYPE_ROOT
, PREFERENCES
);
4274 ASSERT_TRUE(pref_root
.good());
4276 syncable::Directory::Metahandles children
;
4277 directory()->GetChildHandlesById(&trans
, pref_root
.GetId(), &children
);
4278 ASSERT_EQ(2U, children
.size());
4282 TEST_F(SyncerTest
, ClientTagClashWithinBatchOfUpdates
) {
4283 // This test is written assuming that ID comparison
4284 // will work out in a particular way.
4285 EXPECT_TRUE(ids_
.FromNumber(1) < ids_
.FromNumber(4));
4286 EXPECT_TRUE(ids_
.FromNumber(201) < ids_
.FromNumber(205));
4288 // Least ID: winner.
4289 mock_server_
->AddUpdatePref(ids_
.FromNumber(1).GetServerId(), "", "tag a", 1,
4291 mock_server_
->AddUpdatePref(ids_
.FromNumber(2).GetServerId(), "", "tag a", 11,
4293 mock_server_
->AddUpdatePref(ids_
.FromNumber(3).GetServerId(), "", "tag a", 12,
4295 mock_server_
->AddUpdatePref(ids_
.FromNumber(4).GetServerId(), "", "tag a", 13,
4297 mock_server_
->AddUpdatePref(ids_
.FromNumber(105).GetServerId(), "", "tag b",
4299 mock_server_
->AddUpdatePref(ids_
.FromNumber(102).GetServerId(), "", "tag b",
4301 // Least ID: winner.
4302 mock_server_
->AddUpdatePref(ids_
.FromNumber(101).GetServerId(), "", "tag b",
4304 mock_server_
->AddUpdatePref(ids_
.FromNumber(104).GetServerId(), "", "tag b",
4307 mock_server_
->AddUpdatePref(ids_
.FromNumber(205).GetServerId(), "", "tag c",
4309 mock_server_
->AddUpdatePref(ids_
.FromNumber(202).GetServerId(), "", "tag c",
4311 mock_server_
->AddUpdatePref(ids_
.FromNumber(204).GetServerId(), "", "tag c",
4313 // Least ID: winner.
4314 mock_server_
->AddUpdatePref(ids_
.FromNumber(201).GetServerId(), "", "tag c",
4317 mock_server_
->set_conflict_all_commits(true);
4320 // This should cause client tag overwrite.
4322 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4324 Entry
tag_a(&trans
, GET_BY_CLIENT_TAG
, "tag a");
4325 ASSERT_TRUE(tag_a
.good());
4326 EXPECT_TRUE(tag_a
.GetId().ServerKnows());
4327 EXPECT_EQ(ids_
.FromNumber(1), tag_a
.GetId());
4328 EXPECT_FALSE(tag_a
.GetIsDel());
4329 EXPECT_FALSE(tag_a
.GetIsUnappliedUpdate());
4330 EXPECT_FALSE(tag_a
.GetIsUnsynced());
4331 EXPECT_EQ(1, tag_a
.GetBaseVersion());
4332 EXPECT_EQ("tag a", tag_a
.GetUniqueClientTag());
4334 Entry
tag_b(&trans
, GET_BY_CLIENT_TAG
, "tag b");
4335 ASSERT_TRUE(tag_b
.good());
4336 EXPECT_TRUE(tag_b
.GetId().ServerKnows());
4337 EXPECT_EQ(ids_
.FromNumber(101), tag_b
.GetId());
4338 EXPECT_FALSE(tag_b
.GetIsDel());
4339 EXPECT_FALSE(tag_b
.GetIsUnappliedUpdate());
4340 EXPECT_FALSE(tag_b
.GetIsUnsynced());
4341 EXPECT_EQ(16, tag_b
.GetBaseVersion());
4342 EXPECT_EQ("tag b", tag_b
.GetUniqueClientTag());
4344 Entry
tag_c(&trans
, GET_BY_CLIENT_TAG
, "tag c");
4345 ASSERT_TRUE(tag_c
.good());
4346 EXPECT_TRUE(tag_c
.GetId().ServerKnows());
4347 EXPECT_EQ(ids_
.FromNumber(201), tag_c
.GetId());
4348 EXPECT_FALSE(tag_c
.GetIsDel());
4349 EXPECT_FALSE(tag_c
.GetIsUnappliedUpdate());
4350 EXPECT_FALSE(tag_c
.GetIsUnsynced());
4351 EXPECT_EQ(21, tag_c
.GetBaseVersion());
4352 EXPECT_EQ("tag c", tag_c
.GetUniqueClientTag());
4354 // Preferences type root should have been created by the updates above.
4355 Entry
pref_root(&trans
, GET_TYPE_ROOT
, PREFERENCES
);
4356 ASSERT_TRUE(pref_root
.good());
4358 // Verify that we have exactly 3 tagged nodes under the type root.
4359 syncable::Directory::Metahandles children
;
4360 directory()->GetChildHandlesById(&trans
, pref_root
.GetId(), &children
);
4361 ASSERT_EQ(3U, children
.size());
4365 // This verifies transition to implicit permanent folders.
4366 TEST_F(SyncerTest
, EntryWithParentIdUpdatedWithEntryWithoutParentId
) {
4367 // Make sure SPECIFICS root exists so that we can get its parent ID.
4368 mock_server_
->AddUpdateSpecifics(1, 0, "Folder", 10, 10, true, 1,
4369 DefaultPreferencesSpecifics());
4370 mock_server_
->SetLastUpdateServerTag(ModelTypeToRootTag(PREFERENCES
));
4375 // Preferences type root should have been created by the update above.
4376 // We need it in order to get its ID.
4377 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4378 Entry
pref_root(&trans
, GET_TYPE_ROOT
, PREFERENCES
);
4379 ASSERT_TRUE(pref_root
.good());
4380 pref_root_id
= pref_root
.GetId();
4383 // Add a preference item with explicit parent ID.
4384 mock_server_
->AddUpdatePref(ids_
.FromNumber(2).GetServerId(),
4385 ids_
.FromNumber(1).GetServerId(), "tag", 1, 10);
4390 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4391 Entry
pref_entry(&trans
, GET_BY_CLIENT_TAG
, "tag");
4392 ASSERT_TRUE(pref_entry
.good());
4393 ASSERT_EQ(pref_root_id
, pref_entry
.GetParentId());
4396 // Make another update where the same item get updated, this time
4397 // with implicit parent ID.
4398 mock_server_
->AddUpdatePref(ids_
.FromNumber(2).GetServerId(), "", "tag", 2,
4404 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4405 Entry
pref_entry(&trans
, GET_BY_CLIENT_TAG
, "tag");
4406 ASSERT_TRUE(pref_entry
.good());
4407 ASSERT_TRUE(pref_entry
.GetParentId().IsNull());
4409 // Verify that there is still one node under the type root.
4410 syncable::Directory::Metahandles children
;
4411 directory()->GetChildHandlesById(&trans
, pref_root_id
, &children
);
4412 ASSERT_EQ(1U, children
.size());
4416 TEST_F(SyncerTest
, UniqueServerTagUpdates
) {
4417 // As a hurdle, introduce an item whose name is the same as the tag value
4419 int64 hurdle_handle
= CreateUnsyncedDirectory("bob", "id_bob");
4421 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4422 Entry
hurdle(&trans
, GET_BY_HANDLE
, hurdle_handle
);
4423 ASSERT_TRUE(hurdle
.good());
4424 ASSERT_TRUE(!hurdle
.GetIsDel());
4425 ASSERT_TRUE(hurdle
.GetUniqueServerTag().empty());
4426 ASSERT_TRUE(hurdle
.GetNonUniqueName()== "bob");
4428 // Try to lookup by the tagname. These should fail.
4429 Entry
tag_alpha(&trans
, GET_BY_SERVER_TAG
, "alpha");
4430 EXPECT_FALSE(tag_alpha
.good());
4431 Entry
tag_bob(&trans
, GET_BY_SERVER_TAG
, "bob");
4432 EXPECT_FALSE(tag_bob
.good());
4435 // Now download some tagged items as updates.
4436 mock_server_
->AddUpdateDirectory(
4437 1, 0, "update1", 1, 10, std::string(), std::string());
4438 mock_server_
->SetLastUpdateServerTag("alpha");
4439 mock_server_
->AddUpdateDirectory(
4440 2, 0, "update2", 2, 20, std::string(), std::string());
4441 mock_server_
->SetLastUpdateServerTag("bob");
4445 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4447 // The new items should be applied as new entries, and we should be able
4448 // to look them up by their tag values.
4449 Entry
tag_alpha(&trans
, GET_BY_SERVER_TAG
, "alpha");
4450 ASSERT_TRUE(tag_alpha
.good());
4451 ASSERT_TRUE(!tag_alpha
.GetIsDel());
4452 ASSERT_TRUE(tag_alpha
.GetUniqueServerTag()== "alpha");
4453 ASSERT_TRUE(tag_alpha
.GetNonUniqueName()== "update1");
4454 Entry
tag_bob(&trans
, GET_BY_SERVER_TAG
, "bob");
4455 ASSERT_TRUE(tag_bob
.good());
4456 ASSERT_TRUE(!tag_bob
.GetIsDel());
4457 ASSERT_TRUE(tag_bob
.GetUniqueServerTag()== "bob");
4458 ASSERT_TRUE(tag_bob
.GetNonUniqueName()== "update2");
4459 // The old item should be unchanged.
4460 Entry
hurdle(&trans
, GET_BY_HANDLE
, hurdle_handle
);
4461 ASSERT_TRUE(hurdle
.good());
4462 ASSERT_TRUE(!hurdle
.GetIsDel());
4463 ASSERT_TRUE(hurdle
.GetUniqueServerTag().empty());
4464 ASSERT_TRUE(hurdle
.GetNonUniqueName()== "bob");
4468 TEST_F(SyncerTest
, GetUpdatesSetsRequestedTypes
) {
4469 // The expectations of this test happen in the MockConnectionManager's
4470 // GetUpdates handler. EnableDatatype sets the expectation value from our
4471 // set of enabled/disabled datatypes.
4472 EnableDatatype(BOOKMARKS
);
4474 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4476 EnableDatatype(AUTOFILL
);
4478 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4480 EnableDatatype(PREFERENCES
);
4482 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4484 DisableDatatype(BOOKMARKS
);
4486 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4488 DisableDatatype(AUTOFILL
);
4490 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4492 DisableDatatype(PREFERENCES
);
4493 EnableDatatype(AUTOFILL
);
4495 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4498 // A typical scenario: server and client each have one update for the other.
4499 // This is the "happy path" alternative to UpdateFailsThenDontCommit.
4500 TEST_F(SyncerTest
, UpdateThenCommit
) {
4501 syncable::Id to_receive
= ids_
.NewServerId();
4502 syncable::Id to_commit
= ids_
.NewLocalId();
4504 mock_server_
->AddUpdateDirectory(to_receive
, ids_
.root(), "x", 1, 10,
4505 foreign_cache_guid(), "-1");
4506 int64 commit_handle
= CreateUnsyncedDirectory("y", to_commit
);
4509 // The sync cycle should have included a GetUpdate, then a commit. By the
4510 // time the commit happened, we should have known for sure that there were no
4511 // hierarchy conflicts, and reported this fact to the server.
4512 ASSERT_TRUE(mock_server_
->last_request().has_commit());
4513 VerifyNoHierarchyConflictsReported(mock_server_
->last_request());
4515 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4517 Entry
received(&trans
, GET_BY_ID
, to_receive
);
4518 ASSERT_TRUE(received
.good());
4519 EXPECT_FALSE(received
.GetIsUnsynced());
4520 EXPECT_FALSE(received
.GetIsUnappliedUpdate());
4522 Entry
committed(&trans
, GET_BY_HANDLE
, commit_handle
);
4523 ASSERT_TRUE(committed
.good());
4524 EXPECT_FALSE(committed
.GetIsUnsynced());
4525 EXPECT_FALSE(committed
.GetIsUnappliedUpdate());
4528 // Same as above, but this time we fail to download updates.
4529 // We should not attempt to commit anything unless we successfully downloaded
4530 // updates, otherwise we risk causing a server-side conflict.
4531 TEST_F(SyncerTest
, UpdateFailsThenDontCommit
) {
4532 syncable::Id to_receive
= ids_
.NewServerId();
4533 syncable::Id to_commit
= ids_
.NewLocalId();
4535 mock_server_
->AddUpdateDirectory(to_receive
, ids_
.root(), "x", 1, 10,
4536 foreign_cache_guid(), "-1");
4537 int64 commit_handle
= CreateUnsyncedDirectory("y", to_commit
);
4538 mock_server_
->FailNextPostBufferToPathCall();
4541 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4543 // We did not receive this update.
4544 Entry
received(&trans
, GET_BY_ID
, to_receive
);
4545 ASSERT_FALSE(received
.good());
4547 // And our local update remains unapplied.
4548 Entry
committed(&trans
, GET_BY_HANDLE
, commit_handle
);
4549 ASSERT_TRUE(committed
.good());
4550 EXPECT_TRUE(committed
.GetIsUnsynced());
4551 EXPECT_FALSE(committed
.GetIsUnappliedUpdate());
4553 // Inform the Mock we won't be fetching all updates.
4554 mock_server_
->ClearUpdatesQueue();
4557 // Downloads two updates and applies them successfully.
4558 // This is the "happy path" alternative to ConfigureFailsDontApplyUpdates.
4559 TEST_F(SyncerTest
, ConfigureDownloadsTwoBatchesSuccess
) {
4560 syncable::Id node1
= ids_
.NewServerId();
4561 syncable::Id node2
= ids_
.NewServerId();
4563 // Construct the first GetUpdates response.
4564 mock_server_
->AddUpdateDirectory(node1
, ids_
.root(), "one", 1, 10,
4565 foreign_cache_guid(), "-2");
4566 mock_server_
->SetChangesRemaining(1);
4567 mock_server_
->NextUpdateBatch();
4569 // Construct the second GetUpdates response.
4570 mock_server_
->AddUpdateDirectory(node2
, ids_
.root(), "two", 1, 20,
4571 foreign_cache_guid(), "-2");
4573 SyncShareConfigure();
4575 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4576 // Both nodes should be downloaded and applied.
4578 Entry
n1(&trans
, GET_BY_ID
, node1
);
4579 ASSERT_TRUE(n1
.good());
4580 EXPECT_FALSE(n1
.GetIsUnappliedUpdate());
4582 Entry
n2(&trans
, GET_BY_ID
, node2
);
4583 ASSERT_TRUE(n2
.good());
4584 EXPECT_FALSE(n2
.GetIsUnappliedUpdate());
4587 // Same as the above case, but this time the second batch fails to download.
4588 TEST_F(SyncerTest
, ConfigureFailsDontApplyUpdates
) {
4589 syncable::Id node1
= ids_
.NewServerId();
4590 syncable::Id node2
= ids_
.NewServerId();
4592 // The scenario: we have two batches of updates with one update each. A
4593 // normal confgure step would download all the updates one batch at a time and
4594 // apply them. This configure will succeed in downloading the first batch
4595 // then fail when downloading the second.
4596 mock_server_
->FailNthPostBufferToPathCall(2);
4598 // Construct the first GetUpdates response.
4599 mock_server_
->AddUpdateDirectory(node1
, ids_
.root(), "one", 1, 10,
4600 foreign_cache_guid(), "-1");
4601 mock_server_
->SetChangesRemaining(1);
4602 mock_server_
->NextUpdateBatch();
4604 // Consutrct the second GetUpdates response.
4605 mock_server_
->AddUpdateDirectory(node2
, ids_
.root(), "two", 1, 20,
4606 foreign_cache_guid(), "-2");
4608 SyncShareConfigure();
4610 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4612 // The first node was downloaded, but not applied.
4613 Entry
n1(&trans
, GET_BY_ID
, node1
);
4614 ASSERT_TRUE(n1
.good());
4615 EXPECT_TRUE(n1
.GetIsUnappliedUpdate());
4617 // The second node was not downloaded.
4618 Entry
n2(&trans
, GET_BY_ID
, node2
);
4619 EXPECT_FALSE(n2
.good());
4621 // One update remains undownloaded.
4622 mock_server_
->ClearUpdatesQueue();
4625 TEST_F(SyncerTest
, GetKeySuccess
) {
4627 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4628 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4631 SyncShareConfigure();
4633 EXPECT_EQ(session_
->status_controller().last_get_key_result(), SYNCER_OK
);
4635 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4636 EXPECT_FALSE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4640 TEST_F(SyncerTest
, GetKeyEmpty
) {
4642 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4643 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4646 mock_server_
->SetKeystoreKey(std::string());
4647 SyncShareConfigure();
4649 EXPECT_NE(session_
->status_controller().last_get_key_result(), SYNCER_OK
);
4651 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4652 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4656 // Tests specifically related to bookmark (and therefore no client tags) sync
4657 // logic. Entities without client tags have custom logic in parts of the code,
4658 // and hence are not covered by e.g. the Undeletion tests below.
4659 class SyncerBookmarksTest
: public SyncerTest
{
4661 SyncerBookmarksTest() : metahandle_(syncable::kInvalidMetaHandle
) {
4665 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4666 MutableEntry
bookmark(
4667 &trans
, CREATE
, BOOKMARKS
, ids_
.root(), "clientname");
4668 ASSERT_TRUE(bookmark
.good());
4669 bookmark
.PutSpecifics(DefaultBookmarkSpecifics());
4670 EXPECT_FALSE(bookmark
.GetIsUnappliedUpdate());
4671 EXPECT_FALSE(bookmark
.GetId().ServerKnows());
4672 metahandle_
= bookmark
.GetMetahandle();
4673 local_id_
= bookmark
.GetId();
4674 bookmark
.PutIsUnsynced(true);
4678 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4679 MutableEntry
bookmark(&trans
, GET_BY_ID
, local_id_
);
4680 ASSERT_TRUE(bookmark
.good());
4681 bookmark
.PutSpecifics(DefaultBookmarkSpecifics());
4682 EXPECT_FALSE(bookmark
.GetIsUnappliedUpdate());
4683 bookmark
.PutIsUnsynced(true);
4684 if (bookmark
.GetSyncing())
4685 bookmark
.PutDirtySync(true);
4689 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4690 MutableEntry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4691 ASSERT_TRUE(entry
.good());
4692 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4693 // The order of setting IS_UNSYNCED vs IS_DEL matters. See
4694 // WriteNode::Tombstone().
4695 entry
.PutIsUnsynced(true);
4696 if (entry
.GetSyncing())
4697 entry
.PutDirtySync(true);
4698 entry
.PutIsDel(true);
4701 void UpdateAndDelete() {
4707 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4708 MutableEntry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4709 ASSERT_TRUE(entry
.good());
4710 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4711 EXPECT_TRUE(entry
.GetIsDel());
4712 entry
.PutIsDel(false);
4713 entry
.PutIsUnsynced(true);
4714 if (entry
.GetSyncing())
4715 entry
.PutDirtySync(true);
4718 int64
GetMetahandleOfTag() {
4719 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4720 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4721 EXPECT_TRUE(entry
.good());
4722 if (!entry
.good()) {
4723 return syncable::kInvalidMetaHandle
;
4725 return entry
.GetMetahandle();
4729 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4730 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4731 EXPECT_TRUE(entry
.good());
4732 if (!entry
.good()) {
4735 return entry
.GetId();
4738 void ExpectUnsyncedCreation() {
4739 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4740 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4742 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4743 EXPECT_FALSE(entry
.GetIsDel());
4744 EXPECT_FALSE(entry
.GetServerIsDel()); // Never been committed.
4745 EXPECT_LT(entry
.GetBaseVersion(), 0);
4746 EXPECT_TRUE(entry
.GetIsUnsynced());
4747 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4750 void ExpectUnsyncedUndeletion() {
4751 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4752 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4754 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4755 EXPECT_FALSE(entry
.GetIsDel());
4756 EXPECT_TRUE(entry
.GetServerIsDel());
4757 EXPECT_GE(entry
.GetBaseVersion(), 0);
4758 EXPECT_TRUE(entry
.GetIsUnsynced());
4759 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4760 EXPECT_TRUE(entry
.GetId().ServerKnows());
4763 void ExpectUnsyncedEdit() {
4764 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4765 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4767 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4768 EXPECT_FALSE(entry
.GetIsDel());
4769 EXPECT_FALSE(entry
.GetServerIsDel());
4770 EXPECT_GE(entry
.GetBaseVersion(), 0);
4771 EXPECT_TRUE(entry
.GetIsUnsynced());
4772 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4773 EXPECT_TRUE(entry
.GetId().ServerKnows());
4776 void ExpectUnsyncedDeletion() {
4777 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4778 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4780 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4781 EXPECT_TRUE(entry
.GetIsDel());
4782 EXPECT_FALSE(entry
.GetServerIsDel());
4783 EXPECT_TRUE(entry
.GetIsUnsynced());
4784 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4785 EXPECT_GE(entry
.GetBaseVersion(), 0);
4786 EXPECT_GE(entry
.GetServerVersion(), 0);
4789 void ExpectSyncedAndCreated() {
4790 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4791 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4793 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4794 EXPECT_FALSE(entry
.GetIsDel());
4795 EXPECT_FALSE(entry
.GetServerIsDel());
4796 EXPECT_GE(entry
.GetBaseVersion(), 0);
4797 EXPECT_EQ(entry
.GetBaseVersion(), entry
.GetServerVersion());
4798 EXPECT_FALSE(entry
.GetIsUnsynced());
4799 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4802 void ExpectSyncedAndDeleted() {
4803 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4804 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4806 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4807 EXPECT_TRUE(entry
.GetIsDel());
4808 EXPECT_TRUE(entry
.GetServerIsDel());
4809 EXPECT_FALSE(entry
.GetIsUnsynced());
4810 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4811 EXPECT_GE(entry
.GetBaseVersion(), 0);
4812 EXPECT_GE(entry
.GetServerVersion(), 0);
4816 syncable::Id local_id_
;
4820 TEST_F(SyncerBookmarksTest
, CreateSyncThenDeleteSync
) {
4822 ExpectUnsyncedCreation();
4824 ExpectSyncedAndCreated();
4826 ExpectUnsyncedDeletion();
4828 ExpectSyncedAndDeleted();
4831 TEST_F(SyncerBookmarksTest
, CreateThenDeleteBeforeSync
) {
4833 ExpectUnsyncedCreation();
4836 // Deleting before the initial commit should result in not needing to send
4837 // the delete to the server. It will still be in an unsynced state, but with
4838 // IS_UNSYNCED set to false.
4840 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4841 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4843 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4844 EXPECT_TRUE(entry
.GetIsDel());
4845 EXPECT_FALSE(entry
.GetServerIsDel());
4846 EXPECT_FALSE(entry
.GetIsUnsynced());
4847 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4848 EXPECT_EQ(entry
.GetBaseVersion(), -1);
4849 EXPECT_EQ(entry
.GetServerVersion(), 0);
4853 TEST_F(SyncerBookmarksTest
, LocalDeleteRemoteChangeConflict
) {
4855 ExpectUnsyncedCreation();
4857 ExpectSyncedAndCreated();
4859 ExpectUnsyncedDeletion();
4861 // Trigger a getupdates that modifies the bookmark. The update should be
4862 // clobbered by the local delete.
4863 mock_server_
->AddUpdateBookmark(GetServerId(), Id::GetRoot(), "dummy", 10, 10,
4864 local_cache_guid(), local_id_
.GetServerId());
4867 ExpectSyncedAndDeleted();
4870 TEST_F(SyncerBookmarksTest
, CreateThenDeleteDuringCommit
) {
4872 ExpectUnsyncedCreation();
4874 // In the middle of the initial creation commit, perform a deletion.
4875 // This should trigger performing two consecutive commit cycles, resulting
4876 // in the bookmark being both deleted and synced.
4877 mock_server_
->SetMidCommitCallback(
4878 base::Bind(&SyncerBookmarksTest::Delete
, base::Unretained(this)));
4881 ExpectSyncedAndDeleted();
4884 TEST_F(SyncerBookmarksTest
, CreateThenUpdateAndDeleteDuringCommit
) {
4886 ExpectUnsyncedCreation();
4888 // In the middle of the initial creation commit, perform an updated followed
4889 // by a deletion. This should trigger performing two consecutive commit
4890 // cycles, resulting in the bookmark being both deleted and synced.
4891 mock_server_
->SetMidCommitCallback(base::Bind(
4892 &SyncerBookmarksTest::UpdateAndDelete
, base::Unretained(this)));
4895 ExpectSyncedAndDeleted();
4898 // Test what happens if a client deletes, then recreates, an object very
4899 // quickly. It is possible that the deletion gets sent as a commit, and
4900 // the undelete happens during the commit request. The principle here
4901 // is that with a single committing client, conflicts should never
4902 // be encountered, and a client encountering its past actions during
4903 // getupdates should never feed back to override later actions.
4905 // In cases of ordering A-F below, the outcome should be the same.
4906 // Exercised by UndeleteDuringCommit:
4907 // A. Delete - commit - undelete - commitresponse.
4908 // B. Delete - commit - undelete - commitresponse - getupdates.
4909 // Exercised by UndeleteBeforeCommit:
4910 // C. Delete - undelete - commit - commitresponse.
4911 // D. Delete - undelete - commit - commitresponse - getupdates.
4912 // Exercised by UndeleteAfterCommit:
4913 // E. Delete - commit - commitresponse - undelete - commit
4914 // - commitresponse.
4915 // F. Delete - commit - commitresponse - undelete - commit -
4916 // - commitresponse - getupdates.
4917 class SyncerUndeletionTest
: public SyncerTest
{
4919 SyncerUndeletionTest()
4920 : client_tag_("foobar"),
4921 metahandle_(syncable::kInvalidMetaHandle
) {
4925 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4926 MutableEntry
perm_folder(
4927 &trans
, CREATE
, PREFERENCES
, ids_
.root(), "clientname");
4928 ASSERT_TRUE(perm_folder
.good());
4929 perm_folder
.PutUniqueClientTag(client_tag_
);
4930 perm_folder
.PutIsUnsynced(true);
4931 if (perm_folder
.GetSyncing())
4932 perm_folder
.PutDirtySync(true);
4933 perm_folder
.PutSpecifics(DefaultPreferencesSpecifics());
4934 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
4935 EXPECT_FALSE(perm_folder
.GetId().ServerKnows());
4936 metahandle_
= perm_folder
.GetMetahandle();
4937 local_id_
= perm_folder
.GetId();
4941 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4942 MutableEntry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4943 ASSERT_TRUE(entry
.good());
4944 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4945 // The order of setting IS_UNSYNCED vs IS_DEL matters. See
4946 // WriteNode::Tombstone().
4947 entry
.PutIsUnsynced(true);
4948 if (entry
.GetSyncing())
4949 entry
.PutDirtySync(true);
4950 entry
.PutIsDel(true);
4954 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4955 MutableEntry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4956 ASSERT_TRUE(entry
.good());
4957 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4958 EXPECT_TRUE(entry
.GetIsDel());
4959 entry
.PutIsDel(false);
4960 entry
.PutIsUnsynced(true);
4961 if (entry
.GetSyncing())
4962 entry
.PutDirtySync(true);
4965 int64
GetMetahandleOfTag() {
4966 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4967 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4968 EXPECT_TRUE(entry
.good());
4969 if (!entry
.good()) {
4970 return syncable::kInvalidMetaHandle
;
4972 return entry
.GetMetahandle();
4975 void ExpectUnsyncedCreation() {
4976 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4977 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4979 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4980 EXPECT_FALSE(entry
.GetIsDel());
4981 EXPECT_FALSE(entry
.GetServerIsDel()); // Never been committed.
4982 EXPECT_LT(entry
.GetBaseVersion(), 0);
4983 EXPECT_TRUE(entry
.GetIsUnsynced());
4984 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4987 void ExpectUnsyncedUndeletion() {
4988 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4989 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4991 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4992 EXPECT_FALSE(entry
.GetIsDel());
4993 EXPECT_TRUE(entry
.GetServerIsDel());
4994 EXPECT_GE(entry
.GetBaseVersion(), 0);
4995 EXPECT_TRUE(entry
.GetIsUnsynced());
4996 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4997 EXPECT_TRUE(entry
.GetId().ServerKnows());
5000 void ExpectUnsyncedEdit() {
5001 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5002 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
5004 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
5005 EXPECT_FALSE(entry
.GetIsDel());
5006 EXPECT_FALSE(entry
.GetServerIsDel());
5007 EXPECT_GE(entry
.GetBaseVersion(), 0);
5008 EXPECT_TRUE(entry
.GetIsUnsynced());
5009 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
5010 EXPECT_TRUE(entry
.GetId().ServerKnows());
5013 void ExpectUnsyncedDeletion() {
5014 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5015 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
5017 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
5018 EXPECT_TRUE(entry
.GetIsDel());
5019 EXPECT_FALSE(entry
.GetServerIsDel());
5020 EXPECT_TRUE(entry
.GetIsUnsynced());
5021 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
5022 EXPECT_GE(entry
.GetBaseVersion(), 0);
5023 EXPECT_GE(entry
.GetServerVersion(), 0);
5026 void ExpectSyncedAndCreated() {
5027 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5028 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
5030 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
5031 EXPECT_FALSE(entry
.GetIsDel());
5032 EXPECT_FALSE(entry
.GetServerIsDel());
5033 EXPECT_GE(entry
.GetBaseVersion(), 0);
5034 EXPECT_EQ(entry
.GetBaseVersion(), entry
.GetServerVersion());
5035 EXPECT_FALSE(entry
.GetIsUnsynced());
5036 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
5039 void ExpectSyncedAndDeleted() {
5040 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5041 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
5043 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
5044 EXPECT_TRUE(entry
.GetIsDel());
5045 EXPECT_TRUE(entry
.GetServerIsDel());
5046 EXPECT_FALSE(entry
.GetIsUnsynced());
5047 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
5048 EXPECT_GE(entry
.GetBaseVersion(), 0);
5049 EXPECT_GE(entry
.GetServerVersion(), 0);
5053 const std::string client_tag_
;
5054 syncable::Id local_id_
;
5058 TEST_F(SyncerUndeletionTest
, UndeleteDuringCommit
) {
5060 ExpectUnsyncedCreation();
5063 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5064 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5065 ExpectSyncedAndCreated();
5067 // Delete, begin committing the delete, then undelete while committing.
5069 ExpectUnsyncedDeletion();
5070 mock_server_
->SetMidCommitCallback(
5071 base::Bind(&SyncerUndeletionTest::Undelete
, base::Unretained(this)));
5074 // We will continue to commit until all nodes are synced, so we expect
5075 // that both the delete and following undelete were committed. We haven't
5076 // downloaded any updates, though, so the SERVER fields will be the same
5077 // as they were at the start of the cycle.
5078 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5079 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5082 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5083 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5085 // Server fields lag behind.
5086 EXPECT_FALSE(entry
.GetServerIsDel());
5088 // We have committed the second (undelete) update.
5089 EXPECT_FALSE(entry
.GetIsDel());
5090 EXPECT_FALSE(entry
.GetIsUnsynced());
5091 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
5094 // Now, encounter a GetUpdates corresponding to the deletion from
5095 // the server. The undeletion should prevail again and be committed.
5096 // None of this should trigger any conflict detection -- it is perfectly
5097 // normal to recieve updates from our own commits.
5098 mock_server_
->SetMidCommitCallback(base::Closure());
5099 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5100 update
->set_originator_cache_guid(local_cache_guid());
5101 update
->set_originator_client_item_id(local_id_
.GetServerId());
5104 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5105 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5106 ExpectSyncedAndCreated();
5109 TEST_F(SyncerUndeletionTest
, UndeleteBeforeCommit
) {
5111 ExpectUnsyncedCreation();
5114 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5115 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5116 ExpectSyncedAndCreated();
5118 // Delete and undelete, then sync to pick up the result.
5120 ExpectUnsyncedDeletion();
5122 ExpectUnsyncedEdit(); // Edit, not undelete: server thinks it exists.
5125 // The item ought to have committed successfully.
5126 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5127 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5128 ExpectSyncedAndCreated();
5130 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5131 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5132 EXPECT_EQ(2, entry
.GetBaseVersion());
5135 // Now, encounter a GetUpdates corresponding to the just-committed
5137 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5138 update
->set_originator_cache_guid(local_cache_guid());
5139 update
->set_originator_client_item_id(local_id_
.GetServerId());
5141 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5142 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5143 ExpectSyncedAndCreated();
5146 TEST_F(SyncerUndeletionTest
, UndeleteAfterCommitButBeforeGetUpdates
) {
5148 ExpectUnsyncedCreation();
5151 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5152 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5153 ExpectSyncedAndCreated();
5155 // Delete and commit.
5157 ExpectUnsyncedDeletion();
5160 // The item ought to have committed successfully.
5161 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5162 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5163 ExpectSyncedAndDeleted();
5165 // Before the GetUpdates, the item is locally undeleted.
5167 ExpectUnsyncedUndeletion();
5169 // Now, encounter a GetUpdates corresponding to the just-committed
5170 // deletion update. The undeletion should prevail.
5171 mock_server_
->AddUpdateFromLastCommit();
5173 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5174 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5175 ExpectSyncedAndCreated();
5178 TEST_F(SyncerUndeletionTest
, UndeleteAfterDeleteAndGetUpdates
) {
5180 ExpectUnsyncedCreation();
5183 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5184 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5185 ExpectSyncedAndCreated();
5187 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5188 update
->set_originator_cache_guid(local_cache_guid());
5189 update
->set_originator_client_item_id(local_id_
.GetServerId());
5191 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5192 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5193 ExpectSyncedAndCreated();
5195 // Delete and commit.
5197 ExpectUnsyncedDeletion();
5200 // The item ought to have committed successfully.
5201 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5202 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5203 ExpectSyncedAndDeleted();
5205 // Now, encounter a GetUpdates corresponding to the just-committed
5206 // deletion update. Should be consistent.
5207 mock_server_
->AddUpdateFromLastCommit();
5209 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5210 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5211 ExpectSyncedAndDeleted();
5213 // After the GetUpdates, the item is locally undeleted.
5215 ExpectUnsyncedUndeletion();
5217 // Now, encounter a GetUpdates corresponding to the just-committed
5218 // deletion update. The undeletion should prevail.
5220 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5221 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5222 ExpectSyncedAndCreated();
5225 // Test processing of undeletion GetUpdateses.
5226 TEST_F(SyncerUndeletionTest
, UndeleteAfterOtherClientDeletes
) {
5228 ExpectUnsyncedCreation();
5231 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5232 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5233 ExpectSyncedAndCreated();
5235 // Add a delete from the server.
5236 sync_pb::SyncEntity
* update1
= mock_server_
->AddUpdateFromLastCommit();
5237 update1
->set_originator_cache_guid(local_cache_guid());
5238 update1
->set_originator_client_item_id(local_id_
.GetServerId());
5240 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5241 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5242 ExpectSyncedAndCreated();
5244 // Some other client deletes the item.
5246 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5247 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5248 mock_server_
->AddUpdateTombstone(entry
.GetId(), PREFERENCES
);
5252 // The update ought to have applied successfully.
5253 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5254 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5255 ExpectSyncedAndDeleted();
5257 // Undelete it locally.
5259 ExpectUnsyncedUndeletion();
5261 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5262 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5263 ExpectSyncedAndCreated();
5265 // Now, encounter a GetUpdates corresponding to the just-committed
5266 // deletion update. The undeletion should prevail.
5267 sync_pb::SyncEntity
* update2
= mock_server_
->AddUpdateFromLastCommit();
5268 update2
->set_originator_cache_guid(local_cache_guid());
5269 update2
->set_originator_client_item_id(local_id_
.GetServerId());
5271 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5272 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5273 ExpectSyncedAndCreated();
5276 TEST_F(SyncerUndeletionTest
, UndeleteAfterOtherClientDeletesImmediately
) {
5278 ExpectUnsyncedCreation();
5281 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5282 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5283 ExpectSyncedAndCreated();
5285 // Some other client deletes the item before we get a chance
5286 // to GetUpdates our original request.
5288 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5289 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5290 mock_server_
->AddUpdateTombstone(entry
.GetId(), PREFERENCES
);
5294 // The update ought to have applied successfully.
5295 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5296 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5297 ExpectSyncedAndDeleted();
5299 // Undelete it locally.
5301 ExpectUnsyncedUndeletion();
5303 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5304 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5305 ExpectSyncedAndCreated();
5307 // Now, encounter a GetUpdates corresponding to the just-committed
5308 // deletion update. The undeletion should prevail.
5309 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5310 update
->set_originator_cache_guid(local_cache_guid());
5311 update
->set_originator_client_item_id(local_id_
.GetServerId());
5313 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5314 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5315 ExpectSyncedAndCreated();
5318 TEST_F(SyncerUndeletionTest
, OtherClientUndeletes
) {
5320 ExpectUnsyncedCreation();
5323 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5324 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5325 ExpectSyncedAndCreated();
5327 // Get the updates of our just-committed entry.
5328 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5329 update
->set_originator_cache_guid(local_cache_guid());
5330 update
->set_originator_client_item_id(local_id_
.GetServerId());
5332 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5333 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5334 ExpectSyncedAndCreated();
5336 // We delete the item.
5338 ExpectUnsyncedDeletion();
5341 // The update ought to have applied successfully.
5342 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5343 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5344 ExpectSyncedAndDeleted();
5346 // Now, encounter a GetUpdates corresponding to the just-committed
5348 mock_server_
->AddUpdateFromLastCommit();
5350 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5351 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5352 ExpectSyncedAndDeleted();
5354 // Some other client undeletes the item.
5356 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5357 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5358 mock_server_
->AddUpdatePref(
5359 entry
.GetId().GetServerId(),
5360 entry
.GetParentId().GetServerId(),
5361 client_tag_
, 100, 1000);
5363 mock_server_
->SetLastUpdateClientTag(client_tag_
);
5365 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5366 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5367 ExpectSyncedAndCreated();
5370 TEST_F(SyncerUndeletionTest
, OtherClientUndeletesImmediately
) {
5372 ExpectUnsyncedCreation();
5375 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5376 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5377 ExpectSyncedAndCreated();
5379 // Get the updates of our just-committed entry.
5380 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5381 update
->set_originator_cache_guid(local_cache_guid());
5383 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5384 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5385 update
->set_originator_client_item_id(local_id_
.GetServerId());
5388 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5389 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5390 ExpectSyncedAndCreated();
5392 // We delete the item.
5394 ExpectUnsyncedDeletion();
5397 // The update ought to have applied successfully.
5398 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5399 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5400 ExpectSyncedAndDeleted();
5402 // Some other client undeletes before we see the update from our
5405 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5406 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5407 mock_server_
->AddUpdatePref(
5408 entry
.GetId().GetServerId(),
5409 entry
.GetParentId().GetServerId(),
5410 client_tag_
, 100, 1000);
5412 mock_server_
->SetLastUpdateClientTag(client_tag_
);
5414 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5415 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5416 ExpectSyncedAndCreated();
5420 TEST_PARAM_BOOKMARK_ENABLE_BIT
,
5421 TEST_PARAM_AUTOFILL_ENABLE_BIT
,
5422 TEST_PARAM_BIT_COUNT
5427 public ::testing::WithParamInterface
<int> {
5429 bool ShouldFailBookmarkCommit() {
5430 return (GetParam() & (1 << TEST_PARAM_BOOKMARK_ENABLE_BIT
)) == 0;
5432 bool ShouldFailAutofillCommit() {
5433 return (GetParam() & (1 << TEST_PARAM_AUTOFILL_ENABLE_BIT
)) == 0;
5437 INSTANTIATE_TEST_CASE_P(ExtensionsActivity
,
5439 testing::Range(0, 1 << TEST_PARAM_BIT_COUNT
));
5441 TEST_P(MixedResult
, ExtensionsActivity
) {
5443 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
5445 MutableEntry
pref(&wtrans
, CREATE
, PREFERENCES
, wtrans
.root_id(), "pref");
5446 ASSERT_TRUE(pref
.good());
5447 pref
.PutIsUnsynced(true);
5449 MutableEntry
bookmark(
5450 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "bookmark");
5451 ASSERT_TRUE(bookmark
.good());
5452 bookmark
.PutIsUnsynced(true);
5454 if (ShouldFailBookmarkCommit()) {
5455 mock_server_
->SetTransientErrorId(bookmark
.GetId());
5458 if (ShouldFailAutofillCommit()) {
5459 mock_server_
->SetTransientErrorId(pref
.GetId());
5464 // Put some extenions activity records into the monitor.
5466 ExtensionsActivity::Records records
;
5467 records
["ABC"].extension_id
= "ABC";
5468 records
["ABC"].bookmark_write_count
= 2049U;
5469 records
["xyz"].extension_id
= "xyz";
5470 records
["xyz"].bookmark_write_count
= 4U;
5471 context_
->extensions_activity()->PutRecords(records
);
5476 ExtensionsActivity::Records final_monitor_records
;
5477 context_
->extensions_activity()->GetAndClearRecords(&final_monitor_records
);
5478 if (ShouldFailBookmarkCommit()) {
5479 ASSERT_EQ(2U, final_monitor_records
.size())
5480 << "Should restore records after unsuccessful bookmark commit.";
5481 EXPECT_EQ("ABC", final_monitor_records
["ABC"].extension_id
);
5482 EXPECT_EQ("xyz", final_monitor_records
["xyz"].extension_id
);
5483 EXPECT_EQ(2049U, final_monitor_records
["ABC"].bookmark_write_count
);
5484 EXPECT_EQ(4U, final_monitor_records
["xyz"].bookmark_write_count
);
5486 EXPECT_TRUE(final_monitor_records
.empty())
5487 << "Should not restore records after successful bookmark commit.";
5491 } // namespace syncer