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
;
75 using syncable::CountEntriesWithName
;
76 using syncable::Directory
;
77 using syncable::Entry
;
78 using syncable::GetFirstEntryWithName
;
79 using syncable::GetOnlyEntryWithName
;
81 using syncable::kEncryptedString
;
82 using syncable::MutableEntry
;
83 using syncable::WriteTransaction
;
85 using syncable::CREATE
;
86 using syncable::GET_BY_HANDLE
;
87 using syncable::GET_BY_ID
;
88 using syncable::GET_BY_CLIENT_TAG
;
89 using syncable::GET_BY_SERVER_TAG
;
90 using syncable::GET_TYPE_ROOT
;
91 using syncable::UNITTEST
;
93 using sessions::MockDebugInfoGetter
;
94 using sessions::StatusController
;
95 using sessions::SyncSessionContext
;
96 using sessions::SyncSession
;
100 // A helper to hold on to the counters emitted by the sync engine.
101 class TypeDebugInfoCache
: public TypeDebugInfoObserver
{
103 TypeDebugInfoCache();
104 ~TypeDebugInfoCache() override
;
106 CommitCounters
GetLatestCommitCounters(ModelType type
) const;
107 UpdateCounters
GetLatestUpdateCounters(ModelType type
) const;
108 StatusCounters
GetLatestStatusCounters(ModelType type
) const;
110 // TypeDebugInfoObserver implementation.
111 void OnCommitCountersUpdated(syncer::ModelType type
,
112 const CommitCounters
& counters
) override
;
113 void OnUpdateCountersUpdated(syncer::ModelType type
,
114 const UpdateCounters
& counters
) override
;
115 void OnStatusCountersUpdated(syncer::ModelType type
,
116 const StatusCounters
& counters
) override
;
119 std::map
<ModelType
, CommitCounters
> commit_counters_map_
;
120 std::map
<ModelType
, UpdateCounters
> update_counters_map_
;
121 std::map
<ModelType
, StatusCounters
> status_counters_map_
;
124 TypeDebugInfoCache::TypeDebugInfoCache() {}
126 TypeDebugInfoCache::~TypeDebugInfoCache() {}
128 CommitCounters
TypeDebugInfoCache::GetLatestCommitCounters(
129 ModelType type
) const {
130 std::map
<ModelType
, CommitCounters
>::const_iterator it
=
131 commit_counters_map_
.find(type
);
132 if (it
== commit_counters_map_
.end()) {
133 return CommitCounters();
139 UpdateCounters
TypeDebugInfoCache::GetLatestUpdateCounters(
140 ModelType type
) const {
141 std::map
<ModelType
, UpdateCounters
>::const_iterator it
=
142 update_counters_map_
.find(type
);
143 if (it
== update_counters_map_
.end()) {
144 return UpdateCounters();
150 StatusCounters
TypeDebugInfoCache::GetLatestStatusCounters(
151 ModelType type
) const {
152 std::map
<ModelType
, StatusCounters
>::const_iterator it
=
153 status_counters_map_
.find(type
);
154 if (it
== status_counters_map_
.end()) {
155 return StatusCounters();
161 void TypeDebugInfoCache::OnCommitCountersUpdated(
162 syncer::ModelType type
,
163 const CommitCounters
& counters
) {
164 commit_counters_map_
[type
] = counters
;
167 void TypeDebugInfoCache::OnUpdateCountersUpdated(
168 syncer::ModelType type
,
169 const UpdateCounters
& counters
) {
170 update_counters_map_
[type
] = counters
;
173 void TypeDebugInfoCache::OnStatusCountersUpdated(
174 syncer::ModelType type
,
175 const StatusCounters
& counters
) {
176 status_counters_map_
[type
] = counters
;
181 class SyncerTest
: public testing::Test
,
182 public SyncSession::Delegate
,
183 public SyncEngineEventListener
{
186 : extensions_activity_(new ExtensionsActivity
),
188 saw_syncer_event_(false),
189 last_client_invalidation_hint_buffer_size_(10) {
192 // SyncSession::Delegate implementation.
193 void OnThrottled(const base::TimeDelta
& throttle_duration
) override
{
194 FAIL() << "Should not get silenced.";
196 void OnTypesThrottled(ModelTypeSet types
,
197 const base::TimeDelta
& throttle_duration
) override
{
198 scheduler_
->OnTypesThrottled(types
, throttle_duration
);
200 bool IsCurrentlyThrottled() override
{ return false; }
201 void OnReceivedLongPollIntervalUpdate(
202 const base::TimeDelta
& new_interval
) override
{
203 last_long_poll_interval_received_
= new_interval
;
205 void OnReceivedShortPollIntervalUpdate(
206 const base::TimeDelta
& new_interval
) override
{
207 last_short_poll_interval_received_
= new_interval
;
209 void OnReceivedCustomNudgeDelays(
210 const std::map
<ModelType
, base::TimeDelta
>& delay_map
) override
{
211 std::map
<ModelType
, base::TimeDelta
>::const_iterator iter
=
212 delay_map
.find(SESSIONS
);
213 if (iter
!= delay_map
.end() && iter
->second
> base::TimeDelta())
214 last_sessions_commit_delay_
= iter
->second
;
215 iter
= delay_map
.find(BOOKMARKS
);
216 if (iter
!= delay_map
.end() && iter
->second
> base::TimeDelta())
217 last_bookmarks_commit_delay_
= iter
->second
;
219 void OnReceivedClientInvalidationHintBufferSize(int size
) override
{
220 last_client_invalidation_hint_buffer_size_
= size
;
222 void OnReceivedGuRetryDelay(const base::TimeDelta
& delay
) override
{}
223 void OnReceivedMigrationRequest(ModelTypeSet types
) override
{}
224 void OnProtocolEvent(const ProtocolEvent
& event
) override
{}
225 void OnSyncProtocolError(const SyncProtocolError
& error
) override
{}
227 void GetModelSafeRoutingInfo(ModelSafeRoutingInfo
* out
) {
228 // We're just testing the sync engine here, so we shunt everything to
229 // the SyncerThread. Datatypes which aren't enabled aren't in the map.
230 for (ModelTypeSet::Iterator it
= enabled_datatypes_
.First();
231 it
.Good(); it
.Inc()) {
232 (*out
)[it
.Get()] = GROUP_PASSIVE
;
236 void OnSyncCycleEvent(const SyncCycleEvent
& event
) override
{
237 DVLOG(1) << "HandleSyncEngineEvent in unittest " << event
.what_happened
;
238 // we only test for entry-specific events, not status changed ones.
239 switch (event
.what_happened
) {
240 case SyncCycleEvent::SYNC_CYCLE_BEGIN
: // Fall through.
241 case SyncCycleEvent::STATUS_CHANGED
:
242 case SyncCycleEvent::SYNC_CYCLE_ENDED
:
245 CHECK(false) << "Handling unknown error type in unit tests!!";
247 saw_syncer_event_
= true;
250 void OnActionableError(const SyncProtocolError
& error
) override
{}
251 void OnRetryTimeChanged(base::Time retry_time
) override
{}
252 void OnThrottledTypesChanged(ModelTypeSet throttled_types
) override
{}
253 void OnMigrationRequested(ModelTypeSet types
) override
{}
255 void ResetSession() {
256 session_
.reset(SyncSession::Build(context_
.get(), this));
259 void SyncShareNudge() {
262 // Pretend we've seen a local change, to make the nudge_tracker look normal.
263 nudge_tracker_
.RecordLocalChange(ModelTypeSet(BOOKMARKS
));
265 EXPECT_TRUE(syncer_
->NormalSyncShare(context_
->GetEnabledTypes(),
266 &nudge_tracker_
, session_
.get()));
269 void SyncShareConfigure() {
271 EXPECT_TRUE(syncer_
->ConfigureSyncShare(
272 context_
->GetEnabledTypes(),
273 sync_pb::GetUpdatesCallerInfo::RECONFIGURATION
,
277 void SetUp() override
{
279 mock_server_
.reset(new MockConnectionManager(directory(),
280 &cancelation_signal_
));
281 debug_info_getter_
.reset(new MockDebugInfoGetter
);
282 EnableDatatype(BOOKMARKS
);
283 EnableDatatype(NIGORI
);
284 EnableDatatype(PREFERENCES
);
285 EnableDatatype(NIGORI
);
286 workers_
.push_back(scoped_refptr
<ModelSafeWorker
>(
287 new FakeModelWorker(GROUP_PASSIVE
)));
288 std::vector
<SyncEngineEventListener
*> listeners
;
289 listeners
.push_back(this);
291 ModelSafeRoutingInfo routing_info
;
292 GetModelSafeRoutingInfo(&routing_info
);
294 model_type_registry_
.reset(
295 new ModelTypeRegistry(workers_
, directory(), &mock_nudge_handler_
));
296 model_type_registry_
->RegisterDirectoryTypeDebugInfoObserver(
299 context_
.reset(new SyncSessionContext(
302 extensions_activity_
.get(),
304 debug_info_getter_
.get(),
305 model_type_registry_
.get(),
306 true, // enable keystore encryption
307 false, // force enable pre-commit GU avoidance experiment
308 "fake_invalidator_client_id"));
309 context_
->SetRoutingInfo(routing_info
);
310 syncer_
= new Syncer(&cancelation_signal_
);
311 scheduler_
.reset(new SyncSchedulerImpl(
313 BackoffDelayProvider::FromDefaults(),
315 // scheduler_ owned syncer_ now and will manage the memory of syncer_
318 syncable::ReadTransaction
trans(FROM_HERE
, directory());
319 syncable::Directory::Metahandles children
;
320 directory()->GetChildHandlesById(&trans
, trans
.root_id(), &children
);
321 ASSERT_EQ(0u, children
.size());
322 saw_syncer_event_
= false;
323 root_id_
= TestIdFactory::root();
324 parent_id_
= ids_
.MakeServer("parent id");
325 child_id_
= ids_
.MakeServer("child id");
326 directory()->set_store_birthday(mock_server_
->store_birthday());
327 mock_server_
->SetKeystoreKey("encryption_key");
330 void TearDown() override
{
331 model_type_registry_
->UnregisterDirectoryTypeDebugInfoObserver(
333 mock_server_
.reset();
335 dir_maker_
.TearDown();
338 void WriteTestDataToEntry(WriteTransaction
* trans
, MutableEntry
* entry
) {
339 EXPECT_FALSE(entry
->GetIsDir());
340 EXPECT_FALSE(entry
->GetIsDel());
341 sync_pb::EntitySpecifics specifics
;
342 specifics
.mutable_bookmark()->set_url("http://demo/");
343 specifics
.mutable_bookmark()->set_favicon("PNG");
344 entry
->PutSpecifics(specifics
);
345 entry
->PutIsUnsynced(true);
347 void VerifyTestDataInEntry(BaseTransaction
* trans
, Entry
* entry
) {
348 EXPECT_FALSE(entry
->GetIsDir());
349 EXPECT_FALSE(entry
->GetIsDel());
350 VerifyTestBookmarkDataInEntry(entry
);
352 void VerifyTestBookmarkDataInEntry(Entry
* entry
) {
353 const sync_pb::EntitySpecifics
& specifics
= entry
->GetSpecifics();
354 EXPECT_TRUE(specifics
.has_bookmark());
355 EXPECT_EQ("PNG", specifics
.bookmark().favicon());
356 EXPECT_EQ("http://demo/", specifics
.bookmark().url());
359 void VerifyHierarchyConflictsReported(
360 const sync_pb::ClientToServerMessage
& message
) {
361 // Our request should have included a warning about hierarchy conflicts.
362 const sync_pb::ClientStatus
& client_status
= message
.client_status();
363 EXPECT_TRUE(client_status
.has_hierarchy_conflict_detected());
364 EXPECT_TRUE(client_status
.hierarchy_conflict_detected());
367 void VerifyNoHierarchyConflictsReported(
368 const sync_pb::ClientToServerMessage
& message
) {
369 // Our request should have reported no hierarchy conflicts detected.
370 const sync_pb::ClientStatus
& client_status
= message
.client_status();
371 EXPECT_TRUE(client_status
.has_hierarchy_conflict_detected());
372 EXPECT_FALSE(client_status
.hierarchy_conflict_detected());
375 void VerifyHierarchyConflictsUnspecified(
376 const sync_pb::ClientToServerMessage
& message
) {
377 // Our request should have neither confirmed nor denied hierarchy conflicts.
378 const sync_pb::ClientStatus
& client_status
= message
.client_status();
379 EXPECT_FALSE(client_status
.has_hierarchy_conflict_detected());
382 sync_pb::EntitySpecifics
DefaultBookmarkSpecifics() {
383 sync_pb::EntitySpecifics result
;
384 AddDefaultFieldValue(BOOKMARKS
, &result
);
388 sync_pb::EntitySpecifics
DefaultPreferencesSpecifics() {
389 sync_pb::EntitySpecifics result
;
390 AddDefaultFieldValue(PREFERENCES
, &result
);
393 // Enumeration of alterations to entries for commit ordering tests.
395 LIST_END
= 0, // Denotes the end of the list of features from below.
396 SYNCED
, // Items are unsynced by default
402 struct CommitOrderingTest
{
403 // expected commit index.
405 // Details about the item
407 syncable::Id parent_id
;
408 EntryFeature features
[10];
410 static CommitOrderingTest
MakeLastCommitItem() {
411 CommitOrderingTest last_commit_item
;
412 last_commit_item
.commit_index
= -1;
413 last_commit_item
.id
= TestIdFactory::root();
414 return last_commit_item
;
418 void RunCommitOrderingTest(CommitOrderingTest
* test
) {
419 map
<int, syncable::Id
> expected_positions
;
420 { // Transaction scope.
421 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
422 while (!test
->id
.IsRoot()) {
423 if (test
->commit_index
>= 0) {
424 map
<int, syncable::Id
>::value_type
entry(test
->commit_index
,
426 bool double_position
= !expected_positions
.insert(entry
).second
;
427 ASSERT_FALSE(double_position
) << "Two id's expected at one position";
429 string utf8_name
= test
->id
.GetServerId();
430 string
name(utf8_name
.begin(), utf8_name
.end());
431 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, test
->parent_id
, name
);
433 entry
.PutId(test
->id
);
434 if (test
->id
.ServerKnows()) {
435 entry
.PutBaseVersion(5);
436 entry
.PutServerVersion(5);
437 entry
.PutServerParentId(test
->parent_id
);
439 entry
.PutIsDir(true);
440 entry
.PutIsUnsynced(true);
441 entry
.PutSpecifics(DefaultBookmarkSpecifics());
442 // Set the time to 30 seconds in the future to reduce the chance of
444 const base::Time
& now_plus_30s
=
445 base::Time::Now() + base::TimeDelta::FromSeconds(30);
446 const base::Time
& now_minus_2h
=
447 base::Time::Now() - base::TimeDelta::FromHours(2);
448 entry
.PutMtime(now_plus_30s
);
449 for (size_t i
= 0 ; i
< arraysize(test
->features
) ; ++i
) {
450 switch (test
->features
[i
]) {
454 entry
.PutIsUnsynced(false);
457 entry
.PutIsDel(true);
460 entry
.PutMtime(now_minus_2h
);
462 case MOVED_FROM_ROOT
:
463 entry
.PutServerParentId(trans
.root_id());
466 FAIL() << "Bad value in CommitOrderingTest list";
473 ASSERT_TRUE(expected_positions
.size() ==
474 mock_server_
->committed_ids().size());
475 // If this test starts failing, be aware other sort orders could be valid.
476 for (size_t i
= 0; i
< expected_positions
.size(); ++i
) {
478 EXPECT_EQ(1u, expected_positions
.count(i
));
479 EXPECT_EQ(expected_positions
[i
], mock_server_
->committed_ids()[i
]);
483 CommitCounters
GetCommitCounters(ModelType type
) {
484 return debug_info_cache_
.GetLatestCommitCounters(type
);
487 UpdateCounters
GetUpdateCounters(ModelType type
) {
488 return debug_info_cache_
.GetLatestUpdateCounters(type
);
491 StatusCounters
GetStatusCounters(ModelType type
) {
492 return debug_info_cache_
.GetLatestStatusCounters(type
);
495 Directory
* directory() {
496 return dir_maker_
.directory();
499 const std::string
local_cache_guid() {
500 return directory()->cache_guid();
503 const std::string
foreign_cache_guid() {
504 return "kqyg7097kro6GSUod+GSg==";
507 int64
CreateUnsyncedDirectory(const string
& entry_name
,
508 const string
& idstring
) {
509 return CreateUnsyncedDirectory(entry_name
,
510 syncable::Id::CreateFromServerId(idstring
));
513 int64
CreateUnsyncedDirectory(const string
& entry_name
,
514 const syncable::Id
& id
) {
515 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
517 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), entry_name
);
518 EXPECT_TRUE(entry
.good());
519 entry
.PutIsUnsynced(true);
520 entry
.PutIsDir(true);
521 entry
.PutSpecifics(DefaultBookmarkSpecifics());
522 entry
.PutBaseVersion(id
.ServerKnows() ? 1 : 0);
524 return entry
.GetMetahandle();
527 void EnableDatatype(ModelType model_type
) {
528 enabled_datatypes_
.Put(model_type
);
530 ModelSafeRoutingInfo routing_info
;
531 GetModelSafeRoutingInfo(&routing_info
);
534 context_
->SetRoutingInfo(routing_info
);
537 mock_server_
->ExpectGetUpdatesRequestTypes(enabled_datatypes_
);
540 void DisableDatatype(ModelType model_type
) {
541 enabled_datatypes_
.Remove(model_type
);
543 ModelSafeRoutingInfo routing_info
;
544 GetModelSafeRoutingInfo(&routing_info
);
547 context_
->SetRoutingInfo(routing_info
);
550 mock_server_
->ExpectGetUpdatesRequestTypes(enabled_datatypes_
);
553 Cryptographer
* GetCryptographer(syncable::BaseTransaction
* trans
) {
554 return directory()->GetCryptographer(trans
);
557 // Configures SyncSessionContext and NudgeTracker so Syncer won't call
558 // GetUpdates prior to Commit. This method can be used to ensure a Commit is
559 // not preceeded by GetUpdates.
560 void ConfigureNoGetUpdatesRequired() {
561 context_
->set_server_enabled_pre_commit_update_avoidance(true);
562 nudge_tracker_
.OnInvalidationsEnabled();
563 nudge_tracker_
.RecordSuccessfulSyncCycle();
565 ASSERT_FALSE(context_
->ShouldFetchUpdatesBeforeCommit());
566 ASSERT_FALSE(nudge_tracker_
.IsGetUpdatesRequired());
569 base::MessageLoop message_loop_
;
571 // Some ids to aid tests. Only the root one's value is specific. The rest
572 // are named for test clarity.
573 // TODO(chron): Get rid of these inbuilt IDs. They only make it
575 syncable::Id root_id_
;
576 syncable::Id parent_id_
;
577 syncable::Id child_id_
;
581 TestDirectorySetterUpper dir_maker_
;
582 FakeEncryptor encryptor_
;
583 scoped_refptr
<ExtensionsActivity
> extensions_activity_
;
584 scoped_ptr
<MockConnectionManager
> mock_server_
;
585 CancelationSignal cancelation_signal_
;
589 scoped_ptr
<SyncSession
> session_
;
590 TypeDebugInfoCache debug_info_cache_
;
591 MockNudgeHandler mock_nudge_handler_
;
592 scoped_ptr
<ModelTypeRegistry
> model_type_registry_
;
593 scoped_ptr
<SyncSchedulerImpl
> scheduler_
;
594 scoped_ptr
<SyncSessionContext
> context_
;
595 bool saw_syncer_event_
;
596 base::TimeDelta last_short_poll_interval_received_
;
597 base::TimeDelta last_long_poll_interval_received_
;
598 base::TimeDelta last_sessions_commit_delay_
;
599 base::TimeDelta last_bookmarks_commit_delay_
;
600 int last_client_invalidation_hint_buffer_size_
;
601 std::vector
<scoped_refptr
<ModelSafeWorker
> > workers_
;
603 ModelTypeSet enabled_datatypes_
;
604 sessions::NudgeTracker nudge_tracker_
;
605 scoped_ptr
<MockDebugInfoGetter
> debug_info_getter_
;
607 DISALLOW_COPY_AND_ASSIGN(SyncerTest
);
610 TEST_F(SyncerTest
, TestCallGatherUnsyncedEntries
) {
612 Syncer::UnsyncedMetaHandles handles
;
614 syncable::ReadTransaction
trans(FROM_HERE
, directory());
615 GetUnsyncedEntries(&trans
, &handles
);
617 ASSERT_EQ(0u, handles
.size());
619 // TODO(sync): When we can dynamically connect and disconnect the mock
620 // ServerConnectionManager test disconnected GetUnsyncedEntries here. It's a
621 // regression for a very old bug.
624 TEST_F(SyncerTest
, GetCommitIdsFiltersThrottledEntries
) {
625 const ModelTypeSet
throttled_types(BOOKMARKS
);
626 sync_pb::EntitySpecifics bookmark_data
;
627 AddDefaultFieldValue(BOOKMARKS
, &bookmark_data
);
629 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
630 foreign_cache_guid(), "-1");
634 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
635 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
636 ASSERT_TRUE(A
.good());
637 A
.PutIsUnsynced(true);
638 A
.PutSpecifics(bookmark_data
);
639 A
.PutNonUniqueName("bookmark");
642 // Now sync without enabling bookmarks.
643 mock_server_
->ExpectGetUpdatesRequestTypes(
644 Difference(context_
->GetEnabledTypes(), ModelTypeSet(BOOKMARKS
)));
646 syncer_
->NormalSyncShare(
647 Difference(context_
->GetEnabledTypes(), ModelTypeSet(BOOKMARKS
)),
648 &nudge_tracker_
, session_
.get());
651 // Nothing should have been committed as bookmarks is throttled.
652 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
653 Entry
entryA(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
654 ASSERT_TRUE(entryA
.good());
655 EXPECT_TRUE(entryA
.GetIsUnsynced());
658 // Sync again with bookmarks enabled.
659 mock_server_
->ExpectGetUpdatesRequestTypes(context_
->GetEnabledTypes());
662 // It should have been committed.
663 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
664 Entry
entryA(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
665 ASSERT_TRUE(entryA
.good());
666 EXPECT_FALSE(entryA
.GetIsUnsynced());
670 // We use a macro so we can preserve the error location.
671 #define VERIFY_ENTRY(id, is_unapplied, is_unsynced, prev_initialized, \
672 parent_id, version, server_version, id_fac, rtrans) \
674 Entry entryA(rtrans, syncable::GET_BY_ID, id_fac.FromNumber(id)); \
675 ASSERT_TRUE(entryA.good()); \
676 /* We don't use EXPECT_EQ here because when the left side param is false,
677 gcc 4.6 warns about converting 'false' to pointer type for argument 1. */ \
678 EXPECT_TRUE(is_unsynced == entryA.GetIsUnsynced()); \
679 EXPECT_TRUE(is_unapplied == entryA.GetIsUnappliedUpdate()); \
680 EXPECT_TRUE(prev_initialized == \
681 IsRealDataType(GetModelTypeFromSpecifics( \
682 entryA.GetBaseServerSpecifics()))); \
683 EXPECT_TRUE(parent_id == -1 || \
684 entryA.GetParentId()== id_fac.FromNumber(parent_id)); \
685 EXPECT_EQ(version, entryA.GetBaseVersion()); \
686 EXPECT_EQ(server_version, entryA.GetServerVersion()); \
689 TEST_F(SyncerTest
, GetCommitIdsFiltersUnreadyEntries
) {
690 KeyParams key_params
= {"localhost", "dummy", "foobar"};
691 KeyParams other_params
= {"localhost", "dummy", "foobar2"};
692 sync_pb::EntitySpecifics bookmark
, encrypted_bookmark
;
693 bookmark
.mutable_bookmark()->set_url("url");
694 bookmark
.mutable_bookmark()->set_title("title");
695 AddDefaultFieldValue(BOOKMARKS
, &encrypted_bookmark
);
696 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
697 foreign_cache_guid(), "-1");
698 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
699 foreign_cache_guid(), "-2");
700 mock_server_
->AddUpdateDirectory(3, 0, "C", 10, 10,
701 foreign_cache_guid(), "-3");
702 mock_server_
->AddUpdateDirectory(4, 0, "D", 10, 10,
703 foreign_cache_guid(), "-4");
705 // Server side change will put A in conflict.
706 mock_server_
->AddUpdateDirectory(1, 0, "A", 20, 20,
707 foreign_cache_guid(), "-1");
709 // Mark bookmarks as encrypted and set the cryptographer to have pending
711 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
712 Cryptographer
other_cryptographer(&encryptor_
);
713 other_cryptographer
.AddKey(other_params
);
714 sync_pb::EntitySpecifics specifics
;
715 sync_pb::NigoriSpecifics
* nigori
= specifics
.mutable_nigori();
716 other_cryptographer
.GetKeys(nigori
->mutable_encryption_keybag());
717 dir_maker_
.encryption_handler()->EnableEncryptEverything();
718 // Set up with an old passphrase, but have pending keys
719 GetCryptographer(&wtrans
)->AddKey(key_params
);
720 GetCryptographer(&wtrans
)->Encrypt(bookmark
,
721 encrypted_bookmark
.mutable_encrypted());
722 GetCryptographer(&wtrans
)->SetPendingKeys(nigori
->encryption_keybag());
724 // In conflict but properly encrypted.
725 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
726 ASSERT_TRUE(A
.good());
727 A
.PutIsUnsynced(true);
728 A
.PutSpecifics(encrypted_bookmark
);
729 A
.PutNonUniqueName(kEncryptedString
);
730 // Not in conflict and properly encrypted.
731 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
732 ASSERT_TRUE(B
.good());
733 B
.PutIsUnsynced(true);
734 B
.PutSpecifics(encrypted_bookmark
);
735 B
.PutNonUniqueName(kEncryptedString
);
736 // Unencrypted specifics.
737 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
738 ASSERT_TRUE(C
.good());
739 C
.PutIsUnsynced(true);
740 C
.PutNonUniqueName(kEncryptedString
);
741 // Unencrypted non_unique_name.
742 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
743 ASSERT_TRUE(D
.good());
744 D
.PutIsUnsynced(true);
745 D
.PutSpecifics(encrypted_bookmark
);
746 D
.PutNonUniqueName("not encrypted");
750 // Nothing should have commited due to bookmarks being encrypted and
751 // the cryptographer having pending keys. A would have been resolved
752 // as a simple conflict, but still be unsynced until the next sync cycle.
753 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
754 VERIFY_ENTRY(1, false, true, false, 0, 20, 20, ids_
, &rtrans
);
755 VERIFY_ENTRY(2, false, true, false, 0, 10, 10, ids_
, &rtrans
);
756 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_
, &rtrans
);
757 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_
, &rtrans
);
759 // Resolve the pending keys.
760 GetCryptographer(&rtrans
)->DecryptPendingKeys(other_params
);
764 // All properly encrypted and non-conflicting items should commit. "A" was
765 // conflicting, but last sync cycle resolved it as simple conflict, so on
766 // this sync cycle it committed succesfullly.
767 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
768 // Committed successfully.
769 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_
, &rtrans
);
770 // Committed successfully.
771 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_
, &rtrans
);
772 // Was not properly encrypted.
773 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_
, &rtrans
);
774 // Was not properly encrypted.
775 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_
, &rtrans
);
778 // Fix the remaining items.
779 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
780 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
781 ASSERT_TRUE(C
.good());
782 C
.PutSpecifics(encrypted_bookmark
);
783 C
.PutNonUniqueName(kEncryptedString
);
784 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
785 ASSERT_TRUE(D
.good());
786 D
.PutSpecifics(encrypted_bookmark
);
787 D
.PutNonUniqueName(kEncryptedString
);
791 const StatusController
& status_controller
= session_
->status_controller();
793 EXPECT_EQ(status_controller
.model_neutral_state().commit_result
, SYNCER_OK
);
794 // None should be unsynced anymore.
795 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
796 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_
, &rtrans
);
797 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_
, &rtrans
);
798 VERIFY_ENTRY(3, false, false, false, 0, 11, 11, ids_
, &rtrans
);
799 VERIFY_ENTRY(4, false, false, false, 0, 11, 11, ids_
, &rtrans
);
803 TEST_F(SyncerTest
, GetUpdatesPartialThrottled
) {
804 sync_pb::EntitySpecifics bookmark
, pref
;
805 bookmark
.mutable_bookmark()->set_title("title");
806 pref
.mutable_preference()->set_name("name");
807 AddDefaultFieldValue(BOOKMARKS
, &bookmark
);
808 AddDefaultFieldValue(PREFERENCES
, &pref
);
810 // Normal sync, all the data types should get synced.
811 mock_server_
->AddUpdateSpecifics(1, 0, "A", 10, 10, true, 0, bookmark
,
812 foreign_cache_guid(), "-1");
813 mock_server_
->AddUpdateSpecifics(2, 1, "B", 10, 10, false, 2, bookmark
,
814 foreign_cache_guid(), "-2");
815 mock_server_
->AddUpdateSpecifics(3, 1, "C", 10, 10, false, 1, bookmark
,
816 foreign_cache_guid(), "-3");
817 mock_server_
->AddUpdateSpecifics(4, 0, "D", 10, 10, false, 0, pref
);
821 // Initial state. Everything is normal.
822 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
823 VERIFY_ENTRY(1, false, false, false, 0, 10, 10, ids_
, &rtrans
);
824 VERIFY_ENTRY(2, false, false, false, 1, 10, 10, ids_
, &rtrans
);
825 VERIFY_ENTRY(3, false, false, false, 1, 10, 10, ids_
, &rtrans
);
826 VERIFY_ENTRY(4, false, false, false, 0, 10, 10, ids_
, &rtrans
);
829 // Set BOOKMARKS throttled but PREFERENCES not,
830 // then BOOKMARKS should not get synced but PREFERENCES should.
831 ModelTypeSet
throttled_types(BOOKMARKS
);
832 mock_server_
->set_partial_throttling(true);
833 mock_server_
->SetThrottledTypes(throttled_types
);
835 mock_server_
->AddUpdateSpecifics(1, 0, "E", 20, 20, true, 0, bookmark
,
836 foreign_cache_guid(), "-1");
837 mock_server_
->AddUpdateSpecifics(2, 1, "F", 20, 20, false, 2, bookmark
,
838 foreign_cache_guid(), "-2");
839 mock_server_
->AddUpdateSpecifics(3, 1, "G", 20, 20, false, 1, bookmark
,
840 foreign_cache_guid(), "-3");
841 mock_server_
->AddUpdateSpecifics(4, 0, "H", 20, 20, false, 0, pref
);
843 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
844 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
845 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
846 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
847 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
848 A
.PutIsUnsynced(true);
849 B
.PutIsUnsynced(true);
850 C
.PutIsUnsynced(true);
851 D
.PutIsUnsynced(true);
855 // BOOKMARKS throttled.
856 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
857 VERIFY_ENTRY(1, false, true, false, 0, 10, 10, ids_
, &rtrans
);
858 VERIFY_ENTRY(2, false, true, false, 1, 10, 10, ids_
, &rtrans
);
859 VERIFY_ENTRY(3, false, true, false, 1, 10, 10, ids_
, &rtrans
);
860 VERIFY_ENTRY(4, false, false, false, 0, 21, 21, ids_
, &rtrans
);
863 // Unthrottled BOOKMARKS, then BOOKMARKS should get synced now.
864 mock_server_
->set_partial_throttling(false);
866 mock_server_
->AddUpdateSpecifics(1, 0, "E", 30, 30, true, 0, bookmark
,
867 foreign_cache_guid(), "-1");
868 mock_server_
->AddUpdateSpecifics(2, 1, "F", 30, 30, false, 2, bookmark
,
869 foreign_cache_guid(), "-2");
870 mock_server_
->AddUpdateSpecifics(3, 1, "G", 30, 30, false, 1, bookmark
,
871 foreign_cache_guid(), "-3");
872 mock_server_
->AddUpdateSpecifics(4, 0, "H", 30, 30, false, 0, pref
);
875 // BOOKMARKS unthrottled.
876 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
877 VERIFY_ENTRY(1, false, false, false, 0, 31, 31, ids_
, &rtrans
);
878 VERIFY_ENTRY(2, false, false, false, 1, 31, 31, ids_
, &rtrans
);
879 VERIFY_ENTRY(3, false, false, false, 1, 31, 31, ids_
, &rtrans
);
880 VERIFY_ENTRY(4, false, false, false, 0, 30, 30, ids_
, &rtrans
);
884 // This test uses internal knowledge of the directory to test correctness of
885 // GetCommitIds. In almost every other test, the hierarchy is created from
886 // parent to child order, and so parents always have metahandles that are
887 // smaller than those of their children. This makes it very difficult to test
888 // some GetCommitIds edge cases, since it uses metahandle ordering as
890 TEST_F(SyncerTest
, GetCommitIds_VerifyDeletionCommitOrder
) {
892 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
894 // Create four bookmarks folders at the root node.
895 for (int i
= 1; i
< 5; ++i
) {
896 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "");
897 entry
.PutId(ids_
.FromNumber(i
));
898 entry
.PutIsDir(true);
899 entry
.PutBaseVersion(5);
900 entry
.PutServerVersion(5);
901 entry
.PutServerParentId(trans
.root_id());
902 entry
.PutServerIsDir(true);
903 entry
.PutIsUnsynced(true);
904 entry
.PutSpecifics(DefaultBookmarkSpecifics());
907 // Now iterate in reverse order make a hierarchy of them.
908 // While we're at it, also mark them as deleted.
909 syncable::Id parent_id
= trans
.root_id();
910 for (int i
= 4; i
> 0; --i
) {
911 MutableEntry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(i
));
912 entry
.PutParentId(parent_id
);
913 entry
.PutServerParentId(parent_id
);
914 entry
.PutIsDel(true);
915 parent_id
= ids_
.FromNumber(i
);
920 // Run GetCommitIds, the function being tested.
921 syncable::Directory::Metahandles result_handles
;
922 syncable::ReadTransaction
trans(FROM_HERE
, directory());
923 GetCommitIdsForType(&trans
, BOOKMARKS
, 100, &result_handles
);
925 // Now verify the output. We expect four results in child to parent order.
926 ASSERT_EQ(4U, result_handles
.size());
928 Entry
entry0(&trans
, GET_BY_HANDLE
, result_handles
[0]);
929 EXPECT_EQ(ids_
.FromNumber(1), entry0
.GetId());
931 Entry
entry1(&trans
, GET_BY_HANDLE
, result_handles
[1]);
932 EXPECT_EQ(ids_
.FromNumber(2), entry1
.GetId());
934 Entry
entry2(&trans
, GET_BY_HANDLE
, result_handles
[2]);
935 EXPECT_EQ(ids_
.FromNumber(3), entry2
.GetId());
937 Entry
entry3(&trans
, GET_BY_HANDLE
, result_handles
[3]);
938 EXPECT_EQ(ids_
.FromNumber(4), entry3
.GetId());
942 // Verify that if there are more deleted items than the maximum number of
943 // entries, child to parent order is still preserved.
944 TEST_F(SyncerTest
, GetCommitIds_VerifyDeletionCommitOrderMaxEntries
) {
946 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
948 // Create a bookmark tree with one root, two second level, and three third
949 // level bookmarks, all folders.
950 for (int i
= 1; i
<= 6; ++i
) {
951 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "");
952 entry
.PutId(ids_
.FromNumber(i
));
953 entry
.PutIsDir(true);
954 entry
.PutBaseVersion(5);
955 entry
.PutServerVersion(5);
956 entry
.PutParentId(ids_
.FromNumber(i
/2));
957 entry
.PutServerParentId(ids_
.FromNumber(i
/2));
958 entry
.PutServerIsDir(true);
959 entry
.PutIsUnsynced(true);
960 entry
.PutSpecifics(DefaultBookmarkSpecifics());
961 entry
.PutIsDel(true);
966 // Run GetCommitIds with a limit of 2 entries to commit.
967 syncable::Directory::Metahandles result_handles
;
968 syncable::ReadTransaction
trans(FROM_HERE
, directory());
969 GetCommitIdsForType(&trans
, BOOKMARKS
, 2, &result_handles
);
971 // Now verify the output. We expect two results in child to parent order
972 // (descending id order).
973 ASSERT_EQ(2U, result_handles
.size());
975 Entry
entry0(&trans
, GET_BY_HANDLE
, result_handles
[0]);
976 EXPECT_EQ(ids_
.FromNumber(6), entry0
.GetId());
978 Entry
entry1(&trans
, GET_BY_HANDLE
, result_handles
[1]);
979 EXPECT_EQ(ids_
.FromNumber(5), entry1
.GetId());
983 TEST_F(SyncerTest
, EncryptionAwareConflicts
) {
984 KeyParams key_params
= {"localhost", "dummy", "foobar"};
985 Cryptographer
other_cryptographer(&encryptor_
);
986 other_cryptographer
.AddKey(key_params
);
987 sync_pb::EntitySpecifics bookmark
, encrypted_bookmark
, modified_bookmark
;
988 bookmark
.mutable_bookmark()->set_title("title");
989 other_cryptographer
.Encrypt(bookmark
,
990 encrypted_bookmark
.mutable_encrypted());
991 AddDefaultFieldValue(BOOKMARKS
, &encrypted_bookmark
);
992 modified_bookmark
.mutable_bookmark()->set_title("title2");
993 other_cryptographer
.Encrypt(modified_bookmark
,
994 modified_bookmark
.mutable_encrypted());
995 sync_pb::EntitySpecifics pref
, encrypted_pref
, modified_pref
;
996 pref
.mutable_preference()->set_name("name");
997 AddDefaultFieldValue(PREFERENCES
, &encrypted_pref
);
998 other_cryptographer
.Encrypt(pref
,
999 encrypted_pref
.mutable_encrypted());
1000 modified_pref
.mutable_preference()->set_name("name2");
1001 other_cryptographer
.Encrypt(modified_pref
,
1002 modified_pref
.mutable_encrypted());
1004 // Mark bookmarks and preferences as encrypted and set the cryptographer to
1005 // have pending keys.
1006 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1007 sync_pb::EntitySpecifics specifics
;
1008 sync_pb::NigoriSpecifics
* nigori
= specifics
.mutable_nigori();
1009 other_cryptographer
.GetKeys(nigori
->mutable_encryption_keybag());
1010 dir_maker_
.encryption_handler()->EnableEncryptEverything();
1011 GetCryptographer(&wtrans
)->SetPendingKeys(nigori
->encryption_keybag());
1012 EXPECT_TRUE(GetCryptographer(&wtrans
)->has_pending_keys());
1015 // We need to remember the exact position of our local items, so we can
1016 // make updates that do not modify those positions.
1017 UniquePosition pos1
;
1018 UniquePosition pos2
;
1019 UniquePosition pos3
;
1021 mock_server_
->AddUpdateSpecifics(1, 0, "A", 10, 10, true, 0, bookmark
,
1022 foreign_cache_guid(), "-1");
1023 mock_server_
->AddUpdateSpecifics(2, 1, "B", 10, 10, false, 2, bookmark
,
1024 foreign_cache_guid(), "-2");
1025 mock_server_
->AddUpdateSpecifics(3, 1, "C", 10, 10, false, 1, bookmark
,
1026 foreign_cache_guid(), "-3");
1027 mock_server_
->AddUpdateSpecifics(4, 0, "D", 10, 10, false, 0, pref
);
1030 // Initial state. Everything is normal.
1031 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1032 VERIFY_ENTRY(1, false, false, false, 0, 10, 10, ids_
, &rtrans
);
1033 VERIFY_ENTRY(2, false, false, false, 1, 10, 10, ids_
, &rtrans
);
1034 VERIFY_ENTRY(3, false, false, false, 1, 10, 10, ids_
, &rtrans
);
1035 VERIFY_ENTRY(4, false, false, false, 0, 10, 10, ids_
, &rtrans
);
1037 Entry
entry1(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
1038 ASSERT_TRUE(entry1
.GetUniquePosition().Equals(
1039 entry1
.GetServerUniquePosition()));
1040 pos1
= entry1
.GetUniquePosition();
1041 Entry
entry2(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(2));
1042 pos2
= entry2
.GetUniquePosition();
1043 Entry
entry3(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(3));
1044 pos3
= entry3
.GetUniquePosition();
1047 // Server side encryption will not be applied due to undecryptable data.
1048 // At this point, BASE_SERVER_SPECIFICS should be filled for all four items.
1049 mock_server_
->AddUpdateSpecifics(1, 0, kEncryptedString
, 20, 20, true, 0,
1051 foreign_cache_guid(), "-1");
1052 mock_server_
->AddUpdateSpecifics(2, 1, kEncryptedString
, 20, 20, false, 2,
1054 foreign_cache_guid(), "-2");
1055 mock_server_
->AddUpdateSpecifics(3, 1, kEncryptedString
, 20, 20, false, 1,
1057 foreign_cache_guid(), "-3");
1058 mock_server_
->AddUpdateSpecifics(4, 0, kEncryptedString
, 20, 20, false, 0,
1060 foreign_cache_guid(), "-4");
1063 // All should be unapplied due to being undecryptable and have a valid
1064 // BASE_SERVER_SPECIFICS.
1065 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1066 VERIFY_ENTRY(1, true, false, true, 0, 10, 20, ids_
, &rtrans
);
1067 VERIFY_ENTRY(2, true, false, true, 1, 10, 20, ids_
, &rtrans
);
1068 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_
, &rtrans
);
1069 VERIFY_ENTRY(4, true, false, true, 0, 10, 20, ids_
, &rtrans
);
1072 // Server side change that don't modify anything should not affect
1073 // BASE_SERVER_SPECIFICS (such as name changes and mtime changes).
1074 mock_server_
->AddUpdateSpecifics(1, 0, kEncryptedString
, 30, 30, true, 0,
1076 foreign_cache_guid(), "-1");
1077 mock_server_
->AddUpdateSpecifics(2, 1, kEncryptedString
, 30, 30, false, 2,
1079 foreign_cache_guid(), "-2");
1080 // Item 3 doesn't change.
1081 mock_server_
->AddUpdateSpecifics(4, 0, kEncryptedString
, 30, 30, false, 0,
1083 foreign_cache_guid(), "-4");
1086 // Items 1, 2, and 4 should have newer server versions, 3 remains the same.
1087 // All should remain unapplied due to be undecryptable.
1088 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1089 VERIFY_ENTRY(1, true, false, true, 0, 10, 30, ids_
, &rtrans
);
1090 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_
, &rtrans
);
1091 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_
, &rtrans
);
1092 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_
, &rtrans
);
1095 // Positional changes, parent changes, and specifics changes should reset
1096 // BASE_SERVER_SPECIFICS.
1097 // Became unencrypted.
1098 mock_server_
->AddUpdateSpecifics(1, 0, "A", 40, 40, true, 0, bookmark
,
1099 foreign_cache_guid(), "-1");
1100 // Reordered to after item 2.
1101 mock_server_
->AddUpdateSpecifics(3, 1, kEncryptedString
, 30, 30, false, 3,
1103 foreign_cache_guid(), "-3");
1106 // Items 2 and 4 should be the only ones with BASE_SERVER_SPECIFICS set.
1107 // Items 1 is now unencrypted, so should have applied normally.
1108 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1109 VERIFY_ENTRY(1, false, false, false, 0, 40, 40, ids_
, &rtrans
);
1110 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_
, &rtrans
);
1111 VERIFY_ENTRY(3, true, false, false, 1, 10, 30, ids_
, &rtrans
);
1112 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_
, &rtrans
);
1115 // Make local changes, which should remain unsynced for items 2, 3, 4.
1117 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1118 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
1119 ASSERT_TRUE(A
.good());
1120 A
.PutSpecifics(modified_bookmark
);
1121 A
.PutNonUniqueName(kEncryptedString
);
1122 A
.PutIsUnsynced(true);
1123 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
1124 ASSERT_TRUE(B
.good());
1125 B
.PutSpecifics(modified_bookmark
);
1126 B
.PutNonUniqueName(kEncryptedString
);
1127 B
.PutIsUnsynced(true);
1128 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
1129 ASSERT_TRUE(C
.good());
1130 C
.PutSpecifics(modified_bookmark
);
1131 C
.PutNonUniqueName(kEncryptedString
);
1132 C
.PutIsUnsynced(true);
1133 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
1134 ASSERT_TRUE(D
.good());
1135 D
.PutSpecifics(modified_pref
);
1136 D
.PutNonUniqueName(kEncryptedString
);
1137 D
.PutIsUnsynced(true);
1141 // Item 1 remains unsynced due to there being pending keys.
1142 // Items 2, 3, 4 should remain unsynced since they were not up to date.
1143 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1144 VERIFY_ENTRY(1, false, true, false, 0, 40, 40, ids_
, &rtrans
);
1145 VERIFY_ENTRY(2, true, true, true, 1, 10, 30, ids_
, &rtrans
);
1146 VERIFY_ENTRY(3, true, true, false, 1, 10, 30, ids_
, &rtrans
);
1147 VERIFY_ENTRY(4, true, true, true, 0, 10, 30, ids_
, &rtrans
);
1151 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1152 // Resolve the pending keys.
1153 GetCryptographer(&rtrans
)->DecryptPendingKeys(key_params
);
1155 // First cycle resolves conflicts, second cycle commits changes.
1157 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_server_overwrites
);
1158 EXPECT_EQ(1, GetUpdateCounters(PREFERENCES
).num_server_overwrites
);
1159 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_local_overwrites
);
1161 // We successfully commited item(s).
1162 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS
).num_commits_attempted
);
1163 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS
).num_commits_success
);
1164 EXPECT_EQ(1, GetCommitCounters(PREFERENCES
).num_commits_attempted
);
1165 EXPECT_EQ(1, GetCommitCounters(PREFERENCES
).num_commits_success
);
1169 // Everything should be resolved now. The local changes should have
1170 // overwritten the server changes for 2 and 4, while the server changes
1171 // overwrote the local for entry 3.
1173 // Expect there will be no new overwrites.
1174 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_server_overwrites
);
1175 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_local_overwrites
);
1177 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS
).num_commits_success
);
1178 EXPECT_EQ(1, GetCommitCounters(PREFERENCES
).num_commits_success
);
1180 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1181 VERIFY_ENTRY(1, false, false, false, 0, 41, 41, ids_
, &rtrans
);
1182 VERIFY_ENTRY(2, false, false, false, 1, 31, 31, ids_
, &rtrans
);
1183 VERIFY_ENTRY(3, false, false, false, 1, 30, 30, ids_
, &rtrans
);
1184 VERIFY_ENTRY(4, false, false, false, 0, 31, 31, ids_
, &rtrans
);
1189 TEST_F(SyncerTest
, TestGetUnsyncedAndSimpleCommit
) {
1191 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1192 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1193 ASSERT_TRUE(parent
.good());
1194 parent
.PutIsUnsynced(true);
1195 parent
.PutIsDir(true);
1196 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1197 parent
.PutBaseVersion(1);
1198 parent
.PutId(parent_id_
);
1199 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete");
1200 ASSERT_TRUE(child
.good());
1201 child
.PutId(child_id_
);
1202 child
.PutBaseVersion(1);
1203 WriteTestDataToEntry(&wtrans
, &child
);
1207 ASSERT_EQ(2u, mock_server_
->committed_ids().size());
1208 // If this test starts failing, be aware other sort orders could be valid.
1209 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1210 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1212 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1213 Entry
entry(&rt
, syncable::GET_BY_ID
, child_id_
);
1214 ASSERT_TRUE(entry
.good());
1215 VerifyTestDataInEntry(&rt
, &entry
);
1219 TEST_F(SyncerTest
, TestPurgeWhileUnsynced
) {
1220 // Similar to above, but throw a purge operation into the mix. Bug 49278.
1221 syncable::Id pref_node_id
= TestIdFactory::MakeServer("Tim");
1223 directory()->SetDownloadProgress(BOOKMARKS
,
1224 syncable::BuildProgress(BOOKMARKS
));
1225 directory()->SetDownloadProgress(PREFERENCES
,
1226 syncable::BuildProgress(PREFERENCES
));
1227 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1228 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1229 ASSERT_TRUE(parent
.good());
1230 parent
.PutIsUnsynced(true);
1231 parent
.PutIsDir(true);
1232 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1233 parent
.PutBaseVersion(1);
1234 parent
.PutId(parent_id_
);
1235 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete");
1236 ASSERT_TRUE(child
.good());
1237 child
.PutId(child_id_
);
1238 child
.PutBaseVersion(1);
1239 WriteTestDataToEntry(&wtrans
, &child
);
1241 MutableEntry
parent2(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Tim");
1242 ASSERT_TRUE(parent2
.good());
1243 parent2
.PutIsUnsynced(true);
1244 parent2
.PutIsDir(true);
1245 parent2
.PutSpecifics(DefaultPreferencesSpecifics());
1246 parent2
.PutBaseVersion(1);
1247 parent2
.PutId(pref_node_id
);
1250 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES
),
1255 ASSERT_EQ(2U, mock_server_
->committed_ids().size());
1256 // If this test starts failing, be aware other sort orders could be valid.
1257 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1258 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1260 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1261 Entry
entry(&rt
, syncable::GET_BY_ID
, child_id_
);
1262 ASSERT_TRUE(entry
.good());
1263 VerifyTestDataInEntry(&rt
, &entry
);
1265 directory()->SaveChanges();
1267 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1268 Entry
entry(&rt
, syncable::GET_BY_ID
, pref_node_id
);
1269 ASSERT_FALSE(entry
.good());
1273 TEST_F(SyncerTest
, TestPurgeWhileUnapplied
) {
1274 // Similar to above, but for unapplied items. Bug 49278.
1276 directory()->SetDownloadProgress(BOOKMARKS
,
1277 syncable::BuildProgress(BOOKMARKS
));
1278 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1279 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1280 ASSERT_TRUE(parent
.good());
1281 parent
.PutIsUnappliedUpdate(true);
1282 parent
.PutIsDir(true);
1283 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1284 parent
.PutBaseVersion(1);
1285 parent
.PutId(parent_id_
);
1288 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(BOOKMARKS
),
1293 directory()->SaveChanges();
1295 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1296 Entry
entry(&rt
, syncable::GET_BY_ID
, parent_id_
);
1297 ASSERT_FALSE(entry
.good());
1301 TEST_F(SyncerTest
, TestPurgeWithJournal
) {
1303 directory()->SetDownloadProgress(BOOKMARKS
,
1304 syncable::BuildProgress(BOOKMARKS
));
1305 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1306 MutableEntry
parent(&wtrans
, syncable::CREATE
, BOOKMARKS
, wtrans
.root_id(),
1308 ASSERT_TRUE(parent
.good());
1309 parent
.PutIsDir(true);
1310 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1311 parent
.PutBaseVersion(1);
1312 parent
.PutId(parent_id_
);
1313 MutableEntry
child(&wtrans
, syncable::CREATE
, BOOKMARKS
, parent_id_
,
1315 ASSERT_TRUE(child
.good());
1316 child
.PutId(child_id_
);
1317 child
.PutBaseVersion(1);
1318 WriteTestDataToEntry(&wtrans
, &child
);
1320 MutableEntry
parent2(&wtrans
, syncable::CREATE
, PREFERENCES
,
1321 wtrans
.root_id(), "Tim");
1322 ASSERT_TRUE(parent2
.good());
1323 parent2
.PutIsDir(true);
1324 parent2
.PutSpecifics(DefaultPreferencesSpecifics());
1325 parent2
.PutBaseVersion(1);
1326 parent2
.PutId(TestIdFactory::MakeServer("Tim"));
1329 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES
, BOOKMARKS
),
1330 ModelTypeSet(BOOKMARKS
),
1333 // Verify bookmark nodes are saved in delete journal but not preference
1335 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1336 syncable::DeleteJournal
* delete_journal
= directory()->delete_journal();
1337 EXPECT_EQ(2u, delete_journal
->GetDeleteJournalSize(&rt
));
1338 syncable::EntryKernelSet journal_entries
;
1339 directory()->delete_journal()->GetDeleteJournals(&rt
, BOOKMARKS
,
1341 EXPECT_EQ(parent_id_
, (*journal_entries
.begin())->ref(syncable::ID
));
1342 EXPECT_EQ(child_id_
, (*journal_entries
.rbegin())->ref(syncable::ID
));
1346 TEST_F(SyncerTest
, ResetVersions
) {
1347 // Download some pref items.
1348 mock_server_
->AddUpdatePref("id1", "", "tag1", 20, 20);
1349 mock_server_
->AddUpdatePref("id2", "", "tag2", 30, 30);
1350 mock_server_
->AddUpdatePref("id3", "", "tag3", 40, 40);
1354 // Modify one of the preferences locally, mark another one as unapplied,
1355 // and create another unsynced preference.
1356 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1357 MutableEntry
entry(&wtrans
, GET_BY_CLIENT_TAG
, "tag1");
1358 entry
.PutIsUnsynced(true);
1360 MutableEntry
entry2(&wtrans
, GET_BY_CLIENT_TAG
, "tag2");
1361 entry2
.PutIsUnappliedUpdate(true);
1363 MutableEntry
entry4(&wtrans
, CREATE
, PREFERENCES
, "name");
1364 entry4
.PutUniqueClientTag("tag4");
1365 entry4
.PutIsUnsynced(true);
1369 // Reset the versions.
1370 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1371 ASSERT_TRUE(directory()->ResetVersionsForType(&wtrans
, PREFERENCES
));
1375 // Verify the synced items are all with version 1 now, with
1376 // unsynced/unapplied state preserved.
1377 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1378 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, "tag1");
1379 EXPECT_EQ(1, entry
.GetBaseVersion());
1380 EXPECT_EQ(1, entry
.GetServerVersion());
1381 EXPECT_TRUE(entry
.GetIsUnsynced());
1382 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
1383 Entry
entry2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
1384 EXPECT_EQ(1, entry2
.GetBaseVersion());
1385 EXPECT_EQ(1, entry2
.GetServerVersion());
1386 EXPECT_FALSE(entry2
.GetIsUnsynced());
1387 EXPECT_TRUE(entry2
.GetIsUnappliedUpdate());
1388 Entry
entry3(&trans
, GET_BY_CLIENT_TAG
, "tag3");
1389 EXPECT_EQ(1, entry3
.GetBaseVersion());
1390 EXPECT_EQ(1, entry3
.GetServerVersion());
1391 EXPECT_FALSE(entry3
.GetIsUnsynced());
1392 EXPECT_FALSE(entry3
.GetIsUnappliedUpdate());
1394 // Entry 4 (the locally created one) should remain the same.
1395 Entry
entry4(&trans
, GET_BY_CLIENT_TAG
, "tag4");
1396 EXPECT_EQ(-1, entry4
.GetBaseVersion());
1397 EXPECT_EQ(0, entry4
.GetServerVersion());
1398 EXPECT_TRUE(entry4
.GetIsUnsynced());
1399 EXPECT_FALSE(entry4
.GetIsUnappliedUpdate());
1403 TEST_F(SyncerTest
, TestCommitListOrderingTwoItemsTall
) {
1404 CommitOrderingTest items
[] = {
1405 {1, ids_
.FromNumber(-1001), ids_
.FromNumber(-1000)},
1406 {0, ids_
.FromNumber(-1000), ids_
.FromNumber(0)},
1407 CommitOrderingTest::MakeLastCommitItem(),
1409 RunCommitOrderingTest(items
);
1412 TEST_F(SyncerTest
, TestCommitListOrderingThreeItemsTall
) {
1413 CommitOrderingTest items
[] = {
1414 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1415 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1416 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1417 CommitOrderingTest::MakeLastCommitItem(),
1419 RunCommitOrderingTest(items
);
1422 TEST_F(SyncerTest
, TestCommitListOrderingFourItemsTall
) {
1423 CommitOrderingTest items
[] = {
1424 {3, ids_
.FromNumber(-2003), ids_
.FromNumber(-2002)},
1425 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1426 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1427 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1428 CommitOrderingTest::MakeLastCommitItem(),
1430 RunCommitOrderingTest(items
);
1433 TEST_F(SyncerTest
, TestCommitListOrderingThreeItemsTallLimitedSize
) {
1434 context_
->set_max_commit_batch_size(2);
1435 CommitOrderingTest items
[] = {
1436 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1437 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1438 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1439 CommitOrderingTest::MakeLastCommitItem(),
1441 RunCommitOrderingTest(items
);
1444 TEST_F(SyncerTest
, TestCommitListOrderingSingleDeletedItem
) {
1445 CommitOrderingTest items
[] = {
1446 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1447 CommitOrderingTest::MakeLastCommitItem(),
1449 RunCommitOrderingTest(items
);
1452 TEST_F(SyncerTest
, TestCommitListOrderingSingleUncommittedDeletedItem
) {
1453 CommitOrderingTest items
[] = {
1454 {-1, ids_
.FromNumber(-1000), ids_
.FromNumber(0), {DELETED
}},
1455 CommitOrderingTest::MakeLastCommitItem(),
1457 RunCommitOrderingTest(items
);
1460 TEST_F(SyncerTest
, TestCommitListOrderingSingleDeletedItemWithUnroll
) {
1461 CommitOrderingTest items
[] = {
1462 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1463 CommitOrderingTest::MakeLastCommitItem(),
1465 RunCommitOrderingTest(items
);
1469 TestCommitListOrderingSingleLongDeletedItemWithUnroll
) {
1470 CommitOrderingTest items
[] = {
1471 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1472 CommitOrderingTest::MakeLastCommitItem(),
1474 RunCommitOrderingTest(items
);
1477 TEST_F(SyncerTest
, TestCommitListOrderingTwoLongDeletedItemWithUnroll
) {
1478 CommitOrderingTest items
[] = {
1479 {1, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1480 {0, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
, OLD_MTIME
}},
1481 CommitOrderingTest::MakeLastCommitItem(),
1483 RunCommitOrderingTest(items
);
1486 TEST_F(SyncerTest
, TestCommitListOrdering3LongDeletedItemsWithSizeLimit
) {
1487 context_
->set_max_commit_batch_size(2);
1488 CommitOrderingTest items
[] = {
1489 {2, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1490 {1, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
, OLD_MTIME
}},
1491 {0, ids_
.FromNumber(1002), ids_
.FromNumber(1001), {DELETED
, OLD_MTIME
}},
1492 CommitOrderingTest::MakeLastCommitItem(),
1494 RunCommitOrderingTest(items
);
1497 TEST_F(SyncerTest
, TestCommitListOrderingTwoDeletedItemsWithUnroll
) {
1498 CommitOrderingTest items
[] = {
1499 {1, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1500 {0, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
}},
1501 CommitOrderingTest::MakeLastCommitItem(),
1503 RunCommitOrderingTest(items
);
1506 TEST_F(SyncerTest
, TestCommitListOrderingComplexDeletionScenario
) {
1507 CommitOrderingTest items
[] = {
1508 {2, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1509 {-1, ids_
.FromNumber(1001), ids_
.FromNumber(0), {SYNCED
}},
1510 {1, ids_
.FromNumber(1002), ids_
.FromNumber(1001), {DELETED
, OLD_MTIME
}},
1511 {-1, ids_
.FromNumber(1003), ids_
.FromNumber(1001), {SYNCED
}},
1512 {0, ids_
.FromNumber(1004), ids_
.FromNumber(1003), {DELETED
}},
1513 CommitOrderingTest::MakeLastCommitItem(),
1515 RunCommitOrderingTest(items
);
1519 TestCommitListOrderingComplexDeletionScenarioWith2RecentDeletes
) {
1520 CommitOrderingTest items
[] = {
1521 {3, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1522 {-1, ids_
.FromNumber(1001), ids_
.FromNumber(0), {SYNCED
}},
1523 {2, ids_
.FromNumber(1002), ids_
.FromNumber(1001), {DELETED
, OLD_MTIME
}},
1524 {-1, ids_
.FromNumber(1003), ids_
.FromNumber(1001), {SYNCED
}},
1525 {1, ids_
.FromNumber(1004), ids_
.FromNumber(1003), {DELETED
}},
1526 {0, ids_
.FromNumber(1005), ids_
.FromNumber(1003), {DELETED
}},
1527 CommitOrderingTest::MakeLastCommitItem(),
1529 RunCommitOrderingTest(items
);
1532 TEST_F(SyncerTest
, TestCommitListOrderingDeleteMovedItems
) {
1533 CommitOrderingTest items
[] = {
1534 {1, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1535 {0, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
, OLD_MTIME
,
1537 CommitOrderingTest::MakeLastCommitItem(),
1539 RunCommitOrderingTest(items
);
1542 TEST_F(SyncerTest
, TestCommitListOrderingWithNesting
) {
1543 const base::Time
& now_minus_2h
=
1544 base::Time::Now() - base::TimeDelta::FromHours(2);
1546 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1548 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bob");
1549 ASSERT_TRUE(parent
.good());
1550 parent
.PutIsUnsynced(true);
1551 parent
.PutIsDir(true);
1552 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1553 parent
.PutId(ids_
.FromNumber(100));
1554 parent
.PutBaseVersion(1);
1556 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(100), "Bob");
1557 ASSERT_TRUE(child
.good());
1558 child
.PutIsUnsynced(true);
1559 child
.PutIsDir(true);
1560 child
.PutSpecifics(DefaultBookmarkSpecifics());
1561 child
.PutId(ids_
.FromNumber(101));
1562 child
.PutBaseVersion(1);
1563 MutableEntry
grandchild(
1564 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(101), "Bob");
1565 ASSERT_TRUE(grandchild
.good());
1566 grandchild
.PutId(ids_
.FromNumber(102));
1567 grandchild
.PutIsUnsynced(true);
1568 grandchild
.PutSpecifics(DefaultBookmarkSpecifics());
1569 grandchild
.PutBaseVersion(1);
1572 // Create three deleted items which deletions we expect to be sent to the
1574 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1575 ASSERT_TRUE(parent
.good());
1576 parent
.PutId(ids_
.FromNumber(103));
1577 parent
.PutIsUnsynced(true);
1578 parent
.PutIsDir(true);
1579 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1580 parent
.PutIsDel(true);
1581 parent
.PutBaseVersion(1);
1582 parent
.PutMtime(now_minus_2h
);
1584 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(103), "Pete");
1585 ASSERT_TRUE(child
.good());
1586 child
.PutId(ids_
.FromNumber(104));
1587 child
.PutIsUnsynced(true);
1588 child
.PutIsDir(true);
1589 child
.PutSpecifics(DefaultBookmarkSpecifics());
1590 child
.PutIsDel(true);
1591 child
.PutBaseVersion(1);
1592 child
.PutMtime(now_minus_2h
);
1593 MutableEntry
grandchild(
1594 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(104), "Pete");
1595 ASSERT_TRUE(grandchild
.good());
1596 grandchild
.PutId(ids_
.FromNumber(105));
1597 grandchild
.PutIsUnsynced(true);
1598 grandchild
.PutIsDel(true);
1599 grandchild
.PutIsDir(false);
1600 grandchild
.PutSpecifics(DefaultBookmarkSpecifics());
1601 grandchild
.PutBaseVersion(1);
1602 grandchild
.PutMtime(now_minus_2h
);
1607 ASSERT_EQ(6u, mock_server_
->committed_ids().size());
1608 // This test will NOT unroll deletes because SERVER_PARENT_ID is not set.
1609 // It will treat these like moves.
1610 vector
<syncable::Id
> commit_ids(mock_server_
->committed_ids());
1611 EXPECT_TRUE(ids_
.FromNumber(100) == commit_ids
[0]);
1612 EXPECT_TRUE(ids_
.FromNumber(101) == commit_ids
[1]);
1613 EXPECT_TRUE(ids_
.FromNumber(102) == commit_ids
[2]);
1614 // We don't guarantee the delete orders in this test, only that they occur
1616 std::sort(commit_ids
.begin() + 3, commit_ids
.end());
1617 EXPECT_TRUE(ids_
.FromNumber(103) == commit_ids
[3]);
1618 EXPECT_TRUE(ids_
.FromNumber(104) == commit_ids
[4]);
1619 EXPECT_TRUE(ids_
.FromNumber(105) == commit_ids
[5]);
1622 TEST_F(SyncerTest
, TestCommitListOrderingWithNewItems
) {
1623 syncable::Id parent1_id
= ids_
.MakeServer("p1");
1624 syncable::Id parent2_id
= ids_
.MakeServer("p2");
1627 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1628 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "1");
1629 ASSERT_TRUE(parent
.good());
1630 parent
.PutIsUnsynced(true);
1631 parent
.PutIsDir(true);
1632 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1633 parent
.PutId(parent1_id
);
1634 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "2");
1635 ASSERT_TRUE(child
.good());
1636 child
.PutIsUnsynced(true);
1637 child
.PutIsDir(true);
1638 child
.PutSpecifics(DefaultBookmarkSpecifics());
1639 child
.PutId(parent2_id
);
1640 parent
.PutBaseVersion(1);
1641 child
.PutBaseVersion(1);
1644 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1645 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, parent1_id
, "A");
1646 ASSERT_TRUE(parent
.good());
1647 parent
.PutIsUnsynced(true);
1648 parent
.PutIsDir(true);
1649 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1650 parent
.PutId(ids_
.FromNumber(102));
1651 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent1_id
, "B");
1652 ASSERT_TRUE(child
.good());
1653 child
.PutIsUnsynced(true);
1654 child
.PutIsDir(true);
1655 child
.PutSpecifics(DefaultBookmarkSpecifics());
1656 child
.PutId(ids_
.FromNumber(-103));
1657 parent
.PutBaseVersion(1);
1660 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1661 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, parent2_id
, "A");
1662 ASSERT_TRUE(parent
.good());
1663 parent
.PutIsUnsynced(true);
1664 parent
.PutIsDir(true);
1665 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1666 parent
.PutId(ids_
.FromNumber(-104));
1667 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent2_id
, "B");
1668 ASSERT_TRUE(child
.good());
1669 child
.PutIsUnsynced(true);
1670 child
.PutIsDir(true);
1671 child
.PutSpecifics(DefaultBookmarkSpecifics());
1672 child
.PutId(ids_
.FromNumber(105));
1673 child
.PutBaseVersion(1);
1677 ASSERT_EQ(6u, mock_server_
->committed_ids().size());
1679 // This strange iteration and std::count() usage is to allow the order to
1680 // vary. All we really care about is that parent1_id and parent2_id are the
1681 // first two IDs, and that the children make up the next four. Other than
1682 // that, ordering doesn't matter.
1684 vector
<syncable::Id
>::const_iterator i
=
1685 mock_server_
->committed_ids().begin();
1686 vector
<syncable::Id
>::const_iterator parents_begin
= i
;
1689 vector
<syncable::Id
>::const_iterator parents_end
= i
;
1690 vector
<syncable::Id
>::const_iterator children_begin
= i
;
1691 vector
<syncable::Id
>::const_iterator children_end
=
1692 mock_server_
->committed_ids().end();
1694 EXPECT_EQ(1, count(parents_begin
, parents_end
, parent1_id
));
1695 EXPECT_EQ(1, count(parents_begin
, parents_end
, parent2_id
));
1697 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(-103)));
1698 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(102)));
1699 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(105)));
1700 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(-104)));
1703 TEST_F(SyncerTest
, TestCommitListOrderingCounterexample
) {
1704 syncable::Id child2_id
= ids_
.NewServerId();
1707 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1708 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "P");
1709 ASSERT_TRUE(parent
.good());
1710 parent
.PutIsUnsynced(true);
1711 parent
.PutIsDir(true);
1712 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1713 parent
.PutId(parent_id_
);
1714 MutableEntry
child1(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "1");
1715 ASSERT_TRUE(child1
.good());
1716 child1
.PutIsUnsynced(true);
1717 child1
.PutId(child_id_
);
1718 child1
.PutSpecifics(DefaultBookmarkSpecifics());
1719 MutableEntry
child2(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "2");
1720 ASSERT_TRUE(child2
.good());
1721 child2
.PutIsUnsynced(true);
1722 child2
.PutSpecifics(DefaultBookmarkSpecifics());
1723 child2
.PutId(child2_id
);
1725 parent
.PutBaseVersion(1);
1726 child1
.PutBaseVersion(1);
1727 child2
.PutBaseVersion(1);
1731 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1732 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1733 // There are two possible valid orderings.
1734 if (child2_id
== mock_server_
->committed_ids()[1]) {
1735 EXPECT_TRUE(child2_id
== mock_server_
->committed_ids()[1]);
1736 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[2]);
1738 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1739 EXPECT_TRUE(child2_id
== mock_server_
->committed_ids()[2]);
1743 TEST_F(SyncerTest
, TestCommitListOrderingAndNewParent
) {
1744 string parent1_name
= "1";
1745 string parent2_name
= "A";
1746 string child_name
= "B";
1749 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1750 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(),
1752 ASSERT_TRUE(parent
.good());
1753 parent
.PutIsUnsynced(true);
1754 parent
.PutIsDir(true);
1755 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1756 parent
.PutId(parent_id_
);
1757 parent
.PutBaseVersion(1);
1760 syncable::Id parent2_id
= ids_
.NewLocalId();
1761 syncable::Id child_id
= ids_
.NewServerId();
1763 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1764 MutableEntry
parent2(
1765 &wtrans
, CREATE
, BOOKMARKS
, parent_id_
, parent2_name
);
1766 ASSERT_TRUE(parent2
.good());
1767 parent2
.PutIsUnsynced(true);
1768 parent2
.PutIsDir(true);
1769 parent2
.PutSpecifics(DefaultBookmarkSpecifics());
1770 parent2
.PutId(parent2_id
);
1773 &wtrans
, CREATE
, BOOKMARKS
, parent2_id
, child_name
);
1774 ASSERT_TRUE(child
.good());
1775 child
.PutIsUnsynced(true);
1776 child
.PutIsDir(true);
1777 child
.PutSpecifics(DefaultBookmarkSpecifics());
1778 child
.PutId(child_id
);
1779 child
.PutBaseVersion(1);
1783 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1784 // If this test starts failing, be aware other sort orders could be valid.
1785 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1786 EXPECT_TRUE(parent2_id
== mock_server_
->committed_ids()[1]);
1787 EXPECT_TRUE(child_id
== mock_server_
->committed_ids()[2]);
1789 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1790 // Check that things committed correctly.
1791 Entry
entry_1(&rtrans
, syncable::GET_BY_ID
, parent_id_
);
1792 EXPECT_EQ(entry_1
.GetNonUniqueName(), parent1_name
);
1793 // Check that parent2 is a subfolder of parent1.
1794 EXPECT_EQ(1, CountEntriesWithName(&rtrans
,
1798 // Parent2 was a local ID and thus should have changed on commit!
1799 Entry
pre_commit_entry_parent2(&rtrans
, syncable::GET_BY_ID
, parent2_id
);
1800 ASSERT_FALSE(pre_commit_entry_parent2
.good());
1802 // Look up the new ID.
1803 Id parent2_committed_id
=
1804 GetOnlyEntryWithName(&rtrans
, parent_id_
, parent2_name
);
1805 EXPECT_TRUE(parent2_committed_id
.ServerKnows());
1807 Entry
child(&rtrans
, syncable::GET_BY_ID
, child_id
);
1808 EXPECT_EQ(parent2_committed_id
, child
.GetParentId());
1812 TEST_F(SyncerTest
, TestCommitListOrderingAndNewParentAndChild
) {
1813 string parent_name
= "1";
1814 string parent2_name
= "A";
1815 string child_name
= "B";
1818 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1819 MutableEntry
parent(&wtrans
,
1823 ASSERT_TRUE(parent
.good());
1824 parent
.PutIsUnsynced(true);
1825 parent
.PutIsDir(true);
1826 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1827 parent
.PutId(parent_id_
);
1828 parent
.PutBaseVersion(1);
1831 int64 meta_handle_b
;
1832 const Id parent2_local_id
= ids_
.NewLocalId();
1833 const Id child_local_id
= ids_
.NewLocalId();
1835 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1836 MutableEntry
parent2(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, parent2_name
);
1837 ASSERT_TRUE(parent2
.good());
1838 parent2
.PutIsUnsynced(true);
1839 parent2
.PutIsDir(true);
1840 parent2
.PutSpecifics(DefaultBookmarkSpecifics());
1842 parent2
.PutId(parent2_local_id
);
1844 &wtrans
, CREATE
, BOOKMARKS
, parent2_local_id
, child_name
);
1845 ASSERT_TRUE(child
.good());
1846 child
.PutIsUnsynced(true);
1847 child
.PutIsDir(true);
1848 child
.PutSpecifics(DefaultBookmarkSpecifics());
1849 child
.PutId(child_local_id
);
1850 meta_handle_b
= child
.GetMetahandle();
1854 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1855 // If this test starts failing, be aware other sort orders could be valid.
1856 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1857 EXPECT_TRUE(parent2_local_id
== mock_server_
->committed_ids()[1]);
1858 EXPECT_TRUE(child_local_id
== mock_server_
->committed_ids()[2]);
1860 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1862 Entry
parent(&rtrans
, syncable::GET_BY_ID
,
1863 GetOnlyEntryWithName(&rtrans
, rtrans
.root_id(), parent_name
));
1864 ASSERT_TRUE(parent
.good());
1865 EXPECT_TRUE(parent
.GetId().ServerKnows());
1867 Entry
parent2(&rtrans
, syncable::GET_BY_ID
,
1868 GetOnlyEntryWithName(&rtrans
, parent
.GetId(), parent2_name
));
1869 ASSERT_TRUE(parent2
.good());
1870 EXPECT_TRUE(parent2
.GetId().ServerKnows());
1872 // Id changed on commit, so this should fail.
1873 Entry
local_parent2_id_entry(&rtrans
,
1874 syncable::GET_BY_ID
,
1876 ASSERT_FALSE(local_parent2_id_entry
.good());
1878 Entry
entry_b(&rtrans
, syncable::GET_BY_HANDLE
, meta_handle_b
);
1879 EXPECT_TRUE(entry_b
.GetId().ServerKnows());
1880 EXPECT_TRUE(parent2
.GetId()== entry_b
.GetParentId());
1884 TEST_F(SyncerTest
, UpdateWithZeroLengthName
) {
1885 // One illegal update
1886 mock_server_
->AddUpdateDirectory(
1887 1, 0, std::string(), 1, 10, foreign_cache_guid(), "-1");
1888 // And one legal one that we're going to delete.
1889 mock_server_
->AddUpdateDirectory(2, 0, "FOO", 1, 10,
1890 foreign_cache_guid(), "-2");
1892 // Delete the legal one. The new update has a null name.
1893 mock_server_
->AddUpdateDirectory(
1894 2, 0, std::string(), 2, 20, foreign_cache_guid(), "-2");
1895 mock_server_
->SetLastUpdateDeleted();
1899 TEST_F(SyncerTest
, TestBasicUpdate
) {
1900 string id
= "some_id";
1901 string parent_id
= "0";
1902 string name
= "in_root";
1904 int64 timestamp
= 10;
1905 mock_server_
->AddUpdateDirectory(id
, parent_id
, name
, version
, timestamp
,
1906 foreign_cache_guid(), "-1");
1910 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1911 Entry
entry(&trans
, GET_BY_ID
,
1912 syncable::Id::CreateFromServerId("some_id"));
1913 ASSERT_TRUE(entry
.good());
1914 EXPECT_TRUE(entry
.GetIsDir());
1915 EXPECT_TRUE(entry
.GetServerVersion()== version
);
1916 EXPECT_TRUE(entry
.GetBaseVersion()== version
);
1917 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
1918 EXPECT_FALSE(entry
.GetIsUnsynced());
1919 EXPECT_FALSE(entry
.GetServerIsDel());
1920 EXPECT_FALSE(entry
.GetIsDel());
1924 TEST_F(SyncerTest
, IllegalAndLegalUpdates
) {
1925 Id root
= TestIdFactory::root();
1926 // Should apply just fine.
1927 mock_server_
->AddUpdateDirectory(1, 0, "in_root", 10, 10,
1928 foreign_cache_guid(), "-1");
1930 // Same name. But this SHOULD work.
1931 mock_server_
->AddUpdateDirectory(2, 0, "in_root", 10, 10,
1932 foreign_cache_guid(), "-2");
1934 // Unknown parent: should never be applied. "-80" is a legal server ID,
1935 // because any string sent by the server is a legal server ID in the sync
1936 // protocol, but it's not the ID of any item known to the client. This
1937 // update should succeed validation, but be stuck in the unapplied state
1938 // until an item with the server ID "-80" arrives.
1939 mock_server_
->AddUpdateDirectory(3, -80, "bad_parent", 10, 10,
1940 foreign_cache_guid(), "-3");
1944 // Id 3 should be in conflict now.
1947 GetUpdateCounters(BOOKMARKS
).num_hierarchy_conflict_application_failures
);
1949 // The only request in that loop should have been a GetUpdate.
1950 // At that point, we didn't know whether or not we had conflicts.
1951 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
1952 VerifyHierarchyConflictsUnspecified(mock_server_
->last_request());
1954 // These entries will be used in the second set of updates.
1955 mock_server_
->AddUpdateDirectory(4, 0, "newer_version", 20, 10,
1956 foreign_cache_guid(), "-4");
1957 mock_server_
->AddUpdateDirectory(5, 0, "circular1", 10, 10,
1958 foreign_cache_guid(), "-5");
1959 mock_server_
->AddUpdateDirectory(6, 5, "circular2", 10, 10,
1960 foreign_cache_guid(), "-6");
1961 mock_server_
->AddUpdateDirectory(9, 3, "bad_parent_child", 10, 10,
1962 foreign_cache_guid(), "-9");
1963 mock_server_
->AddUpdateDirectory(100, 9, "bad_parent_child2", 10, 10,
1964 foreign_cache_guid(), "-100");
1965 mock_server_
->AddUpdateDirectory(10, 0, "dir_to_bookmark", 10, 10,
1966 foreign_cache_guid(), "-10");
1969 // The three items with an unresolved parent should be unapplied (3, 9, 100).
1970 // The name clash should also still be in conflict.
1973 GetUpdateCounters(BOOKMARKS
).num_hierarchy_conflict_application_failures
);
1975 // This time around, we knew that there were conflicts.
1976 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
1977 VerifyHierarchyConflictsReported(mock_server_
->last_request());
1980 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1981 // Even though it has the same name, it should work.
1982 Entry
name_clash(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
1983 ASSERT_TRUE(name_clash
.good());
1984 EXPECT_FALSE(name_clash
.GetIsUnappliedUpdate())
1985 << "Duplicate name SHOULD be OK.";
1987 Entry
bad_parent(&trans
, GET_BY_ID
, ids_
.FromNumber(3));
1988 ASSERT_TRUE(bad_parent
.good());
1989 EXPECT_TRUE(bad_parent
.GetIsUnappliedUpdate())
1990 << "child of unknown parent should be in conflict";
1992 Entry
bad_parent_child(&trans
, GET_BY_ID
, ids_
.FromNumber(9));
1993 ASSERT_TRUE(bad_parent_child
.good());
1994 EXPECT_TRUE(bad_parent_child
.GetIsUnappliedUpdate())
1995 << "grandchild of unknown parent should be in conflict";
1997 Entry
bad_parent_child2(&trans
, GET_BY_ID
, ids_
.FromNumber(100));
1998 ASSERT_TRUE(bad_parent_child2
.good());
1999 EXPECT_TRUE(bad_parent_child2
.GetIsUnappliedUpdate())
2000 << "great-grandchild of unknown parent should be in conflict";
2003 // Updating 1 should not affect item 2 of the same name.
2004 mock_server_
->AddUpdateDirectory(1, 0, "new_name", 20, 20,
2005 foreign_cache_guid(), "-1");
2007 // Moving 5 under 6 will create a cycle: a conflict.
2008 mock_server_
->AddUpdateDirectory(5, 6, "circular3", 20, 20,
2009 foreign_cache_guid(), "-5");
2011 // Flip the is_dir bit: should fail verify & be dropped.
2012 mock_server_
->AddUpdateBookmark(10, 0, "dir_to_bookmark", 20, 20,
2013 foreign_cache_guid(), "-10");
2016 // Version number older than last known: should fail verify & be dropped.
2017 mock_server_
->AddUpdateDirectory(4, 0, "old_version", 10, 10,
2018 foreign_cache_guid(), "-4");
2021 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2023 Entry
still_a_dir(&trans
, GET_BY_ID
, ids_
.FromNumber(10));
2024 ASSERT_TRUE(still_a_dir
.good());
2025 EXPECT_FALSE(still_a_dir
.GetIsUnappliedUpdate());
2026 EXPECT_EQ(10u, still_a_dir
.GetBaseVersion());
2027 EXPECT_EQ(10u, still_a_dir
.GetServerVersion());
2028 EXPECT_TRUE(still_a_dir
.GetIsDir());
2030 Entry
rename(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2031 ASSERT_TRUE(rename
.good());
2032 EXPECT_EQ(root
, rename
.GetParentId());
2033 EXPECT_EQ("new_name", rename
.GetNonUniqueName());
2034 EXPECT_FALSE(rename
.GetIsUnappliedUpdate());
2035 EXPECT_TRUE(ids_
.FromNumber(1) == rename
.GetId());
2036 EXPECT_EQ(20u, rename
.GetBaseVersion());
2038 Entry
name_clash(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
2039 ASSERT_TRUE(name_clash
.good());
2040 EXPECT_EQ(root
, name_clash
.GetParentId());
2041 EXPECT_TRUE(ids_
.FromNumber(2) == name_clash
.GetId());
2042 EXPECT_EQ(10u, name_clash
.GetBaseVersion());
2043 EXPECT_EQ("in_root", name_clash
.GetNonUniqueName());
2045 Entry
ignored_old_version(&trans
, GET_BY_ID
, ids_
.FromNumber(4));
2046 ASSERT_TRUE(ignored_old_version
.good());
2048 ignored_old_version
.GetNonUniqueName()== "newer_version");
2049 EXPECT_FALSE(ignored_old_version
.GetIsUnappliedUpdate());
2050 EXPECT_EQ(20u, ignored_old_version
.GetBaseVersion());
2052 Entry
circular_parent_issue(&trans
, GET_BY_ID
, ids_
.FromNumber(5));
2053 ASSERT_TRUE(circular_parent_issue
.good());
2054 EXPECT_TRUE(circular_parent_issue
.GetIsUnappliedUpdate())
2055 << "circular move should be in conflict";
2056 EXPECT_TRUE(circular_parent_issue
.GetParentId()== root_id_
);
2057 EXPECT_TRUE(circular_parent_issue
.GetServerParentId()==
2058 ids_
.FromNumber(6));
2059 EXPECT_EQ(10u, circular_parent_issue
.GetBaseVersion());
2061 Entry
circular_parent_target(&trans
, GET_BY_ID
, ids_
.FromNumber(6));
2062 ASSERT_TRUE(circular_parent_target
.good());
2063 EXPECT_FALSE(circular_parent_target
.GetIsUnappliedUpdate());
2064 EXPECT_TRUE(circular_parent_issue
.GetId()==
2065 circular_parent_target
.GetParentId());
2066 EXPECT_EQ(10u, circular_parent_target
.GetBaseVersion());
2069 EXPECT_FALSE(saw_syncer_event_
);
2072 GetUpdateCounters(BOOKMARKS
).num_hierarchy_conflict_application_failures
);
2075 // A commit with a lost response produces an update that has to be reunited with
2077 TEST_F(SyncerTest
, CommitReuniteUpdateAdjustsChildren
) {
2078 // Create a folder in the root.
2079 int64 metahandle_folder
;
2081 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2083 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_folder");
2084 ASSERT_TRUE(entry
.good());
2085 entry
.PutIsDir(true);
2086 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2087 entry
.PutIsUnsynced(true);
2088 metahandle_folder
= entry
.GetMetahandle();
2091 // Verify it and pull the ID out of the folder.
2092 syncable::Id folder_id
;
2093 int64 metahandle_entry
;
2095 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2096 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_folder
);
2097 ASSERT_TRUE(entry
.good());
2098 folder_id
= entry
.GetId();
2099 ASSERT_TRUE(!folder_id
.ServerKnows());
2102 // Create an entry in the newly created folder.
2104 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2105 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, folder_id
, "new_entry");
2106 ASSERT_TRUE(entry
.good());
2107 metahandle_entry
= entry
.GetMetahandle();
2108 WriteTestDataToEntry(&trans
, &entry
);
2111 // Verify it and pull the ID out of the entry.
2112 syncable::Id entry_id
;
2114 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2115 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, metahandle_entry
);
2116 ASSERT_TRUE(entry
.good());
2117 EXPECT_EQ(folder_id
, entry
.GetParentId());
2118 EXPECT_EQ("new_entry", entry
.GetNonUniqueName());
2119 entry_id
= entry
.GetId();
2120 EXPECT_TRUE(!entry_id
.ServerKnows());
2121 VerifyTestDataInEntry(&trans
, &entry
);
2124 // Now, to emulate a commit response failure, we just don't commit it.
2125 int64 new_version
= 150; // any larger value.
2126 int64 timestamp
= 20; // arbitrary value.
2127 syncable::Id new_folder_id
=
2128 syncable::Id::CreateFromServerId("folder_server_id");
2130 // The following update should cause the folder to both apply the update, as
2131 // well as reassociate the id.
2132 mock_server_
->AddUpdateDirectory(new_folder_id
, root_id_
,
2133 "new_folder", new_version
, timestamp
,
2134 local_cache_guid(), folder_id
.GetServerId());
2136 // We don't want it accidentally committed, just the update applied.
2137 mock_server_
->set_conflict_all_commits(true);
2139 // Alright! Apply that update!
2142 // The folder's ID should have been updated.
2143 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2144 Entry
folder(&trans
, GET_BY_HANDLE
, metahandle_folder
);
2145 ASSERT_TRUE(folder
.good());
2146 EXPECT_EQ("new_folder", folder
.GetNonUniqueName());
2147 EXPECT_TRUE(new_version
== folder
.GetBaseVersion());
2148 EXPECT_TRUE(new_folder_id
== folder
.GetId());
2149 EXPECT_TRUE(folder
.GetId().ServerKnows());
2150 EXPECT_EQ(trans
.root_id(), folder
.GetParentId());
2152 // Since it was updated, the old folder should not exist.
2153 Entry
old_dead_folder(&trans
, GET_BY_ID
, folder_id
);
2154 EXPECT_FALSE(old_dead_folder
.good());
2156 // The child's parent should have changed.
2157 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, metahandle_entry
);
2158 ASSERT_TRUE(entry
.good());
2159 EXPECT_EQ("new_entry", entry
.GetNonUniqueName());
2160 EXPECT_EQ(new_folder_id
, entry
.GetParentId());
2161 EXPECT_TRUE(!entry
.GetId().ServerKnows());
2162 VerifyTestDataInEntry(&trans
, &entry
);
2166 // A commit with a lost response produces an update that has to be reunited with
2168 TEST_F(SyncerTest
, CommitReuniteUpdate
) {
2169 // Create an entry in the root.
2170 int64 entry_metahandle
;
2172 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2173 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_entry");
2174 ASSERT_TRUE(entry
.good());
2175 entry_metahandle
= entry
.GetMetahandle();
2176 WriteTestDataToEntry(&trans
, &entry
);
2179 // Verify it and pull the ID out.
2180 syncable::Id entry_id
;
2182 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2184 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2185 ASSERT_TRUE(entry
.good());
2186 entry_id
= entry
.GetId();
2187 EXPECT_TRUE(!entry_id
.ServerKnows());
2188 VerifyTestDataInEntry(&trans
, &entry
);
2191 // Now, to emulate a commit response failure, we just don't commit it.
2192 int64 new_version
= 150; // any larger value.
2193 int64 timestamp
= 20; // arbitrary value.
2194 syncable::Id new_entry_id
= syncable::Id::CreateFromServerId("server_id");
2196 // Generate an update from the server with a relevant ID reassignment.
2197 mock_server_
->AddUpdateBookmark(new_entry_id
, root_id_
,
2198 "new_entry", new_version
, timestamp
,
2199 local_cache_guid(), entry_id
.GetServerId());
2201 // We don't want it accidentally committed, just the update applied.
2202 mock_server_
->set_conflict_all_commits(true);
2204 // Alright! Apply that update!
2207 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2208 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2209 ASSERT_TRUE(entry
.good());
2210 EXPECT_TRUE(new_version
== entry
.GetBaseVersion());
2211 EXPECT_TRUE(new_entry_id
== entry
.GetId());
2212 EXPECT_EQ("new_entry", entry
.GetNonUniqueName());
2216 // A commit with a lost response must work even if the local entry was deleted
2217 // before the update is applied. We should not duplicate the local entry in
2218 // this case, but just create another one alongside. We may wish to examine
2219 // this behavior in the future as it can create hanging uploads that never
2220 // finish, that must be cleaned up on the server side after some time.
2221 TEST_F(SyncerTest
, CommitReuniteUpdateDoesNotChokeOnDeletedLocalEntry
) {
2222 // Create a entry in the root.
2223 int64 entry_metahandle
;
2225 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2226 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_entry");
2227 ASSERT_TRUE(entry
.good());
2228 entry_metahandle
= entry
.GetMetahandle();
2229 WriteTestDataToEntry(&trans
, &entry
);
2231 // Verify it and pull the ID out.
2232 syncable::Id entry_id
;
2234 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2235 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2236 ASSERT_TRUE(entry
.good());
2237 entry_id
= entry
.GetId();
2238 EXPECT_TRUE(!entry_id
.ServerKnows());
2239 VerifyTestDataInEntry(&trans
, &entry
);
2242 // Now, to emulate a commit response failure, we just don't commit it.
2243 int64 new_version
= 150; // any larger value.
2244 int64 timestamp
= 20; // arbitrary value.
2245 syncable::Id new_entry_id
= syncable::Id::CreateFromServerId("server_id");
2247 // Generate an update from the server with a relevant ID reassignment.
2248 mock_server_
->AddUpdateBookmark(new_entry_id
, root_id_
,
2249 "new_entry", new_version
, timestamp
,
2250 local_cache_guid(), entry_id
.GetServerId());
2252 // We don't want it accidentally committed, just the update applied.
2253 mock_server_
->set_conflict_all_commits(true);
2255 // Purposefully delete the entry now before the update application finishes.
2257 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2258 Id new_entry_id
= GetOnlyEntryWithName(
2259 &trans
, trans
.root_id(), "new_entry");
2260 MutableEntry
entry(&trans
, GET_BY_ID
, new_entry_id
);
2261 ASSERT_TRUE(entry
.good());
2262 entry
.PutIsDel(true);
2265 // Just don't CHECK fail in sync, have the update split.
2268 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2269 Id new_entry_id
= GetOnlyEntryWithName(
2270 &trans
, trans
.root_id(), "new_entry");
2271 Entry
entry(&trans
, GET_BY_ID
, new_entry_id
);
2272 ASSERT_TRUE(entry
.good());
2273 EXPECT_FALSE(entry
.GetIsDel());
2275 Entry
old_entry(&trans
, GET_BY_ID
, entry_id
);
2276 ASSERT_TRUE(old_entry
.good());
2277 EXPECT_TRUE(old_entry
.GetIsDel());
2281 // TODO(chron): Add more unsanitized name tests.
2282 TEST_F(SyncerTest
, ConflictMatchingEntryHandlesUnsanitizedNames
) {
2283 mock_server_
->AddUpdateDirectory(1, 0, "A/A", 10, 10,
2284 foreign_cache_guid(), "-1");
2285 mock_server_
->AddUpdateDirectory(2, 0, "B/B", 10, 10,
2286 foreign_cache_guid(), "-2");
2287 mock_server_
->set_conflict_all_commits(true);
2290 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2292 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2293 ASSERT_TRUE(A
.good());
2294 A
.PutIsUnsynced(true);
2295 A
.PutIsUnappliedUpdate(true);
2296 A
.PutServerVersion(20);
2298 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
2299 ASSERT_TRUE(B
.good());
2300 B
.PutIsUnappliedUpdate(true);
2301 B
.PutServerVersion(20);
2304 saw_syncer_event_
= false;
2305 mock_server_
->set_conflict_all_commits(false);
2308 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2310 Entry
A(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2311 ASSERT_TRUE(A
.good());
2312 EXPECT_TRUE(A
.GetIsUnsynced()== false);
2313 EXPECT_TRUE(A
.GetIsUnappliedUpdate()== false);
2314 EXPECT_TRUE(A
.GetServerVersion()== 20);
2316 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
2317 ASSERT_TRUE(B
.good());
2318 EXPECT_TRUE(B
.GetIsUnsynced()== false);
2319 EXPECT_TRUE(B
.GetIsUnappliedUpdate()== false);
2320 EXPECT_TRUE(B
.GetServerVersion()== 20);
2324 TEST_F(SyncerTest
, ConflictMatchingEntryHandlesNormalNames
) {
2325 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
2326 foreign_cache_guid(), "-1");
2327 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
2328 foreign_cache_guid(), "-2");
2329 mock_server_
->set_conflict_all_commits(true);
2332 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2334 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2335 ASSERT_TRUE(A
.good());
2336 A
.PutIsUnsynced(true);
2337 A
.PutIsUnappliedUpdate(true);
2338 A
.PutServerVersion(20);
2340 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
2341 ASSERT_TRUE(B
.good());
2342 B
.PutIsUnappliedUpdate(true);
2343 B
.PutServerVersion(20);
2346 saw_syncer_event_
= false;
2347 mock_server_
->set_conflict_all_commits(false);
2350 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2352 Entry
A(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2353 ASSERT_TRUE(A
.good());
2354 EXPECT_TRUE(A
.GetIsUnsynced()== false);
2355 EXPECT_TRUE(A
.GetIsUnappliedUpdate()== false);
2356 EXPECT_TRUE(A
.GetServerVersion()== 20);
2358 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
2359 ASSERT_TRUE(B
.good());
2360 EXPECT_TRUE(B
.GetIsUnsynced()== false);
2361 EXPECT_TRUE(B
.GetIsUnappliedUpdate()== false);
2362 EXPECT_TRUE(B
.GetServerVersion()== 20);
2366 TEST_F(SyncerTest
, ReverseFolderOrderingTest
) {
2367 mock_server_
->AddUpdateDirectory(4, 3, "ggchild", 10, 10,
2368 foreign_cache_guid(), "-4");
2369 mock_server_
->AddUpdateDirectory(3, 2, "gchild", 10, 10,
2370 foreign_cache_guid(), "-3");
2371 mock_server_
->AddUpdateDirectory(5, 4, "gggchild", 10, 10,
2372 foreign_cache_guid(), "-5");
2373 mock_server_
->AddUpdateDirectory(2, 1, "child", 10, 10,
2374 foreign_cache_guid(), "-2");
2375 mock_server_
->AddUpdateDirectory(1, 0, "parent", 10, 10,
2376 foreign_cache_guid(), "-1");
2378 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2380 Id child_id
= GetOnlyEntryWithName(
2381 &trans
, ids_
.FromNumber(4), "gggchild");
2382 Entry
child(&trans
, GET_BY_ID
, child_id
);
2383 ASSERT_TRUE(child
.good());
2386 class EntryCreatedInNewFolderTest
: public SyncerTest
{
2388 void CreateFolderInBob() {
2389 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2390 MutableEntry
bob(&trans
,
2391 syncable::GET_BY_ID
,
2392 GetOnlyEntryWithName(&trans
,
2393 TestIdFactory::root(),
2397 MutableEntry
entry2(
2398 &trans
, CREATE
, BOOKMARKS
, bob
.GetId(), "bob");
2399 CHECK(entry2
.good());
2400 entry2
.PutIsDir(true);
2401 entry2
.PutIsUnsynced(true);
2402 entry2
.PutSpecifics(DefaultBookmarkSpecifics());
2406 TEST_F(EntryCreatedInNewFolderTest
, EntryCreatedInNewFolderMidSync
) {
2408 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2409 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2410 ASSERT_TRUE(entry
.good());
2411 entry
.PutIsDir(true);
2412 entry
.PutIsUnsynced(true);
2413 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2416 mock_server_
->SetMidCommitCallback(
2417 base::Bind(&EntryCreatedInNewFolderTest::CreateFolderInBob
,
2418 base::Unretained(this)));
2420 // We loop until no unsynced handles remain, so we will commit both ids.
2421 EXPECT_EQ(2u, mock_server_
->committed_ids().size());
2423 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2424 Entry
parent_entry(&trans
, syncable::GET_BY_ID
,
2425 GetOnlyEntryWithName(&trans
, TestIdFactory::root(), "bob"));
2426 ASSERT_TRUE(parent_entry
.good());
2429 GetOnlyEntryWithName(&trans
, parent_entry
.GetId(), "bob");
2430 Entry
child(&trans
, syncable::GET_BY_ID
, child_id
);
2431 ASSERT_TRUE(child
.good());
2432 EXPECT_EQ(parent_entry
.GetId(), child
.GetParentId());
2436 TEST_F(SyncerTest
, NegativeIDInUpdate
) {
2437 mock_server_
->AddUpdateBookmark(-10, 0, "bad", 40, 40,
2438 foreign_cache_guid(), "-100");
2440 // The negative id would make us CHECK!
2443 TEST_F(SyncerTest
, UnappliedUpdateOnCreatedItemItemDoesNotCrash
) {
2444 int64 metahandle_fred
;
2445 syncable::Id orig_id
;
2448 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2449 MutableEntry
fred_match(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(),
2451 ASSERT_TRUE(fred_match
.good());
2452 metahandle_fred
= fred_match
.GetMetahandle();
2453 orig_id
= fred_match
.GetId();
2454 WriteTestDataToEntry(&trans
, &fred_match
);
2458 EXPECT_EQ(1u, mock_server_
->committed_ids().size());
2459 mock_server_
->set_conflict_all_commits(true);
2460 syncable::Id fred_match_id
;
2462 // Now receive a change from outside.
2463 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2464 MutableEntry
fred_match(&trans
, GET_BY_HANDLE
, metahandle_fred
);
2465 ASSERT_TRUE(fred_match
.good());
2466 EXPECT_TRUE(fred_match
.GetId().ServerKnows());
2467 fred_match_id
= fred_match
.GetId();
2468 mock_server_
->AddUpdateBookmark(fred_match_id
, trans
.root_id(),
2469 "fred_match", 40, 40, local_cache_guid(), orig_id
.GetServerId());
2472 for (int i
= 0 ; i
< 30 ; ++i
) {
2478 * In the event that we have a double changed entry, that is changed on both
2479 * the client and the server, the conflict resolver should just drop one of
2480 * them and accept the other.
2483 TEST_F(SyncerTest
, DoublyChangedWithResolver
) {
2484 syncable::Id local_id
;
2486 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2487 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Folder");
2488 ASSERT_TRUE(parent
.good());
2489 parent
.PutIsDir(true);
2490 parent
.PutId(parent_id_
);
2491 parent
.PutBaseVersion(5);
2492 parent
.PutSpecifics(DefaultBookmarkSpecifics());
2493 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete.htm");
2494 ASSERT_TRUE(child
.good());
2495 local_id
= child
.GetId();
2496 child
.PutId(child_id_
);
2497 child
.PutBaseVersion(10);
2498 WriteTestDataToEntry(&wtrans
, &child
);
2500 mock_server_
->AddUpdateBookmark(child_id_
, parent_id_
, "Pete2.htm", 11, 10,
2501 local_cache_guid(), local_id
.GetServerId());
2502 mock_server_
->set_conflict_all_commits(true);
2504 syncable::Directory::Metahandles children
;
2506 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2507 directory()->GetChildHandlesById(&trans
, parent_id_
, &children
);
2508 // We expect the conflict resolver to preserve the local entry.
2509 Entry
child(&trans
, syncable::GET_BY_ID
, child_id_
);
2510 ASSERT_TRUE(child
.good());
2511 EXPECT_TRUE(child
.GetIsUnsynced());
2512 EXPECT_FALSE(child
.GetIsUnappliedUpdate());
2513 EXPECT_TRUE(child
.GetSpecifics().has_bookmark());
2514 EXPECT_EQ("Pete.htm", child
.GetNonUniqueName());
2515 VerifyTestBookmarkDataInEntry(&child
);
2518 // Only one entry, since we just overwrite one.
2519 EXPECT_EQ(1u, children
.size());
2520 saw_syncer_event_
= false;
2523 // We got this repro case when someone was editing bookmarks while sync was
2524 // occuring. The entry had changed out underneath the user.
2525 TEST_F(SyncerTest
, CommitsUpdateDoesntAlterEntry
) {
2526 const base::Time
& test_time
= ProtoTimeToTime(123456);
2527 syncable::Id local_id
;
2528 int64 entry_metahandle
;
2530 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2531 MutableEntry
entry(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Pete");
2532 ASSERT_TRUE(entry
.good());
2533 EXPECT_FALSE(entry
.GetId().ServerKnows());
2534 local_id
= entry
.GetId();
2535 entry
.PutIsDir(true);
2536 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2537 entry
.PutIsUnsynced(true);
2538 entry
.PutMtime(test_time
);
2539 entry_metahandle
= entry
.GetMetahandle();
2545 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2546 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, entry_metahandle
);
2547 ASSERT_TRUE(entry
.good());
2549 EXPECT_TRUE(id
.ServerKnows());
2550 version
= entry
.GetBaseVersion();
2552 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
2553 update
->set_originator_cache_guid(local_cache_guid());
2554 update
->set_originator_client_item_id(local_id
.GetServerId());
2555 EXPECT_EQ("Pete", update
->name());
2556 EXPECT_EQ(id
.GetServerId(), update
->id_string());
2557 EXPECT_EQ(root_id_
.GetServerId(), update
->parent_id_string());
2558 EXPECT_EQ(version
, update
->version());
2561 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2562 Entry
entry(&trans
, syncable::GET_BY_ID
, id
);
2563 ASSERT_TRUE(entry
.good());
2564 EXPECT_TRUE(entry
.GetMtime()== test_time
);
2568 TEST_F(SyncerTest
, ParentAndChildBothMatch
) {
2569 // Disable PREFERENCES which is enabled at the setup step to avoid
2571 // PREFERENCES root folder and failing the test below that verifies the number
2572 // of children at the root.
2573 DisableDatatype(PREFERENCES
);
2575 const FullModelTypeSet all_types
= FullModelTypeSet::All();
2576 syncable::Id parent_id
= ids_
.NewServerId();
2577 syncable::Id child_id
= ids_
.NewServerId();
2578 syncable::Id parent_local_id
;
2579 syncable::Id child_local_id
;
2582 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2583 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Folder");
2584 ASSERT_TRUE(parent
.good());
2585 parent_local_id
= parent
.GetId();
2586 parent
.PutIsDir(true);
2587 parent
.PutIsUnsynced(true);
2588 parent
.PutId(parent_id
);
2589 parent
.PutBaseVersion(1);
2590 parent
.PutSpecifics(DefaultBookmarkSpecifics());
2592 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent
.GetId(), "test.htm");
2593 ASSERT_TRUE(child
.good());
2594 child_local_id
= child
.GetId();
2595 child
.PutId(child_id
);
2596 child
.PutBaseVersion(1);
2597 child
.PutSpecifics(DefaultBookmarkSpecifics());
2598 WriteTestDataToEntry(&wtrans
, &child
);
2600 mock_server_
->AddUpdateDirectory(parent_id
, root_id_
, "Folder", 10, 10,
2602 parent_local_id
.GetServerId());
2603 mock_server_
->AddUpdateBookmark(child_id
, parent_id
, "test.htm", 10, 10,
2605 child_local_id
.GetServerId());
2606 mock_server_
->set_conflict_all_commits(true);
2611 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2612 Directory::Metahandles children
;
2613 directory()->GetChildHandlesById(&trans
, root_id_
, &children
);
2614 EXPECT_EQ(1u, children
.size());
2615 directory()->GetChildHandlesById(&trans
, parent_id
, &children
);
2616 EXPECT_EQ(1u, children
.size());
2617 std::vector
<int64
> unapplied
;
2618 directory()->GetUnappliedUpdateMetaHandles(&trans
, all_types
, &unapplied
);
2619 EXPECT_EQ(0u, unapplied
.size());
2620 syncable::Directory::Metahandles unsynced
;
2621 directory()->GetUnsyncedMetaHandles(&trans
, &unsynced
);
2622 EXPECT_EQ(0u, unsynced
.size());
2623 saw_syncer_event_
= false;
2627 TEST_F(SyncerTest
, CommittingNewDeleted
) {
2629 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2630 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2631 entry
.PutIsUnsynced(true);
2632 entry
.PutIsDel(true);
2635 EXPECT_EQ(0u, mock_server_
->committed_ids().size());
2638 // Original problem synopsis:
2639 // Check failed: entry->GetBaseVersion()<= entry->GetServerVersion()
2640 // Client creates entry, client finishes committing entry. Between
2641 // commit and getting update back, we delete the entry.
2642 // We get the update for the entry, but the local one was modified
2643 // so we store the entry but don't apply it. IS_UNAPPLIED_UPDATE is set.
2644 // We commit deletion and get a new version number.
2645 // We apply unapplied updates again before we get the update about the deletion.
2646 // This means we have an unapplied update where server_version < base_version.
2647 TEST_F(SyncerTest
, UnappliedUpdateDuringCommit
) {
2648 // This test is a little fake.
2650 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2651 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2652 entry
.PutId(ids_
.FromNumber(20));
2653 entry
.PutBaseVersion(1);
2654 entry
.PutServerVersion(1);
2655 entry
.PutServerParentId(ids_
.FromNumber(9999)); // Bad parent.
2656 entry
.PutIsUnsynced(true);
2657 entry
.PutIsUnappliedUpdate(true);
2658 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2659 entry
.PutServerSpecifics(DefaultBookmarkSpecifics());
2660 entry
.PutIsDel(false);
2663 EXPECT_EQ(1, session_
->status_controller().TotalNumConflictingItems());
2664 saw_syncer_event_
= false;
2667 // Original problem synopsis:
2669 // Unexpected error during sync if we:
2670 // make a new folder bob
2672 // make a new folder fred
2673 // move bob into fred
2676 // if no syncing occured midway, bob will have an illegal parent
2677 TEST_F(SyncerTest
, DeletingEntryInFolder
) {
2678 // This test is a little fake.
2679 int64 existing_metahandle
;
2681 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2682 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "existing");
2683 ASSERT_TRUE(entry
.good());
2684 entry
.PutIsDir(true);
2685 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2686 entry
.PutIsUnsynced(true);
2687 existing_metahandle
= entry
.GetMetahandle();
2691 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2692 MutableEntry
newfolder(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new");
2693 ASSERT_TRUE(newfolder
.good());
2694 newfolder
.PutIsDir(true);
2695 newfolder
.PutSpecifics(DefaultBookmarkSpecifics());
2696 newfolder
.PutIsUnsynced(true);
2698 MutableEntry
existing(&trans
, GET_BY_HANDLE
, existing_metahandle
);
2699 ASSERT_TRUE(existing
.good());
2700 existing
.PutParentId(newfolder
.GetId());
2701 existing
.PutIsUnsynced(true);
2702 EXPECT_TRUE(existing
.GetId().ServerKnows());
2704 newfolder
.PutIsDel(true);
2705 existing
.PutIsDel(true);
2708 EXPECT_EQ(0, GetCommitCounters(BOOKMARKS
).num_commits_conflict
);
2711 TEST_F(SyncerTest
, DeletingEntryWithLocalEdits
) {
2712 int64 newfolder_metahandle
;
2714 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
2715 foreign_cache_guid(), "-1");
2718 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2719 MutableEntry
newfolder(
2720 &trans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(1), "local");
2721 ASSERT_TRUE(newfolder
.good());
2722 newfolder
.PutIsUnsynced(true);
2723 newfolder
.PutIsDir(true);
2724 newfolder
.PutSpecifics(DefaultBookmarkSpecifics());
2725 newfolder_metahandle
= newfolder
.GetMetahandle();
2727 mock_server_
->AddUpdateDirectory(1, 0, "bob", 2, 20,
2728 foreign_cache_guid(), "-1");
2729 mock_server_
->SetLastUpdateDeleted();
2730 SyncShareConfigure();
2732 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2733 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, newfolder_metahandle
);
2734 ASSERT_TRUE(entry
.good());
2738 TEST_F(SyncerTest
, FolderSwapUpdate
) {
2739 mock_server_
->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2740 foreign_cache_guid(), "-7801");
2741 mock_server_
->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2742 foreign_cache_guid(), "-1024");
2744 mock_server_
->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2745 foreign_cache_guid(), "-1024");
2746 mock_server_
->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2747 foreign_cache_guid(), "-7801");
2750 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2751 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2752 ASSERT_TRUE(id1
.good());
2753 EXPECT_TRUE("fred" == id1
.GetNonUniqueName());
2754 EXPECT_TRUE(root_id_
== id1
.GetParentId());
2755 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2756 ASSERT_TRUE(id2
.good());
2757 EXPECT_TRUE("bob" == id2
.GetNonUniqueName());
2758 EXPECT_TRUE(root_id_
== id2
.GetParentId());
2760 saw_syncer_event_
= false;
2763 TEST_F(SyncerTest
, NameCollidingFolderSwapWorksFine
) {
2764 mock_server_
->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2765 foreign_cache_guid(), "-7801");
2766 mock_server_
->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2767 foreign_cache_guid(), "-1024");
2768 mock_server_
->AddUpdateDirectory(4096, 0, "alice", 1, 10,
2769 foreign_cache_guid(), "-4096");
2772 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2773 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2774 ASSERT_TRUE(id1
.good());
2775 EXPECT_TRUE("bob" == id1
.GetNonUniqueName());
2776 EXPECT_TRUE(root_id_
== id1
.GetParentId());
2777 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2778 ASSERT_TRUE(id2
.good());
2779 EXPECT_TRUE("fred" == id2
.GetNonUniqueName());
2780 EXPECT_TRUE(root_id_
== id2
.GetParentId());
2781 Entry
id3(&trans
, GET_BY_ID
, ids_
.FromNumber(4096));
2782 ASSERT_TRUE(id3
.good());
2783 EXPECT_TRUE("alice" == id3
.GetNonUniqueName());
2784 EXPECT_TRUE(root_id_
== id3
.GetParentId());
2786 mock_server_
->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2787 foreign_cache_guid(), "-1024");
2788 mock_server_
->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2789 foreign_cache_guid(), "-7801");
2790 mock_server_
->AddUpdateDirectory(4096, 0, "bob", 2, 20,
2791 foreign_cache_guid(), "-4096");
2794 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2795 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2796 ASSERT_TRUE(id1
.good());
2797 EXPECT_TRUE("fred" == id1
.GetNonUniqueName());
2798 EXPECT_TRUE(root_id_
== id1
.GetParentId());
2799 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2800 ASSERT_TRUE(id2
.good());
2801 EXPECT_TRUE("bob" == id2
.GetNonUniqueName());
2802 EXPECT_TRUE(root_id_
== id2
.GetParentId());
2803 Entry
id3(&trans
, GET_BY_ID
, ids_
.FromNumber(4096));
2804 ASSERT_TRUE(id3
.good());
2805 EXPECT_TRUE("bob" == id3
.GetNonUniqueName());
2806 EXPECT_TRUE(root_id_
== id3
.GetParentId());
2808 saw_syncer_event_
= false;
2811 // Committing more than kDefaultMaxCommitBatchSize items requires that
2812 // we post more than one commit command to the server. This test makes
2813 // sure that scenario works as expected.
2814 TEST_F(SyncerTest
, CommitManyItemsInOneGo_Success
) {
2815 uint32 num_batches
= 3;
2816 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2818 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2819 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2820 string nameutf8
= base::StringPrintf("%d", i
);
2821 string
name(nameutf8
.begin(), nameutf8
.end());
2822 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2823 e
.PutIsUnsynced(true);
2825 e
.PutSpecifics(DefaultBookmarkSpecifics());
2828 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2831 EXPECT_EQ(num_batches
, mock_server_
->commit_messages().size());
2832 EXPECT_EQ(0, directory()->unsynced_entity_count());
2835 // Test that a single failure to contact the server will cause us to exit the
2836 // commit loop immediately.
2837 TEST_F(SyncerTest
, CommitManyItemsInOneGo_PostBufferFail
) {
2838 uint32 num_batches
= 3;
2839 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2841 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2842 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2843 string nameutf8
= base::StringPrintf("%d", i
);
2844 string
name(nameutf8
.begin(), nameutf8
.end());
2845 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2846 e
.PutIsUnsynced(true);
2848 e
.PutSpecifics(DefaultBookmarkSpecifics());
2851 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2853 // The second commit should fail. It will be preceded by one successful
2854 // GetUpdate and one succesful commit.
2855 mock_server_
->FailNthPostBufferToPathCall(3);
2858 EXPECT_EQ(1U, mock_server_
->commit_messages().size());
2859 EXPECT_EQ(SYNC_SERVER_ERROR
,
2860 session_
->status_controller().model_neutral_state().commit_result
);
2861 EXPECT_EQ(items_to_commit
- kDefaultMaxCommitBatchSize
,
2862 directory()->unsynced_entity_count());
2865 // Test that a single conflict response from the server will cause us to exit
2866 // the commit loop immediately.
2867 TEST_F(SyncerTest
, CommitManyItemsInOneGo_CommitConflict
) {
2868 uint32 num_batches
= 2;
2869 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2871 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2872 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2873 string nameutf8
= base::StringPrintf("%d", i
);
2874 string
name(nameutf8
.begin(), nameutf8
.end());
2875 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2876 e
.PutIsUnsynced(true);
2878 e
.PutSpecifics(DefaultBookmarkSpecifics());
2881 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2883 // Return a CONFLICT response for the first item.
2884 mock_server_
->set_conflict_n_commits(1);
2887 // We should stop looping at the first sign of trouble.
2888 EXPECT_EQ(1U, mock_server_
->commit_messages().size());
2889 EXPECT_EQ(items_to_commit
- (kDefaultMaxCommitBatchSize
- 1),
2890 directory()->unsynced_entity_count());
2893 // Tests that sending debug info events works.
2894 TEST_F(SyncerTest
, SendDebugInfoEventsOnGetUpdates_HappyCase
) {
2895 debug_info_getter_
->AddDebugEvent();
2896 debug_info_getter_
->AddDebugEvent();
2900 // Verify we received one GetUpdates request with two debug info events.
2901 EXPECT_EQ(1U, mock_server_
->requests().size());
2902 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2903 EXPECT_EQ(2, mock_server_
->last_request().debug_info().events_size());
2907 // See that we received another GetUpdates request, but that it contains no
2908 // debug info events.
2909 EXPECT_EQ(2U, mock_server_
->requests().size());
2910 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2911 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
2913 debug_info_getter_
->AddDebugEvent();
2917 // See that we received another GetUpdates request and it contains one debug
2919 EXPECT_EQ(3U, mock_server_
->requests().size());
2920 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2921 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
2924 // Tests that debug info events are dropped on server error.
2925 TEST_F(SyncerTest
, SendDebugInfoEventsOnGetUpdates_PostFailsDontDrop
) {
2926 debug_info_getter_
->AddDebugEvent();
2927 debug_info_getter_
->AddDebugEvent();
2929 mock_server_
->FailNextPostBufferToPathCall();
2932 // Verify we attempted to send one GetUpdates request with two debug info
2934 EXPECT_EQ(1U, mock_server_
->requests().size());
2935 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2936 EXPECT_EQ(2, mock_server_
->last_request().debug_info().events_size());
2940 // See that the client resent the two debug info events.
2941 EXPECT_EQ(2U, mock_server_
->requests().size());
2942 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2943 EXPECT_EQ(2, mock_server_
->last_request().debug_info().events_size());
2945 // The previous send was successful so this next one shouldn't generate any
2946 // debug info events.
2948 EXPECT_EQ(3U, mock_server_
->requests().size());
2949 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2950 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
2953 // Tests that commit failure with conflict will trigger GetUpdates for next
2955 TEST_F(SyncerTest
, CommitFailureWithConflict
) {
2956 ConfigureNoGetUpdatesRequired();
2957 CreateUnsyncedDirectory("X", "id_X");
2958 EXPECT_FALSE(nudge_tracker_
.IsGetUpdatesRequired());
2961 EXPECT_FALSE(nudge_tracker_
.IsGetUpdatesRequired());
2963 CreateUnsyncedDirectory("Y", "id_Y");
2964 mock_server_
->set_conflict_n_commits(1);
2966 EXPECT_TRUE(nudge_tracker_
.IsGetUpdatesRequired());
2968 nudge_tracker_
.RecordSuccessfulSyncCycle();
2969 EXPECT_FALSE(nudge_tracker_
.IsGetUpdatesRequired());
2972 // Tests that sending debug info events on Commit works.
2973 TEST_F(SyncerTest
, SendDebugInfoEventsOnCommit_HappyCase
) {
2974 // Make sure GetUpdate isn't call as it would "steal" debug info events before
2975 // Commit has a chance to send them.
2976 ConfigureNoGetUpdatesRequired();
2978 // Generate a debug info event and trigger a commit.
2979 debug_info_getter_
->AddDebugEvent();
2980 CreateUnsyncedDirectory("X", "id_X");
2983 // Verify that the last request received is a Commit and that it contains a
2984 // debug info event.
2985 EXPECT_EQ(1U, mock_server_
->requests().size());
2986 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2987 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
2989 // Generate another commit, but no debug info event.
2990 CreateUnsyncedDirectory("Y", "id_Y");
2993 // See that it was received and contains no debug info events.
2994 EXPECT_EQ(2U, mock_server_
->requests().size());
2995 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2996 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
2999 // Tests that debug info events are not dropped on server error.
3000 TEST_F(SyncerTest
, SendDebugInfoEventsOnCommit_PostFailsDontDrop
) {
3001 // Make sure GetUpdate isn't call as it would "steal" debug info events before
3002 // Commit has a chance to send them.
3003 ConfigureNoGetUpdatesRequired();
3005 mock_server_
->FailNextPostBufferToPathCall();
3007 // Generate a debug info event and trigger a commit.
3008 debug_info_getter_
->AddDebugEvent();
3009 CreateUnsyncedDirectory("X", "id_X");
3012 // Verify that the last request sent is a Commit and that it contains a debug
3014 EXPECT_EQ(1U, mock_server_
->requests().size());
3015 ASSERT_TRUE(mock_server_
->last_request().has_commit());
3016 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
3021 // Verify that we've received another Commit and that it contains a debug info
3022 // event (just like the previous one).
3023 EXPECT_EQ(2U, mock_server_
->requests().size());
3024 ASSERT_TRUE(mock_server_
->last_request().has_commit());
3025 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
3027 // Generate another commit and try again.
3028 CreateUnsyncedDirectory("Y", "id_Y");
3031 // See that it was received and contains no debug info events.
3032 EXPECT_EQ(3U, mock_server_
->requests().size());
3033 ASSERT_TRUE(mock_server_
->last_request().has_commit());
3034 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
3037 TEST_F(SyncerTest
, HugeConflict
) {
3038 int item_count
= 300; // We should be able to do 300 or 3000 w/o issue.
3040 syncable::Id parent_id
= ids_
.NewServerId();
3041 syncable::Id last_id
= parent_id
;
3042 vector
<syncable::Id
> tree_ids
;
3044 // Create a lot of updates for which the parent does not exist yet.
3045 // Generate a huge deep tree which should all fail to apply at first.
3047 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3048 for (int i
= 0; i
< item_count
; i
++) {
3049 syncable::Id next_id
= ids_
.NewServerId();
3050 syncable::Id local_id
= ids_
.NewLocalId();
3051 tree_ids
.push_back(next_id
);
3052 mock_server_
->AddUpdateDirectory(next_id
, last_id
, "BOB", 2, 20,
3053 foreign_cache_guid(),
3054 local_id
.GetServerId());
3060 // Check they're in the expected conflict state.
3062 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3063 for (int i
= 0; i
< item_count
; i
++) {
3064 Entry
e(&trans
, GET_BY_ID
, tree_ids
[i
]);
3065 // They should all exist but none should be applied.
3066 ASSERT_TRUE(e
.good());
3067 EXPECT_TRUE(e
.GetIsDel());
3068 EXPECT_TRUE(e
.GetIsUnappliedUpdate());
3072 // Add the missing parent directory.
3073 mock_server_
->AddUpdateDirectory(parent_id
, TestIdFactory::root(),
3074 "BOB", 2, 20, foreign_cache_guid(), "-3500");
3077 // Now they should all be OK.
3079 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3080 for (int i
= 0; i
< item_count
; i
++) {
3081 Entry
e(&trans
, GET_BY_ID
, tree_ids
[i
]);
3082 ASSERT_TRUE(e
.good());
3083 EXPECT_FALSE(e
.GetIsDel());
3084 EXPECT_FALSE(e
.GetIsUnappliedUpdate());
3089 TEST_F(SyncerTest
, DontCrashOnCaseChange
) {
3090 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
3091 foreign_cache_guid(), "-1");
3094 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3095 MutableEntry
e(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3096 ASSERT_TRUE(e
.good());
3097 e
.PutIsUnsynced(true);
3099 mock_server_
->set_conflict_all_commits(true);
3100 mock_server_
->AddUpdateDirectory(1, 0, "BOB", 2, 20,
3101 foreign_cache_guid(), "-1");
3102 SyncShareNudge(); // USED TO CAUSE AN ASSERT
3103 saw_syncer_event_
= false;
3106 TEST_F(SyncerTest
, UnsyncedItemAndUpdate
) {
3107 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
3108 foreign_cache_guid(), "-1");
3110 mock_server_
->set_conflict_all_commits(true);
3111 mock_server_
->AddUpdateDirectory(2, 0, "bob", 2, 20,
3112 foreign_cache_guid(), "-2");
3113 SyncShareNudge(); // USED TO CAUSE AN ASSERT
3114 saw_syncer_event_
= false;
3117 TEST_F(SyncerTest
, NewEntryAndAlteredServerEntrySharePath
) {
3118 mock_server_
->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
3119 foreign_cache_guid(), "-1");
3121 int64 local_folder_handle
;
3122 syncable::Id local_folder_id
;
3124 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3125 MutableEntry
new_entry(
3126 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bar.htm");
3127 ASSERT_TRUE(new_entry
.good());
3128 local_folder_id
= new_entry
.GetId();
3129 local_folder_handle
= new_entry
.GetMetahandle();
3130 new_entry
.PutIsUnsynced(true);
3131 new_entry
.PutSpecifics(DefaultBookmarkSpecifics());
3132 MutableEntry
old(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3133 ASSERT_TRUE(old
.good());
3134 WriteTestDataToEntry(&wtrans
, &old
);
3136 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
3137 foreign_cache_guid(), "-1");
3138 mock_server_
->set_conflict_all_commits(true);
3140 saw_syncer_event_
= false;
3142 // Update #20 should have been dropped in favor of the local version.
3143 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3144 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3145 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3146 ASSERT_TRUE(server
.good());
3147 ASSERT_TRUE(local
.good());
3148 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3149 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3150 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3151 EXPECT_TRUE(server
.GetIsUnsynced());
3152 EXPECT_TRUE(local
.GetIsUnsynced());
3153 EXPECT_EQ("Foo.htm", server
.GetNonUniqueName());
3154 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3156 // Allow local changes to commit.
3157 mock_server_
->set_conflict_all_commits(false);
3159 saw_syncer_event_
= false;
3161 // Now add a server change to make the two names equal. There should
3162 // be no conflict with that, since names are not unique.
3163 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
3164 foreign_cache_guid(), "-1");
3166 saw_syncer_event_
= false;
3168 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3169 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3170 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3171 ASSERT_TRUE(server
.good());
3172 ASSERT_TRUE(local
.good());
3173 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3174 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3175 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3176 EXPECT_FALSE(server
.GetIsUnsynced());
3177 EXPECT_FALSE(local
.GetIsUnsynced());
3178 EXPECT_EQ("Bar.htm", server
.GetNonUniqueName());
3179 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3180 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
3181 server
.GetSpecifics().bookmark().url());
3185 // Same as NewEntryAnddServerEntrySharePath, but using the old-style protocol.
3186 TEST_F(SyncerTest
, NewEntryAndAlteredServerEntrySharePath_OldBookmarksProto
) {
3187 mock_server_
->set_use_legacy_bookmarks_protocol(true);
3188 mock_server_
->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
3189 foreign_cache_guid(), "-1");
3191 int64 local_folder_handle
;
3192 syncable::Id local_folder_id
;
3194 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3195 MutableEntry
new_entry(
3196 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bar.htm");
3197 ASSERT_TRUE(new_entry
.good());
3198 local_folder_id
= new_entry
.GetId();
3199 local_folder_handle
= new_entry
.GetMetahandle();
3200 new_entry
.PutIsUnsynced(true);
3201 new_entry
.PutSpecifics(DefaultBookmarkSpecifics());
3202 MutableEntry
old(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3203 ASSERT_TRUE(old
.good());
3204 WriteTestDataToEntry(&wtrans
, &old
);
3206 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
3207 foreign_cache_guid(), "-1");
3208 mock_server_
->set_conflict_all_commits(true);
3210 saw_syncer_event_
= false;
3212 // Update #20 should have been dropped in favor of the local version.
3213 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3214 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3215 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3216 ASSERT_TRUE(server
.good());
3217 ASSERT_TRUE(local
.good());
3218 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3219 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3220 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3221 EXPECT_TRUE(server
.GetIsUnsynced());
3222 EXPECT_TRUE(local
.GetIsUnsynced());
3223 EXPECT_EQ("Foo.htm", server
.GetNonUniqueName());
3224 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3226 // Allow local changes to commit.
3227 mock_server_
->set_conflict_all_commits(false);
3229 saw_syncer_event_
= false;
3231 // Now add a server change to make the two names equal. There should
3232 // be no conflict with that, since names are not unique.
3233 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
3234 foreign_cache_guid(), "-1");
3236 saw_syncer_event_
= false;
3238 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3239 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3240 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3241 ASSERT_TRUE(server
.good());
3242 ASSERT_TRUE(local
.good());
3243 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3244 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3245 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3246 EXPECT_FALSE(server
.GetIsUnsynced());
3247 EXPECT_FALSE(local
.GetIsUnsynced());
3248 EXPECT_EQ("Bar.htm", server
.GetNonUniqueName());
3249 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3250 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
3251 server
.GetSpecifics().bookmark().url());
3255 // Circular links should be resolved by the server.
3256 TEST_F(SyncerTest
, SiblingDirectoriesBecomeCircular
) {
3257 // we don't currently resolve this. This test ensures we don't.
3258 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
3259 foreign_cache_guid(), "-1");
3260 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
3261 foreign_cache_guid(), "-2");
3264 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3265 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3266 ASSERT_TRUE(A
.good());
3267 A
.PutIsUnsynced(true);
3268 A
.PutParentId(ids_
.FromNumber(2));
3269 A
.PutNonUniqueName("B");
3271 mock_server_
->AddUpdateDirectory(2, 1, "A", 20, 20,
3272 foreign_cache_guid(), "-2");
3273 mock_server_
->set_conflict_all_commits(true);
3275 saw_syncer_event_
= false;
3277 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3278 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3279 ASSERT_TRUE(A
.good());
3280 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
3281 ASSERT_TRUE(B
.good());
3282 EXPECT_TRUE(A
.GetNonUniqueName()== "B");
3283 EXPECT_TRUE(B
.GetNonUniqueName()== "B");
3287 TEST_F(SyncerTest
, SwapEntryNames
) {
3288 // Simple transaction test.
3289 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
3290 foreign_cache_guid(), "-1");
3291 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
3292 foreign_cache_guid(), "-2");
3293 mock_server_
->set_conflict_all_commits(true);
3296 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3297 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3298 ASSERT_TRUE(A
.good());
3299 A
.PutIsUnsynced(true);
3300 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
3301 ASSERT_TRUE(B
.good());
3302 B
.PutIsUnsynced(true);
3303 A
.PutNonUniqueName("C");
3304 B
.PutNonUniqueName("A");
3305 A
.PutNonUniqueName("B");
3308 saw_syncer_event_
= false;
3311 TEST_F(SyncerTest
, DualDeletionWithNewItemNameClash
) {
3312 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
3313 foreign_cache_guid(), "-1");
3314 mock_server_
->AddUpdateBookmark(2, 0, "B", 10, 10,
3315 foreign_cache_guid(), "-2");
3316 mock_server_
->set_conflict_all_commits(true);
3319 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3320 MutableEntry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3321 ASSERT_TRUE(B
.good());
3322 WriteTestDataToEntry(&trans
, &B
);
3325 mock_server_
->AddUpdateBookmark(2, 0, "A", 11, 11,
3326 foreign_cache_guid(), "-2");
3327 mock_server_
->SetLastUpdateDeleted();
3330 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3331 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3332 ASSERT_TRUE(B
.good());
3333 EXPECT_FALSE(B
.GetIsUnsynced());
3334 EXPECT_FALSE(B
.GetIsUnappliedUpdate());
3336 saw_syncer_event_
= false;
3339 // When we undelete an entity as a result of conflict resolution, we reuse the
3340 // existing server id and preserve the old version, simply updating the server
3341 // version with the new non-deleted entity.
3342 TEST_F(SyncerTest
, ResolveWeWroteTheyDeleted
) {
3343 int64 bob_metahandle
;
3345 mock_server_
->AddUpdateBookmark(1, 0, "bob", 1, 10,
3346 foreign_cache_guid(), "-1");
3349 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3350 MutableEntry
bob(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3351 ASSERT_TRUE(bob
.good());
3352 bob_metahandle
= bob
.GetMetahandle();
3353 WriteTestDataToEntry(&trans
, &bob
);
3355 mock_server_
->AddUpdateBookmark(1, 0, "bob", 2, 10,
3356 foreign_cache_guid(), "-1");
3357 mock_server_
->SetLastUpdateDeleted();
3358 mock_server_
->set_conflict_all_commits(true);
3362 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3363 Entry
bob(&trans
, GET_BY_HANDLE
, bob_metahandle
);
3364 ASSERT_TRUE(bob
.good());
3365 EXPECT_TRUE(bob
.GetIsUnsynced());
3366 EXPECT_TRUE(bob
.GetId().ServerKnows());
3367 EXPECT_FALSE(bob
.GetIsUnappliedUpdate());
3368 EXPECT_FALSE(bob
.GetIsDel());
3369 EXPECT_EQ(2, bob
.GetServerVersion());
3370 EXPECT_EQ(2, bob
.GetBaseVersion());
3372 saw_syncer_event_
= false;
3375 // This test is to reproduce a check failure. Sometimes we would get a bad ID
3376 // back when creating an entry.
3377 TEST_F(SyncerTest
, DuplicateIDReturn
) {
3379 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3380 MutableEntry
folder(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
3381 ASSERT_TRUE(folder
.good());
3382 folder
.PutIsUnsynced(true);
3383 folder
.PutIsDir(true);
3384 folder
.PutSpecifics(DefaultBookmarkSpecifics());
3385 MutableEntry
folder2(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "fred");
3386 ASSERT_TRUE(folder2
.good());
3387 folder2
.PutIsUnsynced(false);
3388 folder2
.PutIsDir(true);
3389 folder2
.PutSpecifics(DefaultBookmarkSpecifics());
3390 folder2
.PutBaseVersion(3);
3391 folder2
.PutId(syncable::Id::CreateFromServerId("mock_server:10000"));
3393 mock_server_
->set_next_new_id(10000);
3394 EXPECT_EQ(1u, directory()->unsynced_entity_count());
3395 // we get back a bad id in here (should never happen).
3397 EXPECT_EQ(1u, directory()->unsynced_entity_count());
3398 SyncShareNudge(); // another bad id in here.
3399 EXPECT_EQ(0u, directory()->unsynced_entity_count());
3400 saw_syncer_event_
= false;
3403 TEST_F(SyncerTest
, DeletedEntryWithBadParentInLoopCalculation
) {
3404 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
3405 foreign_cache_guid(), "-1");
3408 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3409 MutableEntry
bob(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3410 ASSERT_TRUE(bob
.good());
3411 // This is valid, because the parent could have gone away a long time ago.
3412 bob
.PutParentId(ids_
.FromNumber(54));
3414 bob
.PutIsUnsynced(true);
3416 mock_server_
->AddUpdateDirectory(2, 1, "fred", 1, 10,
3417 foreign_cache_guid(), "-2");
3422 TEST_F(SyncerTest
, ConflictResolverMergesLocalDeleteAndServerUpdate
) {
3423 syncable::Id local_id
;
3425 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3427 MutableEntry
local_deleted(
3428 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "name");
3429 local_id
= local_deleted
.GetId();
3430 local_deleted
.PutId(ids_
.FromNumber(1));
3431 local_deleted
.PutBaseVersion(1);
3432 local_deleted
.PutIsDel(true);
3433 local_deleted
.PutIsDir(false);
3434 local_deleted
.PutIsUnsynced(true);
3435 local_deleted
.PutSpecifics(DefaultBookmarkSpecifics());
3438 mock_server_
->AddUpdateBookmark(ids_
.FromNumber(1), root_id_
, "name", 10, 10,
3440 local_id
.GetServerId());
3442 // We don't care about actually committing, just the resolution.
3443 mock_server_
->set_conflict_all_commits(true);
3447 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3448 Entry
local_deleted(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3449 EXPECT_TRUE(local_deleted
.GetBaseVersion()== 10);
3450 EXPECT_TRUE(local_deleted
.GetIsUnappliedUpdate()== false);
3451 EXPECT_TRUE(local_deleted
.GetIsUnsynced()== true);
3452 EXPECT_TRUE(local_deleted
.GetIsDel()== true);
3453 EXPECT_TRUE(local_deleted
.GetIsDir()== false);
3457 // See what happens if the IS_DIR bit gets flipped. This can cause us
3458 // all kinds of disasters.
3459 TEST_F(SyncerTest
, UpdateFlipsTheFolderBit
) {
3460 // Local object: a deleted directory (container), revision 1, unsynced.
3462 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3464 MutableEntry
local_deleted(
3465 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "name");
3466 local_deleted
.PutId(ids_
.FromNumber(1));
3467 local_deleted
.PutBaseVersion(1);
3468 local_deleted
.PutIsDel(true);
3469 local_deleted
.PutIsDir(true);
3470 local_deleted
.PutIsUnsynced(true);
3471 local_deleted
.PutSpecifics(DefaultBookmarkSpecifics());
3474 // Server update: entry-type object (not a container), revision 10.
3475 mock_server_
->AddUpdateBookmark(ids_
.FromNumber(1), root_id_
, "name", 10, 10,
3477 ids_
.FromNumber(1).GetServerId());
3479 // Don't attempt to commit.
3480 mock_server_
->set_conflict_all_commits(true);
3482 // The syncer should not attempt to apply the invalid update.
3486 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3487 Entry
local_deleted(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3488 EXPECT_TRUE(local_deleted
.GetBaseVersion()== 1);
3489 EXPECT_TRUE(local_deleted
.GetIsUnappliedUpdate()== false);
3490 EXPECT_TRUE(local_deleted
.GetIsUnsynced()== true);
3491 EXPECT_TRUE(local_deleted
.GetIsDel()== true);
3492 EXPECT_TRUE(local_deleted
.GetIsDir()== true);
3497 // Merge conflict resolution will merge a new local entry with another entry
3498 // that needs updates, resulting in CHECK.
3499 TEST_F(SyncerTest
, MergingExistingItems
) {
3500 mock_server_
->set_conflict_all_commits(true);
3501 mock_server_
->AddUpdateBookmark(1, 0, "base", 10, 10,
3502 local_cache_guid(), "-1");
3505 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3507 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "Copy of base");
3508 WriteTestDataToEntry(&trans
, &entry
);
3510 mock_server_
->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
3511 local_cache_guid(), "-1");
3515 // In this test a long changelog contains a child at the start of the changelog
3516 // and a parent at the end. While these updates are in progress the client would
3518 TEST_F(SyncerTest
, LongChangelistWithApplicationConflict
) {
3519 const int depth
= 400;
3520 syncable::Id folder_id
= ids_
.FromNumber(1);
3522 // First we an item in a folder in the root. However the folder won't come
3524 syncable::Id stuck_entry_id
= TestIdFactory::FromNumber(99999);
3525 mock_server_
->AddUpdateDirectory(stuck_entry_id
,
3526 folder_id
, "stuck", 1, 1,
3527 foreign_cache_guid(), "-99999");
3528 mock_server_
->SetChangesRemaining(depth
- 1);
3531 // Buffer up a very long series of downloads.
3532 // We should never be stuck (conflict resolution shouldn't
3533 // kick in so long as we're making forward progress).
3534 for (int i
= 0; i
< depth
; i
++) {
3535 mock_server_
->NextUpdateBatch();
3536 mock_server_
->SetNewTimestamp(i
+ 1);
3537 mock_server_
->SetChangesRemaining(depth
- i
);
3542 // Ensure our folder hasn't somehow applied.
3544 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3545 Entry
child(&trans
, GET_BY_ID
, stuck_entry_id
);
3546 EXPECT_TRUE(child
.good());
3547 EXPECT_TRUE(child
.GetIsUnappliedUpdate());
3548 EXPECT_TRUE(child
.GetIsDel());
3549 EXPECT_FALSE(child
.GetIsUnsynced());
3552 // And finally the folder.
3553 mock_server_
->AddUpdateDirectory(folder_id
,
3554 TestIdFactory::root(), "folder", 1, 1,
3555 foreign_cache_guid(), "-1");
3556 mock_server_
->SetChangesRemaining(0);
3559 // Check that everything is as expected after the commit.
3561 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3562 Entry
entry(&trans
, GET_BY_ID
, folder_id
);
3563 ASSERT_TRUE(entry
.good());
3564 Entry
child(&trans
, GET_BY_ID
, stuck_entry_id
);
3565 EXPECT_EQ(entry
.GetId(), child
.GetParentId());
3566 EXPECT_EQ("stuck", child
.GetNonUniqueName());
3567 EXPECT_TRUE(child
.good());
3571 TEST_F(SyncerTest
, DontMergeTwoExistingItems
) {
3572 mock_server_
->set_conflict_all_commits(true);
3573 mock_server_
->AddUpdateBookmark(1, 0, "base", 10, 10,
3574 foreign_cache_guid(), "-1");
3575 mock_server_
->AddUpdateBookmark(2, 0, "base2", 10, 10,
3576 foreign_cache_guid(), "-2");
3579 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3580 MutableEntry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3581 ASSERT_TRUE(entry
.good());
3582 entry
.PutNonUniqueName("Copy of base");
3583 entry
.PutIsUnsynced(true);
3585 mock_server_
->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
3586 foreign_cache_guid(), "-1");
3589 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3590 Entry
entry1(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3591 EXPECT_FALSE(entry1
.GetIsUnappliedUpdate());
3592 EXPECT_FALSE(entry1
.GetIsUnsynced());
3593 EXPECT_FALSE(entry1
.GetIsDel());
3594 Entry
entry2(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3595 EXPECT_FALSE(entry2
.GetIsUnappliedUpdate());
3596 EXPECT_TRUE(entry2
.GetIsUnsynced());
3597 EXPECT_FALSE(entry2
.GetIsDel());
3598 EXPECT_EQ(entry1
.GetNonUniqueName(), entry2
.GetNonUniqueName());
3602 TEST_F(SyncerTest
, TestUndeleteUpdate
) {
3603 mock_server_
->set_conflict_all_commits(true);
3604 mock_server_
->AddUpdateDirectory(1, 0, "foo", 1, 1,
3605 foreign_cache_guid(), "-1");
3606 mock_server_
->AddUpdateDirectory(2, 1, "bar", 1, 2,
3607 foreign_cache_guid(), "-2");
3609 mock_server_
->AddUpdateDirectory(2, 1, "bar", 2, 3,
3610 foreign_cache_guid(), "-2");
3611 mock_server_
->SetLastUpdateDeleted();
3616 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3617 Entry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3618 ASSERT_TRUE(entry
.good());
3619 EXPECT_TRUE(entry
.GetIsDel());
3620 metahandle
= entry
.GetMetahandle();
3622 mock_server_
->AddUpdateDirectory(1, 0, "foo", 2, 4,
3623 foreign_cache_guid(), "-1");
3624 mock_server_
->SetLastUpdateDeleted();
3626 // This used to be rejected as it's an undeletion. Now, it results in moving
3627 // the delete path aside.
3628 mock_server_
->AddUpdateDirectory(2, 1, "bar", 3, 5,
3629 foreign_cache_guid(), "-2");
3632 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3633 Entry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3634 ASSERT_TRUE(entry
.good());
3635 EXPECT_TRUE(entry
.GetIsDel());
3636 EXPECT_FALSE(entry
.GetServerIsDel());
3637 EXPECT_TRUE(entry
.GetIsUnappliedUpdate());
3638 EXPECT_NE(entry
.GetMetahandle(), metahandle
);
3642 TEST_F(SyncerTest
, TestMoveSanitizedNamedFolder
) {
3643 mock_server_
->AddUpdateDirectory(1, 0, "foo", 1, 1,
3644 foreign_cache_guid(), "-1");
3645 mock_server_
->AddUpdateDirectory(2, 0, ":::", 1, 2,
3646 foreign_cache_guid(), "-2");
3649 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3650 MutableEntry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3651 ASSERT_TRUE(entry
.good());
3652 entry
.PutParentId(ids_
.FromNumber(1));
3653 EXPECT_TRUE(entry
.PutIsUnsynced(true));
3656 // We use the same sync ts as before so our times match up.
3657 mock_server_
->AddUpdateDirectory(2, 1, ":::", 2, 2,
3658 foreign_cache_guid(), "-2");
3662 // Don't crash when this occurs.
3663 TEST_F(SyncerTest
, UpdateWhereParentIsNotAFolder
) {
3664 mock_server_
->AddUpdateBookmark(1, 0, "B", 10, 10,
3665 foreign_cache_guid(), "-1");
3666 mock_server_
->AddUpdateDirectory(2, 1, "BookmarkParent", 10, 10,
3667 foreign_cache_guid(), "-2");
3668 // Used to cause a CHECK
3671 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
3672 Entry
good_entry(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
3673 ASSERT_TRUE(good_entry
.good());
3674 EXPECT_FALSE(good_entry
.GetIsUnappliedUpdate());
3675 Entry
bad_parent(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(2));
3676 ASSERT_TRUE(bad_parent
.good());
3677 EXPECT_TRUE(bad_parent
.GetIsUnappliedUpdate());
3681 TEST_F(SyncerTest
, DirectoryUpdateTest
) {
3682 Id in_root_id
= ids_
.NewServerId();
3683 Id in_in_root_id
= ids_
.NewServerId();
3685 mock_server_
->AddUpdateDirectory(in_root_id
, TestIdFactory::root(),
3686 "in_root_name", 2, 2,
3687 foreign_cache_guid(), "-1");
3688 mock_server_
->AddUpdateDirectory(in_in_root_id
, in_root_id
,
3689 "in_in_root_name", 3, 3,
3690 foreign_cache_guid(), "-2");
3693 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3694 Entry
in_root(&trans
, GET_BY_ID
, in_root_id
);
3695 ASSERT_TRUE(in_root
.good());
3696 EXPECT_EQ("in_root_name", in_root
.GetNonUniqueName());
3697 EXPECT_EQ(TestIdFactory::root(), in_root
.GetParentId());
3699 Entry
in_in_root(&trans
, GET_BY_ID
, in_in_root_id
);
3700 ASSERT_TRUE(in_in_root
.good());
3701 EXPECT_EQ("in_in_root_name", in_in_root
.GetNonUniqueName());
3702 EXPECT_EQ(in_root_id
, in_in_root
.GetParentId());
3706 TEST_F(SyncerTest
, DirectoryCommitTest
) {
3707 syncable::Id in_root_id
, in_dir_id
;
3708 int64 foo_metahandle
;
3709 int64 bar_metahandle
;
3712 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3713 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "foo");
3714 ASSERT_TRUE(parent
.good());
3715 parent
.PutIsUnsynced(true);
3716 parent
.PutIsDir(true);
3717 parent
.PutSpecifics(DefaultBookmarkSpecifics());
3718 in_root_id
= parent
.GetId();
3719 foo_metahandle
= parent
.GetMetahandle();
3721 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent
.GetId(), "bar");
3722 ASSERT_TRUE(child
.good());
3723 child
.PutIsUnsynced(true);
3724 child
.PutIsDir(true);
3725 child
.PutSpecifics(DefaultBookmarkSpecifics());
3726 bar_metahandle
= child
.GetMetahandle();
3727 in_dir_id
= parent
.GetId();
3731 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3732 Entry
fail_by_old_id_entry(&trans
, GET_BY_ID
, in_root_id
);
3733 ASSERT_FALSE(fail_by_old_id_entry
.good());
3735 Entry
foo_entry(&trans
, GET_BY_HANDLE
, foo_metahandle
);
3736 ASSERT_TRUE(foo_entry
.good());
3737 EXPECT_EQ("foo", foo_entry
.GetNonUniqueName());
3738 EXPECT_NE(foo_entry
.GetId(), in_root_id
);
3740 Entry
bar_entry(&trans
, GET_BY_HANDLE
, bar_metahandle
);
3741 ASSERT_TRUE(bar_entry
.good());
3742 EXPECT_EQ("bar", bar_entry
.GetNonUniqueName());
3743 EXPECT_NE(bar_entry
.GetId(), in_dir_id
);
3744 EXPECT_EQ(foo_entry
.GetId(), bar_entry
.GetParentId());
3748 TEST_F(SyncerTest
, TestClientCommandDuringUpdate
) {
3749 using sync_pb::ClientCommand
;
3751 ClientCommand
* command
= new ClientCommand();
3752 command
->set_set_sync_poll_interval(8);
3753 command
->set_set_sync_long_poll_interval(800);
3754 command
->set_sessions_commit_delay_seconds(3141);
3755 sync_pb::CustomNudgeDelay
* bookmark_delay
=
3756 command
->add_custom_nudge_delays();
3757 bookmark_delay
->set_datatype_id(
3758 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3759 bookmark_delay
->set_delay_ms(950);
3760 command
->set_client_invalidation_hint_buffer_size(11);
3761 mock_server_
->AddUpdateDirectory(1, 0, "in_root", 1, 1,
3762 foreign_cache_guid(), "-1");
3763 mock_server_
->SetGUClientCommand(command
);
3766 EXPECT_EQ(TimeDelta::FromSeconds(8), last_short_poll_interval_received_
);
3767 EXPECT_EQ(TimeDelta::FromSeconds(800), last_long_poll_interval_received_
);
3768 EXPECT_EQ(TimeDelta::FromSeconds(3141), last_sessions_commit_delay_
);
3769 EXPECT_EQ(TimeDelta::FromMilliseconds(950), last_bookmarks_commit_delay_
);
3770 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_
);
3772 command
= new ClientCommand();
3773 command
->set_set_sync_poll_interval(180);
3774 command
->set_set_sync_long_poll_interval(190);
3775 command
->set_sessions_commit_delay_seconds(2718);
3776 bookmark_delay
= command
->add_custom_nudge_delays();
3777 bookmark_delay
->set_datatype_id(
3778 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3779 bookmark_delay
->set_delay_ms(1050);
3780 command
->set_client_invalidation_hint_buffer_size(9);
3781 mock_server_
->AddUpdateDirectory(
3782 1, 0, "in_root", 1, 1, foreign_cache_guid(), "-1");
3783 mock_server_
->SetGUClientCommand(command
);
3786 EXPECT_EQ(TimeDelta::FromSeconds(180), last_short_poll_interval_received_
);
3787 EXPECT_EQ(TimeDelta::FromSeconds(190), last_long_poll_interval_received_
);
3788 EXPECT_EQ(TimeDelta::FromSeconds(2718), last_sessions_commit_delay_
);
3789 EXPECT_EQ(TimeDelta::FromMilliseconds(1050), last_bookmarks_commit_delay_
);
3790 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_
);
3793 TEST_F(SyncerTest
, TestClientCommandDuringCommit
) {
3794 using sync_pb::ClientCommand
;
3796 ClientCommand
* command
= new ClientCommand();
3797 command
->set_set_sync_poll_interval(8);
3798 command
->set_set_sync_long_poll_interval(800);
3799 command
->set_sessions_commit_delay_seconds(3141);
3800 sync_pb::CustomNudgeDelay
* bookmark_delay
=
3801 command
->add_custom_nudge_delays();
3802 bookmark_delay
->set_datatype_id(
3803 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3804 bookmark_delay
->set_delay_ms(950);
3805 command
->set_client_invalidation_hint_buffer_size(11);
3806 CreateUnsyncedDirectory("X", "id_X");
3807 mock_server_
->SetCommitClientCommand(command
);
3810 EXPECT_EQ(TimeDelta::FromSeconds(8), last_short_poll_interval_received_
);
3811 EXPECT_EQ(TimeDelta::FromSeconds(800), last_long_poll_interval_received_
);
3812 EXPECT_EQ(TimeDelta::FromSeconds(3141), last_sessions_commit_delay_
);
3813 EXPECT_EQ(TimeDelta::FromMilliseconds(950), last_bookmarks_commit_delay_
);
3814 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_
);
3816 command
= new ClientCommand();
3817 command
->set_set_sync_poll_interval(180);
3818 command
->set_set_sync_long_poll_interval(190);
3819 command
->set_sessions_commit_delay_seconds(2718);
3820 bookmark_delay
= command
->add_custom_nudge_delays();
3821 bookmark_delay
->set_datatype_id(
3822 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3823 bookmark_delay
->set_delay_ms(1050);
3824 command
->set_client_invalidation_hint_buffer_size(9);
3825 CreateUnsyncedDirectory("Y", "id_Y");
3826 mock_server_
->SetCommitClientCommand(command
);
3829 EXPECT_EQ(TimeDelta::FromSeconds(180), last_short_poll_interval_received_
);
3830 EXPECT_EQ(TimeDelta::FromSeconds(190), last_long_poll_interval_received_
);
3831 EXPECT_EQ(TimeDelta::FromSeconds(2718), last_sessions_commit_delay_
);
3832 EXPECT_EQ(TimeDelta::FromMilliseconds(1050), last_bookmarks_commit_delay_
);
3833 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_
);
3836 TEST_F(SyncerTest
, EnsureWeSendUpOldParent
) {
3837 syncable::Id folder_one_id
= ids_
.FromNumber(1);
3838 syncable::Id folder_two_id
= ids_
.FromNumber(2);
3840 mock_server_
->AddUpdateDirectory(folder_one_id
, TestIdFactory::root(),
3841 "folder_one", 1, 1, foreign_cache_guid(), "-1");
3842 mock_server_
->AddUpdateDirectory(folder_two_id
, TestIdFactory::root(),
3843 "folder_two", 1, 1, foreign_cache_guid(), "-2");
3846 // A moved entry should send an "old parent."
3847 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3848 MutableEntry
entry(&trans
, GET_BY_ID
, folder_one_id
);
3849 ASSERT_TRUE(entry
.good());
3850 entry
.PutParentId(folder_two_id
);
3851 entry
.PutIsUnsynced(true);
3852 // A new entry should send no "old parent."
3853 MutableEntry
create(
3854 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_folder");
3855 create
.PutIsUnsynced(true);
3856 create
.PutSpecifics(DefaultBookmarkSpecifics());
3859 const sync_pb::CommitMessage
& commit
= mock_server_
->last_sent_commit();
3860 ASSERT_EQ(2, commit
.entries_size());
3861 EXPECT_TRUE(commit
.entries(0).parent_id_string() == "2");
3862 EXPECT_TRUE(commit
.entries(0).old_parent_id() == "0");
3863 EXPECT_FALSE(commit
.entries(1).has_old_parent_id());
3866 TEST_F(SyncerTest
, Test64BitVersionSupport
) {
3867 int64 really_big_int
= std::numeric_limits
<int64
>::max() - 12;
3868 const string
name("ringo's dang orang ran rings around my o-ring");
3869 int64 item_metahandle
;
3871 // Try writing max int64 to the version fields of a meta entry.
3873 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3874 MutableEntry
entry(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), name
);
3875 ASSERT_TRUE(entry
.good());
3876 entry
.PutBaseVersion(really_big_int
);
3877 entry
.PutServerVersion(really_big_int
);
3878 entry
.PutId(ids_
.NewServerId());
3879 item_metahandle
= entry
.GetMetahandle();
3881 // Now read it back out and make sure the value is max int64.
3882 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
3883 Entry
entry(&rtrans
, syncable::GET_BY_HANDLE
, item_metahandle
);
3884 ASSERT_TRUE(entry
.good());
3885 EXPECT_TRUE(really_big_int
== entry
.GetBaseVersion());
3888 TEST_F(SyncerTest
, TestSimpleUndelete
) {
3889 Id id
= ids_
.MakeServer("undeletion item"), root
= TestIdFactory::root();
3890 mock_server_
->set_conflict_all_commits(true);
3891 // Let there be an entry from the server.
3892 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 1, 10,
3893 foreign_cache_guid(), "-1");
3895 // Check it out and delete it.
3897 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3898 MutableEntry
entry(&wtrans
, GET_BY_ID
, id
);
3899 ASSERT_TRUE(entry
.good());
3900 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3901 EXPECT_FALSE(entry
.GetIsUnsynced());
3902 EXPECT_FALSE(entry
.GetIsDel());
3903 // Delete it locally.
3904 entry
.PutIsDel(true);
3907 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3909 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3910 Entry
entry(&trans
, GET_BY_ID
, id
);
3911 ASSERT_TRUE(entry
.good());
3912 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3913 EXPECT_FALSE(entry
.GetIsUnsynced());
3914 EXPECT_TRUE(entry
.GetIsDel());
3915 EXPECT_FALSE(entry
.GetServerIsDel());
3918 // Update from server confirming deletion.
3919 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 11,
3920 foreign_cache_guid(), "-1");
3921 mock_server_
->SetLastUpdateDeleted();
3923 // IS_DEL AND SERVER_IS_DEL now both true.
3925 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3926 Entry
entry(&trans
, GET_BY_ID
, id
);
3927 ASSERT_TRUE(entry
.good());
3928 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3929 EXPECT_FALSE(entry
.GetIsUnsynced());
3930 EXPECT_TRUE(entry
.GetIsDel());
3931 EXPECT_TRUE(entry
.GetServerIsDel());
3933 // Undelete from server.
3934 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 12,
3935 foreign_cache_guid(), "-1");
3937 // IS_DEL and SERVER_IS_DEL now both false.
3939 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3940 Entry
entry(&trans
, GET_BY_ID
, id
);
3941 ASSERT_TRUE(entry
.good());
3942 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3943 EXPECT_FALSE(entry
.GetIsUnsynced());
3944 EXPECT_FALSE(entry
.GetIsDel());
3945 EXPECT_FALSE(entry
.GetServerIsDel());
3949 TEST_F(SyncerTest
, TestUndeleteWithMissingDeleteUpdate
) {
3950 Id id
= ids_
.MakeServer("undeletion item"), root
= TestIdFactory::root();
3951 // Let there be a entry, from the server.
3952 mock_server_
->set_conflict_all_commits(true);
3953 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 1, 10,
3954 foreign_cache_guid(), "-1");
3956 // Check it out and delete it.
3958 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3959 MutableEntry
entry(&wtrans
, GET_BY_ID
, id
);
3960 ASSERT_TRUE(entry
.good());
3961 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3962 EXPECT_FALSE(entry
.GetIsUnsynced());
3963 EXPECT_FALSE(entry
.GetIsDel());
3964 // Delete it locally.
3965 entry
.PutIsDel(true);
3968 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3970 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3971 Entry
entry(&trans
, GET_BY_ID
, id
);
3972 ASSERT_TRUE(entry
.good());
3973 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3974 EXPECT_FALSE(entry
.GetIsUnsynced());
3975 EXPECT_TRUE(entry
.GetIsDel());
3976 EXPECT_FALSE(entry
.GetServerIsDel());
3979 // Say we do not get an update from server confirming deletion. Undelete
3981 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 12,
3982 foreign_cache_guid(), "-1");
3984 // IS_DEL and SERVER_IS_DEL now both false.
3986 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3987 Entry
entry(&trans
, GET_BY_ID
, id
);
3988 ASSERT_TRUE(entry
.good());
3989 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3990 EXPECT_FALSE(entry
.GetIsUnsynced());
3991 EXPECT_FALSE(entry
.GetIsDel());
3992 EXPECT_FALSE(entry
.GetServerIsDel());
3996 TEST_F(SyncerTest
, TestUndeleteIgnoreCorrectlyUnappliedUpdate
) {
3997 Id id1
= ids_
.MakeServer("first"), id2
= ids_
.MakeServer("second");
3998 Id root
= TestIdFactory::root();
3999 // Duplicate! expect path clashing!
4000 mock_server_
->set_conflict_all_commits(true);
4001 mock_server_
->AddUpdateBookmark(id1
, root
, "foo", 1, 10,
4002 foreign_cache_guid(), "-1");
4003 mock_server_
->AddUpdateBookmark(id2
, root
, "foo", 1, 10,
4004 foreign_cache_guid(), "-2");
4006 mock_server_
->AddUpdateBookmark(id2
, root
, "foo2", 2, 20,
4007 foreign_cache_guid(), "-2");
4008 SyncShareNudge(); // Now just don't explode.
4011 TEST_F(SyncerTest
, ClientTagServerCreatedUpdatesWork
) {
4012 mock_server_
->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
4013 foreign_cache_guid(), "-1");
4014 mock_server_
->SetLastUpdateClientTag("permfolder");
4019 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4020 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
4021 ASSERT_TRUE(perm_folder
.good());
4022 EXPECT_FALSE(perm_folder
.GetIsDel());
4023 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
4024 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
4025 EXPECT_EQ(perm_folder
.GetUniqueClientTag(), "permfolder");
4026 EXPECT_EQ(perm_folder
.GetNonUniqueName(), "permitem1");
4029 mock_server_
->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
4030 foreign_cache_guid(), "-1");
4031 mock_server_
->SetLastUpdateClientTag("permfolder");
4035 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4037 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
4038 ASSERT_TRUE(perm_folder
.good());
4039 EXPECT_FALSE(perm_folder
.GetIsDel());
4040 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
4041 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
4042 EXPECT_EQ(perm_folder
.GetUniqueClientTag(), "permfolder");
4043 EXPECT_EQ(perm_folder
.GetNonUniqueName(), "permitem_renamed");
4047 TEST_F(SyncerTest
, ClientTagIllegalUpdateIgnored
) {
4048 mock_server_
->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
4049 foreign_cache_guid(), "-1");
4050 mock_server_
->SetLastUpdateClientTag("permfolder");
4055 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4056 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
4057 ASSERT_TRUE(perm_folder
.good());
4058 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
4059 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
4060 EXPECT_EQ(perm_folder
.GetUniqueClientTag(), "permfolder");
4061 EXPECT_TRUE(perm_folder
.GetNonUniqueName()== "permitem1");
4062 EXPECT_TRUE(perm_folder
.GetId().ServerKnows());
4065 mock_server_
->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
4066 foreign_cache_guid(), "-1");
4067 mock_server_
->SetLastUpdateClientTag("wrongtag");
4071 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4073 // This update is rejected because it has the same ID, but a
4074 // different tag than one that is already on the client.
4075 // The client has a ServerKnows ID, which cannot be overwritten.
4076 Entry
rejected_update(&trans
, GET_BY_CLIENT_TAG
, "wrongtag");
4077 EXPECT_FALSE(rejected_update
.good());
4079 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
4080 ASSERT_TRUE(perm_folder
.good());
4081 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
4082 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
4083 EXPECT_EQ(perm_folder
.GetNonUniqueName(), "permitem1");
4087 TEST_F(SyncerTest
, ClientTagUncommittedTagMatchesUpdate
) {
4088 int64 original_metahandle
= 0;
4091 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4093 &trans
, CREATE
, PREFERENCES
, ids_
.root(), "name");
4094 ASSERT_TRUE(pref
.good());
4095 pref
.PutUniqueClientTag("tag");
4096 pref
.PutIsUnsynced(true);
4097 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
4098 EXPECT_FALSE(pref
.GetId().ServerKnows());
4099 original_metahandle
= pref
.GetMetahandle();
4102 syncable::Id server_id
= TestIdFactory::MakeServer("id");
4103 mock_server_
->AddUpdatePref(server_id
.GetServerId(),
4104 ids_
.root().GetServerId(),
4106 mock_server_
->set_conflict_all_commits(true);
4109 // This should cause client tag reunion, preserving the metahandle.
4111 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4113 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
4114 ASSERT_TRUE(pref
.good());
4115 EXPECT_FALSE(pref
.GetIsDel());
4116 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
4117 EXPECT_TRUE(pref
.GetIsUnsynced());
4118 EXPECT_EQ(10, pref
.GetBaseVersion());
4119 // Entry should have been given the new ID while preserving the
4120 // metahandle; client should have won the conflict resolution.
4121 EXPECT_EQ(original_metahandle
, pref
.GetMetahandle());
4122 EXPECT_EQ("tag", pref
.GetUniqueClientTag());
4123 EXPECT_TRUE(pref
.GetId().ServerKnows());
4126 mock_server_
->set_conflict_all_commits(false);
4129 // The resolved entry ought to commit cleanly.
4131 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4133 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
4134 ASSERT_TRUE(pref
.good());
4135 EXPECT_FALSE(pref
.GetIsDel());
4136 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
4137 EXPECT_FALSE(pref
.GetIsUnsynced());
4138 EXPECT_TRUE(10 < pref
.GetBaseVersion());
4139 // Entry should have been given the new ID while preserving the
4140 // metahandle; client should have won the conflict resolution.
4141 EXPECT_EQ(original_metahandle
, pref
.GetMetahandle());
4142 EXPECT_EQ("tag", pref
.GetUniqueClientTag());
4143 EXPECT_TRUE(pref
.GetId().ServerKnows());
4147 TEST_F(SyncerTest
, ClientTagConflictWithDeletedLocalEntry
) {
4149 // Create a deleted local entry with a unique client tag.
4150 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4152 &trans
, CREATE
, PREFERENCES
, ids_
.root(), "name");
4153 ASSERT_TRUE(pref
.good());
4154 ASSERT_FALSE(pref
.GetId().ServerKnows());
4155 pref
.PutUniqueClientTag("tag");
4156 pref
.PutIsUnsynced(true);
4158 // Note: IS_DEL && !ServerKnows() will clear the UNSYNCED bit.
4159 // (We never attempt to commit server-unknown deleted items, so this
4160 // helps us clean up those entries).
4161 pref
.PutIsDel(true);
4164 // Prepare an update with the same unique client tag.
4165 syncable::Id server_id
= TestIdFactory::MakeServer("id");
4166 mock_server_
->AddUpdatePref(server_id
.GetServerId(),
4167 ids_
.root().GetServerId(),
4171 // The local entry will be overwritten.
4173 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4175 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
4176 ASSERT_TRUE(pref
.good());
4177 ASSERT_TRUE(pref
.GetId().ServerKnows());
4178 EXPECT_FALSE(pref
.GetIsDel());
4179 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
4180 EXPECT_FALSE(pref
.GetIsUnsynced());
4181 EXPECT_EQ(pref
.GetBaseVersion(), 10);
4182 EXPECT_EQ(pref
.GetUniqueClientTag(), "tag");
4186 TEST_F(SyncerTest
, ClientTagUpdateClashesWithLocalEntry
) {
4187 // This test is written assuming that ID comparison
4188 // will work out in a particular way.
4189 EXPECT_TRUE(ids_
.FromNumber(1) < ids_
.FromNumber(2));
4190 EXPECT_TRUE(ids_
.FromNumber(3) < ids_
.FromNumber(4));
4192 syncable::Id id1
= TestIdFactory::MakeServer("1");
4193 mock_server_
->AddUpdatePref(id1
.GetServerId(), "", "tag1", 10, 100);
4195 syncable::Id id4
= TestIdFactory::MakeServer("4");
4196 mock_server_
->AddUpdatePref(id4
.GetServerId(), "", "tag2", 11, 110);
4198 mock_server_
->set_conflict_all_commits(true);
4201 int64 tag1_metahandle
= syncable::kInvalidMetaHandle
;
4202 int64 tag2_metahandle
= syncable::kInvalidMetaHandle
;
4203 // This should cause client tag overwrite.
4205 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4207 Entry
tag1(&trans
, GET_BY_CLIENT_TAG
, "tag1");
4208 ASSERT_TRUE(tag1
.good());
4209 ASSERT_TRUE(tag1
.GetId().ServerKnows());
4210 ASSERT_TRUE(id1
== tag1
.GetId());
4211 EXPECT_FALSE(tag1
.GetIsDel());
4212 EXPECT_FALSE(tag1
.GetIsUnappliedUpdate());
4213 EXPECT_FALSE(tag1
.GetIsUnsynced());
4214 EXPECT_EQ(10, tag1
.GetBaseVersion());
4215 EXPECT_EQ("tag1", tag1
.GetUniqueClientTag());
4216 tag1_metahandle
= tag1
.GetMetahandle();
4218 Entry
tag2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
4219 ASSERT_TRUE(tag2
.good());
4220 ASSERT_TRUE(tag2
.GetId().ServerKnows());
4221 ASSERT_TRUE(id4
== tag2
.GetId());
4222 EXPECT_FALSE(tag2
.GetIsDel());
4223 EXPECT_FALSE(tag2
.GetIsUnappliedUpdate());
4224 EXPECT_FALSE(tag2
.GetIsUnsynced());
4225 EXPECT_EQ(11, tag2
.GetBaseVersion());
4226 EXPECT_EQ("tag2", tag2
.GetUniqueClientTag());
4227 tag2_metahandle
= tag2
.GetMetahandle();
4229 // Preferences type root should have been created by the updates above.
4230 Entry
pref_root(&trans
, GET_TYPE_ROOT
, PREFERENCES
);
4231 ASSERT_TRUE(pref_root
.good());
4233 syncable::Directory::Metahandles children
;
4234 directory()->GetChildHandlesById(&trans
, pref_root
.GetId(), &children
);
4235 ASSERT_EQ(2U, children
.size());
4238 syncable::Id id2
= TestIdFactory::MakeServer("2");
4239 mock_server_
->AddUpdatePref(id2
.GetServerId(), "", "tag1", 12, 120);
4240 syncable::Id id3
= TestIdFactory::MakeServer("3");
4241 mock_server_
->AddUpdatePref(id3
.GetServerId(), "", "tag2", 13, 130);
4245 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4247 Entry
tag1(&trans
, GET_BY_CLIENT_TAG
, "tag1");
4248 ASSERT_TRUE(tag1
.good());
4249 ASSERT_TRUE(tag1
.GetId().ServerKnows());
4250 ASSERT_EQ(id1
, tag1
.GetId())
4251 << "ID 1 should be kept, since it was less than ID 2.";
4252 EXPECT_FALSE(tag1
.GetIsDel());
4253 EXPECT_FALSE(tag1
.GetIsUnappliedUpdate());
4254 EXPECT_FALSE(tag1
.GetIsUnsynced());
4255 EXPECT_EQ(10, tag1
.GetBaseVersion());
4256 EXPECT_EQ("tag1", tag1
.GetUniqueClientTag());
4257 EXPECT_EQ(tag1_metahandle
, tag1
.GetMetahandle());
4259 Entry
tag2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
4260 ASSERT_TRUE(tag2
.good());
4261 ASSERT_TRUE(tag2
.GetId().ServerKnows());
4262 ASSERT_EQ(id3
, tag2
.GetId())
4263 << "ID 3 should be kept, since it was less than ID 4.";
4264 EXPECT_FALSE(tag2
.GetIsDel());
4265 EXPECT_FALSE(tag2
.GetIsUnappliedUpdate());
4266 EXPECT_FALSE(tag2
.GetIsUnsynced());
4267 EXPECT_EQ(13, tag2
.GetBaseVersion());
4268 EXPECT_EQ("tag2", tag2
.GetUniqueClientTag());
4269 EXPECT_EQ(tag2_metahandle
, tag2
.GetMetahandle());
4271 // Preferences type root should have been created by the updates above.
4272 Entry
pref_root(&trans
, GET_TYPE_ROOT
, PREFERENCES
);
4273 ASSERT_TRUE(pref_root
.good());
4275 syncable::Directory::Metahandles children
;
4276 directory()->GetChildHandlesById(&trans
, pref_root
.GetId(), &children
);
4277 ASSERT_EQ(2U, children
.size());
4281 TEST_F(SyncerTest
, ClientTagClashWithinBatchOfUpdates
) {
4282 // This test is written assuming that ID comparison
4283 // will work out in a particular way.
4284 EXPECT_TRUE(ids_
.FromNumber(1) < ids_
.FromNumber(4));
4285 EXPECT_TRUE(ids_
.FromNumber(201) < ids_
.FromNumber(205));
4287 // Least ID: winner.
4288 mock_server_
->AddUpdatePref(ids_
.FromNumber(1).GetServerId(), "", "tag a", 1,
4290 mock_server_
->AddUpdatePref(ids_
.FromNumber(2).GetServerId(), "", "tag a", 11,
4292 mock_server_
->AddUpdatePref(ids_
.FromNumber(3).GetServerId(), "", "tag a", 12,
4294 mock_server_
->AddUpdatePref(ids_
.FromNumber(4).GetServerId(), "", "tag a", 13,
4296 mock_server_
->AddUpdatePref(ids_
.FromNumber(105).GetServerId(), "", "tag b",
4298 mock_server_
->AddUpdatePref(ids_
.FromNumber(102).GetServerId(), "", "tag b",
4300 // Least ID: winner.
4301 mock_server_
->AddUpdatePref(ids_
.FromNumber(101).GetServerId(), "", "tag b",
4303 mock_server_
->AddUpdatePref(ids_
.FromNumber(104).GetServerId(), "", "tag b",
4306 mock_server_
->AddUpdatePref(ids_
.FromNumber(205).GetServerId(), "", "tag c",
4308 mock_server_
->AddUpdatePref(ids_
.FromNumber(202).GetServerId(), "", "tag c",
4310 mock_server_
->AddUpdatePref(ids_
.FromNumber(204).GetServerId(), "", "tag c",
4312 // Least ID: winner.
4313 mock_server_
->AddUpdatePref(ids_
.FromNumber(201).GetServerId(), "", "tag c",
4316 mock_server_
->set_conflict_all_commits(true);
4319 // This should cause client tag overwrite.
4321 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4323 Entry
tag_a(&trans
, GET_BY_CLIENT_TAG
, "tag a");
4324 ASSERT_TRUE(tag_a
.good());
4325 EXPECT_TRUE(tag_a
.GetId().ServerKnows());
4326 EXPECT_EQ(ids_
.FromNumber(1), tag_a
.GetId());
4327 EXPECT_FALSE(tag_a
.GetIsDel());
4328 EXPECT_FALSE(tag_a
.GetIsUnappliedUpdate());
4329 EXPECT_FALSE(tag_a
.GetIsUnsynced());
4330 EXPECT_EQ(1, tag_a
.GetBaseVersion());
4331 EXPECT_EQ("tag a", tag_a
.GetUniqueClientTag());
4333 Entry
tag_b(&trans
, GET_BY_CLIENT_TAG
, "tag b");
4334 ASSERT_TRUE(tag_b
.good());
4335 EXPECT_TRUE(tag_b
.GetId().ServerKnows());
4336 EXPECT_EQ(ids_
.FromNumber(101), tag_b
.GetId());
4337 EXPECT_FALSE(tag_b
.GetIsDel());
4338 EXPECT_FALSE(tag_b
.GetIsUnappliedUpdate());
4339 EXPECT_FALSE(tag_b
.GetIsUnsynced());
4340 EXPECT_EQ(16, tag_b
.GetBaseVersion());
4341 EXPECT_EQ("tag b", tag_b
.GetUniqueClientTag());
4343 Entry
tag_c(&trans
, GET_BY_CLIENT_TAG
, "tag c");
4344 ASSERT_TRUE(tag_c
.good());
4345 EXPECT_TRUE(tag_c
.GetId().ServerKnows());
4346 EXPECT_EQ(ids_
.FromNumber(201), tag_c
.GetId());
4347 EXPECT_FALSE(tag_c
.GetIsDel());
4348 EXPECT_FALSE(tag_c
.GetIsUnappliedUpdate());
4349 EXPECT_FALSE(tag_c
.GetIsUnsynced());
4350 EXPECT_EQ(21, tag_c
.GetBaseVersion());
4351 EXPECT_EQ("tag c", tag_c
.GetUniqueClientTag());
4353 // Preferences type root should have been created by the updates above.
4354 Entry
pref_root(&trans
, GET_TYPE_ROOT
, PREFERENCES
);
4355 ASSERT_TRUE(pref_root
.good());
4357 // Verify that we have exactly 3 tagged nodes under the type root.
4358 syncable::Directory::Metahandles children
;
4359 directory()->GetChildHandlesById(&trans
, pref_root
.GetId(), &children
);
4360 ASSERT_EQ(3U, children
.size());
4364 // This verifies transition to implicit permanent folders.
4365 TEST_F(SyncerTest
, EntryWithParentIdUpdatedWithEntryWithoutParentId
) {
4366 // Make sure SPECIFICS root exists so that we can get its parent ID.
4367 mock_server_
->AddUpdateSpecifics(1, 0, "Folder", 10, 10, true, 1,
4368 DefaultPreferencesSpecifics());
4369 mock_server_
->SetLastUpdateServerTag(ModelTypeToRootTag(PREFERENCES
));
4374 // Preferences type root should have been created by the update above.
4375 // We need it in order to get its ID.
4376 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4377 Entry
pref_root(&trans
, GET_TYPE_ROOT
, PREFERENCES
);
4378 ASSERT_TRUE(pref_root
.good());
4379 pref_root_id
= pref_root
.GetId();
4382 // Add a preference item with explicit parent ID.
4383 mock_server_
->AddUpdatePref(ids_
.FromNumber(2).GetServerId(),
4384 ids_
.FromNumber(1).GetServerId(), "tag", 1, 10);
4389 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4390 Entry
pref_entry(&trans
, GET_BY_CLIENT_TAG
, "tag");
4391 ASSERT_TRUE(pref_entry
.good());
4392 ASSERT_EQ(pref_root_id
, pref_entry
.GetParentId());
4395 // Make another update where the same item get updated, this time
4396 // with implicit parent ID.
4397 mock_server_
->AddUpdatePref(ids_
.FromNumber(2).GetServerId(), "", "tag", 2,
4403 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4404 Entry
pref_entry(&trans
, GET_BY_CLIENT_TAG
, "tag");
4405 ASSERT_TRUE(pref_entry
.good());
4406 ASSERT_TRUE(pref_entry
.GetParentId().IsNull());
4408 // Verify that there is still one node under the type root.
4409 syncable::Directory::Metahandles children
;
4410 directory()->GetChildHandlesById(&trans
, pref_root_id
, &children
);
4411 ASSERT_EQ(1U, children
.size());
4415 TEST_F(SyncerTest
, UniqueServerTagUpdates
) {
4416 // As a hurdle, introduce an item whose name is the same as the tag value
4418 int64 hurdle_handle
= CreateUnsyncedDirectory("bob", "id_bob");
4420 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4421 Entry
hurdle(&trans
, GET_BY_HANDLE
, hurdle_handle
);
4422 ASSERT_TRUE(hurdle
.good());
4423 ASSERT_TRUE(!hurdle
.GetIsDel());
4424 ASSERT_TRUE(hurdle
.GetUniqueServerTag().empty());
4425 ASSERT_TRUE(hurdle
.GetNonUniqueName()== "bob");
4427 // Try to lookup by the tagname. These should fail.
4428 Entry
tag_alpha(&trans
, GET_BY_SERVER_TAG
, "alpha");
4429 EXPECT_FALSE(tag_alpha
.good());
4430 Entry
tag_bob(&trans
, GET_BY_SERVER_TAG
, "bob");
4431 EXPECT_FALSE(tag_bob
.good());
4434 // Now download some tagged items as updates.
4435 mock_server_
->AddUpdateDirectory(
4436 1, 0, "update1", 1, 10, std::string(), std::string());
4437 mock_server_
->SetLastUpdateServerTag("alpha");
4438 mock_server_
->AddUpdateDirectory(
4439 2, 0, "update2", 2, 20, std::string(), std::string());
4440 mock_server_
->SetLastUpdateServerTag("bob");
4444 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4446 // The new items should be applied as new entries, and we should be able
4447 // to look them up by their tag values.
4448 Entry
tag_alpha(&trans
, GET_BY_SERVER_TAG
, "alpha");
4449 ASSERT_TRUE(tag_alpha
.good());
4450 ASSERT_TRUE(!tag_alpha
.GetIsDel());
4451 ASSERT_TRUE(tag_alpha
.GetUniqueServerTag()== "alpha");
4452 ASSERT_TRUE(tag_alpha
.GetNonUniqueName()== "update1");
4453 Entry
tag_bob(&trans
, GET_BY_SERVER_TAG
, "bob");
4454 ASSERT_TRUE(tag_bob
.good());
4455 ASSERT_TRUE(!tag_bob
.GetIsDel());
4456 ASSERT_TRUE(tag_bob
.GetUniqueServerTag()== "bob");
4457 ASSERT_TRUE(tag_bob
.GetNonUniqueName()== "update2");
4458 // The old item should be unchanged.
4459 Entry
hurdle(&trans
, GET_BY_HANDLE
, hurdle_handle
);
4460 ASSERT_TRUE(hurdle
.good());
4461 ASSERT_TRUE(!hurdle
.GetIsDel());
4462 ASSERT_TRUE(hurdle
.GetUniqueServerTag().empty());
4463 ASSERT_TRUE(hurdle
.GetNonUniqueName()== "bob");
4467 TEST_F(SyncerTest
, GetUpdatesSetsRequestedTypes
) {
4468 // The expectations of this test happen in the MockConnectionManager's
4469 // GetUpdates handler. EnableDatatype sets the expectation value from our
4470 // set of enabled/disabled datatypes.
4471 EnableDatatype(BOOKMARKS
);
4473 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4475 EnableDatatype(AUTOFILL
);
4477 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4479 EnableDatatype(PREFERENCES
);
4481 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4483 DisableDatatype(BOOKMARKS
);
4485 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4487 DisableDatatype(AUTOFILL
);
4489 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4491 DisableDatatype(PREFERENCES
);
4492 EnableDatatype(AUTOFILL
);
4494 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4497 // A typical scenario: server and client each have one update for the other.
4498 // This is the "happy path" alternative to UpdateFailsThenDontCommit.
4499 TEST_F(SyncerTest
, UpdateThenCommit
) {
4500 syncable::Id to_receive
= ids_
.NewServerId();
4501 syncable::Id to_commit
= ids_
.NewLocalId();
4503 mock_server_
->AddUpdateDirectory(to_receive
, ids_
.root(), "x", 1, 10,
4504 foreign_cache_guid(), "-1");
4505 int64 commit_handle
= CreateUnsyncedDirectory("y", to_commit
);
4508 // The sync cycle should have included a GetUpdate, then a commit. By the
4509 // time the commit happened, we should have known for sure that there were no
4510 // hierarchy conflicts, and reported this fact to the server.
4511 ASSERT_TRUE(mock_server_
->last_request().has_commit());
4512 VerifyNoHierarchyConflictsReported(mock_server_
->last_request());
4514 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4516 Entry
received(&trans
, GET_BY_ID
, to_receive
);
4517 ASSERT_TRUE(received
.good());
4518 EXPECT_FALSE(received
.GetIsUnsynced());
4519 EXPECT_FALSE(received
.GetIsUnappliedUpdate());
4521 Entry
committed(&trans
, GET_BY_HANDLE
, commit_handle
);
4522 ASSERT_TRUE(committed
.good());
4523 EXPECT_FALSE(committed
.GetIsUnsynced());
4524 EXPECT_FALSE(committed
.GetIsUnappliedUpdate());
4527 // Same as above, but this time we fail to download updates.
4528 // We should not attempt to commit anything unless we successfully downloaded
4529 // updates, otherwise we risk causing a server-side conflict.
4530 TEST_F(SyncerTest
, UpdateFailsThenDontCommit
) {
4531 syncable::Id to_receive
= ids_
.NewServerId();
4532 syncable::Id to_commit
= ids_
.NewLocalId();
4534 mock_server_
->AddUpdateDirectory(to_receive
, ids_
.root(), "x", 1, 10,
4535 foreign_cache_guid(), "-1");
4536 int64 commit_handle
= CreateUnsyncedDirectory("y", to_commit
);
4537 mock_server_
->FailNextPostBufferToPathCall();
4540 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4542 // We did not receive this update.
4543 Entry
received(&trans
, GET_BY_ID
, to_receive
);
4544 ASSERT_FALSE(received
.good());
4546 // And our local update remains unapplied.
4547 Entry
committed(&trans
, GET_BY_HANDLE
, commit_handle
);
4548 ASSERT_TRUE(committed
.good());
4549 EXPECT_TRUE(committed
.GetIsUnsynced());
4550 EXPECT_FALSE(committed
.GetIsUnappliedUpdate());
4552 // Inform the Mock we won't be fetching all updates.
4553 mock_server_
->ClearUpdatesQueue();
4556 // Downloads two updates and applies them successfully.
4557 // This is the "happy path" alternative to ConfigureFailsDontApplyUpdates.
4558 TEST_F(SyncerTest
, ConfigureDownloadsTwoBatchesSuccess
) {
4559 syncable::Id node1
= ids_
.NewServerId();
4560 syncable::Id node2
= ids_
.NewServerId();
4562 // Construct the first GetUpdates response.
4563 mock_server_
->AddUpdateDirectory(node1
, ids_
.root(), "one", 1, 10,
4564 foreign_cache_guid(), "-2");
4565 mock_server_
->SetChangesRemaining(1);
4566 mock_server_
->NextUpdateBatch();
4568 // Construct the second GetUpdates response.
4569 mock_server_
->AddUpdateDirectory(node2
, ids_
.root(), "two", 1, 20,
4570 foreign_cache_guid(), "-2");
4572 SyncShareConfigure();
4574 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4575 // Both nodes should be downloaded and applied.
4577 Entry
n1(&trans
, GET_BY_ID
, node1
);
4578 ASSERT_TRUE(n1
.good());
4579 EXPECT_FALSE(n1
.GetIsUnappliedUpdate());
4581 Entry
n2(&trans
, GET_BY_ID
, node2
);
4582 ASSERT_TRUE(n2
.good());
4583 EXPECT_FALSE(n2
.GetIsUnappliedUpdate());
4586 // Same as the above case, but this time the second batch fails to download.
4587 TEST_F(SyncerTest
, ConfigureFailsDontApplyUpdates
) {
4588 syncable::Id node1
= ids_
.NewServerId();
4589 syncable::Id node2
= ids_
.NewServerId();
4591 // The scenario: we have two batches of updates with one update each. A
4592 // normal confgure step would download all the updates one batch at a time and
4593 // apply them. This configure will succeed in downloading the first batch
4594 // then fail when downloading the second.
4595 mock_server_
->FailNthPostBufferToPathCall(2);
4597 // Construct the first GetUpdates response.
4598 mock_server_
->AddUpdateDirectory(node1
, ids_
.root(), "one", 1, 10,
4599 foreign_cache_guid(), "-1");
4600 mock_server_
->SetChangesRemaining(1);
4601 mock_server_
->NextUpdateBatch();
4603 // Consutrct the second GetUpdates response.
4604 mock_server_
->AddUpdateDirectory(node2
, ids_
.root(), "two", 1, 20,
4605 foreign_cache_guid(), "-2");
4607 SyncShareConfigure();
4609 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4611 // The first node was downloaded, but not applied.
4612 Entry
n1(&trans
, GET_BY_ID
, node1
);
4613 ASSERT_TRUE(n1
.good());
4614 EXPECT_TRUE(n1
.GetIsUnappliedUpdate());
4616 // The second node was not downloaded.
4617 Entry
n2(&trans
, GET_BY_ID
, node2
);
4618 EXPECT_FALSE(n2
.good());
4620 // One update remains undownloaded.
4621 mock_server_
->ClearUpdatesQueue();
4624 TEST_F(SyncerTest
, GetKeySuccess
) {
4626 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4627 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4630 SyncShareConfigure();
4632 EXPECT_EQ(session_
->status_controller().last_get_key_result(), SYNCER_OK
);
4634 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4635 EXPECT_FALSE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4639 TEST_F(SyncerTest
, GetKeyEmpty
) {
4641 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4642 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4645 mock_server_
->SetKeystoreKey(std::string());
4646 SyncShareConfigure();
4648 EXPECT_NE(session_
->status_controller().last_get_key_result(), SYNCER_OK
);
4650 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4651 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4655 // Tests specifically related to bookmark (and therefore no client tags) sync
4656 // logic. Entities without client tags have custom logic in parts of the code,
4657 // and hence are not covered by e.g. the Undeletion tests below.
4658 class SyncerBookmarksTest
: public SyncerTest
{
4660 SyncerBookmarksTest() : metahandle_(syncable::kInvalidMetaHandle
) {
4664 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4665 MutableEntry
bookmark(
4666 &trans
, CREATE
, BOOKMARKS
, ids_
.root(), "clientname");
4667 ASSERT_TRUE(bookmark
.good());
4668 bookmark
.PutSpecifics(DefaultBookmarkSpecifics());
4669 EXPECT_FALSE(bookmark
.GetIsUnappliedUpdate());
4670 EXPECT_FALSE(bookmark
.GetId().ServerKnows());
4671 metahandle_
= bookmark
.GetMetahandle();
4672 local_id_
= bookmark
.GetId();
4673 bookmark
.PutIsUnsynced(true);
4677 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4678 MutableEntry
bookmark(&trans
, GET_BY_ID
, local_id_
);
4679 ASSERT_TRUE(bookmark
.good());
4680 bookmark
.PutSpecifics(DefaultBookmarkSpecifics());
4681 EXPECT_FALSE(bookmark
.GetIsUnappliedUpdate());
4682 bookmark
.PutIsUnsynced(true);
4683 if (bookmark
.GetSyncing())
4684 bookmark
.PutDirtySync(true);
4688 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4689 MutableEntry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4690 ASSERT_TRUE(entry
.good());
4691 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4692 // The order of setting IS_UNSYNCED vs IS_DEL matters. See
4693 // WriteNode::Tombstone().
4694 entry
.PutIsUnsynced(true);
4695 if (entry
.GetSyncing())
4696 entry
.PutDirtySync(true);
4697 entry
.PutIsDel(true);
4700 void UpdateAndDelete() {
4706 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4707 MutableEntry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4708 ASSERT_TRUE(entry
.good());
4709 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4710 EXPECT_TRUE(entry
.GetIsDel());
4711 entry
.PutIsDel(false);
4712 entry
.PutIsUnsynced(true);
4713 if (entry
.GetSyncing())
4714 entry
.PutDirtySync(true);
4717 int64
GetMetahandleOfTag() {
4718 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4719 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4720 EXPECT_TRUE(entry
.good());
4721 if (!entry
.good()) {
4722 return syncable::kInvalidMetaHandle
;
4724 return entry
.GetMetahandle();
4728 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4729 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4730 EXPECT_TRUE(entry
.good());
4731 if (!entry
.good()) {
4734 return entry
.GetId();
4737 void ExpectUnsyncedCreation() {
4738 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4739 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4741 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4742 EXPECT_FALSE(entry
.GetIsDel());
4743 EXPECT_FALSE(entry
.GetServerIsDel()); // Never been committed.
4744 EXPECT_LT(entry
.GetBaseVersion(), 0);
4745 EXPECT_TRUE(entry
.GetIsUnsynced());
4746 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4749 void ExpectUnsyncedUndeletion() {
4750 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4751 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4753 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4754 EXPECT_FALSE(entry
.GetIsDel());
4755 EXPECT_TRUE(entry
.GetServerIsDel());
4756 EXPECT_GE(entry
.GetBaseVersion(), 0);
4757 EXPECT_TRUE(entry
.GetIsUnsynced());
4758 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4759 EXPECT_TRUE(entry
.GetId().ServerKnows());
4762 void ExpectUnsyncedEdit() {
4763 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4764 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4766 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4767 EXPECT_FALSE(entry
.GetIsDel());
4768 EXPECT_FALSE(entry
.GetServerIsDel());
4769 EXPECT_GE(entry
.GetBaseVersion(), 0);
4770 EXPECT_TRUE(entry
.GetIsUnsynced());
4771 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4772 EXPECT_TRUE(entry
.GetId().ServerKnows());
4775 void ExpectUnsyncedDeletion() {
4776 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4777 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4779 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4780 EXPECT_TRUE(entry
.GetIsDel());
4781 EXPECT_FALSE(entry
.GetServerIsDel());
4782 EXPECT_TRUE(entry
.GetIsUnsynced());
4783 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4784 EXPECT_GE(entry
.GetBaseVersion(), 0);
4785 EXPECT_GE(entry
.GetServerVersion(), 0);
4788 void ExpectSyncedAndCreated() {
4789 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4790 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4792 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4793 EXPECT_FALSE(entry
.GetIsDel());
4794 EXPECT_FALSE(entry
.GetServerIsDel());
4795 EXPECT_GE(entry
.GetBaseVersion(), 0);
4796 EXPECT_EQ(entry
.GetBaseVersion(), entry
.GetServerVersion());
4797 EXPECT_FALSE(entry
.GetIsUnsynced());
4798 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4801 void ExpectSyncedAndDeleted() {
4802 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4803 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4805 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4806 EXPECT_TRUE(entry
.GetIsDel());
4807 EXPECT_TRUE(entry
.GetServerIsDel());
4808 EXPECT_FALSE(entry
.GetIsUnsynced());
4809 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4810 EXPECT_GE(entry
.GetBaseVersion(), 0);
4811 EXPECT_GE(entry
.GetServerVersion(), 0);
4815 syncable::Id local_id_
;
4819 TEST_F(SyncerBookmarksTest
, CreateSyncThenDeleteSync
) {
4821 ExpectUnsyncedCreation();
4823 ExpectSyncedAndCreated();
4825 ExpectUnsyncedDeletion();
4827 ExpectSyncedAndDeleted();
4830 TEST_F(SyncerBookmarksTest
, CreateThenDeleteBeforeSync
) {
4832 ExpectUnsyncedCreation();
4835 // Deleting before the initial commit should result in not needing to send
4836 // the delete to the server. It will still be in an unsynced state, but with
4837 // IS_UNSYNCED set to false.
4839 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4840 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4842 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4843 EXPECT_TRUE(entry
.GetIsDel());
4844 EXPECT_FALSE(entry
.GetServerIsDel());
4845 EXPECT_FALSE(entry
.GetIsUnsynced());
4846 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4847 EXPECT_EQ(entry
.GetBaseVersion(), -1);
4848 EXPECT_EQ(entry
.GetServerVersion(), 0);
4852 TEST_F(SyncerBookmarksTest
, LocalDeleteRemoteChangeConflict
) {
4854 ExpectUnsyncedCreation();
4856 ExpectSyncedAndCreated();
4858 ExpectUnsyncedDeletion();
4860 // Trigger a getupdates that modifies the bookmark. The update should be
4861 // clobbered by the local delete.
4862 mock_server_
->AddUpdateBookmark(GetServerId(), Id::GetRoot(), "dummy", 10, 10,
4863 local_cache_guid(), local_id_
.GetServerId());
4866 ExpectSyncedAndDeleted();
4869 TEST_F(SyncerBookmarksTest
, CreateThenDeleteDuringCommit
) {
4871 ExpectUnsyncedCreation();
4873 // In the middle of the initial creation commit, perform a deletion.
4874 // This should trigger performing two consecutive commit cycles, resulting
4875 // in the bookmark being both deleted and synced.
4876 mock_server_
->SetMidCommitCallback(
4877 base::Bind(&SyncerBookmarksTest::Delete
, base::Unretained(this)));
4880 ExpectSyncedAndDeleted();
4883 TEST_F(SyncerBookmarksTest
, CreateThenUpdateAndDeleteDuringCommit
) {
4885 ExpectUnsyncedCreation();
4887 // In the middle of the initial creation commit, perform an updated followed
4888 // by a deletion. This should trigger performing two consecutive commit
4889 // cycles, resulting in the bookmark being both deleted and synced.
4890 mock_server_
->SetMidCommitCallback(base::Bind(
4891 &SyncerBookmarksTest::UpdateAndDelete
, base::Unretained(this)));
4894 ExpectSyncedAndDeleted();
4897 // Test what happens if a client deletes, then recreates, an object very
4898 // quickly. It is possible that the deletion gets sent as a commit, and
4899 // the undelete happens during the commit request. The principle here
4900 // is that with a single committing client, conflicts should never
4901 // be encountered, and a client encountering its past actions during
4902 // getupdates should never feed back to override later actions.
4904 // In cases of ordering A-F below, the outcome should be the same.
4905 // Exercised by UndeleteDuringCommit:
4906 // A. Delete - commit - undelete - commitresponse.
4907 // B. Delete - commit - undelete - commitresponse - getupdates.
4908 // Exercised by UndeleteBeforeCommit:
4909 // C. Delete - undelete - commit - commitresponse.
4910 // D. Delete - undelete - commit - commitresponse - getupdates.
4911 // Exercised by UndeleteAfterCommit:
4912 // E. Delete - commit - commitresponse - undelete - commit
4913 // - commitresponse.
4914 // F. Delete - commit - commitresponse - undelete - commit -
4915 // - commitresponse - getupdates.
4916 class SyncerUndeletionTest
: public SyncerTest
{
4918 SyncerUndeletionTest()
4919 : client_tag_("foobar"),
4920 metahandle_(syncable::kInvalidMetaHandle
) {
4924 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4925 MutableEntry
perm_folder(
4926 &trans
, CREATE
, PREFERENCES
, ids_
.root(), "clientname");
4927 ASSERT_TRUE(perm_folder
.good());
4928 perm_folder
.PutUniqueClientTag(client_tag_
);
4929 perm_folder
.PutIsUnsynced(true);
4930 if (perm_folder
.GetSyncing())
4931 perm_folder
.PutDirtySync(true);
4932 perm_folder
.PutSpecifics(DefaultPreferencesSpecifics());
4933 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
4934 EXPECT_FALSE(perm_folder
.GetId().ServerKnows());
4935 metahandle_
= perm_folder
.GetMetahandle();
4936 local_id_
= perm_folder
.GetId();
4940 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4941 MutableEntry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4942 ASSERT_TRUE(entry
.good());
4943 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4944 // The order of setting IS_UNSYNCED vs IS_DEL matters. See
4945 // WriteNode::Tombstone().
4946 entry
.PutIsUnsynced(true);
4947 if (entry
.GetSyncing())
4948 entry
.PutDirtySync(true);
4949 entry
.PutIsDel(true);
4953 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4954 MutableEntry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4955 ASSERT_TRUE(entry
.good());
4956 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4957 EXPECT_TRUE(entry
.GetIsDel());
4958 entry
.PutIsDel(false);
4959 entry
.PutIsUnsynced(true);
4960 if (entry
.GetSyncing())
4961 entry
.PutDirtySync(true);
4964 int64
GetMetahandleOfTag() {
4965 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4966 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4967 EXPECT_TRUE(entry
.good());
4968 if (!entry
.good()) {
4969 return syncable::kInvalidMetaHandle
;
4971 return entry
.GetMetahandle();
4974 void ExpectUnsyncedCreation() {
4975 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4976 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4978 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4979 EXPECT_FALSE(entry
.GetIsDel());
4980 EXPECT_FALSE(entry
.GetServerIsDel()); // Never been committed.
4981 EXPECT_LT(entry
.GetBaseVersion(), 0);
4982 EXPECT_TRUE(entry
.GetIsUnsynced());
4983 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4986 void ExpectUnsyncedUndeletion() {
4987 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4988 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4990 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4991 EXPECT_FALSE(entry
.GetIsDel());
4992 EXPECT_TRUE(entry
.GetServerIsDel());
4993 EXPECT_GE(entry
.GetBaseVersion(), 0);
4994 EXPECT_TRUE(entry
.GetIsUnsynced());
4995 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4996 EXPECT_TRUE(entry
.GetId().ServerKnows());
4999 void ExpectUnsyncedEdit() {
5000 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5001 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
5003 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
5004 EXPECT_FALSE(entry
.GetIsDel());
5005 EXPECT_FALSE(entry
.GetServerIsDel());
5006 EXPECT_GE(entry
.GetBaseVersion(), 0);
5007 EXPECT_TRUE(entry
.GetIsUnsynced());
5008 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
5009 EXPECT_TRUE(entry
.GetId().ServerKnows());
5012 void ExpectUnsyncedDeletion() {
5013 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5014 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
5016 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
5017 EXPECT_TRUE(entry
.GetIsDel());
5018 EXPECT_FALSE(entry
.GetServerIsDel());
5019 EXPECT_TRUE(entry
.GetIsUnsynced());
5020 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
5021 EXPECT_GE(entry
.GetBaseVersion(), 0);
5022 EXPECT_GE(entry
.GetServerVersion(), 0);
5025 void ExpectSyncedAndCreated() {
5026 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5027 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
5029 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
5030 EXPECT_FALSE(entry
.GetIsDel());
5031 EXPECT_FALSE(entry
.GetServerIsDel());
5032 EXPECT_GE(entry
.GetBaseVersion(), 0);
5033 EXPECT_EQ(entry
.GetBaseVersion(), entry
.GetServerVersion());
5034 EXPECT_FALSE(entry
.GetIsUnsynced());
5035 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
5038 void ExpectSyncedAndDeleted() {
5039 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5040 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
5042 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
5043 EXPECT_TRUE(entry
.GetIsDel());
5044 EXPECT_TRUE(entry
.GetServerIsDel());
5045 EXPECT_FALSE(entry
.GetIsUnsynced());
5046 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
5047 EXPECT_GE(entry
.GetBaseVersion(), 0);
5048 EXPECT_GE(entry
.GetServerVersion(), 0);
5052 const std::string client_tag_
;
5053 syncable::Id local_id_
;
5057 TEST_F(SyncerUndeletionTest
, UndeleteDuringCommit
) {
5059 ExpectUnsyncedCreation();
5062 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5063 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5064 ExpectSyncedAndCreated();
5066 // Delete, begin committing the delete, then undelete while committing.
5068 ExpectUnsyncedDeletion();
5069 mock_server_
->SetMidCommitCallback(
5070 base::Bind(&SyncerUndeletionTest::Undelete
, base::Unretained(this)));
5073 // We will continue to commit until all nodes are synced, so we expect
5074 // that both the delete and following undelete were committed. We haven't
5075 // downloaded any updates, though, so the SERVER fields will be the same
5076 // as they were at the start of the cycle.
5077 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5078 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5081 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5082 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5084 // Server fields lag behind.
5085 EXPECT_FALSE(entry
.GetServerIsDel());
5087 // We have committed the second (undelete) update.
5088 EXPECT_FALSE(entry
.GetIsDel());
5089 EXPECT_FALSE(entry
.GetIsUnsynced());
5090 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
5093 // Now, encounter a GetUpdates corresponding to the deletion from
5094 // the server. The undeletion should prevail again and be committed.
5095 // None of this should trigger any conflict detection -- it is perfectly
5096 // normal to recieve updates from our own commits.
5097 mock_server_
->SetMidCommitCallback(base::Closure());
5098 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5099 update
->set_originator_cache_guid(local_cache_guid());
5100 update
->set_originator_client_item_id(local_id_
.GetServerId());
5103 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5104 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5105 ExpectSyncedAndCreated();
5108 TEST_F(SyncerUndeletionTest
, UndeleteBeforeCommit
) {
5110 ExpectUnsyncedCreation();
5113 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5114 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5115 ExpectSyncedAndCreated();
5117 // Delete and undelete, then sync to pick up the result.
5119 ExpectUnsyncedDeletion();
5121 ExpectUnsyncedEdit(); // Edit, not undelete: server thinks it exists.
5124 // The item ought to have committed successfully.
5125 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5126 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5127 ExpectSyncedAndCreated();
5129 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5130 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5131 EXPECT_EQ(2, entry
.GetBaseVersion());
5134 // Now, encounter a GetUpdates corresponding to the just-committed
5136 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5137 update
->set_originator_cache_guid(local_cache_guid());
5138 update
->set_originator_client_item_id(local_id_
.GetServerId());
5140 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5141 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5142 ExpectSyncedAndCreated();
5145 TEST_F(SyncerUndeletionTest
, UndeleteAfterCommitButBeforeGetUpdates
) {
5147 ExpectUnsyncedCreation();
5150 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5151 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5152 ExpectSyncedAndCreated();
5154 // Delete and commit.
5156 ExpectUnsyncedDeletion();
5159 // The item ought to have committed successfully.
5160 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5161 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5162 ExpectSyncedAndDeleted();
5164 // Before the GetUpdates, the item is locally undeleted.
5166 ExpectUnsyncedUndeletion();
5168 // Now, encounter a GetUpdates corresponding to the just-committed
5169 // deletion update. The undeletion should prevail.
5170 mock_server_
->AddUpdateFromLastCommit();
5172 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5173 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5174 ExpectSyncedAndCreated();
5177 TEST_F(SyncerUndeletionTest
, UndeleteAfterDeleteAndGetUpdates
) {
5179 ExpectUnsyncedCreation();
5182 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5183 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5184 ExpectSyncedAndCreated();
5186 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5187 update
->set_originator_cache_guid(local_cache_guid());
5188 update
->set_originator_client_item_id(local_id_
.GetServerId());
5190 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5191 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5192 ExpectSyncedAndCreated();
5194 // Delete and commit.
5196 ExpectUnsyncedDeletion();
5199 // The item ought to have committed successfully.
5200 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5201 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5202 ExpectSyncedAndDeleted();
5204 // Now, encounter a GetUpdates corresponding to the just-committed
5205 // deletion update. Should be consistent.
5206 mock_server_
->AddUpdateFromLastCommit();
5208 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5209 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5210 ExpectSyncedAndDeleted();
5212 // After the GetUpdates, the item is locally undeleted.
5214 ExpectUnsyncedUndeletion();
5216 // Now, encounter a GetUpdates corresponding to the just-committed
5217 // deletion update. The undeletion should prevail.
5219 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5220 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5221 ExpectSyncedAndCreated();
5224 // Test processing of undeletion GetUpdateses.
5225 TEST_F(SyncerUndeletionTest
, UndeleteAfterOtherClientDeletes
) {
5227 ExpectUnsyncedCreation();
5230 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5231 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5232 ExpectSyncedAndCreated();
5234 // Add a delete from the server.
5235 sync_pb::SyncEntity
* update1
= mock_server_
->AddUpdateFromLastCommit();
5236 update1
->set_originator_cache_guid(local_cache_guid());
5237 update1
->set_originator_client_item_id(local_id_
.GetServerId());
5239 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5240 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5241 ExpectSyncedAndCreated();
5243 // Some other client deletes the item.
5245 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5246 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5247 mock_server_
->AddUpdateTombstone(entry
.GetId(), PREFERENCES
);
5251 // The update ought to have applied successfully.
5252 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5253 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5254 ExpectSyncedAndDeleted();
5256 // Undelete it locally.
5258 ExpectUnsyncedUndeletion();
5260 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5261 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5262 ExpectSyncedAndCreated();
5264 // Now, encounter a GetUpdates corresponding to the just-committed
5265 // deletion update. The undeletion should prevail.
5266 sync_pb::SyncEntity
* update2
= mock_server_
->AddUpdateFromLastCommit();
5267 update2
->set_originator_cache_guid(local_cache_guid());
5268 update2
->set_originator_client_item_id(local_id_
.GetServerId());
5270 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5271 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5272 ExpectSyncedAndCreated();
5275 TEST_F(SyncerUndeletionTest
, UndeleteAfterOtherClientDeletesImmediately
) {
5277 ExpectUnsyncedCreation();
5280 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5281 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5282 ExpectSyncedAndCreated();
5284 // Some other client deletes the item before we get a chance
5285 // to GetUpdates our original request.
5287 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5288 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5289 mock_server_
->AddUpdateTombstone(entry
.GetId(), PREFERENCES
);
5293 // The update ought to have applied successfully.
5294 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5295 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5296 ExpectSyncedAndDeleted();
5298 // Undelete it locally.
5300 ExpectUnsyncedUndeletion();
5302 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5303 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5304 ExpectSyncedAndCreated();
5306 // Now, encounter a GetUpdates corresponding to the just-committed
5307 // deletion update. The undeletion should prevail.
5308 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5309 update
->set_originator_cache_guid(local_cache_guid());
5310 update
->set_originator_client_item_id(local_id_
.GetServerId());
5312 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5313 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5314 ExpectSyncedAndCreated();
5317 TEST_F(SyncerUndeletionTest
, OtherClientUndeletes
) {
5319 ExpectUnsyncedCreation();
5322 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5323 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5324 ExpectSyncedAndCreated();
5326 // Get the updates of our just-committed entry.
5327 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5328 update
->set_originator_cache_guid(local_cache_guid());
5329 update
->set_originator_client_item_id(local_id_
.GetServerId());
5331 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5332 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5333 ExpectSyncedAndCreated();
5335 // We delete the item.
5337 ExpectUnsyncedDeletion();
5340 // The update ought to have applied successfully.
5341 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5342 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5343 ExpectSyncedAndDeleted();
5345 // Now, encounter a GetUpdates corresponding to the just-committed
5347 mock_server_
->AddUpdateFromLastCommit();
5349 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5350 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5351 ExpectSyncedAndDeleted();
5353 // Some other client undeletes the item.
5355 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5356 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5357 mock_server_
->AddUpdatePref(
5358 entry
.GetId().GetServerId(),
5359 entry
.GetParentId().GetServerId(),
5360 client_tag_
, 100, 1000);
5362 mock_server_
->SetLastUpdateClientTag(client_tag_
);
5364 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5365 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5366 ExpectSyncedAndCreated();
5369 TEST_F(SyncerUndeletionTest
, OtherClientUndeletesImmediately
) {
5371 ExpectUnsyncedCreation();
5374 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5375 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5376 ExpectSyncedAndCreated();
5378 // Get the updates of our just-committed entry.
5379 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5380 update
->set_originator_cache_guid(local_cache_guid());
5382 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5383 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5384 update
->set_originator_client_item_id(local_id_
.GetServerId());
5387 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5388 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5389 ExpectSyncedAndCreated();
5391 // We delete the item.
5393 ExpectUnsyncedDeletion();
5396 // The update ought to have applied successfully.
5397 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5398 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5399 ExpectSyncedAndDeleted();
5401 // Some other client undeletes before we see the update from our
5404 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5405 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5406 mock_server_
->AddUpdatePref(
5407 entry
.GetId().GetServerId(),
5408 entry
.GetParentId().GetServerId(),
5409 client_tag_
, 100, 1000);
5411 mock_server_
->SetLastUpdateClientTag(client_tag_
);
5413 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5414 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5415 ExpectSyncedAndCreated();
5419 TEST_PARAM_BOOKMARK_ENABLE_BIT
,
5420 TEST_PARAM_AUTOFILL_ENABLE_BIT
,
5421 TEST_PARAM_BIT_COUNT
5426 public ::testing::WithParamInterface
<int> {
5428 bool ShouldFailBookmarkCommit() {
5429 return (GetParam() & (1 << TEST_PARAM_BOOKMARK_ENABLE_BIT
)) == 0;
5431 bool ShouldFailAutofillCommit() {
5432 return (GetParam() & (1 << TEST_PARAM_AUTOFILL_ENABLE_BIT
)) == 0;
5436 INSTANTIATE_TEST_CASE_P(ExtensionsActivity
,
5438 testing::Range(0, 1 << TEST_PARAM_BIT_COUNT
));
5440 TEST_P(MixedResult
, ExtensionsActivity
) {
5442 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
5444 MutableEntry
pref(&wtrans
, CREATE
, PREFERENCES
, wtrans
.root_id(), "pref");
5445 ASSERT_TRUE(pref
.good());
5446 pref
.PutIsUnsynced(true);
5448 MutableEntry
bookmark(
5449 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "bookmark");
5450 ASSERT_TRUE(bookmark
.good());
5451 bookmark
.PutIsUnsynced(true);
5453 if (ShouldFailBookmarkCommit()) {
5454 mock_server_
->SetTransientErrorId(bookmark
.GetId());
5457 if (ShouldFailAutofillCommit()) {
5458 mock_server_
->SetTransientErrorId(pref
.GetId());
5463 // Put some extenions activity records into the monitor.
5465 ExtensionsActivity::Records records
;
5466 records
["ABC"].extension_id
= "ABC";
5467 records
["ABC"].bookmark_write_count
= 2049U;
5468 records
["xyz"].extension_id
= "xyz";
5469 records
["xyz"].bookmark_write_count
= 4U;
5470 context_
->extensions_activity()->PutRecords(records
);
5475 ExtensionsActivity::Records final_monitor_records
;
5476 context_
->extensions_activity()->GetAndClearRecords(&final_monitor_records
);
5477 if (ShouldFailBookmarkCommit()) {
5478 ASSERT_EQ(2U, final_monitor_records
.size())
5479 << "Should restore records after unsuccessful bookmark commit.";
5480 EXPECT_EQ("ABC", final_monitor_records
["ABC"].extension_id
);
5481 EXPECT_EQ("xyz", final_monitor_records
["xyz"].extension_id
);
5482 EXPECT_EQ(2049U, final_monitor_records
["ABC"].bookmark_write_count
);
5483 EXPECT_EQ(4U, final_monitor_records
["xyz"].bookmark_write_count
);
5485 EXPECT_TRUE(final_monitor_records
.empty())
5486 << "Should not restore records after successful bookmark commit.";
5490 } // namespace syncer