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 void SetUp() override
{
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 void TearDown() override
{
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 // Verify that if there are more deleted items than the maximum number of
860 // entries, child to parent order is still preserved.
861 TEST_F(SyncerTest
, GetCommitIds_VerifyDeletionCommitOrderMaxEntries
) {
863 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
865 // Create a bookmark tree with one root, two second level, and three third
866 // level bookmarks, all folders.
867 for (int i
= 1; i
<= 6; ++i
) {
868 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "");
869 entry
.PutId(ids_
.FromNumber(i
));
870 entry
.PutIsDir(true);
871 entry
.PutBaseVersion(5);
872 entry
.PutServerVersion(5);
873 entry
.PutParentId(ids_
.FromNumber(i
/2));
874 entry
.PutServerParentId(ids_
.FromNumber(i
/2));
875 entry
.PutServerIsDir(true);
876 entry
.PutIsUnsynced(true);
877 entry
.PutSpecifics(DefaultBookmarkSpecifics());
878 entry
.PutIsDel(true);
883 // Run GetCommitIds with a limit of 2 entries to commit.
884 syncable::Directory::Metahandles result_handles
;
885 syncable::ReadTransaction
trans(FROM_HERE
, directory());
886 GetCommitIdsForType(&trans
, BOOKMARKS
, 2, &result_handles
);
888 // Now verify the output. We expect two results in child to parent order
889 // (descending id order).
890 ASSERT_EQ(2U, result_handles
.size());
892 Entry
entry0(&trans
, GET_BY_HANDLE
, result_handles
[0]);
893 EXPECT_EQ(ids_
.FromNumber(6), entry0
.GetId());
895 Entry
entry1(&trans
, GET_BY_HANDLE
, result_handles
[1]);
896 EXPECT_EQ(ids_
.FromNumber(5), entry1
.GetId());
900 TEST_F(SyncerTest
, EncryptionAwareConflicts
) {
901 KeyParams key_params
= {"localhost", "dummy", "foobar"};
902 Cryptographer
other_cryptographer(&encryptor_
);
903 other_cryptographer
.AddKey(key_params
);
904 sync_pb::EntitySpecifics bookmark
, encrypted_bookmark
, modified_bookmark
;
905 bookmark
.mutable_bookmark()->set_title("title");
906 other_cryptographer
.Encrypt(bookmark
,
907 encrypted_bookmark
.mutable_encrypted());
908 AddDefaultFieldValue(BOOKMARKS
, &encrypted_bookmark
);
909 modified_bookmark
.mutable_bookmark()->set_title("title2");
910 other_cryptographer
.Encrypt(modified_bookmark
,
911 modified_bookmark
.mutable_encrypted());
912 sync_pb::EntitySpecifics pref
, encrypted_pref
, modified_pref
;
913 pref
.mutable_preference()->set_name("name");
914 AddDefaultFieldValue(PREFERENCES
, &encrypted_pref
);
915 other_cryptographer
.Encrypt(pref
,
916 encrypted_pref
.mutable_encrypted());
917 modified_pref
.mutable_preference()->set_name("name2");
918 other_cryptographer
.Encrypt(modified_pref
,
919 modified_pref
.mutable_encrypted());
921 // Mark bookmarks and preferences as encrypted and set the cryptographer to
922 // have pending keys.
923 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
924 sync_pb::EntitySpecifics specifics
;
925 sync_pb::NigoriSpecifics
* nigori
= specifics
.mutable_nigori();
926 other_cryptographer
.GetKeys(nigori
->mutable_encryption_keybag());
927 dir_maker_
.encryption_handler()->EnableEncryptEverything();
928 GetCryptographer(&wtrans
)->SetPendingKeys(nigori
->encryption_keybag());
929 EXPECT_TRUE(GetCryptographer(&wtrans
)->has_pending_keys());
932 // We need to remember the exact position of our local items, so we can
933 // make updates that do not modify those positions.
938 mock_server_
->AddUpdateSpecifics(1, 0, "A", 10, 10, true, 0, bookmark
,
939 foreign_cache_guid(), "-1");
940 mock_server_
->AddUpdateSpecifics(2, 1, "B", 10, 10, false, 2, bookmark
,
941 foreign_cache_guid(), "-2");
942 mock_server_
->AddUpdateSpecifics(3, 1, "C", 10, 10, false, 1, bookmark
,
943 foreign_cache_guid(), "-3");
944 mock_server_
->AddUpdateSpecifics(4, 0, "D", 10, 10, false, 0, pref
);
947 // Initial state. Everything is normal.
948 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
949 VERIFY_ENTRY(1, false, false, false, 0, 10, 10, ids_
, &rtrans
);
950 VERIFY_ENTRY(2, false, false, false, 1, 10, 10, ids_
, &rtrans
);
951 VERIFY_ENTRY(3, false, false, false, 1, 10, 10, ids_
, &rtrans
);
952 VERIFY_ENTRY(4, false, false, false, 0, 10, 10, ids_
, &rtrans
);
954 Entry
entry1(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
955 ASSERT_TRUE(entry1
.GetUniquePosition().Equals(
956 entry1
.GetServerUniquePosition()));
957 pos1
= entry1
.GetUniquePosition();
958 Entry
entry2(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(2));
959 pos2
= entry2
.GetUniquePosition();
960 Entry
entry3(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(3));
961 pos3
= entry3
.GetUniquePosition();
964 // Server side encryption will not be applied due to undecryptable data.
965 // At this point, BASE_SERVER_SPECIFICS should be filled for all four items.
966 mock_server_
->AddUpdateSpecifics(1, 0, kEncryptedString
, 20, 20, true, 0,
968 foreign_cache_guid(), "-1");
969 mock_server_
->AddUpdateSpecifics(2, 1, kEncryptedString
, 20, 20, false, 2,
971 foreign_cache_guid(), "-2");
972 mock_server_
->AddUpdateSpecifics(3, 1, kEncryptedString
, 20, 20, false, 1,
974 foreign_cache_guid(), "-3");
975 mock_server_
->AddUpdateSpecifics(4, 0, kEncryptedString
, 20, 20, false, 0,
977 foreign_cache_guid(), "-4");
980 // All should be unapplied due to being undecryptable and have a valid
981 // BASE_SERVER_SPECIFICS.
982 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
983 VERIFY_ENTRY(1, true, false, true, 0, 10, 20, ids_
, &rtrans
);
984 VERIFY_ENTRY(2, true, false, true, 1, 10, 20, ids_
, &rtrans
);
985 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_
, &rtrans
);
986 VERIFY_ENTRY(4, true, false, true, 0, 10, 20, ids_
, &rtrans
);
989 // Server side change that don't modify anything should not affect
990 // BASE_SERVER_SPECIFICS (such as name changes and mtime changes).
991 mock_server_
->AddUpdateSpecifics(1, 0, kEncryptedString
, 30, 30, true, 0,
993 foreign_cache_guid(), "-1");
994 mock_server_
->AddUpdateSpecifics(2, 1, kEncryptedString
, 30, 30, false, 2,
996 foreign_cache_guid(), "-2");
997 // Item 3 doesn't change.
998 mock_server_
->AddUpdateSpecifics(4, 0, kEncryptedString
, 30, 30, false, 0,
1000 foreign_cache_guid(), "-4");
1003 // Items 1, 2, and 4 should have newer server versions, 3 remains the same.
1004 // All should remain unapplied due to be undecryptable.
1005 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1006 VERIFY_ENTRY(1, true, false, true, 0, 10, 30, ids_
, &rtrans
);
1007 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_
, &rtrans
);
1008 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_
, &rtrans
);
1009 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_
, &rtrans
);
1012 // Positional changes, parent changes, and specifics changes should reset
1013 // BASE_SERVER_SPECIFICS.
1014 // Became unencrypted.
1015 mock_server_
->AddUpdateSpecifics(1, 0, "A", 40, 40, true, 0, bookmark
,
1016 foreign_cache_guid(), "-1");
1017 // Reordered to after item 2.
1018 mock_server_
->AddUpdateSpecifics(3, 1, kEncryptedString
, 30, 30, false, 3,
1020 foreign_cache_guid(), "-3");
1023 // Items 2 and 4 should be the only ones with BASE_SERVER_SPECIFICS set.
1024 // Items 1 is now unencrypted, so should have applied normally.
1025 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1026 VERIFY_ENTRY(1, false, false, false, 0, 40, 40, ids_
, &rtrans
);
1027 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_
, &rtrans
);
1028 VERIFY_ENTRY(3, true, false, false, 1, 10, 30, ids_
, &rtrans
);
1029 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_
, &rtrans
);
1032 // Make local changes, which should remain unsynced for items 2, 3, 4.
1034 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1035 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
1036 ASSERT_TRUE(A
.good());
1037 A
.PutSpecifics(modified_bookmark
);
1038 A
.PutNonUniqueName(kEncryptedString
);
1039 A
.PutIsUnsynced(true);
1040 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
1041 ASSERT_TRUE(B
.good());
1042 B
.PutSpecifics(modified_bookmark
);
1043 B
.PutNonUniqueName(kEncryptedString
);
1044 B
.PutIsUnsynced(true);
1045 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
1046 ASSERT_TRUE(C
.good());
1047 C
.PutSpecifics(modified_bookmark
);
1048 C
.PutNonUniqueName(kEncryptedString
);
1049 C
.PutIsUnsynced(true);
1050 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
1051 ASSERT_TRUE(D
.good());
1052 D
.PutSpecifics(modified_pref
);
1053 D
.PutNonUniqueName(kEncryptedString
);
1054 D
.PutIsUnsynced(true);
1058 // Item 1 remains unsynced due to there being pending keys.
1059 // Items 2, 3, 4 should remain unsynced since they were not up to date.
1060 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1061 VERIFY_ENTRY(1, false, true, false, 0, 40, 40, ids_
, &rtrans
);
1062 VERIFY_ENTRY(2, true, true, true, 1, 10, 30, ids_
, &rtrans
);
1063 VERIFY_ENTRY(3, true, true, false, 1, 10, 30, ids_
, &rtrans
);
1064 VERIFY_ENTRY(4, true, true, true, 0, 10, 30, ids_
, &rtrans
);
1068 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1069 // Resolve the pending keys.
1070 GetCryptographer(&rtrans
)->DecryptPendingKeys(key_params
);
1072 // First cycle resolves conflicts, second cycle commits changes.
1074 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_server_overwrites
);
1075 EXPECT_EQ(1, GetUpdateCounters(PREFERENCES
).num_server_overwrites
);
1076 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_local_overwrites
);
1078 // We successfully commited item(s).
1079 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS
).num_commits_attempted
);
1080 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS
).num_commits_success
);
1081 EXPECT_EQ(1, GetCommitCounters(PREFERENCES
).num_commits_attempted
);
1082 EXPECT_EQ(1, GetCommitCounters(PREFERENCES
).num_commits_success
);
1086 // Everything should be resolved now. The local changes should have
1087 // overwritten the server changes for 2 and 4, while the server changes
1088 // overwrote the local for entry 3.
1090 // Expect there will be no new overwrites.
1091 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_server_overwrites
);
1092 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_local_overwrites
);
1094 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS
).num_commits_success
);
1095 EXPECT_EQ(1, GetCommitCounters(PREFERENCES
).num_commits_success
);
1097 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1098 VERIFY_ENTRY(1, false, false, false, 0, 41, 41, ids_
, &rtrans
);
1099 VERIFY_ENTRY(2, false, false, false, 1, 31, 31, ids_
, &rtrans
);
1100 VERIFY_ENTRY(3, false, false, false, 1, 30, 30, ids_
, &rtrans
);
1101 VERIFY_ENTRY(4, false, false, false, 0, 31, 31, ids_
, &rtrans
);
1106 TEST_F(SyncerTest
, TestGetUnsyncedAndSimpleCommit
) {
1108 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1109 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1110 ASSERT_TRUE(parent
.good());
1111 parent
.PutIsUnsynced(true);
1112 parent
.PutIsDir(true);
1113 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1114 parent
.PutBaseVersion(1);
1115 parent
.PutId(parent_id_
);
1116 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete");
1117 ASSERT_TRUE(child
.good());
1118 child
.PutId(child_id_
);
1119 child
.PutBaseVersion(1);
1120 WriteTestDataToEntry(&wtrans
, &child
);
1124 ASSERT_EQ(2u, mock_server_
->committed_ids().size());
1125 // If this test starts failing, be aware other sort orders could be valid.
1126 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1127 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1129 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1130 Entry
entry(&rt
, syncable::GET_BY_ID
, child_id_
);
1131 ASSERT_TRUE(entry
.good());
1132 VerifyTestDataInEntry(&rt
, &entry
);
1136 TEST_F(SyncerTest
, TestPurgeWhileUnsynced
) {
1137 // Similar to above, but throw a purge operation into the mix. Bug 49278.
1138 syncable::Id pref_node_id
= TestIdFactory::MakeServer("Tim");
1140 directory()->SetDownloadProgress(BOOKMARKS
,
1141 syncable::BuildProgress(BOOKMARKS
));
1142 directory()->SetDownloadProgress(PREFERENCES
,
1143 syncable::BuildProgress(PREFERENCES
));
1144 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1145 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1146 ASSERT_TRUE(parent
.good());
1147 parent
.PutIsUnsynced(true);
1148 parent
.PutIsDir(true);
1149 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1150 parent
.PutBaseVersion(1);
1151 parent
.PutId(parent_id_
);
1152 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete");
1153 ASSERT_TRUE(child
.good());
1154 child
.PutId(child_id_
);
1155 child
.PutBaseVersion(1);
1156 WriteTestDataToEntry(&wtrans
, &child
);
1158 MutableEntry
parent2(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Tim");
1159 ASSERT_TRUE(parent2
.good());
1160 parent2
.PutIsUnsynced(true);
1161 parent2
.PutIsDir(true);
1162 parent2
.PutSpecifics(DefaultPreferencesSpecifics());
1163 parent2
.PutBaseVersion(1);
1164 parent2
.PutId(pref_node_id
);
1167 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES
),
1172 ASSERT_EQ(2U, mock_server_
->committed_ids().size());
1173 // If this test starts failing, be aware other sort orders could be valid.
1174 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1175 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1177 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1178 Entry
entry(&rt
, syncable::GET_BY_ID
, child_id_
);
1179 ASSERT_TRUE(entry
.good());
1180 VerifyTestDataInEntry(&rt
, &entry
);
1182 directory()->SaveChanges();
1184 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1185 Entry
entry(&rt
, syncable::GET_BY_ID
, pref_node_id
);
1186 ASSERT_FALSE(entry
.good());
1190 TEST_F(SyncerTest
, TestPurgeWhileUnapplied
) {
1191 // Similar to above, but for unapplied items. Bug 49278.
1193 directory()->SetDownloadProgress(BOOKMARKS
,
1194 syncable::BuildProgress(BOOKMARKS
));
1195 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1196 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1197 ASSERT_TRUE(parent
.good());
1198 parent
.PutIsUnappliedUpdate(true);
1199 parent
.PutIsDir(true);
1200 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1201 parent
.PutBaseVersion(1);
1202 parent
.PutId(parent_id_
);
1205 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(BOOKMARKS
),
1210 directory()->SaveChanges();
1212 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1213 Entry
entry(&rt
, syncable::GET_BY_ID
, parent_id_
);
1214 ASSERT_FALSE(entry
.good());
1218 TEST_F(SyncerTest
, TestPurgeWithJournal
) {
1220 directory()->SetDownloadProgress(BOOKMARKS
,
1221 syncable::BuildProgress(BOOKMARKS
));
1222 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1223 MutableEntry
parent(&wtrans
, syncable::CREATE
, BOOKMARKS
, wtrans
.root_id(),
1225 ASSERT_TRUE(parent
.good());
1226 parent
.PutIsDir(true);
1227 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1228 parent
.PutBaseVersion(1);
1229 parent
.PutId(parent_id_
);
1230 MutableEntry
child(&wtrans
, syncable::CREATE
, BOOKMARKS
, parent_id_
,
1232 ASSERT_TRUE(child
.good());
1233 child
.PutId(child_id_
);
1234 child
.PutBaseVersion(1);
1235 WriteTestDataToEntry(&wtrans
, &child
);
1237 MutableEntry
parent2(&wtrans
, syncable::CREATE
, PREFERENCES
,
1238 wtrans
.root_id(), "Tim");
1239 ASSERT_TRUE(parent2
.good());
1240 parent2
.PutIsDir(true);
1241 parent2
.PutSpecifics(DefaultPreferencesSpecifics());
1242 parent2
.PutBaseVersion(1);
1243 parent2
.PutId(TestIdFactory::MakeServer("Tim"));
1246 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES
, BOOKMARKS
),
1247 ModelTypeSet(BOOKMARKS
),
1250 // Verify bookmark nodes are saved in delete journal but not preference
1252 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1253 syncable::DeleteJournal
* delete_journal
= directory()->delete_journal();
1254 EXPECT_EQ(2u, delete_journal
->GetDeleteJournalSize(&rt
));
1255 syncable::EntryKernelSet journal_entries
;
1256 directory()->delete_journal()->GetDeleteJournals(&rt
, BOOKMARKS
,
1258 EXPECT_EQ(parent_id_
, (*journal_entries
.begin())->ref(syncable::ID
));
1259 EXPECT_EQ(child_id_
, (*journal_entries
.rbegin())->ref(syncable::ID
));
1263 TEST_F(SyncerTest
, ResetVersions
) {
1264 // Download the top level pref node and some pref items.
1265 mock_server_
->AddUpdateDirectory(
1266 parent_id_
, root_id_
, "prefs", 1, 10, std::string(), std::string());
1267 mock_server_
->SetLastUpdateServerTag(ModelTypeToRootTag(PREFERENCES
));
1268 mock_server_
->AddUpdatePref("id1", parent_id_
.GetServerId(), "tag1", 20, 20);
1269 mock_server_
->AddUpdatePref("id2", parent_id_
.GetServerId(), "tag2", 30, 30);
1270 mock_server_
->AddUpdatePref("id3", parent_id_
.GetServerId(), "tag3", 40, 40);
1274 // Modify one of the preferences locally, mark another one as unapplied,
1275 // and create another unsynced preference.
1276 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1277 MutableEntry
entry(&wtrans
, GET_BY_CLIENT_TAG
, "tag1");
1278 entry
.PutIsUnsynced(true);
1280 MutableEntry
entry2(&wtrans
, GET_BY_CLIENT_TAG
, "tag2");
1281 entry2
.PutIsUnappliedUpdate(true);
1283 MutableEntry
entry4(&wtrans
, CREATE
, PREFERENCES
, parent_id_
, "name");
1284 entry4
.PutUniqueClientTag("tag4");
1285 entry4
.PutIsUnsynced(true);
1289 // Reset the versions.
1290 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1291 ASSERT_TRUE(directory()->ResetVersionsForType(&wtrans
, PREFERENCES
));
1295 // Verify the synced items are all with version 1 now, with
1296 // unsynced/unapplied state preserved.
1297 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1298 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, "tag1");
1299 EXPECT_EQ(1, entry
.GetBaseVersion());
1300 EXPECT_EQ(1, entry
.GetServerVersion());
1301 EXPECT_TRUE(entry
.GetIsUnsynced());
1302 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
1303 Entry
entry2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
1304 EXPECT_EQ(1, entry2
.GetBaseVersion());
1305 EXPECT_EQ(1, entry2
.GetServerVersion());
1306 EXPECT_FALSE(entry2
.GetIsUnsynced());
1307 EXPECT_TRUE(entry2
.GetIsUnappliedUpdate());
1308 Entry
entry3(&trans
, GET_BY_CLIENT_TAG
, "tag3");
1309 EXPECT_EQ(1, entry3
.GetBaseVersion());
1310 EXPECT_EQ(1, entry3
.GetServerVersion());
1311 EXPECT_FALSE(entry3
.GetIsUnsynced());
1312 EXPECT_FALSE(entry3
.GetIsUnappliedUpdate());
1314 // Entry 4 (the locally created one) should remain the same.
1315 Entry
entry4(&trans
, GET_BY_CLIENT_TAG
, "tag4");
1316 EXPECT_EQ(-1, entry4
.GetBaseVersion());
1317 EXPECT_EQ(0, entry4
.GetServerVersion());
1318 EXPECT_TRUE(entry4
.GetIsUnsynced());
1319 EXPECT_FALSE(entry4
.GetIsUnappliedUpdate());
1323 TEST_F(SyncerTest
, TestCommitListOrderingTwoItemsTall
) {
1324 CommitOrderingTest items
[] = {
1325 {1, ids_
.FromNumber(-1001), ids_
.FromNumber(-1000)},
1326 {0, ids_
.FromNumber(-1000), ids_
.FromNumber(0)},
1327 CommitOrderingTest::MakeLastCommitItem(),
1329 RunCommitOrderingTest(items
);
1332 TEST_F(SyncerTest
, TestCommitListOrderingThreeItemsTall
) {
1333 CommitOrderingTest items
[] = {
1334 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1335 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1336 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1337 CommitOrderingTest::MakeLastCommitItem(),
1339 RunCommitOrderingTest(items
);
1342 TEST_F(SyncerTest
, TestCommitListOrderingFourItemsTall
) {
1343 CommitOrderingTest items
[] = {
1344 {3, ids_
.FromNumber(-2003), ids_
.FromNumber(-2002)},
1345 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1346 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1347 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1348 CommitOrderingTest::MakeLastCommitItem(),
1350 RunCommitOrderingTest(items
);
1353 TEST_F(SyncerTest
, TestCommitListOrderingThreeItemsTallLimitedSize
) {
1354 context_
->set_max_commit_batch_size(2);
1355 CommitOrderingTest items
[] = {
1356 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1357 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1358 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1359 CommitOrderingTest::MakeLastCommitItem(),
1361 RunCommitOrderingTest(items
);
1364 TEST_F(SyncerTest
, TestCommitListOrderingSingleDeletedItem
) {
1365 CommitOrderingTest items
[] = {
1366 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1367 CommitOrderingTest::MakeLastCommitItem(),
1369 RunCommitOrderingTest(items
);
1372 TEST_F(SyncerTest
, TestCommitListOrderingSingleUncommittedDeletedItem
) {
1373 CommitOrderingTest items
[] = {
1374 {-1, ids_
.FromNumber(-1000), ids_
.FromNumber(0), {DELETED
}},
1375 CommitOrderingTest::MakeLastCommitItem(),
1377 RunCommitOrderingTest(items
);
1380 TEST_F(SyncerTest
, TestCommitListOrderingSingleDeletedItemWithUnroll
) {
1381 CommitOrderingTest items
[] = {
1382 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1383 CommitOrderingTest::MakeLastCommitItem(),
1385 RunCommitOrderingTest(items
);
1389 TestCommitListOrderingSingleLongDeletedItemWithUnroll
) {
1390 CommitOrderingTest items
[] = {
1391 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1392 CommitOrderingTest::MakeLastCommitItem(),
1394 RunCommitOrderingTest(items
);
1397 TEST_F(SyncerTest
, TestCommitListOrderingTwoLongDeletedItemWithUnroll
) {
1398 CommitOrderingTest items
[] = {
1399 {1, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1400 {0, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
, OLD_MTIME
}},
1401 CommitOrderingTest::MakeLastCommitItem(),
1403 RunCommitOrderingTest(items
);
1406 TEST_F(SyncerTest
, TestCommitListOrdering3LongDeletedItemsWithSizeLimit
) {
1407 context_
->set_max_commit_batch_size(2);
1408 CommitOrderingTest items
[] = {
1409 {2, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1410 {1, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
, OLD_MTIME
}},
1411 {0, ids_
.FromNumber(1002), ids_
.FromNumber(1001), {DELETED
, OLD_MTIME
}},
1412 CommitOrderingTest::MakeLastCommitItem(),
1414 RunCommitOrderingTest(items
);
1417 TEST_F(SyncerTest
, TestCommitListOrderingTwoDeletedItemsWithUnroll
) {
1418 CommitOrderingTest items
[] = {
1419 {1, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1420 {0, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
}},
1421 CommitOrderingTest::MakeLastCommitItem(),
1423 RunCommitOrderingTest(items
);
1426 TEST_F(SyncerTest
, TestCommitListOrderingComplexDeletionScenario
) {
1427 CommitOrderingTest items
[] = {
1428 {2, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1429 {-1, ids_
.FromNumber(1001), ids_
.FromNumber(0), {SYNCED
}},
1430 {1, ids_
.FromNumber(1002), ids_
.FromNumber(1001), {DELETED
, OLD_MTIME
}},
1431 {-1, ids_
.FromNumber(1003), ids_
.FromNumber(1001), {SYNCED
}},
1432 {0, ids_
.FromNumber(1004), ids_
.FromNumber(1003), {DELETED
}},
1433 CommitOrderingTest::MakeLastCommitItem(),
1435 RunCommitOrderingTest(items
);
1439 TestCommitListOrderingComplexDeletionScenarioWith2RecentDeletes
) {
1440 CommitOrderingTest items
[] = {
1441 {3, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1442 {-1, ids_
.FromNumber(1001), ids_
.FromNumber(0), {SYNCED
}},
1443 {2, ids_
.FromNumber(1002), ids_
.FromNumber(1001), {DELETED
, OLD_MTIME
}},
1444 {-1, ids_
.FromNumber(1003), ids_
.FromNumber(1001), {SYNCED
}},
1445 {1, ids_
.FromNumber(1004), ids_
.FromNumber(1003), {DELETED
}},
1446 {0, ids_
.FromNumber(1005), ids_
.FromNumber(1003), {DELETED
}},
1447 CommitOrderingTest::MakeLastCommitItem(),
1449 RunCommitOrderingTest(items
);
1452 TEST_F(SyncerTest
, TestCommitListOrderingDeleteMovedItems
) {
1453 CommitOrderingTest items
[] = {
1454 {1, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1455 {0, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
, OLD_MTIME
,
1457 CommitOrderingTest::MakeLastCommitItem(),
1459 RunCommitOrderingTest(items
);
1462 TEST_F(SyncerTest
, TestCommitListOrderingWithNesting
) {
1463 const base::Time
& now_minus_2h
=
1464 base::Time::Now() - base::TimeDelta::FromHours(2);
1466 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1468 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bob");
1469 ASSERT_TRUE(parent
.good());
1470 parent
.PutIsUnsynced(true);
1471 parent
.PutIsDir(true);
1472 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1473 parent
.PutId(ids_
.FromNumber(100));
1474 parent
.PutBaseVersion(1);
1476 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(100), "Bob");
1477 ASSERT_TRUE(child
.good());
1478 child
.PutIsUnsynced(true);
1479 child
.PutIsDir(true);
1480 child
.PutSpecifics(DefaultBookmarkSpecifics());
1481 child
.PutId(ids_
.FromNumber(101));
1482 child
.PutBaseVersion(1);
1483 MutableEntry
grandchild(
1484 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(101), "Bob");
1485 ASSERT_TRUE(grandchild
.good());
1486 grandchild
.PutId(ids_
.FromNumber(102));
1487 grandchild
.PutIsUnsynced(true);
1488 grandchild
.PutSpecifics(DefaultBookmarkSpecifics());
1489 grandchild
.PutBaseVersion(1);
1492 // Create three deleted items which deletions we expect to be sent to the
1494 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1495 ASSERT_TRUE(parent
.good());
1496 parent
.PutId(ids_
.FromNumber(103));
1497 parent
.PutIsUnsynced(true);
1498 parent
.PutIsDir(true);
1499 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1500 parent
.PutIsDel(true);
1501 parent
.PutBaseVersion(1);
1502 parent
.PutMtime(now_minus_2h
);
1504 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(103), "Pete");
1505 ASSERT_TRUE(child
.good());
1506 child
.PutId(ids_
.FromNumber(104));
1507 child
.PutIsUnsynced(true);
1508 child
.PutIsDir(true);
1509 child
.PutSpecifics(DefaultBookmarkSpecifics());
1510 child
.PutIsDel(true);
1511 child
.PutBaseVersion(1);
1512 child
.PutMtime(now_minus_2h
);
1513 MutableEntry
grandchild(
1514 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(104), "Pete");
1515 ASSERT_TRUE(grandchild
.good());
1516 grandchild
.PutId(ids_
.FromNumber(105));
1517 grandchild
.PutIsUnsynced(true);
1518 grandchild
.PutIsDel(true);
1519 grandchild
.PutIsDir(false);
1520 grandchild
.PutSpecifics(DefaultBookmarkSpecifics());
1521 grandchild
.PutBaseVersion(1);
1522 grandchild
.PutMtime(now_minus_2h
);
1527 ASSERT_EQ(6u, mock_server_
->committed_ids().size());
1528 // This test will NOT unroll deletes because SERVER_PARENT_ID is not set.
1529 // It will treat these like moves.
1530 vector
<syncable::Id
> commit_ids(mock_server_
->committed_ids());
1531 EXPECT_TRUE(ids_
.FromNumber(100) == commit_ids
[0]);
1532 EXPECT_TRUE(ids_
.FromNumber(101) == commit_ids
[1]);
1533 EXPECT_TRUE(ids_
.FromNumber(102) == commit_ids
[2]);
1534 // We don't guarantee the delete orders in this test, only that they occur
1536 std::sort(commit_ids
.begin() + 3, commit_ids
.end());
1537 EXPECT_TRUE(ids_
.FromNumber(103) == commit_ids
[3]);
1538 EXPECT_TRUE(ids_
.FromNumber(104) == commit_ids
[4]);
1539 EXPECT_TRUE(ids_
.FromNumber(105) == commit_ids
[5]);
1542 TEST_F(SyncerTest
, TestCommitListOrderingWithNewItems
) {
1543 syncable::Id parent1_id
= ids_
.MakeServer("p1");
1544 syncable::Id parent2_id
= ids_
.MakeServer("p2");
1547 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1548 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "1");
1549 ASSERT_TRUE(parent
.good());
1550 parent
.PutIsUnsynced(true);
1551 parent
.PutIsDir(true);
1552 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1553 parent
.PutId(parent1_id
);
1554 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "2");
1555 ASSERT_TRUE(child
.good());
1556 child
.PutIsUnsynced(true);
1557 child
.PutIsDir(true);
1558 child
.PutSpecifics(DefaultBookmarkSpecifics());
1559 child
.PutId(parent2_id
);
1560 parent
.PutBaseVersion(1);
1561 child
.PutBaseVersion(1);
1564 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1565 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, parent1_id
, "A");
1566 ASSERT_TRUE(parent
.good());
1567 parent
.PutIsUnsynced(true);
1568 parent
.PutIsDir(true);
1569 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1570 parent
.PutId(ids_
.FromNumber(102));
1571 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent1_id
, "B");
1572 ASSERT_TRUE(child
.good());
1573 child
.PutIsUnsynced(true);
1574 child
.PutIsDir(true);
1575 child
.PutSpecifics(DefaultBookmarkSpecifics());
1576 child
.PutId(ids_
.FromNumber(-103));
1577 parent
.PutBaseVersion(1);
1580 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1581 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, parent2_id
, "A");
1582 ASSERT_TRUE(parent
.good());
1583 parent
.PutIsUnsynced(true);
1584 parent
.PutIsDir(true);
1585 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1586 parent
.PutId(ids_
.FromNumber(-104));
1587 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent2_id
, "B");
1588 ASSERT_TRUE(child
.good());
1589 child
.PutIsUnsynced(true);
1590 child
.PutIsDir(true);
1591 child
.PutSpecifics(DefaultBookmarkSpecifics());
1592 child
.PutId(ids_
.FromNumber(105));
1593 child
.PutBaseVersion(1);
1597 ASSERT_EQ(6u, mock_server_
->committed_ids().size());
1599 // This strange iteration and std::count() usage is to allow the order to
1600 // vary. All we really care about is that parent1_id and parent2_id are the
1601 // first two IDs, and that the children make up the next four. Other than
1602 // that, ordering doesn't matter.
1604 vector
<syncable::Id
>::const_iterator i
=
1605 mock_server_
->committed_ids().begin();
1606 vector
<syncable::Id
>::const_iterator parents_begin
= i
;
1609 vector
<syncable::Id
>::const_iterator parents_end
= i
;
1610 vector
<syncable::Id
>::const_iterator children_begin
= i
;
1611 vector
<syncable::Id
>::const_iterator children_end
=
1612 mock_server_
->committed_ids().end();
1614 EXPECT_EQ(1, count(parents_begin
, parents_end
, parent1_id
));
1615 EXPECT_EQ(1, count(parents_begin
, parents_end
, parent2_id
));
1617 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(-103)));
1618 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(102)));
1619 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(105)));
1620 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(-104)));
1623 TEST_F(SyncerTest
, TestCommitListOrderingCounterexample
) {
1624 syncable::Id child2_id
= ids_
.NewServerId();
1627 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1628 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "P");
1629 ASSERT_TRUE(parent
.good());
1630 parent
.PutIsUnsynced(true);
1631 parent
.PutIsDir(true);
1632 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1633 parent
.PutId(parent_id_
);
1634 MutableEntry
child1(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "1");
1635 ASSERT_TRUE(child1
.good());
1636 child1
.PutIsUnsynced(true);
1637 child1
.PutId(child_id_
);
1638 child1
.PutSpecifics(DefaultBookmarkSpecifics());
1639 MutableEntry
child2(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "2");
1640 ASSERT_TRUE(child2
.good());
1641 child2
.PutIsUnsynced(true);
1642 child2
.PutSpecifics(DefaultBookmarkSpecifics());
1643 child2
.PutId(child2_id
);
1645 parent
.PutBaseVersion(1);
1646 child1
.PutBaseVersion(1);
1647 child2
.PutBaseVersion(1);
1651 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1652 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1653 // There are two possible valid orderings.
1654 if (child2_id
== mock_server_
->committed_ids()[1]) {
1655 EXPECT_TRUE(child2_id
== mock_server_
->committed_ids()[1]);
1656 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[2]);
1658 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1659 EXPECT_TRUE(child2_id
== mock_server_
->committed_ids()[2]);
1663 TEST_F(SyncerTest
, TestCommitListOrderingAndNewParent
) {
1664 string parent1_name
= "1";
1665 string parent2_name
= "A";
1666 string child_name
= "B";
1669 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1670 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(),
1672 ASSERT_TRUE(parent
.good());
1673 parent
.PutIsUnsynced(true);
1674 parent
.PutIsDir(true);
1675 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1676 parent
.PutId(parent_id_
);
1677 parent
.PutBaseVersion(1);
1680 syncable::Id parent2_id
= ids_
.NewLocalId();
1681 syncable::Id child_id
= ids_
.NewServerId();
1683 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1684 MutableEntry
parent2(
1685 &wtrans
, CREATE
, BOOKMARKS
, parent_id_
, parent2_name
);
1686 ASSERT_TRUE(parent2
.good());
1687 parent2
.PutIsUnsynced(true);
1688 parent2
.PutIsDir(true);
1689 parent2
.PutSpecifics(DefaultBookmarkSpecifics());
1690 parent2
.PutId(parent2_id
);
1693 &wtrans
, CREATE
, BOOKMARKS
, parent2_id
, child_name
);
1694 ASSERT_TRUE(child
.good());
1695 child
.PutIsUnsynced(true);
1696 child
.PutIsDir(true);
1697 child
.PutSpecifics(DefaultBookmarkSpecifics());
1698 child
.PutId(child_id
);
1699 child
.PutBaseVersion(1);
1703 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1704 // If this test starts failing, be aware other sort orders could be valid.
1705 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1706 EXPECT_TRUE(parent2_id
== mock_server_
->committed_ids()[1]);
1707 EXPECT_TRUE(child_id
== mock_server_
->committed_ids()[2]);
1709 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1710 // Check that things committed correctly.
1711 Entry
entry_1(&rtrans
, syncable::GET_BY_ID
, parent_id_
);
1712 EXPECT_EQ(entry_1
.GetNonUniqueName(), parent1_name
);
1713 // Check that parent2 is a subfolder of parent1.
1714 EXPECT_EQ(1, CountEntriesWithName(&rtrans
,
1718 // Parent2 was a local ID and thus should have changed on commit!
1719 Entry
pre_commit_entry_parent2(&rtrans
, syncable::GET_BY_ID
, parent2_id
);
1720 ASSERT_FALSE(pre_commit_entry_parent2
.good());
1722 // Look up the new ID.
1723 Id parent2_committed_id
=
1724 GetOnlyEntryWithName(&rtrans
, parent_id_
, parent2_name
);
1725 EXPECT_TRUE(parent2_committed_id
.ServerKnows());
1727 Entry
child(&rtrans
, syncable::GET_BY_ID
, child_id
);
1728 EXPECT_EQ(parent2_committed_id
, child
.GetParentId());
1732 TEST_F(SyncerTest
, TestCommitListOrderingAndNewParentAndChild
) {
1733 string parent_name
= "1";
1734 string parent2_name
= "A";
1735 string child_name
= "B";
1738 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1739 MutableEntry
parent(&wtrans
,
1743 ASSERT_TRUE(parent
.good());
1744 parent
.PutIsUnsynced(true);
1745 parent
.PutIsDir(true);
1746 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1747 parent
.PutId(parent_id_
);
1748 parent
.PutBaseVersion(1);
1751 int64 meta_handle_b
;
1752 const Id parent2_local_id
= ids_
.NewLocalId();
1753 const Id child_local_id
= ids_
.NewLocalId();
1755 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1756 MutableEntry
parent2(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, parent2_name
);
1757 ASSERT_TRUE(parent2
.good());
1758 parent2
.PutIsUnsynced(true);
1759 parent2
.PutIsDir(true);
1760 parent2
.PutSpecifics(DefaultBookmarkSpecifics());
1762 parent2
.PutId(parent2_local_id
);
1764 &wtrans
, CREATE
, BOOKMARKS
, parent2_local_id
, child_name
);
1765 ASSERT_TRUE(child
.good());
1766 child
.PutIsUnsynced(true);
1767 child
.PutIsDir(true);
1768 child
.PutSpecifics(DefaultBookmarkSpecifics());
1769 child
.PutId(child_local_id
);
1770 meta_handle_b
= child
.GetMetahandle();
1774 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1775 // If this test starts failing, be aware other sort orders could be valid.
1776 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1777 EXPECT_TRUE(parent2_local_id
== mock_server_
->committed_ids()[1]);
1778 EXPECT_TRUE(child_local_id
== mock_server_
->committed_ids()[2]);
1780 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1782 Entry
parent(&rtrans
, syncable::GET_BY_ID
,
1783 GetOnlyEntryWithName(&rtrans
, rtrans
.root_id(), parent_name
));
1784 ASSERT_TRUE(parent
.good());
1785 EXPECT_TRUE(parent
.GetId().ServerKnows());
1787 Entry
parent2(&rtrans
, syncable::GET_BY_ID
,
1788 GetOnlyEntryWithName(&rtrans
, parent
.GetId(), parent2_name
));
1789 ASSERT_TRUE(parent2
.good());
1790 EXPECT_TRUE(parent2
.GetId().ServerKnows());
1792 // Id changed on commit, so this should fail.
1793 Entry
local_parent2_id_entry(&rtrans
,
1794 syncable::GET_BY_ID
,
1796 ASSERT_FALSE(local_parent2_id_entry
.good());
1798 Entry
entry_b(&rtrans
, syncable::GET_BY_HANDLE
, meta_handle_b
);
1799 EXPECT_TRUE(entry_b
.GetId().ServerKnows());
1800 EXPECT_TRUE(parent2
.GetId()== entry_b
.GetParentId());
1804 TEST_F(SyncerTest
, UpdateWithZeroLengthName
) {
1805 // One illegal update
1806 mock_server_
->AddUpdateDirectory(
1807 1, 0, std::string(), 1, 10, foreign_cache_guid(), "-1");
1808 // And one legal one that we're going to delete.
1809 mock_server_
->AddUpdateDirectory(2, 0, "FOO", 1, 10,
1810 foreign_cache_guid(), "-2");
1812 // Delete the legal one. The new update has a null name.
1813 mock_server_
->AddUpdateDirectory(
1814 2, 0, std::string(), 2, 20, foreign_cache_guid(), "-2");
1815 mock_server_
->SetLastUpdateDeleted();
1819 TEST_F(SyncerTest
, TestBasicUpdate
) {
1820 string id
= "some_id";
1821 string parent_id
= "0";
1822 string name
= "in_root";
1824 int64 timestamp
= 10;
1825 mock_server_
->AddUpdateDirectory(id
, parent_id
, name
, version
, timestamp
,
1826 foreign_cache_guid(), "-1");
1830 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1831 Entry
entry(&trans
, GET_BY_ID
,
1832 syncable::Id::CreateFromServerId("some_id"));
1833 ASSERT_TRUE(entry
.good());
1834 EXPECT_TRUE(entry
.GetIsDir());
1835 EXPECT_TRUE(entry
.GetServerVersion()== version
);
1836 EXPECT_TRUE(entry
.GetBaseVersion()== version
);
1837 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
1838 EXPECT_FALSE(entry
.GetIsUnsynced());
1839 EXPECT_FALSE(entry
.GetServerIsDel());
1840 EXPECT_FALSE(entry
.GetIsDel());
1844 TEST_F(SyncerTest
, IllegalAndLegalUpdates
) {
1845 Id root
= TestIdFactory::root();
1846 // Should apply just fine.
1847 mock_server_
->AddUpdateDirectory(1, 0, "in_root", 10, 10,
1848 foreign_cache_guid(), "-1");
1850 // Same name. But this SHOULD work.
1851 mock_server_
->AddUpdateDirectory(2, 0, "in_root", 10, 10,
1852 foreign_cache_guid(), "-2");
1854 // Unknown parent: should never be applied. "-80" is a legal server ID,
1855 // because any string sent by the server is a legal server ID in the sync
1856 // protocol, but it's not the ID of any item known to the client. This
1857 // update should succeed validation, but be stuck in the unapplied state
1858 // until an item with the server ID "-80" arrives.
1859 mock_server_
->AddUpdateDirectory(3, -80, "bad_parent", 10, 10,
1860 foreign_cache_guid(), "-3");
1864 // Id 3 should be in conflict now.
1867 GetUpdateCounters(BOOKMARKS
).num_hierarchy_conflict_application_failures
);
1869 // The only request in that loop should have been a GetUpdate.
1870 // At that point, we didn't know whether or not we had conflicts.
1871 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
1872 VerifyHierarchyConflictsUnspecified(mock_server_
->last_request());
1874 // These entries will be used in the second set of updates.
1875 mock_server_
->AddUpdateDirectory(4, 0, "newer_version", 20, 10,
1876 foreign_cache_guid(), "-4");
1877 mock_server_
->AddUpdateDirectory(5, 0, "circular1", 10, 10,
1878 foreign_cache_guid(), "-5");
1879 mock_server_
->AddUpdateDirectory(6, 5, "circular2", 10, 10,
1880 foreign_cache_guid(), "-6");
1881 mock_server_
->AddUpdateDirectory(9, 3, "bad_parent_child", 10, 10,
1882 foreign_cache_guid(), "-9");
1883 mock_server_
->AddUpdateDirectory(100, 9, "bad_parent_child2", 10, 10,
1884 foreign_cache_guid(), "-100");
1885 mock_server_
->AddUpdateDirectory(10, 0, "dir_to_bookmark", 10, 10,
1886 foreign_cache_guid(), "-10");
1889 // The three items with an unresolved parent should be unapplied (3, 9, 100).
1890 // The name clash should also still be in conflict.
1893 GetUpdateCounters(BOOKMARKS
).num_hierarchy_conflict_application_failures
);
1895 // This time around, we knew that there were conflicts.
1896 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
1897 VerifyHierarchyConflictsReported(mock_server_
->last_request());
1900 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1901 // Even though it has the same name, it should work.
1902 Entry
name_clash(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
1903 ASSERT_TRUE(name_clash
.good());
1904 EXPECT_FALSE(name_clash
.GetIsUnappliedUpdate())
1905 << "Duplicate name SHOULD be OK.";
1907 Entry
bad_parent(&trans
, GET_BY_ID
, ids_
.FromNumber(3));
1908 ASSERT_TRUE(bad_parent
.good());
1909 EXPECT_TRUE(bad_parent
.GetIsUnappliedUpdate())
1910 << "child of unknown parent should be in conflict";
1912 Entry
bad_parent_child(&trans
, GET_BY_ID
, ids_
.FromNumber(9));
1913 ASSERT_TRUE(bad_parent_child
.good());
1914 EXPECT_TRUE(bad_parent_child
.GetIsUnappliedUpdate())
1915 << "grandchild of unknown parent should be in conflict";
1917 Entry
bad_parent_child2(&trans
, GET_BY_ID
, ids_
.FromNumber(100));
1918 ASSERT_TRUE(bad_parent_child2
.good());
1919 EXPECT_TRUE(bad_parent_child2
.GetIsUnappliedUpdate())
1920 << "great-grandchild of unknown parent should be in conflict";
1923 // Updating 1 should not affect item 2 of the same name.
1924 mock_server_
->AddUpdateDirectory(1, 0, "new_name", 20, 20,
1925 foreign_cache_guid(), "-1");
1927 // Moving 5 under 6 will create a cycle: a conflict.
1928 mock_server_
->AddUpdateDirectory(5, 6, "circular3", 20, 20,
1929 foreign_cache_guid(), "-5");
1931 // Flip the is_dir bit: should fail verify & be dropped.
1932 mock_server_
->AddUpdateBookmark(10, 0, "dir_to_bookmark", 20, 20,
1933 foreign_cache_guid(), "-10");
1936 // Version number older than last known: should fail verify & be dropped.
1937 mock_server_
->AddUpdateDirectory(4, 0, "old_version", 10, 10,
1938 foreign_cache_guid(), "-4");
1941 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1943 Entry
still_a_dir(&trans
, GET_BY_ID
, ids_
.FromNumber(10));
1944 ASSERT_TRUE(still_a_dir
.good());
1945 EXPECT_FALSE(still_a_dir
.GetIsUnappliedUpdate());
1946 EXPECT_EQ(10u, still_a_dir
.GetBaseVersion());
1947 EXPECT_EQ(10u, still_a_dir
.GetServerVersion());
1948 EXPECT_TRUE(still_a_dir
.GetIsDir());
1950 Entry
rename(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
1951 ASSERT_TRUE(rename
.good());
1952 EXPECT_EQ(root
, rename
.GetParentId());
1953 EXPECT_EQ("new_name", rename
.GetNonUniqueName());
1954 EXPECT_FALSE(rename
.GetIsUnappliedUpdate());
1955 EXPECT_TRUE(ids_
.FromNumber(1) == rename
.GetId());
1956 EXPECT_EQ(20u, rename
.GetBaseVersion());
1958 Entry
name_clash(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
1959 ASSERT_TRUE(name_clash
.good());
1960 EXPECT_EQ(root
, name_clash
.GetParentId());
1961 EXPECT_TRUE(ids_
.FromNumber(2) == name_clash
.GetId());
1962 EXPECT_EQ(10u, name_clash
.GetBaseVersion());
1963 EXPECT_EQ("in_root", name_clash
.GetNonUniqueName());
1965 Entry
ignored_old_version(&trans
, GET_BY_ID
, ids_
.FromNumber(4));
1966 ASSERT_TRUE(ignored_old_version
.good());
1968 ignored_old_version
.GetNonUniqueName()== "newer_version");
1969 EXPECT_FALSE(ignored_old_version
.GetIsUnappliedUpdate());
1970 EXPECT_EQ(20u, ignored_old_version
.GetBaseVersion());
1972 Entry
circular_parent_issue(&trans
, GET_BY_ID
, ids_
.FromNumber(5));
1973 ASSERT_TRUE(circular_parent_issue
.good());
1974 EXPECT_TRUE(circular_parent_issue
.GetIsUnappliedUpdate())
1975 << "circular move should be in conflict";
1976 EXPECT_TRUE(circular_parent_issue
.GetParentId()== root_id_
);
1977 EXPECT_TRUE(circular_parent_issue
.GetServerParentId()==
1978 ids_
.FromNumber(6));
1979 EXPECT_EQ(10u, circular_parent_issue
.GetBaseVersion());
1981 Entry
circular_parent_target(&trans
, GET_BY_ID
, ids_
.FromNumber(6));
1982 ASSERT_TRUE(circular_parent_target
.good());
1983 EXPECT_FALSE(circular_parent_target
.GetIsUnappliedUpdate());
1984 EXPECT_TRUE(circular_parent_issue
.GetId()==
1985 circular_parent_target
.GetParentId());
1986 EXPECT_EQ(10u, circular_parent_target
.GetBaseVersion());
1989 EXPECT_FALSE(saw_syncer_event_
);
1992 GetUpdateCounters(BOOKMARKS
).num_hierarchy_conflict_application_failures
);
1995 // A commit with a lost response produces an update that has to be reunited with
1997 TEST_F(SyncerTest
, CommitReuniteUpdateAdjustsChildren
) {
1998 // Create a folder in the root.
1999 int64 metahandle_folder
;
2001 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2003 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_folder");
2004 ASSERT_TRUE(entry
.good());
2005 entry
.PutIsDir(true);
2006 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2007 entry
.PutIsUnsynced(true);
2008 metahandle_folder
= entry
.GetMetahandle();
2011 // Verify it and pull the ID out of the folder.
2012 syncable::Id folder_id
;
2013 int64 metahandle_entry
;
2015 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2016 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_folder
);
2017 ASSERT_TRUE(entry
.good());
2018 folder_id
= entry
.GetId();
2019 ASSERT_TRUE(!folder_id
.ServerKnows());
2022 // Create an entry in the newly created folder.
2024 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2025 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, folder_id
, "new_entry");
2026 ASSERT_TRUE(entry
.good());
2027 metahandle_entry
= entry
.GetMetahandle();
2028 WriteTestDataToEntry(&trans
, &entry
);
2031 // Verify it and pull the ID out of the entry.
2032 syncable::Id entry_id
;
2034 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2035 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, metahandle_entry
);
2036 ASSERT_TRUE(entry
.good());
2037 EXPECT_EQ(folder_id
, entry
.GetParentId());
2038 EXPECT_EQ("new_entry", entry
.GetNonUniqueName());
2039 entry_id
= entry
.GetId();
2040 EXPECT_TRUE(!entry_id
.ServerKnows());
2041 VerifyTestDataInEntry(&trans
, &entry
);
2044 // Now, to emulate a commit response failure, we just don't commit it.
2045 int64 new_version
= 150; // any larger value.
2046 int64 timestamp
= 20; // arbitrary value.
2047 syncable::Id new_folder_id
=
2048 syncable::Id::CreateFromServerId("folder_server_id");
2050 // The following update should cause the folder to both apply the update, as
2051 // well as reassociate the id.
2052 mock_server_
->AddUpdateDirectory(new_folder_id
, root_id_
,
2053 "new_folder", new_version
, timestamp
,
2054 local_cache_guid(), folder_id
.GetServerId());
2056 // We don't want it accidentally committed, just the update applied.
2057 mock_server_
->set_conflict_all_commits(true);
2059 // Alright! Apply that update!
2062 // The folder's ID should have been updated.
2063 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2064 Entry
folder(&trans
, GET_BY_HANDLE
, metahandle_folder
);
2065 ASSERT_TRUE(folder
.good());
2066 EXPECT_EQ("new_folder", folder
.GetNonUniqueName());
2067 EXPECT_TRUE(new_version
== folder
.GetBaseVersion());
2068 EXPECT_TRUE(new_folder_id
== folder
.GetId());
2069 EXPECT_TRUE(folder
.GetId().ServerKnows());
2070 EXPECT_EQ(trans
.root_id(), folder
.GetParentId());
2072 // Since it was updated, the old folder should not exist.
2073 Entry
old_dead_folder(&trans
, GET_BY_ID
, folder_id
);
2074 EXPECT_FALSE(old_dead_folder
.good());
2076 // The child's parent should have changed.
2077 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, metahandle_entry
);
2078 ASSERT_TRUE(entry
.good());
2079 EXPECT_EQ("new_entry", entry
.GetNonUniqueName());
2080 EXPECT_EQ(new_folder_id
, entry
.GetParentId());
2081 EXPECT_TRUE(!entry
.GetId().ServerKnows());
2082 VerifyTestDataInEntry(&trans
, &entry
);
2086 // A commit with a lost response produces an update that has to be reunited with
2088 TEST_F(SyncerTest
, CommitReuniteUpdate
) {
2089 // Create an entry in the root.
2090 int64 entry_metahandle
;
2092 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2093 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_entry");
2094 ASSERT_TRUE(entry
.good());
2095 entry_metahandle
= entry
.GetMetahandle();
2096 WriteTestDataToEntry(&trans
, &entry
);
2099 // Verify it and pull the ID out.
2100 syncable::Id entry_id
;
2102 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2104 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2105 ASSERT_TRUE(entry
.good());
2106 entry_id
= entry
.GetId();
2107 EXPECT_TRUE(!entry_id
.ServerKnows());
2108 VerifyTestDataInEntry(&trans
, &entry
);
2111 // Now, to emulate a commit response failure, we just don't commit it.
2112 int64 new_version
= 150; // any larger value.
2113 int64 timestamp
= 20; // arbitrary value.
2114 syncable::Id new_entry_id
= syncable::Id::CreateFromServerId("server_id");
2116 // Generate an update from the server with a relevant ID reassignment.
2117 mock_server_
->AddUpdateBookmark(new_entry_id
, root_id_
,
2118 "new_entry", new_version
, timestamp
,
2119 local_cache_guid(), entry_id
.GetServerId());
2121 // We don't want it accidentally committed, just the update applied.
2122 mock_server_
->set_conflict_all_commits(true);
2124 // Alright! Apply that update!
2127 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2128 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2129 ASSERT_TRUE(entry
.good());
2130 EXPECT_TRUE(new_version
== entry
.GetBaseVersion());
2131 EXPECT_TRUE(new_entry_id
== entry
.GetId());
2132 EXPECT_EQ("new_entry", entry
.GetNonUniqueName());
2136 // A commit with a lost response must work even if the local entry was deleted
2137 // before the update is applied. We should not duplicate the local entry in
2138 // this case, but just create another one alongside. We may wish to examine
2139 // this behavior in the future as it can create hanging uploads that never
2140 // finish, that must be cleaned up on the server side after some time.
2141 TEST_F(SyncerTest
, CommitReuniteUpdateDoesNotChokeOnDeletedLocalEntry
) {
2142 // Create a entry in the root.
2143 int64 entry_metahandle
;
2145 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2146 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_entry");
2147 ASSERT_TRUE(entry
.good());
2148 entry_metahandle
= entry
.GetMetahandle();
2149 WriteTestDataToEntry(&trans
, &entry
);
2151 // Verify it and pull the ID out.
2152 syncable::Id entry_id
;
2154 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2155 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2156 ASSERT_TRUE(entry
.good());
2157 entry_id
= entry
.GetId();
2158 EXPECT_TRUE(!entry_id
.ServerKnows());
2159 VerifyTestDataInEntry(&trans
, &entry
);
2162 // Now, to emulate a commit response failure, we just don't commit it.
2163 int64 new_version
= 150; // any larger value.
2164 int64 timestamp
= 20; // arbitrary value.
2165 syncable::Id new_entry_id
= syncable::Id::CreateFromServerId("server_id");
2167 // Generate an update from the server with a relevant ID reassignment.
2168 mock_server_
->AddUpdateBookmark(new_entry_id
, root_id_
,
2169 "new_entry", new_version
, timestamp
,
2170 local_cache_guid(), entry_id
.GetServerId());
2172 // We don't want it accidentally committed, just the update applied.
2173 mock_server_
->set_conflict_all_commits(true);
2175 // Purposefully delete the entry now before the update application finishes.
2177 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2178 Id new_entry_id
= GetOnlyEntryWithName(
2179 &trans
, trans
.root_id(), "new_entry");
2180 MutableEntry
entry(&trans
, GET_BY_ID
, new_entry_id
);
2181 ASSERT_TRUE(entry
.good());
2182 entry
.PutIsDel(true);
2185 // Just don't CHECK fail in sync, have the update split.
2188 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2189 Id new_entry_id
= GetOnlyEntryWithName(
2190 &trans
, trans
.root_id(), "new_entry");
2191 Entry
entry(&trans
, GET_BY_ID
, new_entry_id
);
2192 ASSERT_TRUE(entry
.good());
2193 EXPECT_FALSE(entry
.GetIsDel());
2195 Entry
old_entry(&trans
, GET_BY_ID
, entry_id
);
2196 ASSERT_TRUE(old_entry
.good());
2197 EXPECT_TRUE(old_entry
.GetIsDel());
2201 // TODO(chron): Add more unsanitized name tests.
2202 TEST_F(SyncerTest
, ConflictMatchingEntryHandlesUnsanitizedNames
) {
2203 mock_server_
->AddUpdateDirectory(1, 0, "A/A", 10, 10,
2204 foreign_cache_guid(), "-1");
2205 mock_server_
->AddUpdateDirectory(2, 0, "B/B", 10, 10,
2206 foreign_cache_guid(), "-2");
2207 mock_server_
->set_conflict_all_commits(true);
2210 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2212 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2213 ASSERT_TRUE(A
.good());
2214 A
.PutIsUnsynced(true);
2215 A
.PutIsUnappliedUpdate(true);
2216 A
.PutServerVersion(20);
2218 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
2219 ASSERT_TRUE(B
.good());
2220 B
.PutIsUnappliedUpdate(true);
2221 B
.PutServerVersion(20);
2224 saw_syncer_event_
= false;
2225 mock_server_
->set_conflict_all_commits(false);
2228 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2230 Entry
A(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2231 ASSERT_TRUE(A
.good());
2232 EXPECT_TRUE(A
.GetIsUnsynced()== false);
2233 EXPECT_TRUE(A
.GetIsUnappliedUpdate()== false);
2234 EXPECT_TRUE(A
.GetServerVersion()== 20);
2236 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
2237 ASSERT_TRUE(B
.good());
2238 EXPECT_TRUE(B
.GetIsUnsynced()== false);
2239 EXPECT_TRUE(B
.GetIsUnappliedUpdate()== false);
2240 EXPECT_TRUE(B
.GetServerVersion()== 20);
2244 TEST_F(SyncerTest
, ConflictMatchingEntryHandlesNormalNames
) {
2245 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
2246 foreign_cache_guid(), "-1");
2247 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
2248 foreign_cache_guid(), "-2");
2249 mock_server_
->set_conflict_all_commits(true);
2252 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2254 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2255 ASSERT_TRUE(A
.good());
2256 A
.PutIsUnsynced(true);
2257 A
.PutIsUnappliedUpdate(true);
2258 A
.PutServerVersion(20);
2260 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
2261 ASSERT_TRUE(B
.good());
2262 B
.PutIsUnappliedUpdate(true);
2263 B
.PutServerVersion(20);
2266 saw_syncer_event_
= false;
2267 mock_server_
->set_conflict_all_commits(false);
2270 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2272 Entry
A(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2273 ASSERT_TRUE(A
.good());
2274 EXPECT_TRUE(A
.GetIsUnsynced()== false);
2275 EXPECT_TRUE(A
.GetIsUnappliedUpdate()== false);
2276 EXPECT_TRUE(A
.GetServerVersion()== 20);
2278 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
2279 ASSERT_TRUE(B
.good());
2280 EXPECT_TRUE(B
.GetIsUnsynced()== false);
2281 EXPECT_TRUE(B
.GetIsUnappliedUpdate()== false);
2282 EXPECT_TRUE(B
.GetServerVersion()== 20);
2286 TEST_F(SyncerTest
, ReverseFolderOrderingTest
) {
2287 mock_server_
->AddUpdateDirectory(4, 3, "ggchild", 10, 10,
2288 foreign_cache_guid(), "-4");
2289 mock_server_
->AddUpdateDirectory(3, 2, "gchild", 10, 10,
2290 foreign_cache_guid(), "-3");
2291 mock_server_
->AddUpdateDirectory(5, 4, "gggchild", 10, 10,
2292 foreign_cache_guid(), "-5");
2293 mock_server_
->AddUpdateDirectory(2, 1, "child", 10, 10,
2294 foreign_cache_guid(), "-2");
2295 mock_server_
->AddUpdateDirectory(1, 0, "parent", 10, 10,
2296 foreign_cache_guid(), "-1");
2298 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2300 Id child_id
= GetOnlyEntryWithName(
2301 &trans
, ids_
.FromNumber(4), "gggchild");
2302 Entry
child(&trans
, GET_BY_ID
, child_id
);
2303 ASSERT_TRUE(child
.good());
2306 class EntryCreatedInNewFolderTest
: public SyncerTest
{
2308 void CreateFolderInBob() {
2309 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2310 MutableEntry
bob(&trans
,
2311 syncable::GET_BY_ID
,
2312 GetOnlyEntryWithName(&trans
,
2313 TestIdFactory::root(),
2317 MutableEntry
entry2(
2318 &trans
, CREATE
, BOOKMARKS
, bob
.GetId(), "bob");
2319 CHECK(entry2
.good());
2320 entry2
.PutIsDir(true);
2321 entry2
.PutIsUnsynced(true);
2322 entry2
.PutSpecifics(DefaultBookmarkSpecifics());
2326 TEST_F(EntryCreatedInNewFolderTest
, EntryCreatedInNewFolderMidSync
) {
2328 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2329 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2330 ASSERT_TRUE(entry
.good());
2331 entry
.PutIsDir(true);
2332 entry
.PutIsUnsynced(true);
2333 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2336 mock_server_
->SetMidCommitCallback(
2337 base::Bind(&EntryCreatedInNewFolderTest::CreateFolderInBob
,
2338 base::Unretained(this)));
2340 // We loop until no unsynced handles remain, so we will commit both ids.
2341 EXPECT_EQ(2u, mock_server_
->committed_ids().size());
2343 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2344 Entry
parent_entry(&trans
, syncable::GET_BY_ID
,
2345 GetOnlyEntryWithName(&trans
, TestIdFactory::root(), "bob"));
2346 ASSERT_TRUE(parent_entry
.good());
2349 GetOnlyEntryWithName(&trans
, parent_entry
.GetId(), "bob");
2350 Entry
child(&trans
, syncable::GET_BY_ID
, child_id
);
2351 ASSERT_TRUE(child
.good());
2352 EXPECT_EQ(parent_entry
.GetId(), child
.GetParentId());
2356 TEST_F(SyncerTest
, NegativeIDInUpdate
) {
2357 mock_server_
->AddUpdateBookmark(-10, 0, "bad", 40, 40,
2358 foreign_cache_guid(), "-100");
2360 // The negative id would make us CHECK!
2363 TEST_F(SyncerTest
, UnappliedUpdateOnCreatedItemItemDoesNotCrash
) {
2364 int64 metahandle_fred
;
2365 syncable::Id orig_id
;
2368 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2369 MutableEntry
fred_match(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(),
2371 ASSERT_TRUE(fred_match
.good());
2372 metahandle_fred
= fred_match
.GetMetahandle();
2373 orig_id
= fred_match
.GetId();
2374 WriteTestDataToEntry(&trans
, &fred_match
);
2378 EXPECT_EQ(1u, mock_server_
->committed_ids().size());
2379 mock_server_
->set_conflict_all_commits(true);
2380 syncable::Id fred_match_id
;
2382 // Now receive a change from outside.
2383 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2384 MutableEntry
fred_match(&trans
, GET_BY_HANDLE
, metahandle_fred
);
2385 ASSERT_TRUE(fred_match
.good());
2386 EXPECT_TRUE(fred_match
.GetId().ServerKnows());
2387 fred_match_id
= fred_match
.GetId();
2388 mock_server_
->AddUpdateBookmark(fred_match_id
, trans
.root_id(),
2389 "fred_match", 40, 40, local_cache_guid(), orig_id
.GetServerId());
2392 for (int i
= 0 ; i
< 30 ; ++i
) {
2398 * In the event that we have a double changed entry, that is changed on both
2399 * the client and the server, the conflict resolver should just drop one of
2400 * them and accept the other.
2403 TEST_F(SyncerTest
, DoublyChangedWithResolver
) {
2404 syncable::Id local_id
;
2406 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2407 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Folder");
2408 ASSERT_TRUE(parent
.good());
2409 parent
.PutIsDir(true);
2410 parent
.PutId(parent_id_
);
2411 parent
.PutBaseVersion(5);
2412 parent
.PutSpecifics(DefaultBookmarkSpecifics());
2413 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete.htm");
2414 ASSERT_TRUE(child
.good());
2415 local_id
= child
.GetId();
2416 child
.PutId(child_id_
);
2417 child
.PutBaseVersion(10);
2418 WriteTestDataToEntry(&wtrans
, &child
);
2420 mock_server_
->AddUpdateBookmark(child_id_
, parent_id_
, "Pete2.htm", 11, 10,
2421 local_cache_guid(), local_id
.GetServerId());
2422 mock_server_
->set_conflict_all_commits(true);
2424 syncable::Directory::Metahandles children
;
2426 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2427 directory()->GetChildHandlesById(&trans
, parent_id_
, &children
);
2428 // We expect the conflict resolver to preserve the local entry.
2429 Entry
child(&trans
, syncable::GET_BY_ID
, child_id_
);
2430 ASSERT_TRUE(child
.good());
2431 EXPECT_TRUE(child
.GetIsUnsynced());
2432 EXPECT_FALSE(child
.GetIsUnappliedUpdate());
2433 EXPECT_TRUE(child
.GetSpecifics().has_bookmark());
2434 EXPECT_EQ("Pete.htm", child
.GetNonUniqueName());
2435 VerifyTestBookmarkDataInEntry(&child
);
2438 // Only one entry, since we just overwrite one.
2439 EXPECT_EQ(1u, children
.size());
2440 saw_syncer_event_
= false;
2443 // We got this repro case when someone was editing bookmarks while sync was
2444 // occuring. The entry had changed out underneath the user.
2445 TEST_F(SyncerTest
, CommitsUpdateDoesntAlterEntry
) {
2446 const base::Time
& test_time
= ProtoTimeToTime(123456);
2447 syncable::Id local_id
;
2448 int64 entry_metahandle
;
2450 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2451 MutableEntry
entry(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Pete");
2452 ASSERT_TRUE(entry
.good());
2453 EXPECT_FALSE(entry
.GetId().ServerKnows());
2454 local_id
= entry
.GetId();
2455 entry
.PutIsDir(true);
2456 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2457 entry
.PutIsUnsynced(true);
2458 entry
.PutMtime(test_time
);
2459 entry_metahandle
= entry
.GetMetahandle();
2465 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2466 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, entry_metahandle
);
2467 ASSERT_TRUE(entry
.good());
2469 EXPECT_TRUE(id
.ServerKnows());
2470 version
= entry
.GetBaseVersion();
2472 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
2473 update
->set_originator_cache_guid(local_cache_guid());
2474 update
->set_originator_client_item_id(local_id
.GetServerId());
2475 EXPECT_EQ("Pete", update
->name());
2476 EXPECT_EQ(id
.GetServerId(), update
->id_string());
2477 EXPECT_EQ(root_id_
.GetServerId(), update
->parent_id_string());
2478 EXPECT_EQ(version
, update
->version());
2481 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2482 Entry
entry(&trans
, syncable::GET_BY_ID
, id
);
2483 ASSERT_TRUE(entry
.good());
2484 EXPECT_TRUE(entry
.GetMtime()== test_time
);
2488 TEST_F(SyncerTest
, ParentAndChildBothMatch
) {
2489 const FullModelTypeSet all_types
= FullModelTypeSet::All();
2490 syncable::Id parent_id
= ids_
.NewServerId();
2491 syncable::Id child_id
= ids_
.NewServerId();
2492 syncable::Id parent_local_id
;
2493 syncable::Id child_local_id
;
2497 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2498 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Folder");
2499 ASSERT_TRUE(parent
.good());
2500 parent_local_id
= parent
.GetId();
2501 parent
.PutIsDir(true);
2502 parent
.PutIsUnsynced(true);
2503 parent
.PutId(parent_id
);
2504 parent
.PutBaseVersion(1);
2505 parent
.PutSpecifics(DefaultBookmarkSpecifics());
2507 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent
.GetId(), "test.htm");
2508 ASSERT_TRUE(child
.good());
2509 child_local_id
= child
.GetId();
2510 child
.PutId(child_id
);
2511 child
.PutBaseVersion(1);
2512 child
.PutSpecifics(DefaultBookmarkSpecifics());
2513 WriteTestDataToEntry(&wtrans
, &child
);
2515 mock_server_
->AddUpdateDirectory(parent_id
, root_id_
, "Folder", 10, 10,
2517 parent_local_id
.GetServerId());
2518 mock_server_
->AddUpdateBookmark(child_id
, parent_id
, "test.htm", 10, 10,
2520 child_local_id
.GetServerId());
2521 mock_server_
->set_conflict_all_commits(true);
2526 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2527 Directory::Metahandles children
;
2528 directory()->GetChildHandlesById(&trans
, root_id_
, &children
);
2529 EXPECT_EQ(1u, children
.size());
2530 directory()->GetChildHandlesById(&trans
, parent_id
, &children
);
2531 EXPECT_EQ(1u, children
.size());
2532 std::vector
<int64
> unapplied
;
2533 directory()->GetUnappliedUpdateMetaHandles(&trans
, all_types
, &unapplied
);
2534 EXPECT_EQ(0u, unapplied
.size());
2535 syncable::Directory::Metahandles unsynced
;
2536 directory()->GetUnsyncedMetaHandles(&trans
, &unsynced
);
2537 EXPECT_EQ(0u, unsynced
.size());
2538 saw_syncer_event_
= false;
2542 TEST_F(SyncerTest
, CommittingNewDeleted
) {
2544 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2545 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2546 entry
.PutIsUnsynced(true);
2547 entry
.PutIsDel(true);
2550 EXPECT_EQ(0u, mock_server_
->committed_ids().size());
2553 // Original problem synopsis:
2554 // Check failed: entry->GetBaseVersion()<= entry->GetServerVersion()
2555 // Client creates entry, client finishes committing entry. Between
2556 // commit and getting update back, we delete the entry.
2557 // We get the update for the entry, but the local one was modified
2558 // so we store the entry but don't apply it. IS_UNAPPLIED_UPDATE is set.
2559 // We commit deletion and get a new version number.
2560 // We apply unapplied updates again before we get the update about the deletion.
2561 // This means we have an unapplied update where server_version < base_version.
2562 TEST_F(SyncerTest
, UnappliedUpdateDuringCommit
) {
2563 // This test is a little fake.
2565 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2566 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2567 entry
.PutId(ids_
.FromNumber(20));
2568 entry
.PutBaseVersion(1);
2569 entry
.PutServerVersion(1);
2570 entry
.PutServerParentId(ids_
.FromNumber(9999)); // Bad parent.
2571 entry
.PutIsUnsynced(true);
2572 entry
.PutIsUnappliedUpdate(true);
2573 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2574 entry
.PutServerSpecifics(DefaultBookmarkSpecifics());
2575 entry
.PutIsDel(false);
2578 EXPECT_EQ(1, session_
->status_controller().TotalNumConflictingItems());
2579 saw_syncer_event_
= false;
2582 // Original problem synopsis:
2584 // Unexpected error during sync if we:
2585 // make a new folder bob
2587 // make a new folder fred
2588 // move bob into fred
2591 // if no syncing occured midway, bob will have an illegal parent
2592 TEST_F(SyncerTest
, DeletingEntryInFolder
) {
2593 // This test is a little fake.
2594 int64 existing_metahandle
;
2596 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2597 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "existing");
2598 ASSERT_TRUE(entry
.good());
2599 entry
.PutIsDir(true);
2600 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2601 entry
.PutIsUnsynced(true);
2602 existing_metahandle
= entry
.GetMetahandle();
2606 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2607 MutableEntry
newfolder(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new");
2608 ASSERT_TRUE(newfolder
.good());
2609 newfolder
.PutIsDir(true);
2610 newfolder
.PutSpecifics(DefaultBookmarkSpecifics());
2611 newfolder
.PutIsUnsynced(true);
2613 MutableEntry
existing(&trans
, GET_BY_HANDLE
, existing_metahandle
);
2614 ASSERT_TRUE(existing
.good());
2615 existing
.PutParentId(newfolder
.GetId());
2616 existing
.PutIsUnsynced(true);
2617 EXPECT_TRUE(existing
.GetId().ServerKnows());
2619 newfolder
.PutIsDel(true);
2620 existing
.PutIsDel(true);
2623 EXPECT_EQ(0, GetCommitCounters(BOOKMARKS
).num_commits_conflict
);
2626 TEST_F(SyncerTest
, DeletingEntryWithLocalEdits
) {
2627 int64 newfolder_metahandle
;
2629 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
2630 foreign_cache_guid(), "-1");
2633 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2634 MutableEntry
newfolder(
2635 &trans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(1), "local");
2636 ASSERT_TRUE(newfolder
.good());
2637 newfolder
.PutIsUnsynced(true);
2638 newfolder
.PutIsDir(true);
2639 newfolder
.PutSpecifics(DefaultBookmarkSpecifics());
2640 newfolder_metahandle
= newfolder
.GetMetahandle();
2642 mock_server_
->AddUpdateDirectory(1, 0, "bob", 2, 20,
2643 foreign_cache_guid(), "-1");
2644 mock_server_
->SetLastUpdateDeleted();
2645 SyncShareConfigure();
2647 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2648 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, newfolder_metahandle
);
2649 ASSERT_TRUE(entry
.good());
2653 TEST_F(SyncerTest
, FolderSwapUpdate
) {
2654 mock_server_
->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2655 foreign_cache_guid(), "-7801");
2656 mock_server_
->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2657 foreign_cache_guid(), "-1024");
2659 mock_server_
->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2660 foreign_cache_guid(), "-1024");
2661 mock_server_
->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2662 foreign_cache_guid(), "-7801");
2665 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2666 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2667 ASSERT_TRUE(id1
.good());
2668 EXPECT_TRUE("fred" == id1
.GetNonUniqueName());
2669 EXPECT_TRUE(root_id_
== id1
.GetParentId());
2670 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2671 ASSERT_TRUE(id2
.good());
2672 EXPECT_TRUE("bob" == id2
.GetNonUniqueName());
2673 EXPECT_TRUE(root_id_
== id2
.GetParentId());
2675 saw_syncer_event_
= false;
2678 TEST_F(SyncerTest
, NameCollidingFolderSwapWorksFine
) {
2679 mock_server_
->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2680 foreign_cache_guid(), "-7801");
2681 mock_server_
->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2682 foreign_cache_guid(), "-1024");
2683 mock_server_
->AddUpdateDirectory(4096, 0, "alice", 1, 10,
2684 foreign_cache_guid(), "-4096");
2687 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2688 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2689 ASSERT_TRUE(id1
.good());
2690 EXPECT_TRUE("bob" == id1
.GetNonUniqueName());
2691 EXPECT_TRUE(root_id_
== id1
.GetParentId());
2692 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2693 ASSERT_TRUE(id2
.good());
2694 EXPECT_TRUE("fred" == id2
.GetNonUniqueName());
2695 EXPECT_TRUE(root_id_
== id2
.GetParentId());
2696 Entry
id3(&trans
, GET_BY_ID
, ids_
.FromNumber(4096));
2697 ASSERT_TRUE(id3
.good());
2698 EXPECT_TRUE("alice" == id3
.GetNonUniqueName());
2699 EXPECT_TRUE(root_id_
== id3
.GetParentId());
2701 mock_server_
->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2702 foreign_cache_guid(), "-1024");
2703 mock_server_
->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2704 foreign_cache_guid(), "-7801");
2705 mock_server_
->AddUpdateDirectory(4096, 0, "bob", 2, 20,
2706 foreign_cache_guid(), "-4096");
2709 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2710 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2711 ASSERT_TRUE(id1
.good());
2712 EXPECT_TRUE("fred" == id1
.GetNonUniqueName());
2713 EXPECT_TRUE(root_id_
== id1
.GetParentId());
2714 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2715 ASSERT_TRUE(id2
.good());
2716 EXPECT_TRUE("bob" == id2
.GetNonUniqueName());
2717 EXPECT_TRUE(root_id_
== id2
.GetParentId());
2718 Entry
id3(&trans
, GET_BY_ID
, ids_
.FromNumber(4096));
2719 ASSERT_TRUE(id3
.good());
2720 EXPECT_TRUE("bob" == id3
.GetNonUniqueName());
2721 EXPECT_TRUE(root_id_
== id3
.GetParentId());
2723 saw_syncer_event_
= false;
2726 // Committing more than kDefaultMaxCommitBatchSize items requires that
2727 // we post more than one commit command to the server. This test makes
2728 // sure that scenario works as expected.
2729 TEST_F(SyncerTest
, CommitManyItemsInOneGo_Success
) {
2730 uint32 num_batches
= 3;
2731 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2733 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2734 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2735 string nameutf8
= base::StringPrintf("%d", i
);
2736 string
name(nameutf8
.begin(), nameutf8
.end());
2737 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2738 e
.PutIsUnsynced(true);
2740 e
.PutSpecifics(DefaultBookmarkSpecifics());
2743 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2746 EXPECT_EQ(num_batches
, mock_server_
->commit_messages().size());
2747 EXPECT_EQ(0, directory()->unsynced_entity_count());
2750 // Test that a single failure to contact the server will cause us to exit the
2751 // commit loop immediately.
2752 TEST_F(SyncerTest
, CommitManyItemsInOneGo_PostBufferFail
) {
2753 uint32 num_batches
= 3;
2754 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2756 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2757 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2758 string nameutf8
= base::StringPrintf("%d", i
);
2759 string
name(nameutf8
.begin(), nameutf8
.end());
2760 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2761 e
.PutIsUnsynced(true);
2763 e
.PutSpecifics(DefaultBookmarkSpecifics());
2766 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2768 // The second commit should fail. It will be preceded by one successful
2769 // GetUpdate and one succesful commit.
2770 mock_server_
->FailNthPostBufferToPathCall(3);
2773 EXPECT_EQ(1U, mock_server_
->commit_messages().size());
2774 EXPECT_EQ(SYNC_SERVER_ERROR
,
2775 session_
->status_controller().model_neutral_state().commit_result
);
2776 EXPECT_EQ(items_to_commit
- kDefaultMaxCommitBatchSize
,
2777 directory()->unsynced_entity_count());
2780 // Test that a single conflict response from the server will cause us to exit
2781 // the commit loop immediately.
2782 TEST_F(SyncerTest
, CommitManyItemsInOneGo_CommitConflict
) {
2783 uint32 num_batches
= 2;
2784 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2786 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2787 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2788 string nameutf8
= base::StringPrintf("%d", i
);
2789 string
name(nameutf8
.begin(), nameutf8
.end());
2790 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2791 e
.PutIsUnsynced(true);
2793 e
.PutSpecifics(DefaultBookmarkSpecifics());
2796 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2798 // Return a CONFLICT response for the first item.
2799 mock_server_
->set_conflict_n_commits(1);
2802 // We should stop looping at the first sign of trouble.
2803 EXPECT_EQ(1U, mock_server_
->commit_messages().size());
2804 EXPECT_EQ(items_to_commit
- (kDefaultMaxCommitBatchSize
- 1),
2805 directory()->unsynced_entity_count());
2808 // Tests that sending debug info events works.
2809 TEST_F(SyncerTest
, SendDebugInfoEventsOnGetUpdates_HappyCase
) {
2810 debug_info_getter_
->AddDebugEvent();
2811 debug_info_getter_
->AddDebugEvent();
2815 // Verify we received one GetUpdates request with two debug info events.
2816 EXPECT_EQ(1U, mock_server_
->requests().size());
2817 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2818 EXPECT_EQ(2, mock_server_
->last_request().debug_info().events_size());
2822 // See that we received another GetUpdates request, but that it contains no
2823 // debug info events.
2824 EXPECT_EQ(2U, mock_server_
->requests().size());
2825 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2826 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
2828 debug_info_getter_
->AddDebugEvent();
2832 // See that we received another GetUpdates request and it contains one debug
2834 EXPECT_EQ(3U, mock_server_
->requests().size());
2835 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2836 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
2839 // Tests that debug info events are dropped on server error.
2840 TEST_F(SyncerTest
, SendDebugInfoEventsOnGetUpdates_PostFailsDontDrop
) {
2841 debug_info_getter_
->AddDebugEvent();
2842 debug_info_getter_
->AddDebugEvent();
2844 mock_server_
->FailNextPostBufferToPathCall();
2847 // Verify we attempted to send one GetUpdates request with two debug info
2849 EXPECT_EQ(1U, mock_server_
->requests().size());
2850 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2851 EXPECT_EQ(2, mock_server_
->last_request().debug_info().events_size());
2855 // See that the client resent the two debug info events.
2856 EXPECT_EQ(2U, mock_server_
->requests().size());
2857 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2858 EXPECT_EQ(2, mock_server_
->last_request().debug_info().events_size());
2860 // The previous send was successful so this next one shouldn't generate any
2861 // debug info events.
2863 EXPECT_EQ(3U, mock_server_
->requests().size());
2864 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2865 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
2868 // Tests that sending debug info events on Commit works.
2869 TEST_F(SyncerTest
, SendDebugInfoEventsOnCommit_HappyCase
) {
2870 // Make sure GetUpdate isn't call as it would "steal" debug info events before
2871 // Commit has a chance to send them.
2872 ConfigureNoGetUpdatesRequired();
2874 // Generate a debug info event and trigger a commit.
2875 debug_info_getter_
->AddDebugEvent();
2876 CreateUnsyncedDirectory("X", "id_X");
2879 // Verify that the last request received is a Commit and that it contains a
2880 // debug info event.
2881 EXPECT_EQ(1U, mock_server_
->requests().size());
2882 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2883 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
2885 // Generate another commit, but no debug info event.
2886 CreateUnsyncedDirectory("Y", "id_Y");
2889 // See that it was received and contains no debug info events.
2890 EXPECT_EQ(2U, mock_server_
->requests().size());
2891 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2892 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
2895 // Tests that debug info events are not dropped on server error.
2896 TEST_F(SyncerTest
, SendDebugInfoEventsOnCommit_PostFailsDontDrop
) {
2897 // Make sure GetUpdate isn't call as it would "steal" debug info events before
2898 // Commit has a chance to send them.
2899 ConfigureNoGetUpdatesRequired();
2901 mock_server_
->FailNextPostBufferToPathCall();
2903 // Generate a debug info event and trigger a commit.
2904 debug_info_getter_
->AddDebugEvent();
2905 CreateUnsyncedDirectory("X", "id_X");
2908 // Verify that the last request sent is a Commit and that it contains a debug
2910 EXPECT_EQ(1U, mock_server_
->requests().size());
2911 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2912 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
2917 // Verify that we've received another Commit and that it contains a debug info
2918 // event (just like the previous one).
2919 EXPECT_EQ(2U, mock_server_
->requests().size());
2920 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2921 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
2923 // Generate another commit and try again.
2924 CreateUnsyncedDirectory("Y", "id_Y");
2927 // See that it was received and contains no debug info events.
2928 EXPECT_EQ(3U, mock_server_
->requests().size());
2929 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2930 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
2933 TEST_F(SyncerTest
, HugeConflict
) {
2934 int item_count
= 300; // We should be able to do 300 or 3000 w/o issue.
2936 syncable::Id parent_id
= ids_
.NewServerId();
2937 syncable::Id last_id
= parent_id
;
2938 vector
<syncable::Id
> tree_ids
;
2940 // Create a lot of updates for which the parent does not exist yet.
2941 // Generate a huge deep tree which should all fail to apply at first.
2943 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2944 for (int i
= 0; i
< item_count
; i
++) {
2945 syncable::Id next_id
= ids_
.NewServerId();
2946 syncable::Id local_id
= ids_
.NewLocalId();
2947 tree_ids
.push_back(next_id
);
2948 mock_server_
->AddUpdateDirectory(next_id
, last_id
, "BOB", 2, 20,
2949 foreign_cache_guid(),
2950 local_id
.GetServerId());
2956 // Check they're in the expected conflict state.
2958 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2959 for (int i
= 0; i
< item_count
; i
++) {
2960 Entry
e(&trans
, GET_BY_ID
, tree_ids
[i
]);
2961 // They should all exist but none should be applied.
2962 ASSERT_TRUE(e
.good());
2963 EXPECT_TRUE(e
.GetIsDel());
2964 EXPECT_TRUE(e
.GetIsUnappliedUpdate());
2968 // Add the missing parent directory.
2969 mock_server_
->AddUpdateDirectory(parent_id
, TestIdFactory::root(),
2970 "BOB", 2, 20, foreign_cache_guid(), "-3500");
2973 // Now they should all be OK.
2975 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2976 for (int i
= 0; i
< item_count
; i
++) {
2977 Entry
e(&trans
, GET_BY_ID
, tree_ids
[i
]);
2978 ASSERT_TRUE(e
.good());
2979 EXPECT_FALSE(e
.GetIsDel());
2980 EXPECT_FALSE(e
.GetIsUnappliedUpdate());
2985 TEST_F(SyncerTest
, DontCrashOnCaseChange
) {
2986 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
2987 foreign_cache_guid(), "-1");
2990 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2991 MutableEntry
e(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2992 ASSERT_TRUE(e
.good());
2993 e
.PutIsUnsynced(true);
2995 mock_server_
->set_conflict_all_commits(true);
2996 mock_server_
->AddUpdateDirectory(1, 0, "BOB", 2, 20,
2997 foreign_cache_guid(), "-1");
2998 SyncShareNudge(); // USED TO CAUSE AN ASSERT
2999 saw_syncer_event_
= false;
3002 TEST_F(SyncerTest
, UnsyncedItemAndUpdate
) {
3003 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
3004 foreign_cache_guid(), "-1");
3006 mock_server_
->set_conflict_all_commits(true);
3007 mock_server_
->AddUpdateDirectory(2, 0, "bob", 2, 20,
3008 foreign_cache_guid(), "-2");
3009 SyncShareNudge(); // USED TO CAUSE AN ASSERT
3010 saw_syncer_event_
= false;
3013 TEST_F(SyncerTest
, NewEntryAndAlteredServerEntrySharePath
) {
3014 mock_server_
->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
3015 foreign_cache_guid(), "-1");
3017 int64 local_folder_handle
;
3018 syncable::Id local_folder_id
;
3020 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3021 MutableEntry
new_entry(
3022 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bar.htm");
3023 ASSERT_TRUE(new_entry
.good());
3024 local_folder_id
= new_entry
.GetId();
3025 local_folder_handle
= new_entry
.GetMetahandle();
3026 new_entry
.PutIsUnsynced(true);
3027 new_entry
.PutSpecifics(DefaultBookmarkSpecifics());
3028 MutableEntry
old(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3029 ASSERT_TRUE(old
.good());
3030 WriteTestDataToEntry(&wtrans
, &old
);
3032 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
3033 foreign_cache_guid(), "-1");
3034 mock_server_
->set_conflict_all_commits(true);
3036 saw_syncer_event_
= false;
3038 // Update #20 should have been dropped in favor of the local version.
3039 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3040 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3041 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3042 ASSERT_TRUE(server
.good());
3043 ASSERT_TRUE(local
.good());
3044 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3045 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3046 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3047 EXPECT_TRUE(server
.GetIsUnsynced());
3048 EXPECT_TRUE(local
.GetIsUnsynced());
3049 EXPECT_EQ("Foo.htm", server
.GetNonUniqueName());
3050 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3052 // Allow local changes to commit.
3053 mock_server_
->set_conflict_all_commits(false);
3055 saw_syncer_event_
= false;
3057 // Now add a server change to make the two names equal. There should
3058 // be no conflict with that, since names are not unique.
3059 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
3060 foreign_cache_guid(), "-1");
3062 saw_syncer_event_
= false;
3064 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3065 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3066 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3067 ASSERT_TRUE(server
.good());
3068 ASSERT_TRUE(local
.good());
3069 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3070 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3071 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3072 EXPECT_FALSE(server
.GetIsUnsynced());
3073 EXPECT_FALSE(local
.GetIsUnsynced());
3074 EXPECT_EQ("Bar.htm", server
.GetNonUniqueName());
3075 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3076 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
3077 server
.GetSpecifics().bookmark().url());
3081 // Same as NewEntryAnddServerEntrySharePath, but using the old-style protocol.
3082 TEST_F(SyncerTest
, NewEntryAndAlteredServerEntrySharePath_OldBookmarksProto
) {
3083 mock_server_
->set_use_legacy_bookmarks_protocol(true);
3084 mock_server_
->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
3085 foreign_cache_guid(), "-1");
3087 int64 local_folder_handle
;
3088 syncable::Id local_folder_id
;
3090 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3091 MutableEntry
new_entry(
3092 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bar.htm");
3093 ASSERT_TRUE(new_entry
.good());
3094 local_folder_id
= new_entry
.GetId();
3095 local_folder_handle
= new_entry
.GetMetahandle();
3096 new_entry
.PutIsUnsynced(true);
3097 new_entry
.PutSpecifics(DefaultBookmarkSpecifics());
3098 MutableEntry
old(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3099 ASSERT_TRUE(old
.good());
3100 WriteTestDataToEntry(&wtrans
, &old
);
3102 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
3103 foreign_cache_guid(), "-1");
3104 mock_server_
->set_conflict_all_commits(true);
3106 saw_syncer_event_
= false;
3108 // Update #20 should have been dropped in favor of the local version.
3109 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3110 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3111 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3112 ASSERT_TRUE(server
.good());
3113 ASSERT_TRUE(local
.good());
3114 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3115 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3116 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3117 EXPECT_TRUE(server
.GetIsUnsynced());
3118 EXPECT_TRUE(local
.GetIsUnsynced());
3119 EXPECT_EQ("Foo.htm", server
.GetNonUniqueName());
3120 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3122 // Allow local changes to commit.
3123 mock_server_
->set_conflict_all_commits(false);
3125 saw_syncer_event_
= false;
3127 // Now add a server change to make the two names equal. There should
3128 // be no conflict with that, since names are not unique.
3129 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
3130 foreign_cache_guid(), "-1");
3132 saw_syncer_event_
= false;
3134 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3135 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3136 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3137 ASSERT_TRUE(server
.good());
3138 ASSERT_TRUE(local
.good());
3139 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3140 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3141 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3142 EXPECT_FALSE(server
.GetIsUnsynced());
3143 EXPECT_FALSE(local
.GetIsUnsynced());
3144 EXPECT_EQ("Bar.htm", server
.GetNonUniqueName());
3145 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3146 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
3147 server
.GetSpecifics().bookmark().url());
3151 // Circular links should be resolved by the server.
3152 TEST_F(SyncerTest
, SiblingDirectoriesBecomeCircular
) {
3153 // we don't currently resolve this. This test ensures we don't.
3154 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
3155 foreign_cache_guid(), "-1");
3156 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
3157 foreign_cache_guid(), "-2");
3160 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3161 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3162 ASSERT_TRUE(A
.good());
3163 A
.PutIsUnsynced(true);
3164 A
.PutParentId(ids_
.FromNumber(2));
3165 A
.PutNonUniqueName("B");
3167 mock_server_
->AddUpdateDirectory(2, 1, "A", 20, 20,
3168 foreign_cache_guid(), "-2");
3169 mock_server_
->set_conflict_all_commits(true);
3171 saw_syncer_event_
= false;
3173 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3174 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3175 ASSERT_TRUE(A
.good());
3176 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
3177 ASSERT_TRUE(B
.good());
3178 EXPECT_TRUE(A
.GetNonUniqueName()== "B");
3179 EXPECT_TRUE(B
.GetNonUniqueName()== "B");
3183 TEST_F(SyncerTest
, SwapEntryNames
) {
3184 // Simple transaction test.
3185 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
3186 foreign_cache_guid(), "-1");
3187 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
3188 foreign_cache_guid(), "-2");
3189 mock_server_
->set_conflict_all_commits(true);
3192 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3193 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3194 ASSERT_TRUE(A
.good());
3195 A
.PutIsUnsynced(true);
3196 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
3197 ASSERT_TRUE(B
.good());
3198 B
.PutIsUnsynced(true);
3199 A
.PutNonUniqueName("C");
3200 B
.PutNonUniqueName("A");
3201 A
.PutNonUniqueName("B");
3204 saw_syncer_event_
= false;
3207 TEST_F(SyncerTest
, DualDeletionWithNewItemNameClash
) {
3208 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
3209 foreign_cache_guid(), "-1");
3210 mock_server_
->AddUpdateBookmark(2, 0, "B", 10, 10,
3211 foreign_cache_guid(), "-2");
3212 mock_server_
->set_conflict_all_commits(true);
3215 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3216 MutableEntry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3217 ASSERT_TRUE(B
.good());
3218 WriteTestDataToEntry(&trans
, &B
);
3221 mock_server_
->AddUpdateBookmark(2, 0, "A", 11, 11,
3222 foreign_cache_guid(), "-2");
3223 mock_server_
->SetLastUpdateDeleted();
3226 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3227 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3228 ASSERT_TRUE(B
.good());
3229 EXPECT_FALSE(B
.GetIsUnsynced());
3230 EXPECT_FALSE(B
.GetIsUnappliedUpdate());
3232 saw_syncer_event_
= false;
3235 // When we undelete an entity as a result of conflict resolution, we reuse the
3236 // existing server id and preserve the old version, simply updating the server
3237 // version with the new non-deleted entity.
3238 TEST_F(SyncerTest
, ResolveWeWroteTheyDeleted
) {
3239 int64 bob_metahandle
;
3241 mock_server_
->AddUpdateBookmark(1, 0, "bob", 1, 10,
3242 foreign_cache_guid(), "-1");
3245 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3246 MutableEntry
bob(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3247 ASSERT_TRUE(bob
.good());
3248 bob_metahandle
= bob
.GetMetahandle();
3249 WriteTestDataToEntry(&trans
, &bob
);
3251 mock_server_
->AddUpdateBookmark(1, 0, "bob", 2, 10,
3252 foreign_cache_guid(), "-1");
3253 mock_server_
->SetLastUpdateDeleted();
3254 mock_server_
->set_conflict_all_commits(true);
3258 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3259 Entry
bob(&trans
, GET_BY_HANDLE
, bob_metahandle
);
3260 ASSERT_TRUE(bob
.good());
3261 EXPECT_TRUE(bob
.GetIsUnsynced());
3262 EXPECT_TRUE(bob
.GetId().ServerKnows());
3263 EXPECT_FALSE(bob
.GetIsUnappliedUpdate());
3264 EXPECT_FALSE(bob
.GetIsDel());
3265 EXPECT_EQ(2, bob
.GetServerVersion());
3266 EXPECT_EQ(2, bob
.GetBaseVersion());
3268 saw_syncer_event_
= false;
3271 // This test is to reproduce a check failure. Sometimes we would get a bad ID
3272 // back when creating an entry.
3273 TEST_F(SyncerTest
, DuplicateIDReturn
) {
3275 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3276 MutableEntry
folder(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
3277 ASSERT_TRUE(folder
.good());
3278 folder
.PutIsUnsynced(true);
3279 folder
.PutIsDir(true);
3280 folder
.PutSpecifics(DefaultBookmarkSpecifics());
3281 MutableEntry
folder2(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "fred");
3282 ASSERT_TRUE(folder2
.good());
3283 folder2
.PutIsUnsynced(false);
3284 folder2
.PutIsDir(true);
3285 folder2
.PutSpecifics(DefaultBookmarkSpecifics());
3286 folder2
.PutBaseVersion(3);
3287 folder2
.PutId(syncable::Id::CreateFromServerId("mock_server:10000"));
3289 mock_server_
->set_next_new_id(10000);
3290 EXPECT_EQ(1u, directory()->unsynced_entity_count());
3291 // we get back a bad id in here (should never happen).
3293 EXPECT_EQ(1u, directory()->unsynced_entity_count());
3294 SyncShareNudge(); // another bad id in here.
3295 EXPECT_EQ(0u, directory()->unsynced_entity_count());
3296 saw_syncer_event_
= false;
3299 TEST_F(SyncerTest
, DeletedEntryWithBadParentInLoopCalculation
) {
3300 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
3301 foreign_cache_guid(), "-1");
3304 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3305 MutableEntry
bob(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3306 ASSERT_TRUE(bob
.good());
3307 // This is valid, because the parent could have gone away a long time ago.
3308 bob
.PutParentId(ids_
.FromNumber(54));
3310 bob
.PutIsUnsynced(true);
3312 mock_server_
->AddUpdateDirectory(2, 1, "fred", 1, 10,
3313 foreign_cache_guid(), "-2");
3318 TEST_F(SyncerTest
, ConflictResolverMergesLocalDeleteAndServerUpdate
) {
3319 syncable::Id local_id
;
3321 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3323 MutableEntry
local_deleted(
3324 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "name");
3325 local_id
= local_deleted
.GetId();
3326 local_deleted
.PutId(ids_
.FromNumber(1));
3327 local_deleted
.PutBaseVersion(1);
3328 local_deleted
.PutIsDel(true);
3329 local_deleted
.PutIsDir(false);
3330 local_deleted
.PutIsUnsynced(true);
3331 local_deleted
.PutSpecifics(DefaultBookmarkSpecifics());
3334 mock_server_
->AddUpdateBookmark(ids_
.FromNumber(1), root_id_
, "name", 10, 10,
3336 local_id
.GetServerId());
3338 // We don't care about actually committing, just the resolution.
3339 mock_server_
->set_conflict_all_commits(true);
3343 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3344 Entry
local_deleted(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3345 EXPECT_TRUE(local_deleted
.GetBaseVersion()== 10);
3346 EXPECT_TRUE(local_deleted
.GetIsUnappliedUpdate()== false);
3347 EXPECT_TRUE(local_deleted
.GetIsUnsynced()== true);
3348 EXPECT_TRUE(local_deleted
.GetIsDel()== true);
3349 EXPECT_TRUE(local_deleted
.GetIsDir()== false);
3353 // See what happens if the IS_DIR bit gets flipped. This can cause us
3354 // all kinds of disasters.
3355 TEST_F(SyncerTest
, UpdateFlipsTheFolderBit
) {
3356 // Local object: a deleted directory (container), revision 1, unsynced.
3358 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3360 MutableEntry
local_deleted(
3361 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "name");
3362 local_deleted
.PutId(ids_
.FromNumber(1));
3363 local_deleted
.PutBaseVersion(1);
3364 local_deleted
.PutIsDel(true);
3365 local_deleted
.PutIsDir(true);
3366 local_deleted
.PutIsUnsynced(true);
3367 local_deleted
.PutSpecifics(DefaultBookmarkSpecifics());
3370 // Server update: entry-type object (not a container), revision 10.
3371 mock_server_
->AddUpdateBookmark(ids_
.FromNumber(1), root_id_
, "name", 10, 10,
3373 ids_
.FromNumber(1).GetServerId());
3375 // Don't attempt to commit.
3376 mock_server_
->set_conflict_all_commits(true);
3378 // The syncer should not attempt to apply the invalid update.
3382 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3383 Entry
local_deleted(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3384 EXPECT_TRUE(local_deleted
.GetBaseVersion()== 1);
3385 EXPECT_TRUE(local_deleted
.GetIsUnappliedUpdate()== false);
3386 EXPECT_TRUE(local_deleted
.GetIsUnsynced()== true);
3387 EXPECT_TRUE(local_deleted
.GetIsDel()== true);
3388 EXPECT_TRUE(local_deleted
.GetIsDir()== true);
3393 // Merge conflict resolution will merge a new local entry with another entry
3394 // that needs updates, resulting in CHECK.
3395 TEST_F(SyncerTest
, MergingExistingItems
) {
3396 mock_server_
->set_conflict_all_commits(true);
3397 mock_server_
->AddUpdateBookmark(1, 0, "base", 10, 10,
3398 local_cache_guid(), "-1");
3401 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3403 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "Copy of base");
3404 WriteTestDataToEntry(&trans
, &entry
);
3406 mock_server_
->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
3407 local_cache_guid(), "-1");
3411 // In this test a long changelog contains a child at the start of the changelog
3412 // and a parent at the end. While these updates are in progress the client would
3414 TEST_F(SyncerTest
, LongChangelistWithApplicationConflict
) {
3415 const int depth
= 400;
3416 syncable::Id folder_id
= ids_
.FromNumber(1);
3418 // First we an item in a folder in the root. However the folder won't come
3420 syncable::Id stuck_entry_id
= TestIdFactory::FromNumber(99999);
3421 mock_server_
->AddUpdateDirectory(stuck_entry_id
,
3422 folder_id
, "stuck", 1, 1,
3423 foreign_cache_guid(), "-99999");
3424 mock_server_
->SetChangesRemaining(depth
- 1);
3427 // Buffer up a very long series of downloads.
3428 // We should never be stuck (conflict resolution shouldn't
3429 // kick in so long as we're making forward progress).
3430 for (int i
= 0; i
< depth
; i
++) {
3431 mock_server_
->NextUpdateBatch();
3432 mock_server_
->SetNewTimestamp(i
+ 1);
3433 mock_server_
->SetChangesRemaining(depth
- i
);
3438 // Ensure our folder hasn't somehow applied.
3440 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3441 Entry
child(&trans
, GET_BY_ID
, stuck_entry_id
);
3442 EXPECT_TRUE(child
.good());
3443 EXPECT_TRUE(child
.GetIsUnappliedUpdate());
3444 EXPECT_TRUE(child
.GetIsDel());
3445 EXPECT_FALSE(child
.GetIsUnsynced());
3448 // And finally the folder.
3449 mock_server_
->AddUpdateDirectory(folder_id
,
3450 TestIdFactory::root(), "folder", 1, 1,
3451 foreign_cache_guid(), "-1");
3452 mock_server_
->SetChangesRemaining(0);
3455 // Check that everything is as expected after the commit.
3457 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3458 Entry
entry(&trans
, GET_BY_ID
, folder_id
);
3459 ASSERT_TRUE(entry
.good());
3460 Entry
child(&trans
, GET_BY_ID
, stuck_entry_id
);
3461 EXPECT_EQ(entry
.GetId(), child
.GetParentId());
3462 EXPECT_EQ("stuck", child
.GetNonUniqueName());
3463 EXPECT_TRUE(child
.good());
3467 TEST_F(SyncerTest
, DontMergeTwoExistingItems
) {
3468 mock_server_
->set_conflict_all_commits(true);
3469 mock_server_
->AddUpdateBookmark(1, 0, "base", 10, 10,
3470 foreign_cache_guid(), "-1");
3471 mock_server_
->AddUpdateBookmark(2, 0, "base2", 10, 10,
3472 foreign_cache_guid(), "-2");
3475 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3476 MutableEntry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3477 ASSERT_TRUE(entry
.good());
3478 entry
.PutNonUniqueName("Copy of base");
3479 entry
.PutIsUnsynced(true);
3481 mock_server_
->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
3482 foreign_cache_guid(), "-1");
3485 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3486 Entry
entry1(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3487 EXPECT_FALSE(entry1
.GetIsUnappliedUpdate());
3488 EXPECT_FALSE(entry1
.GetIsUnsynced());
3489 EXPECT_FALSE(entry1
.GetIsDel());
3490 Entry
entry2(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3491 EXPECT_FALSE(entry2
.GetIsUnappliedUpdate());
3492 EXPECT_TRUE(entry2
.GetIsUnsynced());
3493 EXPECT_FALSE(entry2
.GetIsDel());
3494 EXPECT_EQ(entry1
.GetNonUniqueName(), entry2
.GetNonUniqueName());
3498 TEST_F(SyncerTest
, TestUndeleteUpdate
) {
3499 mock_server_
->set_conflict_all_commits(true);
3500 mock_server_
->AddUpdateDirectory(1, 0, "foo", 1, 1,
3501 foreign_cache_guid(), "-1");
3502 mock_server_
->AddUpdateDirectory(2, 1, "bar", 1, 2,
3503 foreign_cache_guid(), "-2");
3505 mock_server_
->AddUpdateDirectory(2, 1, "bar", 2, 3,
3506 foreign_cache_guid(), "-2");
3507 mock_server_
->SetLastUpdateDeleted();
3512 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3513 Entry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3514 ASSERT_TRUE(entry
.good());
3515 EXPECT_TRUE(entry
.GetIsDel());
3516 metahandle
= entry
.GetMetahandle();
3518 mock_server_
->AddUpdateDirectory(1, 0, "foo", 2, 4,
3519 foreign_cache_guid(), "-1");
3520 mock_server_
->SetLastUpdateDeleted();
3522 // This used to be rejected as it's an undeletion. Now, it results in moving
3523 // the delete path aside.
3524 mock_server_
->AddUpdateDirectory(2, 1, "bar", 3, 5,
3525 foreign_cache_guid(), "-2");
3528 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3529 Entry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3530 ASSERT_TRUE(entry
.good());
3531 EXPECT_TRUE(entry
.GetIsDel());
3532 EXPECT_FALSE(entry
.GetServerIsDel());
3533 EXPECT_TRUE(entry
.GetIsUnappliedUpdate());
3534 EXPECT_NE(entry
.GetMetahandle(), metahandle
);
3538 TEST_F(SyncerTest
, TestMoveSanitizedNamedFolder
) {
3539 mock_server_
->AddUpdateDirectory(1, 0, "foo", 1, 1,
3540 foreign_cache_guid(), "-1");
3541 mock_server_
->AddUpdateDirectory(2, 0, ":::", 1, 2,
3542 foreign_cache_guid(), "-2");
3545 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3546 MutableEntry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3547 ASSERT_TRUE(entry
.good());
3548 entry
.PutParentId(ids_
.FromNumber(1));
3549 EXPECT_TRUE(entry
.PutIsUnsynced(true));
3552 // We use the same sync ts as before so our times match up.
3553 mock_server_
->AddUpdateDirectory(2, 1, ":::", 2, 2,
3554 foreign_cache_guid(), "-2");
3558 // Don't crash when this occurs.
3559 TEST_F(SyncerTest
, UpdateWhereParentIsNotAFolder
) {
3560 mock_server_
->AddUpdateBookmark(1, 0, "B", 10, 10,
3561 foreign_cache_guid(), "-1");
3562 mock_server_
->AddUpdateDirectory(2, 1, "BookmarkParent", 10, 10,
3563 foreign_cache_guid(), "-2");
3564 // Used to cause a CHECK
3567 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
3568 Entry
good_entry(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
3569 ASSERT_TRUE(good_entry
.good());
3570 EXPECT_FALSE(good_entry
.GetIsUnappliedUpdate());
3571 Entry
bad_parent(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(2));
3572 ASSERT_TRUE(bad_parent
.good());
3573 EXPECT_TRUE(bad_parent
.GetIsUnappliedUpdate());
3577 TEST_F(SyncerTest
, DirectoryUpdateTest
) {
3578 Id in_root_id
= ids_
.NewServerId();
3579 Id in_in_root_id
= ids_
.NewServerId();
3581 mock_server_
->AddUpdateDirectory(in_root_id
, TestIdFactory::root(),
3582 "in_root_name", 2, 2,
3583 foreign_cache_guid(), "-1");
3584 mock_server_
->AddUpdateDirectory(in_in_root_id
, in_root_id
,
3585 "in_in_root_name", 3, 3,
3586 foreign_cache_guid(), "-2");
3589 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3590 Entry
in_root(&trans
, GET_BY_ID
, in_root_id
);
3591 ASSERT_TRUE(in_root
.good());
3592 EXPECT_EQ("in_root_name", in_root
.GetNonUniqueName());
3593 EXPECT_EQ(TestIdFactory::root(), in_root
.GetParentId());
3595 Entry
in_in_root(&trans
, GET_BY_ID
, in_in_root_id
);
3596 ASSERT_TRUE(in_in_root
.good());
3597 EXPECT_EQ("in_in_root_name", in_in_root
.GetNonUniqueName());
3598 EXPECT_EQ(in_root_id
, in_in_root
.GetParentId());
3602 TEST_F(SyncerTest
, DirectoryCommitTest
) {
3603 syncable::Id in_root_id
, in_dir_id
;
3604 int64 foo_metahandle
;
3605 int64 bar_metahandle
;
3608 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3609 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "foo");
3610 ASSERT_TRUE(parent
.good());
3611 parent
.PutIsUnsynced(true);
3612 parent
.PutIsDir(true);
3613 parent
.PutSpecifics(DefaultBookmarkSpecifics());
3614 in_root_id
= parent
.GetId();
3615 foo_metahandle
= parent
.GetMetahandle();
3617 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent
.GetId(), "bar");
3618 ASSERT_TRUE(child
.good());
3619 child
.PutIsUnsynced(true);
3620 child
.PutIsDir(true);
3621 child
.PutSpecifics(DefaultBookmarkSpecifics());
3622 bar_metahandle
= child
.GetMetahandle();
3623 in_dir_id
= parent
.GetId();
3627 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3628 Entry
fail_by_old_id_entry(&trans
, GET_BY_ID
, in_root_id
);
3629 ASSERT_FALSE(fail_by_old_id_entry
.good());
3631 Entry
foo_entry(&trans
, GET_BY_HANDLE
, foo_metahandle
);
3632 ASSERT_TRUE(foo_entry
.good());
3633 EXPECT_EQ("foo", foo_entry
.GetNonUniqueName());
3634 EXPECT_NE(foo_entry
.GetId(), in_root_id
);
3636 Entry
bar_entry(&trans
, GET_BY_HANDLE
, bar_metahandle
);
3637 ASSERT_TRUE(bar_entry
.good());
3638 EXPECT_EQ("bar", bar_entry
.GetNonUniqueName());
3639 EXPECT_NE(bar_entry
.GetId(), in_dir_id
);
3640 EXPECT_EQ(foo_entry
.GetId(), bar_entry
.GetParentId());
3644 TEST_F(SyncerTest
, TestClientCommandDuringUpdate
) {
3645 using sync_pb::ClientCommand
;
3647 ClientCommand
* command
= new ClientCommand();
3648 command
->set_set_sync_poll_interval(8);
3649 command
->set_set_sync_long_poll_interval(800);
3650 command
->set_sessions_commit_delay_seconds(3141);
3651 sync_pb::CustomNudgeDelay
* bookmark_delay
=
3652 command
->add_custom_nudge_delays();
3653 bookmark_delay
->set_datatype_id(
3654 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3655 bookmark_delay
->set_delay_ms(950);
3656 command
->set_client_invalidation_hint_buffer_size(11);
3657 mock_server_
->AddUpdateDirectory(1, 0, "in_root", 1, 1,
3658 foreign_cache_guid(), "-1");
3659 mock_server_
->SetGUClientCommand(command
);
3662 EXPECT_EQ(TimeDelta::FromSeconds(8), last_short_poll_interval_received_
);
3663 EXPECT_EQ(TimeDelta::FromSeconds(800), last_long_poll_interval_received_
);
3664 EXPECT_EQ(TimeDelta::FromSeconds(3141), last_sessions_commit_delay_
);
3665 EXPECT_EQ(TimeDelta::FromMilliseconds(950), last_bookmarks_commit_delay_
);
3666 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_
);
3668 command
= new ClientCommand();
3669 command
->set_set_sync_poll_interval(180);
3670 command
->set_set_sync_long_poll_interval(190);
3671 command
->set_sessions_commit_delay_seconds(2718);
3672 bookmark_delay
= command
->add_custom_nudge_delays();
3673 bookmark_delay
->set_datatype_id(
3674 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3675 bookmark_delay
->set_delay_ms(1050);
3676 command
->set_client_invalidation_hint_buffer_size(9);
3677 mock_server_
->AddUpdateDirectory(
3678 1, 0, "in_root", 1, 1, foreign_cache_guid(), "-1");
3679 mock_server_
->SetGUClientCommand(command
);
3682 EXPECT_EQ(TimeDelta::FromSeconds(180), last_short_poll_interval_received_
);
3683 EXPECT_EQ(TimeDelta::FromSeconds(190), last_long_poll_interval_received_
);
3684 EXPECT_EQ(TimeDelta::FromSeconds(2718), last_sessions_commit_delay_
);
3685 EXPECT_EQ(TimeDelta::FromMilliseconds(1050), last_bookmarks_commit_delay_
);
3686 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_
);
3689 TEST_F(SyncerTest
, TestClientCommandDuringCommit
) {
3690 using sync_pb::ClientCommand
;
3692 ClientCommand
* command
= new ClientCommand();
3693 command
->set_set_sync_poll_interval(8);
3694 command
->set_set_sync_long_poll_interval(800);
3695 command
->set_sessions_commit_delay_seconds(3141);
3696 sync_pb::CustomNudgeDelay
* bookmark_delay
=
3697 command
->add_custom_nudge_delays();
3698 bookmark_delay
->set_datatype_id(
3699 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3700 bookmark_delay
->set_delay_ms(950);
3701 command
->set_client_invalidation_hint_buffer_size(11);
3702 CreateUnsyncedDirectory("X", "id_X");
3703 mock_server_
->SetCommitClientCommand(command
);
3706 EXPECT_EQ(TimeDelta::FromSeconds(8), last_short_poll_interval_received_
);
3707 EXPECT_EQ(TimeDelta::FromSeconds(800), last_long_poll_interval_received_
);
3708 EXPECT_EQ(TimeDelta::FromSeconds(3141), last_sessions_commit_delay_
);
3709 EXPECT_EQ(TimeDelta::FromMilliseconds(950), last_bookmarks_commit_delay_
);
3710 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_
);
3712 command
= new ClientCommand();
3713 command
->set_set_sync_poll_interval(180);
3714 command
->set_set_sync_long_poll_interval(190);
3715 command
->set_sessions_commit_delay_seconds(2718);
3716 bookmark_delay
= command
->add_custom_nudge_delays();
3717 bookmark_delay
->set_datatype_id(
3718 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3719 bookmark_delay
->set_delay_ms(1050);
3720 command
->set_client_invalidation_hint_buffer_size(9);
3721 CreateUnsyncedDirectory("Y", "id_Y");
3722 mock_server_
->SetCommitClientCommand(command
);
3725 EXPECT_EQ(TimeDelta::FromSeconds(180), last_short_poll_interval_received_
);
3726 EXPECT_EQ(TimeDelta::FromSeconds(190), last_long_poll_interval_received_
);
3727 EXPECT_EQ(TimeDelta::FromSeconds(2718), last_sessions_commit_delay_
);
3728 EXPECT_EQ(TimeDelta::FromMilliseconds(1050), last_bookmarks_commit_delay_
);
3729 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_
);
3732 TEST_F(SyncerTest
, EnsureWeSendUpOldParent
) {
3733 syncable::Id folder_one_id
= ids_
.FromNumber(1);
3734 syncable::Id folder_two_id
= ids_
.FromNumber(2);
3736 mock_server_
->AddUpdateDirectory(folder_one_id
, TestIdFactory::root(),
3737 "folder_one", 1, 1, foreign_cache_guid(), "-1");
3738 mock_server_
->AddUpdateDirectory(folder_two_id
, TestIdFactory::root(),
3739 "folder_two", 1, 1, foreign_cache_guid(), "-2");
3742 // A moved entry should send an "old parent."
3743 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3744 MutableEntry
entry(&trans
, GET_BY_ID
, folder_one_id
);
3745 ASSERT_TRUE(entry
.good());
3746 entry
.PutParentId(folder_two_id
);
3747 entry
.PutIsUnsynced(true);
3748 // A new entry should send no "old parent."
3749 MutableEntry
create(
3750 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_folder");
3751 create
.PutIsUnsynced(true);
3752 create
.PutSpecifics(DefaultBookmarkSpecifics());
3755 const sync_pb::CommitMessage
& commit
= mock_server_
->last_sent_commit();
3756 ASSERT_EQ(2, commit
.entries_size());
3757 EXPECT_TRUE(commit
.entries(0).parent_id_string() == "2");
3758 EXPECT_TRUE(commit
.entries(0).old_parent_id() == "0");
3759 EXPECT_FALSE(commit
.entries(1).has_old_parent_id());
3762 TEST_F(SyncerTest
, Test64BitVersionSupport
) {
3763 int64 really_big_int
= std::numeric_limits
<int64
>::max() - 12;
3764 const string
name("ringo's dang orang ran rings around my o-ring");
3765 int64 item_metahandle
;
3767 // Try writing max int64 to the version fields of a meta entry.
3769 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3770 MutableEntry
entry(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), name
);
3771 ASSERT_TRUE(entry
.good());
3772 entry
.PutBaseVersion(really_big_int
);
3773 entry
.PutServerVersion(really_big_int
);
3774 entry
.PutId(ids_
.NewServerId());
3775 item_metahandle
= entry
.GetMetahandle();
3777 // Now read it back out and make sure the value is max int64.
3778 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
3779 Entry
entry(&rtrans
, syncable::GET_BY_HANDLE
, item_metahandle
);
3780 ASSERT_TRUE(entry
.good());
3781 EXPECT_TRUE(really_big_int
== entry
.GetBaseVersion());
3784 TEST_F(SyncerTest
, TestSimpleUndelete
) {
3785 Id id
= ids_
.MakeServer("undeletion item"), root
= TestIdFactory::root();
3786 mock_server_
->set_conflict_all_commits(true);
3787 // Let there be an entry from the server.
3788 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 1, 10,
3789 foreign_cache_guid(), "-1");
3791 // Check it out and delete it.
3793 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3794 MutableEntry
entry(&wtrans
, GET_BY_ID
, id
);
3795 ASSERT_TRUE(entry
.good());
3796 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3797 EXPECT_FALSE(entry
.GetIsUnsynced());
3798 EXPECT_FALSE(entry
.GetIsDel());
3799 // Delete it locally.
3800 entry
.PutIsDel(true);
3803 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3805 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3806 Entry
entry(&trans
, GET_BY_ID
, id
);
3807 ASSERT_TRUE(entry
.good());
3808 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3809 EXPECT_FALSE(entry
.GetIsUnsynced());
3810 EXPECT_TRUE(entry
.GetIsDel());
3811 EXPECT_FALSE(entry
.GetServerIsDel());
3814 // Update from server confirming deletion.
3815 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 11,
3816 foreign_cache_guid(), "-1");
3817 mock_server_
->SetLastUpdateDeleted();
3819 // IS_DEL AND SERVER_IS_DEL now both true.
3821 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3822 Entry
entry(&trans
, GET_BY_ID
, id
);
3823 ASSERT_TRUE(entry
.good());
3824 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3825 EXPECT_FALSE(entry
.GetIsUnsynced());
3826 EXPECT_TRUE(entry
.GetIsDel());
3827 EXPECT_TRUE(entry
.GetServerIsDel());
3829 // Undelete from server.
3830 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 12,
3831 foreign_cache_guid(), "-1");
3833 // IS_DEL and SERVER_IS_DEL now both false.
3835 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3836 Entry
entry(&trans
, GET_BY_ID
, id
);
3837 ASSERT_TRUE(entry
.good());
3838 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3839 EXPECT_FALSE(entry
.GetIsUnsynced());
3840 EXPECT_FALSE(entry
.GetIsDel());
3841 EXPECT_FALSE(entry
.GetServerIsDel());
3845 TEST_F(SyncerTest
, TestUndeleteWithMissingDeleteUpdate
) {
3846 Id id
= ids_
.MakeServer("undeletion item"), root
= TestIdFactory::root();
3847 // Let there be a entry, from the server.
3848 mock_server_
->set_conflict_all_commits(true);
3849 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 1, 10,
3850 foreign_cache_guid(), "-1");
3852 // Check it out and delete it.
3854 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3855 MutableEntry
entry(&wtrans
, GET_BY_ID
, id
);
3856 ASSERT_TRUE(entry
.good());
3857 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3858 EXPECT_FALSE(entry
.GetIsUnsynced());
3859 EXPECT_FALSE(entry
.GetIsDel());
3860 // Delete it locally.
3861 entry
.PutIsDel(true);
3864 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3866 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3867 Entry
entry(&trans
, GET_BY_ID
, id
);
3868 ASSERT_TRUE(entry
.good());
3869 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3870 EXPECT_FALSE(entry
.GetIsUnsynced());
3871 EXPECT_TRUE(entry
.GetIsDel());
3872 EXPECT_FALSE(entry
.GetServerIsDel());
3875 // Say we do not get an update from server confirming deletion. Undelete
3877 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 12,
3878 foreign_cache_guid(), "-1");
3880 // IS_DEL and SERVER_IS_DEL now both false.
3882 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3883 Entry
entry(&trans
, GET_BY_ID
, id
);
3884 ASSERT_TRUE(entry
.good());
3885 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3886 EXPECT_FALSE(entry
.GetIsUnsynced());
3887 EXPECT_FALSE(entry
.GetIsDel());
3888 EXPECT_FALSE(entry
.GetServerIsDel());
3892 TEST_F(SyncerTest
, TestUndeleteIgnoreCorrectlyUnappliedUpdate
) {
3893 Id id1
= ids_
.MakeServer("first"), id2
= ids_
.MakeServer("second");
3894 Id root
= TestIdFactory::root();
3895 // Duplicate! expect path clashing!
3896 mock_server_
->set_conflict_all_commits(true);
3897 mock_server_
->AddUpdateBookmark(id1
, root
, "foo", 1, 10,
3898 foreign_cache_guid(), "-1");
3899 mock_server_
->AddUpdateBookmark(id2
, root
, "foo", 1, 10,
3900 foreign_cache_guid(), "-2");
3902 mock_server_
->AddUpdateBookmark(id2
, root
, "foo2", 2, 20,
3903 foreign_cache_guid(), "-2");
3904 SyncShareNudge(); // Now just don't explode.
3907 TEST_F(SyncerTest
, ClientTagServerCreatedUpdatesWork
) {
3908 mock_server_
->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
3909 foreign_cache_guid(), "-1");
3910 mock_server_
->SetLastUpdateClientTag("permfolder");
3915 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3916 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
3917 ASSERT_TRUE(perm_folder
.good());
3918 EXPECT_FALSE(perm_folder
.GetIsDel());
3919 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
3920 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
3921 EXPECT_EQ(perm_folder
.GetUniqueClientTag(), "permfolder");
3922 EXPECT_EQ(perm_folder
.GetNonUniqueName(), "permitem1");
3925 mock_server_
->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
3926 foreign_cache_guid(), "-1");
3927 mock_server_
->SetLastUpdateClientTag("permfolder");
3931 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3933 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
3934 ASSERT_TRUE(perm_folder
.good());
3935 EXPECT_FALSE(perm_folder
.GetIsDel());
3936 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
3937 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
3938 EXPECT_EQ(perm_folder
.GetUniqueClientTag(), "permfolder");
3939 EXPECT_EQ(perm_folder
.GetNonUniqueName(), "permitem_renamed");
3943 TEST_F(SyncerTest
, ClientTagIllegalUpdateIgnored
) {
3944 mock_server_
->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
3945 foreign_cache_guid(), "-1");
3946 mock_server_
->SetLastUpdateClientTag("permfolder");
3951 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3952 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
3953 ASSERT_TRUE(perm_folder
.good());
3954 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
3955 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
3956 EXPECT_EQ(perm_folder
.GetUniqueClientTag(), "permfolder");
3957 EXPECT_TRUE(perm_folder
.GetNonUniqueName()== "permitem1");
3958 EXPECT_TRUE(perm_folder
.GetId().ServerKnows());
3961 mock_server_
->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
3962 foreign_cache_guid(), "-1");
3963 mock_server_
->SetLastUpdateClientTag("wrongtag");
3967 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3969 // This update is rejected because it has the same ID, but a
3970 // different tag than one that is already on the client.
3971 // The client has a ServerKnows ID, which cannot be overwritten.
3972 Entry
rejected_update(&trans
, GET_BY_CLIENT_TAG
, "wrongtag");
3973 EXPECT_FALSE(rejected_update
.good());
3975 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
3976 ASSERT_TRUE(perm_folder
.good());
3977 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
3978 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
3979 EXPECT_EQ(perm_folder
.GetNonUniqueName(), "permitem1");
3983 TEST_F(SyncerTest
, ClientTagUncommittedTagMatchesUpdate
) {
3984 int64 original_metahandle
= 0;
3987 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3989 &trans
, CREATE
, PREFERENCES
, ids_
.root(), "name");
3990 ASSERT_TRUE(pref
.good());
3991 pref
.PutUniqueClientTag("tag");
3992 pref
.PutIsUnsynced(true);
3993 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
3994 EXPECT_FALSE(pref
.GetId().ServerKnows());
3995 original_metahandle
= pref
.GetMetahandle();
3998 syncable::Id server_id
= TestIdFactory::MakeServer("id");
3999 mock_server_
->AddUpdatePref(server_id
.GetServerId(),
4000 ids_
.root().GetServerId(),
4002 mock_server_
->set_conflict_all_commits(true);
4005 // This should cause client tag reunion, preserving the metahandle.
4007 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4009 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
4010 ASSERT_TRUE(pref
.good());
4011 EXPECT_FALSE(pref
.GetIsDel());
4012 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
4013 EXPECT_TRUE(pref
.GetIsUnsynced());
4014 EXPECT_EQ(10, pref
.GetBaseVersion());
4015 // Entry should have been given the new ID while preserving the
4016 // metahandle; client should have won the conflict resolution.
4017 EXPECT_EQ(original_metahandle
, pref
.GetMetahandle());
4018 EXPECT_EQ("tag", pref
.GetUniqueClientTag());
4019 EXPECT_TRUE(pref
.GetId().ServerKnows());
4022 mock_server_
->set_conflict_all_commits(false);
4025 // The resolved entry ought to commit cleanly.
4027 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4029 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
4030 ASSERT_TRUE(pref
.good());
4031 EXPECT_FALSE(pref
.GetIsDel());
4032 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
4033 EXPECT_FALSE(pref
.GetIsUnsynced());
4034 EXPECT_TRUE(10 < pref
.GetBaseVersion());
4035 // Entry should have been given the new ID while preserving the
4036 // metahandle; client should have won the conflict resolution.
4037 EXPECT_EQ(original_metahandle
, pref
.GetMetahandle());
4038 EXPECT_EQ("tag", pref
.GetUniqueClientTag());
4039 EXPECT_TRUE(pref
.GetId().ServerKnows());
4043 TEST_F(SyncerTest
, ClientTagConflictWithDeletedLocalEntry
) {
4045 // Create a deleted local entry with a unique client tag.
4046 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4048 &trans
, CREATE
, PREFERENCES
, ids_
.root(), "name");
4049 ASSERT_TRUE(pref
.good());
4050 ASSERT_FALSE(pref
.GetId().ServerKnows());
4051 pref
.PutUniqueClientTag("tag");
4052 pref
.PutIsUnsynced(true);
4054 // Note: IS_DEL && !ServerKnows() will clear the UNSYNCED bit.
4055 // (We never attempt to commit server-unknown deleted items, so this
4056 // helps us clean up those entries).
4057 pref
.PutIsDel(true);
4060 // Prepare an update with the same unique client tag.
4061 syncable::Id server_id
= TestIdFactory::MakeServer("id");
4062 mock_server_
->AddUpdatePref(server_id
.GetServerId(),
4063 ids_
.root().GetServerId(),
4067 // The local entry will be overwritten.
4069 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4071 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
4072 ASSERT_TRUE(pref
.good());
4073 ASSERT_TRUE(pref
.GetId().ServerKnows());
4074 EXPECT_FALSE(pref
.GetIsDel());
4075 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
4076 EXPECT_FALSE(pref
.GetIsUnsynced());
4077 EXPECT_EQ(pref
.GetBaseVersion(), 10);
4078 EXPECT_EQ(pref
.GetUniqueClientTag(), "tag");
4082 TEST_F(SyncerTest
, ClientTagUpdateClashesWithLocalEntry
) {
4083 // This test is written assuming that ID comparison
4084 // will work out in a particular way.
4085 EXPECT_TRUE(ids_
.FromNumber(1) < ids_
.FromNumber(2));
4086 EXPECT_TRUE(ids_
.FromNumber(3) < ids_
.FromNumber(4));
4088 syncable::Id id1
= TestIdFactory::MakeServer("1");
4089 mock_server_
->AddUpdatePref(id1
.GetServerId(), ids_
.root().GetServerId(),
4092 syncable::Id id4
= TestIdFactory::MakeServer("4");
4093 mock_server_
->AddUpdatePref(id4
.GetServerId(), ids_
.root().GetServerId(),
4096 mock_server_
->set_conflict_all_commits(true);
4099 int64 tag1_metahandle
= syncable::kInvalidMetaHandle
;
4100 int64 tag2_metahandle
= syncable::kInvalidMetaHandle
;
4101 // This should cause client tag overwrite.
4103 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4105 Entry
tag1(&trans
, GET_BY_CLIENT_TAG
, "tag1");
4106 ASSERT_TRUE(tag1
.good());
4107 ASSERT_TRUE(tag1
.GetId().ServerKnows());
4108 ASSERT_TRUE(id1
== tag1
.GetId());
4109 EXPECT_FALSE(tag1
.GetIsDel());
4110 EXPECT_FALSE(tag1
.GetIsUnappliedUpdate());
4111 EXPECT_FALSE(tag1
.GetIsUnsynced());
4112 EXPECT_EQ(10, tag1
.GetBaseVersion());
4113 EXPECT_EQ("tag1", tag1
.GetUniqueClientTag());
4114 tag1_metahandle
= tag1
.GetMetahandle();
4116 Entry
tag2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
4117 ASSERT_TRUE(tag2
.good());
4118 ASSERT_TRUE(tag2
.GetId().ServerKnows());
4119 ASSERT_TRUE(id4
== tag2
.GetId());
4120 EXPECT_FALSE(tag2
.GetIsDel());
4121 EXPECT_FALSE(tag2
.GetIsUnappliedUpdate());
4122 EXPECT_FALSE(tag2
.GetIsUnsynced());
4123 EXPECT_EQ(11, tag2
.GetBaseVersion());
4124 EXPECT_EQ("tag2", tag2
.GetUniqueClientTag());
4125 tag2_metahandle
= tag2
.GetMetahandle();
4127 syncable::Directory::Metahandles children
;
4128 directory()->GetChildHandlesById(&trans
, trans
.root_id(), &children
);
4129 ASSERT_EQ(2U, children
.size());
4132 syncable::Id id2
= TestIdFactory::MakeServer("2");
4133 mock_server_
->AddUpdatePref(id2
.GetServerId(), ids_
.root().GetServerId(),
4135 syncable::Id id3
= TestIdFactory::MakeServer("3");
4136 mock_server_
->AddUpdatePref(id3
.GetServerId(), ids_
.root().GetServerId(),
4141 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4143 Entry
tag1(&trans
, GET_BY_CLIENT_TAG
, "tag1");
4144 ASSERT_TRUE(tag1
.good());
4145 ASSERT_TRUE(tag1
.GetId().ServerKnows());
4146 ASSERT_EQ(id1
, tag1
.GetId())
4147 << "ID 1 should be kept, since it was less than ID 2.";
4148 EXPECT_FALSE(tag1
.GetIsDel());
4149 EXPECT_FALSE(tag1
.GetIsUnappliedUpdate());
4150 EXPECT_FALSE(tag1
.GetIsUnsynced());
4151 EXPECT_EQ(10, tag1
.GetBaseVersion());
4152 EXPECT_EQ("tag1", tag1
.GetUniqueClientTag());
4153 EXPECT_EQ(tag1_metahandle
, tag1
.GetMetahandle());
4155 Entry
tag2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
4156 ASSERT_TRUE(tag2
.good());
4157 ASSERT_TRUE(tag2
.GetId().ServerKnows());
4158 ASSERT_EQ(id3
, tag2
.GetId())
4159 << "ID 3 should be kept, since it was less than ID 4.";
4160 EXPECT_FALSE(tag2
.GetIsDel());
4161 EXPECT_FALSE(tag2
.GetIsUnappliedUpdate());
4162 EXPECT_FALSE(tag2
.GetIsUnsynced());
4163 EXPECT_EQ(13, tag2
.GetBaseVersion());
4164 EXPECT_EQ("tag2", tag2
.GetUniqueClientTag());
4165 EXPECT_EQ(tag2_metahandle
, tag2
.GetMetahandle());
4167 syncable::Directory::Metahandles children
;
4168 directory()->GetChildHandlesById(&trans
, trans
.root_id(), &children
);
4169 ASSERT_EQ(2U, children
.size());
4173 TEST_F(SyncerTest
, ClientTagClashWithinBatchOfUpdates
) {
4174 // This test is written assuming that ID comparison
4175 // will work out in a particular way.
4176 EXPECT_TRUE(ids_
.FromNumber(1) < ids_
.FromNumber(4));
4177 EXPECT_TRUE(ids_
.FromNumber(201) < ids_
.FromNumber(205));
4179 // Least ID: winner.
4180 mock_server_
->AddUpdatePref(ids_
.FromNumber(1).GetServerId(),
4181 ids_
.root().GetServerId(), "tag a", 1, 10);
4182 mock_server_
->AddUpdatePref(ids_
.FromNumber(2).GetServerId(),
4183 ids_
.root().GetServerId(), "tag a", 11, 110);
4184 mock_server_
->AddUpdatePref(ids_
.FromNumber(3).GetServerId(),
4185 ids_
.root().GetServerId(), "tag a", 12, 120);
4186 mock_server_
->AddUpdatePref(ids_
.FromNumber(4).GetServerId(),
4187 ids_
.root().GetServerId(), "tag a", 13, 130);
4189 mock_server_
->AddUpdatePref(ids_
.FromNumber(105).GetServerId(),
4190 ids_
.root().GetServerId(), "tag b", 14, 140);
4191 mock_server_
->AddUpdatePref(ids_
.FromNumber(102).GetServerId(),
4192 ids_
.root().GetServerId(), "tag b", 15, 150);
4193 // Least ID: winner.
4194 mock_server_
->AddUpdatePref(ids_
.FromNumber(101).GetServerId(),
4195 ids_
.root().GetServerId(), "tag b", 16, 160);
4196 mock_server_
->AddUpdatePref(ids_
.FromNumber(104).GetServerId(),
4197 ids_
.root().GetServerId(), "tag b", 17, 170);
4199 mock_server_
->AddUpdatePref(ids_
.FromNumber(205).GetServerId(),
4200 ids_
.root().GetServerId(), "tag c", 18, 180);
4201 mock_server_
->AddUpdatePref(ids_
.FromNumber(202).GetServerId(),
4202 ids_
.root().GetServerId(), "tag c", 19, 190);
4203 mock_server_
->AddUpdatePref(ids_
.FromNumber(204).GetServerId(),
4204 ids_
.root().GetServerId(), "tag c", 20, 200);
4205 // Least ID: winner.
4206 mock_server_
->AddUpdatePref(ids_
.FromNumber(201).GetServerId(),
4207 ids_
.root().GetServerId(), "tag c", 21, 210);
4209 mock_server_
->set_conflict_all_commits(true);
4212 // This should cause client tag overwrite.
4214 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4216 Entry
tag_a(&trans
, GET_BY_CLIENT_TAG
, "tag a");
4217 ASSERT_TRUE(tag_a
.good());
4218 EXPECT_TRUE(tag_a
.GetId().ServerKnows());
4219 EXPECT_EQ(ids_
.FromNumber(1), tag_a
.GetId());
4220 EXPECT_FALSE(tag_a
.GetIsDel());
4221 EXPECT_FALSE(tag_a
.GetIsUnappliedUpdate());
4222 EXPECT_FALSE(tag_a
.GetIsUnsynced());
4223 EXPECT_EQ(1, tag_a
.GetBaseVersion());
4224 EXPECT_EQ("tag a", tag_a
.GetUniqueClientTag());
4226 Entry
tag_b(&trans
, GET_BY_CLIENT_TAG
, "tag b");
4227 ASSERT_TRUE(tag_b
.good());
4228 EXPECT_TRUE(tag_b
.GetId().ServerKnows());
4229 EXPECT_EQ(ids_
.FromNumber(101), tag_b
.GetId());
4230 EXPECT_FALSE(tag_b
.GetIsDel());
4231 EXPECT_FALSE(tag_b
.GetIsUnappliedUpdate());
4232 EXPECT_FALSE(tag_b
.GetIsUnsynced());
4233 EXPECT_EQ(16, tag_b
.GetBaseVersion());
4234 EXPECT_EQ("tag b", tag_b
.GetUniqueClientTag());
4236 Entry
tag_c(&trans
, GET_BY_CLIENT_TAG
, "tag c");
4237 ASSERT_TRUE(tag_c
.good());
4238 EXPECT_TRUE(tag_c
.GetId().ServerKnows());
4239 EXPECT_EQ(ids_
.FromNumber(201), tag_c
.GetId());
4240 EXPECT_FALSE(tag_c
.GetIsDel());
4241 EXPECT_FALSE(tag_c
.GetIsUnappliedUpdate());
4242 EXPECT_FALSE(tag_c
.GetIsUnsynced());
4243 EXPECT_EQ(21, tag_c
.GetBaseVersion());
4244 EXPECT_EQ("tag c", tag_c
.GetUniqueClientTag());
4246 syncable::Directory::Metahandles children
;
4247 directory()->GetChildHandlesById(&trans
, trans
.root_id(), &children
);
4248 ASSERT_EQ(3U, children
.size());
4252 TEST_F(SyncerTest
, UniqueServerTagUpdates
) {
4253 // As a hurdle, introduce an item whose name is the same as the tag value
4255 int64 hurdle_handle
= CreateUnsyncedDirectory("bob", "id_bob");
4257 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4258 Entry
hurdle(&trans
, GET_BY_HANDLE
, hurdle_handle
);
4259 ASSERT_TRUE(hurdle
.good());
4260 ASSERT_TRUE(!hurdle
.GetIsDel());
4261 ASSERT_TRUE(hurdle
.GetUniqueServerTag().empty());
4262 ASSERT_TRUE(hurdle
.GetNonUniqueName()== "bob");
4264 // Try to lookup by the tagname. These should fail.
4265 Entry
tag_alpha(&trans
, GET_BY_SERVER_TAG
, "alpha");
4266 EXPECT_FALSE(tag_alpha
.good());
4267 Entry
tag_bob(&trans
, GET_BY_SERVER_TAG
, "bob");
4268 EXPECT_FALSE(tag_bob
.good());
4271 // Now download some tagged items as updates.
4272 mock_server_
->AddUpdateDirectory(
4273 1, 0, "update1", 1, 10, std::string(), std::string());
4274 mock_server_
->SetLastUpdateServerTag("alpha");
4275 mock_server_
->AddUpdateDirectory(
4276 2, 0, "update2", 2, 20, std::string(), std::string());
4277 mock_server_
->SetLastUpdateServerTag("bob");
4281 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4283 // The new items should be applied as new entries, and we should be able
4284 // to look them up by their tag values.
4285 Entry
tag_alpha(&trans
, GET_BY_SERVER_TAG
, "alpha");
4286 ASSERT_TRUE(tag_alpha
.good());
4287 ASSERT_TRUE(!tag_alpha
.GetIsDel());
4288 ASSERT_TRUE(tag_alpha
.GetUniqueServerTag()== "alpha");
4289 ASSERT_TRUE(tag_alpha
.GetNonUniqueName()== "update1");
4290 Entry
tag_bob(&trans
, GET_BY_SERVER_TAG
, "bob");
4291 ASSERT_TRUE(tag_bob
.good());
4292 ASSERT_TRUE(!tag_bob
.GetIsDel());
4293 ASSERT_TRUE(tag_bob
.GetUniqueServerTag()== "bob");
4294 ASSERT_TRUE(tag_bob
.GetNonUniqueName()== "update2");
4295 // The old item should be unchanged.
4296 Entry
hurdle(&trans
, GET_BY_HANDLE
, hurdle_handle
);
4297 ASSERT_TRUE(hurdle
.good());
4298 ASSERT_TRUE(!hurdle
.GetIsDel());
4299 ASSERT_TRUE(hurdle
.GetUniqueServerTag().empty());
4300 ASSERT_TRUE(hurdle
.GetNonUniqueName()== "bob");
4304 TEST_F(SyncerTest
, GetUpdatesSetsRequestedTypes
) {
4305 // The expectations of this test happen in the MockConnectionManager's
4306 // GetUpdates handler. EnableDatatype sets the expectation value from our
4307 // set of enabled/disabled datatypes.
4308 EnableDatatype(BOOKMARKS
);
4310 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4312 EnableDatatype(AUTOFILL
);
4314 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4316 EnableDatatype(PREFERENCES
);
4318 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4320 DisableDatatype(BOOKMARKS
);
4322 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4324 DisableDatatype(AUTOFILL
);
4326 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4328 DisableDatatype(PREFERENCES
);
4329 EnableDatatype(AUTOFILL
);
4331 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4334 // A typical scenario: server and client each have one update for the other.
4335 // This is the "happy path" alternative to UpdateFailsThenDontCommit.
4336 TEST_F(SyncerTest
, UpdateThenCommit
) {
4337 syncable::Id to_receive
= ids_
.NewServerId();
4338 syncable::Id to_commit
= ids_
.NewLocalId();
4340 mock_server_
->AddUpdateDirectory(to_receive
, ids_
.root(), "x", 1, 10,
4341 foreign_cache_guid(), "-1");
4342 int64 commit_handle
= CreateUnsyncedDirectory("y", to_commit
);
4345 // The sync cycle should have included a GetUpdate, then a commit. By the
4346 // time the commit happened, we should have known for sure that there were no
4347 // hierarchy conflicts, and reported this fact to the server.
4348 ASSERT_TRUE(mock_server_
->last_request().has_commit());
4349 VerifyNoHierarchyConflictsReported(mock_server_
->last_request());
4351 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4353 Entry
received(&trans
, GET_BY_ID
, to_receive
);
4354 ASSERT_TRUE(received
.good());
4355 EXPECT_FALSE(received
.GetIsUnsynced());
4356 EXPECT_FALSE(received
.GetIsUnappliedUpdate());
4358 Entry
committed(&trans
, GET_BY_HANDLE
, commit_handle
);
4359 ASSERT_TRUE(committed
.good());
4360 EXPECT_FALSE(committed
.GetIsUnsynced());
4361 EXPECT_FALSE(committed
.GetIsUnappliedUpdate());
4364 // Same as above, but this time we fail to download updates.
4365 // We should not attempt to commit anything unless we successfully downloaded
4366 // updates, otherwise we risk causing a server-side conflict.
4367 TEST_F(SyncerTest
, UpdateFailsThenDontCommit
) {
4368 syncable::Id to_receive
= ids_
.NewServerId();
4369 syncable::Id to_commit
= ids_
.NewLocalId();
4371 mock_server_
->AddUpdateDirectory(to_receive
, ids_
.root(), "x", 1, 10,
4372 foreign_cache_guid(), "-1");
4373 int64 commit_handle
= CreateUnsyncedDirectory("y", to_commit
);
4374 mock_server_
->FailNextPostBufferToPathCall();
4377 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4379 // We did not receive this update.
4380 Entry
received(&trans
, GET_BY_ID
, to_receive
);
4381 ASSERT_FALSE(received
.good());
4383 // And our local update remains unapplied.
4384 Entry
committed(&trans
, GET_BY_HANDLE
, commit_handle
);
4385 ASSERT_TRUE(committed
.good());
4386 EXPECT_TRUE(committed
.GetIsUnsynced());
4387 EXPECT_FALSE(committed
.GetIsUnappliedUpdate());
4389 // Inform the Mock we won't be fetching all updates.
4390 mock_server_
->ClearUpdatesQueue();
4393 // Downloads two updates and applies them successfully.
4394 // This is the "happy path" alternative to ConfigureFailsDontApplyUpdates.
4395 TEST_F(SyncerTest
, ConfigureDownloadsTwoBatchesSuccess
) {
4396 syncable::Id node1
= ids_
.NewServerId();
4397 syncable::Id node2
= ids_
.NewServerId();
4399 // Construct the first GetUpdates response.
4400 mock_server_
->AddUpdateDirectory(node1
, ids_
.root(), "one", 1, 10,
4401 foreign_cache_guid(), "-2");
4402 mock_server_
->SetChangesRemaining(1);
4403 mock_server_
->NextUpdateBatch();
4405 // Construct the second GetUpdates response.
4406 mock_server_
->AddUpdateDirectory(node2
, ids_
.root(), "two", 1, 20,
4407 foreign_cache_guid(), "-2");
4409 SyncShareConfigure();
4411 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4412 // Both nodes should be downloaded and applied.
4414 Entry
n1(&trans
, GET_BY_ID
, node1
);
4415 ASSERT_TRUE(n1
.good());
4416 EXPECT_FALSE(n1
.GetIsUnappliedUpdate());
4418 Entry
n2(&trans
, GET_BY_ID
, node2
);
4419 ASSERT_TRUE(n2
.good());
4420 EXPECT_FALSE(n2
.GetIsUnappliedUpdate());
4423 // Same as the above case, but this time the second batch fails to download.
4424 TEST_F(SyncerTest
, ConfigureFailsDontApplyUpdates
) {
4425 syncable::Id node1
= ids_
.NewServerId();
4426 syncable::Id node2
= ids_
.NewServerId();
4428 // The scenario: we have two batches of updates with one update each. A
4429 // normal confgure step would download all the updates one batch at a time and
4430 // apply them. This configure will succeed in downloading the first batch
4431 // then fail when downloading the second.
4432 mock_server_
->FailNthPostBufferToPathCall(2);
4434 // Construct the first GetUpdates response.
4435 mock_server_
->AddUpdateDirectory(node1
, ids_
.root(), "one", 1, 10,
4436 foreign_cache_guid(), "-1");
4437 mock_server_
->SetChangesRemaining(1);
4438 mock_server_
->NextUpdateBatch();
4440 // Consutrct the second GetUpdates response.
4441 mock_server_
->AddUpdateDirectory(node2
, ids_
.root(), "two", 1, 20,
4442 foreign_cache_guid(), "-2");
4444 SyncShareConfigure();
4446 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4448 // The first node was downloaded, but not applied.
4449 Entry
n1(&trans
, GET_BY_ID
, node1
);
4450 ASSERT_TRUE(n1
.good());
4451 EXPECT_TRUE(n1
.GetIsUnappliedUpdate());
4453 // The second node was not downloaded.
4454 Entry
n2(&trans
, GET_BY_ID
, node2
);
4455 EXPECT_FALSE(n2
.good());
4457 // One update remains undownloaded.
4458 mock_server_
->ClearUpdatesQueue();
4461 TEST_F(SyncerTest
, GetKeySuccess
) {
4463 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4464 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4467 SyncShareConfigure();
4469 EXPECT_EQ(session_
->status_controller().last_get_key_result(), SYNCER_OK
);
4471 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4472 EXPECT_FALSE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4476 TEST_F(SyncerTest
, GetKeyEmpty
) {
4478 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4479 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4482 mock_server_
->SetKeystoreKey(std::string());
4483 SyncShareConfigure();
4485 EXPECT_NE(session_
->status_controller().last_get_key_result(), SYNCER_OK
);
4487 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4488 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4492 // Tests specifically related to bookmark (and therefore no client tags) sync
4493 // logic. Entities without client tags have custom logic in parts of the code,
4494 // and hence are not covered by e.g. the Undeletion tests below.
4495 class SyncerBookmarksTest
: public SyncerTest
{
4497 SyncerBookmarksTest() : metahandle_(syncable::kInvalidMetaHandle
) {
4501 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4502 MutableEntry
bookmark(
4503 &trans
, CREATE
, BOOKMARKS
, ids_
.root(), "clientname");
4504 ASSERT_TRUE(bookmark
.good());
4505 bookmark
.PutIsUnsynced(true);
4506 bookmark
.PutSyncing(false);
4507 bookmark
.PutSpecifics(DefaultBookmarkSpecifics());
4508 EXPECT_FALSE(bookmark
.GetIsUnappliedUpdate());
4509 EXPECT_FALSE(bookmark
.GetId().ServerKnows());
4510 metahandle_
= bookmark
.GetMetahandle();
4511 local_id_
= bookmark
.GetId();
4515 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4516 MutableEntry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4517 ASSERT_TRUE(entry
.good());
4518 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4519 // The order of setting IS_UNSYNCED vs IS_DEL matters. See
4520 // WriteNode::Tombstone().
4521 entry
.PutIsUnsynced(true);
4522 entry
.PutIsDel(true);
4523 entry
.PutSyncing(false);
4527 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4528 MutableEntry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4529 ASSERT_TRUE(entry
.good());
4530 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4531 EXPECT_TRUE(entry
.GetIsDel());
4532 entry
.PutIsDel(false);
4533 entry
.PutIsUnsynced(true);
4534 entry
.PutSyncing(false);
4537 int64
GetMetahandleOfTag() {
4538 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4539 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4540 EXPECT_TRUE(entry
.good());
4541 if (!entry
.good()) {
4542 return syncable::kInvalidMetaHandle
;
4544 return entry
.GetMetahandle();
4548 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4549 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4550 EXPECT_TRUE(entry
.good());
4551 if (!entry
.good()) {
4554 return entry
.GetId();
4557 void ExpectUnsyncedCreation() {
4558 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4559 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4561 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4562 EXPECT_FALSE(entry
.GetIsDel());
4563 EXPECT_FALSE(entry
.GetServerIsDel()); // Never been committed.
4564 EXPECT_LT(entry
.GetBaseVersion(), 0);
4565 EXPECT_TRUE(entry
.GetIsUnsynced());
4566 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4569 void ExpectUnsyncedUndeletion() {
4570 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4571 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4573 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4574 EXPECT_FALSE(entry
.GetIsDel());
4575 EXPECT_TRUE(entry
.GetServerIsDel());
4576 EXPECT_GE(entry
.GetBaseVersion(), 0);
4577 EXPECT_TRUE(entry
.GetIsUnsynced());
4578 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4579 EXPECT_TRUE(entry
.GetId().ServerKnows());
4582 void ExpectUnsyncedEdit() {
4583 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4584 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4586 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4587 EXPECT_FALSE(entry
.GetIsDel());
4588 EXPECT_FALSE(entry
.GetServerIsDel());
4589 EXPECT_GE(entry
.GetBaseVersion(), 0);
4590 EXPECT_TRUE(entry
.GetIsUnsynced());
4591 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4592 EXPECT_TRUE(entry
.GetId().ServerKnows());
4595 void ExpectUnsyncedDeletion() {
4596 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4597 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4599 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4600 EXPECT_TRUE(entry
.GetIsDel());
4601 EXPECT_FALSE(entry
.GetServerIsDel());
4602 EXPECT_TRUE(entry
.GetIsUnsynced());
4603 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4604 EXPECT_GE(entry
.GetBaseVersion(), 0);
4605 EXPECT_GE(entry
.GetServerVersion(), 0);
4608 void ExpectSyncedAndCreated() {
4609 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4610 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4612 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4613 EXPECT_FALSE(entry
.GetIsDel());
4614 EXPECT_FALSE(entry
.GetServerIsDel());
4615 EXPECT_GE(entry
.GetBaseVersion(), 0);
4616 EXPECT_EQ(entry
.GetBaseVersion(), entry
.GetServerVersion());
4617 EXPECT_FALSE(entry
.GetIsUnsynced());
4618 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4621 void ExpectSyncedAndDeleted() {
4622 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4623 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4625 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4626 EXPECT_TRUE(entry
.GetIsDel());
4627 EXPECT_TRUE(entry
.GetServerIsDel());
4628 EXPECT_FALSE(entry
.GetIsUnsynced());
4629 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4630 EXPECT_GE(entry
.GetBaseVersion(), 0);
4631 EXPECT_GE(entry
.GetServerVersion(), 0);
4635 syncable::Id local_id_
;
4639 TEST_F(SyncerBookmarksTest
, CreateSyncThenDeleteSync
) {
4641 ExpectUnsyncedCreation();
4643 ExpectSyncedAndCreated();
4645 ExpectUnsyncedDeletion();
4647 ExpectSyncedAndDeleted();
4650 TEST_F(SyncerBookmarksTest
, CreateThenDeleteBeforeSync
) {
4652 ExpectUnsyncedCreation();
4655 // Deleting before the initial commit should result in not needing to send
4656 // the delete to the server. It will still be in an unsynced state, but with
4657 // IS_UNSYNCED set to false.
4659 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4660 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4662 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4663 EXPECT_TRUE(entry
.GetIsDel());
4664 EXPECT_FALSE(entry
.GetServerIsDel());
4665 EXPECT_FALSE(entry
.GetIsUnsynced());
4666 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4667 EXPECT_EQ(entry
.GetBaseVersion(), -1);
4668 EXPECT_EQ(entry
.GetServerVersion(), 0);
4672 TEST_F(SyncerBookmarksTest
, LocalDeleteRemoteChangeConflict
) {
4674 ExpectUnsyncedCreation();
4676 ExpectSyncedAndCreated();
4678 ExpectUnsyncedDeletion();
4680 // Trigger a getupdates that modifies the bookmark. The update should be
4681 // clobbered by the local delete.
4682 mock_server_
->AddUpdateBookmark(GetServerId(), Id::GetRoot(), "dummy", 10, 10,
4683 local_cache_guid(), local_id_
.GetServerId());
4686 ExpectSyncedAndDeleted();
4689 TEST_F(SyncerBookmarksTest
, CreateThenDeleteDuringCommit
) {
4691 ExpectUnsyncedCreation();
4693 // In the middle of the initial creation commit, perform a deletion.
4694 // This should trigger performing two consecutive commit cycles, resulting
4695 // in the bookmark being both deleted and synced.
4696 mock_server_
->SetMidCommitCallback(
4697 base::Bind(&SyncerBookmarksTest::Delete
, base::Unretained(this)));
4700 ExpectSyncedAndDeleted();
4703 // Test what happens if a client deletes, then recreates, an object very
4704 // quickly. It is possible that the deletion gets sent as a commit, and
4705 // the undelete happens during the commit request. The principle here
4706 // is that with a single committing client, conflicts should never
4707 // be encountered, and a client encountering its past actions during
4708 // getupdates should never feed back to override later actions.
4710 // In cases of ordering A-F below, the outcome should be the same.
4711 // Exercised by UndeleteDuringCommit:
4712 // A. Delete - commit - undelete - commitresponse.
4713 // B. Delete - commit - undelete - commitresponse - getupdates.
4714 // Exercised by UndeleteBeforeCommit:
4715 // C. Delete - undelete - commit - commitresponse.
4716 // D. Delete - undelete - commit - commitresponse - getupdates.
4717 // Exercised by UndeleteAfterCommit:
4718 // E. Delete - commit - commitresponse - undelete - commit
4719 // - commitresponse.
4720 // F. Delete - commit - commitresponse - undelete - commit -
4721 // - commitresponse - getupdates.
4722 class SyncerUndeletionTest
: public SyncerTest
{
4724 SyncerUndeletionTest()
4725 : client_tag_("foobar"),
4726 metahandle_(syncable::kInvalidMetaHandle
) {
4730 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4731 MutableEntry
perm_folder(
4732 &trans
, CREATE
, PREFERENCES
, ids_
.root(), "clientname");
4733 ASSERT_TRUE(perm_folder
.good());
4734 perm_folder
.PutUniqueClientTag(client_tag_
);
4735 perm_folder
.PutIsUnsynced(true);
4736 perm_folder
.PutSyncing(false);
4737 perm_folder
.PutSpecifics(DefaultPreferencesSpecifics());
4738 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
4739 EXPECT_FALSE(perm_folder
.GetId().ServerKnows());
4740 metahandle_
= perm_folder
.GetMetahandle();
4741 local_id_
= perm_folder
.GetId();
4745 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4746 MutableEntry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4747 ASSERT_TRUE(entry
.good());
4748 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4749 // The order of setting IS_UNSYNCED vs IS_DEL matters. See
4750 // WriteNode::Tombstone().
4751 entry
.PutIsUnsynced(true);
4752 entry
.PutIsDel(true);
4753 entry
.PutSyncing(false);
4757 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4758 MutableEntry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4759 ASSERT_TRUE(entry
.good());
4760 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4761 EXPECT_TRUE(entry
.GetIsDel());
4762 entry
.PutIsDel(false);
4763 entry
.PutIsUnsynced(true);
4764 entry
.PutSyncing(false);
4767 int64
GetMetahandleOfTag() {
4768 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4769 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4770 EXPECT_TRUE(entry
.good());
4771 if (!entry
.good()) {
4772 return syncable::kInvalidMetaHandle
;
4774 return entry
.GetMetahandle();
4777 void ExpectUnsyncedCreation() {
4778 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4779 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4781 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4782 EXPECT_FALSE(entry
.GetIsDel());
4783 EXPECT_FALSE(entry
.GetServerIsDel()); // Never been committed.
4784 EXPECT_LT(entry
.GetBaseVersion(), 0);
4785 EXPECT_TRUE(entry
.GetIsUnsynced());
4786 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4789 void ExpectUnsyncedUndeletion() {
4790 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4791 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4793 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4794 EXPECT_FALSE(entry
.GetIsDel());
4795 EXPECT_TRUE(entry
.GetServerIsDel());
4796 EXPECT_GE(entry
.GetBaseVersion(), 0);
4797 EXPECT_TRUE(entry
.GetIsUnsynced());
4798 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4799 EXPECT_TRUE(entry
.GetId().ServerKnows());
4802 void ExpectUnsyncedEdit() {
4803 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4804 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4806 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4807 EXPECT_FALSE(entry
.GetIsDel());
4808 EXPECT_FALSE(entry
.GetServerIsDel());
4809 EXPECT_GE(entry
.GetBaseVersion(), 0);
4810 EXPECT_TRUE(entry
.GetIsUnsynced());
4811 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4812 EXPECT_TRUE(entry
.GetId().ServerKnows());
4815 void ExpectUnsyncedDeletion() {
4816 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4817 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4819 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4820 EXPECT_TRUE(entry
.GetIsDel());
4821 EXPECT_FALSE(entry
.GetServerIsDel());
4822 EXPECT_TRUE(entry
.GetIsUnsynced());
4823 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4824 EXPECT_GE(entry
.GetBaseVersion(), 0);
4825 EXPECT_GE(entry
.GetServerVersion(), 0);
4828 void ExpectSyncedAndCreated() {
4829 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4830 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4832 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4833 EXPECT_FALSE(entry
.GetIsDel());
4834 EXPECT_FALSE(entry
.GetServerIsDel());
4835 EXPECT_GE(entry
.GetBaseVersion(), 0);
4836 EXPECT_EQ(entry
.GetBaseVersion(), entry
.GetServerVersion());
4837 EXPECT_FALSE(entry
.GetIsUnsynced());
4838 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4841 void ExpectSyncedAndDeleted() {
4842 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4843 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4845 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4846 EXPECT_TRUE(entry
.GetIsDel());
4847 EXPECT_TRUE(entry
.GetServerIsDel());
4848 EXPECT_FALSE(entry
.GetIsUnsynced());
4849 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4850 EXPECT_GE(entry
.GetBaseVersion(), 0);
4851 EXPECT_GE(entry
.GetServerVersion(), 0);
4855 const std::string client_tag_
;
4856 syncable::Id local_id_
;
4860 TEST_F(SyncerUndeletionTest
, UndeleteDuringCommit
) {
4862 ExpectUnsyncedCreation();
4865 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4866 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4867 ExpectSyncedAndCreated();
4869 // Delete, begin committing the delete, then undelete while committing.
4871 ExpectUnsyncedDeletion();
4872 mock_server_
->SetMidCommitCallback(
4873 base::Bind(&SyncerUndeletionTest::Undelete
, base::Unretained(this)));
4876 // We will continue to commit until all nodes are synced, so we expect
4877 // that both the delete and following undelete were committed. We haven't
4878 // downloaded any updates, though, so the SERVER fields will be the same
4879 // as they were at the start of the cycle.
4880 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4881 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4884 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4885 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4887 // Server fields lag behind.
4888 EXPECT_FALSE(entry
.GetServerIsDel());
4890 // We have committed the second (undelete) update.
4891 EXPECT_FALSE(entry
.GetIsDel());
4892 EXPECT_FALSE(entry
.GetIsUnsynced());
4893 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4896 // Now, encounter a GetUpdates corresponding to the deletion from
4897 // the server. The undeletion should prevail again and be committed.
4898 // None of this should trigger any conflict detection -- it is perfectly
4899 // normal to recieve updates from our own commits.
4900 mock_server_
->SetMidCommitCallback(base::Closure());
4901 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
4902 update
->set_originator_cache_guid(local_cache_guid());
4903 update
->set_originator_client_item_id(local_id_
.GetServerId());
4906 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4907 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4908 ExpectSyncedAndCreated();
4911 TEST_F(SyncerUndeletionTest
, UndeleteBeforeCommit
) {
4913 ExpectUnsyncedCreation();
4916 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4917 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4918 ExpectSyncedAndCreated();
4920 // Delete and undelete, then sync to pick up the result.
4922 ExpectUnsyncedDeletion();
4924 ExpectUnsyncedEdit(); // Edit, not undelete: server thinks it exists.
4927 // The item ought to have committed successfully.
4928 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4929 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4930 ExpectSyncedAndCreated();
4932 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4933 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4934 EXPECT_EQ(2, entry
.GetBaseVersion());
4937 // Now, encounter a GetUpdates corresponding to the just-committed
4939 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
4940 update
->set_originator_cache_guid(local_cache_guid());
4941 update
->set_originator_client_item_id(local_id_
.GetServerId());
4943 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4944 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4945 ExpectSyncedAndCreated();
4948 TEST_F(SyncerUndeletionTest
, UndeleteAfterCommitButBeforeGetUpdates
) {
4950 ExpectUnsyncedCreation();
4953 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4954 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4955 ExpectSyncedAndCreated();
4957 // Delete and commit.
4959 ExpectUnsyncedDeletion();
4962 // The item ought to have committed successfully.
4963 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4964 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4965 ExpectSyncedAndDeleted();
4967 // Before the GetUpdates, the item is locally undeleted.
4969 ExpectUnsyncedUndeletion();
4971 // Now, encounter a GetUpdates corresponding to the just-committed
4972 // deletion update. The undeletion should prevail.
4973 mock_server_
->AddUpdateFromLastCommit();
4975 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4976 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4977 ExpectSyncedAndCreated();
4980 TEST_F(SyncerUndeletionTest
, UndeleteAfterDeleteAndGetUpdates
) {
4982 ExpectUnsyncedCreation();
4985 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4986 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4987 ExpectSyncedAndCreated();
4989 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
4990 update
->set_originator_cache_guid(local_cache_guid());
4991 update
->set_originator_client_item_id(local_id_
.GetServerId());
4993 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4994 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4995 ExpectSyncedAndCreated();
4997 // Delete and commit.
4999 ExpectUnsyncedDeletion();
5002 // The item ought to have committed successfully.
5003 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5004 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5005 ExpectSyncedAndDeleted();
5007 // Now, encounter a GetUpdates corresponding to the just-committed
5008 // deletion update. Should be consistent.
5009 mock_server_
->AddUpdateFromLastCommit();
5011 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5012 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5013 ExpectSyncedAndDeleted();
5015 // After the GetUpdates, the item is locally undeleted.
5017 ExpectUnsyncedUndeletion();
5019 // Now, encounter a GetUpdates corresponding to the just-committed
5020 // deletion update. The undeletion should prevail.
5022 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5023 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5024 ExpectSyncedAndCreated();
5027 // Test processing of undeletion GetUpdateses.
5028 TEST_F(SyncerUndeletionTest
, UndeleteAfterOtherClientDeletes
) {
5030 ExpectUnsyncedCreation();
5033 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5034 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5035 ExpectSyncedAndCreated();
5037 // Add a delete from the server.
5038 sync_pb::SyncEntity
* update1
= mock_server_
->AddUpdateFromLastCommit();
5039 update1
->set_originator_cache_guid(local_cache_guid());
5040 update1
->set_originator_client_item_id(local_id_
.GetServerId());
5042 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5043 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5044 ExpectSyncedAndCreated();
5046 // Some other client deletes the item.
5048 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5049 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5050 mock_server_
->AddUpdateTombstone(entry
.GetId(), PREFERENCES
);
5054 // The update ought to have applied successfully.
5055 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5056 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5057 ExpectSyncedAndDeleted();
5059 // Undelete it locally.
5061 ExpectUnsyncedUndeletion();
5063 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5064 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5065 ExpectSyncedAndCreated();
5067 // Now, encounter a GetUpdates corresponding to the just-committed
5068 // deletion update. The undeletion should prevail.
5069 sync_pb::SyncEntity
* update2
= mock_server_
->AddUpdateFromLastCommit();
5070 update2
->set_originator_cache_guid(local_cache_guid());
5071 update2
->set_originator_client_item_id(local_id_
.GetServerId());
5073 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5074 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5075 ExpectSyncedAndCreated();
5078 TEST_F(SyncerUndeletionTest
, UndeleteAfterOtherClientDeletesImmediately
) {
5080 ExpectUnsyncedCreation();
5083 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5084 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5085 ExpectSyncedAndCreated();
5087 // Some other client deletes the item before we get a chance
5088 // to GetUpdates our original request.
5090 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5091 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5092 mock_server_
->AddUpdateTombstone(entry
.GetId(), PREFERENCES
);
5096 // The update ought to have applied successfully.
5097 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5098 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5099 ExpectSyncedAndDeleted();
5101 // Undelete it locally.
5103 ExpectUnsyncedUndeletion();
5105 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5106 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5107 ExpectSyncedAndCreated();
5109 // Now, encounter a GetUpdates corresponding to the just-committed
5110 // deletion update. The undeletion should prevail.
5111 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5112 update
->set_originator_cache_guid(local_cache_guid());
5113 update
->set_originator_client_item_id(local_id_
.GetServerId());
5115 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5116 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5117 ExpectSyncedAndCreated();
5120 TEST_F(SyncerUndeletionTest
, OtherClientUndeletes
) {
5122 ExpectUnsyncedCreation();
5125 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5126 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5127 ExpectSyncedAndCreated();
5129 // Get the updates of our just-committed entry.
5130 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5131 update
->set_originator_cache_guid(local_cache_guid());
5132 update
->set_originator_client_item_id(local_id_
.GetServerId());
5134 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5135 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5136 ExpectSyncedAndCreated();
5138 // We delete the item.
5140 ExpectUnsyncedDeletion();
5143 // The update ought to have applied successfully.
5144 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5145 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5146 ExpectSyncedAndDeleted();
5148 // Now, encounter a GetUpdates corresponding to the just-committed
5150 mock_server_
->AddUpdateFromLastCommit();
5152 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5153 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5154 ExpectSyncedAndDeleted();
5156 // Some other client undeletes the item.
5158 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5159 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5160 mock_server_
->AddUpdatePref(
5161 entry
.GetId().GetServerId(),
5162 entry
.GetParentId().GetServerId(),
5163 client_tag_
, 100, 1000);
5165 mock_server_
->SetLastUpdateClientTag(client_tag_
);
5167 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5168 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5169 ExpectSyncedAndCreated();
5172 TEST_F(SyncerUndeletionTest
, OtherClientUndeletesImmediately
) {
5174 ExpectUnsyncedCreation();
5177 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5178 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5179 ExpectSyncedAndCreated();
5181 // Get the updates of our just-committed entry.
5182 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5183 update
->set_originator_cache_guid(local_cache_guid());
5185 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5186 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5187 update
->set_originator_client_item_id(local_id_
.GetServerId());
5190 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5191 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5192 ExpectSyncedAndCreated();
5194 // We delete the item.
5196 ExpectUnsyncedDeletion();
5199 // The update ought to have applied successfully.
5200 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5201 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5202 ExpectSyncedAndDeleted();
5204 // Some other client undeletes before we see the update from our
5207 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5208 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5209 mock_server_
->AddUpdatePref(
5210 entry
.GetId().GetServerId(),
5211 entry
.GetParentId().GetServerId(),
5212 client_tag_
, 100, 1000);
5214 mock_server_
->SetLastUpdateClientTag(client_tag_
);
5216 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5217 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5218 ExpectSyncedAndCreated();
5222 TEST_PARAM_BOOKMARK_ENABLE_BIT
,
5223 TEST_PARAM_AUTOFILL_ENABLE_BIT
,
5224 TEST_PARAM_BIT_COUNT
5229 public ::testing::WithParamInterface
<int> {
5231 bool ShouldFailBookmarkCommit() {
5232 return (GetParam() & (1 << TEST_PARAM_BOOKMARK_ENABLE_BIT
)) == 0;
5234 bool ShouldFailAutofillCommit() {
5235 return (GetParam() & (1 << TEST_PARAM_AUTOFILL_ENABLE_BIT
)) == 0;
5239 INSTANTIATE_TEST_CASE_P(ExtensionsActivity
,
5241 testing::Range(0, 1 << TEST_PARAM_BIT_COUNT
));
5243 TEST_P(MixedResult
, ExtensionsActivity
) {
5245 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
5247 MutableEntry
pref(&wtrans
, CREATE
, PREFERENCES
, wtrans
.root_id(), "pref");
5248 ASSERT_TRUE(pref
.good());
5249 pref
.PutIsUnsynced(true);
5251 MutableEntry
bookmark(
5252 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "bookmark");
5253 ASSERT_TRUE(bookmark
.good());
5254 bookmark
.PutIsUnsynced(true);
5256 if (ShouldFailBookmarkCommit()) {
5257 mock_server_
->SetTransientErrorId(bookmark
.GetId());
5260 if (ShouldFailAutofillCommit()) {
5261 mock_server_
->SetTransientErrorId(pref
.GetId());
5266 // Put some extenions activity records into the monitor.
5268 ExtensionsActivity::Records records
;
5269 records
["ABC"].extension_id
= "ABC";
5270 records
["ABC"].bookmark_write_count
= 2049U;
5271 records
["xyz"].extension_id
= "xyz";
5272 records
["xyz"].bookmark_write_count
= 4U;
5273 context_
->extensions_activity()->PutRecords(records
);
5278 ExtensionsActivity::Records final_monitor_records
;
5279 context_
->extensions_activity()->GetAndClearRecords(&final_monitor_records
);
5280 if (ShouldFailBookmarkCommit()) {
5281 ASSERT_EQ(2U, final_monitor_records
.size())
5282 << "Should restore records after unsuccessful bookmark commit.";
5283 EXPECT_EQ("ABC", final_monitor_records
["ABC"].extension_id
);
5284 EXPECT_EQ("xyz", final_monitor_records
["xyz"].extension_id
);
5285 EXPECT_EQ(2049U, final_monitor_records
["ABC"].bookmark_write_count
);
5286 EXPECT_EQ(4U, final_monitor_records
["xyz"].bookmark_write_count
);
5288 EXPECT_TRUE(final_monitor_records
.empty())
5289 << "Should not restore records after successful bookmark commit.";
5293 } // namespace syncer