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/get_commit_ids.h"
27 #include "sync/engine/net/server_connection_manager.h"
28 #include "sync/engine/sync_scheduler_impl.h"
29 #include "sync/engine/syncer.h"
30 #include "sync/engine/syncer_proto_util.h"
31 #include "sync/internal_api/public/base/cancelation_signal.h"
32 #include "sync/internal_api/public/base/model_type.h"
33 #include "sync/internal_api/public/engine/model_safe_worker.h"
34 #include "sync/internal_api/public/sessions/commit_counters.h"
35 #include "sync/internal_api/public/sessions/status_counters.h"
36 #include "sync/internal_api/public/sessions/update_counters.h"
37 #include "sync/protocol/bookmark_specifics.pb.h"
38 #include "sync/protocol/nigori_specifics.pb.h"
39 #include "sync/protocol/preference_specifics.pb.h"
40 #include "sync/protocol/sync.pb.h"
41 #include "sync/sessions/sync_session_context.h"
42 #include "sync/syncable/mutable_entry.h"
43 #include "sync/syncable/nigori_util.h"
44 #include "sync/syncable/syncable_delete_journal.h"
45 #include "sync/syncable/syncable_read_transaction.h"
46 #include "sync/syncable/syncable_util.h"
47 #include "sync/syncable/syncable_write_transaction.h"
48 #include "sync/test/engine/fake_model_worker.h"
49 #include "sync/test/engine/mock_connection_manager.h"
50 #include "sync/test/engine/mock_nudge_handler.h"
51 #include "sync/test/engine/test_directory_setter_upper.h"
52 #include "sync/test/engine/test_id_factory.h"
53 #include "sync/test/engine/test_syncable_utils.h"
54 #include "sync/test/fake_encryptor.h"
55 #include "sync/test/fake_sync_encryption_handler.h"
56 #include "sync/test/sessions/mock_debug_info_getter.h"
57 #include "sync/util/cryptographer.h"
58 #include "sync/util/extensions_activity.h"
59 #include "sync/util/time.h"
60 #include "testing/gtest/include/gtest/gtest.h"
62 using base::TimeDelta
;
73 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 FAIL() << "Should not get silenced.";
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
));
266 syncer_
->NormalSyncShare(
267 context_
->GetEnabledTypes(),
272 void SyncShareConfigure() {
274 EXPECT_TRUE(syncer_
->ConfigureSyncShare(
275 context_
->GetEnabledTypes(),
276 sync_pb::GetUpdatesCallerInfo::RECONFIGURATION
,
280 virtual void SetUp() {
282 mock_server_
.reset(new MockConnectionManager(directory(),
283 &cancelation_signal_
));
284 debug_info_getter_
.reset(new MockDebugInfoGetter
);
285 EnableDatatype(BOOKMARKS
);
286 EnableDatatype(NIGORI
);
287 EnableDatatype(PREFERENCES
);
288 EnableDatatype(NIGORI
);
289 workers_
.push_back(scoped_refptr
<ModelSafeWorker
>(
290 new FakeModelWorker(GROUP_PASSIVE
)));
291 std::vector
<SyncEngineEventListener
*> listeners
;
292 listeners
.push_back(this);
294 ModelSafeRoutingInfo routing_info
;
295 GetModelSafeRoutingInfo(&routing_info
);
297 model_type_registry_
.reset(
298 new ModelTypeRegistry(workers_
, directory(), &mock_nudge_handler_
));
299 model_type_registry_
->RegisterDirectoryTypeDebugInfoObserver(
302 context_
.reset(new SyncSessionContext(
305 extensions_activity_
.get(),
307 debug_info_getter_
.get(),
308 model_type_registry_
.get(),
309 true, // enable keystore encryption
310 false, // force enable pre-commit GU avoidance experiment
311 "fake_invalidator_client_id"));
312 context_
->SetRoutingInfo(routing_info
);
313 syncer_
= new Syncer(&cancelation_signal_
);
315 syncable::ReadTransaction
trans(FROM_HERE
, directory());
316 syncable::Directory::Metahandles children
;
317 directory()->GetChildHandlesById(&trans
, trans
.root_id(), &children
);
318 ASSERT_EQ(0u, children
.size());
319 saw_syncer_event_
= false;
320 root_id_
= TestIdFactory::root();
321 parent_id_
= ids_
.MakeServer("parent id");
322 child_id_
= ids_
.MakeServer("child id");
323 directory()->set_store_birthday(mock_server_
->store_birthday());
324 mock_server_
->SetKeystoreKey("encryption_key");
327 virtual void TearDown() {
328 model_type_registry_
->UnregisterDirectoryTypeDebugInfoObserver(
330 mock_server_
.reset();
333 dir_maker_
.TearDown();
336 void WriteTestDataToEntry(WriteTransaction
* trans
, MutableEntry
* entry
) {
337 EXPECT_FALSE(entry
->GetIsDir());
338 EXPECT_FALSE(entry
->GetIsDel());
339 sync_pb::EntitySpecifics specifics
;
340 specifics
.mutable_bookmark()->set_url("http://demo/");
341 specifics
.mutable_bookmark()->set_favicon("PNG");
342 entry
->PutSpecifics(specifics
);
343 entry
->PutIsUnsynced(true);
345 void VerifyTestDataInEntry(BaseTransaction
* trans
, Entry
* entry
) {
346 EXPECT_FALSE(entry
->GetIsDir());
347 EXPECT_FALSE(entry
->GetIsDel());
348 VerifyTestBookmarkDataInEntry(entry
);
350 void VerifyTestBookmarkDataInEntry(Entry
* entry
) {
351 const sync_pb::EntitySpecifics
& specifics
= entry
->GetSpecifics();
352 EXPECT_TRUE(specifics
.has_bookmark());
353 EXPECT_EQ("PNG", specifics
.bookmark().favicon());
354 EXPECT_EQ("http://demo/", specifics
.bookmark().url());
357 void VerifyHierarchyConflictsReported(
358 const sync_pb::ClientToServerMessage
& message
) {
359 // Our request should have included a warning about hierarchy conflicts.
360 const sync_pb::ClientStatus
& client_status
= message
.client_status();
361 EXPECT_TRUE(client_status
.has_hierarchy_conflict_detected());
362 EXPECT_TRUE(client_status
.hierarchy_conflict_detected());
365 void VerifyNoHierarchyConflictsReported(
366 const sync_pb::ClientToServerMessage
& message
) {
367 // Our request should have reported no hierarchy conflicts detected.
368 const sync_pb::ClientStatus
& client_status
= message
.client_status();
369 EXPECT_TRUE(client_status
.has_hierarchy_conflict_detected());
370 EXPECT_FALSE(client_status
.hierarchy_conflict_detected());
373 void VerifyHierarchyConflictsUnspecified(
374 const sync_pb::ClientToServerMessage
& message
) {
375 // Our request should have neither confirmed nor denied hierarchy conflicts.
376 const sync_pb::ClientStatus
& client_status
= message
.client_status();
377 EXPECT_FALSE(client_status
.has_hierarchy_conflict_detected());
380 sync_pb::EntitySpecifics
DefaultBookmarkSpecifics() {
381 sync_pb::EntitySpecifics result
;
382 AddDefaultFieldValue(BOOKMARKS
, &result
);
386 sync_pb::EntitySpecifics
DefaultPreferencesSpecifics() {
387 sync_pb::EntitySpecifics result
;
388 AddDefaultFieldValue(PREFERENCES
, &result
);
391 // Enumeration of alterations to entries for commit ordering tests.
393 LIST_END
= 0, // Denotes the end of the list of features from below.
394 SYNCED
, // Items are unsynced by default
400 struct CommitOrderingTest
{
401 // expected commit index.
403 // Details about the item
405 syncable::Id parent_id
;
406 EntryFeature features
[10];
408 static CommitOrderingTest
MakeLastCommitItem() {
409 CommitOrderingTest last_commit_item
;
410 last_commit_item
.commit_index
= -1;
411 last_commit_item
.id
= TestIdFactory::root();
412 return last_commit_item
;
416 void RunCommitOrderingTest(CommitOrderingTest
* test
) {
417 map
<int, syncable::Id
> expected_positions
;
418 { // Transaction scope.
419 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
420 while (!test
->id
.IsRoot()) {
421 if (test
->commit_index
>= 0) {
422 map
<int, syncable::Id
>::value_type
entry(test
->commit_index
,
424 bool double_position
= !expected_positions
.insert(entry
).second
;
425 ASSERT_FALSE(double_position
) << "Two id's expected at one position";
427 string utf8_name
= test
->id
.GetServerId();
428 string
name(utf8_name
.begin(), utf8_name
.end());
429 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, test
->parent_id
, name
);
431 entry
.PutId(test
->id
);
432 if (test
->id
.ServerKnows()) {
433 entry
.PutBaseVersion(5);
434 entry
.PutServerVersion(5);
435 entry
.PutServerParentId(test
->parent_id
);
437 entry
.PutIsDir(true);
438 entry
.PutIsUnsynced(true);
439 entry
.PutSpecifics(DefaultBookmarkSpecifics());
440 // Set the time to 30 seconds in the future to reduce the chance of
442 const base::Time
& now_plus_30s
=
443 base::Time::Now() + base::TimeDelta::FromSeconds(30);
444 const base::Time
& now_minus_2h
=
445 base::Time::Now() - base::TimeDelta::FromHours(2);
446 entry
.PutMtime(now_plus_30s
);
447 for (size_t i
= 0 ; i
< arraysize(test
->features
) ; ++i
) {
448 switch (test
->features
[i
]) {
452 entry
.PutIsUnsynced(false);
455 entry
.PutIsDel(true);
458 entry
.PutMtime(now_minus_2h
);
460 case MOVED_FROM_ROOT
:
461 entry
.PutServerParentId(trans
.root_id());
464 FAIL() << "Bad value in CommitOrderingTest list";
471 ASSERT_TRUE(expected_positions
.size() ==
472 mock_server_
->committed_ids().size());
473 // If this test starts failing, be aware other sort orders could be valid.
474 for (size_t i
= 0; i
< expected_positions
.size(); ++i
) {
476 EXPECT_EQ(1u, expected_positions
.count(i
));
477 EXPECT_EQ(expected_positions
[i
], mock_server_
->committed_ids()[i
]);
481 CommitCounters
GetCommitCounters(ModelType type
) {
482 return debug_info_cache_
.GetLatestCommitCounters(type
);
485 UpdateCounters
GetUpdateCounters(ModelType type
) {
486 return debug_info_cache_
.GetLatestUpdateCounters(type
);
489 StatusCounters
GetStatusCounters(ModelType type
) {
490 return debug_info_cache_
.GetLatestStatusCounters(type
);
493 Directory
* directory() {
494 return dir_maker_
.directory();
497 const std::string
local_cache_guid() {
498 return directory()->cache_guid();
501 const std::string
foreign_cache_guid() {
502 return "kqyg7097kro6GSUod+GSg==";
505 int64
CreateUnsyncedDirectory(const string
& entry_name
,
506 const string
& idstring
) {
507 return CreateUnsyncedDirectory(entry_name
,
508 syncable::Id::CreateFromServerId(idstring
));
511 int64
CreateUnsyncedDirectory(const string
& entry_name
,
512 const syncable::Id
& id
) {
513 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
515 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), entry_name
);
516 EXPECT_TRUE(entry
.good());
517 entry
.PutIsUnsynced(true);
518 entry
.PutIsDir(true);
519 entry
.PutSpecifics(DefaultBookmarkSpecifics());
520 entry
.PutBaseVersion(id
.ServerKnows() ? 1 : 0);
522 return entry
.GetMetahandle();
525 void EnableDatatype(ModelType model_type
) {
526 enabled_datatypes_
.Put(model_type
);
528 ModelSafeRoutingInfo routing_info
;
529 GetModelSafeRoutingInfo(&routing_info
);
532 context_
->SetRoutingInfo(routing_info
);
535 mock_server_
->ExpectGetUpdatesRequestTypes(enabled_datatypes_
);
538 void DisableDatatype(ModelType model_type
) {
539 enabled_datatypes_
.Remove(model_type
);
541 ModelSafeRoutingInfo routing_info
;
542 GetModelSafeRoutingInfo(&routing_info
);
545 context_
->SetRoutingInfo(routing_info
);
548 mock_server_
->ExpectGetUpdatesRequestTypes(enabled_datatypes_
);
551 Cryptographer
* GetCryptographer(syncable::BaseTransaction
* trans
) {
552 return directory()->GetCryptographer(trans
);
555 // Configures SyncSessionContext and NudgeTracker so Syncer won't call
556 // GetUpdates prior to Commit. This method can be used to ensure a Commit is
557 // not preceeded by GetUpdates.
558 void ConfigureNoGetUpdatesRequired() {
559 context_
->set_server_enabled_pre_commit_update_avoidance(true);
560 nudge_tracker_
.OnInvalidationsEnabled();
561 nudge_tracker_
.RecordSuccessfulSyncCycle();
563 ASSERT_FALSE(context_
->ShouldFetchUpdatesBeforeCommit());
564 ASSERT_FALSE(nudge_tracker_
.IsGetUpdatesRequired());
567 base::MessageLoop message_loop_
;
569 // Some ids to aid tests. Only the root one's value is specific. The rest
570 // are named for test clarity.
571 // TODO(chron): Get rid of these inbuilt IDs. They only make it
573 syncable::Id root_id_
;
574 syncable::Id parent_id_
;
575 syncable::Id child_id_
;
579 TestDirectorySetterUpper dir_maker_
;
580 FakeEncryptor encryptor_
;
581 scoped_refptr
<ExtensionsActivity
> extensions_activity_
;
582 scoped_ptr
<MockConnectionManager
> mock_server_
;
583 CancelationSignal cancelation_signal_
;
587 scoped_ptr
<SyncSession
> session_
;
588 TypeDebugInfoCache debug_info_cache_
;
589 MockNudgeHandler mock_nudge_handler_
;
590 scoped_ptr
<ModelTypeRegistry
> model_type_registry_
;
591 scoped_ptr
<SyncSessionContext
> context_
;
592 bool saw_syncer_event_
;
593 base::TimeDelta last_short_poll_interval_received_
;
594 base::TimeDelta last_long_poll_interval_received_
;
595 base::TimeDelta last_sessions_commit_delay_
;
596 base::TimeDelta last_bookmarks_commit_delay_
;
597 int last_client_invalidation_hint_buffer_size_
;
598 std::vector
<scoped_refptr
<ModelSafeWorker
> > workers_
;
600 ModelTypeSet enabled_datatypes_
;
601 sessions::NudgeTracker nudge_tracker_
;
602 scoped_ptr
<MockDebugInfoGetter
> debug_info_getter_
;
604 DISALLOW_COPY_AND_ASSIGN(SyncerTest
);
607 TEST_F(SyncerTest
, TestCallGatherUnsyncedEntries
) {
609 Syncer::UnsyncedMetaHandles handles
;
611 syncable::ReadTransaction
trans(FROM_HERE
, directory());
612 GetUnsyncedEntries(&trans
, &handles
);
614 ASSERT_EQ(0u, handles
.size());
616 // TODO(sync): When we can dynamically connect and disconnect the mock
617 // ServerConnectionManager test disconnected GetUnsyncedEntries here. It's a
618 // regression for a very old bug.
621 TEST_F(SyncerTest
, GetCommitIdsFiltersThrottledEntries
) {
622 const ModelTypeSet
throttled_types(BOOKMARKS
);
623 sync_pb::EntitySpecifics bookmark_data
;
624 AddDefaultFieldValue(BOOKMARKS
, &bookmark_data
);
626 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
627 foreign_cache_guid(), "-1");
631 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
632 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
633 ASSERT_TRUE(A
.good());
634 A
.PutIsUnsynced(true);
635 A
.PutSpecifics(bookmark_data
);
636 A
.PutNonUniqueName("bookmark");
639 // Now sync without enabling bookmarks.
640 mock_server_
->ExpectGetUpdatesRequestTypes(
641 Difference(context_
->GetEnabledTypes(), ModelTypeSet(BOOKMARKS
)));
643 syncer_
->NormalSyncShare(
644 Difference(context_
->GetEnabledTypes(), ModelTypeSet(BOOKMARKS
)),
649 // Nothing should have been committed as bookmarks is throttled.
650 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
651 Entry
entryA(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
652 ASSERT_TRUE(entryA
.good());
653 EXPECT_TRUE(entryA
.GetIsUnsynced());
656 // Sync again with bookmarks enabled.
657 mock_server_
->ExpectGetUpdatesRequestTypes(context_
->GetEnabledTypes());
660 // It should have been committed.
661 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
662 Entry
entryA(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
663 ASSERT_TRUE(entryA
.good());
664 EXPECT_FALSE(entryA
.GetIsUnsynced());
668 // We use a macro so we can preserve the error location.
669 #define VERIFY_ENTRY(id, is_unapplied, is_unsynced, prev_initialized, \
670 parent_id, version, server_version, id_fac, rtrans) \
672 Entry entryA(rtrans, syncable::GET_BY_ID, id_fac.FromNumber(id)); \
673 ASSERT_TRUE(entryA.good()); \
674 /* We don't use EXPECT_EQ here because when the left side param is false,
675 gcc 4.6 warns about converting 'false' to pointer type for argument 1. */ \
676 EXPECT_TRUE(is_unsynced == entryA.GetIsUnsynced()); \
677 EXPECT_TRUE(is_unapplied == entryA.GetIsUnappliedUpdate()); \
678 EXPECT_TRUE(prev_initialized == \
679 IsRealDataType(GetModelTypeFromSpecifics( \
680 entryA.GetBaseServerSpecifics()))); \
681 EXPECT_TRUE(parent_id == -1 || \
682 entryA.GetParentId()== id_fac.FromNumber(parent_id)); \
683 EXPECT_EQ(version, entryA.GetBaseVersion()); \
684 EXPECT_EQ(server_version, entryA.GetServerVersion()); \
687 TEST_F(SyncerTest
, GetCommitIdsFiltersUnreadyEntries
) {
688 KeyParams key_params
= {"localhost", "dummy", "foobar"};
689 KeyParams other_params
= {"localhost", "dummy", "foobar2"};
690 sync_pb::EntitySpecifics bookmark
, encrypted_bookmark
;
691 bookmark
.mutable_bookmark()->set_url("url");
692 bookmark
.mutable_bookmark()->set_title("title");
693 AddDefaultFieldValue(BOOKMARKS
, &encrypted_bookmark
);
694 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
695 foreign_cache_guid(), "-1");
696 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
697 foreign_cache_guid(), "-2");
698 mock_server_
->AddUpdateDirectory(3, 0, "C", 10, 10,
699 foreign_cache_guid(), "-3");
700 mock_server_
->AddUpdateDirectory(4, 0, "D", 10, 10,
701 foreign_cache_guid(), "-4");
703 // Server side change will put A in conflict.
704 mock_server_
->AddUpdateDirectory(1, 0, "A", 20, 20,
705 foreign_cache_guid(), "-1");
707 // Mark bookmarks as encrypted and set the cryptographer to have pending
709 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
710 Cryptographer
other_cryptographer(&encryptor_
);
711 other_cryptographer
.AddKey(other_params
);
712 sync_pb::EntitySpecifics specifics
;
713 sync_pb::NigoriSpecifics
* nigori
= specifics
.mutable_nigori();
714 other_cryptographer
.GetKeys(nigori
->mutable_encryption_keybag());
715 dir_maker_
.encryption_handler()->EnableEncryptEverything();
716 // Set up with an old passphrase, but have pending keys
717 GetCryptographer(&wtrans
)->AddKey(key_params
);
718 GetCryptographer(&wtrans
)->Encrypt(bookmark
,
719 encrypted_bookmark
.mutable_encrypted());
720 GetCryptographer(&wtrans
)->SetPendingKeys(nigori
->encryption_keybag());
722 // In conflict but properly encrypted.
723 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
724 ASSERT_TRUE(A
.good());
725 A
.PutIsUnsynced(true);
726 A
.PutSpecifics(encrypted_bookmark
);
727 A
.PutNonUniqueName(kEncryptedString
);
728 // Not in conflict and properly encrypted.
729 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
730 ASSERT_TRUE(B
.good());
731 B
.PutIsUnsynced(true);
732 B
.PutSpecifics(encrypted_bookmark
);
733 B
.PutNonUniqueName(kEncryptedString
);
734 // Unencrypted specifics.
735 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
736 ASSERT_TRUE(C
.good());
737 C
.PutIsUnsynced(true);
738 C
.PutNonUniqueName(kEncryptedString
);
739 // Unencrypted non_unique_name.
740 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
741 ASSERT_TRUE(D
.good());
742 D
.PutIsUnsynced(true);
743 D
.PutSpecifics(encrypted_bookmark
);
744 D
.PutNonUniqueName("not encrypted");
748 // Nothing should have commited due to bookmarks being encrypted and
749 // the cryptographer having pending keys. A would have been resolved
750 // as a simple conflict, but still be unsynced until the next sync cycle.
751 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
752 VERIFY_ENTRY(1, false, true, false, 0, 20, 20, ids_
, &rtrans
);
753 VERIFY_ENTRY(2, false, true, false, 0, 10, 10, ids_
, &rtrans
);
754 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_
, &rtrans
);
755 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_
, &rtrans
);
757 // Resolve the pending keys.
758 GetCryptographer(&rtrans
)->DecryptPendingKeys(other_params
);
762 // All properly encrypted and non-conflicting items should commit. "A" was
763 // conflicting, but last sync cycle resolved it as simple conflict, so on
764 // this sync cycle it committed succesfullly.
765 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
766 // Committed successfully.
767 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_
, &rtrans
);
768 // Committed successfully.
769 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_
, &rtrans
);
770 // Was not properly encrypted.
771 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_
, &rtrans
);
772 // Was not properly encrypted.
773 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_
, &rtrans
);
776 // Fix the remaining items.
777 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
778 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
779 ASSERT_TRUE(C
.good());
780 C
.PutSpecifics(encrypted_bookmark
);
781 C
.PutNonUniqueName(kEncryptedString
);
782 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
783 ASSERT_TRUE(D
.good());
784 D
.PutSpecifics(encrypted_bookmark
);
785 D
.PutNonUniqueName(kEncryptedString
);
789 const StatusController
& status_controller
= session_
->status_controller();
791 EXPECT_EQ(status_controller
.model_neutral_state().commit_result
, SYNCER_OK
);
792 // None should be unsynced anymore.
793 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
794 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_
, &rtrans
);
795 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_
, &rtrans
);
796 VERIFY_ENTRY(3, false, false, false, 0, 11, 11, ids_
, &rtrans
);
797 VERIFY_ENTRY(4, false, false, false, 0, 11, 11, ids_
, &rtrans
);
801 // This test uses internal knowledge of the directory to test correctness of
802 // GetCommitIds. In almost every other test, the hierarchy is created from
803 // parent to child order, and so parents always have metahandles that are
804 // smaller than those of their children. This makes it very difficult to test
805 // some GetCommitIds edge cases, since it uses metahandle ordering as
807 TEST_F(SyncerTest
, GetCommitIds_VerifyDeletionCommitOrder
) {
809 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
811 // Create four bookmarks folders at the root node.
812 for (int i
= 1; i
< 5; ++i
) {
813 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "");
814 entry
.PutId(ids_
.FromNumber(i
));
815 entry
.PutIsDir(true);
816 entry
.PutBaseVersion(5);
817 entry
.PutServerVersion(5);
818 entry
.PutServerParentId(trans
.root_id());
819 entry
.PutServerIsDir(true);
820 entry
.PutIsUnsynced(true);
821 entry
.PutSpecifics(DefaultBookmarkSpecifics());
824 // Now iterate in reverse order make a hierarchy of them.
825 // While we're at it, also mark them as deleted.
826 syncable::Id parent_id
= trans
.root_id();
827 for (int i
= 4; i
> 0; --i
) {
828 MutableEntry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(i
));
829 entry
.PutParentId(parent_id
);
830 entry
.PutServerParentId(parent_id
);
831 entry
.PutIsDel(true);
832 parent_id
= ids_
.FromNumber(i
);
837 // Run GetCommitIds, the function being tested.
838 syncable::Directory::Metahandles result_handles
;
839 syncable::ReadTransaction
trans(FROM_HERE
, directory());
840 GetCommitIdsForType(&trans
, BOOKMARKS
, 100, &result_handles
);
842 // Now verify the output. We expect four results in child to parent order.
843 ASSERT_EQ(4U, result_handles
.size());
845 Entry
entry0(&trans
, GET_BY_HANDLE
, result_handles
[0]);
846 EXPECT_EQ(ids_
.FromNumber(1), entry0
.GetId());
848 Entry
entry1(&trans
, GET_BY_HANDLE
, result_handles
[1]);
849 EXPECT_EQ(ids_
.FromNumber(2), entry1
.GetId());
851 Entry
entry2(&trans
, GET_BY_HANDLE
, result_handles
[2]);
852 EXPECT_EQ(ids_
.FromNumber(3), entry2
.GetId());
854 Entry
entry3(&trans
, GET_BY_HANDLE
, result_handles
[3]);
855 EXPECT_EQ(ids_
.FromNumber(4), entry3
.GetId());
859 TEST_F(SyncerTest
, EncryptionAwareConflicts
) {
860 KeyParams key_params
= {"localhost", "dummy", "foobar"};
861 Cryptographer
other_cryptographer(&encryptor_
);
862 other_cryptographer
.AddKey(key_params
);
863 sync_pb::EntitySpecifics bookmark
, encrypted_bookmark
, modified_bookmark
;
864 bookmark
.mutable_bookmark()->set_title("title");
865 other_cryptographer
.Encrypt(bookmark
,
866 encrypted_bookmark
.mutable_encrypted());
867 AddDefaultFieldValue(BOOKMARKS
, &encrypted_bookmark
);
868 modified_bookmark
.mutable_bookmark()->set_title("title2");
869 other_cryptographer
.Encrypt(modified_bookmark
,
870 modified_bookmark
.mutable_encrypted());
871 sync_pb::EntitySpecifics pref
, encrypted_pref
, modified_pref
;
872 pref
.mutable_preference()->set_name("name");
873 AddDefaultFieldValue(PREFERENCES
, &encrypted_pref
);
874 other_cryptographer
.Encrypt(pref
,
875 encrypted_pref
.mutable_encrypted());
876 modified_pref
.mutable_preference()->set_name("name2");
877 other_cryptographer
.Encrypt(modified_pref
,
878 modified_pref
.mutable_encrypted());
880 // Mark bookmarks and preferences as encrypted and set the cryptographer to
881 // have pending keys.
882 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
883 sync_pb::EntitySpecifics specifics
;
884 sync_pb::NigoriSpecifics
* nigori
= specifics
.mutable_nigori();
885 other_cryptographer
.GetKeys(nigori
->mutable_encryption_keybag());
886 dir_maker_
.encryption_handler()->EnableEncryptEverything();
887 GetCryptographer(&wtrans
)->SetPendingKeys(nigori
->encryption_keybag());
888 EXPECT_TRUE(GetCryptographer(&wtrans
)->has_pending_keys());
891 // We need to remember the exact position of our local items, so we can
892 // make updates that do not modify those positions.
897 mock_server_
->AddUpdateSpecifics(1, 0, "A", 10, 10, true, 0, bookmark
,
898 foreign_cache_guid(), "-1");
899 mock_server_
->AddUpdateSpecifics(2, 1, "B", 10, 10, false, 2, bookmark
,
900 foreign_cache_guid(), "-2");
901 mock_server_
->AddUpdateSpecifics(3, 1, "C", 10, 10, false, 1, bookmark
,
902 foreign_cache_guid(), "-3");
903 mock_server_
->AddUpdateSpecifics(4, 0, "D", 10, 10, false, 0, pref
);
906 // Initial state. Everything is normal.
907 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
908 VERIFY_ENTRY(1, false, false, false, 0, 10, 10, ids_
, &rtrans
);
909 VERIFY_ENTRY(2, false, false, false, 1, 10, 10, ids_
, &rtrans
);
910 VERIFY_ENTRY(3, false, false, false, 1, 10, 10, ids_
, &rtrans
);
911 VERIFY_ENTRY(4, false, false, false, 0, 10, 10, ids_
, &rtrans
);
913 Entry
entry1(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
914 ASSERT_TRUE(entry1
.GetUniquePosition().Equals(
915 entry1
.GetServerUniquePosition()));
916 pos1
= entry1
.GetUniquePosition();
917 Entry
entry2(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(2));
918 pos2
= entry2
.GetUniquePosition();
919 Entry
entry3(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(3));
920 pos3
= entry3
.GetUniquePosition();
923 // Server side encryption will not be applied due to undecryptable data.
924 // At this point, BASE_SERVER_SPECIFICS should be filled for all four items.
925 mock_server_
->AddUpdateSpecifics(1, 0, kEncryptedString
, 20, 20, true, 0,
927 foreign_cache_guid(), "-1");
928 mock_server_
->AddUpdateSpecifics(2, 1, kEncryptedString
, 20, 20, false, 2,
930 foreign_cache_guid(), "-2");
931 mock_server_
->AddUpdateSpecifics(3, 1, kEncryptedString
, 20, 20, false, 1,
933 foreign_cache_guid(), "-3");
934 mock_server_
->AddUpdateSpecifics(4, 0, kEncryptedString
, 20, 20, false, 0,
936 foreign_cache_guid(), "-4");
939 // All should be unapplied due to being undecryptable and have a valid
940 // BASE_SERVER_SPECIFICS.
941 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
942 VERIFY_ENTRY(1, true, false, true, 0, 10, 20, ids_
, &rtrans
);
943 VERIFY_ENTRY(2, true, false, true, 1, 10, 20, ids_
, &rtrans
);
944 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_
, &rtrans
);
945 VERIFY_ENTRY(4, true, false, true, 0, 10, 20, ids_
, &rtrans
);
948 // Server side change that don't modify anything should not affect
949 // BASE_SERVER_SPECIFICS (such as name changes and mtime changes).
950 mock_server_
->AddUpdateSpecifics(1, 0, kEncryptedString
, 30, 30, true, 0,
952 foreign_cache_guid(), "-1");
953 mock_server_
->AddUpdateSpecifics(2, 1, kEncryptedString
, 30, 30, false, 2,
955 foreign_cache_guid(), "-2");
956 // Item 3 doesn't change.
957 mock_server_
->AddUpdateSpecifics(4, 0, kEncryptedString
, 30, 30, false, 0,
959 foreign_cache_guid(), "-4");
962 // Items 1, 2, and 4 should have newer server versions, 3 remains the same.
963 // All should remain unapplied due to be undecryptable.
964 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
965 VERIFY_ENTRY(1, true, false, true, 0, 10, 30, ids_
, &rtrans
);
966 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_
, &rtrans
);
967 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_
, &rtrans
);
968 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_
, &rtrans
);
971 // Positional changes, parent changes, and specifics changes should reset
972 // BASE_SERVER_SPECIFICS.
973 // Became unencrypted.
974 mock_server_
->AddUpdateSpecifics(1, 0, "A", 40, 40, true, 0, bookmark
,
975 foreign_cache_guid(), "-1");
976 // Reordered to after item 2.
977 mock_server_
->AddUpdateSpecifics(3, 1, kEncryptedString
, 30, 30, false, 3,
979 foreign_cache_guid(), "-3");
982 // Items 2 and 4 should be the only ones with BASE_SERVER_SPECIFICS set.
983 // Items 1 is now unencrypted, so should have applied normally.
984 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
985 VERIFY_ENTRY(1, false, false, false, 0, 40, 40, ids_
, &rtrans
);
986 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_
, &rtrans
);
987 VERIFY_ENTRY(3, true, false, false, 1, 10, 30, ids_
, &rtrans
);
988 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_
, &rtrans
);
991 // Make local changes, which should remain unsynced for items 2, 3, 4.
993 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
994 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
995 ASSERT_TRUE(A
.good());
996 A
.PutSpecifics(modified_bookmark
);
997 A
.PutNonUniqueName(kEncryptedString
);
998 A
.PutIsUnsynced(true);
999 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
1000 ASSERT_TRUE(B
.good());
1001 B
.PutSpecifics(modified_bookmark
);
1002 B
.PutNonUniqueName(kEncryptedString
);
1003 B
.PutIsUnsynced(true);
1004 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
1005 ASSERT_TRUE(C
.good());
1006 C
.PutSpecifics(modified_bookmark
);
1007 C
.PutNonUniqueName(kEncryptedString
);
1008 C
.PutIsUnsynced(true);
1009 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
1010 ASSERT_TRUE(D
.good());
1011 D
.PutSpecifics(modified_pref
);
1012 D
.PutNonUniqueName(kEncryptedString
);
1013 D
.PutIsUnsynced(true);
1017 // Item 1 remains unsynced due to there being pending keys.
1018 // Items 2, 3, 4 should remain unsynced since they were not up to date.
1019 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1020 VERIFY_ENTRY(1, false, true, false, 0, 40, 40, ids_
, &rtrans
);
1021 VERIFY_ENTRY(2, true, true, true, 1, 10, 30, ids_
, &rtrans
);
1022 VERIFY_ENTRY(3, true, true, false, 1, 10, 30, ids_
, &rtrans
);
1023 VERIFY_ENTRY(4, true, true, true, 0, 10, 30, ids_
, &rtrans
);
1027 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1028 // Resolve the pending keys.
1029 GetCryptographer(&rtrans
)->DecryptPendingKeys(key_params
);
1031 // First cycle resolves conflicts, second cycle commits changes.
1033 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_server_overwrites
);
1034 EXPECT_EQ(1, GetUpdateCounters(PREFERENCES
).num_server_overwrites
);
1035 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_local_overwrites
);
1037 // We successfully commited item(s).
1038 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS
).num_commits_attempted
);
1039 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS
).num_commits_success
);
1040 EXPECT_EQ(1, GetCommitCounters(PREFERENCES
).num_commits_attempted
);
1041 EXPECT_EQ(1, GetCommitCounters(PREFERENCES
).num_commits_success
);
1045 // Everything should be resolved now. The local changes should have
1046 // overwritten the server changes for 2 and 4, while the server changes
1047 // overwrote the local for entry 3.
1049 // Expect there will be no new overwrites.
1050 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_server_overwrites
);
1051 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_local_overwrites
);
1053 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS
).num_commits_success
);
1054 EXPECT_EQ(1, GetCommitCounters(PREFERENCES
).num_commits_success
);
1056 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1057 VERIFY_ENTRY(1, false, false, false, 0, 41, 41, ids_
, &rtrans
);
1058 VERIFY_ENTRY(2, false, false, false, 1, 31, 31, ids_
, &rtrans
);
1059 VERIFY_ENTRY(3, false, false, false, 1, 30, 30, ids_
, &rtrans
);
1060 VERIFY_ENTRY(4, false, false, false, 0, 31, 31, ids_
, &rtrans
);
1065 TEST_F(SyncerTest
, TestGetUnsyncedAndSimpleCommit
) {
1067 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1068 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1069 ASSERT_TRUE(parent
.good());
1070 parent
.PutIsUnsynced(true);
1071 parent
.PutIsDir(true);
1072 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1073 parent
.PutBaseVersion(1);
1074 parent
.PutId(parent_id_
);
1075 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete");
1076 ASSERT_TRUE(child
.good());
1077 child
.PutId(child_id_
);
1078 child
.PutBaseVersion(1);
1079 WriteTestDataToEntry(&wtrans
, &child
);
1083 ASSERT_EQ(2u, mock_server_
->committed_ids().size());
1084 // If this test starts failing, be aware other sort orders could be valid.
1085 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1086 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1088 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1089 Entry
entry(&rt
, syncable::GET_BY_ID
, child_id_
);
1090 ASSERT_TRUE(entry
.good());
1091 VerifyTestDataInEntry(&rt
, &entry
);
1095 TEST_F(SyncerTest
, TestPurgeWhileUnsynced
) {
1096 // Similar to above, but throw a purge operation into the mix. Bug 49278.
1097 syncable::Id pref_node_id
= TestIdFactory::MakeServer("Tim");
1099 directory()->SetDownloadProgress(BOOKMARKS
,
1100 syncable::BuildProgress(BOOKMARKS
));
1101 directory()->SetDownloadProgress(PREFERENCES
,
1102 syncable::BuildProgress(PREFERENCES
));
1103 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1104 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1105 ASSERT_TRUE(parent
.good());
1106 parent
.PutIsUnsynced(true);
1107 parent
.PutIsDir(true);
1108 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1109 parent
.PutBaseVersion(1);
1110 parent
.PutId(parent_id_
);
1111 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete");
1112 ASSERT_TRUE(child
.good());
1113 child
.PutId(child_id_
);
1114 child
.PutBaseVersion(1);
1115 WriteTestDataToEntry(&wtrans
, &child
);
1117 MutableEntry
parent2(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Tim");
1118 ASSERT_TRUE(parent2
.good());
1119 parent2
.PutIsUnsynced(true);
1120 parent2
.PutIsDir(true);
1121 parent2
.PutSpecifics(DefaultPreferencesSpecifics());
1122 parent2
.PutBaseVersion(1);
1123 parent2
.PutId(pref_node_id
);
1126 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES
),
1131 ASSERT_EQ(2U, mock_server_
->committed_ids().size());
1132 // If this test starts failing, be aware other sort orders could be valid.
1133 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1134 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1136 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1137 Entry
entry(&rt
, syncable::GET_BY_ID
, child_id_
);
1138 ASSERT_TRUE(entry
.good());
1139 VerifyTestDataInEntry(&rt
, &entry
);
1141 directory()->SaveChanges();
1143 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1144 Entry
entry(&rt
, syncable::GET_BY_ID
, pref_node_id
);
1145 ASSERT_FALSE(entry
.good());
1149 TEST_F(SyncerTest
, TestPurgeWhileUnapplied
) {
1150 // Similar to above, but for unapplied items. Bug 49278.
1152 directory()->SetDownloadProgress(BOOKMARKS
,
1153 syncable::BuildProgress(BOOKMARKS
));
1154 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1155 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1156 ASSERT_TRUE(parent
.good());
1157 parent
.PutIsUnappliedUpdate(true);
1158 parent
.PutIsDir(true);
1159 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1160 parent
.PutBaseVersion(1);
1161 parent
.PutId(parent_id_
);
1164 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(BOOKMARKS
),
1169 directory()->SaveChanges();
1171 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1172 Entry
entry(&rt
, syncable::GET_BY_ID
, parent_id_
);
1173 ASSERT_FALSE(entry
.good());
1177 TEST_F(SyncerTest
, TestPurgeWithJournal
) {
1179 directory()->SetDownloadProgress(BOOKMARKS
,
1180 syncable::BuildProgress(BOOKMARKS
));
1181 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1182 MutableEntry
parent(&wtrans
, syncable::CREATE
, BOOKMARKS
, wtrans
.root_id(),
1184 ASSERT_TRUE(parent
.good());
1185 parent
.PutIsDir(true);
1186 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1187 parent
.PutBaseVersion(1);
1188 parent
.PutId(parent_id_
);
1189 MutableEntry
child(&wtrans
, syncable::CREATE
, BOOKMARKS
, parent_id_
,
1191 ASSERT_TRUE(child
.good());
1192 child
.PutId(child_id_
);
1193 child
.PutBaseVersion(1);
1194 WriteTestDataToEntry(&wtrans
, &child
);
1196 MutableEntry
parent2(&wtrans
, syncable::CREATE
, PREFERENCES
,
1197 wtrans
.root_id(), "Tim");
1198 ASSERT_TRUE(parent2
.good());
1199 parent2
.PutIsDir(true);
1200 parent2
.PutSpecifics(DefaultPreferencesSpecifics());
1201 parent2
.PutBaseVersion(1);
1202 parent2
.PutId(TestIdFactory::MakeServer("Tim"));
1205 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES
, BOOKMARKS
),
1206 ModelTypeSet(BOOKMARKS
),
1209 // Verify bookmark nodes are saved in delete journal but not preference
1211 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1212 syncable::DeleteJournal
* delete_journal
= directory()->delete_journal();
1213 EXPECT_EQ(2u, delete_journal
->GetDeleteJournalSize(&rt
));
1214 syncable::EntryKernelSet journal_entries
;
1215 directory()->delete_journal()->GetDeleteJournals(&rt
, BOOKMARKS
,
1217 EXPECT_EQ(parent_id_
, (*journal_entries
.begin())->ref(syncable::ID
));
1218 EXPECT_EQ(child_id_
, (*journal_entries
.rbegin())->ref(syncable::ID
));
1222 TEST_F(SyncerTest
, ResetVersions
) {
1223 // Download the top level pref node and some pref items.
1224 mock_server_
->AddUpdateDirectory(
1225 parent_id_
, root_id_
, "prefs", 1, 10, std::string(), std::string());
1226 mock_server_
->SetLastUpdateServerTag(ModelTypeToRootTag(PREFERENCES
));
1227 mock_server_
->AddUpdatePref("id1", parent_id_
.GetServerId(), "tag1", 20, 20);
1228 mock_server_
->AddUpdatePref("id2", parent_id_
.GetServerId(), "tag2", 30, 30);
1229 mock_server_
->AddUpdatePref("id3", parent_id_
.GetServerId(), "tag3", 40, 40);
1233 // Modify one of the preferences locally, mark another one as unapplied,
1234 // and create another unsynced preference.
1235 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1236 MutableEntry
entry(&wtrans
, GET_BY_CLIENT_TAG
, "tag1");
1237 entry
.PutIsUnsynced(true);
1239 MutableEntry
entry2(&wtrans
, GET_BY_CLIENT_TAG
, "tag2");
1240 entry2
.PutIsUnappliedUpdate(true);
1242 MutableEntry
entry4(&wtrans
, CREATE
, PREFERENCES
, parent_id_
, "name");
1243 entry4
.PutUniqueClientTag("tag4");
1244 entry4
.PutIsUnsynced(true);
1248 // Reset the versions.
1249 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1250 ASSERT_TRUE(directory()->ResetVersionsForType(&wtrans
, PREFERENCES
));
1254 // Verify the synced items are all with version 1 now, with
1255 // unsynced/unapplied state preserved.
1256 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1257 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, "tag1");
1258 EXPECT_EQ(1, entry
.GetBaseVersion());
1259 EXPECT_EQ(1, entry
.GetServerVersion());
1260 EXPECT_TRUE(entry
.GetIsUnsynced());
1261 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
1262 Entry
entry2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
1263 EXPECT_EQ(1, entry2
.GetBaseVersion());
1264 EXPECT_EQ(1, entry2
.GetServerVersion());
1265 EXPECT_FALSE(entry2
.GetIsUnsynced());
1266 EXPECT_TRUE(entry2
.GetIsUnappliedUpdate());
1267 Entry
entry3(&trans
, GET_BY_CLIENT_TAG
, "tag3");
1268 EXPECT_EQ(1, entry3
.GetBaseVersion());
1269 EXPECT_EQ(1, entry3
.GetServerVersion());
1270 EXPECT_FALSE(entry3
.GetIsUnsynced());
1271 EXPECT_FALSE(entry3
.GetIsUnappliedUpdate());
1273 // Entry 4 (the locally created one) should remain the same.
1274 Entry
entry4(&trans
, GET_BY_CLIENT_TAG
, "tag4");
1275 EXPECT_EQ(-1, entry4
.GetBaseVersion());
1276 EXPECT_EQ(0, entry4
.GetServerVersion());
1277 EXPECT_TRUE(entry4
.GetIsUnsynced());
1278 EXPECT_FALSE(entry4
.GetIsUnappliedUpdate());
1282 TEST_F(SyncerTest
, TestCommitListOrderingTwoItemsTall
) {
1283 CommitOrderingTest items
[] = {
1284 {1, ids_
.FromNumber(-1001), ids_
.FromNumber(-1000)},
1285 {0, ids_
.FromNumber(-1000), ids_
.FromNumber(0)},
1286 CommitOrderingTest::MakeLastCommitItem(),
1288 RunCommitOrderingTest(items
);
1291 TEST_F(SyncerTest
, TestCommitListOrderingThreeItemsTall
) {
1292 CommitOrderingTest items
[] = {
1293 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1294 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1295 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1296 CommitOrderingTest::MakeLastCommitItem(),
1298 RunCommitOrderingTest(items
);
1301 TEST_F(SyncerTest
, TestCommitListOrderingFourItemsTall
) {
1302 CommitOrderingTest items
[] = {
1303 {3, ids_
.FromNumber(-2003), ids_
.FromNumber(-2002)},
1304 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1305 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1306 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1307 CommitOrderingTest::MakeLastCommitItem(),
1309 RunCommitOrderingTest(items
);
1312 TEST_F(SyncerTest
, TestCommitListOrderingThreeItemsTallLimitedSize
) {
1313 context_
->set_max_commit_batch_size(2);
1314 CommitOrderingTest items
[] = {
1315 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1316 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1317 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1318 CommitOrderingTest::MakeLastCommitItem(),
1320 RunCommitOrderingTest(items
);
1323 TEST_F(SyncerTest
, TestCommitListOrderingSingleDeletedItem
) {
1324 CommitOrderingTest items
[] = {
1325 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1326 CommitOrderingTest::MakeLastCommitItem(),
1328 RunCommitOrderingTest(items
);
1331 TEST_F(SyncerTest
, TestCommitListOrderingSingleUncommittedDeletedItem
) {
1332 CommitOrderingTest items
[] = {
1333 {-1, ids_
.FromNumber(-1000), ids_
.FromNumber(0), {DELETED
}},
1334 CommitOrderingTest::MakeLastCommitItem(),
1336 RunCommitOrderingTest(items
);
1339 TEST_F(SyncerTest
, TestCommitListOrderingSingleDeletedItemWithUnroll
) {
1340 CommitOrderingTest items
[] = {
1341 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1342 CommitOrderingTest::MakeLastCommitItem(),
1344 RunCommitOrderingTest(items
);
1348 TestCommitListOrderingSingleLongDeletedItemWithUnroll
) {
1349 CommitOrderingTest items
[] = {
1350 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1351 CommitOrderingTest::MakeLastCommitItem(),
1353 RunCommitOrderingTest(items
);
1356 TEST_F(SyncerTest
, TestCommitListOrderingTwoLongDeletedItemWithUnroll
) {
1357 CommitOrderingTest items
[] = {
1358 {1, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1359 {0, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
, OLD_MTIME
}},
1360 CommitOrderingTest::MakeLastCommitItem(),
1362 RunCommitOrderingTest(items
);
1365 TEST_F(SyncerTest
, TestCommitListOrdering3LongDeletedItemsWithSizeLimit
) {
1366 context_
->set_max_commit_batch_size(2);
1367 CommitOrderingTest items
[] = {
1368 {2, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1369 {1, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
, OLD_MTIME
}},
1370 {0, ids_
.FromNumber(1002), ids_
.FromNumber(1001), {DELETED
, OLD_MTIME
}},
1371 CommitOrderingTest::MakeLastCommitItem(),
1373 RunCommitOrderingTest(items
);
1376 TEST_F(SyncerTest
, TestCommitListOrderingTwoDeletedItemsWithUnroll
) {
1377 CommitOrderingTest items
[] = {
1378 {1, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1379 {0, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
}},
1380 CommitOrderingTest::MakeLastCommitItem(),
1382 RunCommitOrderingTest(items
);
1385 TEST_F(SyncerTest
, TestCommitListOrderingComplexDeletionScenario
) {
1386 CommitOrderingTest items
[] = {
1387 {2, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1388 {-1, ids_
.FromNumber(1001), ids_
.FromNumber(0), {SYNCED
}},
1389 {1, ids_
.FromNumber(1002), ids_
.FromNumber(1001), {DELETED
, OLD_MTIME
}},
1390 {-1, ids_
.FromNumber(1003), ids_
.FromNumber(1001), {SYNCED
}},
1391 {0, ids_
.FromNumber(1004), ids_
.FromNumber(1003), {DELETED
}},
1392 CommitOrderingTest::MakeLastCommitItem(),
1394 RunCommitOrderingTest(items
);
1398 TestCommitListOrderingComplexDeletionScenarioWith2RecentDeletes
) {
1399 CommitOrderingTest items
[] = {
1400 {3, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1401 {-1, ids_
.FromNumber(1001), ids_
.FromNumber(0), {SYNCED
}},
1402 {2, ids_
.FromNumber(1002), ids_
.FromNumber(1001), {DELETED
, OLD_MTIME
}},
1403 {-1, ids_
.FromNumber(1003), ids_
.FromNumber(1001), {SYNCED
}},
1404 {1, ids_
.FromNumber(1004), ids_
.FromNumber(1003), {DELETED
}},
1405 {0, ids_
.FromNumber(1005), ids_
.FromNumber(1003), {DELETED
}},
1406 CommitOrderingTest::MakeLastCommitItem(),
1408 RunCommitOrderingTest(items
);
1411 TEST_F(SyncerTest
, TestCommitListOrderingDeleteMovedItems
) {
1412 CommitOrderingTest items
[] = {
1413 {1, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1414 {0, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
, OLD_MTIME
,
1416 CommitOrderingTest::MakeLastCommitItem(),
1418 RunCommitOrderingTest(items
);
1421 TEST_F(SyncerTest
, TestCommitListOrderingWithNesting
) {
1422 const base::Time
& now_minus_2h
=
1423 base::Time::Now() - base::TimeDelta::FromHours(2);
1425 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1427 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bob");
1428 ASSERT_TRUE(parent
.good());
1429 parent
.PutIsUnsynced(true);
1430 parent
.PutIsDir(true);
1431 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1432 parent
.PutId(ids_
.FromNumber(100));
1433 parent
.PutBaseVersion(1);
1435 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(100), "Bob");
1436 ASSERT_TRUE(child
.good());
1437 child
.PutIsUnsynced(true);
1438 child
.PutIsDir(true);
1439 child
.PutSpecifics(DefaultBookmarkSpecifics());
1440 child
.PutId(ids_
.FromNumber(101));
1441 child
.PutBaseVersion(1);
1442 MutableEntry
grandchild(
1443 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(101), "Bob");
1444 ASSERT_TRUE(grandchild
.good());
1445 grandchild
.PutId(ids_
.FromNumber(102));
1446 grandchild
.PutIsUnsynced(true);
1447 grandchild
.PutSpecifics(DefaultBookmarkSpecifics());
1448 grandchild
.PutBaseVersion(1);
1451 // Create three deleted items which deletions we expect to be sent to the
1453 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1454 ASSERT_TRUE(parent
.good());
1455 parent
.PutId(ids_
.FromNumber(103));
1456 parent
.PutIsUnsynced(true);
1457 parent
.PutIsDir(true);
1458 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1459 parent
.PutIsDel(true);
1460 parent
.PutBaseVersion(1);
1461 parent
.PutMtime(now_minus_2h
);
1463 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(103), "Pete");
1464 ASSERT_TRUE(child
.good());
1465 child
.PutId(ids_
.FromNumber(104));
1466 child
.PutIsUnsynced(true);
1467 child
.PutIsDir(true);
1468 child
.PutSpecifics(DefaultBookmarkSpecifics());
1469 child
.PutIsDel(true);
1470 child
.PutBaseVersion(1);
1471 child
.PutMtime(now_minus_2h
);
1472 MutableEntry
grandchild(
1473 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(104), "Pete");
1474 ASSERT_TRUE(grandchild
.good());
1475 grandchild
.PutId(ids_
.FromNumber(105));
1476 grandchild
.PutIsUnsynced(true);
1477 grandchild
.PutIsDel(true);
1478 grandchild
.PutIsDir(false);
1479 grandchild
.PutSpecifics(DefaultBookmarkSpecifics());
1480 grandchild
.PutBaseVersion(1);
1481 grandchild
.PutMtime(now_minus_2h
);
1486 ASSERT_EQ(6u, mock_server_
->committed_ids().size());
1487 // This test will NOT unroll deletes because SERVER_PARENT_ID is not set.
1488 // It will treat these like moves.
1489 vector
<syncable::Id
> commit_ids(mock_server_
->committed_ids());
1490 EXPECT_TRUE(ids_
.FromNumber(100) == commit_ids
[0]);
1491 EXPECT_TRUE(ids_
.FromNumber(101) == commit_ids
[1]);
1492 EXPECT_TRUE(ids_
.FromNumber(102) == commit_ids
[2]);
1493 // We don't guarantee the delete orders in this test, only that they occur
1495 std::sort(commit_ids
.begin() + 3, commit_ids
.end());
1496 EXPECT_TRUE(ids_
.FromNumber(103) == commit_ids
[3]);
1497 EXPECT_TRUE(ids_
.FromNumber(104) == commit_ids
[4]);
1498 EXPECT_TRUE(ids_
.FromNumber(105) == commit_ids
[5]);
1501 TEST_F(SyncerTest
, TestCommitListOrderingWithNewItems
) {
1502 syncable::Id parent1_id
= ids_
.MakeServer("p1");
1503 syncable::Id parent2_id
= ids_
.MakeServer("p2");
1506 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1507 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "1");
1508 ASSERT_TRUE(parent
.good());
1509 parent
.PutIsUnsynced(true);
1510 parent
.PutIsDir(true);
1511 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1512 parent
.PutId(parent1_id
);
1513 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "2");
1514 ASSERT_TRUE(child
.good());
1515 child
.PutIsUnsynced(true);
1516 child
.PutIsDir(true);
1517 child
.PutSpecifics(DefaultBookmarkSpecifics());
1518 child
.PutId(parent2_id
);
1519 parent
.PutBaseVersion(1);
1520 child
.PutBaseVersion(1);
1523 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1524 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, parent1_id
, "A");
1525 ASSERT_TRUE(parent
.good());
1526 parent
.PutIsUnsynced(true);
1527 parent
.PutIsDir(true);
1528 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1529 parent
.PutId(ids_
.FromNumber(102));
1530 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent1_id
, "B");
1531 ASSERT_TRUE(child
.good());
1532 child
.PutIsUnsynced(true);
1533 child
.PutIsDir(true);
1534 child
.PutSpecifics(DefaultBookmarkSpecifics());
1535 child
.PutId(ids_
.FromNumber(-103));
1536 parent
.PutBaseVersion(1);
1539 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1540 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, parent2_id
, "A");
1541 ASSERT_TRUE(parent
.good());
1542 parent
.PutIsUnsynced(true);
1543 parent
.PutIsDir(true);
1544 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1545 parent
.PutId(ids_
.FromNumber(-104));
1546 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent2_id
, "B");
1547 ASSERT_TRUE(child
.good());
1548 child
.PutIsUnsynced(true);
1549 child
.PutIsDir(true);
1550 child
.PutSpecifics(DefaultBookmarkSpecifics());
1551 child
.PutId(ids_
.FromNumber(105));
1552 child
.PutBaseVersion(1);
1556 ASSERT_EQ(6u, mock_server_
->committed_ids().size());
1558 // This strange iteration and std::count() usage is to allow the order to
1559 // vary. All we really care about is that parent1_id and parent2_id are the
1560 // first two IDs, and that the children make up the next four. Other than
1561 // that, ordering doesn't matter.
1563 vector
<syncable::Id
>::const_iterator i
=
1564 mock_server_
->committed_ids().begin();
1565 vector
<syncable::Id
>::const_iterator parents_begin
= i
;
1568 vector
<syncable::Id
>::const_iterator parents_end
= i
;
1569 vector
<syncable::Id
>::const_iterator children_begin
= i
;
1570 vector
<syncable::Id
>::const_iterator children_end
=
1571 mock_server_
->committed_ids().end();
1573 EXPECT_EQ(1, count(parents_begin
, parents_end
, parent1_id
));
1574 EXPECT_EQ(1, count(parents_begin
, parents_end
, parent2_id
));
1576 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(-103)));
1577 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(102)));
1578 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(105)));
1579 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(-104)));
1582 TEST_F(SyncerTest
, TestCommitListOrderingCounterexample
) {
1583 syncable::Id child2_id
= ids_
.NewServerId();
1586 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1587 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "P");
1588 ASSERT_TRUE(parent
.good());
1589 parent
.PutIsUnsynced(true);
1590 parent
.PutIsDir(true);
1591 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1592 parent
.PutId(parent_id_
);
1593 MutableEntry
child1(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "1");
1594 ASSERT_TRUE(child1
.good());
1595 child1
.PutIsUnsynced(true);
1596 child1
.PutId(child_id_
);
1597 child1
.PutSpecifics(DefaultBookmarkSpecifics());
1598 MutableEntry
child2(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "2");
1599 ASSERT_TRUE(child2
.good());
1600 child2
.PutIsUnsynced(true);
1601 child2
.PutSpecifics(DefaultBookmarkSpecifics());
1602 child2
.PutId(child2_id
);
1604 parent
.PutBaseVersion(1);
1605 child1
.PutBaseVersion(1);
1606 child2
.PutBaseVersion(1);
1610 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1611 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1612 // There are two possible valid orderings.
1613 if (child2_id
== mock_server_
->committed_ids()[1]) {
1614 EXPECT_TRUE(child2_id
== mock_server_
->committed_ids()[1]);
1615 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[2]);
1617 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1618 EXPECT_TRUE(child2_id
== mock_server_
->committed_ids()[2]);
1622 TEST_F(SyncerTest
, TestCommitListOrderingAndNewParent
) {
1623 string parent1_name
= "1";
1624 string parent2_name
= "A";
1625 string child_name
= "B";
1628 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1629 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(),
1631 ASSERT_TRUE(parent
.good());
1632 parent
.PutIsUnsynced(true);
1633 parent
.PutIsDir(true);
1634 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1635 parent
.PutId(parent_id_
);
1636 parent
.PutBaseVersion(1);
1639 syncable::Id parent2_id
= ids_
.NewLocalId();
1640 syncable::Id child_id
= ids_
.NewServerId();
1642 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1643 MutableEntry
parent2(
1644 &wtrans
, CREATE
, BOOKMARKS
, parent_id_
, parent2_name
);
1645 ASSERT_TRUE(parent2
.good());
1646 parent2
.PutIsUnsynced(true);
1647 parent2
.PutIsDir(true);
1648 parent2
.PutSpecifics(DefaultBookmarkSpecifics());
1649 parent2
.PutId(parent2_id
);
1652 &wtrans
, CREATE
, BOOKMARKS
, parent2_id
, child_name
);
1653 ASSERT_TRUE(child
.good());
1654 child
.PutIsUnsynced(true);
1655 child
.PutIsDir(true);
1656 child
.PutSpecifics(DefaultBookmarkSpecifics());
1657 child
.PutId(child_id
);
1658 child
.PutBaseVersion(1);
1662 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1663 // If this test starts failing, be aware other sort orders could be valid.
1664 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1665 EXPECT_TRUE(parent2_id
== mock_server_
->committed_ids()[1]);
1666 EXPECT_TRUE(child_id
== mock_server_
->committed_ids()[2]);
1668 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1669 // Check that things committed correctly.
1670 Entry
entry_1(&rtrans
, syncable::GET_BY_ID
, parent_id_
);
1671 EXPECT_EQ(entry_1
.GetNonUniqueName(), parent1_name
);
1672 // Check that parent2 is a subfolder of parent1.
1673 EXPECT_EQ(1, CountEntriesWithName(&rtrans
,
1677 // Parent2 was a local ID and thus should have changed on commit!
1678 Entry
pre_commit_entry_parent2(&rtrans
, syncable::GET_BY_ID
, parent2_id
);
1679 ASSERT_FALSE(pre_commit_entry_parent2
.good());
1681 // Look up the new ID.
1682 Id parent2_committed_id
=
1683 GetOnlyEntryWithName(&rtrans
, parent_id_
, parent2_name
);
1684 EXPECT_TRUE(parent2_committed_id
.ServerKnows());
1686 Entry
child(&rtrans
, syncable::GET_BY_ID
, child_id
);
1687 EXPECT_EQ(parent2_committed_id
, child
.GetParentId());
1691 TEST_F(SyncerTest
, TestCommitListOrderingAndNewParentAndChild
) {
1692 string parent_name
= "1";
1693 string parent2_name
= "A";
1694 string child_name
= "B";
1697 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1698 MutableEntry
parent(&wtrans
,
1702 ASSERT_TRUE(parent
.good());
1703 parent
.PutIsUnsynced(true);
1704 parent
.PutIsDir(true);
1705 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1706 parent
.PutId(parent_id_
);
1707 parent
.PutBaseVersion(1);
1710 int64 meta_handle_b
;
1711 const Id parent2_local_id
= ids_
.NewLocalId();
1712 const Id child_local_id
= ids_
.NewLocalId();
1714 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1715 MutableEntry
parent2(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, parent2_name
);
1716 ASSERT_TRUE(parent2
.good());
1717 parent2
.PutIsUnsynced(true);
1718 parent2
.PutIsDir(true);
1719 parent2
.PutSpecifics(DefaultBookmarkSpecifics());
1721 parent2
.PutId(parent2_local_id
);
1723 &wtrans
, CREATE
, BOOKMARKS
, parent2_local_id
, child_name
);
1724 ASSERT_TRUE(child
.good());
1725 child
.PutIsUnsynced(true);
1726 child
.PutIsDir(true);
1727 child
.PutSpecifics(DefaultBookmarkSpecifics());
1728 child
.PutId(child_local_id
);
1729 meta_handle_b
= child
.GetMetahandle();
1733 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1734 // If this test starts failing, be aware other sort orders could be valid.
1735 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1736 EXPECT_TRUE(parent2_local_id
== mock_server_
->committed_ids()[1]);
1737 EXPECT_TRUE(child_local_id
== mock_server_
->committed_ids()[2]);
1739 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1741 Entry
parent(&rtrans
, syncable::GET_BY_ID
,
1742 GetOnlyEntryWithName(&rtrans
, rtrans
.root_id(), parent_name
));
1743 ASSERT_TRUE(parent
.good());
1744 EXPECT_TRUE(parent
.GetId().ServerKnows());
1746 Entry
parent2(&rtrans
, syncable::GET_BY_ID
,
1747 GetOnlyEntryWithName(&rtrans
, parent
.GetId(), parent2_name
));
1748 ASSERT_TRUE(parent2
.good());
1749 EXPECT_TRUE(parent2
.GetId().ServerKnows());
1751 // Id changed on commit, so this should fail.
1752 Entry
local_parent2_id_entry(&rtrans
,
1753 syncable::GET_BY_ID
,
1755 ASSERT_FALSE(local_parent2_id_entry
.good());
1757 Entry
entry_b(&rtrans
, syncable::GET_BY_HANDLE
, meta_handle_b
);
1758 EXPECT_TRUE(entry_b
.GetId().ServerKnows());
1759 EXPECT_TRUE(parent2
.GetId()== entry_b
.GetParentId());
1763 TEST_F(SyncerTest
, UpdateWithZeroLengthName
) {
1764 // One illegal update
1765 mock_server_
->AddUpdateDirectory(
1766 1, 0, std::string(), 1, 10, foreign_cache_guid(), "-1");
1767 // And one legal one that we're going to delete.
1768 mock_server_
->AddUpdateDirectory(2, 0, "FOO", 1, 10,
1769 foreign_cache_guid(), "-2");
1771 // Delete the legal one. The new update has a null name.
1772 mock_server_
->AddUpdateDirectory(
1773 2, 0, std::string(), 2, 20, foreign_cache_guid(), "-2");
1774 mock_server_
->SetLastUpdateDeleted();
1778 TEST_F(SyncerTest
, TestBasicUpdate
) {
1779 string id
= "some_id";
1780 string parent_id
= "0";
1781 string name
= "in_root";
1783 int64 timestamp
= 10;
1784 mock_server_
->AddUpdateDirectory(id
, parent_id
, name
, version
, timestamp
,
1785 foreign_cache_guid(), "-1");
1789 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1790 Entry
entry(&trans
, GET_BY_ID
,
1791 syncable::Id::CreateFromServerId("some_id"));
1792 ASSERT_TRUE(entry
.good());
1793 EXPECT_TRUE(entry
.GetIsDir());
1794 EXPECT_TRUE(entry
.GetServerVersion()== version
);
1795 EXPECT_TRUE(entry
.GetBaseVersion()== version
);
1796 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
1797 EXPECT_FALSE(entry
.GetIsUnsynced());
1798 EXPECT_FALSE(entry
.GetServerIsDel());
1799 EXPECT_FALSE(entry
.GetIsDel());
1803 TEST_F(SyncerTest
, IllegalAndLegalUpdates
) {
1804 Id root
= TestIdFactory::root();
1805 // Should apply just fine.
1806 mock_server_
->AddUpdateDirectory(1, 0, "in_root", 10, 10,
1807 foreign_cache_guid(), "-1");
1809 // Same name. But this SHOULD work.
1810 mock_server_
->AddUpdateDirectory(2, 0, "in_root", 10, 10,
1811 foreign_cache_guid(), "-2");
1813 // Unknown parent: should never be applied. "-80" is a legal server ID,
1814 // because any string sent by the server is a legal server ID in the sync
1815 // protocol, but it's not the ID of any item known to the client. This
1816 // update should succeed validation, but be stuck in the unapplied state
1817 // until an item with the server ID "-80" arrives.
1818 mock_server_
->AddUpdateDirectory(3, -80, "bad_parent", 10, 10,
1819 foreign_cache_guid(), "-3");
1823 // Id 3 should be in conflict now.
1826 GetUpdateCounters(BOOKMARKS
).num_hierarchy_conflict_application_failures
);
1828 // The only request in that loop should have been a GetUpdate.
1829 // At that point, we didn't know whether or not we had conflicts.
1830 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
1831 VerifyHierarchyConflictsUnspecified(mock_server_
->last_request());
1833 // These entries will be used in the second set of updates.
1834 mock_server_
->AddUpdateDirectory(4, 0, "newer_version", 20, 10,
1835 foreign_cache_guid(), "-4");
1836 mock_server_
->AddUpdateDirectory(5, 0, "circular1", 10, 10,
1837 foreign_cache_guid(), "-5");
1838 mock_server_
->AddUpdateDirectory(6, 5, "circular2", 10, 10,
1839 foreign_cache_guid(), "-6");
1840 mock_server_
->AddUpdateDirectory(9, 3, "bad_parent_child", 10, 10,
1841 foreign_cache_guid(), "-9");
1842 mock_server_
->AddUpdateDirectory(100, 9, "bad_parent_child2", 10, 10,
1843 foreign_cache_guid(), "-100");
1844 mock_server_
->AddUpdateDirectory(10, 0, "dir_to_bookmark", 10, 10,
1845 foreign_cache_guid(), "-10");
1848 // The three items with an unresolved parent should be unapplied (3, 9, 100).
1849 // The name clash should also still be in conflict.
1852 GetUpdateCounters(BOOKMARKS
).num_hierarchy_conflict_application_failures
);
1854 // This time around, we knew that there were conflicts.
1855 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
1856 VerifyHierarchyConflictsReported(mock_server_
->last_request());
1859 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1860 // Even though it has the same name, it should work.
1861 Entry
name_clash(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
1862 ASSERT_TRUE(name_clash
.good());
1863 EXPECT_FALSE(name_clash
.GetIsUnappliedUpdate())
1864 << "Duplicate name SHOULD be OK.";
1866 Entry
bad_parent(&trans
, GET_BY_ID
, ids_
.FromNumber(3));
1867 ASSERT_TRUE(bad_parent
.good());
1868 EXPECT_TRUE(bad_parent
.GetIsUnappliedUpdate())
1869 << "child of unknown parent should be in conflict";
1871 Entry
bad_parent_child(&trans
, GET_BY_ID
, ids_
.FromNumber(9));
1872 ASSERT_TRUE(bad_parent_child
.good());
1873 EXPECT_TRUE(bad_parent_child
.GetIsUnappliedUpdate())
1874 << "grandchild of unknown parent should be in conflict";
1876 Entry
bad_parent_child2(&trans
, GET_BY_ID
, ids_
.FromNumber(100));
1877 ASSERT_TRUE(bad_parent_child2
.good());
1878 EXPECT_TRUE(bad_parent_child2
.GetIsUnappliedUpdate())
1879 << "great-grandchild of unknown parent should be in conflict";
1882 // Updating 1 should not affect item 2 of the same name.
1883 mock_server_
->AddUpdateDirectory(1, 0, "new_name", 20, 20,
1884 foreign_cache_guid(), "-1");
1886 // Moving 5 under 6 will create a cycle: a conflict.
1887 mock_server_
->AddUpdateDirectory(5, 6, "circular3", 20, 20,
1888 foreign_cache_guid(), "-5");
1890 // Flip the is_dir bit: should fail verify & be dropped.
1891 mock_server_
->AddUpdateBookmark(10, 0, "dir_to_bookmark", 20, 20,
1892 foreign_cache_guid(), "-10");
1895 // Version number older than last known: should fail verify & be dropped.
1896 mock_server_
->AddUpdateDirectory(4, 0, "old_version", 10, 10,
1897 foreign_cache_guid(), "-4");
1900 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1902 Entry
still_a_dir(&trans
, GET_BY_ID
, ids_
.FromNumber(10));
1903 ASSERT_TRUE(still_a_dir
.good());
1904 EXPECT_FALSE(still_a_dir
.GetIsUnappliedUpdate());
1905 EXPECT_EQ(10u, still_a_dir
.GetBaseVersion());
1906 EXPECT_EQ(10u, still_a_dir
.GetServerVersion());
1907 EXPECT_TRUE(still_a_dir
.GetIsDir());
1909 Entry
rename(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
1910 ASSERT_TRUE(rename
.good());
1911 EXPECT_EQ(root
, rename
.GetParentId());
1912 EXPECT_EQ("new_name", rename
.GetNonUniqueName());
1913 EXPECT_FALSE(rename
.GetIsUnappliedUpdate());
1914 EXPECT_TRUE(ids_
.FromNumber(1) == rename
.GetId());
1915 EXPECT_EQ(20u, rename
.GetBaseVersion());
1917 Entry
name_clash(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
1918 ASSERT_TRUE(name_clash
.good());
1919 EXPECT_EQ(root
, name_clash
.GetParentId());
1920 EXPECT_TRUE(ids_
.FromNumber(2) == name_clash
.GetId());
1921 EXPECT_EQ(10u, name_clash
.GetBaseVersion());
1922 EXPECT_EQ("in_root", name_clash
.GetNonUniqueName());
1924 Entry
ignored_old_version(&trans
, GET_BY_ID
, ids_
.FromNumber(4));
1925 ASSERT_TRUE(ignored_old_version
.good());
1927 ignored_old_version
.GetNonUniqueName()== "newer_version");
1928 EXPECT_FALSE(ignored_old_version
.GetIsUnappliedUpdate());
1929 EXPECT_EQ(20u, ignored_old_version
.GetBaseVersion());
1931 Entry
circular_parent_issue(&trans
, GET_BY_ID
, ids_
.FromNumber(5));
1932 ASSERT_TRUE(circular_parent_issue
.good());
1933 EXPECT_TRUE(circular_parent_issue
.GetIsUnappliedUpdate())
1934 << "circular move should be in conflict";
1935 EXPECT_TRUE(circular_parent_issue
.GetParentId()== root_id_
);
1936 EXPECT_TRUE(circular_parent_issue
.GetServerParentId()==
1937 ids_
.FromNumber(6));
1938 EXPECT_EQ(10u, circular_parent_issue
.GetBaseVersion());
1940 Entry
circular_parent_target(&trans
, GET_BY_ID
, ids_
.FromNumber(6));
1941 ASSERT_TRUE(circular_parent_target
.good());
1942 EXPECT_FALSE(circular_parent_target
.GetIsUnappliedUpdate());
1943 EXPECT_TRUE(circular_parent_issue
.GetId()==
1944 circular_parent_target
.GetParentId());
1945 EXPECT_EQ(10u, circular_parent_target
.GetBaseVersion());
1948 EXPECT_FALSE(saw_syncer_event_
);
1951 GetUpdateCounters(BOOKMARKS
).num_hierarchy_conflict_application_failures
);
1954 // A commit with a lost response produces an update that has to be reunited with
1956 TEST_F(SyncerTest
, CommitReuniteUpdateAdjustsChildren
) {
1957 // Create a folder in the root.
1958 int64 metahandle_folder
;
1960 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1962 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_folder");
1963 ASSERT_TRUE(entry
.good());
1964 entry
.PutIsDir(true);
1965 entry
.PutSpecifics(DefaultBookmarkSpecifics());
1966 entry
.PutIsUnsynced(true);
1967 metahandle_folder
= entry
.GetMetahandle();
1970 // Verify it and pull the ID out of the folder.
1971 syncable::Id folder_id
;
1972 int64 metahandle_entry
;
1974 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1975 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_folder
);
1976 ASSERT_TRUE(entry
.good());
1977 folder_id
= entry
.GetId();
1978 ASSERT_TRUE(!folder_id
.ServerKnows());
1981 // Create an entry in the newly created folder.
1983 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1984 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, folder_id
, "new_entry");
1985 ASSERT_TRUE(entry
.good());
1986 metahandle_entry
= entry
.GetMetahandle();
1987 WriteTestDataToEntry(&trans
, &entry
);
1990 // Verify it and pull the ID out of the entry.
1991 syncable::Id entry_id
;
1993 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1994 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, metahandle_entry
);
1995 ASSERT_TRUE(entry
.good());
1996 EXPECT_EQ(folder_id
, entry
.GetParentId());
1997 EXPECT_EQ("new_entry", entry
.GetNonUniqueName());
1998 entry_id
= entry
.GetId();
1999 EXPECT_TRUE(!entry_id
.ServerKnows());
2000 VerifyTestDataInEntry(&trans
, &entry
);
2003 // Now, to emulate a commit response failure, we just don't commit it.
2004 int64 new_version
= 150; // any larger value.
2005 int64 timestamp
= 20; // arbitrary value.
2006 syncable::Id new_folder_id
=
2007 syncable::Id::CreateFromServerId("folder_server_id");
2009 // The following update should cause the folder to both apply the update, as
2010 // well as reassociate the id.
2011 mock_server_
->AddUpdateDirectory(new_folder_id
, root_id_
,
2012 "new_folder", new_version
, timestamp
,
2013 local_cache_guid(), folder_id
.GetServerId());
2015 // We don't want it accidentally committed, just the update applied.
2016 mock_server_
->set_conflict_all_commits(true);
2018 // Alright! Apply that update!
2021 // The folder's ID should have been updated.
2022 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2023 Entry
folder(&trans
, GET_BY_HANDLE
, metahandle_folder
);
2024 ASSERT_TRUE(folder
.good());
2025 EXPECT_EQ("new_folder", folder
.GetNonUniqueName());
2026 EXPECT_TRUE(new_version
== folder
.GetBaseVersion());
2027 EXPECT_TRUE(new_folder_id
== folder
.GetId());
2028 EXPECT_TRUE(folder
.GetId().ServerKnows());
2029 EXPECT_EQ(trans
.root_id(), folder
.GetParentId());
2031 // Since it was updated, the old folder should not exist.
2032 Entry
old_dead_folder(&trans
, GET_BY_ID
, folder_id
);
2033 EXPECT_FALSE(old_dead_folder
.good());
2035 // The child's parent should have changed.
2036 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, metahandle_entry
);
2037 ASSERT_TRUE(entry
.good());
2038 EXPECT_EQ("new_entry", entry
.GetNonUniqueName());
2039 EXPECT_EQ(new_folder_id
, entry
.GetParentId());
2040 EXPECT_TRUE(!entry
.GetId().ServerKnows());
2041 VerifyTestDataInEntry(&trans
, &entry
);
2045 // A commit with a lost response produces an update that has to be reunited with
2047 TEST_F(SyncerTest
, CommitReuniteUpdate
) {
2048 // Create an entry in the root.
2049 int64 entry_metahandle
;
2051 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2052 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_entry");
2053 ASSERT_TRUE(entry
.good());
2054 entry_metahandle
= entry
.GetMetahandle();
2055 WriteTestDataToEntry(&trans
, &entry
);
2058 // Verify it and pull the ID out.
2059 syncable::Id entry_id
;
2061 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2063 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2064 ASSERT_TRUE(entry
.good());
2065 entry_id
= entry
.GetId();
2066 EXPECT_TRUE(!entry_id
.ServerKnows());
2067 VerifyTestDataInEntry(&trans
, &entry
);
2070 // Now, to emulate a commit response failure, we just don't commit it.
2071 int64 new_version
= 150; // any larger value.
2072 int64 timestamp
= 20; // arbitrary value.
2073 syncable::Id new_entry_id
= syncable::Id::CreateFromServerId("server_id");
2075 // Generate an update from the server with a relevant ID reassignment.
2076 mock_server_
->AddUpdateBookmark(new_entry_id
, root_id_
,
2077 "new_entry", new_version
, timestamp
,
2078 local_cache_guid(), entry_id
.GetServerId());
2080 // We don't want it accidentally committed, just the update applied.
2081 mock_server_
->set_conflict_all_commits(true);
2083 // Alright! Apply that update!
2086 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2087 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2088 ASSERT_TRUE(entry
.good());
2089 EXPECT_TRUE(new_version
== entry
.GetBaseVersion());
2090 EXPECT_TRUE(new_entry_id
== entry
.GetId());
2091 EXPECT_EQ("new_entry", entry
.GetNonUniqueName());
2095 // A commit with a lost response must work even if the local entry was deleted
2096 // before the update is applied. We should not duplicate the local entry in
2097 // this case, but just create another one alongside. We may wish to examine
2098 // this behavior in the future as it can create hanging uploads that never
2099 // finish, that must be cleaned up on the server side after some time.
2100 TEST_F(SyncerTest
, CommitReuniteUpdateDoesNotChokeOnDeletedLocalEntry
) {
2101 // Create a entry in the root.
2102 int64 entry_metahandle
;
2104 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2105 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_entry");
2106 ASSERT_TRUE(entry
.good());
2107 entry_metahandle
= entry
.GetMetahandle();
2108 WriteTestDataToEntry(&trans
, &entry
);
2110 // Verify it and pull the ID out.
2111 syncable::Id entry_id
;
2113 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2114 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2115 ASSERT_TRUE(entry
.good());
2116 entry_id
= entry
.GetId();
2117 EXPECT_TRUE(!entry_id
.ServerKnows());
2118 VerifyTestDataInEntry(&trans
, &entry
);
2121 // Now, to emulate a commit response failure, we just don't commit it.
2122 int64 new_version
= 150; // any larger value.
2123 int64 timestamp
= 20; // arbitrary value.
2124 syncable::Id new_entry_id
= syncable::Id::CreateFromServerId("server_id");
2126 // Generate an update from the server with a relevant ID reassignment.
2127 mock_server_
->AddUpdateBookmark(new_entry_id
, root_id_
,
2128 "new_entry", new_version
, timestamp
,
2129 local_cache_guid(), entry_id
.GetServerId());
2131 // We don't want it accidentally committed, just the update applied.
2132 mock_server_
->set_conflict_all_commits(true);
2134 // Purposefully delete the entry now before the update application finishes.
2136 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2137 Id new_entry_id
= GetOnlyEntryWithName(
2138 &trans
, trans
.root_id(), "new_entry");
2139 MutableEntry
entry(&trans
, GET_BY_ID
, new_entry_id
);
2140 ASSERT_TRUE(entry
.good());
2141 entry
.PutIsDel(true);
2144 // Just don't CHECK fail in sync, have the update split.
2147 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2148 Id new_entry_id
= GetOnlyEntryWithName(
2149 &trans
, trans
.root_id(), "new_entry");
2150 Entry
entry(&trans
, GET_BY_ID
, new_entry_id
);
2151 ASSERT_TRUE(entry
.good());
2152 EXPECT_FALSE(entry
.GetIsDel());
2154 Entry
old_entry(&trans
, GET_BY_ID
, entry_id
);
2155 ASSERT_TRUE(old_entry
.good());
2156 EXPECT_TRUE(old_entry
.GetIsDel());
2160 // TODO(chron): Add more unsanitized name tests.
2161 TEST_F(SyncerTest
, ConflictMatchingEntryHandlesUnsanitizedNames
) {
2162 mock_server_
->AddUpdateDirectory(1, 0, "A/A", 10, 10,
2163 foreign_cache_guid(), "-1");
2164 mock_server_
->AddUpdateDirectory(2, 0, "B/B", 10, 10,
2165 foreign_cache_guid(), "-2");
2166 mock_server_
->set_conflict_all_commits(true);
2169 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2171 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2172 ASSERT_TRUE(A
.good());
2173 A
.PutIsUnsynced(true);
2174 A
.PutIsUnappliedUpdate(true);
2175 A
.PutServerVersion(20);
2177 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
2178 ASSERT_TRUE(B
.good());
2179 B
.PutIsUnappliedUpdate(true);
2180 B
.PutServerVersion(20);
2183 saw_syncer_event_
= false;
2184 mock_server_
->set_conflict_all_commits(false);
2187 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2189 Entry
A(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2190 ASSERT_TRUE(A
.good());
2191 EXPECT_TRUE(A
.GetIsUnsynced()== false);
2192 EXPECT_TRUE(A
.GetIsUnappliedUpdate()== false);
2193 EXPECT_TRUE(A
.GetServerVersion()== 20);
2195 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
2196 ASSERT_TRUE(B
.good());
2197 EXPECT_TRUE(B
.GetIsUnsynced()== false);
2198 EXPECT_TRUE(B
.GetIsUnappliedUpdate()== false);
2199 EXPECT_TRUE(B
.GetServerVersion()== 20);
2203 TEST_F(SyncerTest
, ConflictMatchingEntryHandlesNormalNames
) {
2204 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
2205 foreign_cache_guid(), "-1");
2206 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
2207 foreign_cache_guid(), "-2");
2208 mock_server_
->set_conflict_all_commits(true);
2211 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2213 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2214 ASSERT_TRUE(A
.good());
2215 A
.PutIsUnsynced(true);
2216 A
.PutIsUnappliedUpdate(true);
2217 A
.PutServerVersion(20);
2219 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
2220 ASSERT_TRUE(B
.good());
2221 B
.PutIsUnappliedUpdate(true);
2222 B
.PutServerVersion(20);
2225 saw_syncer_event_
= false;
2226 mock_server_
->set_conflict_all_commits(false);
2229 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2231 Entry
A(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2232 ASSERT_TRUE(A
.good());
2233 EXPECT_TRUE(A
.GetIsUnsynced()== false);
2234 EXPECT_TRUE(A
.GetIsUnappliedUpdate()== false);
2235 EXPECT_TRUE(A
.GetServerVersion()== 20);
2237 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
2238 ASSERT_TRUE(B
.good());
2239 EXPECT_TRUE(B
.GetIsUnsynced()== false);
2240 EXPECT_TRUE(B
.GetIsUnappliedUpdate()== false);
2241 EXPECT_TRUE(B
.GetServerVersion()== 20);
2245 TEST_F(SyncerTest
, ReverseFolderOrderingTest
) {
2246 mock_server_
->AddUpdateDirectory(4, 3, "ggchild", 10, 10,
2247 foreign_cache_guid(), "-4");
2248 mock_server_
->AddUpdateDirectory(3, 2, "gchild", 10, 10,
2249 foreign_cache_guid(), "-3");
2250 mock_server_
->AddUpdateDirectory(5, 4, "gggchild", 10, 10,
2251 foreign_cache_guid(), "-5");
2252 mock_server_
->AddUpdateDirectory(2, 1, "child", 10, 10,
2253 foreign_cache_guid(), "-2");
2254 mock_server_
->AddUpdateDirectory(1, 0, "parent", 10, 10,
2255 foreign_cache_guid(), "-1");
2257 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2259 Id child_id
= GetOnlyEntryWithName(
2260 &trans
, ids_
.FromNumber(4), "gggchild");
2261 Entry
child(&trans
, GET_BY_ID
, child_id
);
2262 ASSERT_TRUE(child
.good());
2265 class EntryCreatedInNewFolderTest
: public SyncerTest
{
2267 void CreateFolderInBob() {
2268 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2269 MutableEntry
bob(&trans
,
2270 syncable::GET_BY_ID
,
2271 GetOnlyEntryWithName(&trans
,
2272 TestIdFactory::root(),
2276 MutableEntry
entry2(
2277 &trans
, CREATE
, BOOKMARKS
, bob
.GetId(), "bob");
2278 CHECK(entry2
.good());
2279 entry2
.PutIsDir(true);
2280 entry2
.PutIsUnsynced(true);
2281 entry2
.PutSpecifics(DefaultBookmarkSpecifics());
2285 TEST_F(EntryCreatedInNewFolderTest
, EntryCreatedInNewFolderMidSync
) {
2287 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2288 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2289 ASSERT_TRUE(entry
.good());
2290 entry
.PutIsDir(true);
2291 entry
.PutIsUnsynced(true);
2292 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2295 mock_server_
->SetMidCommitCallback(
2296 base::Bind(&EntryCreatedInNewFolderTest::CreateFolderInBob
,
2297 base::Unretained(this)));
2299 // We loop until no unsynced handles remain, so we will commit both ids.
2300 EXPECT_EQ(2u, mock_server_
->committed_ids().size());
2302 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2303 Entry
parent_entry(&trans
, syncable::GET_BY_ID
,
2304 GetOnlyEntryWithName(&trans
, TestIdFactory::root(), "bob"));
2305 ASSERT_TRUE(parent_entry
.good());
2308 GetOnlyEntryWithName(&trans
, parent_entry
.GetId(), "bob");
2309 Entry
child(&trans
, syncable::GET_BY_ID
, child_id
);
2310 ASSERT_TRUE(child
.good());
2311 EXPECT_EQ(parent_entry
.GetId(), child
.GetParentId());
2315 TEST_F(SyncerTest
, NegativeIDInUpdate
) {
2316 mock_server_
->AddUpdateBookmark(-10, 0, "bad", 40, 40,
2317 foreign_cache_guid(), "-100");
2319 // The negative id would make us CHECK!
2322 TEST_F(SyncerTest
, UnappliedUpdateOnCreatedItemItemDoesNotCrash
) {
2323 int64 metahandle_fred
;
2324 syncable::Id orig_id
;
2327 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2328 MutableEntry
fred_match(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(),
2330 ASSERT_TRUE(fred_match
.good());
2331 metahandle_fred
= fred_match
.GetMetahandle();
2332 orig_id
= fred_match
.GetId();
2333 WriteTestDataToEntry(&trans
, &fred_match
);
2337 EXPECT_EQ(1u, mock_server_
->committed_ids().size());
2338 mock_server_
->set_conflict_all_commits(true);
2339 syncable::Id fred_match_id
;
2341 // Now receive a change from outside.
2342 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2343 MutableEntry
fred_match(&trans
, GET_BY_HANDLE
, metahandle_fred
);
2344 ASSERT_TRUE(fred_match
.good());
2345 EXPECT_TRUE(fred_match
.GetId().ServerKnows());
2346 fred_match_id
= fred_match
.GetId();
2347 mock_server_
->AddUpdateBookmark(fred_match_id
, trans
.root_id(),
2348 "fred_match", 40, 40, local_cache_guid(), orig_id
.GetServerId());
2351 for (int i
= 0 ; i
< 30 ; ++i
) {
2357 * In the event that we have a double changed entry, that is changed on both
2358 * the client and the server, the conflict resolver should just drop one of
2359 * them and accept the other.
2362 TEST_F(SyncerTest
, DoublyChangedWithResolver
) {
2363 syncable::Id local_id
;
2365 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2366 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Folder");
2367 ASSERT_TRUE(parent
.good());
2368 parent
.PutIsDir(true);
2369 parent
.PutId(parent_id_
);
2370 parent
.PutBaseVersion(5);
2371 parent
.PutSpecifics(DefaultBookmarkSpecifics());
2372 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete.htm");
2373 ASSERT_TRUE(child
.good());
2374 local_id
= child
.GetId();
2375 child
.PutId(child_id_
);
2376 child
.PutBaseVersion(10);
2377 WriteTestDataToEntry(&wtrans
, &child
);
2379 mock_server_
->AddUpdateBookmark(child_id_
, parent_id_
, "Pete2.htm", 11, 10,
2380 local_cache_guid(), local_id
.GetServerId());
2381 mock_server_
->set_conflict_all_commits(true);
2383 syncable::Directory::Metahandles children
;
2385 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2386 directory()->GetChildHandlesById(&trans
, parent_id_
, &children
);
2387 // We expect the conflict resolver to preserve the local entry.
2388 Entry
child(&trans
, syncable::GET_BY_ID
, child_id_
);
2389 ASSERT_TRUE(child
.good());
2390 EXPECT_TRUE(child
.GetIsUnsynced());
2391 EXPECT_FALSE(child
.GetIsUnappliedUpdate());
2392 EXPECT_TRUE(child
.GetSpecifics().has_bookmark());
2393 EXPECT_EQ("Pete.htm", child
.GetNonUniqueName());
2394 VerifyTestBookmarkDataInEntry(&child
);
2397 // Only one entry, since we just overwrite one.
2398 EXPECT_EQ(1u, children
.size());
2399 saw_syncer_event_
= false;
2402 // We got this repro case when someone was editing bookmarks while sync was
2403 // occuring. The entry had changed out underneath the user.
2404 TEST_F(SyncerTest
, CommitsUpdateDoesntAlterEntry
) {
2405 const base::Time
& test_time
= ProtoTimeToTime(123456);
2406 syncable::Id local_id
;
2407 int64 entry_metahandle
;
2409 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2410 MutableEntry
entry(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Pete");
2411 ASSERT_TRUE(entry
.good());
2412 EXPECT_FALSE(entry
.GetId().ServerKnows());
2413 local_id
= entry
.GetId();
2414 entry
.PutIsDir(true);
2415 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2416 entry
.PutIsUnsynced(true);
2417 entry
.PutMtime(test_time
);
2418 entry_metahandle
= entry
.GetMetahandle();
2424 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2425 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, entry_metahandle
);
2426 ASSERT_TRUE(entry
.good());
2428 EXPECT_TRUE(id
.ServerKnows());
2429 version
= entry
.GetBaseVersion();
2431 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
2432 update
->set_originator_cache_guid(local_cache_guid());
2433 update
->set_originator_client_item_id(local_id
.GetServerId());
2434 EXPECT_EQ("Pete", update
->name());
2435 EXPECT_EQ(id
.GetServerId(), update
->id_string());
2436 EXPECT_EQ(root_id_
.GetServerId(), update
->parent_id_string());
2437 EXPECT_EQ(version
, update
->version());
2440 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2441 Entry
entry(&trans
, syncable::GET_BY_ID
, id
);
2442 ASSERT_TRUE(entry
.good());
2443 EXPECT_TRUE(entry
.GetMtime()== test_time
);
2447 TEST_F(SyncerTest
, ParentAndChildBothMatch
) {
2448 const FullModelTypeSet all_types
= FullModelTypeSet::All();
2449 syncable::Id parent_id
= ids_
.NewServerId();
2450 syncable::Id child_id
= ids_
.NewServerId();
2451 syncable::Id parent_local_id
;
2452 syncable::Id child_local_id
;
2456 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2457 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Folder");
2458 ASSERT_TRUE(parent
.good());
2459 parent_local_id
= parent
.GetId();
2460 parent
.PutIsDir(true);
2461 parent
.PutIsUnsynced(true);
2462 parent
.PutId(parent_id
);
2463 parent
.PutBaseVersion(1);
2464 parent
.PutSpecifics(DefaultBookmarkSpecifics());
2466 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent
.GetId(), "test.htm");
2467 ASSERT_TRUE(child
.good());
2468 child_local_id
= child
.GetId();
2469 child
.PutId(child_id
);
2470 child
.PutBaseVersion(1);
2471 child
.PutSpecifics(DefaultBookmarkSpecifics());
2472 WriteTestDataToEntry(&wtrans
, &child
);
2474 mock_server_
->AddUpdateDirectory(parent_id
, root_id_
, "Folder", 10, 10,
2476 parent_local_id
.GetServerId());
2477 mock_server_
->AddUpdateBookmark(child_id
, parent_id
, "test.htm", 10, 10,
2479 child_local_id
.GetServerId());
2480 mock_server_
->set_conflict_all_commits(true);
2485 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2486 Directory::Metahandles children
;
2487 directory()->GetChildHandlesById(&trans
, root_id_
, &children
);
2488 EXPECT_EQ(1u, children
.size());
2489 directory()->GetChildHandlesById(&trans
, parent_id
, &children
);
2490 EXPECT_EQ(1u, children
.size());
2491 std::vector
<int64
> unapplied
;
2492 directory()->GetUnappliedUpdateMetaHandles(&trans
, all_types
, &unapplied
);
2493 EXPECT_EQ(0u, unapplied
.size());
2494 syncable::Directory::Metahandles unsynced
;
2495 directory()->GetUnsyncedMetaHandles(&trans
, &unsynced
);
2496 EXPECT_EQ(0u, unsynced
.size());
2497 saw_syncer_event_
= false;
2501 TEST_F(SyncerTest
, CommittingNewDeleted
) {
2503 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2504 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2505 entry
.PutIsUnsynced(true);
2506 entry
.PutIsDel(true);
2509 EXPECT_EQ(0u, mock_server_
->committed_ids().size());
2512 // Original problem synopsis:
2513 // Check failed: entry->GetBaseVersion()<= entry->GetServerVersion()
2514 // Client creates entry, client finishes committing entry. Between
2515 // commit and getting update back, we delete the entry.
2516 // We get the update for the entry, but the local one was modified
2517 // so we store the entry but don't apply it. IS_UNAPPLIED_UPDATE is set.
2518 // We commit deletion and get a new version number.
2519 // We apply unapplied updates again before we get the update about the deletion.
2520 // This means we have an unapplied update where server_version < base_version.
2521 TEST_F(SyncerTest
, UnappliedUpdateDuringCommit
) {
2522 // This test is a little fake.
2524 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2525 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2526 entry
.PutId(ids_
.FromNumber(20));
2527 entry
.PutBaseVersion(1);
2528 entry
.PutServerVersion(1);
2529 entry
.PutServerParentId(ids_
.FromNumber(9999)); // Bad parent.
2530 entry
.PutIsUnsynced(true);
2531 entry
.PutIsUnappliedUpdate(true);
2532 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2533 entry
.PutServerSpecifics(DefaultBookmarkSpecifics());
2534 entry
.PutIsDel(false);
2537 EXPECT_EQ(1, session_
->status_controller().TotalNumConflictingItems());
2538 saw_syncer_event_
= false;
2541 // Original problem synopsis:
2543 // Unexpected error during sync if we:
2544 // make a new folder bob
2546 // make a new folder fred
2547 // move bob into fred
2550 // if no syncing occured midway, bob will have an illegal parent
2551 TEST_F(SyncerTest
, DeletingEntryInFolder
) {
2552 // This test is a little fake.
2553 int64 existing_metahandle
;
2555 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2556 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "existing");
2557 ASSERT_TRUE(entry
.good());
2558 entry
.PutIsDir(true);
2559 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2560 entry
.PutIsUnsynced(true);
2561 existing_metahandle
= entry
.GetMetahandle();
2565 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2566 MutableEntry
newfolder(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new");
2567 ASSERT_TRUE(newfolder
.good());
2568 newfolder
.PutIsDir(true);
2569 newfolder
.PutSpecifics(DefaultBookmarkSpecifics());
2570 newfolder
.PutIsUnsynced(true);
2572 MutableEntry
existing(&trans
, GET_BY_HANDLE
, existing_metahandle
);
2573 ASSERT_TRUE(existing
.good());
2574 existing
.PutParentId(newfolder
.GetId());
2575 existing
.PutIsUnsynced(true);
2576 EXPECT_TRUE(existing
.GetId().ServerKnows());
2578 newfolder
.PutIsDel(true);
2579 existing
.PutIsDel(true);
2582 EXPECT_EQ(0, GetCommitCounters(BOOKMARKS
).num_commits_conflict
);
2585 TEST_F(SyncerTest
, DeletingEntryWithLocalEdits
) {
2586 int64 newfolder_metahandle
;
2588 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
2589 foreign_cache_guid(), "-1");
2592 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2593 MutableEntry
newfolder(
2594 &trans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(1), "local");
2595 ASSERT_TRUE(newfolder
.good());
2596 newfolder
.PutIsUnsynced(true);
2597 newfolder
.PutIsDir(true);
2598 newfolder
.PutSpecifics(DefaultBookmarkSpecifics());
2599 newfolder_metahandle
= newfolder
.GetMetahandle();
2601 mock_server_
->AddUpdateDirectory(1, 0, "bob", 2, 20,
2602 foreign_cache_guid(), "-1");
2603 mock_server_
->SetLastUpdateDeleted();
2604 SyncShareConfigure();
2606 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2607 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, newfolder_metahandle
);
2608 ASSERT_TRUE(entry
.good());
2612 TEST_F(SyncerTest
, FolderSwapUpdate
) {
2613 mock_server_
->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2614 foreign_cache_guid(), "-7801");
2615 mock_server_
->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2616 foreign_cache_guid(), "-1024");
2618 mock_server_
->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2619 foreign_cache_guid(), "-1024");
2620 mock_server_
->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2621 foreign_cache_guid(), "-7801");
2624 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2625 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2626 ASSERT_TRUE(id1
.good());
2627 EXPECT_TRUE("fred" == id1
.GetNonUniqueName());
2628 EXPECT_TRUE(root_id_
== id1
.GetParentId());
2629 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2630 ASSERT_TRUE(id2
.good());
2631 EXPECT_TRUE("bob" == id2
.GetNonUniqueName());
2632 EXPECT_TRUE(root_id_
== id2
.GetParentId());
2634 saw_syncer_event_
= false;
2637 TEST_F(SyncerTest
, NameCollidingFolderSwapWorksFine
) {
2638 mock_server_
->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2639 foreign_cache_guid(), "-7801");
2640 mock_server_
->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2641 foreign_cache_guid(), "-1024");
2642 mock_server_
->AddUpdateDirectory(4096, 0, "alice", 1, 10,
2643 foreign_cache_guid(), "-4096");
2646 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2647 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2648 ASSERT_TRUE(id1
.good());
2649 EXPECT_TRUE("bob" == id1
.GetNonUniqueName());
2650 EXPECT_TRUE(root_id_
== id1
.GetParentId());
2651 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2652 ASSERT_TRUE(id2
.good());
2653 EXPECT_TRUE("fred" == id2
.GetNonUniqueName());
2654 EXPECT_TRUE(root_id_
== id2
.GetParentId());
2655 Entry
id3(&trans
, GET_BY_ID
, ids_
.FromNumber(4096));
2656 ASSERT_TRUE(id3
.good());
2657 EXPECT_TRUE("alice" == id3
.GetNonUniqueName());
2658 EXPECT_TRUE(root_id_
== id3
.GetParentId());
2660 mock_server_
->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2661 foreign_cache_guid(), "-1024");
2662 mock_server_
->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2663 foreign_cache_guid(), "-7801");
2664 mock_server_
->AddUpdateDirectory(4096, 0, "bob", 2, 20,
2665 foreign_cache_guid(), "-4096");
2668 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2669 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2670 ASSERT_TRUE(id1
.good());
2671 EXPECT_TRUE("fred" == id1
.GetNonUniqueName());
2672 EXPECT_TRUE(root_id_
== id1
.GetParentId());
2673 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2674 ASSERT_TRUE(id2
.good());
2675 EXPECT_TRUE("bob" == id2
.GetNonUniqueName());
2676 EXPECT_TRUE(root_id_
== id2
.GetParentId());
2677 Entry
id3(&trans
, GET_BY_ID
, ids_
.FromNumber(4096));
2678 ASSERT_TRUE(id3
.good());
2679 EXPECT_TRUE("bob" == id3
.GetNonUniqueName());
2680 EXPECT_TRUE(root_id_
== id3
.GetParentId());
2682 saw_syncer_event_
= false;
2685 // Committing more than kDefaultMaxCommitBatchSize items requires that
2686 // we post more than one commit command to the server. This test makes
2687 // sure that scenario works as expected.
2688 TEST_F(SyncerTest
, CommitManyItemsInOneGo_Success
) {
2689 uint32 num_batches
= 3;
2690 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2692 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2693 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2694 string nameutf8
= base::StringPrintf("%d", i
);
2695 string
name(nameutf8
.begin(), nameutf8
.end());
2696 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2697 e
.PutIsUnsynced(true);
2699 e
.PutSpecifics(DefaultBookmarkSpecifics());
2702 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2705 EXPECT_EQ(num_batches
, mock_server_
->commit_messages().size());
2706 EXPECT_EQ(0, directory()->unsynced_entity_count());
2709 // Test that a single failure to contact the server will cause us to exit the
2710 // commit loop immediately.
2711 TEST_F(SyncerTest
, CommitManyItemsInOneGo_PostBufferFail
) {
2712 uint32 num_batches
= 3;
2713 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2715 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2716 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2717 string nameutf8
= base::StringPrintf("%d", i
);
2718 string
name(nameutf8
.begin(), nameutf8
.end());
2719 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2720 e
.PutIsUnsynced(true);
2722 e
.PutSpecifics(DefaultBookmarkSpecifics());
2725 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2727 // The second commit should fail. It will be preceded by one successful
2728 // GetUpdate and one succesful commit.
2729 mock_server_
->FailNthPostBufferToPathCall(3);
2732 EXPECT_EQ(1U, mock_server_
->commit_messages().size());
2733 EXPECT_EQ(SYNC_SERVER_ERROR
,
2734 session_
->status_controller().model_neutral_state().commit_result
);
2735 EXPECT_EQ(items_to_commit
- kDefaultMaxCommitBatchSize
,
2736 directory()->unsynced_entity_count());
2739 // Test that a single conflict response from the server will cause us to exit
2740 // the commit loop immediately.
2741 TEST_F(SyncerTest
, CommitManyItemsInOneGo_CommitConflict
) {
2742 uint32 num_batches
= 2;
2743 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2745 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2746 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2747 string nameutf8
= base::StringPrintf("%d", i
);
2748 string
name(nameutf8
.begin(), nameutf8
.end());
2749 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2750 e
.PutIsUnsynced(true);
2752 e
.PutSpecifics(DefaultBookmarkSpecifics());
2755 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2757 // Return a CONFLICT response for the first item.
2758 mock_server_
->set_conflict_n_commits(1);
2761 // We should stop looping at the first sign of trouble.
2762 EXPECT_EQ(1U, mock_server_
->commit_messages().size());
2763 EXPECT_EQ(items_to_commit
- (kDefaultMaxCommitBatchSize
- 1),
2764 directory()->unsynced_entity_count());
2767 // Tests that sending debug info events works.
2768 TEST_F(SyncerTest
, SendDebugInfoEventsOnGetUpdates_HappyCase
) {
2769 debug_info_getter_
->AddDebugEvent();
2770 debug_info_getter_
->AddDebugEvent();
2774 // Verify we received one GetUpdates request with two debug info events.
2775 EXPECT_EQ(1U, mock_server_
->requests().size());
2776 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2777 EXPECT_EQ(2, mock_server_
->last_request().debug_info().events_size());
2781 // See that we received another GetUpdates request, but that it contains no
2782 // debug info events.
2783 EXPECT_EQ(2U, mock_server_
->requests().size());
2784 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2785 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
2787 debug_info_getter_
->AddDebugEvent();
2791 // See that we received another GetUpdates request and it contains one debug
2793 EXPECT_EQ(3U, mock_server_
->requests().size());
2794 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2795 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
2798 // Tests that debug info events are dropped on server error.
2799 TEST_F(SyncerTest
, SendDebugInfoEventsOnGetUpdates_PostFailsDontDrop
) {
2800 debug_info_getter_
->AddDebugEvent();
2801 debug_info_getter_
->AddDebugEvent();
2803 mock_server_
->FailNextPostBufferToPathCall();
2806 // Verify we attempted to send one GetUpdates request with two debug info
2808 EXPECT_EQ(1U, mock_server_
->requests().size());
2809 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2810 EXPECT_EQ(2, mock_server_
->last_request().debug_info().events_size());
2814 // See that the client resent the two debug info events.
2815 EXPECT_EQ(2U, mock_server_
->requests().size());
2816 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2817 EXPECT_EQ(2, mock_server_
->last_request().debug_info().events_size());
2819 // The previous send was successful so this next one shouldn't generate any
2820 // debug info events.
2822 EXPECT_EQ(3U, mock_server_
->requests().size());
2823 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2824 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
2827 // Tests that sending debug info events on Commit works.
2828 TEST_F(SyncerTest
, SendDebugInfoEventsOnCommit_HappyCase
) {
2829 // Make sure GetUpdate isn't call as it would "steal" debug info events before
2830 // Commit has a chance to send them.
2831 ConfigureNoGetUpdatesRequired();
2833 // Generate a debug info event and trigger a commit.
2834 debug_info_getter_
->AddDebugEvent();
2835 CreateUnsyncedDirectory("X", "id_X");
2838 // Verify that the last request received is a Commit and that it contains a
2839 // debug info event.
2840 EXPECT_EQ(1U, mock_server_
->requests().size());
2841 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2842 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
2844 // Generate another commit, but no debug info event.
2845 CreateUnsyncedDirectory("Y", "id_Y");
2848 // See that it was received and contains no debug info events.
2849 EXPECT_EQ(2U, mock_server_
->requests().size());
2850 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2851 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
2854 // Tests that debug info events are not dropped on server error.
2855 TEST_F(SyncerTest
, SendDebugInfoEventsOnCommit_PostFailsDontDrop
) {
2856 // Make sure GetUpdate isn't call as it would "steal" debug info events before
2857 // Commit has a chance to send them.
2858 ConfigureNoGetUpdatesRequired();
2860 mock_server_
->FailNextPostBufferToPathCall();
2862 // Generate a debug info event and trigger a commit.
2863 debug_info_getter_
->AddDebugEvent();
2864 CreateUnsyncedDirectory("X", "id_X");
2867 // Verify that the last request sent is a Commit and that it contains a debug
2869 EXPECT_EQ(1U, mock_server_
->requests().size());
2870 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2871 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
2876 // Verify that we've received another Commit and that it contains a debug info
2877 // event (just like the previous one).
2878 EXPECT_EQ(2U, mock_server_
->requests().size());
2879 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2880 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
2882 // Generate another commit and try again.
2883 CreateUnsyncedDirectory("Y", "id_Y");
2886 // See that it was received and contains no debug info events.
2887 EXPECT_EQ(3U, mock_server_
->requests().size());
2888 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2889 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
2892 TEST_F(SyncerTest
, HugeConflict
) {
2893 int item_count
= 300; // We should be able to do 300 or 3000 w/o issue.
2895 syncable::Id parent_id
= ids_
.NewServerId();
2896 syncable::Id last_id
= parent_id
;
2897 vector
<syncable::Id
> tree_ids
;
2899 // Create a lot of updates for which the parent does not exist yet.
2900 // Generate a huge deep tree which should all fail to apply at first.
2902 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2903 for (int i
= 0; i
< item_count
; i
++) {
2904 syncable::Id next_id
= ids_
.NewServerId();
2905 syncable::Id local_id
= ids_
.NewLocalId();
2906 tree_ids
.push_back(next_id
);
2907 mock_server_
->AddUpdateDirectory(next_id
, last_id
, "BOB", 2, 20,
2908 foreign_cache_guid(),
2909 local_id
.GetServerId());
2915 // Check they're in the expected conflict state.
2917 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2918 for (int i
= 0; i
< item_count
; i
++) {
2919 Entry
e(&trans
, GET_BY_ID
, tree_ids
[i
]);
2920 // They should all exist but none should be applied.
2921 ASSERT_TRUE(e
.good());
2922 EXPECT_TRUE(e
.GetIsDel());
2923 EXPECT_TRUE(e
.GetIsUnappliedUpdate());
2927 // Add the missing parent directory.
2928 mock_server_
->AddUpdateDirectory(parent_id
, TestIdFactory::root(),
2929 "BOB", 2, 20, foreign_cache_guid(), "-3500");
2932 // Now they should all be OK.
2934 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2935 for (int i
= 0; i
< item_count
; i
++) {
2936 Entry
e(&trans
, GET_BY_ID
, tree_ids
[i
]);
2937 ASSERT_TRUE(e
.good());
2938 EXPECT_FALSE(e
.GetIsDel());
2939 EXPECT_FALSE(e
.GetIsUnappliedUpdate());
2944 TEST_F(SyncerTest
, DontCrashOnCaseChange
) {
2945 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
2946 foreign_cache_guid(), "-1");
2949 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2950 MutableEntry
e(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2951 ASSERT_TRUE(e
.good());
2952 e
.PutIsUnsynced(true);
2954 mock_server_
->set_conflict_all_commits(true);
2955 mock_server_
->AddUpdateDirectory(1, 0, "BOB", 2, 20,
2956 foreign_cache_guid(), "-1");
2957 SyncShareNudge(); // USED TO CAUSE AN ASSERT
2958 saw_syncer_event_
= false;
2961 TEST_F(SyncerTest
, UnsyncedItemAndUpdate
) {
2962 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
2963 foreign_cache_guid(), "-1");
2965 mock_server_
->set_conflict_all_commits(true);
2966 mock_server_
->AddUpdateDirectory(2, 0, "bob", 2, 20,
2967 foreign_cache_guid(), "-2");
2968 SyncShareNudge(); // USED TO CAUSE AN ASSERT
2969 saw_syncer_event_
= false;
2972 TEST_F(SyncerTest
, NewEntryAndAlteredServerEntrySharePath
) {
2973 mock_server_
->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
2974 foreign_cache_guid(), "-1");
2976 int64 local_folder_handle
;
2977 syncable::Id local_folder_id
;
2979 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2980 MutableEntry
new_entry(
2981 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bar.htm");
2982 ASSERT_TRUE(new_entry
.good());
2983 local_folder_id
= new_entry
.GetId();
2984 local_folder_handle
= new_entry
.GetMetahandle();
2985 new_entry
.PutIsUnsynced(true);
2986 new_entry
.PutSpecifics(DefaultBookmarkSpecifics());
2987 MutableEntry
old(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2988 ASSERT_TRUE(old
.good());
2989 WriteTestDataToEntry(&wtrans
, &old
);
2991 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
2992 foreign_cache_guid(), "-1");
2993 mock_server_
->set_conflict_all_commits(true);
2995 saw_syncer_event_
= false;
2997 // Update #20 should have been dropped in favor of the local version.
2998 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2999 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3000 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3001 ASSERT_TRUE(server
.good());
3002 ASSERT_TRUE(local
.good());
3003 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3004 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3005 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3006 EXPECT_TRUE(server
.GetIsUnsynced());
3007 EXPECT_TRUE(local
.GetIsUnsynced());
3008 EXPECT_EQ("Foo.htm", server
.GetNonUniqueName());
3009 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3011 // Allow local changes to commit.
3012 mock_server_
->set_conflict_all_commits(false);
3014 saw_syncer_event_
= false;
3016 // Now add a server change to make the two names equal. There should
3017 // be no conflict with that, since names are not unique.
3018 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
3019 foreign_cache_guid(), "-1");
3021 saw_syncer_event_
= false;
3023 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3024 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3025 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3026 ASSERT_TRUE(server
.good());
3027 ASSERT_TRUE(local
.good());
3028 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3029 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3030 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3031 EXPECT_FALSE(server
.GetIsUnsynced());
3032 EXPECT_FALSE(local
.GetIsUnsynced());
3033 EXPECT_EQ("Bar.htm", server
.GetNonUniqueName());
3034 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3035 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
3036 server
.GetSpecifics().bookmark().url());
3040 // Same as NewEntryAnddServerEntrySharePath, but using the old-style protocol.
3041 TEST_F(SyncerTest
, NewEntryAndAlteredServerEntrySharePath_OldBookmarksProto
) {
3042 mock_server_
->set_use_legacy_bookmarks_protocol(true);
3043 mock_server_
->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
3044 foreign_cache_guid(), "-1");
3046 int64 local_folder_handle
;
3047 syncable::Id local_folder_id
;
3049 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3050 MutableEntry
new_entry(
3051 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bar.htm");
3052 ASSERT_TRUE(new_entry
.good());
3053 local_folder_id
= new_entry
.GetId();
3054 local_folder_handle
= new_entry
.GetMetahandle();
3055 new_entry
.PutIsUnsynced(true);
3056 new_entry
.PutSpecifics(DefaultBookmarkSpecifics());
3057 MutableEntry
old(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3058 ASSERT_TRUE(old
.good());
3059 WriteTestDataToEntry(&wtrans
, &old
);
3061 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
3062 foreign_cache_guid(), "-1");
3063 mock_server_
->set_conflict_all_commits(true);
3065 saw_syncer_event_
= false;
3067 // Update #20 should have been dropped in favor of the local version.
3068 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3069 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3070 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3071 ASSERT_TRUE(server
.good());
3072 ASSERT_TRUE(local
.good());
3073 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3074 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3075 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3076 EXPECT_TRUE(server
.GetIsUnsynced());
3077 EXPECT_TRUE(local
.GetIsUnsynced());
3078 EXPECT_EQ("Foo.htm", server
.GetNonUniqueName());
3079 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3081 // Allow local changes to commit.
3082 mock_server_
->set_conflict_all_commits(false);
3084 saw_syncer_event_
= false;
3086 // Now add a server change to make the two names equal. There should
3087 // be no conflict with that, since names are not unique.
3088 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
3089 foreign_cache_guid(), "-1");
3091 saw_syncer_event_
= false;
3093 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3094 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3095 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3096 ASSERT_TRUE(server
.good());
3097 ASSERT_TRUE(local
.good());
3098 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3099 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3100 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3101 EXPECT_FALSE(server
.GetIsUnsynced());
3102 EXPECT_FALSE(local
.GetIsUnsynced());
3103 EXPECT_EQ("Bar.htm", server
.GetNonUniqueName());
3104 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3105 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
3106 server
.GetSpecifics().bookmark().url());
3110 // Circular links should be resolved by the server.
3111 TEST_F(SyncerTest
, SiblingDirectoriesBecomeCircular
) {
3112 // we don't currently resolve this. This test ensures we don't.
3113 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
3114 foreign_cache_guid(), "-1");
3115 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
3116 foreign_cache_guid(), "-2");
3119 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3120 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3121 ASSERT_TRUE(A
.good());
3122 A
.PutIsUnsynced(true);
3123 A
.PutParentId(ids_
.FromNumber(2));
3124 A
.PutNonUniqueName("B");
3126 mock_server_
->AddUpdateDirectory(2, 1, "A", 20, 20,
3127 foreign_cache_guid(), "-2");
3128 mock_server_
->set_conflict_all_commits(true);
3130 saw_syncer_event_
= false;
3132 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3133 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3134 ASSERT_TRUE(A
.good());
3135 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
3136 ASSERT_TRUE(B
.good());
3137 EXPECT_TRUE(A
.GetNonUniqueName()== "B");
3138 EXPECT_TRUE(B
.GetNonUniqueName()== "B");
3142 TEST_F(SyncerTest
, SwapEntryNames
) {
3143 // Simple transaction test.
3144 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
3145 foreign_cache_guid(), "-1");
3146 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
3147 foreign_cache_guid(), "-2");
3148 mock_server_
->set_conflict_all_commits(true);
3151 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3152 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3153 ASSERT_TRUE(A
.good());
3154 A
.PutIsUnsynced(true);
3155 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
3156 ASSERT_TRUE(B
.good());
3157 B
.PutIsUnsynced(true);
3158 A
.PutNonUniqueName("C");
3159 B
.PutNonUniqueName("A");
3160 A
.PutNonUniqueName("B");
3163 saw_syncer_event_
= false;
3166 TEST_F(SyncerTest
, DualDeletionWithNewItemNameClash
) {
3167 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
3168 foreign_cache_guid(), "-1");
3169 mock_server_
->AddUpdateBookmark(2, 0, "B", 10, 10,
3170 foreign_cache_guid(), "-2");
3171 mock_server_
->set_conflict_all_commits(true);
3174 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3175 MutableEntry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3176 ASSERT_TRUE(B
.good());
3177 WriteTestDataToEntry(&trans
, &B
);
3180 mock_server_
->AddUpdateBookmark(2, 0, "A", 11, 11,
3181 foreign_cache_guid(), "-2");
3182 mock_server_
->SetLastUpdateDeleted();
3185 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3186 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3187 ASSERT_TRUE(B
.good());
3188 EXPECT_FALSE(B
.GetIsUnsynced());
3189 EXPECT_FALSE(B
.GetIsUnappliedUpdate());
3191 saw_syncer_event_
= false;
3194 // When we undelete an entity as a result of conflict resolution, we reuse the
3195 // existing server id and preserve the old version, simply updating the server
3196 // version with the new non-deleted entity.
3197 TEST_F(SyncerTest
, ResolveWeWroteTheyDeleted
) {
3198 int64 bob_metahandle
;
3200 mock_server_
->AddUpdateBookmark(1, 0, "bob", 1, 10,
3201 foreign_cache_guid(), "-1");
3204 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3205 MutableEntry
bob(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3206 ASSERT_TRUE(bob
.good());
3207 bob_metahandle
= bob
.GetMetahandle();
3208 WriteTestDataToEntry(&trans
, &bob
);
3210 mock_server_
->AddUpdateBookmark(1, 0, "bob", 2, 10,
3211 foreign_cache_guid(), "-1");
3212 mock_server_
->SetLastUpdateDeleted();
3213 mock_server_
->set_conflict_all_commits(true);
3217 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3218 Entry
bob(&trans
, GET_BY_HANDLE
, bob_metahandle
);
3219 ASSERT_TRUE(bob
.good());
3220 EXPECT_TRUE(bob
.GetIsUnsynced());
3221 EXPECT_TRUE(bob
.GetId().ServerKnows());
3222 EXPECT_FALSE(bob
.GetIsUnappliedUpdate());
3223 EXPECT_FALSE(bob
.GetIsDel());
3224 EXPECT_EQ(2, bob
.GetServerVersion());
3225 EXPECT_EQ(2, bob
.GetBaseVersion());
3227 saw_syncer_event_
= false;
3230 // This test is to reproduce a check failure. Sometimes we would get a bad ID
3231 // back when creating an entry.
3232 TEST_F(SyncerTest
, DuplicateIDReturn
) {
3234 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3235 MutableEntry
folder(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
3236 ASSERT_TRUE(folder
.good());
3237 folder
.PutIsUnsynced(true);
3238 folder
.PutIsDir(true);
3239 folder
.PutSpecifics(DefaultBookmarkSpecifics());
3240 MutableEntry
folder2(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "fred");
3241 ASSERT_TRUE(folder2
.good());
3242 folder2
.PutIsUnsynced(false);
3243 folder2
.PutIsDir(true);
3244 folder2
.PutSpecifics(DefaultBookmarkSpecifics());
3245 folder2
.PutBaseVersion(3);
3246 folder2
.PutId(syncable::Id::CreateFromServerId("mock_server:10000"));
3248 mock_server_
->set_next_new_id(10000);
3249 EXPECT_EQ(1u, directory()->unsynced_entity_count());
3250 // we get back a bad id in here (should never happen).
3252 EXPECT_EQ(1u, directory()->unsynced_entity_count());
3253 SyncShareNudge(); // another bad id in here.
3254 EXPECT_EQ(0u, directory()->unsynced_entity_count());
3255 saw_syncer_event_
= false;
3258 TEST_F(SyncerTest
, DeletedEntryWithBadParentInLoopCalculation
) {
3259 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
3260 foreign_cache_guid(), "-1");
3263 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3264 MutableEntry
bob(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3265 ASSERT_TRUE(bob
.good());
3266 // This is valid, because the parent could have gone away a long time ago.
3267 bob
.PutParentId(ids_
.FromNumber(54));
3269 bob
.PutIsUnsynced(true);
3271 mock_server_
->AddUpdateDirectory(2, 1, "fred", 1, 10,
3272 foreign_cache_guid(), "-2");
3277 TEST_F(SyncerTest
, ConflictResolverMergesLocalDeleteAndServerUpdate
) {
3278 syncable::Id local_id
;
3280 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3282 MutableEntry
local_deleted(
3283 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "name");
3284 local_id
= local_deleted
.GetId();
3285 local_deleted
.PutId(ids_
.FromNumber(1));
3286 local_deleted
.PutBaseVersion(1);
3287 local_deleted
.PutIsDel(true);
3288 local_deleted
.PutIsDir(false);
3289 local_deleted
.PutIsUnsynced(true);
3290 local_deleted
.PutSpecifics(DefaultBookmarkSpecifics());
3293 mock_server_
->AddUpdateBookmark(ids_
.FromNumber(1), root_id_
, "name", 10, 10,
3295 local_id
.GetServerId());
3297 // We don't care about actually committing, just the resolution.
3298 mock_server_
->set_conflict_all_commits(true);
3302 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3303 Entry
local_deleted(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3304 EXPECT_TRUE(local_deleted
.GetBaseVersion()== 10);
3305 EXPECT_TRUE(local_deleted
.GetIsUnappliedUpdate()== false);
3306 EXPECT_TRUE(local_deleted
.GetIsUnsynced()== true);
3307 EXPECT_TRUE(local_deleted
.GetIsDel()== true);
3308 EXPECT_TRUE(local_deleted
.GetIsDir()== false);
3312 // See what happens if the IS_DIR bit gets flipped. This can cause us
3313 // all kinds of disasters.
3314 TEST_F(SyncerTest
, UpdateFlipsTheFolderBit
) {
3315 // Local object: a deleted directory (container), revision 1, unsynced.
3317 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3319 MutableEntry
local_deleted(
3320 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "name");
3321 local_deleted
.PutId(ids_
.FromNumber(1));
3322 local_deleted
.PutBaseVersion(1);
3323 local_deleted
.PutIsDel(true);
3324 local_deleted
.PutIsDir(true);
3325 local_deleted
.PutIsUnsynced(true);
3326 local_deleted
.PutSpecifics(DefaultBookmarkSpecifics());
3329 // Server update: entry-type object (not a container), revision 10.
3330 mock_server_
->AddUpdateBookmark(ids_
.FromNumber(1), root_id_
, "name", 10, 10,
3332 ids_
.FromNumber(1).GetServerId());
3334 // Don't attempt to commit.
3335 mock_server_
->set_conflict_all_commits(true);
3337 // The syncer should not attempt to apply the invalid update.
3341 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3342 Entry
local_deleted(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3343 EXPECT_TRUE(local_deleted
.GetBaseVersion()== 1);
3344 EXPECT_TRUE(local_deleted
.GetIsUnappliedUpdate()== false);
3345 EXPECT_TRUE(local_deleted
.GetIsUnsynced()== true);
3346 EXPECT_TRUE(local_deleted
.GetIsDel()== true);
3347 EXPECT_TRUE(local_deleted
.GetIsDir()== true);
3352 // Merge conflict resolution will merge a new local entry with another entry
3353 // that needs updates, resulting in CHECK.
3354 TEST_F(SyncerTest
, MergingExistingItems
) {
3355 mock_server_
->set_conflict_all_commits(true);
3356 mock_server_
->AddUpdateBookmark(1, 0, "base", 10, 10,
3357 local_cache_guid(), "-1");
3360 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3362 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "Copy of base");
3363 WriteTestDataToEntry(&trans
, &entry
);
3365 mock_server_
->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
3366 local_cache_guid(), "-1");
3370 // In this test a long changelog contains a child at the start of the changelog
3371 // and a parent at the end. While these updates are in progress the client would
3373 TEST_F(SyncerTest
, LongChangelistWithApplicationConflict
) {
3374 const int depth
= 400;
3375 syncable::Id folder_id
= ids_
.FromNumber(1);
3377 // First we an item in a folder in the root. However the folder won't come
3379 syncable::Id stuck_entry_id
= TestIdFactory::FromNumber(99999);
3380 mock_server_
->AddUpdateDirectory(stuck_entry_id
,
3381 folder_id
, "stuck", 1, 1,
3382 foreign_cache_guid(), "-99999");
3383 mock_server_
->SetChangesRemaining(depth
- 1);
3386 // Buffer up a very long series of downloads.
3387 // We should never be stuck (conflict resolution shouldn't
3388 // kick in so long as we're making forward progress).
3389 for (int i
= 0; i
< depth
; i
++) {
3390 mock_server_
->NextUpdateBatch();
3391 mock_server_
->SetNewTimestamp(i
+ 1);
3392 mock_server_
->SetChangesRemaining(depth
- i
);
3397 // Ensure our folder hasn't somehow applied.
3399 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3400 Entry
child(&trans
, GET_BY_ID
, stuck_entry_id
);
3401 EXPECT_TRUE(child
.good());
3402 EXPECT_TRUE(child
.GetIsUnappliedUpdate());
3403 EXPECT_TRUE(child
.GetIsDel());
3404 EXPECT_FALSE(child
.GetIsUnsynced());
3407 // And finally the folder.
3408 mock_server_
->AddUpdateDirectory(folder_id
,
3409 TestIdFactory::root(), "folder", 1, 1,
3410 foreign_cache_guid(), "-1");
3411 mock_server_
->SetChangesRemaining(0);
3414 // Check that everything is as expected after the commit.
3416 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3417 Entry
entry(&trans
, GET_BY_ID
, folder_id
);
3418 ASSERT_TRUE(entry
.good());
3419 Entry
child(&trans
, GET_BY_ID
, stuck_entry_id
);
3420 EXPECT_EQ(entry
.GetId(), child
.GetParentId());
3421 EXPECT_EQ("stuck", child
.GetNonUniqueName());
3422 EXPECT_TRUE(child
.good());
3426 TEST_F(SyncerTest
, DontMergeTwoExistingItems
) {
3427 mock_server_
->set_conflict_all_commits(true);
3428 mock_server_
->AddUpdateBookmark(1, 0, "base", 10, 10,
3429 foreign_cache_guid(), "-1");
3430 mock_server_
->AddUpdateBookmark(2, 0, "base2", 10, 10,
3431 foreign_cache_guid(), "-2");
3434 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3435 MutableEntry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3436 ASSERT_TRUE(entry
.good());
3437 entry
.PutNonUniqueName("Copy of base");
3438 entry
.PutIsUnsynced(true);
3440 mock_server_
->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
3441 foreign_cache_guid(), "-1");
3444 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3445 Entry
entry1(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3446 EXPECT_FALSE(entry1
.GetIsUnappliedUpdate());
3447 EXPECT_FALSE(entry1
.GetIsUnsynced());
3448 EXPECT_FALSE(entry1
.GetIsDel());
3449 Entry
entry2(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3450 EXPECT_FALSE(entry2
.GetIsUnappliedUpdate());
3451 EXPECT_TRUE(entry2
.GetIsUnsynced());
3452 EXPECT_FALSE(entry2
.GetIsDel());
3453 EXPECT_EQ(entry1
.GetNonUniqueName(), entry2
.GetNonUniqueName());
3457 TEST_F(SyncerTest
, TestUndeleteUpdate
) {
3458 mock_server_
->set_conflict_all_commits(true);
3459 mock_server_
->AddUpdateDirectory(1, 0, "foo", 1, 1,
3460 foreign_cache_guid(), "-1");
3461 mock_server_
->AddUpdateDirectory(2, 1, "bar", 1, 2,
3462 foreign_cache_guid(), "-2");
3464 mock_server_
->AddUpdateDirectory(2, 1, "bar", 2, 3,
3465 foreign_cache_guid(), "-2");
3466 mock_server_
->SetLastUpdateDeleted();
3471 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3472 Entry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3473 ASSERT_TRUE(entry
.good());
3474 EXPECT_TRUE(entry
.GetIsDel());
3475 metahandle
= entry
.GetMetahandle();
3477 mock_server_
->AddUpdateDirectory(1, 0, "foo", 2, 4,
3478 foreign_cache_guid(), "-1");
3479 mock_server_
->SetLastUpdateDeleted();
3481 // This used to be rejected as it's an undeletion. Now, it results in moving
3482 // the delete path aside.
3483 mock_server_
->AddUpdateDirectory(2, 1, "bar", 3, 5,
3484 foreign_cache_guid(), "-2");
3487 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3488 Entry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3489 ASSERT_TRUE(entry
.good());
3490 EXPECT_TRUE(entry
.GetIsDel());
3491 EXPECT_FALSE(entry
.GetServerIsDel());
3492 EXPECT_TRUE(entry
.GetIsUnappliedUpdate());
3493 EXPECT_NE(entry
.GetMetahandle(), metahandle
);
3497 TEST_F(SyncerTest
, TestMoveSanitizedNamedFolder
) {
3498 mock_server_
->AddUpdateDirectory(1, 0, "foo", 1, 1,
3499 foreign_cache_guid(), "-1");
3500 mock_server_
->AddUpdateDirectory(2, 0, ":::", 1, 2,
3501 foreign_cache_guid(), "-2");
3504 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3505 MutableEntry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3506 ASSERT_TRUE(entry
.good());
3507 entry
.PutParentId(ids_
.FromNumber(1));
3508 EXPECT_TRUE(entry
.PutIsUnsynced(true));
3511 // We use the same sync ts as before so our times match up.
3512 mock_server_
->AddUpdateDirectory(2, 1, ":::", 2, 2,
3513 foreign_cache_guid(), "-2");
3517 // Don't crash when this occurs.
3518 TEST_F(SyncerTest
, UpdateWhereParentIsNotAFolder
) {
3519 mock_server_
->AddUpdateBookmark(1, 0, "B", 10, 10,
3520 foreign_cache_guid(), "-1");
3521 mock_server_
->AddUpdateDirectory(2, 1, "BookmarkParent", 10, 10,
3522 foreign_cache_guid(), "-2");
3523 // Used to cause a CHECK
3526 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
3527 Entry
good_entry(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
3528 ASSERT_TRUE(good_entry
.good());
3529 EXPECT_FALSE(good_entry
.GetIsUnappliedUpdate());
3530 Entry
bad_parent(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(2));
3531 ASSERT_TRUE(bad_parent
.good());
3532 EXPECT_TRUE(bad_parent
.GetIsUnappliedUpdate());
3536 TEST_F(SyncerTest
, DirectoryUpdateTest
) {
3537 Id in_root_id
= ids_
.NewServerId();
3538 Id in_in_root_id
= ids_
.NewServerId();
3540 mock_server_
->AddUpdateDirectory(in_root_id
, TestIdFactory::root(),
3541 "in_root_name", 2, 2,
3542 foreign_cache_guid(), "-1");
3543 mock_server_
->AddUpdateDirectory(in_in_root_id
, in_root_id
,
3544 "in_in_root_name", 3, 3,
3545 foreign_cache_guid(), "-2");
3548 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3549 Entry
in_root(&trans
, GET_BY_ID
, in_root_id
);
3550 ASSERT_TRUE(in_root
.good());
3551 EXPECT_EQ("in_root_name", in_root
.GetNonUniqueName());
3552 EXPECT_EQ(TestIdFactory::root(), in_root
.GetParentId());
3554 Entry
in_in_root(&trans
, GET_BY_ID
, in_in_root_id
);
3555 ASSERT_TRUE(in_in_root
.good());
3556 EXPECT_EQ("in_in_root_name", in_in_root
.GetNonUniqueName());
3557 EXPECT_EQ(in_root_id
, in_in_root
.GetParentId());
3561 TEST_F(SyncerTest
, DirectoryCommitTest
) {
3562 syncable::Id in_root_id
, in_dir_id
;
3563 int64 foo_metahandle
;
3564 int64 bar_metahandle
;
3567 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3568 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "foo");
3569 ASSERT_TRUE(parent
.good());
3570 parent
.PutIsUnsynced(true);
3571 parent
.PutIsDir(true);
3572 parent
.PutSpecifics(DefaultBookmarkSpecifics());
3573 in_root_id
= parent
.GetId();
3574 foo_metahandle
= parent
.GetMetahandle();
3576 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent
.GetId(), "bar");
3577 ASSERT_TRUE(child
.good());
3578 child
.PutIsUnsynced(true);
3579 child
.PutIsDir(true);
3580 child
.PutSpecifics(DefaultBookmarkSpecifics());
3581 bar_metahandle
= child
.GetMetahandle();
3582 in_dir_id
= parent
.GetId();
3586 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3587 Entry
fail_by_old_id_entry(&trans
, GET_BY_ID
, in_root_id
);
3588 ASSERT_FALSE(fail_by_old_id_entry
.good());
3590 Entry
foo_entry(&trans
, GET_BY_HANDLE
, foo_metahandle
);
3591 ASSERT_TRUE(foo_entry
.good());
3592 EXPECT_EQ("foo", foo_entry
.GetNonUniqueName());
3593 EXPECT_NE(foo_entry
.GetId(), in_root_id
);
3595 Entry
bar_entry(&trans
, GET_BY_HANDLE
, bar_metahandle
);
3596 ASSERT_TRUE(bar_entry
.good());
3597 EXPECT_EQ("bar", bar_entry
.GetNonUniqueName());
3598 EXPECT_NE(bar_entry
.GetId(), in_dir_id
);
3599 EXPECT_EQ(foo_entry
.GetId(), bar_entry
.GetParentId());
3603 TEST_F(SyncerTest
, TestClientCommandDuringUpdate
) {
3604 using sync_pb::ClientCommand
;
3606 ClientCommand
* command
= new ClientCommand();
3607 command
->set_set_sync_poll_interval(8);
3608 command
->set_set_sync_long_poll_interval(800);
3609 command
->set_sessions_commit_delay_seconds(3141);
3610 sync_pb::CustomNudgeDelay
* bookmark_delay
=
3611 command
->add_custom_nudge_delays();
3612 bookmark_delay
->set_datatype_id(
3613 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3614 bookmark_delay
->set_delay_ms(950);
3615 command
->set_client_invalidation_hint_buffer_size(11);
3616 mock_server_
->AddUpdateDirectory(1, 0, "in_root", 1, 1,
3617 foreign_cache_guid(), "-1");
3618 mock_server_
->SetGUClientCommand(command
);
3621 EXPECT_EQ(TimeDelta::FromSeconds(8), last_short_poll_interval_received_
);
3622 EXPECT_EQ(TimeDelta::FromSeconds(800), last_long_poll_interval_received_
);
3623 EXPECT_EQ(TimeDelta::FromSeconds(3141), last_sessions_commit_delay_
);
3624 EXPECT_EQ(TimeDelta::FromMilliseconds(950), last_bookmarks_commit_delay_
);
3625 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_
);
3627 command
= new ClientCommand();
3628 command
->set_set_sync_poll_interval(180);
3629 command
->set_set_sync_long_poll_interval(190);
3630 command
->set_sessions_commit_delay_seconds(2718);
3631 bookmark_delay
= command
->add_custom_nudge_delays();
3632 bookmark_delay
->set_datatype_id(
3633 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3634 bookmark_delay
->set_delay_ms(1050);
3635 command
->set_client_invalidation_hint_buffer_size(9);
3636 mock_server_
->AddUpdateDirectory(
3637 1, 0, "in_root", 1, 1, foreign_cache_guid(), "-1");
3638 mock_server_
->SetGUClientCommand(command
);
3641 EXPECT_EQ(TimeDelta::FromSeconds(180), last_short_poll_interval_received_
);
3642 EXPECT_EQ(TimeDelta::FromSeconds(190), last_long_poll_interval_received_
);
3643 EXPECT_EQ(TimeDelta::FromSeconds(2718), last_sessions_commit_delay_
);
3644 EXPECT_EQ(TimeDelta::FromMilliseconds(1050), last_bookmarks_commit_delay_
);
3645 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_
);
3648 TEST_F(SyncerTest
, TestClientCommandDuringCommit
) {
3649 using sync_pb::ClientCommand
;
3651 ClientCommand
* command
= new ClientCommand();
3652 command
->set_set_sync_poll_interval(8);
3653 command
->set_set_sync_long_poll_interval(800);
3654 command
->set_sessions_commit_delay_seconds(3141);
3655 sync_pb::CustomNudgeDelay
* bookmark_delay
=
3656 command
->add_custom_nudge_delays();
3657 bookmark_delay
->set_datatype_id(
3658 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3659 bookmark_delay
->set_delay_ms(950);
3660 command
->set_client_invalidation_hint_buffer_size(11);
3661 CreateUnsyncedDirectory("X", "id_X");
3662 mock_server_
->SetCommitClientCommand(command
);
3665 EXPECT_EQ(TimeDelta::FromSeconds(8), last_short_poll_interval_received_
);
3666 EXPECT_EQ(TimeDelta::FromSeconds(800), last_long_poll_interval_received_
);
3667 EXPECT_EQ(TimeDelta::FromSeconds(3141), last_sessions_commit_delay_
);
3668 EXPECT_EQ(TimeDelta::FromMilliseconds(950), last_bookmarks_commit_delay_
);
3669 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_
);
3671 command
= new ClientCommand();
3672 command
->set_set_sync_poll_interval(180);
3673 command
->set_set_sync_long_poll_interval(190);
3674 command
->set_sessions_commit_delay_seconds(2718);
3675 bookmark_delay
= command
->add_custom_nudge_delays();
3676 bookmark_delay
->set_datatype_id(
3677 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3678 bookmark_delay
->set_delay_ms(1050);
3679 command
->set_client_invalidation_hint_buffer_size(9);
3680 CreateUnsyncedDirectory("Y", "id_Y");
3681 mock_server_
->SetCommitClientCommand(command
);
3684 EXPECT_EQ(TimeDelta::FromSeconds(180), last_short_poll_interval_received_
);
3685 EXPECT_EQ(TimeDelta::FromSeconds(190), last_long_poll_interval_received_
);
3686 EXPECT_EQ(TimeDelta::FromSeconds(2718), last_sessions_commit_delay_
);
3687 EXPECT_EQ(TimeDelta::FromMilliseconds(1050), last_bookmarks_commit_delay_
);
3688 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_
);
3691 TEST_F(SyncerTest
, EnsureWeSendUpOldParent
) {
3692 syncable::Id folder_one_id
= ids_
.FromNumber(1);
3693 syncable::Id folder_two_id
= ids_
.FromNumber(2);
3695 mock_server_
->AddUpdateDirectory(folder_one_id
, TestIdFactory::root(),
3696 "folder_one", 1, 1, foreign_cache_guid(), "-1");
3697 mock_server_
->AddUpdateDirectory(folder_two_id
, TestIdFactory::root(),
3698 "folder_two", 1, 1, foreign_cache_guid(), "-2");
3701 // A moved entry should send an "old parent."
3702 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3703 MutableEntry
entry(&trans
, GET_BY_ID
, folder_one_id
);
3704 ASSERT_TRUE(entry
.good());
3705 entry
.PutParentId(folder_two_id
);
3706 entry
.PutIsUnsynced(true);
3707 // A new entry should send no "old parent."
3708 MutableEntry
create(
3709 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_folder");
3710 create
.PutIsUnsynced(true);
3711 create
.PutSpecifics(DefaultBookmarkSpecifics());
3714 const sync_pb::CommitMessage
& commit
= mock_server_
->last_sent_commit();
3715 ASSERT_EQ(2, commit
.entries_size());
3716 EXPECT_TRUE(commit
.entries(0).parent_id_string() == "2");
3717 EXPECT_TRUE(commit
.entries(0).old_parent_id() == "0");
3718 EXPECT_FALSE(commit
.entries(1).has_old_parent_id());
3721 TEST_F(SyncerTest
, Test64BitVersionSupport
) {
3722 int64 really_big_int
= std::numeric_limits
<int64
>::max() - 12;
3723 const string
name("ringo's dang orang ran rings around my o-ring");
3724 int64 item_metahandle
;
3726 // Try writing max int64 to the version fields of a meta entry.
3728 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3729 MutableEntry
entry(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), name
);
3730 ASSERT_TRUE(entry
.good());
3731 entry
.PutBaseVersion(really_big_int
);
3732 entry
.PutServerVersion(really_big_int
);
3733 entry
.PutId(ids_
.NewServerId());
3734 item_metahandle
= entry
.GetMetahandle();
3736 // Now read it back out and make sure the value is max int64.
3737 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
3738 Entry
entry(&rtrans
, syncable::GET_BY_HANDLE
, item_metahandle
);
3739 ASSERT_TRUE(entry
.good());
3740 EXPECT_TRUE(really_big_int
== entry
.GetBaseVersion());
3743 TEST_F(SyncerTest
, TestSimpleUndelete
) {
3744 Id id
= ids_
.MakeServer("undeletion item"), root
= TestIdFactory::root();
3745 mock_server_
->set_conflict_all_commits(true);
3746 // Let there be an entry from the server.
3747 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 1, 10,
3748 foreign_cache_guid(), "-1");
3750 // Check it out and delete it.
3752 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3753 MutableEntry
entry(&wtrans
, GET_BY_ID
, id
);
3754 ASSERT_TRUE(entry
.good());
3755 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3756 EXPECT_FALSE(entry
.GetIsUnsynced());
3757 EXPECT_FALSE(entry
.GetIsDel());
3758 // Delete it locally.
3759 entry
.PutIsDel(true);
3762 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3764 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3765 Entry
entry(&trans
, GET_BY_ID
, id
);
3766 ASSERT_TRUE(entry
.good());
3767 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3768 EXPECT_FALSE(entry
.GetIsUnsynced());
3769 EXPECT_TRUE(entry
.GetIsDel());
3770 EXPECT_FALSE(entry
.GetServerIsDel());
3773 // Update from server confirming deletion.
3774 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 11,
3775 foreign_cache_guid(), "-1");
3776 mock_server_
->SetLastUpdateDeleted();
3778 // IS_DEL AND SERVER_IS_DEL now both true.
3780 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3781 Entry
entry(&trans
, GET_BY_ID
, id
);
3782 ASSERT_TRUE(entry
.good());
3783 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3784 EXPECT_FALSE(entry
.GetIsUnsynced());
3785 EXPECT_TRUE(entry
.GetIsDel());
3786 EXPECT_TRUE(entry
.GetServerIsDel());
3788 // Undelete from server.
3789 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 12,
3790 foreign_cache_guid(), "-1");
3792 // IS_DEL and SERVER_IS_DEL now both false.
3794 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3795 Entry
entry(&trans
, GET_BY_ID
, id
);
3796 ASSERT_TRUE(entry
.good());
3797 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3798 EXPECT_FALSE(entry
.GetIsUnsynced());
3799 EXPECT_FALSE(entry
.GetIsDel());
3800 EXPECT_FALSE(entry
.GetServerIsDel());
3804 TEST_F(SyncerTest
, TestUndeleteWithMissingDeleteUpdate
) {
3805 Id id
= ids_
.MakeServer("undeletion item"), root
= TestIdFactory::root();
3806 // Let there be a entry, from the server.
3807 mock_server_
->set_conflict_all_commits(true);
3808 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 1, 10,
3809 foreign_cache_guid(), "-1");
3811 // Check it out and delete it.
3813 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3814 MutableEntry
entry(&wtrans
, GET_BY_ID
, id
);
3815 ASSERT_TRUE(entry
.good());
3816 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3817 EXPECT_FALSE(entry
.GetIsUnsynced());
3818 EXPECT_FALSE(entry
.GetIsDel());
3819 // Delete it locally.
3820 entry
.PutIsDel(true);
3823 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3825 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3826 Entry
entry(&trans
, GET_BY_ID
, id
);
3827 ASSERT_TRUE(entry
.good());
3828 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3829 EXPECT_FALSE(entry
.GetIsUnsynced());
3830 EXPECT_TRUE(entry
.GetIsDel());
3831 EXPECT_FALSE(entry
.GetServerIsDel());
3834 // Say we do not get an update from server confirming deletion. Undelete
3836 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 12,
3837 foreign_cache_guid(), "-1");
3839 // IS_DEL and SERVER_IS_DEL now both false.
3841 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3842 Entry
entry(&trans
, GET_BY_ID
, id
);
3843 ASSERT_TRUE(entry
.good());
3844 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3845 EXPECT_FALSE(entry
.GetIsUnsynced());
3846 EXPECT_FALSE(entry
.GetIsDel());
3847 EXPECT_FALSE(entry
.GetServerIsDel());
3851 TEST_F(SyncerTest
, TestUndeleteIgnoreCorrectlyUnappliedUpdate
) {
3852 Id id1
= ids_
.MakeServer("first"), id2
= ids_
.MakeServer("second");
3853 Id root
= TestIdFactory::root();
3854 // Duplicate! expect path clashing!
3855 mock_server_
->set_conflict_all_commits(true);
3856 mock_server_
->AddUpdateBookmark(id1
, root
, "foo", 1, 10,
3857 foreign_cache_guid(), "-1");
3858 mock_server_
->AddUpdateBookmark(id2
, root
, "foo", 1, 10,
3859 foreign_cache_guid(), "-2");
3861 mock_server_
->AddUpdateBookmark(id2
, root
, "foo2", 2, 20,
3862 foreign_cache_guid(), "-2");
3863 SyncShareNudge(); // Now just don't explode.
3866 TEST_F(SyncerTest
, ClientTagServerCreatedUpdatesWork
) {
3867 mock_server_
->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
3868 foreign_cache_guid(), "-1");
3869 mock_server_
->SetLastUpdateClientTag("permfolder");
3874 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3875 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
3876 ASSERT_TRUE(perm_folder
.good());
3877 EXPECT_FALSE(perm_folder
.GetIsDel());
3878 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
3879 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
3880 EXPECT_EQ(perm_folder
.GetUniqueClientTag(), "permfolder");
3881 EXPECT_EQ(perm_folder
.GetNonUniqueName(), "permitem1");
3884 mock_server_
->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
3885 foreign_cache_guid(), "-1");
3886 mock_server_
->SetLastUpdateClientTag("permfolder");
3890 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3892 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
3893 ASSERT_TRUE(perm_folder
.good());
3894 EXPECT_FALSE(perm_folder
.GetIsDel());
3895 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
3896 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
3897 EXPECT_EQ(perm_folder
.GetUniqueClientTag(), "permfolder");
3898 EXPECT_EQ(perm_folder
.GetNonUniqueName(), "permitem_renamed");
3902 TEST_F(SyncerTest
, ClientTagIllegalUpdateIgnored
) {
3903 mock_server_
->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
3904 foreign_cache_guid(), "-1");
3905 mock_server_
->SetLastUpdateClientTag("permfolder");
3910 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3911 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
3912 ASSERT_TRUE(perm_folder
.good());
3913 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
3914 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
3915 EXPECT_EQ(perm_folder
.GetUniqueClientTag(), "permfolder");
3916 EXPECT_TRUE(perm_folder
.GetNonUniqueName()== "permitem1");
3917 EXPECT_TRUE(perm_folder
.GetId().ServerKnows());
3920 mock_server_
->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
3921 foreign_cache_guid(), "-1");
3922 mock_server_
->SetLastUpdateClientTag("wrongtag");
3926 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3928 // This update is rejected because it has the same ID, but a
3929 // different tag than one that is already on the client.
3930 // The client has a ServerKnows ID, which cannot be overwritten.
3931 Entry
rejected_update(&trans
, GET_BY_CLIENT_TAG
, "wrongtag");
3932 EXPECT_FALSE(rejected_update
.good());
3934 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
3935 ASSERT_TRUE(perm_folder
.good());
3936 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
3937 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
3938 EXPECT_EQ(perm_folder
.GetNonUniqueName(), "permitem1");
3942 TEST_F(SyncerTest
, ClientTagUncommittedTagMatchesUpdate
) {
3943 int64 original_metahandle
= 0;
3946 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3948 &trans
, CREATE
, PREFERENCES
, ids_
.root(), "name");
3949 ASSERT_TRUE(pref
.good());
3950 pref
.PutUniqueClientTag("tag");
3951 pref
.PutIsUnsynced(true);
3952 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
3953 EXPECT_FALSE(pref
.GetId().ServerKnows());
3954 original_metahandle
= pref
.GetMetahandle();
3957 syncable::Id server_id
= TestIdFactory::MakeServer("id");
3958 mock_server_
->AddUpdatePref(server_id
.GetServerId(),
3959 ids_
.root().GetServerId(),
3961 mock_server_
->set_conflict_all_commits(true);
3964 // This should cause client tag reunion, preserving the metahandle.
3966 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3968 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
3969 ASSERT_TRUE(pref
.good());
3970 EXPECT_FALSE(pref
.GetIsDel());
3971 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
3972 EXPECT_TRUE(pref
.GetIsUnsynced());
3973 EXPECT_EQ(10, pref
.GetBaseVersion());
3974 // Entry should have been given the new ID while preserving the
3975 // metahandle; client should have won the conflict resolution.
3976 EXPECT_EQ(original_metahandle
, pref
.GetMetahandle());
3977 EXPECT_EQ("tag", pref
.GetUniqueClientTag());
3978 EXPECT_TRUE(pref
.GetId().ServerKnows());
3981 mock_server_
->set_conflict_all_commits(false);
3984 // The resolved entry ought to commit cleanly.
3986 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3988 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
3989 ASSERT_TRUE(pref
.good());
3990 EXPECT_FALSE(pref
.GetIsDel());
3991 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
3992 EXPECT_FALSE(pref
.GetIsUnsynced());
3993 EXPECT_TRUE(10 < pref
.GetBaseVersion());
3994 // Entry should have been given the new ID while preserving the
3995 // metahandle; client should have won the conflict resolution.
3996 EXPECT_EQ(original_metahandle
, pref
.GetMetahandle());
3997 EXPECT_EQ("tag", pref
.GetUniqueClientTag());
3998 EXPECT_TRUE(pref
.GetId().ServerKnows());
4002 TEST_F(SyncerTest
, ClientTagConflictWithDeletedLocalEntry
) {
4004 // Create a deleted local entry with a unique client tag.
4005 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4007 &trans
, CREATE
, PREFERENCES
, ids_
.root(), "name");
4008 ASSERT_TRUE(pref
.good());
4009 ASSERT_FALSE(pref
.GetId().ServerKnows());
4010 pref
.PutUniqueClientTag("tag");
4011 pref
.PutIsUnsynced(true);
4013 // Note: IS_DEL && !ServerKnows() will clear the UNSYNCED bit.
4014 // (We never attempt to commit server-unknown deleted items, so this
4015 // helps us clean up those entries).
4016 pref
.PutIsDel(true);
4019 // Prepare an update with the same unique client tag.
4020 syncable::Id server_id
= TestIdFactory::MakeServer("id");
4021 mock_server_
->AddUpdatePref(server_id
.GetServerId(),
4022 ids_
.root().GetServerId(),
4026 // The local entry will be overwritten.
4028 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4030 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
4031 ASSERT_TRUE(pref
.good());
4032 ASSERT_TRUE(pref
.GetId().ServerKnows());
4033 EXPECT_FALSE(pref
.GetIsDel());
4034 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
4035 EXPECT_FALSE(pref
.GetIsUnsynced());
4036 EXPECT_EQ(pref
.GetBaseVersion(), 10);
4037 EXPECT_EQ(pref
.GetUniqueClientTag(), "tag");
4041 TEST_F(SyncerTest
, ClientTagUpdateClashesWithLocalEntry
) {
4042 // This test is written assuming that ID comparison
4043 // will work out in a particular way.
4044 EXPECT_TRUE(ids_
.FromNumber(1) < ids_
.FromNumber(2));
4045 EXPECT_TRUE(ids_
.FromNumber(3) < ids_
.FromNumber(4));
4047 syncable::Id id1
= TestIdFactory::MakeServer("1");
4048 mock_server_
->AddUpdatePref(id1
.GetServerId(), ids_
.root().GetServerId(),
4051 syncable::Id id4
= TestIdFactory::MakeServer("4");
4052 mock_server_
->AddUpdatePref(id4
.GetServerId(), ids_
.root().GetServerId(),
4055 mock_server_
->set_conflict_all_commits(true);
4058 int64 tag1_metahandle
= syncable::kInvalidMetaHandle
;
4059 int64 tag2_metahandle
= syncable::kInvalidMetaHandle
;
4060 // This should cause client tag overwrite.
4062 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4064 Entry
tag1(&trans
, GET_BY_CLIENT_TAG
, "tag1");
4065 ASSERT_TRUE(tag1
.good());
4066 ASSERT_TRUE(tag1
.GetId().ServerKnows());
4067 ASSERT_TRUE(id1
== tag1
.GetId());
4068 EXPECT_FALSE(tag1
.GetIsDel());
4069 EXPECT_FALSE(tag1
.GetIsUnappliedUpdate());
4070 EXPECT_FALSE(tag1
.GetIsUnsynced());
4071 EXPECT_EQ(10, tag1
.GetBaseVersion());
4072 EXPECT_EQ("tag1", tag1
.GetUniqueClientTag());
4073 tag1_metahandle
= tag1
.GetMetahandle();
4075 Entry
tag2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
4076 ASSERT_TRUE(tag2
.good());
4077 ASSERT_TRUE(tag2
.GetId().ServerKnows());
4078 ASSERT_TRUE(id4
== tag2
.GetId());
4079 EXPECT_FALSE(tag2
.GetIsDel());
4080 EXPECT_FALSE(tag2
.GetIsUnappliedUpdate());
4081 EXPECT_FALSE(tag2
.GetIsUnsynced());
4082 EXPECT_EQ(11, tag2
.GetBaseVersion());
4083 EXPECT_EQ("tag2", tag2
.GetUniqueClientTag());
4084 tag2_metahandle
= tag2
.GetMetahandle();
4086 syncable::Directory::Metahandles children
;
4087 directory()->GetChildHandlesById(&trans
, trans
.root_id(), &children
);
4088 ASSERT_EQ(2U, children
.size());
4091 syncable::Id id2
= TestIdFactory::MakeServer("2");
4092 mock_server_
->AddUpdatePref(id2
.GetServerId(), ids_
.root().GetServerId(),
4094 syncable::Id id3
= TestIdFactory::MakeServer("3");
4095 mock_server_
->AddUpdatePref(id3
.GetServerId(), ids_
.root().GetServerId(),
4100 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4102 Entry
tag1(&trans
, GET_BY_CLIENT_TAG
, "tag1");
4103 ASSERT_TRUE(tag1
.good());
4104 ASSERT_TRUE(tag1
.GetId().ServerKnows());
4105 ASSERT_EQ(id1
, tag1
.GetId())
4106 << "ID 1 should be kept, since it was less than ID 2.";
4107 EXPECT_FALSE(tag1
.GetIsDel());
4108 EXPECT_FALSE(tag1
.GetIsUnappliedUpdate());
4109 EXPECT_FALSE(tag1
.GetIsUnsynced());
4110 EXPECT_EQ(10, tag1
.GetBaseVersion());
4111 EXPECT_EQ("tag1", tag1
.GetUniqueClientTag());
4112 EXPECT_EQ(tag1_metahandle
, tag1
.GetMetahandle());
4114 Entry
tag2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
4115 ASSERT_TRUE(tag2
.good());
4116 ASSERT_TRUE(tag2
.GetId().ServerKnows());
4117 ASSERT_EQ(id3
, tag2
.GetId())
4118 << "ID 3 should be kept, since it was less than ID 4.";
4119 EXPECT_FALSE(tag2
.GetIsDel());
4120 EXPECT_FALSE(tag2
.GetIsUnappliedUpdate());
4121 EXPECT_FALSE(tag2
.GetIsUnsynced());
4122 EXPECT_EQ(13, tag2
.GetBaseVersion());
4123 EXPECT_EQ("tag2", tag2
.GetUniqueClientTag());
4124 EXPECT_EQ(tag2_metahandle
, tag2
.GetMetahandle());
4126 syncable::Directory::Metahandles children
;
4127 directory()->GetChildHandlesById(&trans
, trans
.root_id(), &children
);
4128 ASSERT_EQ(2U, children
.size());
4132 TEST_F(SyncerTest
, ClientTagClashWithinBatchOfUpdates
) {
4133 // This test is written assuming that ID comparison
4134 // will work out in a particular way.
4135 EXPECT_TRUE(ids_
.FromNumber(1) < ids_
.FromNumber(4));
4136 EXPECT_TRUE(ids_
.FromNumber(201) < ids_
.FromNumber(205));
4138 // Least ID: winner.
4139 mock_server_
->AddUpdatePref(ids_
.FromNumber(1).GetServerId(),
4140 ids_
.root().GetServerId(), "tag a", 1, 10);
4141 mock_server_
->AddUpdatePref(ids_
.FromNumber(2).GetServerId(),
4142 ids_
.root().GetServerId(), "tag a", 11, 110);
4143 mock_server_
->AddUpdatePref(ids_
.FromNumber(3).GetServerId(),
4144 ids_
.root().GetServerId(), "tag a", 12, 120);
4145 mock_server_
->AddUpdatePref(ids_
.FromNumber(4).GetServerId(),
4146 ids_
.root().GetServerId(), "tag a", 13, 130);
4148 mock_server_
->AddUpdatePref(ids_
.FromNumber(105).GetServerId(),
4149 ids_
.root().GetServerId(), "tag b", 14, 140);
4150 mock_server_
->AddUpdatePref(ids_
.FromNumber(102).GetServerId(),
4151 ids_
.root().GetServerId(), "tag b", 15, 150);
4152 // Least ID: winner.
4153 mock_server_
->AddUpdatePref(ids_
.FromNumber(101).GetServerId(),
4154 ids_
.root().GetServerId(), "tag b", 16, 160);
4155 mock_server_
->AddUpdatePref(ids_
.FromNumber(104).GetServerId(),
4156 ids_
.root().GetServerId(), "tag b", 17, 170);
4158 mock_server_
->AddUpdatePref(ids_
.FromNumber(205).GetServerId(),
4159 ids_
.root().GetServerId(), "tag c", 18, 180);
4160 mock_server_
->AddUpdatePref(ids_
.FromNumber(202).GetServerId(),
4161 ids_
.root().GetServerId(), "tag c", 19, 190);
4162 mock_server_
->AddUpdatePref(ids_
.FromNumber(204).GetServerId(),
4163 ids_
.root().GetServerId(), "tag c", 20, 200);
4164 // Least ID: winner.
4165 mock_server_
->AddUpdatePref(ids_
.FromNumber(201).GetServerId(),
4166 ids_
.root().GetServerId(), "tag c", 21, 210);
4168 mock_server_
->set_conflict_all_commits(true);
4171 // This should cause client tag overwrite.
4173 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4175 Entry
tag_a(&trans
, GET_BY_CLIENT_TAG
, "tag a");
4176 ASSERT_TRUE(tag_a
.good());
4177 EXPECT_TRUE(tag_a
.GetId().ServerKnows());
4178 EXPECT_EQ(ids_
.FromNumber(1), tag_a
.GetId());
4179 EXPECT_FALSE(tag_a
.GetIsDel());
4180 EXPECT_FALSE(tag_a
.GetIsUnappliedUpdate());
4181 EXPECT_FALSE(tag_a
.GetIsUnsynced());
4182 EXPECT_EQ(1, tag_a
.GetBaseVersion());
4183 EXPECT_EQ("tag a", tag_a
.GetUniqueClientTag());
4185 Entry
tag_b(&trans
, GET_BY_CLIENT_TAG
, "tag b");
4186 ASSERT_TRUE(tag_b
.good());
4187 EXPECT_TRUE(tag_b
.GetId().ServerKnows());
4188 EXPECT_EQ(ids_
.FromNumber(101), tag_b
.GetId());
4189 EXPECT_FALSE(tag_b
.GetIsDel());
4190 EXPECT_FALSE(tag_b
.GetIsUnappliedUpdate());
4191 EXPECT_FALSE(tag_b
.GetIsUnsynced());
4192 EXPECT_EQ(16, tag_b
.GetBaseVersion());
4193 EXPECT_EQ("tag b", tag_b
.GetUniqueClientTag());
4195 Entry
tag_c(&trans
, GET_BY_CLIENT_TAG
, "tag c");
4196 ASSERT_TRUE(tag_c
.good());
4197 EXPECT_TRUE(tag_c
.GetId().ServerKnows());
4198 EXPECT_EQ(ids_
.FromNumber(201), tag_c
.GetId());
4199 EXPECT_FALSE(tag_c
.GetIsDel());
4200 EXPECT_FALSE(tag_c
.GetIsUnappliedUpdate());
4201 EXPECT_FALSE(tag_c
.GetIsUnsynced());
4202 EXPECT_EQ(21, tag_c
.GetBaseVersion());
4203 EXPECT_EQ("tag c", tag_c
.GetUniqueClientTag());
4205 syncable::Directory::Metahandles children
;
4206 directory()->GetChildHandlesById(&trans
, trans
.root_id(), &children
);
4207 ASSERT_EQ(3U, children
.size());
4211 TEST_F(SyncerTest
, UniqueServerTagUpdates
) {
4212 // As a hurdle, introduce an item whose name is the same as the tag value
4214 int64 hurdle_handle
= CreateUnsyncedDirectory("bob", "id_bob");
4216 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4217 Entry
hurdle(&trans
, GET_BY_HANDLE
, hurdle_handle
);
4218 ASSERT_TRUE(hurdle
.good());
4219 ASSERT_TRUE(!hurdle
.GetIsDel());
4220 ASSERT_TRUE(hurdle
.GetUniqueServerTag().empty());
4221 ASSERT_TRUE(hurdle
.GetNonUniqueName()== "bob");
4223 // Try to lookup by the tagname. These should fail.
4224 Entry
tag_alpha(&trans
, GET_BY_SERVER_TAG
, "alpha");
4225 EXPECT_FALSE(tag_alpha
.good());
4226 Entry
tag_bob(&trans
, GET_BY_SERVER_TAG
, "bob");
4227 EXPECT_FALSE(tag_bob
.good());
4230 // Now download some tagged items as updates.
4231 mock_server_
->AddUpdateDirectory(
4232 1, 0, "update1", 1, 10, std::string(), std::string());
4233 mock_server_
->SetLastUpdateServerTag("alpha");
4234 mock_server_
->AddUpdateDirectory(
4235 2, 0, "update2", 2, 20, std::string(), std::string());
4236 mock_server_
->SetLastUpdateServerTag("bob");
4240 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4242 // The new items should be applied as new entries, and we should be able
4243 // to look them up by their tag values.
4244 Entry
tag_alpha(&trans
, GET_BY_SERVER_TAG
, "alpha");
4245 ASSERT_TRUE(tag_alpha
.good());
4246 ASSERT_TRUE(!tag_alpha
.GetIsDel());
4247 ASSERT_TRUE(tag_alpha
.GetUniqueServerTag()== "alpha");
4248 ASSERT_TRUE(tag_alpha
.GetNonUniqueName()== "update1");
4249 Entry
tag_bob(&trans
, GET_BY_SERVER_TAG
, "bob");
4250 ASSERT_TRUE(tag_bob
.good());
4251 ASSERT_TRUE(!tag_bob
.GetIsDel());
4252 ASSERT_TRUE(tag_bob
.GetUniqueServerTag()== "bob");
4253 ASSERT_TRUE(tag_bob
.GetNonUniqueName()== "update2");
4254 // The old item should be unchanged.
4255 Entry
hurdle(&trans
, GET_BY_HANDLE
, hurdle_handle
);
4256 ASSERT_TRUE(hurdle
.good());
4257 ASSERT_TRUE(!hurdle
.GetIsDel());
4258 ASSERT_TRUE(hurdle
.GetUniqueServerTag().empty());
4259 ASSERT_TRUE(hurdle
.GetNonUniqueName()== "bob");
4263 TEST_F(SyncerTest
, GetUpdatesSetsRequestedTypes
) {
4264 // The expectations of this test happen in the MockConnectionManager's
4265 // GetUpdates handler. EnableDatatype sets the expectation value from our
4266 // set of enabled/disabled datatypes.
4267 EnableDatatype(BOOKMARKS
);
4269 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4271 EnableDatatype(AUTOFILL
);
4273 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4275 EnableDatatype(PREFERENCES
);
4277 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4279 DisableDatatype(BOOKMARKS
);
4281 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4283 DisableDatatype(AUTOFILL
);
4285 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4287 DisableDatatype(PREFERENCES
);
4288 EnableDatatype(AUTOFILL
);
4290 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4293 // A typical scenario: server and client each have one update for the other.
4294 // This is the "happy path" alternative to UpdateFailsThenDontCommit.
4295 TEST_F(SyncerTest
, UpdateThenCommit
) {
4296 syncable::Id to_receive
= ids_
.NewServerId();
4297 syncable::Id to_commit
= ids_
.NewLocalId();
4299 mock_server_
->AddUpdateDirectory(to_receive
, ids_
.root(), "x", 1, 10,
4300 foreign_cache_guid(), "-1");
4301 int64 commit_handle
= CreateUnsyncedDirectory("y", to_commit
);
4304 // The sync cycle should have included a GetUpdate, then a commit. By the
4305 // time the commit happened, we should have known for sure that there were no
4306 // hierarchy conflicts, and reported this fact to the server.
4307 ASSERT_TRUE(mock_server_
->last_request().has_commit());
4308 VerifyNoHierarchyConflictsReported(mock_server_
->last_request());
4310 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4312 Entry
received(&trans
, GET_BY_ID
, to_receive
);
4313 ASSERT_TRUE(received
.good());
4314 EXPECT_FALSE(received
.GetIsUnsynced());
4315 EXPECT_FALSE(received
.GetIsUnappliedUpdate());
4317 Entry
committed(&trans
, GET_BY_HANDLE
, commit_handle
);
4318 ASSERT_TRUE(committed
.good());
4319 EXPECT_FALSE(committed
.GetIsUnsynced());
4320 EXPECT_FALSE(committed
.GetIsUnappliedUpdate());
4323 // Same as above, but this time we fail to download updates.
4324 // We should not attempt to commit anything unless we successfully downloaded
4325 // updates, otherwise we risk causing a server-side conflict.
4326 TEST_F(SyncerTest
, UpdateFailsThenDontCommit
) {
4327 syncable::Id to_receive
= ids_
.NewServerId();
4328 syncable::Id to_commit
= ids_
.NewLocalId();
4330 mock_server_
->AddUpdateDirectory(to_receive
, ids_
.root(), "x", 1, 10,
4331 foreign_cache_guid(), "-1");
4332 int64 commit_handle
= CreateUnsyncedDirectory("y", to_commit
);
4333 mock_server_
->FailNextPostBufferToPathCall();
4336 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4338 // We did not receive this update.
4339 Entry
received(&trans
, GET_BY_ID
, to_receive
);
4340 ASSERT_FALSE(received
.good());
4342 // And our local update remains unapplied.
4343 Entry
committed(&trans
, GET_BY_HANDLE
, commit_handle
);
4344 ASSERT_TRUE(committed
.good());
4345 EXPECT_TRUE(committed
.GetIsUnsynced());
4346 EXPECT_FALSE(committed
.GetIsUnappliedUpdate());
4348 // Inform the Mock we won't be fetching all updates.
4349 mock_server_
->ClearUpdatesQueue();
4352 // Downloads two updates and applies them successfully.
4353 // This is the "happy path" alternative to ConfigureFailsDontApplyUpdates.
4354 TEST_F(SyncerTest
, ConfigureDownloadsTwoBatchesSuccess
) {
4355 syncable::Id node1
= ids_
.NewServerId();
4356 syncable::Id node2
= ids_
.NewServerId();
4358 // Construct the first GetUpdates response.
4359 mock_server_
->AddUpdateDirectory(node1
, ids_
.root(), "one", 1, 10,
4360 foreign_cache_guid(), "-2");
4361 mock_server_
->SetChangesRemaining(1);
4362 mock_server_
->NextUpdateBatch();
4364 // Construct the second GetUpdates response.
4365 mock_server_
->AddUpdateDirectory(node2
, ids_
.root(), "two", 1, 20,
4366 foreign_cache_guid(), "-2");
4368 SyncShareConfigure();
4370 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4371 // Both nodes should be downloaded and applied.
4373 Entry
n1(&trans
, GET_BY_ID
, node1
);
4374 ASSERT_TRUE(n1
.good());
4375 EXPECT_FALSE(n1
.GetIsUnappliedUpdate());
4377 Entry
n2(&trans
, GET_BY_ID
, node2
);
4378 ASSERT_TRUE(n2
.good());
4379 EXPECT_FALSE(n2
.GetIsUnappliedUpdate());
4382 // Same as the above case, but this time the second batch fails to download.
4383 TEST_F(SyncerTest
, ConfigureFailsDontApplyUpdates
) {
4384 syncable::Id node1
= ids_
.NewServerId();
4385 syncable::Id node2
= ids_
.NewServerId();
4387 // The scenario: we have two batches of updates with one update each. A
4388 // normal confgure step would download all the updates one batch at a time and
4389 // apply them. This configure will succeed in downloading the first batch
4390 // then fail when downloading the second.
4391 mock_server_
->FailNthPostBufferToPathCall(2);
4393 // Construct the first GetUpdates response.
4394 mock_server_
->AddUpdateDirectory(node1
, ids_
.root(), "one", 1, 10,
4395 foreign_cache_guid(), "-1");
4396 mock_server_
->SetChangesRemaining(1);
4397 mock_server_
->NextUpdateBatch();
4399 // Consutrct the second GetUpdates response.
4400 mock_server_
->AddUpdateDirectory(node2
, ids_
.root(), "two", 1, 20,
4401 foreign_cache_guid(), "-2");
4403 SyncShareConfigure();
4405 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4407 // The first node was downloaded, but not applied.
4408 Entry
n1(&trans
, GET_BY_ID
, node1
);
4409 ASSERT_TRUE(n1
.good());
4410 EXPECT_TRUE(n1
.GetIsUnappliedUpdate());
4412 // The second node was not downloaded.
4413 Entry
n2(&trans
, GET_BY_ID
, node2
);
4414 EXPECT_FALSE(n2
.good());
4416 // One update remains undownloaded.
4417 mock_server_
->ClearUpdatesQueue();
4420 TEST_F(SyncerTest
, GetKeySuccess
) {
4422 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4423 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4426 SyncShareConfigure();
4428 EXPECT_EQ(session_
->status_controller().last_get_key_result(), SYNCER_OK
);
4430 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4431 EXPECT_FALSE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4435 TEST_F(SyncerTest
, GetKeyEmpty
) {
4437 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4438 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4441 mock_server_
->SetKeystoreKey(std::string());
4442 SyncShareConfigure();
4444 EXPECT_NE(session_
->status_controller().last_get_key_result(), SYNCER_OK
);
4446 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4447 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4451 // Tests specifically related to bookmark (and therefore no client tags) sync
4452 // logic. Entities without client tags have custom logic in parts of the code,
4453 // and hence are not covered by e.g. the Undeletion tests below.
4454 class SyncerBookmarksTest
: public SyncerTest
{
4456 SyncerBookmarksTest() : metahandle_(syncable::kInvalidMetaHandle
) {
4460 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4461 MutableEntry
bookmark(
4462 &trans
, CREATE
, BOOKMARKS
, ids_
.root(), "clientname");
4463 ASSERT_TRUE(bookmark
.good());
4464 bookmark
.PutIsUnsynced(true);
4465 bookmark
.PutSyncing(false);
4466 bookmark
.PutSpecifics(DefaultBookmarkSpecifics());
4467 EXPECT_FALSE(bookmark
.GetIsUnappliedUpdate());
4468 EXPECT_FALSE(bookmark
.GetId().ServerKnows());
4469 metahandle_
= bookmark
.GetMetahandle();
4470 local_id_
= bookmark
.GetId();
4474 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4475 MutableEntry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4476 ASSERT_TRUE(entry
.good());
4477 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4478 // The order of setting IS_UNSYNCED vs IS_DEL matters. See
4479 // WriteNode::Tombstone().
4480 entry
.PutIsUnsynced(true);
4481 entry
.PutIsDel(true);
4482 entry
.PutSyncing(false);
4486 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4487 MutableEntry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4488 ASSERT_TRUE(entry
.good());
4489 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4490 EXPECT_TRUE(entry
.GetIsDel());
4491 entry
.PutIsDel(false);
4492 entry
.PutIsUnsynced(true);
4493 entry
.PutSyncing(false);
4496 int64
GetMetahandleOfTag() {
4497 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4498 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4499 EXPECT_TRUE(entry
.good());
4500 if (!entry
.good()) {
4501 return syncable::kInvalidMetaHandle
;
4503 return entry
.GetMetahandle();
4507 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4508 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4509 EXPECT_TRUE(entry
.good());
4510 if (!entry
.good()) {
4513 return entry
.GetId();
4516 void ExpectUnsyncedCreation() {
4517 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4518 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4520 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4521 EXPECT_FALSE(entry
.GetIsDel());
4522 EXPECT_FALSE(entry
.GetServerIsDel()); // Never been committed.
4523 EXPECT_LT(entry
.GetBaseVersion(), 0);
4524 EXPECT_TRUE(entry
.GetIsUnsynced());
4525 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4528 void ExpectUnsyncedUndeletion() {
4529 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4530 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4532 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4533 EXPECT_FALSE(entry
.GetIsDel());
4534 EXPECT_TRUE(entry
.GetServerIsDel());
4535 EXPECT_GE(entry
.GetBaseVersion(), 0);
4536 EXPECT_TRUE(entry
.GetIsUnsynced());
4537 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4538 EXPECT_TRUE(entry
.GetId().ServerKnows());
4541 void ExpectUnsyncedEdit() {
4542 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4543 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4545 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4546 EXPECT_FALSE(entry
.GetIsDel());
4547 EXPECT_FALSE(entry
.GetServerIsDel());
4548 EXPECT_GE(entry
.GetBaseVersion(), 0);
4549 EXPECT_TRUE(entry
.GetIsUnsynced());
4550 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4551 EXPECT_TRUE(entry
.GetId().ServerKnows());
4554 void ExpectUnsyncedDeletion() {
4555 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4556 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4558 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4559 EXPECT_TRUE(entry
.GetIsDel());
4560 EXPECT_FALSE(entry
.GetServerIsDel());
4561 EXPECT_TRUE(entry
.GetIsUnsynced());
4562 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4563 EXPECT_GE(entry
.GetBaseVersion(), 0);
4564 EXPECT_GE(entry
.GetServerVersion(), 0);
4567 void ExpectSyncedAndCreated() {
4568 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4569 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4571 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4572 EXPECT_FALSE(entry
.GetIsDel());
4573 EXPECT_FALSE(entry
.GetServerIsDel());
4574 EXPECT_GE(entry
.GetBaseVersion(), 0);
4575 EXPECT_EQ(entry
.GetBaseVersion(), entry
.GetServerVersion());
4576 EXPECT_FALSE(entry
.GetIsUnsynced());
4577 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4580 void ExpectSyncedAndDeleted() {
4581 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4582 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4584 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4585 EXPECT_TRUE(entry
.GetIsDel());
4586 EXPECT_TRUE(entry
.GetServerIsDel());
4587 EXPECT_FALSE(entry
.GetIsUnsynced());
4588 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4589 EXPECT_GE(entry
.GetBaseVersion(), 0);
4590 EXPECT_GE(entry
.GetServerVersion(), 0);
4594 syncable::Id local_id_
;
4598 TEST_F(SyncerBookmarksTest
, CreateSyncThenDeleteSync
) {
4600 ExpectUnsyncedCreation();
4602 ExpectSyncedAndCreated();
4604 ExpectUnsyncedDeletion();
4606 ExpectSyncedAndDeleted();
4609 TEST_F(SyncerBookmarksTest
, CreateThenDeleteBeforeSync
) {
4611 ExpectUnsyncedCreation();
4614 // Deleting before the initial commit should result in not needing to send
4615 // the delete to the server. It will still be in an unsynced state, but with
4616 // IS_UNSYNCED set to false.
4618 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4619 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4621 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4622 EXPECT_TRUE(entry
.GetIsDel());
4623 EXPECT_FALSE(entry
.GetServerIsDel());
4624 EXPECT_FALSE(entry
.GetIsUnsynced());
4625 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4626 EXPECT_EQ(entry
.GetBaseVersion(), -1);
4627 EXPECT_EQ(entry
.GetServerVersion(), 0);
4631 TEST_F(SyncerBookmarksTest
, LocalDeleteRemoteChangeConflict
) {
4633 ExpectUnsyncedCreation();
4635 ExpectSyncedAndCreated();
4637 ExpectUnsyncedDeletion();
4639 // Trigger a getupdates that modifies the bookmark. The update should be
4640 // clobbered by the local delete.
4641 mock_server_
->AddUpdateBookmark(GetServerId(), Id(), "dummy", 10, 10,
4642 local_cache_guid(), local_id_
.GetServerId());
4645 ExpectSyncedAndDeleted();
4648 TEST_F(SyncerBookmarksTest
, CreateThenDeleteDuringCommit
) {
4650 ExpectUnsyncedCreation();
4652 // In the middle of the initial creation commit, perform a deletion.
4653 // This should trigger performing two consecutive commit cycles, resulting
4654 // in the bookmark being both deleted and synced.
4655 mock_server_
->SetMidCommitCallback(
4656 base::Bind(&SyncerBookmarksTest::Delete
, base::Unretained(this)));
4659 ExpectSyncedAndDeleted();
4662 // Test what happens if a client deletes, then recreates, an object very
4663 // quickly. It is possible that the deletion gets sent as a commit, and
4664 // the undelete happens during the commit request. The principle here
4665 // is that with a single committing client, conflicts should never
4666 // be encountered, and a client encountering its past actions during
4667 // getupdates should never feed back to override later actions.
4669 // In cases of ordering A-F below, the outcome should be the same.
4670 // Exercised by UndeleteDuringCommit:
4671 // A. Delete - commit - undelete - commitresponse.
4672 // B. Delete - commit - undelete - commitresponse - getupdates.
4673 // Exercised by UndeleteBeforeCommit:
4674 // C. Delete - undelete - commit - commitresponse.
4675 // D. Delete - undelete - commit - commitresponse - getupdates.
4676 // Exercised by UndeleteAfterCommit:
4677 // E. Delete - commit - commitresponse - undelete - commit
4678 // - commitresponse.
4679 // F. Delete - commit - commitresponse - undelete - commit -
4680 // - commitresponse - getupdates.
4681 class SyncerUndeletionTest
: public SyncerTest
{
4683 SyncerUndeletionTest()
4684 : client_tag_("foobar"),
4685 metahandle_(syncable::kInvalidMetaHandle
) {
4689 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4690 MutableEntry
perm_folder(
4691 &trans
, CREATE
, PREFERENCES
, ids_
.root(), "clientname");
4692 ASSERT_TRUE(perm_folder
.good());
4693 perm_folder
.PutUniqueClientTag(client_tag_
);
4694 perm_folder
.PutIsUnsynced(true);
4695 perm_folder
.PutSyncing(false);
4696 perm_folder
.PutSpecifics(DefaultPreferencesSpecifics());
4697 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
4698 EXPECT_FALSE(perm_folder
.GetId().ServerKnows());
4699 metahandle_
= perm_folder
.GetMetahandle();
4700 local_id_
= perm_folder
.GetId();
4704 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4705 MutableEntry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4706 ASSERT_TRUE(entry
.good());
4707 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4708 // The order of setting IS_UNSYNCED vs IS_DEL matters. See
4709 // WriteNode::Tombstone().
4710 entry
.PutIsUnsynced(true);
4711 entry
.PutIsDel(true);
4712 entry
.PutSyncing(false);
4716 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4717 MutableEntry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4718 ASSERT_TRUE(entry
.good());
4719 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4720 EXPECT_TRUE(entry
.GetIsDel());
4721 entry
.PutIsDel(false);
4722 entry
.PutIsUnsynced(true);
4723 entry
.PutSyncing(false);
4726 int64
GetMetahandleOfTag() {
4727 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4728 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4729 EXPECT_TRUE(entry
.good());
4730 if (!entry
.good()) {
4731 return syncable::kInvalidMetaHandle
;
4733 return entry
.GetMetahandle();
4736 void ExpectUnsyncedCreation() {
4737 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4738 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4740 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4741 EXPECT_FALSE(entry
.GetIsDel());
4742 EXPECT_FALSE(entry
.GetServerIsDel()); // Never been committed.
4743 EXPECT_LT(entry
.GetBaseVersion(), 0);
4744 EXPECT_TRUE(entry
.GetIsUnsynced());
4745 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4748 void ExpectUnsyncedUndeletion() {
4749 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4750 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4752 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4753 EXPECT_FALSE(entry
.GetIsDel());
4754 EXPECT_TRUE(entry
.GetServerIsDel());
4755 EXPECT_GE(entry
.GetBaseVersion(), 0);
4756 EXPECT_TRUE(entry
.GetIsUnsynced());
4757 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4758 EXPECT_TRUE(entry
.GetId().ServerKnows());
4761 void ExpectUnsyncedEdit() {
4762 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4763 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4765 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4766 EXPECT_FALSE(entry
.GetIsDel());
4767 EXPECT_FALSE(entry
.GetServerIsDel());
4768 EXPECT_GE(entry
.GetBaseVersion(), 0);
4769 EXPECT_TRUE(entry
.GetIsUnsynced());
4770 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4771 EXPECT_TRUE(entry
.GetId().ServerKnows());
4774 void ExpectUnsyncedDeletion() {
4775 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4776 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4778 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4779 EXPECT_TRUE(entry
.GetIsDel());
4780 EXPECT_FALSE(entry
.GetServerIsDel());
4781 EXPECT_TRUE(entry
.GetIsUnsynced());
4782 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4783 EXPECT_GE(entry
.GetBaseVersion(), 0);
4784 EXPECT_GE(entry
.GetServerVersion(), 0);
4787 void ExpectSyncedAndCreated() {
4788 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4789 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4791 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4792 EXPECT_FALSE(entry
.GetIsDel());
4793 EXPECT_FALSE(entry
.GetServerIsDel());
4794 EXPECT_GE(entry
.GetBaseVersion(), 0);
4795 EXPECT_EQ(entry
.GetBaseVersion(), entry
.GetServerVersion());
4796 EXPECT_FALSE(entry
.GetIsUnsynced());
4797 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4800 void ExpectSyncedAndDeleted() {
4801 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4802 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4804 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4805 EXPECT_TRUE(entry
.GetIsDel());
4806 EXPECT_TRUE(entry
.GetServerIsDel());
4807 EXPECT_FALSE(entry
.GetIsUnsynced());
4808 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4809 EXPECT_GE(entry
.GetBaseVersion(), 0);
4810 EXPECT_GE(entry
.GetServerVersion(), 0);
4814 const std::string client_tag_
;
4815 syncable::Id local_id_
;
4819 TEST_F(SyncerUndeletionTest
, UndeleteDuringCommit
) {
4821 ExpectUnsyncedCreation();
4824 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4825 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4826 ExpectSyncedAndCreated();
4828 // Delete, begin committing the delete, then undelete while committing.
4830 ExpectUnsyncedDeletion();
4831 mock_server_
->SetMidCommitCallback(
4832 base::Bind(&SyncerUndeletionTest::Undelete
, base::Unretained(this)));
4835 // We will continue to commit until all nodes are synced, so we expect
4836 // that both the delete and following undelete were committed. We haven't
4837 // downloaded any updates, though, so the SERVER fields will be the same
4838 // as they were at the start of the cycle.
4839 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4840 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4843 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4844 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4846 // Server fields lag behind.
4847 EXPECT_FALSE(entry
.GetServerIsDel());
4849 // We have committed the second (undelete) update.
4850 EXPECT_FALSE(entry
.GetIsDel());
4851 EXPECT_FALSE(entry
.GetIsUnsynced());
4852 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4855 // Now, encounter a GetUpdates corresponding to the deletion from
4856 // the server. The undeletion should prevail again and be committed.
4857 // None of this should trigger any conflict detection -- it is perfectly
4858 // normal to recieve updates from our own commits.
4859 mock_server_
->SetMidCommitCallback(base::Closure());
4860 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
4861 update
->set_originator_cache_guid(local_cache_guid());
4862 update
->set_originator_client_item_id(local_id_
.GetServerId());
4865 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4866 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4867 ExpectSyncedAndCreated();
4870 TEST_F(SyncerUndeletionTest
, UndeleteBeforeCommit
) {
4872 ExpectUnsyncedCreation();
4875 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4876 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4877 ExpectSyncedAndCreated();
4879 // Delete and undelete, then sync to pick up the result.
4881 ExpectUnsyncedDeletion();
4883 ExpectUnsyncedEdit(); // Edit, not undelete: server thinks it exists.
4886 // The item ought to have committed successfully.
4887 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4888 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4889 ExpectSyncedAndCreated();
4891 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4892 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4893 EXPECT_EQ(2, entry
.GetBaseVersion());
4896 // Now, encounter a GetUpdates corresponding to the just-committed
4898 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
4899 update
->set_originator_cache_guid(local_cache_guid());
4900 update
->set_originator_client_item_id(local_id_
.GetServerId());
4902 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4903 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4904 ExpectSyncedAndCreated();
4907 TEST_F(SyncerUndeletionTest
, UndeleteAfterCommitButBeforeGetUpdates
) {
4909 ExpectUnsyncedCreation();
4912 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4913 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4914 ExpectSyncedAndCreated();
4916 // Delete and commit.
4918 ExpectUnsyncedDeletion();
4921 // The item ought to have committed successfully.
4922 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4923 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4924 ExpectSyncedAndDeleted();
4926 // Before the GetUpdates, the item is locally undeleted.
4928 ExpectUnsyncedUndeletion();
4930 // Now, encounter a GetUpdates corresponding to the just-committed
4931 // deletion update. The undeletion should prevail.
4932 mock_server_
->AddUpdateFromLastCommit();
4934 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4935 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4936 ExpectSyncedAndCreated();
4939 TEST_F(SyncerUndeletionTest
, UndeleteAfterDeleteAndGetUpdates
) {
4941 ExpectUnsyncedCreation();
4944 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4945 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4946 ExpectSyncedAndCreated();
4948 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
4949 update
->set_originator_cache_guid(local_cache_guid());
4950 update
->set_originator_client_item_id(local_id_
.GetServerId());
4952 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4953 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4954 ExpectSyncedAndCreated();
4956 // Delete and commit.
4958 ExpectUnsyncedDeletion();
4961 // The item ought to have committed successfully.
4962 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4963 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4964 ExpectSyncedAndDeleted();
4966 // Now, encounter a GetUpdates corresponding to the just-committed
4967 // deletion update. Should be consistent.
4968 mock_server_
->AddUpdateFromLastCommit();
4970 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4971 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4972 ExpectSyncedAndDeleted();
4974 // After the GetUpdates, the item is locally undeleted.
4976 ExpectUnsyncedUndeletion();
4978 // Now, encounter a GetUpdates corresponding to the just-committed
4979 // deletion update. The undeletion should prevail.
4981 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4982 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4983 ExpectSyncedAndCreated();
4986 // Test processing of undeletion GetUpdateses.
4987 TEST_F(SyncerUndeletionTest
, UndeleteAfterOtherClientDeletes
) {
4989 ExpectUnsyncedCreation();
4992 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4993 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4994 ExpectSyncedAndCreated();
4996 // Add a delete from the server.
4997 sync_pb::SyncEntity
* update1
= mock_server_
->AddUpdateFromLastCommit();
4998 update1
->set_originator_cache_guid(local_cache_guid());
4999 update1
->set_originator_client_item_id(local_id_
.GetServerId());
5001 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5002 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5003 ExpectSyncedAndCreated();
5005 // Some other client deletes the item.
5007 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5008 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5009 mock_server_
->AddUpdateTombstone(entry
.GetId(), PREFERENCES
);
5013 // The update ought to have applied successfully.
5014 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5015 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5016 ExpectSyncedAndDeleted();
5018 // Undelete it locally.
5020 ExpectUnsyncedUndeletion();
5022 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5023 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5024 ExpectSyncedAndCreated();
5026 // Now, encounter a GetUpdates corresponding to the just-committed
5027 // deletion update. The undeletion should prevail.
5028 sync_pb::SyncEntity
* update2
= mock_server_
->AddUpdateFromLastCommit();
5029 update2
->set_originator_cache_guid(local_cache_guid());
5030 update2
->set_originator_client_item_id(local_id_
.GetServerId());
5032 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5033 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5034 ExpectSyncedAndCreated();
5037 TEST_F(SyncerUndeletionTest
, UndeleteAfterOtherClientDeletesImmediately
) {
5039 ExpectUnsyncedCreation();
5042 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5043 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5044 ExpectSyncedAndCreated();
5046 // Some other client deletes the item before we get a chance
5047 // to GetUpdates our original request.
5049 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5050 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5051 mock_server_
->AddUpdateTombstone(entry
.GetId(), PREFERENCES
);
5055 // The update ought to have applied successfully.
5056 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5057 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5058 ExpectSyncedAndDeleted();
5060 // Undelete it locally.
5062 ExpectUnsyncedUndeletion();
5064 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5065 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5066 ExpectSyncedAndCreated();
5068 // Now, encounter a GetUpdates corresponding to the just-committed
5069 // deletion update. The undeletion should prevail.
5070 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5071 update
->set_originator_cache_guid(local_cache_guid());
5072 update
->set_originator_client_item_id(local_id_
.GetServerId());
5074 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5075 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5076 ExpectSyncedAndCreated();
5079 TEST_F(SyncerUndeletionTest
, OtherClientUndeletes
) {
5081 ExpectUnsyncedCreation();
5084 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5085 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5086 ExpectSyncedAndCreated();
5088 // Get the updates of our just-committed entry.
5089 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5090 update
->set_originator_cache_guid(local_cache_guid());
5091 update
->set_originator_client_item_id(local_id_
.GetServerId());
5093 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5094 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5095 ExpectSyncedAndCreated();
5097 // We delete the item.
5099 ExpectUnsyncedDeletion();
5102 // The update ought to have applied successfully.
5103 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5104 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5105 ExpectSyncedAndDeleted();
5107 // Now, encounter a GetUpdates corresponding to the just-committed
5109 mock_server_
->AddUpdateFromLastCommit();
5111 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5112 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5113 ExpectSyncedAndDeleted();
5115 // Some other client undeletes the item.
5117 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5118 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5119 mock_server_
->AddUpdatePref(
5120 entry
.GetId().GetServerId(),
5121 entry
.GetParentId().GetServerId(),
5122 client_tag_
, 100, 1000);
5124 mock_server_
->SetLastUpdateClientTag(client_tag_
);
5126 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5127 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5128 ExpectSyncedAndCreated();
5131 TEST_F(SyncerUndeletionTest
, OtherClientUndeletesImmediately
) {
5133 ExpectUnsyncedCreation();
5136 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5137 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5138 ExpectSyncedAndCreated();
5140 // Get the updates of our just-committed entry.
5141 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5142 update
->set_originator_cache_guid(local_cache_guid());
5144 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5145 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5146 update
->set_originator_client_item_id(local_id_
.GetServerId());
5149 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5150 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5151 ExpectSyncedAndCreated();
5153 // We delete the item.
5155 ExpectUnsyncedDeletion();
5158 // The update ought to have applied successfully.
5159 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5160 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5161 ExpectSyncedAndDeleted();
5163 // Some other client undeletes before we see the update from our
5166 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5167 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5168 mock_server_
->AddUpdatePref(
5169 entry
.GetId().GetServerId(),
5170 entry
.GetParentId().GetServerId(),
5171 client_tag_
, 100, 1000);
5173 mock_server_
->SetLastUpdateClientTag(client_tag_
);
5175 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5176 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5177 ExpectSyncedAndCreated();
5181 TEST_PARAM_BOOKMARK_ENABLE_BIT
,
5182 TEST_PARAM_AUTOFILL_ENABLE_BIT
,
5183 TEST_PARAM_BIT_COUNT
5188 public ::testing::WithParamInterface
<int> {
5190 bool ShouldFailBookmarkCommit() {
5191 return (GetParam() & (1 << TEST_PARAM_BOOKMARK_ENABLE_BIT
)) == 0;
5193 bool ShouldFailAutofillCommit() {
5194 return (GetParam() & (1 << TEST_PARAM_AUTOFILL_ENABLE_BIT
)) == 0;
5198 INSTANTIATE_TEST_CASE_P(ExtensionsActivity
,
5200 testing::Range(0, 1 << TEST_PARAM_BIT_COUNT
));
5202 TEST_P(MixedResult
, ExtensionsActivity
) {
5204 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
5206 MutableEntry
pref(&wtrans
, CREATE
, PREFERENCES
, wtrans
.root_id(), "pref");
5207 ASSERT_TRUE(pref
.good());
5208 pref
.PutIsUnsynced(true);
5210 MutableEntry
bookmark(
5211 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "bookmark");
5212 ASSERT_TRUE(bookmark
.good());
5213 bookmark
.PutIsUnsynced(true);
5215 if (ShouldFailBookmarkCommit()) {
5216 mock_server_
->SetTransientErrorId(bookmark
.GetId());
5219 if (ShouldFailAutofillCommit()) {
5220 mock_server_
->SetTransientErrorId(pref
.GetId());
5225 // Put some extenions activity records into the monitor.
5227 ExtensionsActivity::Records records
;
5228 records
["ABC"].extension_id
= "ABC";
5229 records
["ABC"].bookmark_write_count
= 2049U;
5230 records
["xyz"].extension_id
= "xyz";
5231 records
["xyz"].bookmark_write_count
= 4U;
5232 context_
->extensions_activity()->PutRecords(records
);
5237 ExtensionsActivity::Records final_monitor_records
;
5238 context_
->extensions_activity()->GetAndClearRecords(&final_monitor_records
);
5239 if (ShouldFailBookmarkCommit()) {
5240 ASSERT_EQ(2U, final_monitor_records
.size())
5241 << "Should restore records after unsuccessful bookmark commit.";
5242 EXPECT_EQ("ABC", final_monitor_records
["ABC"].extension_id
);
5243 EXPECT_EQ("xyz", final_monitor_records
["xyz"].extension_id
);
5244 EXPECT_EQ(2049U, final_monitor_records
["ABC"].bookmark_write_count
);
5245 EXPECT_EQ(4U, final_monitor_records
["xyz"].bookmark_write_count
);
5247 EXPECT_TRUE(final_monitor_records
.empty())
5248 << "Should not restore records after successful bookmark commit.";
5252 } // namespace syncer