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/test/histogram_tester.h"
24 #include "base/time/time.h"
25 #include "build/build_config.h"
26 #include "sync/engine/backoff_delay_provider.h"
27 #include "sync/engine/get_commit_ids.h"
28 #include "sync/engine/net/server_connection_manager.h"
29 #include "sync/engine/sync_scheduler_impl.h"
30 #include "sync/engine/syncer.h"
31 #include "sync/engine/syncer_proto_util.h"
32 #include "sync/internal_api/public/base/cancelation_signal.h"
33 #include "sync/internal_api/public/base/model_type.h"
34 #include "sync/internal_api/public/engine/model_safe_worker.h"
35 #include "sync/internal_api/public/sessions/commit_counters.h"
36 #include "sync/internal_api/public/sessions/status_counters.h"
37 #include "sync/internal_api/public/sessions/update_counters.h"
38 #include "sync/protocol/bookmark_specifics.pb.h"
39 #include "sync/protocol/nigori_specifics.pb.h"
40 #include "sync/protocol/preference_specifics.pb.h"
41 #include "sync/protocol/sync.pb.h"
42 #include "sync/sessions/sync_session_context.h"
43 #include "sync/syncable/mutable_entry.h"
44 #include "sync/syncable/nigori_util.h"
45 #include "sync/syncable/syncable_delete_journal.h"
46 #include "sync/syncable/syncable_read_transaction.h"
47 #include "sync/syncable/syncable_util.h"
48 #include "sync/syncable/syncable_write_transaction.h"
49 #include "sync/test/engine/fake_model_worker.h"
50 #include "sync/test/engine/mock_connection_manager.h"
51 #include "sync/test/engine/mock_nudge_handler.h"
52 #include "sync/test/engine/test_directory_setter_upper.h"
53 #include "sync/test/engine/test_id_factory.h"
54 #include "sync/test/engine/test_syncable_utils.h"
55 #include "sync/test/fake_encryptor.h"
56 #include "sync/test/fake_sync_encryption_handler.h"
57 #include "sync/test/sessions/mock_debug_info_getter.h"
58 #include "sync/util/cryptographer.h"
59 #include "sync/util/extensions_activity.h"
60 #include "sync/util/time.h"
61 #include "testing/gtest/include/gtest/gtest.h"
63 using base::TimeDelta
;
74 using syncable::BaseTransaction
;
75 using syncable::CountEntriesWithName
;
76 using syncable::Directory
;
77 using syncable::Entry
;
78 using syncable::GetFirstEntryWithName
;
79 using syncable::GetOnlyEntryWithName
;
81 using syncable::kEncryptedString
;
82 using syncable::MutableEntry
;
83 using syncable::WriteTransaction
;
85 using syncable::CREATE
;
86 using syncable::GET_BY_HANDLE
;
87 using syncable::GET_BY_ID
;
88 using syncable::GET_BY_CLIENT_TAG
;
89 using syncable::GET_BY_SERVER_TAG
;
90 using syncable::GET_TYPE_ROOT
;
91 using syncable::UNITTEST
;
93 using sessions::MockDebugInfoGetter
;
94 using sessions::StatusController
;
95 using sessions::SyncSessionContext
;
96 using sessions::SyncSession
;
100 // A helper to hold on to the counters emitted by the sync engine.
101 class TypeDebugInfoCache
: public TypeDebugInfoObserver
{
103 TypeDebugInfoCache();
104 ~TypeDebugInfoCache() override
;
106 CommitCounters
GetLatestCommitCounters(ModelType type
) const;
107 UpdateCounters
GetLatestUpdateCounters(ModelType type
) const;
108 StatusCounters
GetLatestStatusCounters(ModelType type
) const;
110 // TypeDebugInfoObserver implementation.
111 void OnCommitCountersUpdated(syncer::ModelType type
,
112 const CommitCounters
& counters
) override
;
113 void OnUpdateCountersUpdated(syncer::ModelType type
,
114 const UpdateCounters
& counters
) override
;
115 void OnStatusCountersUpdated(syncer::ModelType type
,
116 const StatusCounters
& counters
) override
;
119 std::map
<ModelType
, CommitCounters
> commit_counters_map_
;
120 std::map
<ModelType
, UpdateCounters
> update_counters_map_
;
121 std::map
<ModelType
, StatusCounters
> status_counters_map_
;
124 TypeDebugInfoCache::TypeDebugInfoCache() {}
126 TypeDebugInfoCache::~TypeDebugInfoCache() {}
128 CommitCounters
TypeDebugInfoCache::GetLatestCommitCounters(
129 ModelType type
) const {
130 std::map
<ModelType
, CommitCounters
>::const_iterator it
=
131 commit_counters_map_
.find(type
);
132 if (it
== commit_counters_map_
.end()) {
133 return CommitCounters();
139 UpdateCounters
TypeDebugInfoCache::GetLatestUpdateCounters(
140 ModelType type
) const {
141 std::map
<ModelType
, UpdateCounters
>::const_iterator it
=
142 update_counters_map_
.find(type
);
143 if (it
== update_counters_map_
.end()) {
144 return UpdateCounters();
150 StatusCounters
TypeDebugInfoCache::GetLatestStatusCounters(
151 ModelType type
) const {
152 std::map
<ModelType
, StatusCounters
>::const_iterator it
=
153 status_counters_map_
.find(type
);
154 if (it
== status_counters_map_
.end()) {
155 return StatusCounters();
161 void TypeDebugInfoCache::OnCommitCountersUpdated(
162 syncer::ModelType type
,
163 const CommitCounters
& counters
) {
164 commit_counters_map_
[type
] = counters
;
167 void TypeDebugInfoCache::OnUpdateCountersUpdated(
168 syncer::ModelType type
,
169 const UpdateCounters
& counters
) {
170 update_counters_map_
[type
] = counters
;
173 void TypeDebugInfoCache::OnStatusCountersUpdated(
174 syncer::ModelType type
,
175 const StatusCounters
& counters
) {
176 status_counters_map_
[type
] = counters
;
181 class SyncerTest
: public testing::Test
,
182 public SyncSession::Delegate
,
183 public SyncEngineEventListener
{
186 : extensions_activity_(new ExtensionsActivity
),
188 saw_syncer_event_(false),
189 last_client_invalidation_hint_buffer_size_(10) {
192 // SyncSession::Delegate implementation.
193 void OnThrottled(const base::TimeDelta
& throttle_duration
) override
{
194 FAIL() << "Should not get silenced.";
196 void OnTypesThrottled(ModelTypeSet types
,
197 const base::TimeDelta
& throttle_duration
) override
{
198 scheduler_
->OnTypesThrottled(types
, throttle_duration
);
200 bool IsCurrentlyThrottled() override
{ return false; }
201 void OnReceivedLongPollIntervalUpdate(
202 const base::TimeDelta
& new_interval
) override
{
203 last_long_poll_interval_received_
= new_interval
;
205 void OnReceivedShortPollIntervalUpdate(
206 const base::TimeDelta
& new_interval
) override
{
207 last_short_poll_interval_received_
= new_interval
;
209 void OnReceivedCustomNudgeDelays(
210 const std::map
<ModelType
, base::TimeDelta
>& delay_map
) override
{
211 std::map
<ModelType
, base::TimeDelta
>::const_iterator iter
=
212 delay_map
.find(SESSIONS
);
213 if (iter
!= delay_map
.end() && iter
->second
> base::TimeDelta())
214 last_sessions_commit_delay_
= iter
->second
;
215 iter
= delay_map
.find(BOOKMARKS
);
216 if (iter
!= delay_map
.end() && iter
->second
> base::TimeDelta())
217 last_bookmarks_commit_delay_
= iter
->second
;
219 void OnReceivedClientInvalidationHintBufferSize(int size
) override
{
220 last_client_invalidation_hint_buffer_size_
= size
;
222 void OnReceivedGuRetryDelay(const base::TimeDelta
& delay
) override
{}
223 void OnReceivedMigrationRequest(ModelTypeSet types
) override
{}
224 void OnProtocolEvent(const ProtocolEvent
& event
) override
{}
225 void OnSyncProtocolError(const SyncProtocolError
& error
) override
{}
227 void GetModelSafeRoutingInfo(ModelSafeRoutingInfo
* out
) {
228 // We're just testing the sync engine here, so we shunt everything to
229 // the SyncerThread. Datatypes which aren't enabled aren't in the map.
230 for (ModelTypeSet::Iterator it
= enabled_datatypes_
.First();
231 it
.Good(); it
.Inc()) {
232 (*out
)[it
.Get()] = GROUP_PASSIVE
;
236 void OnSyncCycleEvent(const SyncCycleEvent
& event
) override
{
237 DVLOG(1) << "HandleSyncEngineEvent in unittest " << event
.what_happened
;
238 // we only test for entry-specific events, not status changed ones.
239 switch (event
.what_happened
) {
240 case SyncCycleEvent::SYNC_CYCLE_BEGIN
: // Fall through.
241 case SyncCycleEvent::STATUS_CHANGED
:
242 case SyncCycleEvent::SYNC_CYCLE_ENDED
:
245 CHECK(false) << "Handling unknown error type in unit tests!!";
247 saw_syncer_event_
= true;
250 void OnActionableError(const SyncProtocolError
& error
) override
{}
251 void OnRetryTimeChanged(base::Time retry_time
) override
{}
252 void OnThrottledTypesChanged(ModelTypeSet throttled_types
) override
{}
253 void OnMigrationRequested(ModelTypeSet types
) override
{}
255 void ResetSession() {
256 session_
.reset(SyncSession::Build(context_
.get(), this));
259 bool SyncShareNudge() {
262 // Pretend we've seen a local change, to make the nudge_tracker look normal.
263 nudge_tracker_
.RecordLocalChange(ModelTypeSet(BOOKMARKS
));
265 return syncer_
->NormalSyncShare(context_
->GetEnabledTypes(),
266 &nudge_tracker_
, session_
.get());
269 bool SyncShareConfigure() {
271 return syncer_
->ConfigureSyncShare(
272 context_
->GetEnabledTypes(),
273 sync_pb::GetUpdatesCallerInfo::RECONFIGURATION
,
277 void SetUp() override
{
279 mock_server_
.reset(new MockConnectionManager(directory(),
280 &cancelation_signal_
));
281 debug_info_getter_
.reset(new MockDebugInfoGetter
);
282 EnableDatatype(BOOKMARKS
);
283 EnableDatatype(NIGORI
);
284 EnableDatatype(PREFERENCES
);
285 EnableDatatype(NIGORI
);
286 workers_
.push_back(scoped_refptr
<ModelSafeWorker
>(
287 new FakeModelWorker(GROUP_PASSIVE
)));
288 std::vector
<SyncEngineEventListener
*> listeners
;
289 listeners
.push_back(this);
291 ModelSafeRoutingInfo routing_info
;
292 GetModelSafeRoutingInfo(&routing_info
);
294 model_type_registry_
.reset(
295 new ModelTypeRegistry(workers_
, directory(), &mock_nudge_handler_
));
296 model_type_registry_
->RegisterDirectoryTypeDebugInfoObserver(
299 context_
.reset(new SyncSessionContext(
302 extensions_activity_
.get(),
304 debug_info_getter_
.get(),
305 model_type_registry_
.get(),
306 true, // enable keystore encryption
307 false, // force enable pre-commit GU avoidance experiment
308 "fake_invalidator_client_id"));
309 context_
->SetRoutingInfo(routing_info
);
310 syncer_
= new Syncer(&cancelation_signal_
);
311 scheduler_
.reset(new SyncSchedulerImpl(
313 BackoffDelayProvider::FromDefaults(),
315 // scheduler_ owned syncer_ now and will manage the memory of syncer_
318 syncable::ReadTransaction
trans(FROM_HERE
, directory());
319 syncable::Directory::Metahandles children
;
320 directory()->GetChildHandlesById(&trans
, trans
.root_id(), &children
);
321 ASSERT_EQ(0u, children
.size());
322 saw_syncer_event_
= false;
323 root_id_
= TestIdFactory::root();
324 parent_id_
= ids_
.MakeServer("parent id");
325 child_id_
= ids_
.MakeServer("child id");
326 directory()->set_store_birthday(mock_server_
->store_birthday());
327 mock_server_
->SetKeystoreKey("encryption_key");
330 void TearDown() override
{
331 model_type_registry_
->UnregisterDirectoryTypeDebugInfoObserver(
333 mock_server_
.reset();
335 dir_maker_
.TearDown();
338 void WriteTestDataToEntry(WriteTransaction
* trans
, MutableEntry
* entry
) {
339 EXPECT_FALSE(entry
->GetIsDir());
340 EXPECT_FALSE(entry
->GetIsDel());
341 sync_pb::EntitySpecifics specifics
;
342 specifics
.mutable_bookmark()->set_url("http://demo/");
343 specifics
.mutable_bookmark()->set_favicon("PNG");
344 entry
->PutSpecifics(specifics
);
345 entry
->PutIsUnsynced(true);
347 void VerifyTestDataInEntry(BaseTransaction
* trans
, Entry
* entry
) {
348 EXPECT_FALSE(entry
->GetIsDir());
349 EXPECT_FALSE(entry
->GetIsDel());
350 VerifyTestBookmarkDataInEntry(entry
);
352 void VerifyTestBookmarkDataInEntry(Entry
* entry
) {
353 const sync_pb::EntitySpecifics
& specifics
= entry
->GetSpecifics();
354 EXPECT_TRUE(specifics
.has_bookmark());
355 EXPECT_EQ("PNG", specifics
.bookmark().favicon());
356 EXPECT_EQ("http://demo/", specifics
.bookmark().url());
359 void VerifyHierarchyConflictsReported(
360 const sync_pb::ClientToServerMessage
& message
) {
361 // Our request should have included a warning about hierarchy conflicts.
362 const sync_pb::ClientStatus
& client_status
= message
.client_status();
363 EXPECT_TRUE(client_status
.has_hierarchy_conflict_detected());
364 EXPECT_TRUE(client_status
.hierarchy_conflict_detected());
367 void VerifyNoHierarchyConflictsReported(
368 const sync_pb::ClientToServerMessage
& message
) {
369 // Our request should have reported no hierarchy conflicts detected.
370 const sync_pb::ClientStatus
& client_status
= message
.client_status();
371 EXPECT_TRUE(client_status
.has_hierarchy_conflict_detected());
372 EXPECT_FALSE(client_status
.hierarchy_conflict_detected());
375 void VerifyHierarchyConflictsUnspecified(
376 const sync_pb::ClientToServerMessage
& message
) {
377 // Our request should have neither confirmed nor denied hierarchy conflicts.
378 const sync_pb::ClientStatus
& client_status
= message
.client_status();
379 EXPECT_FALSE(client_status
.has_hierarchy_conflict_detected());
382 sync_pb::EntitySpecifics
DefaultBookmarkSpecifics() {
383 sync_pb::EntitySpecifics result
;
384 AddDefaultFieldValue(BOOKMARKS
, &result
);
388 sync_pb::EntitySpecifics
DefaultPreferencesSpecifics() {
389 sync_pb::EntitySpecifics result
;
390 AddDefaultFieldValue(PREFERENCES
, &result
);
393 // Enumeration of alterations to entries for commit ordering tests.
395 LIST_END
= 0, // Denotes the end of the list of features from below.
396 SYNCED
, // Items are unsynced by default
402 struct CommitOrderingTest
{
403 // expected commit index.
405 // Details about the item
407 syncable::Id parent_id
;
408 EntryFeature features
[10];
410 static CommitOrderingTest
MakeLastCommitItem() {
411 CommitOrderingTest last_commit_item
;
412 last_commit_item
.commit_index
= -1;
413 last_commit_item
.id
= TestIdFactory::root();
414 return last_commit_item
;
418 void RunCommitOrderingTest(CommitOrderingTest
* test
) {
419 map
<int, syncable::Id
> expected_positions
;
420 { // Transaction scope.
421 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
422 while (!test
->id
.IsRoot()) {
423 if (test
->commit_index
>= 0) {
424 map
<int, syncable::Id
>::value_type
entry(test
->commit_index
,
426 bool double_position
= !expected_positions
.insert(entry
).second
;
427 ASSERT_FALSE(double_position
) << "Two id's expected at one position";
429 string utf8_name
= test
->id
.GetServerId();
430 string
name(utf8_name
.begin(), utf8_name
.end());
431 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, test
->parent_id
, name
);
433 entry
.PutId(test
->id
);
434 if (test
->id
.ServerKnows()) {
435 entry
.PutBaseVersion(5);
436 entry
.PutServerVersion(5);
437 entry
.PutServerParentId(test
->parent_id
);
439 entry
.PutIsDir(true);
440 entry
.PutIsUnsynced(true);
441 entry
.PutSpecifics(DefaultBookmarkSpecifics());
442 // Set the time to 30 seconds in the future to reduce the chance of
444 const base::Time
& now_plus_30s
=
445 base::Time::Now() + base::TimeDelta::FromSeconds(30);
446 const base::Time
& now_minus_2h
=
447 base::Time::Now() - base::TimeDelta::FromHours(2);
448 entry
.PutMtime(now_plus_30s
);
449 for (size_t i
= 0 ; i
< arraysize(test
->features
) ; ++i
) {
450 switch (test
->features
[i
]) {
454 entry
.PutIsUnsynced(false);
457 entry
.PutIsDel(true);
460 entry
.PutMtime(now_minus_2h
);
462 case MOVED_FROM_ROOT
:
463 entry
.PutServerParentId(trans
.root_id());
466 FAIL() << "Bad value in CommitOrderingTest list";
472 EXPECT_TRUE(SyncShareNudge());
473 ASSERT_TRUE(expected_positions
.size() ==
474 mock_server_
->committed_ids().size());
475 // If this test starts failing, be aware other sort orders could be valid.
476 for (size_t i
= 0; i
< expected_positions
.size(); ++i
) {
478 EXPECT_EQ(1u, expected_positions
.count(i
));
479 EXPECT_EQ(expected_positions
[i
], mock_server_
->committed_ids()[i
]);
483 CommitCounters
GetCommitCounters(ModelType type
) {
484 return debug_info_cache_
.GetLatestCommitCounters(type
);
487 UpdateCounters
GetUpdateCounters(ModelType type
) {
488 return debug_info_cache_
.GetLatestUpdateCounters(type
);
491 StatusCounters
GetStatusCounters(ModelType type
) {
492 return debug_info_cache_
.GetLatestStatusCounters(type
);
495 Directory
* directory() {
496 return dir_maker_
.directory();
499 const std::string
local_cache_guid() {
500 return directory()->cache_guid();
503 const std::string
foreign_cache_guid() {
504 return "kqyg7097kro6GSUod+GSg==";
507 int64
CreateUnsyncedDirectory(const string
& entry_name
,
508 const string
& idstring
) {
509 return CreateUnsyncedDirectory(entry_name
,
510 syncable::Id::CreateFromServerId(idstring
));
513 int64
CreateUnsyncedDirectory(const string
& entry_name
,
514 const syncable::Id
& id
) {
515 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
517 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), entry_name
);
518 EXPECT_TRUE(entry
.good());
519 entry
.PutIsUnsynced(true);
520 entry
.PutIsDir(true);
521 entry
.PutSpecifics(DefaultBookmarkSpecifics());
522 entry
.PutBaseVersion(id
.ServerKnows() ? 1 : 0);
524 return entry
.GetMetahandle();
527 void EnableDatatype(ModelType model_type
) {
528 enabled_datatypes_
.Put(model_type
);
530 ModelSafeRoutingInfo routing_info
;
531 GetModelSafeRoutingInfo(&routing_info
);
534 context_
->SetRoutingInfo(routing_info
);
537 mock_server_
->ExpectGetUpdatesRequestTypes(enabled_datatypes_
);
540 void DisableDatatype(ModelType model_type
) {
541 enabled_datatypes_
.Remove(model_type
);
543 ModelSafeRoutingInfo routing_info
;
544 GetModelSafeRoutingInfo(&routing_info
);
547 context_
->SetRoutingInfo(routing_info
);
550 mock_server_
->ExpectGetUpdatesRequestTypes(enabled_datatypes_
);
553 Cryptographer
* GetCryptographer(syncable::BaseTransaction
* trans
) {
554 return directory()->GetCryptographer(trans
);
557 // Configures SyncSessionContext and NudgeTracker so Syncer won't call
558 // GetUpdates prior to Commit. This method can be used to ensure a Commit is
559 // not preceeded by GetUpdates.
560 void ConfigureNoGetUpdatesRequired() {
561 context_
->set_server_enabled_pre_commit_update_avoidance(true);
562 nudge_tracker_
.OnInvalidationsEnabled();
563 nudge_tracker_
.RecordSuccessfulSyncCycle();
565 ASSERT_FALSE(context_
->ShouldFetchUpdatesBeforeCommit());
566 ASSERT_FALSE(nudge_tracker_
.IsGetUpdatesRequired());
569 base::MessageLoop message_loop_
;
571 // Some ids to aid tests. Only the root one's value is specific. The rest
572 // are named for test clarity.
573 // TODO(chron): Get rid of these inbuilt IDs. They only make it
575 syncable::Id root_id_
;
576 syncable::Id parent_id_
;
577 syncable::Id child_id_
;
581 TestDirectorySetterUpper dir_maker_
;
582 FakeEncryptor encryptor_
;
583 scoped_refptr
<ExtensionsActivity
> extensions_activity_
;
584 scoped_ptr
<MockConnectionManager
> mock_server_
;
585 CancelationSignal cancelation_signal_
;
589 scoped_ptr
<SyncSession
> session_
;
590 TypeDebugInfoCache debug_info_cache_
;
591 MockNudgeHandler mock_nudge_handler_
;
592 scoped_ptr
<ModelTypeRegistry
> model_type_registry_
;
593 scoped_ptr
<SyncSchedulerImpl
> scheduler_
;
594 scoped_ptr
<SyncSessionContext
> context_
;
595 bool saw_syncer_event_
;
596 base::TimeDelta last_short_poll_interval_received_
;
597 base::TimeDelta last_long_poll_interval_received_
;
598 base::TimeDelta last_sessions_commit_delay_
;
599 base::TimeDelta last_bookmarks_commit_delay_
;
600 int last_client_invalidation_hint_buffer_size_
;
601 std::vector
<scoped_refptr
<ModelSafeWorker
> > workers_
;
603 ModelTypeSet enabled_datatypes_
;
604 sessions::NudgeTracker nudge_tracker_
;
605 scoped_ptr
<MockDebugInfoGetter
> debug_info_getter_
;
607 DISALLOW_COPY_AND_ASSIGN(SyncerTest
);
610 TEST_F(SyncerTest
, TestCallGatherUnsyncedEntries
) {
612 Syncer::UnsyncedMetaHandles handles
;
614 syncable::ReadTransaction
trans(FROM_HERE
, directory());
615 GetUnsyncedEntries(&trans
, &handles
);
617 ASSERT_EQ(0u, handles
.size());
619 // TODO(sync): When we can dynamically connect and disconnect the mock
620 // ServerConnectionManager test disconnected GetUnsyncedEntries here. It's a
621 // regression for a very old bug.
624 TEST_F(SyncerTest
, GetCommitIdsFiltersThrottledEntries
) {
625 const ModelTypeSet
throttled_types(BOOKMARKS
);
626 sync_pb::EntitySpecifics bookmark_data
;
627 AddDefaultFieldValue(BOOKMARKS
, &bookmark_data
);
629 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
630 foreign_cache_guid(), "-1");
631 EXPECT_TRUE(SyncShareNudge());
634 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
635 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
636 ASSERT_TRUE(A
.good());
637 A
.PutIsUnsynced(true);
638 A
.PutSpecifics(bookmark_data
);
639 A
.PutNonUniqueName("bookmark");
642 // Now sync without enabling bookmarks.
643 mock_server_
->ExpectGetUpdatesRequestTypes(
644 Difference(context_
->GetEnabledTypes(), ModelTypeSet(BOOKMARKS
)));
646 syncer_
->NormalSyncShare(
647 Difference(context_
->GetEnabledTypes(), ModelTypeSet(BOOKMARKS
)),
648 &nudge_tracker_
, session_
.get());
651 // Nothing should have been committed as bookmarks is throttled.
652 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
653 Entry
entryA(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
654 ASSERT_TRUE(entryA
.good());
655 EXPECT_TRUE(entryA
.GetIsUnsynced());
658 // Sync again with bookmarks enabled.
659 mock_server_
->ExpectGetUpdatesRequestTypes(context_
->GetEnabledTypes());
660 EXPECT_TRUE(SyncShareNudge());
662 // It should have been committed.
663 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
664 Entry
entryA(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
665 ASSERT_TRUE(entryA
.good());
666 EXPECT_FALSE(entryA
.GetIsUnsynced());
670 // This test has three steps. In the first step, a BOOKMARK update is received.
671 // In the next step, syncing BOOKMARKS is disabled, so no BOOKMARK is sent or
672 // received. In the last step, a BOOKMARK update is committed.
673 TEST_F(SyncerTest
, DataUseHistogramsTest
) {
674 base::HistogramTester histogram_tester
;
675 sync_pb::EntitySpecifics bookmark_data
;
676 AddDefaultFieldValue(BOOKMARKS
, &bookmark_data
);
678 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10, foreign_cache_guid(),
680 int download_bytes_bookmark
= 0;
681 vector
<unsigned int> progress_bookmark(3, 0);
682 vector
<unsigned int> progress_all(3, 0);
683 vector
<base::Bucket
> samples
;
684 EXPECT_TRUE(SyncShareNudge());
686 histogram_tester
.ExpectTotalCount("DataUse.Sync.Upload.Count", 0);
687 histogram_tester
.ExpectTotalCount("DataUse.Sync.Upload.Bytes", 0);
688 histogram_tester
.ExpectTotalCount("DataUse.Sync.Download.Count", 1);
689 histogram_tester
.ExpectUniqueSample("DataUse.Sync.Download.Count",
691 samples
= histogram_tester
.GetAllSamples("DataUse.Sync.Download.Bytes");
692 EXPECT_EQ(samples
.size(), 1u);
693 EXPECT_EQ(samples
.at(0).min
, BOOKMARKS
);
694 EXPECT_GE(samples
.at(0).count
, 0);
695 download_bytes_bookmark
= samples
.at(0).count
;
698 histogram_tester
.GetAllSamples("DataUse.Sync.ProgressMarker.Bytes");
700 for (const base::Bucket
& bucket
: samples
) {
701 if (bucket
.min
== BOOKMARKS
)
702 progress_bookmark
.at(0) += bucket
.count
;
703 progress_all
.at(0) += bucket
.count
;
705 EXPECT_GT(progress_bookmark
.at(0), 0u);
706 EXPECT_GT(progress_all
.at(0), 0u);
708 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
709 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
710 A
.PutIsUnsynced(true);
711 A
.PutSpecifics(bookmark_data
);
712 A
.PutNonUniqueName("bookmark");
715 // Now sync without enabling bookmarks.
716 mock_server_
->ExpectGetUpdatesRequestTypes(
717 Difference(context_
->GetEnabledTypes(), ModelTypeSet(BOOKMARKS
)));
719 syncer_
->NormalSyncShare(
720 Difference(context_
->GetEnabledTypes(), ModelTypeSet(BOOKMARKS
)),
721 &nudge_tracker_
, session_
.get());
724 // Nothing should have been committed as bookmarks is throttled.
725 histogram_tester
.ExpectTotalCount("DataUse.Sync.Upload.Count", 0);
726 histogram_tester
.ExpectTotalCount("DataUse.Sync.Upload.Bytes", 0);
727 histogram_tester
.ExpectTotalCount("DataUse.Sync.Download.Count", 1);
728 histogram_tester
.ExpectUniqueSample("DataUse.Sync.Download.Count",
731 samples
= histogram_tester
.GetAllSamples("DataUse.Sync.Download.Bytes");
732 EXPECT_EQ(samples
.size(), 1u);
733 EXPECT_EQ(samples
.at(0).min
, BOOKMARKS
);
734 EXPECT_EQ(samples
.at(0).count
, download_bytes_bookmark
);
737 histogram_tester
.GetAllSamples("DataUse.Sync.ProgressMarker.Bytes");
738 for (const base::Bucket
& bucket
: samples
) {
739 if (bucket
.min
== BOOKMARKS
)
740 progress_bookmark
.at(1) += bucket
.count
;
741 progress_all
.at(1) += bucket
.count
;
743 EXPECT_EQ(progress_bookmark
.at(1), progress_bookmark
.at(0));
744 EXPECT_GT(progress_all
.at(1), progress_all
.at(0));
747 // Sync again with bookmarks enabled.
748 mock_server_
->ExpectGetUpdatesRequestTypes(context_
->GetEnabledTypes());
749 EXPECT_TRUE(SyncShareNudge());
751 // It should have been committed.
752 histogram_tester
.ExpectTotalCount("DataUse.Sync.Upload.Count", 1);
753 histogram_tester
.ExpectUniqueSample("DataUse.Sync.Upload.Count", BOOKMARKS
,
755 samples
= histogram_tester
.GetAllSamples("DataUse.Sync.Upload.Bytes");
756 EXPECT_EQ(samples
.size(), 1u);
757 EXPECT_EQ(samples
.at(0).min
, BOOKMARKS
);
758 EXPECT_GE(samples
.at(0).count
, 0);
760 samples
= histogram_tester
.GetAllSamples("DataUse.Sync.Download.Bytes");
761 EXPECT_EQ(samples
.size(), 1u);
762 EXPECT_EQ(samples
.at(0).min
, BOOKMARKS
);
763 EXPECT_EQ(samples
.at(0).count
, download_bytes_bookmark
);
765 histogram_tester
.ExpectTotalCount("DataUse.Sync.Download.Count", 1);
768 histogram_tester
.GetAllSamples("DataUse.Sync.ProgressMarker.Bytes");
769 for (const base::Bucket
& bucket
: samples
) {
770 if (bucket
.min
== BOOKMARKS
)
771 progress_bookmark
.at(2) += bucket
.count
;
772 progress_all
.at(2) += bucket
.count
;
774 EXPECT_GT(progress_bookmark
.at(2), progress_bookmark
.at(1));
775 EXPECT_GT(progress_all
.at(2), progress_all
.at(1));
779 // We use a macro so we can preserve the error location.
780 #define VERIFY_ENTRY(id, is_unapplied, is_unsynced, prev_initialized, \
781 parent_id, version, server_version, id_fac, rtrans) \
783 Entry entryA(rtrans, syncable::GET_BY_ID, id_fac.FromNumber(id)); \
784 ASSERT_TRUE(entryA.good()); \
785 /* We don't use EXPECT_EQ here because when the left side param is false,
786 gcc 4.6 warns about converting 'false' to pointer type for argument 1. */ \
787 EXPECT_TRUE(is_unsynced == entryA.GetIsUnsynced()); \
788 EXPECT_TRUE(is_unapplied == entryA.GetIsUnappliedUpdate()); \
789 EXPECT_TRUE(prev_initialized == \
790 IsRealDataType(GetModelTypeFromSpecifics( \
791 entryA.GetBaseServerSpecifics()))); \
792 EXPECT_TRUE(parent_id == -1 || \
793 entryA.GetParentId()== id_fac.FromNumber(parent_id)); \
794 EXPECT_EQ(version, entryA.GetBaseVersion()); \
795 EXPECT_EQ(server_version, entryA.GetServerVersion()); \
798 TEST_F(SyncerTest
, GetCommitIdsFiltersUnreadyEntries
) {
799 KeyParams key_params
= {"localhost", "dummy", "foobar"};
800 KeyParams other_params
= {"localhost", "dummy", "foobar2"};
801 sync_pb::EntitySpecifics bookmark
, encrypted_bookmark
;
802 bookmark
.mutable_bookmark()->set_url("url");
803 bookmark
.mutable_bookmark()->set_title("title");
804 AddDefaultFieldValue(BOOKMARKS
, &encrypted_bookmark
);
805 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
806 foreign_cache_guid(), "-1");
807 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
808 foreign_cache_guid(), "-2");
809 mock_server_
->AddUpdateDirectory(3, 0, "C", 10, 10,
810 foreign_cache_guid(), "-3");
811 mock_server_
->AddUpdateDirectory(4, 0, "D", 10, 10,
812 foreign_cache_guid(), "-4");
813 EXPECT_TRUE(SyncShareNudge());
814 // Server side change will put A in conflict.
815 mock_server_
->AddUpdateDirectory(1, 0, "A", 20, 20,
816 foreign_cache_guid(), "-1");
818 // Mark bookmarks as encrypted and set the cryptographer to have pending
820 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
821 Cryptographer
other_cryptographer(&encryptor_
);
822 other_cryptographer
.AddKey(other_params
);
823 sync_pb::EntitySpecifics specifics
;
824 sync_pb::NigoriSpecifics
* nigori
= specifics
.mutable_nigori();
825 other_cryptographer
.GetKeys(nigori
->mutable_encryption_keybag());
826 dir_maker_
.encryption_handler()->EnableEncryptEverything();
827 // Set up with an old passphrase, but have pending keys
828 GetCryptographer(&wtrans
)->AddKey(key_params
);
829 GetCryptographer(&wtrans
)->Encrypt(bookmark
,
830 encrypted_bookmark
.mutable_encrypted());
831 GetCryptographer(&wtrans
)->SetPendingKeys(nigori
->encryption_keybag());
833 // In conflict but properly encrypted.
834 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
835 ASSERT_TRUE(A
.good());
836 A
.PutIsUnsynced(true);
837 A
.PutSpecifics(encrypted_bookmark
);
838 A
.PutNonUniqueName(kEncryptedString
);
839 // Not in conflict and properly encrypted.
840 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
841 ASSERT_TRUE(B
.good());
842 B
.PutIsUnsynced(true);
843 B
.PutSpecifics(encrypted_bookmark
);
844 B
.PutNonUniqueName(kEncryptedString
);
845 // Unencrypted specifics.
846 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
847 ASSERT_TRUE(C
.good());
848 C
.PutIsUnsynced(true);
849 C
.PutNonUniqueName(kEncryptedString
);
850 // Unencrypted non_unique_name.
851 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
852 ASSERT_TRUE(D
.good());
853 D
.PutIsUnsynced(true);
854 D
.PutSpecifics(encrypted_bookmark
);
855 D
.PutNonUniqueName("not encrypted");
857 EXPECT_TRUE(SyncShareNudge());
859 // Nothing should have commited due to bookmarks being encrypted and
860 // the cryptographer having pending keys. A would have been resolved
861 // as a simple conflict, but still be unsynced until the next sync cycle.
862 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
863 VERIFY_ENTRY(1, false, true, false, 0, 20, 20, ids_
, &rtrans
);
864 VERIFY_ENTRY(2, false, true, false, 0, 10, 10, ids_
, &rtrans
);
865 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_
, &rtrans
);
866 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_
, &rtrans
);
868 // Resolve the pending keys.
869 GetCryptographer(&rtrans
)->DecryptPendingKeys(other_params
);
871 EXPECT_TRUE(SyncShareNudge());
873 // All properly encrypted and non-conflicting items should commit. "A" was
874 // conflicting, but last sync cycle resolved it as simple conflict, so on
875 // this sync cycle it committed succesfullly.
876 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
877 // Committed successfully.
878 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_
, &rtrans
);
879 // Committed successfully.
880 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_
, &rtrans
);
881 // Was not properly encrypted.
882 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_
, &rtrans
);
883 // Was not properly encrypted.
884 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_
, &rtrans
);
887 // Fix the remaining items.
888 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
889 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
890 ASSERT_TRUE(C
.good());
891 C
.PutSpecifics(encrypted_bookmark
);
892 C
.PutNonUniqueName(kEncryptedString
);
893 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
894 ASSERT_TRUE(D
.good());
895 D
.PutSpecifics(encrypted_bookmark
);
896 D
.PutNonUniqueName(kEncryptedString
);
898 EXPECT_TRUE(SyncShareNudge());
900 const StatusController
& status_controller
= session_
->status_controller();
902 EXPECT_EQ(status_controller
.model_neutral_state().commit_result
, SYNCER_OK
);
903 // None should be unsynced anymore.
904 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
905 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_
, &rtrans
);
906 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_
, &rtrans
);
907 VERIFY_ENTRY(3, false, false, false, 0, 11, 11, ids_
, &rtrans
);
908 VERIFY_ENTRY(4, false, false, false, 0, 11, 11, ids_
, &rtrans
);
912 TEST_F(SyncerTest
, GetUpdatesPartialThrottled
) {
913 sync_pb::EntitySpecifics bookmark
, pref
;
914 bookmark
.mutable_bookmark()->set_title("title");
915 pref
.mutable_preference()->set_name("name");
916 AddDefaultFieldValue(BOOKMARKS
, &bookmark
);
917 AddDefaultFieldValue(PREFERENCES
, &pref
);
919 // Normal sync, all the data types should get synced.
920 mock_server_
->AddUpdateSpecifics(1, 0, "A", 10, 10, true, 0, bookmark
,
921 foreign_cache_guid(), "-1");
922 mock_server_
->AddUpdateSpecifics(2, 1, "B", 10, 10, false, 2, bookmark
,
923 foreign_cache_guid(), "-2");
924 mock_server_
->AddUpdateSpecifics(3, 1, "C", 10, 10, false, 1, bookmark
,
925 foreign_cache_guid(), "-3");
926 mock_server_
->AddUpdateSpecifics(4, 0, "D", 10, 10, false, 0, pref
);
928 EXPECT_TRUE(SyncShareNudge());
930 // Initial state. Everything is normal.
931 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
932 VERIFY_ENTRY(1, false, false, false, 0, 10, 10, ids_
, &rtrans
);
933 VERIFY_ENTRY(2, false, false, false, 1, 10, 10, ids_
, &rtrans
);
934 VERIFY_ENTRY(3, false, false, false, 1, 10, 10, ids_
, &rtrans
);
935 VERIFY_ENTRY(4, false, false, false, 0, 10, 10, ids_
, &rtrans
);
938 // Set BOOKMARKS throttled but PREFERENCES not,
939 // then BOOKMARKS should not get synced but PREFERENCES should.
940 ModelTypeSet
throttled_types(BOOKMARKS
);
941 mock_server_
->set_partial_throttling(true);
942 mock_server_
->SetThrottledTypes(throttled_types
);
944 mock_server_
->AddUpdateSpecifics(1, 0, "E", 20, 20, true, 0, bookmark
,
945 foreign_cache_guid(), "-1");
946 mock_server_
->AddUpdateSpecifics(2, 1, "F", 20, 20, false, 2, bookmark
,
947 foreign_cache_guid(), "-2");
948 mock_server_
->AddUpdateSpecifics(3, 1, "G", 20, 20, false, 1, bookmark
,
949 foreign_cache_guid(), "-3");
950 mock_server_
->AddUpdateSpecifics(4, 0, "H", 20, 20, false, 0, pref
);
952 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
953 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
954 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
955 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
956 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
957 A
.PutIsUnsynced(true);
958 B
.PutIsUnsynced(true);
959 C
.PutIsUnsynced(true);
960 D
.PutIsUnsynced(true);
962 EXPECT_TRUE(SyncShareNudge());
964 // BOOKMARKS throttled.
965 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
966 VERIFY_ENTRY(1, false, true, false, 0, 10, 10, ids_
, &rtrans
);
967 VERIFY_ENTRY(2, false, true, false, 1, 10, 10, ids_
, &rtrans
);
968 VERIFY_ENTRY(3, false, true, false, 1, 10, 10, ids_
, &rtrans
);
969 VERIFY_ENTRY(4, false, false, false, 0, 21, 21, ids_
, &rtrans
);
972 // Unthrottled BOOKMARKS, then BOOKMARKS should get synced now.
973 mock_server_
->set_partial_throttling(false);
975 mock_server_
->AddUpdateSpecifics(1, 0, "E", 30, 30, true, 0, bookmark
,
976 foreign_cache_guid(), "-1");
977 mock_server_
->AddUpdateSpecifics(2, 1, "F", 30, 30, false, 2, bookmark
,
978 foreign_cache_guid(), "-2");
979 mock_server_
->AddUpdateSpecifics(3, 1, "G", 30, 30, false, 1, bookmark
,
980 foreign_cache_guid(), "-3");
981 mock_server_
->AddUpdateSpecifics(4, 0, "H", 30, 30, false, 0, pref
);
982 EXPECT_TRUE(SyncShareNudge());
984 // BOOKMARKS unthrottled.
985 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
986 VERIFY_ENTRY(1, false, false, false, 0, 31, 31, ids_
, &rtrans
);
987 VERIFY_ENTRY(2, false, false, false, 1, 31, 31, ids_
, &rtrans
);
988 VERIFY_ENTRY(3, false, false, false, 1, 31, 31, ids_
, &rtrans
);
989 VERIFY_ENTRY(4, false, false, false, 0, 30, 30, ids_
, &rtrans
);
993 // This test uses internal knowledge of the directory to test correctness of
994 // GetCommitIds. In almost every other test, the hierarchy is created from
995 // parent to child order, and so parents always have metahandles that are
996 // smaller than those of their children. This makes it very difficult to test
997 // some GetCommitIds edge cases, since it uses metahandle ordering as
999 TEST_F(SyncerTest
, GetCommitIds_VerifyDeletionCommitOrder
) {
1001 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1003 // Create four bookmarks folders at the root node.
1004 for (int i
= 1; i
< 5; ++i
) {
1005 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "");
1006 entry
.PutId(ids_
.FromNumber(i
));
1007 entry
.PutIsDir(true);
1008 entry
.PutBaseVersion(5);
1009 entry
.PutServerVersion(5);
1010 entry
.PutServerParentId(trans
.root_id());
1011 entry
.PutServerIsDir(true);
1012 entry
.PutIsUnsynced(true);
1013 entry
.PutSpecifics(DefaultBookmarkSpecifics());
1016 // Now iterate in reverse order make a hierarchy of them.
1017 // While we're at it, also mark them as deleted.
1018 syncable::Id parent_id
= trans
.root_id();
1019 for (int i
= 4; i
> 0; --i
) {
1020 MutableEntry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(i
));
1021 entry
.PutParentId(parent_id
);
1022 entry
.PutServerParentId(parent_id
);
1023 entry
.PutIsDel(true);
1024 parent_id
= ids_
.FromNumber(i
);
1029 // Run GetCommitIds, the function being tested.
1030 syncable::Directory::Metahandles result_handles
;
1031 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1032 GetCommitIdsForType(&trans
, BOOKMARKS
, 100, &result_handles
);
1034 // Now verify the output. We expect four results in child to parent order.
1035 ASSERT_EQ(4U, result_handles
.size());
1037 Entry
entry0(&trans
, GET_BY_HANDLE
, result_handles
[0]);
1038 EXPECT_EQ(ids_
.FromNumber(1), entry0
.GetId());
1040 Entry
entry1(&trans
, GET_BY_HANDLE
, result_handles
[1]);
1041 EXPECT_EQ(ids_
.FromNumber(2), entry1
.GetId());
1043 Entry
entry2(&trans
, GET_BY_HANDLE
, result_handles
[2]);
1044 EXPECT_EQ(ids_
.FromNumber(3), entry2
.GetId());
1046 Entry
entry3(&trans
, GET_BY_HANDLE
, result_handles
[3]);
1047 EXPECT_EQ(ids_
.FromNumber(4), entry3
.GetId());
1051 // Verify that if there are more deleted items than the maximum number of
1052 // entries, child to parent order is still preserved.
1053 TEST_F(SyncerTest
, GetCommitIds_VerifyDeletionCommitOrderMaxEntries
) {
1055 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1057 // Create a bookmark tree with one root, two second level, and three third
1058 // level bookmarks, all folders.
1059 for (int i
= 1; i
<= 6; ++i
) {
1060 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "");
1061 entry
.PutId(ids_
.FromNumber(i
));
1062 entry
.PutIsDir(true);
1063 entry
.PutBaseVersion(5);
1064 entry
.PutServerVersion(5);
1065 entry
.PutParentId(ids_
.FromNumber(i
/2));
1066 entry
.PutServerParentId(ids_
.FromNumber(i
/2));
1067 entry
.PutServerIsDir(true);
1068 entry
.PutIsUnsynced(true);
1069 entry
.PutSpecifics(DefaultBookmarkSpecifics());
1070 entry
.PutIsDel(true);
1075 // Run GetCommitIds with a limit of 2 entries to commit.
1076 syncable::Directory::Metahandles result_handles
;
1077 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1078 GetCommitIdsForType(&trans
, BOOKMARKS
, 2, &result_handles
);
1080 // Now verify the output. We expect two results in child to parent order
1081 // (descending id order).
1082 ASSERT_EQ(2U, result_handles
.size());
1084 Entry
entry0(&trans
, GET_BY_HANDLE
, result_handles
[0]);
1085 EXPECT_EQ(ids_
.FromNumber(6), entry0
.GetId());
1087 Entry
entry1(&trans
, GET_BY_HANDLE
, result_handles
[1]);
1088 EXPECT_EQ(ids_
.FromNumber(5), entry1
.GetId());
1092 TEST_F(SyncerTest
, EncryptionAwareConflicts
) {
1093 KeyParams key_params
= {"localhost", "dummy", "foobar"};
1094 Cryptographer
other_cryptographer(&encryptor_
);
1095 other_cryptographer
.AddKey(key_params
);
1096 sync_pb::EntitySpecifics bookmark
, encrypted_bookmark
, modified_bookmark
;
1097 bookmark
.mutable_bookmark()->set_title("title");
1098 other_cryptographer
.Encrypt(bookmark
,
1099 encrypted_bookmark
.mutable_encrypted());
1100 AddDefaultFieldValue(BOOKMARKS
, &encrypted_bookmark
);
1101 modified_bookmark
.mutable_bookmark()->set_title("title2");
1102 other_cryptographer
.Encrypt(modified_bookmark
,
1103 modified_bookmark
.mutable_encrypted());
1104 sync_pb::EntitySpecifics pref
, encrypted_pref
, modified_pref
;
1105 pref
.mutable_preference()->set_name("name");
1106 AddDefaultFieldValue(PREFERENCES
, &encrypted_pref
);
1107 other_cryptographer
.Encrypt(pref
,
1108 encrypted_pref
.mutable_encrypted());
1109 modified_pref
.mutable_preference()->set_name("name2");
1110 other_cryptographer
.Encrypt(modified_pref
,
1111 modified_pref
.mutable_encrypted());
1113 // Mark bookmarks and preferences as encrypted and set the cryptographer to
1114 // have pending keys.
1115 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1116 sync_pb::EntitySpecifics specifics
;
1117 sync_pb::NigoriSpecifics
* nigori
= specifics
.mutable_nigori();
1118 other_cryptographer
.GetKeys(nigori
->mutable_encryption_keybag());
1119 dir_maker_
.encryption_handler()->EnableEncryptEverything();
1120 GetCryptographer(&wtrans
)->SetPendingKeys(nigori
->encryption_keybag());
1121 EXPECT_TRUE(GetCryptographer(&wtrans
)->has_pending_keys());
1124 // We need to remember the exact position of our local items, so we can
1125 // make updates that do not modify those positions.
1126 UniquePosition pos1
;
1127 UniquePosition pos2
;
1128 UniquePosition pos3
;
1130 mock_server_
->AddUpdateSpecifics(1, 0, "A", 10, 10, true, 0, bookmark
,
1131 foreign_cache_guid(), "-1");
1132 mock_server_
->AddUpdateSpecifics(2, 1, "B", 10, 10, false, 2, bookmark
,
1133 foreign_cache_guid(), "-2");
1134 mock_server_
->AddUpdateSpecifics(3, 1, "C", 10, 10, false, 1, bookmark
,
1135 foreign_cache_guid(), "-3");
1136 mock_server_
->AddUpdateSpecifics(4, 0, "D", 10, 10, false, 0, pref
);
1137 EXPECT_TRUE(SyncShareNudge());
1139 // Initial state. Everything is normal.
1140 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1141 VERIFY_ENTRY(1, false, false, false, 0, 10, 10, ids_
, &rtrans
);
1142 VERIFY_ENTRY(2, false, false, false, 1, 10, 10, ids_
, &rtrans
);
1143 VERIFY_ENTRY(3, false, false, false, 1, 10, 10, ids_
, &rtrans
);
1144 VERIFY_ENTRY(4, false, false, false, 0, 10, 10, ids_
, &rtrans
);
1146 Entry
entry1(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
1147 ASSERT_TRUE(entry1
.GetUniquePosition().Equals(
1148 entry1
.GetServerUniquePosition()));
1149 pos1
= entry1
.GetUniquePosition();
1150 Entry
entry2(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(2));
1151 pos2
= entry2
.GetUniquePosition();
1152 Entry
entry3(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(3));
1153 pos3
= entry3
.GetUniquePosition();
1156 // Server side encryption will not be applied due to undecryptable data.
1157 // At this point, BASE_SERVER_SPECIFICS should be filled for all four items.
1158 mock_server_
->AddUpdateSpecifics(1, 0, kEncryptedString
, 20, 20, true, 0,
1160 foreign_cache_guid(), "-1");
1161 mock_server_
->AddUpdateSpecifics(2, 1, kEncryptedString
, 20, 20, false, 2,
1163 foreign_cache_guid(), "-2");
1164 mock_server_
->AddUpdateSpecifics(3, 1, kEncryptedString
, 20, 20, false, 1,
1166 foreign_cache_guid(), "-3");
1167 mock_server_
->AddUpdateSpecifics(4, 0, kEncryptedString
, 20, 20, false, 0,
1169 foreign_cache_guid(), "-4");
1170 EXPECT_TRUE(SyncShareNudge());
1172 // All should be unapplied due to being undecryptable and have a valid
1173 // BASE_SERVER_SPECIFICS.
1174 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1175 VERIFY_ENTRY(1, true, false, true, 0, 10, 20, ids_
, &rtrans
);
1176 VERIFY_ENTRY(2, true, false, true, 1, 10, 20, ids_
, &rtrans
);
1177 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_
, &rtrans
);
1178 VERIFY_ENTRY(4, true, false, true, 0, 10, 20, ids_
, &rtrans
);
1181 // Server side change that don't modify anything should not affect
1182 // BASE_SERVER_SPECIFICS (such as name changes and mtime changes).
1183 mock_server_
->AddUpdateSpecifics(1, 0, kEncryptedString
, 30, 30, true, 0,
1185 foreign_cache_guid(), "-1");
1186 mock_server_
->AddUpdateSpecifics(2, 1, kEncryptedString
, 30, 30, false, 2,
1188 foreign_cache_guid(), "-2");
1189 // Item 3 doesn't change.
1190 mock_server_
->AddUpdateSpecifics(4, 0, kEncryptedString
, 30, 30, false, 0,
1192 foreign_cache_guid(), "-4");
1193 EXPECT_TRUE(SyncShareNudge());
1195 // Items 1, 2, and 4 should have newer server versions, 3 remains the same.
1196 // All should remain unapplied due to be undecryptable.
1197 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1198 VERIFY_ENTRY(1, true, false, true, 0, 10, 30, ids_
, &rtrans
);
1199 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_
, &rtrans
);
1200 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_
, &rtrans
);
1201 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_
, &rtrans
);
1204 // Positional changes, parent changes, and specifics changes should reset
1205 // BASE_SERVER_SPECIFICS.
1206 // Became unencrypted.
1207 mock_server_
->AddUpdateSpecifics(1, 0, "A", 40, 40, true, 0, bookmark
,
1208 foreign_cache_guid(), "-1");
1209 // Reordered to after item 2.
1210 mock_server_
->AddUpdateSpecifics(3, 1, kEncryptedString
, 30, 30, false, 3,
1212 foreign_cache_guid(), "-3");
1213 EXPECT_TRUE(SyncShareNudge());
1215 // Items 2 and 4 should be the only ones with BASE_SERVER_SPECIFICS set.
1216 // Items 1 is now unencrypted, so should have applied normally.
1217 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1218 VERIFY_ENTRY(1, false, false, false, 0, 40, 40, ids_
, &rtrans
);
1219 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_
, &rtrans
);
1220 VERIFY_ENTRY(3, true, false, false, 1, 10, 30, ids_
, &rtrans
);
1221 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_
, &rtrans
);
1224 // Make local changes, which should remain unsynced for items 2, 3, 4.
1226 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1227 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
1228 ASSERT_TRUE(A
.good());
1229 A
.PutSpecifics(modified_bookmark
);
1230 A
.PutNonUniqueName(kEncryptedString
);
1231 A
.PutIsUnsynced(true);
1232 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
1233 ASSERT_TRUE(B
.good());
1234 B
.PutSpecifics(modified_bookmark
);
1235 B
.PutNonUniqueName(kEncryptedString
);
1236 B
.PutIsUnsynced(true);
1237 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
1238 ASSERT_TRUE(C
.good());
1239 C
.PutSpecifics(modified_bookmark
);
1240 C
.PutNonUniqueName(kEncryptedString
);
1241 C
.PutIsUnsynced(true);
1242 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
1243 ASSERT_TRUE(D
.good());
1244 D
.PutSpecifics(modified_pref
);
1245 D
.PutNonUniqueName(kEncryptedString
);
1246 D
.PutIsUnsynced(true);
1248 EXPECT_TRUE(SyncShareNudge());
1250 // Item 1 remains unsynced due to there being pending keys.
1251 // Items 2, 3, 4 should remain unsynced since they were not up to date.
1252 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1253 VERIFY_ENTRY(1, false, true, false, 0, 40, 40, ids_
, &rtrans
);
1254 VERIFY_ENTRY(2, true, true, true, 1, 10, 30, ids_
, &rtrans
);
1255 VERIFY_ENTRY(3, true, true, false, 1, 10, 30, ids_
, &rtrans
);
1256 VERIFY_ENTRY(4, true, true, true, 0, 10, 30, ids_
, &rtrans
);
1260 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1261 // Resolve the pending keys.
1262 GetCryptographer(&rtrans
)->DecryptPendingKeys(key_params
);
1264 // First cycle resolves conflicts, second cycle commits changes.
1265 EXPECT_TRUE(SyncShareNudge());
1266 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_server_overwrites
);
1267 EXPECT_EQ(1, GetUpdateCounters(PREFERENCES
).num_server_overwrites
);
1268 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_local_overwrites
);
1270 // We successfully commited item(s).
1271 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS
).num_commits_attempted
);
1272 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS
).num_commits_success
);
1273 EXPECT_EQ(1, GetCommitCounters(PREFERENCES
).num_commits_attempted
);
1274 EXPECT_EQ(1, GetCommitCounters(PREFERENCES
).num_commits_success
);
1276 EXPECT_TRUE(SyncShareNudge());
1278 // Everything should be resolved now. The local changes should have
1279 // overwritten the server changes for 2 and 4, while the server changes
1280 // overwrote the local for entry 3.
1282 // Expect there will be no new overwrites.
1283 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_server_overwrites
);
1284 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_local_overwrites
);
1286 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS
).num_commits_success
);
1287 EXPECT_EQ(1, GetCommitCounters(PREFERENCES
).num_commits_success
);
1289 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1290 VERIFY_ENTRY(1, false, false, false, 0, 41, 41, ids_
, &rtrans
);
1291 VERIFY_ENTRY(2, false, false, false, 1, 31, 31, ids_
, &rtrans
);
1292 VERIFY_ENTRY(3, false, false, false, 1, 30, 30, ids_
, &rtrans
);
1293 VERIFY_ENTRY(4, false, false, false, 0, 31, 31, ids_
, &rtrans
);
1298 TEST_F(SyncerTest
, TestGetUnsyncedAndSimpleCommit
) {
1300 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1301 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1302 ASSERT_TRUE(parent
.good());
1303 parent
.PutIsUnsynced(true);
1304 parent
.PutIsDir(true);
1305 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1306 parent
.PutBaseVersion(1);
1307 parent
.PutId(parent_id_
);
1308 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete");
1309 ASSERT_TRUE(child
.good());
1310 child
.PutId(child_id_
);
1311 child
.PutBaseVersion(1);
1312 WriteTestDataToEntry(&wtrans
, &child
);
1315 EXPECT_TRUE(SyncShareNudge());
1316 ASSERT_EQ(2u, mock_server_
->committed_ids().size());
1317 // If this test starts failing, be aware other sort orders could be valid.
1318 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1319 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1321 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1322 Entry
entry(&rt
, syncable::GET_BY_ID
, child_id_
);
1323 ASSERT_TRUE(entry
.good());
1324 VerifyTestDataInEntry(&rt
, &entry
);
1328 TEST_F(SyncerTest
, TestPurgeWhileUnsynced
) {
1329 // Similar to above, but throw a purge operation into the mix. Bug 49278.
1330 syncable::Id pref_node_id
= TestIdFactory::MakeServer("Tim");
1332 directory()->SetDownloadProgress(BOOKMARKS
,
1333 syncable::BuildProgress(BOOKMARKS
));
1334 directory()->SetDownloadProgress(PREFERENCES
,
1335 syncable::BuildProgress(PREFERENCES
));
1336 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1337 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1338 ASSERT_TRUE(parent
.good());
1339 parent
.PutIsUnsynced(true);
1340 parent
.PutIsDir(true);
1341 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1342 parent
.PutBaseVersion(1);
1343 parent
.PutId(parent_id_
);
1344 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete");
1345 ASSERT_TRUE(child
.good());
1346 child
.PutId(child_id_
);
1347 child
.PutBaseVersion(1);
1348 WriteTestDataToEntry(&wtrans
, &child
);
1350 MutableEntry
parent2(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Tim");
1351 ASSERT_TRUE(parent2
.good());
1352 parent2
.PutIsUnsynced(true);
1353 parent2
.PutIsDir(true);
1354 parent2
.PutSpecifics(DefaultPreferencesSpecifics());
1355 parent2
.PutBaseVersion(1);
1356 parent2
.PutId(pref_node_id
);
1359 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES
),
1363 EXPECT_TRUE(SyncShareNudge());
1364 ASSERT_EQ(2U, mock_server_
->committed_ids().size());
1365 // If this test starts failing, be aware other sort orders could be valid.
1366 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1367 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1369 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1370 Entry
entry(&rt
, syncable::GET_BY_ID
, child_id_
);
1371 ASSERT_TRUE(entry
.good());
1372 VerifyTestDataInEntry(&rt
, &entry
);
1374 directory()->SaveChanges();
1376 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1377 Entry
entry(&rt
, syncable::GET_BY_ID
, pref_node_id
);
1378 ASSERT_FALSE(entry
.good());
1382 TEST_F(SyncerTest
, TestPurgeWhileUnapplied
) {
1383 // Similar to above, but for unapplied items. Bug 49278.
1385 directory()->SetDownloadProgress(BOOKMARKS
,
1386 syncable::BuildProgress(BOOKMARKS
));
1387 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1388 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1389 ASSERT_TRUE(parent
.good());
1390 parent
.PutIsUnappliedUpdate(true);
1391 parent
.PutIsDir(true);
1392 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1393 parent
.PutBaseVersion(1);
1394 parent
.PutId(parent_id_
);
1397 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(BOOKMARKS
),
1401 EXPECT_TRUE(SyncShareNudge());
1402 directory()->SaveChanges();
1404 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1405 Entry
entry(&rt
, syncable::GET_BY_ID
, parent_id_
);
1406 ASSERT_FALSE(entry
.good());
1410 TEST_F(SyncerTest
, TestPurgeWithJournal
) {
1412 directory()->SetDownloadProgress(BOOKMARKS
,
1413 syncable::BuildProgress(BOOKMARKS
));
1414 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1415 MutableEntry
parent(&wtrans
, syncable::CREATE
, BOOKMARKS
, wtrans
.root_id(),
1417 ASSERT_TRUE(parent
.good());
1418 parent
.PutIsDir(true);
1419 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1420 parent
.PutBaseVersion(1);
1421 parent
.PutId(parent_id_
);
1422 MutableEntry
child(&wtrans
, syncable::CREATE
, BOOKMARKS
, parent_id_
,
1424 ASSERT_TRUE(child
.good());
1425 child
.PutId(child_id_
);
1426 child
.PutBaseVersion(1);
1427 WriteTestDataToEntry(&wtrans
, &child
);
1429 MutableEntry
parent2(&wtrans
, syncable::CREATE
, PREFERENCES
,
1430 wtrans
.root_id(), "Tim");
1431 ASSERT_TRUE(parent2
.good());
1432 parent2
.PutIsDir(true);
1433 parent2
.PutSpecifics(DefaultPreferencesSpecifics());
1434 parent2
.PutBaseVersion(1);
1435 parent2
.PutId(TestIdFactory::MakeServer("Tim"));
1438 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES
, BOOKMARKS
),
1439 ModelTypeSet(BOOKMARKS
),
1442 // Verify bookmark nodes are saved in delete journal but not preference
1444 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1445 syncable::DeleteJournal
* delete_journal
= directory()->delete_journal();
1446 EXPECT_EQ(2u, delete_journal
->GetDeleteJournalSize(&rt
));
1447 syncable::EntryKernelSet journal_entries
;
1448 directory()->delete_journal()->GetDeleteJournals(&rt
, BOOKMARKS
,
1450 EXPECT_EQ(parent_id_
, (*journal_entries
.begin())->ref(syncable::ID
));
1451 EXPECT_EQ(child_id_
, (*journal_entries
.rbegin())->ref(syncable::ID
));
1455 TEST_F(SyncerTest
, ResetVersions
) {
1456 // Download some pref items.
1457 mock_server_
->AddUpdatePref("id1", "", "tag1", 20, 20);
1458 mock_server_
->AddUpdatePref("id2", "", "tag2", 30, 30);
1459 mock_server_
->AddUpdatePref("id3", "", "tag3", 40, 40);
1460 EXPECT_TRUE(SyncShareNudge());
1463 // Modify one of the preferences locally, mark another one as unapplied,
1464 // and create another unsynced preference.
1465 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1466 MutableEntry
entry(&wtrans
, GET_BY_CLIENT_TAG
, "tag1");
1467 entry
.PutIsUnsynced(true);
1469 MutableEntry
entry2(&wtrans
, GET_BY_CLIENT_TAG
, "tag2");
1470 entry2
.PutIsUnappliedUpdate(true);
1472 MutableEntry
entry4(&wtrans
, CREATE
, PREFERENCES
, "name");
1473 entry4
.PutUniqueClientTag("tag4");
1474 entry4
.PutIsUnsynced(true);
1478 // Reset the versions.
1479 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1480 ASSERT_TRUE(directory()->ResetVersionsForType(&wtrans
, PREFERENCES
));
1484 // Verify the synced items are all with version 1 now, with
1485 // unsynced/unapplied state preserved.
1486 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1487 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, "tag1");
1488 EXPECT_EQ(1, entry
.GetBaseVersion());
1489 EXPECT_EQ(1, entry
.GetServerVersion());
1490 EXPECT_TRUE(entry
.GetIsUnsynced());
1491 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
1492 Entry
entry2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
1493 EXPECT_EQ(1, entry2
.GetBaseVersion());
1494 EXPECT_EQ(1, entry2
.GetServerVersion());
1495 EXPECT_FALSE(entry2
.GetIsUnsynced());
1496 EXPECT_TRUE(entry2
.GetIsUnappliedUpdate());
1497 Entry
entry3(&trans
, GET_BY_CLIENT_TAG
, "tag3");
1498 EXPECT_EQ(1, entry3
.GetBaseVersion());
1499 EXPECT_EQ(1, entry3
.GetServerVersion());
1500 EXPECT_FALSE(entry3
.GetIsUnsynced());
1501 EXPECT_FALSE(entry3
.GetIsUnappliedUpdate());
1503 // Entry 4 (the locally created one) should remain the same.
1504 Entry
entry4(&trans
, GET_BY_CLIENT_TAG
, "tag4");
1505 EXPECT_EQ(-1, entry4
.GetBaseVersion());
1506 EXPECT_EQ(0, entry4
.GetServerVersion());
1507 EXPECT_TRUE(entry4
.GetIsUnsynced());
1508 EXPECT_FALSE(entry4
.GetIsUnappliedUpdate());
1512 TEST_F(SyncerTest
, TestCommitListOrderingTwoItemsTall
) {
1513 CommitOrderingTest items
[] = {
1514 {1, ids_
.FromNumber(-1001), ids_
.FromNumber(-1000)},
1515 {0, ids_
.FromNumber(-1000), ids_
.FromNumber(0)},
1516 CommitOrderingTest::MakeLastCommitItem(),
1518 RunCommitOrderingTest(items
);
1521 TEST_F(SyncerTest
, TestCommitListOrderingThreeItemsTall
) {
1522 CommitOrderingTest items
[] = {
1523 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1524 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1525 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1526 CommitOrderingTest::MakeLastCommitItem(),
1528 RunCommitOrderingTest(items
);
1531 TEST_F(SyncerTest
, TestCommitListOrderingFourItemsTall
) {
1532 CommitOrderingTest items
[] = {
1533 {3, ids_
.FromNumber(-2003), ids_
.FromNumber(-2002)},
1534 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1535 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1536 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1537 CommitOrderingTest::MakeLastCommitItem(),
1539 RunCommitOrderingTest(items
);
1542 TEST_F(SyncerTest
, TestCommitListOrderingThreeItemsTallLimitedSize
) {
1543 context_
->set_max_commit_batch_size(2);
1544 CommitOrderingTest items
[] = {
1545 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1546 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1547 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1548 CommitOrderingTest::MakeLastCommitItem(),
1550 RunCommitOrderingTest(items
);
1553 TEST_F(SyncerTest
, TestCommitListOrderingSingleDeletedItem
) {
1554 CommitOrderingTest items
[] = {
1555 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1556 CommitOrderingTest::MakeLastCommitItem(),
1558 RunCommitOrderingTest(items
);
1561 TEST_F(SyncerTest
, TestCommitListOrderingSingleUncommittedDeletedItem
) {
1562 CommitOrderingTest items
[] = {
1563 {-1, ids_
.FromNumber(-1000), ids_
.FromNumber(0), {DELETED
}},
1564 CommitOrderingTest::MakeLastCommitItem(),
1566 RunCommitOrderingTest(items
);
1569 TEST_F(SyncerTest
, TestCommitListOrderingSingleDeletedItemWithUnroll
) {
1570 CommitOrderingTest items
[] = {
1571 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1572 CommitOrderingTest::MakeLastCommitItem(),
1574 RunCommitOrderingTest(items
);
1578 TestCommitListOrderingSingleLongDeletedItemWithUnroll
) {
1579 CommitOrderingTest items
[] = {
1580 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1581 CommitOrderingTest::MakeLastCommitItem(),
1583 RunCommitOrderingTest(items
);
1586 TEST_F(SyncerTest
, TestCommitListOrderingTwoLongDeletedItemWithUnroll
) {
1587 CommitOrderingTest items
[] = {
1588 {1, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1589 {0, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
, OLD_MTIME
}},
1590 CommitOrderingTest::MakeLastCommitItem(),
1592 RunCommitOrderingTest(items
);
1595 TEST_F(SyncerTest
, TestCommitListOrdering3LongDeletedItemsWithSizeLimit
) {
1596 context_
->set_max_commit_batch_size(2);
1597 CommitOrderingTest items
[] = {
1598 {2, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1599 {1, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
, OLD_MTIME
}},
1600 {0, ids_
.FromNumber(1002), ids_
.FromNumber(1001), {DELETED
, OLD_MTIME
}},
1601 CommitOrderingTest::MakeLastCommitItem(),
1603 RunCommitOrderingTest(items
);
1606 TEST_F(SyncerTest
, TestCommitListOrderingTwoDeletedItemsWithUnroll
) {
1607 CommitOrderingTest items
[] = {
1608 {1, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1609 {0, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
}},
1610 CommitOrderingTest::MakeLastCommitItem(),
1612 RunCommitOrderingTest(items
);
1615 TEST_F(SyncerTest
, TestCommitListOrderingComplexDeletionScenario
) {
1616 CommitOrderingTest items
[] = {
1617 {2, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1618 {-1, ids_
.FromNumber(1001), ids_
.FromNumber(0), {SYNCED
}},
1619 {1, ids_
.FromNumber(1002), ids_
.FromNumber(1001), {DELETED
, OLD_MTIME
}},
1620 {-1, ids_
.FromNumber(1003), ids_
.FromNumber(1001), {SYNCED
}},
1621 {0, ids_
.FromNumber(1004), ids_
.FromNumber(1003), {DELETED
}},
1622 CommitOrderingTest::MakeLastCommitItem(),
1624 RunCommitOrderingTest(items
);
1628 TestCommitListOrderingComplexDeletionScenarioWith2RecentDeletes
) {
1629 CommitOrderingTest items
[] = {
1630 {3, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1631 {-1, ids_
.FromNumber(1001), ids_
.FromNumber(0), {SYNCED
}},
1632 {2, ids_
.FromNumber(1002), ids_
.FromNumber(1001), {DELETED
, OLD_MTIME
}},
1633 {-1, ids_
.FromNumber(1003), ids_
.FromNumber(1001), {SYNCED
}},
1634 {1, ids_
.FromNumber(1004), ids_
.FromNumber(1003), {DELETED
}},
1635 {0, ids_
.FromNumber(1005), ids_
.FromNumber(1003), {DELETED
}},
1636 CommitOrderingTest::MakeLastCommitItem(),
1638 RunCommitOrderingTest(items
);
1641 TEST_F(SyncerTest
, TestCommitListOrderingDeleteMovedItems
) {
1642 CommitOrderingTest items
[] = {
1643 {1, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1644 {0, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
, OLD_MTIME
,
1646 CommitOrderingTest::MakeLastCommitItem(),
1648 RunCommitOrderingTest(items
);
1651 TEST_F(SyncerTest
, TestCommitListOrderingWithNesting
) {
1652 const base::Time
& now_minus_2h
=
1653 base::Time::Now() - base::TimeDelta::FromHours(2);
1655 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1657 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bob");
1658 ASSERT_TRUE(parent
.good());
1659 parent
.PutIsUnsynced(true);
1660 parent
.PutIsDir(true);
1661 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1662 parent
.PutId(ids_
.FromNumber(100));
1663 parent
.PutBaseVersion(1);
1665 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(100), "Bob");
1666 ASSERT_TRUE(child
.good());
1667 child
.PutIsUnsynced(true);
1668 child
.PutIsDir(true);
1669 child
.PutSpecifics(DefaultBookmarkSpecifics());
1670 child
.PutId(ids_
.FromNumber(101));
1671 child
.PutBaseVersion(1);
1672 MutableEntry
grandchild(
1673 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(101), "Bob");
1674 ASSERT_TRUE(grandchild
.good());
1675 grandchild
.PutId(ids_
.FromNumber(102));
1676 grandchild
.PutIsUnsynced(true);
1677 grandchild
.PutSpecifics(DefaultBookmarkSpecifics());
1678 grandchild
.PutBaseVersion(1);
1681 // Create three deleted items which deletions we expect to be sent to the
1683 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1684 ASSERT_TRUE(parent
.good());
1685 parent
.PutId(ids_
.FromNumber(103));
1686 parent
.PutIsUnsynced(true);
1687 parent
.PutIsDir(true);
1688 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1689 parent
.PutIsDel(true);
1690 parent
.PutBaseVersion(1);
1691 parent
.PutMtime(now_minus_2h
);
1693 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(103), "Pete");
1694 ASSERT_TRUE(child
.good());
1695 child
.PutId(ids_
.FromNumber(104));
1696 child
.PutIsUnsynced(true);
1697 child
.PutIsDir(true);
1698 child
.PutSpecifics(DefaultBookmarkSpecifics());
1699 child
.PutIsDel(true);
1700 child
.PutBaseVersion(1);
1701 child
.PutMtime(now_minus_2h
);
1702 MutableEntry
grandchild(
1703 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(104), "Pete");
1704 ASSERT_TRUE(grandchild
.good());
1705 grandchild
.PutId(ids_
.FromNumber(105));
1706 grandchild
.PutIsUnsynced(true);
1707 grandchild
.PutIsDel(true);
1708 grandchild
.PutIsDir(false);
1709 grandchild
.PutSpecifics(DefaultBookmarkSpecifics());
1710 grandchild
.PutBaseVersion(1);
1711 grandchild
.PutMtime(now_minus_2h
);
1715 EXPECT_TRUE(SyncShareNudge());
1716 ASSERT_EQ(6u, mock_server_
->committed_ids().size());
1717 // This test will NOT unroll deletes because SERVER_PARENT_ID is not set.
1718 // It will treat these like moves.
1719 vector
<syncable::Id
> commit_ids(mock_server_
->committed_ids());
1720 EXPECT_TRUE(ids_
.FromNumber(100) == commit_ids
[0]);
1721 EXPECT_TRUE(ids_
.FromNumber(101) == commit_ids
[1]);
1722 EXPECT_TRUE(ids_
.FromNumber(102) == commit_ids
[2]);
1723 // We don't guarantee the delete orders in this test, only that they occur
1725 std::sort(commit_ids
.begin() + 3, commit_ids
.end());
1726 EXPECT_TRUE(ids_
.FromNumber(103) == commit_ids
[3]);
1727 EXPECT_TRUE(ids_
.FromNumber(104) == commit_ids
[4]);
1728 EXPECT_TRUE(ids_
.FromNumber(105) == commit_ids
[5]);
1731 TEST_F(SyncerTest
, TestCommitListOrderingWithNewItems
) {
1732 syncable::Id parent1_id
= ids_
.MakeServer("p1");
1733 syncable::Id parent2_id
= ids_
.MakeServer("p2");
1736 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1737 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "1");
1738 ASSERT_TRUE(parent
.good());
1739 parent
.PutIsUnsynced(true);
1740 parent
.PutIsDir(true);
1741 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1742 parent
.PutId(parent1_id
);
1743 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "2");
1744 ASSERT_TRUE(child
.good());
1745 child
.PutIsUnsynced(true);
1746 child
.PutIsDir(true);
1747 child
.PutSpecifics(DefaultBookmarkSpecifics());
1748 child
.PutId(parent2_id
);
1749 parent
.PutBaseVersion(1);
1750 child
.PutBaseVersion(1);
1753 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1754 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, parent1_id
, "A");
1755 ASSERT_TRUE(parent
.good());
1756 parent
.PutIsUnsynced(true);
1757 parent
.PutIsDir(true);
1758 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1759 parent
.PutId(ids_
.FromNumber(102));
1760 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent1_id
, "B");
1761 ASSERT_TRUE(child
.good());
1762 child
.PutIsUnsynced(true);
1763 child
.PutIsDir(true);
1764 child
.PutSpecifics(DefaultBookmarkSpecifics());
1765 child
.PutId(ids_
.FromNumber(-103));
1766 parent
.PutBaseVersion(1);
1769 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1770 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, parent2_id
, "A");
1771 ASSERT_TRUE(parent
.good());
1772 parent
.PutIsUnsynced(true);
1773 parent
.PutIsDir(true);
1774 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1775 parent
.PutId(ids_
.FromNumber(-104));
1776 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent2_id
, "B");
1777 ASSERT_TRUE(child
.good());
1778 child
.PutIsUnsynced(true);
1779 child
.PutIsDir(true);
1780 child
.PutSpecifics(DefaultBookmarkSpecifics());
1781 child
.PutId(ids_
.FromNumber(105));
1782 child
.PutBaseVersion(1);
1785 EXPECT_TRUE(SyncShareNudge());
1786 ASSERT_EQ(6u, mock_server_
->committed_ids().size());
1788 // This strange iteration and std::count() usage is to allow the order to
1789 // vary. All we really care about is that parent1_id and parent2_id are the
1790 // first two IDs, and that the children make up the next four. Other than
1791 // that, ordering doesn't matter.
1793 vector
<syncable::Id
>::const_iterator i
=
1794 mock_server_
->committed_ids().begin();
1795 vector
<syncable::Id
>::const_iterator parents_begin
= i
;
1798 vector
<syncable::Id
>::const_iterator parents_end
= i
;
1799 vector
<syncable::Id
>::const_iterator children_begin
= i
;
1800 vector
<syncable::Id
>::const_iterator children_end
=
1801 mock_server_
->committed_ids().end();
1803 EXPECT_EQ(1, count(parents_begin
, parents_end
, parent1_id
));
1804 EXPECT_EQ(1, count(parents_begin
, parents_end
, parent2_id
));
1806 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(-103)));
1807 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(102)));
1808 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(105)));
1809 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(-104)));
1812 TEST_F(SyncerTest
, TestCommitListOrderingCounterexample
) {
1813 syncable::Id child2_id
= ids_
.NewServerId();
1816 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1817 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "P");
1818 ASSERT_TRUE(parent
.good());
1819 parent
.PutIsUnsynced(true);
1820 parent
.PutIsDir(true);
1821 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1822 parent
.PutId(parent_id_
);
1823 MutableEntry
child1(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "1");
1824 ASSERT_TRUE(child1
.good());
1825 child1
.PutIsUnsynced(true);
1826 child1
.PutId(child_id_
);
1827 child1
.PutSpecifics(DefaultBookmarkSpecifics());
1828 MutableEntry
child2(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "2");
1829 ASSERT_TRUE(child2
.good());
1830 child2
.PutIsUnsynced(true);
1831 child2
.PutSpecifics(DefaultBookmarkSpecifics());
1832 child2
.PutId(child2_id
);
1834 parent
.PutBaseVersion(1);
1835 child1
.PutBaseVersion(1);
1836 child2
.PutBaseVersion(1);
1839 EXPECT_TRUE(SyncShareNudge());
1840 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1841 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1842 // There are two possible valid orderings.
1843 if (child2_id
== mock_server_
->committed_ids()[1]) {
1844 EXPECT_TRUE(child2_id
== mock_server_
->committed_ids()[1]);
1845 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[2]);
1847 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1848 EXPECT_TRUE(child2_id
== mock_server_
->committed_ids()[2]);
1852 TEST_F(SyncerTest
, TestCommitListOrderingAndNewParent
) {
1853 string parent1_name
= "1";
1854 string parent2_name
= "A";
1855 string child_name
= "B";
1858 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1859 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(),
1861 ASSERT_TRUE(parent
.good());
1862 parent
.PutIsUnsynced(true);
1863 parent
.PutIsDir(true);
1864 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1865 parent
.PutId(parent_id_
);
1866 parent
.PutBaseVersion(1);
1869 syncable::Id parent2_id
= ids_
.NewLocalId();
1870 syncable::Id child_id
= ids_
.NewServerId();
1872 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1873 MutableEntry
parent2(
1874 &wtrans
, CREATE
, BOOKMARKS
, parent_id_
, parent2_name
);
1875 ASSERT_TRUE(parent2
.good());
1876 parent2
.PutIsUnsynced(true);
1877 parent2
.PutIsDir(true);
1878 parent2
.PutSpecifics(DefaultBookmarkSpecifics());
1879 parent2
.PutId(parent2_id
);
1882 &wtrans
, CREATE
, BOOKMARKS
, parent2_id
, child_name
);
1883 ASSERT_TRUE(child
.good());
1884 child
.PutIsUnsynced(true);
1885 child
.PutIsDir(true);
1886 child
.PutSpecifics(DefaultBookmarkSpecifics());
1887 child
.PutId(child_id
);
1888 child
.PutBaseVersion(1);
1891 EXPECT_TRUE(SyncShareNudge());
1892 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1893 // If this test starts failing, be aware other sort orders could be valid.
1894 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1895 EXPECT_TRUE(parent2_id
== mock_server_
->committed_ids()[1]);
1896 EXPECT_TRUE(child_id
== mock_server_
->committed_ids()[2]);
1898 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1899 // Check that things committed correctly.
1900 Entry
entry_1(&rtrans
, syncable::GET_BY_ID
, parent_id_
);
1901 EXPECT_EQ(entry_1
.GetNonUniqueName(), parent1_name
);
1902 // Check that parent2 is a subfolder of parent1.
1903 EXPECT_EQ(1, CountEntriesWithName(&rtrans
,
1907 // Parent2 was a local ID and thus should have changed on commit!
1908 Entry
pre_commit_entry_parent2(&rtrans
, syncable::GET_BY_ID
, parent2_id
);
1909 ASSERT_FALSE(pre_commit_entry_parent2
.good());
1911 // Look up the new ID.
1912 Id parent2_committed_id
=
1913 GetOnlyEntryWithName(&rtrans
, parent_id_
, parent2_name
);
1914 EXPECT_TRUE(parent2_committed_id
.ServerKnows());
1916 Entry
child(&rtrans
, syncable::GET_BY_ID
, child_id
);
1917 EXPECT_EQ(parent2_committed_id
, child
.GetParentId());
1921 TEST_F(SyncerTest
, TestCommitListOrderingAndNewParentAndChild
) {
1922 string parent_name
= "1";
1923 string parent2_name
= "A";
1924 string child_name
= "B";
1927 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1928 MutableEntry
parent(&wtrans
,
1932 ASSERT_TRUE(parent
.good());
1933 parent
.PutIsUnsynced(true);
1934 parent
.PutIsDir(true);
1935 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1936 parent
.PutId(parent_id_
);
1937 parent
.PutBaseVersion(1);
1940 int64 meta_handle_b
;
1941 const Id parent2_local_id
= ids_
.NewLocalId();
1942 const Id child_local_id
= ids_
.NewLocalId();
1944 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1945 MutableEntry
parent2(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, parent2_name
);
1946 ASSERT_TRUE(parent2
.good());
1947 parent2
.PutIsUnsynced(true);
1948 parent2
.PutIsDir(true);
1949 parent2
.PutSpecifics(DefaultBookmarkSpecifics());
1951 parent2
.PutId(parent2_local_id
);
1953 &wtrans
, CREATE
, BOOKMARKS
, parent2_local_id
, child_name
);
1954 ASSERT_TRUE(child
.good());
1955 child
.PutIsUnsynced(true);
1956 child
.PutIsDir(true);
1957 child
.PutSpecifics(DefaultBookmarkSpecifics());
1958 child
.PutId(child_local_id
);
1959 meta_handle_b
= child
.GetMetahandle();
1962 EXPECT_TRUE(SyncShareNudge());
1963 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1964 // If this test starts failing, be aware other sort orders could be valid.
1965 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1966 EXPECT_TRUE(parent2_local_id
== mock_server_
->committed_ids()[1]);
1967 EXPECT_TRUE(child_local_id
== mock_server_
->committed_ids()[2]);
1969 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1971 Entry
parent(&rtrans
, syncable::GET_BY_ID
,
1972 GetOnlyEntryWithName(&rtrans
, rtrans
.root_id(), parent_name
));
1973 ASSERT_TRUE(parent
.good());
1974 EXPECT_TRUE(parent
.GetId().ServerKnows());
1976 Entry
parent2(&rtrans
, syncable::GET_BY_ID
,
1977 GetOnlyEntryWithName(&rtrans
, parent
.GetId(), parent2_name
));
1978 ASSERT_TRUE(parent2
.good());
1979 EXPECT_TRUE(parent2
.GetId().ServerKnows());
1981 // Id changed on commit, so this should fail.
1982 Entry
local_parent2_id_entry(&rtrans
,
1983 syncable::GET_BY_ID
,
1985 ASSERT_FALSE(local_parent2_id_entry
.good());
1987 Entry
entry_b(&rtrans
, syncable::GET_BY_HANDLE
, meta_handle_b
);
1988 EXPECT_TRUE(entry_b
.GetId().ServerKnows());
1989 EXPECT_TRUE(parent2
.GetId()== entry_b
.GetParentId());
1993 TEST_F(SyncerTest
, UpdateWithZeroLengthName
) {
1994 // One illegal update
1995 mock_server_
->AddUpdateDirectory(
1996 1, 0, std::string(), 1, 10, foreign_cache_guid(), "-1");
1997 // And one legal one that we're going to delete.
1998 mock_server_
->AddUpdateDirectory(2, 0, "FOO", 1, 10,
1999 foreign_cache_guid(), "-2");
2000 EXPECT_TRUE(SyncShareNudge());
2001 // Delete the legal one. The new update has a null name.
2002 mock_server_
->AddUpdateDirectory(
2003 2, 0, std::string(), 2, 20, foreign_cache_guid(), "-2");
2004 mock_server_
->SetLastUpdateDeleted();
2005 EXPECT_TRUE(SyncShareNudge());
2008 TEST_F(SyncerTest
, TestBasicUpdate
) {
2009 string id
= "some_id";
2010 string parent_id
= "0";
2011 string name
= "in_root";
2013 int64 timestamp
= 10;
2014 mock_server_
->AddUpdateDirectory(id
, parent_id
, name
, version
, timestamp
,
2015 foreign_cache_guid(), "-1");
2017 EXPECT_TRUE(SyncShareNudge());
2019 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2020 Entry
entry(&trans
, GET_BY_ID
,
2021 syncable::Id::CreateFromServerId("some_id"));
2022 ASSERT_TRUE(entry
.good());
2023 EXPECT_TRUE(entry
.GetIsDir());
2024 EXPECT_TRUE(entry
.GetServerVersion()== version
);
2025 EXPECT_TRUE(entry
.GetBaseVersion()== version
);
2026 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
2027 EXPECT_FALSE(entry
.GetIsUnsynced());
2028 EXPECT_FALSE(entry
.GetServerIsDel());
2029 EXPECT_FALSE(entry
.GetIsDel());
2033 TEST_F(SyncerTest
, IllegalAndLegalUpdates
) {
2034 Id root
= TestIdFactory::root();
2035 // Should apply just fine.
2036 mock_server_
->AddUpdateDirectory(1, 0, "in_root", 10, 10,
2037 foreign_cache_guid(), "-1");
2039 // Same name. But this SHOULD work.
2040 mock_server_
->AddUpdateDirectory(2, 0, "in_root", 10, 10,
2041 foreign_cache_guid(), "-2");
2043 // Unknown parent: should never be applied. "-80" is a legal server ID,
2044 // because any string sent by the server is a legal server ID in the sync
2045 // protocol, but it's not the ID of any item known to the client. This
2046 // update should succeed validation, but be stuck in the unapplied state
2047 // until an item with the server ID "-80" arrives.
2048 mock_server_
->AddUpdateDirectory(3, -80, "bad_parent", 10, 10,
2049 foreign_cache_guid(), "-3");
2051 EXPECT_TRUE(SyncShareNudge());
2053 // Id 3 should be in conflict now.
2056 GetUpdateCounters(BOOKMARKS
).num_hierarchy_conflict_application_failures
);
2058 // The only request in that loop should have been a GetUpdate.
2059 // At that point, we didn't know whether or not we had conflicts.
2060 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2061 VerifyHierarchyConflictsUnspecified(mock_server_
->last_request());
2063 // These entries will be used in the second set of updates.
2064 mock_server_
->AddUpdateDirectory(4, 0, "newer_version", 20, 10,
2065 foreign_cache_guid(), "-4");
2066 mock_server_
->AddUpdateDirectory(5, 0, "circular1", 10, 10,
2067 foreign_cache_guid(), "-5");
2068 mock_server_
->AddUpdateDirectory(6, 5, "circular2", 10, 10,
2069 foreign_cache_guid(), "-6");
2070 mock_server_
->AddUpdateDirectory(9, 3, "bad_parent_child", 10, 10,
2071 foreign_cache_guid(), "-9");
2072 mock_server_
->AddUpdateDirectory(100, 9, "bad_parent_child2", 10, 10,
2073 foreign_cache_guid(), "-100");
2074 mock_server_
->AddUpdateDirectory(10, 0, "dir_to_bookmark", 10, 10,
2075 foreign_cache_guid(), "-10");
2077 EXPECT_TRUE(SyncShareNudge());
2078 // The three items with an unresolved parent should be unapplied (3, 9, 100).
2079 // The name clash should also still be in conflict.
2082 GetUpdateCounters(BOOKMARKS
).num_hierarchy_conflict_application_failures
);
2084 // This time around, we knew that there were conflicts.
2085 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2086 VerifyHierarchyConflictsReported(mock_server_
->last_request());
2089 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2090 // Even though it has the same name, it should work.
2091 Entry
name_clash(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
2092 ASSERT_TRUE(name_clash
.good());
2093 EXPECT_FALSE(name_clash
.GetIsUnappliedUpdate())
2094 << "Duplicate name SHOULD be OK.";
2096 Entry
bad_parent(&trans
, GET_BY_ID
, ids_
.FromNumber(3));
2097 ASSERT_TRUE(bad_parent
.good());
2098 EXPECT_TRUE(bad_parent
.GetIsUnappliedUpdate())
2099 << "child of unknown parent should be in conflict";
2101 Entry
bad_parent_child(&trans
, GET_BY_ID
, ids_
.FromNumber(9));
2102 ASSERT_TRUE(bad_parent_child
.good());
2103 EXPECT_TRUE(bad_parent_child
.GetIsUnappliedUpdate())
2104 << "grandchild of unknown parent should be in conflict";
2106 Entry
bad_parent_child2(&trans
, GET_BY_ID
, ids_
.FromNumber(100));
2107 ASSERT_TRUE(bad_parent_child2
.good());
2108 EXPECT_TRUE(bad_parent_child2
.GetIsUnappliedUpdate())
2109 << "great-grandchild of unknown parent should be in conflict";
2112 // Updating 1 should not affect item 2 of the same name.
2113 mock_server_
->AddUpdateDirectory(1, 0, "new_name", 20, 20,
2114 foreign_cache_guid(), "-1");
2116 // Moving 5 under 6 will create a cycle: a conflict.
2117 mock_server_
->AddUpdateDirectory(5, 6, "circular3", 20, 20,
2118 foreign_cache_guid(), "-5");
2120 // Flip the is_dir bit: should fail verify & be dropped.
2121 mock_server_
->AddUpdateBookmark(10, 0, "dir_to_bookmark", 20, 20,
2122 foreign_cache_guid(), "-10");
2123 EXPECT_TRUE(SyncShareNudge());
2125 // Version number older than last known: should fail verify & be dropped.
2126 mock_server_
->AddUpdateDirectory(4, 0, "old_version", 10, 10,
2127 foreign_cache_guid(), "-4");
2128 EXPECT_TRUE(SyncShareNudge());
2130 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2132 Entry
still_a_dir(&trans
, GET_BY_ID
, ids_
.FromNumber(10));
2133 ASSERT_TRUE(still_a_dir
.good());
2134 EXPECT_FALSE(still_a_dir
.GetIsUnappliedUpdate());
2135 EXPECT_EQ(10u, still_a_dir
.GetBaseVersion());
2136 EXPECT_EQ(10u, still_a_dir
.GetServerVersion());
2137 EXPECT_TRUE(still_a_dir
.GetIsDir());
2139 Entry
rename(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2140 ASSERT_TRUE(rename
.good());
2141 EXPECT_EQ(root
, rename
.GetParentId());
2142 EXPECT_EQ("new_name", rename
.GetNonUniqueName());
2143 EXPECT_FALSE(rename
.GetIsUnappliedUpdate());
2144 EXPECT_TRUE(ids_
.FromNumber(1) == rename
.GetId());
2145 EXPECT_EQ(20u, rename
.GetBaseVersion());
2147 Entry
name_clash(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
2148 ASSERT_TRUE(name_clash
.good());
2149 EXPECT_EQ(root
, name_clash
.GetParentId());
2150 EXPECT_TRUE(ids_
.FromNumber(2) == name_clash
.GetId());
2151 EXPECT_EQ(10u, name_clash
.GetBaseVersion());
2152 EXPECT_EQ("in_root", name_clash
.GetNonUniqueName());
2154 Entry
ignored_old_version(&trans
, GET_BY_ID
, ids_
.FromNumber(4));
2155 ASSERT_TRUE(ignored_old_version
.good());
2157 ignored_old_version
.GetNonUniqueName()== "newer_version");
2158 EXPECT_FALSE(ignored_old_version
.GetIsUnappliedUpdate());
2159 EXPECT_EQ(20u, ignored_old_version
.GetBaseVersion());
2161 Entry
circular_parent_issue(&trans
, GET_BY_ID
, ids_
.FromNumber(5));
2162 ASSERT_TRUE(circular_parent_issue
.good());
2163 EXPECT_TRUE(circular_parent_issue
.GetIsUnappliedUpdate())
2164 << "circular move should be in conflict";
2165 EXPECT_TRUE(circular_parent_issue
.GetParentId()== root_id_
);
2166 EXPECT_TRUE(circular_parent_issue
.GetServerParentId()==
2167 ids_
.FromNumber(6));
2168 EXPECT_EQ(10u, circular_parent_issue
.GetBaseVersion());
2170 Entry
circular_parent_target(&trans
, GET_BY_ID
, ids_
.FromNumber(6));
2171 ASSERT_TRUE(circular_parent_target
.good());
2172 EXPECT_FALSE(circular_parent_target
.GetIsUnappliedUpdate());
2173 EXPECT_TRUE(circular_parent_issue
.GetId()==
2174 circular_parent_target
.GetParentId());
2175 EXPECT_EQ(10u, circular_parent_target
.GetBaseVersion());
2178 EXPECT_FALSE(saw_syncer_event_
);
2181 GetUpdateCounters(BOOKMARKS
).num_hierarchy_conflict_application_failures
);
2184 // A commit with a lost response produces an update that has to be reunited with
2186 TEST_F(SyncerTest
, CommitReuniteUpdateAdjustsChildren
) {
2187 // Create a folder in the root.
2188 int64 metahandle_folder
;
2190 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2192 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_folder");
2193 ASSERT_TRUE(entry
.good());
2194 entry
.PutIsDir(true);
2195 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2196 entry
.PutIsUnsynced(true);
2197 metahandle_folder
= entry
.GetMetahandle();
2200 // Verify it and pull the ID out of the folder.
2201 syncable::Id folder_id
;
2202 int64 metahandle_entry
;
2204 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2205 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_folder
);
2206 ASSERT_TRUE(entry
.good());
2207 folder_id
= entry
.GetId();
2208 ASSERT_TRUE(!folder_id
.ServerKnows());
2211 // Create an entry in the newly created folder.
2213 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2214 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, folder_id
, "new_entry");
2215 ASSERT_TRUE(entry
.good());
2216 metahandle_entry
= entry
.GetMetahandle();
2217 WriteTestDataToEntry(&trans
, &entry
);
2220 // Verify it and pull the ID out of the entry.
2221 syncable::Id entry_id
;
2223 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2224 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, metahandle_entry
);
2225 ASSERT_TRUE(entry
.good());
2226 EXPECT_EQ(folder_id
, entry
.GetParentId());
2227 EXPECT_EQ("new_entry", entry
.GetNonUniqueName());
2228 entry_id
= entry
.GetId();
2229 EXPECT_TRUE(!entry_id
.ServerKnows());
2230 VerifyTestDataInEntry(&trans
, &entry
);
2233 // Now, to emulate a commit response failure, we just don't commit it.
2234 int64 new_version
= 150; // any larger value.
2235 int64 timestamp
= 20; // arbitrary value.
2236 syncable::Id new_folder_id
=
2237 syncable::Id::CreateFromServerId("folder_server_id");
2239 // The following update should cause the folder to both apply the update, as
2240 // well as reassociate the id.
2241 mock_server_
->AddUpdateDirectory(new_folder_id
, root_id_
,
2242 "new_folder", new_version
, timestamp
,
2243 local_cache_guid(), folder_id
.GetServerId());
2245 // We don't want it accidentally committed, just the update applied.
2246 mock_server_
->set_conflict_all_commits(true);
2248 // Alright! Apply that update!
2249 EXPECT_FALSE(SyncShareNudge());
2251 // The folder's ID should have been updated.
2252 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2253 Entry
folder(&trans
, GET_BY_HANDLE
, metahandle_folder
);
2254 ASSERT_TRUE(folder
.good());
2255 EXPECT_EQ("new_folder", folder
.GetNonUniqueName());
2256 EXPECT_TRUE(new_version
== folder
.GetBaseVersion());
2257 EXPECT_TRUE(new_folder_id
== folder
.GetId());
2258 EXPECT_TRUE(folder
.GetId().ServerKnows());
2259 EXPECT_EQ(trans
.root_id(), folder
.GetParentId());
2261 // Since it was updated, the old folder should not exist.
2262 Entry
old_dead_folder(&trans
, GET_BY_ID
, folder_id
);
2263 EXPECT_FALSE(old_dead_folder
.good());
2265 // The child's parent should have changed.
2266 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, metahandle_entry
);
2267 ASSERT_TRUE(entry
.good());
2268 EXPECT_EQ("new_entry", entry
.GetNonUniqueName());
2269 EXPECT_EQ(new_folder_id
, entry
.GetParentId());
2270 EXPECT_TRUE(!entry
.GetId().ServerKnows());
2271 VerifyTestDataInEntry(&trans
, &entry
);
2275 // A commit with a lost response produces an update that has to be reunited with
2277 TEST_F(SyncerTest
, CommitReuniteUpdate
) {
2278 // Create an entry in the root.
2279 int64 entry_metahandle
;
2281 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2282 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_entry");
2283 ASSERT_TRUE(entry
.good());
2284 entry_metahandle
= entry
.GetMetahandle();
2285 WriteTestDataToEntry(&trans
, &entry
);
2288 // Verify it and pull the ID out.
2289 syncable::Id entry_id
;
2291 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2293 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2294 ASSERT_TRUE(entry
.good());
2295 entry_id
= entry
.GetId();
2296 EXPECT_TRUE(!entry_id
.ServerKnows());
2297 VerifyTestDataInEntry(&trans
, &entry
);
2300 // Now, to emulate a commit response failure, we just don't commit it.
2301 int64 new_version
= 150; // any larger value.
2302 int64 timestamp
= 20; // arbitrary value.
2303 syncable::Id new_entry_id
= syncable::Id::CreateFromServerId("server_id");
2305 // Generate an update from the server with a relevant ID reassignment.
2306 mock_server_
->AddUpdateBookmark(new_entry_id
, root_id_
,
2307 "new_entry", new_version
, timestamp
,
2308 local_cache_guid(), entry_id
.GetServerId());
2310 // We don't want it accidentally committed, just the update applied.
2311 mock_server_
->set_conflict_all_commits(true);
2313 // Alright! Apply that update!
2314 EXPECT_TRUE(SyncShareNudge());
2316 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2317 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2318 ASSERT_TRUE(entry
.good());
2319 EXPECT_TRUE(new_version
== entry
.GetBaseVersion());
2320 EXPECT_TRUE(new_entry_id
== entry
.GetId());
2321 EXPECT_EQ("new_entry", entry
.GetNonUniqueName());
2325 // A commit with a lost response must work even if the local entry was deleted
2326 // before the update is applied. We should not duplicate the local entry in
2327 // this case, but just create another one alongside. We may wish to examine
2328 // this behavior in the future as it can create hanging uploads that never
2329 // finish, that must be cleaned up on the server side after some time.
2330 TEST_F(SyncerTest
, CommitReuniteUpdateDoesNotChokeOnDeletedLocalEntry
) {
2331 // Create a entry in the root.
2332 int64 entry_metahandle
;
2334 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2335 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_entry");
2336 ASSERT_TRUE(entry
.good());
2337 entry_metahandle
= entry
.GetMetahandle();
2338 WriteTestDataToEntry(&trans
, &entry
);
2340 // Verify it and pull the ID out.
2341 syncable::Id entry_id
;
2343 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2344 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2345 ASSERT_TRUE(entry
.good());
2346 entry_id
= entry
.GetId();
2347 EXPECT_TRUE(!entry_id
.ServerKnows());
2348 VerifyTestDataInEntry(&trans
, &entry
);
2351 // Now, to emulate a commit response failure, we just don't commit it.
2352 int64 new_version
= 150; // any larger value.
2353 int64 timestamp
= 20; // arbitrary value.
2354 syncable::Id new_entry_id
= syncable::Id::CreateFromServerId("server_id");
2356 // Generate an update from the server with a relevant ID reassignment.
2357 mock_server_
->AddUpdateBookmark(new_entry_id
, root_id_
,
2358 "new_entry", new_version
, timestamp
,
2359 local_cache_guid(), entry_id
.GetServerId());
2361 // We don't want it accidentally committed, just the update applied.
2362 mock_server_
->set_conflict_all_commits(true);
2364 // Purposefully delete the entry now before the update application finishes.
2366 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2367 Id new_entry_id
= GetOnlyEntryWithName(
2368 &trans
, trans
.root_id(), "new_entry");
2369 MutableEntry
entry(&trans
, GET_BY_ID
, new_entry_id
);
2370 ASSERT_TRUE(entry
.good());
2371 entry
.PutIsDel(true);
2374 // Just don't CHECK fail in sync, have the update split.
2375 EXPECT_TRUE(SyncShareNudge());
2377 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2378 Id new_entry_id
= GetOnlyEntryWithName(
2379 &trans
, trans
.root_id(), "new_entry");
2380 Entry
entry(&trans
, GET_BY_ID
, new_entry_id
);
2381 ASSERT_TRUE(entry
.good());
2382 EXPECT_FALSE(entry
.GetIsDel());
2384 Entry
old_entry(&trans
, GET_BY_ID
, entry_id
);
2385 ASSERT_TRUE(old_entry
.good());
2386 EXPECT_TRUE(old_entry
.GetIsDel());
2390 // TODO(chron): Add more unsanitized name tests.
2391 TEST_F(SyncerTest
, ConflictMatchingEntryHandlesUnsanitizedNames
) {
2392 mock_server_
->AddUpdateDirectory(1, 0, "A/A", 10, 10,
2393 foreign_cache_guid(), "-1");
2394 mock_server_
->AddUpdateDirectory(2, 0, "B/B", 10, 10,
2395 foreign_cache_guid(), "-2");
2396 mock_server_
->set_conflict_all_commits(true);
2397 EXPECT_TRUE(SyncShareNudge());
2399 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2401 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2402 ASSERT_TRUE(A
.good());
2403 A
.PutIsUnsynced(true);
2404 A
.PutIsUnappliedUpdate(true);
2405 A
.PutServerVersion(20);
2407 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
2408 ASSERT_TRUE(B
.good());
2409 B
.PutIsUnappliedUpdate(true);
2410 B
.PutServerVersion(20);
2412 EXPECT_TRUE(SyncShareNudge());
2413 saw_syncer_event_
= false;
2414 mock_server_
->set_conflict_all_commits(false);
2417 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2419 Entry
A(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2420 ASSERT_TRUE(A
.good());
2421 EXPECT_TRUE(A
.GetIsUnsynced()== false);
2422 EXPECT_TRUE(A
.GetIsUnappliedUpdate()== false);
2423 EXPECT_TRUE(A
.GetServerVersion()== 20);
2425 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
2426 ASSERT_TRUE(B
.good());
2427 EXPECT_TRUE(B
.GetIsUnsynced()== false);
2428 EXPECT_TRUE(B
.GetIsUnappliedUpdate()== false);
2429 EXPECT_TRUE(B
.GetServerVersion()== 20);
2433 TEST_F(SyncerTest
, ConflictMatchingEntryHandlesNormalNames
) {
2434 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
2435 foreign_cache_guid(), "-1");
2436 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
2437 foreign_cache_guid(), "-2");
2438 mock_server_
->set_conflict_all_commits(true);
2439 EXPECT_TRUE(SyncShareNudge());
2441 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2443 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2444 ASSERT_TRUE(A
.good());
2445 A
.PutIsUnsynced(true);
2446 A
.PutIsUnappliedUpdate(true);
2447 A
.PutServerVersion(20);
2449 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
2450 ASSERT_TRUE(B
.good());
2451 B
.PutIsUnappliedUpdate(true);
2452 B
.PutServerVersion(20);
2454 EXPECT_TRUE(SyncShareNudge());
2455 saw_syncer_event_
= false;
2456 mock_server_
->set_conflict_all_commits(false);
2459 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2461 Entry
A(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2462 ASSERT_TRUE(A
.good());
2463 EXPECT_TRUE(A
.GetIsUnsynced()== false);
2464 EXPECT_TRUE(A
.GetIsUnappliedUpdate()== false);
2465 EXPECT_TRUE(A
.GetServerVersion()== 20);
2467 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
2468 ASSERT_TRUE(B
.good());
2469 EXPECT_TRUE(B
.GetIsUnsynced()== false);
2470 EXPECT_TRUE(B
.GetIsUnappliedUpdate()== false);
2471 EXPECT_TRUE(B
.GetServerVersion()== 20);
2475 TEST_F(SyncerTest
, ReverseFolderOrderingTest
) {
2476 mock_server_
->AddUpdateDirectory(4, 3, "ggchild", 10, 10,
2477 foreign_cache_guid(), "-4");
2478 mock_server_
->AddUpdateDirectory(3, 2, "gchild", 10, 10,
2479 foreign_cache_guid(), "-3");
2480 mock_server_
->AddUpdateDirectory(5, 4, "gggchild", 10, 10,
2481 foreign_cache_guid(), "-5");
2482 mock_server_
->AddUpdateDirectory(2, 1, "child", 10, 10,
2483 foreign_cache_guid(), "-2");
2484 mock_server_
->AddUpdateDirectory(1, 0, "parent", 10, 10,
2485 foreign_cache_guid(), "-1");
2486 EXPECT_TRUE(SyncShareNudge());
2487 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2489 Id child_id
= GetOnlyEntryWithName(
2490 &trans
, ids_
.FromNumber(4), "gggchild");
2491 Entry
child(&trans
, GET_BY_ID
, child_id
);
2492 ASSERT_TRUE(child
.good());
2495 class EntryCreatedInNewFolderTest
: public SyncerTest
{
2497 void CreateFolderInBob() {
2498 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2499 MutableEntry
bob(&trans
,
2500 syncable::GET_BY_ID
,
2501 GetOnlyEntryWithName(&trans
,
2502 TestIdFactory::root(),
2506 MutableEntry
entry2(
2507 &trans
, CREATE
, BOOKMARKS
, bob
.GetId(), "bob");
2508 CHECK(entry2
.good());
2509 entry2
.PutIsDir(true);
2510 entry2
.PutIsUnsynced(true);
2511 entry2
.PutSpecifics(DefaultBookmarkSpecifics());
2515 TEST_F(EntryCreatedInNewFolderTest
, EntryCreatedInNewFolderMidSync
) {
2517 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2518 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2519 ASSERT_TRUE(entry
.good());
2520 entry
.PutIsDir(true);
2521 entry
.PutIsUnsynced(true);
2522 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2525 mock_server_
->SetMidCommitCallback(
2526 base::Bind(&EntryCreatedInNewFolderTest::CreateFolderInBob
,
2527 base::Unretained(this)));
2528 EXPECT_TRUE(SyncShareNudge());
2529 // We loop until no unsynced handles remain, so we will commit both ids.
2530 EXPECT_EQ(2u, mock_server_
->committed_ids().size());
2532 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2533 Entry
parent_entry(&trans
, syncable::GET_BY_ID
,
2534 GetOnlyEntryWithName(&trans
, TestIdFactory::root(), "bob"));
2535 ASSERT_TRUE(parent_entry
.good());
2538 GetOnlyEntryWithName(&trans
, parent_entry
.GetId(), "bob");
2539 Entry
child(&trans
, syncable::GET_BY_ID
, child_id
);
2540 ASSERT_TRUE(child
.good());
2541 EXPECT_EQ(parent_entry
.GetId(), child
.GetParentId());
2545 TEST_F(SyncerTest
, NegativeIDInUpdate
) {
2546 mock_server_
->AddUpdateBookmark(-10, 0, "bad", 40, 40,
2547 foreign_cache_guid(), "-100");
2548 EXPECT_TRUE(SyncShareNudge());
2549 // The negative id would make us CHECK!
2552 TEST_F(SyncerTest
, UnappliedUpdateOnCreatedItemItemDoesNotCrash
) {
2553 int64 metahandle_fred
;
2554 syncable::Id orig_id
;
2557 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2558 MutableEntry
fred_match(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(),
2560 ASSERT_TRUE(fred_match
.good());
2561 metahandle_fred
= fred_match
.GetMetahandle();
2562 orig_id
= fred_match
.GetId();
2563 WriteTestDataToEntry(&trans
, &fred_match
);
2566 EXPECT_TRUE(SyncShareNudge());
2567 EXPECT_EQ(1u, mock_server_
->committed_ids().size());
2568 mock_server_
->set_conflict_all_commits(true);
2569 syncable::Id fred_match_id
;
2571 // Now receive a change from outside.
2572 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2573 MutableEntry
fred_match(&trans
, GET_BY_HANDLE
, metahandle_fred
);
2574 ASSERT_TRUE(fred_match
.good());
2575 EXPECT_TRUE(fred_match
.GetId().ServerKnows());
2576 fred_match_id
= fred_match
.GetId();
2577 mock_server_
->AddUpdateBookmark(fred_match_id
, trans
.root_id(),
2578 "fred_match", 40, 40, local_cache_guid(), orig_id
.GetServerId());
2581 for (int i
= 0 ; i
< 30 ; ++i
) {
2582 EXPECT_TRUE(SyncShareNudge());
2587 * In the event that we have a double changed entry, that is changed on both
2588 * the client and the server, the conflict resolver should just drop one of
2589 * them and accept the other.
2592 TEST_F(SyncerTest
, DoublyChangedWithResolver
) {
2593 syncable::Id local_id
;
2595 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2596 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Folder");
2597 ASSERT_TRUE(parent
.good());
2598 parent
.PutIsDir(true);
2599 parent
.PutId(parent_id_
);
2600 parent
.PutBaseVersion(5);
2601 parent
.PutSpecifics(DefaultBookmarkSpecifics());
2602 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete.htm");
2603 ASSERT_TRUE(child
.good());
2604 local_id
= child
.GetId();
2605 child
.PutId(child_id_
);
2606 child
.PutBaseVersion(10);
2607 WriteTestDataToEntry(&wtrans
, &child
);
2609 mock_server_
->AddUpdateBookmark(child_id_
, parent_id_
, "Pete2.htm", 11, 10,
2610 local_cache_guid(), local_id
.GetServerId());
2611 mock_server_
->set_conflict_all_commits(true);
2612 EXPECT_FALSE(SyncShareNudge());
2613 syncable::Directory::Metahandles children
;
2615 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2616 directory()->GetChildHandlesById(&trans
, parent_id_
, &children
);
2617 // We expect the conflict resolver to preserve the local entry.
2618 Entry
child(&trans
, syncable::GET_BY_ID
, child_id_
);
2619 ASSERT_TRUE(child
.good());
2620 EXPECT_TRUE(child
.GetIsUnsynced());
2621 EXPECT_FALSE(child
.GetIsUnappliedUpdate());
2622 EXPECT_TRUE(child
.GetSpecifics().has_bookmark());
2623 EXPECT_EQ("Pete.htm", child
.GetNonUniqueName());
2624 VerifyTestBookmarkDataInEntry(&child
);
2627 // Only one entry, since we just overwrite one.
2628 EXPECT_EQ(1u, children
.size());
2629 saw_syncer_event_
= false;
2632 // We got this repro case when someone was editing bookmarks while sync was
2633 // occuring. The entry had changed out underneath the user.
2634 TEST_F(SyncerTest
, CommitsUpdateDoesntAlterEntry
) {
2635 const base::Time
& test_time
= ProtoTimeToTime(123456);
2636 syncable::Id local_id
;
2637 int64 entry_metahandle
;
2639 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2640 MutableEntry
entry(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Pete");
2641 ASSERT_TRUE(entry
.good());
2642 EXPECT_FALSE(entry
.GetId().ServerKnows());
2643 local_id
= entry
.GetId();
2644 entry
.PutIsDir(true);
2645 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2646 entry
.PutIsUnsynced(true);
2647 entry
.PutMtime(test_time
);
2648 entry_metahandle
= entry
.GetMetahandle();
2650 EXPECT_TRUE(SyncShareNudge());
2654 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2655 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, entry_metahandle
);
2656 ASSERT_TRUE(entry
.good());
2658 EXPECT_TRUE(id
.ServerKnows());
2659 version
= entry
.GetBaseVersion();
2661 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
2662 update
->set_originator_cache_guid(local_cache_guid());
2663 update
->set_originator_client_item_id(local_id
.GetServerId());
2664 EXPECT_EQ("Pete", update
->name());
2665 EXPECT_EQ(id
.GetServerId(), update
->id_string());
2666 EXPECT_EQ(root_id_
.GetServerId(), update
->parent_id_string());
2667 EXPECT_EQ(version
, update
->version());
2668 EXPECT_TRUE(SyncShareNudge());
2670 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2671 Entry
entry(&trans
, syncable::GET_BY_ID
, id
);
2672 ASSERT_TRUE(entry
.good());
2673 EXPECT_TRUE(entry
.GetMtime()== test_time
);
2677 TEST_F(SyncerTest
, ParentAndChildBothMatch
) {
2678 // Disable PREFERENCES which is enabled at the setup step to avoid
2680 // PREFERENCES root folder and failing the test below that verifies the number
2681 // of children at the root.
2682 DisableDatatype(PREFERENCES
);
2684 const FullModelTypeSet all_types
= FullModelTypeSet::All();
2685 syncable::Id parent_id
= ids_
.NewServerId();
2686 syncable::Id child_id
= ids_
.NewServerId();
2687 syncable::Id parent_local_id
;
2688 syncable::Id child_local_id
;
2691 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2692 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Folder");
2693 ASSERT_TRUE(parent
.good());
2694 parent_local_id
= parent
.GetId();
2695 parent
.PutIsDir(true);
2696 parent
.PutIsUnsynced(true);
2697 parent
.PutId(parent_id
);
2698 parent
.PutBaseVersion(1);
2699 parent
.PutSpecifics(DefaultBookmarkSpecifics());
2701 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent
.GetId(), "test.htm");
2702 ASSERT_TRUE(child
.good());
2703 child_local_id
= child
.GetId();
2704 child
.PutId(child_id
);
2705 child
.PutBaseVersion(1);
2706 child
.PutSpecifics(DefaultBookmarkSpecifics());
2707 WriteTestDataToEntry(&wtrans
, &child
);
2709 mock_server_
->AddUpdateDirectory(parent_id
, root_id_
, "Folder", 10, 10,
2711 parent_local_id
.GetServerId());
2712 mock_server_
->AddUpdateBookmark(child_id
, parent_id
, "test.htm", 10, 10,
2714 child_local_id
.GetServerId());
2715 mock_server_
->set_conflict_all_commits(true);
2716 EXPECT_TRUE(SyncShareNudge());
2717 EXPECT_TRUE(SyncShareNudge());
2718 EXPECT_TRUE(SyncShareNudge());
2720 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2721 Directory::Metahandles children
;
2722 directory()->GetChildHandlesById(&trans
, root_id_
, &children
);
2723 EXPECT_EQ(1u, children
.size());
2724 directory()->GetChildHandlesById(&trans
, parent_id
, &children
);
2725 EXPECT_EQ(1u, children
.size());
2726 std::vector
<int64
> unapplied
;
2727 directory()->GetUnappliedUpdateMetaHandles(&trans
, all_types
, &unapplied
);
2728 EXPECT_EQ(0u, unapplied
.size());
2729 syncable::Directory::Metahandles unsynced
;
2730 directory()->GetUnsyncedMetaHandles(&trans
, &unsynced
);
2731 EXPECT_EQ(0u, unsynced
.size());
2732 saw_syncer_event_
= false;
2736 TEST_F(SyncerTest
, CommittingNewDeleted
) {
2738 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2739 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2740 entry
.PutIsUnsynced(true);
2741 entry
.PutIsDel(true);
2743 EXPECT_TRUE(SyncShareNudge());
2744 EXPECT_EQ(0u, mock_server_
->committed_ids().size());
2747 // Original problem synopsis:
2748 // Check failed: entry->GetBaseVersion()<= entry->GetServerVersion()
2749 // Client creates entry, client finishes committing entry. Between
2750 // commit and getting update back, we delete the entry.
2751 // We get the update for the entry, but the local one was modified
2752 // so we store the entry but don't apply it. IS_UNAPPLIED_UPDATE is set.
2753 // We commit deletion and get a new version number.
2754 // We apply unapplied updates again before we get the update about the deletion.
2755 // This means we have an unapplied update where server_version < base_version.
2756 TEST_F(SyncerTest
, UnappliedUpdateDuringCommit
) {
2757 // This test is a little fake.
2759 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2760 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2761 entry
.PutId(ids_
.FromNumber(20));
2762 entry
.PutBaseVersion(1);
2763 entry
.PutServerVersion(1);
2764 entry
.PutServerParentId(ids_
.FromNumber(9999)); // Bad parent.
2765 entry
.PutIsUnsynced(true);
2766 entry
.PutIsUnappliedUpdate(true);
2767 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2768 entry
.PutServerSpecifics(DefaultBookmarkSpecifics());
2769 entry
.PutIsDel(false);
2771 EXPECT_TRUE(SyncShareNudge());
2772 EXPECT_EQ(1, session_
->status_controller().TotalNumConflictingItems());
2773 saw_syncer_event_
= false;
2776 // Original problem synopsis:
2778 // Unexpected error during sync if we:
2779 // make a new folder bob
2781 // make a new folder fred
2782 // move bob into fred
2785 // if no syncing occured midway, bob will have an illegal parent
2786 TEST_F(SyncerTest
, DeletingEntryInFolder
) {
2787 // This test is a little fake.
2788 int64 existing_metahandle
;
2790 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2791 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "existing");
2792 ASSERT_TRUE(entry
.good());
2793 entry
.PutIsDir(true);
2794 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2795 entry
.PutIsUnsynced(true);
2796 existing_metahandle
= entry
.GetMetahandle();
2798 EXPECT_TRUE(SyncShareNudge());
2800 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2801 MutableEntry
newfolder(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new");
2802 ASSERT_TRUE(newfolder
.good());
2803 newfolder
.PutIsDir(true);
2804 newfolder
.PutSpecifics(DefaultBookmarkSpecifics());
2805 newfolder
.PutIsUnsynced(true);
2807 MutableEntry
existing(&trans
, GET_BY_HANDLE
, existing_metahandle
);
2808 ASSERT_TRUE(existing
.good());
2809 existing
.PutParentId(newfolder
.GetId());
2810 existing
.PutIsUnsynced(true);
2811 EXPECT_TRUE(existing
.GetId().ServerKnows());
2813 newfolder
.PutIsDel(true);
2814 existing
.PutIsDel(true);
2816 EXPECT_TRUE(SyncShareNudge());
2817 EXPECT_EQ(0, GetCommitCounters(BOOKMARKS
).num_commits_conflict
);
2820 TEST_F(SyncerTest
, DeletingEntryWithLocalEdits
) {
2821 int64 newfolder_metahandle
;
2823 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
2824 foreign_cache_guid(), "-1");
2825 EXPECT_TRUE(SyncShareNudge());
2827 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2828 MutableEntry
newfolder(
2829 &trans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(1), "local");
2830 ASSERT_TRUE(newfolder
.good());
2831 newfolder
.PutIsUnsynced(true);
2832 newfolder
.PutIsDir(true);
2833 newfolder
.PutSpecifics(DefaultBookmarkSpecifics());
2834 newfolder_metahandle
= newfolder
.GetMetahandle();
2836 mock_server_
->AddUpdateDirectory(1, 0, "bob", 2, 20,
2837 foreign_cache_guid(), "-1");
2838 mock_server_
->SetLastUpdateDeleted();
2839 SyncShareConfigure();
2841 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2842 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, newfolder_metahandle
);
2843 ASSERT_TRUE(entry
.good());
2847 TEST_F(SyncerTest
, FolderSwapUpdate
) {
2848 mock_server_
->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2849 foreign_cache_guid(), "-7801");
2850 mock_server_
->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2851 foreign_cache_guid(), "-1024");
2852 EXPECT_TRUE(SyncShareNudge());
2853 mock_server_
->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2854 foreign_cache_guid(), "-1024");
2855 mock_server_
->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2856 foreign_cache_guid(), "-7801");
2857 EXPECT_TRUE(SyncShareNudge());
2859 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2860 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2861 ASSERT_TRUE(id1
.good());
2862 EXPECT_TRUE("fred" == id1
.GetNonUniqueName());
2863 EXPECT_TRUE(root_id_
== id1
.GetParentId());
2864 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2865 ASSERT_TRUE(id2
.good());
2866 EXPECT_TRUE("bob" == id2
.GetNonUniqueName());
2867 EXPECT_TRUE(root_id_
== id2
.GetParentId());
2869 saw_syncer_event_
= false;
2872 TEST_F(SyncerTest
, NameCollidingFolderSwapWorksFine
) {
2873 mock_server_
->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2874 foreign_cache_guid(), "-7801");
2875 mock_server_
->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2876 foreign_cache_guid(), "-1024");
2877 mock_server_
->AddUpdateDirectory(4096, 0, "alice", 1, 10,
2878 foreign_cache_guid(), "-4096");
2879 EXPECT_TRUE(SyncShareNudge());
2881 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2882 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2883 ASSERT_TRUE(id1
.good());
2884 EXPECT_TRUE("bob" == id1
.GetNonUniqueName());
2885 EXPECT_TRUE(root_id_
== id1
.GetParentId());
2886 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2887 ASSERT_TRUE(id2
.good());
2888 EXPECT_TRUE("fred" == id2
.GetNonUniqueName());
2889 EXPECT_TRUE(root_id_
== id2
.GetParentId());
2890 Entry
id3(&trans
, GET_BY_ID
, ids_
.FromNumber(4096));
2891 ASSERT_TRUE(id3
.good());
2892 EXPECT_TRUE("alice" == id3
.GetNonUniqueName());
2893 EXPECT_TRUE(root_id_
== id3
.GetParentId());
2895 mock_server_
->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2896 foreign_cache_guid(), "-1024");
2897 mock_server_
->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2898 foreign_cache_guid(), "-7801");
2899 mock_server_
->AddUpdateDirectory(4096, 0, "bob", 2, 20,
2900 foreign_cache_guid(), "-4096");
2901 EXPECT_TRUE(SyncShareNudge());
2903 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2904 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2905 ASSERT_TRUE(id1
.good());
2906 EXPECT_TRUE("fred" == id1
.GetNonUniqueName());
2907 EXPECT_TRUE(root_id_
== id1
.GetParentId());
2908 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2909 ASSERT_TRUE(id2
.good());
2910 EXPECT_TRUE("bob" == id2
.GetNonUniqueName());
2911 EXPECT_TRUE(root_id_
== id2
.GetParentId());
2912 Entry
id3(&trans
, GET_BY_ID
, ids_
.FromNumber(4096));
2913 ASSERT_TRUE(id3
.good());
2914 EXPECT_TRUE("bob" == id3
.GetNonUniqueName());
2915 EXPECT_TRUE(root_id_
== id3
.GetParentId());
2917 saw_syncer_event_
= false;
2920 // Committing more than kDefaultMaxCommitBatchSize items requires that
2921 // we post more than one commit command to the server. This test makes
2922 // sure that scenario works as expected.
2923 TEST_F(SyncerTest
, CommitManyItemsInOneGo_Success
) {
2924 uint32 num_batches
= 3;
2925 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2927 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2928 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2929 string nameutf8
= base::UintToString(i
);
2930 string
name(nameutf8
.begin(), nameutf8
.end());
2931 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2932 e
.PutIsUnsynced(true);
2934 e
.PutSpecifics(DefaultBookmarkSpecifics());
2937 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2939 EXPECT_TRUE(SyncShareNudge());
2940 EXPECT_EQ(num_batches
, mock_server_
->commit_messages().size());
2941 EXPECT_EQ(0, directory()->unsynced_entity_count());
2944 // Test that a single failure to contact the server will cause us to exit the
2945 // commit loop immediately.
2946 TEST_F(SyncerTest
, CommitManyItemsInOneGo_PostBufferFail
) {
2947 uint32 num_batches
= 3;
2948 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2950 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2951 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2952 string nameutf8
= base::UintToString(i
);
2953 string
name(nameutf8
.begin(), nameutf8
.end());
2954 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2955 e
.PutIsUnsynced(true);
2957 e
.PutSpecifics(DefaultBookmarkSpecifics());
2960 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2962 // The second commit should fail. It will be preceded by one successful
2963 // GetUpdate and one succesful commit.
2964 mock_server_
->FailNthPostBufferToPathCall(3);
2965 EXPECT_FALSE(SyncShareNudge());
2967 EXPECT_EQ(1U, mock_server_
->commit_messages().size());
2968 EXPECT_EQ(SYNC_SERVER_ERROR
,
2969 session_
->status_controller().model_neutral_state().commit_result
);
2970 EXPECT_EQ(items_to_commit
- kDefaultMaxCommitBatchSize
,
2971 directory()->unsynced_entity_count());
2974 // Test that a single conflict response from the server will cause us to exit
2975 // the commit loop immediately.
2976 TEST_F(SyncerTest
, CommitManyItemsInOneGo_CommitConflict
) {
2977 uint32 num_batches
= 2;
2978 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2980 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2981 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2982 string nameutf8
= base::UintToString(i
);
2983 string
name(nameutf8
.begin(), nameutf8
.end());
2984 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2985 e
.PutIsUnsynced(true);
2987 e
.PutSpecifics(DefaultBookmarkSpecifics());
2990 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2992 // Return a CONFLICT response for the first item.
2993 mock_server_
->set_conflict_n_commits(1);
2994 EXPECT_FALSE(SyncShareNudge());
2996 // We should stop looping at the first sign of trouble.
2997 EXPECT_EQ(1U, mock_server_
->commit_messages().size());
2998 EXPECT_EQ(items_to_commit
- (kDefaultMaxCommitBatchSize
- 1),
2999 directory()->unsynced_entity_count());
3002 // Tests that sending debug info events works.
3003 TEST_F(SyncerTest
, SendDebugInfoEventsOnGetUpdates_HappyCase
) {
3004 debug_info_getter_
->AddDebugEvent();
3005 debug_info_getter_
->AddDebugEvent();
3007 EXPECT_TRUE(SyncShareNudge());
3009 // Verify we received one GetUpdates request with two debug info events.
3010 EXPECT_EQ(1U, mock_server_
->requests().size());
3011 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
3012 EXPECT_EQ(2, mock_server_
->last_request().debug_info().events_size());
3014 EXPECT_TRUE(SyncShareNudge());
3016 // See that we received another GetUpdates request, but that it contains no
3017 // debug info events.
3018 EXPECT_EQ(2U, mock_server_
->requests().size());
3019 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
3020 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
3022 debug_info_getter_
->AddDebugEvent();
3024 EXPECT_TRUE(SyncShareNudge());
3026 // See that we received another GetUpdates request and it contains one debug
3028 EXPECT_EQ(3U, mock_server_
->requests().size());
3029 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
3030 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
3033 // Tests that debug info events are dropped on server error.
3034 TEST_F(SyncerTest
, SendDebugInfoEventsOnGetUpdates_PostFailsDontDrop
) {
3035 debug_info_getter_
->AddDebugEvent();
3036 debug_info_getter_
->AddDebugEvent();
3038 mock_server_
->FailNextPostBufferToPathCall();
3039 EXPECT_FALSE(SyncShareNudge());
3041 // Verify we attempted to send one GetUpdates request with two debug info
3043 EXPECT_EQ(1U, mock_server_
->requests().size());
3044 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
3045 EXPECT_EQ(2, mock_server_
->last_request().debug_info().events_size());
3047 EXPECT_TRUE(SyncShareNudge());
3049 // See that the client resent the two debug info events.
3050 EXPECT_EQ(2U, mock_server_
->requests().size());
3051 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
3052 EXPECT_EQ(2, mock_server_
->last_request().debug_info().events_size());
3054 // The previous send was successful so this next one shouldn't generate any
3055 // debug info events.
3056 EXPECT_TRUE(SyncShareNudge());
3057 EXPECT_EQ(3U, mock_server_
->requests().size());
3058 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
3059 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
3062 // Tests that commit failure with conflict will trigger GetUpdates for next
3064 TEST_F(SyncerTest
, CommitFailureWithConflict
) {
3065 ConfigureNoGetUpdatesRequired();
3066 CreateUnsyncedDirectory("X", "id_X");
3067 EXPECT_FALSE(nudge_tracker_
.IsGetUpdatesRequired());
3069 EXPECT_TRUE(SyncShareNudge());
3070 EXPECT_FALSE(nudge_tracker_
.IsGetUpdatesRequired());
3072 CreateUnsyncedDirectory("Y", "id_Y");
3073 mock_server_
->set_conflict_n_commits(1);
3074 EXPECT_FALSE(SyncShareNudge());
3075 EXPECT_TRUE(nudge_tracker_
.IsGetUpdatesRequired());
3077 nudge_tracker_
.RecordSuccessfulSyncCycle();
3078 EXPECT_FALSE(nudge_tracker_
.IsGetUpdatesRequired());
3081 // Tests that sending debug info events on Commit works.
3082 TEST_F(SyncerTest
, SendDebugInfoEventsOnCommit_HappyCase
) {
3083 // Make sure GetUpdate isn't call as it would "steal" debug info events before
3084 // Commit has a chance to send them.
3085 ConfigureNoGetUpdatesRequired();
3087 // Generate a debug info event and trigger a commit.
3088 debug_info_getter_
->AddDebugEvent();
3089 CreateUnsyncedDirectory("X", "id_X");
3090 EXPECT_TRUE(SyncShareNudge());
3092 // Verify that the last request received is a Commit and that it contains a
3093 // debug info event.
3094 EXPECT_EQ(1U, mock_server_
->requests().size());
3095 ASSERT_TRUE(mock_server_
->last_request().has_commit());
3096 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
3098 // Generate another commit, but no debug info event.
3099 CreateUnsyncedDirectory("Y", "id_Y");
3100 EXPECT_TRUE(SyncShareNudge());
3102 // See that it was received and contains no debug info events.
3103 EXPECT_EQ(2U, mock_server_
->requests().size());
3104 ASSERT_TRUE(mock_server_
->last_request().has_commit());
3105 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
3108 // Tests that debug info events are not dropped on server error.
3109 TEST_F(SyncerTest
, SendDebugInfoEventsOnCommit_PostFailsDontDrop
) {
3110 // Make sure GetUpdate isn't call as it would "steal" debug info events before
3111 // Commit has a chance to send them.
3112 ConfigureNoGetUpdatesRequired();
3114 mock_server_
->FailNextPostBufferToPathCall();
3116 // Generate a debug info event and trigger a commit.
3117 debug_info_getter_
->AddDebugEvent();
3118 CreateUnsyncedDirectory("X", "id_X");
3119 EXPECT_FALSE(SyncShareNudge());
3121 // Verify that the last request sent is a Commit and that it contains a debug
3123 EXPECT_EQ(1U, mock_server_
->requests().size());
3124 ASSERT_TRUE(mock_server_
->last_request().has_commit());
3125 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
3128 EXPECT_TRUE(SyncShareNudge());
3130 // Verify that we've received another Commit and that it contains a debug info
3131 // event (just like the previous one).
3132 EXPECT_EQ(2U, mock_server_
->requests().size());
3133 ASSERT_TRUE(mock_server_
->last_request().has_commit());
3134 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
3136 // Generate another commit and try again.
3137 CreateUnsyncedDirectory("Y", "id_Y");
3138 EXPECT_TRUE(SyncShareNudge());
3140 // See that it was received and contains no debug info events.
3141 EXPECT_EQ(3U, mock_server_
->requests().size());
3142 ASSERT_TRUE(mock_server_
->last_request().has_commit());
3143 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
3146 TEST_F(SyncerTest
, HugeConflict
) {
3147 int item_count
= 300; // We should be able to do 300 or 3000 w/o issue.
3149 syncable::Id parent_id
= ids_
.NewServerId();
3150 syncable::Id last_id
= parent_id
;
3151 vector
<syncable::Id
> tree_ids
;
3153 // Create a lot of updates for which the parent does not exist yet.
3154 // Generate a huge deep tree which should all fail to apply at first.
3156 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3157 for (int i
= 0; i
< item_count
; i
++) {
3158 syncable::Id next_id
= ids_
.NewServerId();
3159 syncable::Id local_id
= ids_
.NewLocalId();
3160 tree_ids
.push_back(next_id
);
3161 mock_server_
->AddUpdateDirectory(next_id
, last_id
, "BOB", 2, 20,
3162 foreign_cache_guid(),
3163 local_id
.GetServerId());
3167 EXPECT_TRUE(SyncShareNudge());
3169 // Check they're in the expected conflict state.
3171 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3172 for (int i
= 0; i
< item_count
; i
++) {
3173 Entry
e(&trans
, GET_BY_ID
, tree_ids
[i
]);
3174 // They should all exist but none should be applied.
3175 ASSERT_TRUE(e
.good());
3176 EXPECT_TRUE(e
.GetIsDel());
3177 EXPECT_TRUE(e
.GetIsUnappliedUpdate());
3181 // Add the missing parent directory.
3182 mock_server_
->AddUpdateDirectory(parent_id
, TestIdFactory::root(),
3183 "BOB", 2, 20, foreign_cache_guid(), "-3500");
3184 EXPECT_TRUE(SyncShareNudge());
3186 // Now they should all be OK.
3188 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3189 for (int i
= 0; i
< item_count
; i
++) {
3190 Entry
e(&trans
, GET_BY_ID
, tree_ids
[i
]);
3191 ASSERT_TRUE(e
.good());
3192 EXPECT_FALSE(e
.GetIsDel());
3193 EXPECT_FALSE(e
.GetIsUnappliedUpdate());
3198 TEST_F(SyncerTest
, DontCrashOnCaseChange
) {
3199 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
3200 foreign_cache_guid(), "-1");
3201 EXPECT_TRUE(SyncShareNudge());
3203 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3204 MutableEntry
e(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3205 ASSERT_TRUE(e
.good());
3206 e
.PutIsUnsynced(true);
3208 mock_server_
->set_conflict_all_commits(true);
3209 mock_server_
->AddUpdateDirectory(1, 0, "BOB", 2, 20,
3210 foreign_cache_guid(), "-1");
3211 EXPECT_FALSE(SyncShareNudge()); // USED TO CAUSE AN ASSERT
3212 saw_syncer_event_
= false;
3215 TEST_F(SyncerTest
, UnsyncedItemAndUpdate
) {
3216 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
3217 foreign_cache_guid(), "-1");
3218 EXPECT_TRUE(SyncShareNudge());
3219 mock_server_
->set_conflict_all_commits(true);
3220 mock_server_
->AddUpdateDirectory(2, 0, "bob", 2, 20,
3221 foreign_cache_guid(), "-2");
3222 EXPECT_TRUE(SyncShareNudge()); // USED TO CAUSE AN ASSERT
3223 saw_syncer_event_
= false;
3226 TEST_F(SyncerTest
, NewEntryAndAlteredServerEntrySharePath
) {
3227 mock_server_
->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
3228 foreign_cache_guid(), "-1");
3229 EXPECT_TRUE(SyncShareNudge());
3230 int64 local_folder_handle
;
3231 syncable::Id local_folder_id
;
3233 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3234 MutableEntry
new_entry(
3235 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bar.htm");
3236 ASSERT_TRUE(new_entry
.good());
3237 local_folder_id
= new_entry
.GetId();
3238 local_folder_handle
= new_entry
.GetMetahandle();
3239 new_entry
.PutIsUnsynced(true);
3240 new_entry
.PutSpecifics(DefaultBookmarkSpecifics());
3241 MutableEntry
old(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3242 ASSERT_TRUE(old
.good());
3243 WriteTestDataToEntry(&wtrans
, &old
);
3245 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
3246 foreign_cache_guid(), "-1");
3247 mock_server_
->set_conflict_all_commits(true);
3248 EXPECT_FALSE(SyncShareNudge());
3249 saw_syncer_event_
= false;
3251 // Update #20 should have been dropped in favor of the local version.
3252 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3253 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3254 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3255 ASSERT_TRUE(server
.good());
3256 ASSERT_TRUE(local
.good());
3257 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3258 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3259 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3260 EXPECT_TRUE(server
.GetIsUnsynced());
3261 EXPECT_TRUE(local
.GetIsUnsynced());
3262 EXPECT_EQ("Foo.htm", server
.GetNonUniqueName());
3263 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3265 // Allow local changes to commit.
3266 mock_server_
->set_conflict_all_commits(false);
3267 EXPECT_TRUE(SyncShareNudge());
3268 saw_syncer_event_
= false;
3270 // Now add a server change to make the two names equal. There should
3271 // be no conflict with that, since names are not unique.
3272 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
3273 foreign_cache_guid(), "-1");
3274 EXPECT_TRUE(SyncShareNudge());
3275 saw_syncer_event_
= false;
3277 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3278 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3279 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3280 ASSERT_TRUE(server
.good());
3281 ASSERT_TRUE(local
.good());
3282 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3283 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3284 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3285 EXPECT_FALSE(server
.GetIsUnsynced());
3286 EXPECT_FALSE(local
.GetIsUnsynced());
3287 EXPECT_EQ("Bar.htm", server
.GetNonUniqueName());
3288 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3289 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
3290 server
.GetSpecifics().bookmark().url());
3294 // Same as NewEntryAnddServerEntrySharePath, but using the old-style protocol.
3295 TEST_F(SyncerTest
, NewEntryAndAlteredServerEntrySharePath_OldBookmarksProto
) {
3296 mock_server_
->set_use_legacy_bookmarks_protocol(true);
3297 mock_server_
->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
3298 foreign_cache_guid(), "-1");
3299 EXPECT_TRUE(SyncShareNudge());
3300 int64 local_folder_handle
;
3301 syncable::Id local_folder_id
;
3303 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3304 MutableEntry
new_entry(
3305 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bar.htm");
3306 ASSERT_TRUE(new_entry
.good());
3307 local_folder_id
= new_entry
.GetId();
3308 local_folder_handle
= new_entry
.GetMetahandle();
3309 new_entry
.PutIsUnsynced(true);
3310 new_entry
.PutSpecifics(DefaultBookmarkSpecifics());
3311 MutableEntry
old(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3312 ASSERT_TRUE(old
.good());
3313 WriteTestDataToEntry(&wtrans
, &old
);
3315 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
3316 foreign_cache_guid(), "-1");
3317 mock_server_
->set_conflict_all_commits(true);
3318 EXPECT_FALSE(SyncShareNudge());
3319 saw_syncer_event_
= false;
3321 // Update #20 should have been dropped in favor of the local version.
3322 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3323 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3324 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3325 ASSERT_TRUE(server
.good());
3326 ASSERT_TRUE(local
.good());
3327 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3328 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3329 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3330 EXPECT_TRUE(server
.GetIsUnsynced());
3331 EXPECT_TRUE(local
.GetIsUnsynced());
3332 EXPECT_EQ("Foo.htm", server
.GetNonUniqueName());
3333 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3335 // Allow local changes to commit.
3336 mock_server_
->set_conflict_all_commits(false);
3337 EXPECT_TRUE(SyncShareNudge());
3338 saw_syncer_event_
= false;
3340 // Now add a server change to make the two names equal. There should
3341 // be no conflict with that, since names are not unique.
3342 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
3343 foreign_cache_guid(), "-1");
3344 EXPECT_TRUE(SyncShareNudge());
3345 saw_syncer_event_
= false;
3347 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3348 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3349 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3350 ASSERT_TRUE(server
.good());
3351 ASSERT_TRUE(local
.good());
3352 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3353 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3354 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3355 EXPECT_FALSE(server
.GetIsUnsynced());
3356 EXPECT_FALSE(local
.GetIsUnsynced());
3357 EXPECT_EQ("Bar.htm", server
.GetNonUniqueName());
3358 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3359 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
3360 server
.GetSpecifics().bookmark().url());
3364 // Circular links should be resolved by the server.
3365 TEST_F(SyncerTest
, SiblingDirectoriesBecomeCircular
) {
3366 // we don't currently resolve this. This test ensures we don't.
3367 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
3368 foreign_cache_guid(), "-1");
3369 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
3370 foreign_cache_guid(), "-2");
3371 EXPECT_TRUE(SyncShareNudge());
3373 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3374 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3375 ASSERT_TRUE(A
.good());
3376 A
.PutIsUnsynced(true);
3377 A
.PutParentId(ids_
.FromNumber(2));
3378 A
.PutNonUniqueName("B");
3380 mock_server_
->AddUpdateDirectory(2, 1, "A", 20, 20,
3381 foreign_cache_guid(), "-2");
3382 mock_server_
->set_conflict_all_commits(true);
3383 EXPECT_FALSE(SyncShareNudge());
3384 saw_syncer_event_
= false;
3386 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3387 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3388 ASSERT_TRUE(A
.good());
3389 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
3390 ASSERT_TRUE(B
.good());
3391 EXPECT_TRUE(A
.GetNonUniqueName()== "B");
3392 EXPECT_TRUE(B
.GetNonUniqueName()== "B");
3396 TEST_F(SyncerTest
, SwapEntryNames
) {
3397 // Simple transaction test.
3398 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
3399 foreign_cache_guid(), "-1");
3400 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
3401 foreign_cache_guid(), "-2");
3402 mock_server_
->set_conflict_all_commits(true);
3403 EXPECT_TRUE(SyncShareNudge());
3405 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3406 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3407 ASSERT_TRUE(A
.good());
3408 A
.PutIsUnsynced(true);
3409 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
3410 ASSERT_TRUE(B
.good());
3411 B
.PutIsUnsynced(true);
3412 A
.PutNonUniqueName("C");
3413 B
.PutNonUniqueName("A");
3414 A
.PutNonUniqueName("B");
3416 EXPECT_FALSE(SyncShareNudge());
3417 saw_syncer_event_
= false;
3420 TEST_F(SyncerTest
, DualDeletionWithNewItemNameClash
) {
3421 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
3422 foreign_cache_guid(), "-1");
3423 mock_server_
->AddUpdateBookmark(2, 0, "B", 10, 10,
3424 foreign_cache_guid(), "-2");
3425 mock_server_
->set_conflict_all_commits(true);
3426 EXPECT_TRUE(SyncShareNudge());
3428 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3429 MutableEntry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3430 ASSERT_TRUE(B
.good());
3431 WriteTestDataToEntry(&trans
, &B
);
3434 mock_server_
->AddUpdateBookmark(2, 0, "A", 11, 11,
3435 foreign_cache_guid(), "-2");
3436 mock_server_
->SetLastUpdateDeleted();
3437 EXPECT_TRUE(SyncShareNudge());
3439 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3440 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3441 ASSERT_TRUE(B
.good());
3442 EXPECT_FALSE(B
.GetIsUnsynced());
3443 EXPECT_FALSE(B
.GetIsUnappliedUpdate());
3445 saw_syncer_event_
= false;
3448 // When we undelete an entity as a result of conflict resolution, we reuse the
3449 // existing server id and preserve the old version, simply updating the server
3450 // version with the new non-deleted entity.
3451 TEST_F(SyncerTest
, ResolveWeWroteTheyDeleted
) {
3452 int64 bob_metahandle
;
3454 mock_server_
->AddUpdateBookmark(1, 0, "bob", 1, 10,
3455 foreign_cache_guid(), "-1");
3456 EXPECT_TRUE(SyncShareNudge());
3458 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3459 MutableEntry
bob(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3460 ASSERT_TRUE(bob
.good());
3461 bob_metahandle
= bob
.GetMetahandle();
3462 WriteTestDataToEntry(&trans
, &bob
);
3464 mock_server_
->AddUpdateBookmark(1, 0, "bob", 2, 10,
3465 foreign_cache_guid(), "-1");
3466 mock_server_
->SetLastUpdateDeleted();
3467 mock_server_
->set_conflict_all_commits(true);
3468 EXPECT_FALSE(SyncShareNudge());
3469 EXPECT_FALSE(SyncShareNudge());
3471 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3472 Entry
bob(&trans
, GET_BY_HANDLE
, bob_metahandle
);
3473 ASSERT_TRUE(bob
.good());
3474 EXPECT_TRUE(bob
.GetIsUnsynced());
3475 EXPECT_TRUE(bob
.GetId().ServerKnows());
3476 EXPECT_FALSE(bob
.GetIsUnappliedUpdate());
3477 EXPECT_FALSE(bob
.GetIsDel());
3478 EXPECT_EQ(2, bob
.GetServerVersion());
3479 EXPECT_EQ(2, bob
.GetBaseVersion());
3481 saw_syncer_event_
= false;
3484 // This test is to reproduce a check failure. Sometimes we would get a bad ID
3485 // back when creating an entry.
3486 TEST_F(SyncerTest
, DuplicateIDReturn
) {
3488 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3489 MutableEntry
folder(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
3490 ASSERT_TRUE(folder
.good());
3491 folder
.PutIsUnsynced(true);
3492 folder
.PutIsDir(true);
3493 folder
.PutSpecifics(DefaultBookmarkSpecifics());
3494 MutableEntry
folder2(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "fred");
3495 ASSERT_TRUE(folder2
.good());
3496 folder2
.PutIsUnsynced(false);
3497 folder2
.PutIsDir(true);
3498 folder2
.PutSpecifics(DefaultBookmarkSpecifics());
3499 folder2
.PutBaseVersion(3);
3500 folder2
.PutId(syncable::Id::CreateFromServerId("mock_server:10000"));
3502 mock_server_
->set_next_new_id(10000);
3503 EXPECT_EQ(1u, directory()->unsynced_entity_count());
3504 // we get back a bad id in here (should never happen).
3505 EXPECT_FALSE(SyncShareNudge());
3506 EXPECT_EQ(1u, directory()->unsynced_entity_count());
3507 EXPECT_TRUE(SyncShareNudge()); // another bad id in here.
3508 EXPECT_EQ(0u, directory()->unsynced_entity_count());
3509 saw_syncer_event_
= false;
3512 TEST_F(SyncerTest
, DeletedEntryWithBadParentInLoopCalculation
) {
3513 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
3514 foreign_cache_guid(), "-1");
3515 EXPECT_TRUE(SyncShareNudge());
3517 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3518 MutableEntry
bob(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3519 ASSERT_TRUE(bob
.good());
3520 // This is valid, because the parent could have gone away a long time ago.
3521 bob
.PutParentId(ids_
.FromNumber(54));
3523 bob
.PutIsUnsynced(true);
3525 mock_server_
->AddUpdateDirectory(2, 1, "fred", 1, 10,
3526 foreign_cache_guid(), "-2");
3527 EXPECT_TRUE(SyncShareNudge());
3528 EXPECT_TRUE(SyncShareNudge());
3531 TEST_F(SyncerTest
, ConflictResolverMergesLocalDeleteAndServerUpdate
) {
3532 syncable::Id local_id
;
3534 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3536 MutableEntry
local_deleted(
3537 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "name");
3538 local_id
= local_deleted
.GetId();
3539 local_deleted
.PutId(ids_
.FromNumber(1));
3540 local_deleted
.PutBaseVersion(1);
3541 local_deleted
.PutIsDel(true);
3542 local_deleted
.PutIsDir(false);
3543 local_deleted
.PutIsUnsynced(true);
3544 local_deleted
.PutSpecifics(DefaultBookmarkSpecifics());
3547 mock_server_
->AddUpdateBookmark(ids_
.FromNumber(1), root_id_
, "name", 10, 10,
3549 local_id
.GetServerId());
3551 // We don't care about actually committing, just the resolution.
3552 mock_server_
->set_conflict_all_commits(true);
3553 EXPECT_FALSE(SyncShareNudge());
3556 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3557 Entry
local_deleted(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3558 EXPECT_TRUE(local_deleted
.GetBaseVersion()== 10);
3559 EXPECT_TRUE(local_deleted
.GetIsUnappliedUpdate()== false);
3560 EXPECT_TRUE(local_deleted
.GetIsUnsynced()== true);
3561 EXPECT_TRUE(local_deleted
.GetIsDel()== true);
3562 EXPECT_TRUE(local_deleted
.GetIsDir()== false);
3566 // See what happens if the IS_DIR bit gets flipped. This can cause us
3567 // all kinds of disasters.
3568 TEST_F(SyncerTest
, UpdateFlipsTheFolderBit
) {
3569 // Local object: a deleted directory (container), revision 1, unsynced.
3571 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3573 MutableEntry
local_deleted(
3574 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "name");
3575 local_deleted
.PutId(ids_
.FromNumber(1));
3576 local_deleted
.PutBaseVersion(1);
3577 local_deleted
.PutIsDel(true);
3578 local_deleted
.PutIsDir(true);
3579 local_deleted
.PutIsUnsynced(true);
3580 local_deleted
.PutSpecifics(DefaultBookmarkSpecifics());
3583 // Server update: entry-type object (not a container), revision 10.
3584 mock_server_
->AddUpdateBookmark(ids_
.FromNumber(1), root_id_
, "name", 10, 10,
3586 ids_
.FromNumber(1).GetServerId());
3588 // Don't attempt to commit.
3589 mock_server_
->set_conflict_all_commits(true);
3591 // The syncer should not attempt to apply the invalid update.
3592 EXPECT_FALSE(SyncShareNudge());
3595 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3596 Entry
local_deleted(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3597 EXPECT_TRUE(local_deleted
.GetBaseVersion()== 1);
3598 EXPECT_TRUE(local_deleted
.GetIsUnappliedUpdate()== false);
3599 EXPECT_TRUE(local_deleted
.GetIsUnsynced()== true);
3600 EXPECT_TRUE(local_deleted
.GetIsDel()== true);
3601 EXPECT_TRUE(local_deleted
.GetIsDir()== true);
3606 // Merge conflict resolution will merge a new local entry with another entry
3607 // that needs updates, resulting in CHECK.
3608 TEST_F(SyncerTest
, MergingExistingItems
) {
3609 mock_server_
->set_conflict_all_commits(true);
3610 mock_server_
->AddUpdateBookmark(1, 0, "base", 10, 10,
3611 local_cache_guid(), "-1");
3612 EXPECT_TRUE(SyncShareNudge());
3614 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3616 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "Copy of base");
3617 WriteTestDataToEntry(&trans
, &entry
);
3619 mock_server_
->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
3620 local_cache_guid(), "-1");
3621 EXPECT_FALSE(SyncShareNudge());
3624 // In this test a long changelog contains a child at the start of the changelog
3625 // and a parent at the end. While these updates are in progress the client would
3627 TEST_F(SyncerTest
, LongChangelistWithApplicationConflict
) {
3628 const int depth
= 400;
3629 syncable::Id folder_id
= ids_
.FromNumber(1);
3631 // First we an item in a folder in the root. However the folder won't come
3633 syncable::Id stuck_entry_id
= TestIdFactory::FromNumber(99999);
3634 mock_server_
->AddUpdateDirectory(stuck_entry_id
,
3635 folder_id
, "stuck", 1, 1,
3636 foreign_cache_guid(), "-99999");
3637 mock_server_
->SetChangesRemaining(depth
- 1);
3638 EXPECT_TRUE(SyncShareNudge());
3640 // Buffer up a very long series of downloads.
3641 // We should never be stuck (conflict resolution shouldn't
3642 // kick in so long as we're making forward progress).
3643 for (int i
= 0; i
< depth
; i
++) {
3644 mock_server_
->NextUpdateBatch();
3645 mock_server_
->SetNewTimestamp(i
+ 1);
3646 mock_server_
->SetChangesRemaining(depth
- i
);
3649 EXPECT_TRUE(SyncShareNudge());
3651 // Ensure our folder hasn't somehow applied.
3653 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3654 Entry
child(&trans
, GET_BY_ID
, stuck_entry_id
);
3655 EXPECT_TRUE(child
.good());
3656 EXPECT_TRUE(child
.GetIsUnappliedUpdate());
3657 EXPECT_TRUE(child
.GetIsDel());
3658 EXPECT_FALSE(child
.GetIsUnsynced());
3661 // And finally the folder.
3662 mock_server_
->AddUpdateDirectory(folder_id
,
3663 TestIdFactory::root(), "folder", 1, 1,
3664 foreign_cache_guid(), "-1");
3665 mock_server_
->SetChangesRemaining(0);
3666 EXPECT_TRUE(SyncShareNudge());
3667 EXPECT_TRUE(SyncShareNudge());
3668 // Check that everything is as expected after the commit.
3670 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3671 Entry
entry(&trans
, GET_BY_ID
, folder_id
);
3672 ASSERT_TRUE(entry
.good());
3673 Entry
child(&trans
, GET_BY_ID
, stuck_entry_id
);
3674 EXPECT_EQ(entry
.GetId(), child
.GetParentId());
3675 EXPECT_EQ("stuck", child
.GetNonUniqueName());
3676 EXPECT_TRUE(child
.good());
3680 TEST_F(SyncerTest
, DontMergeTwoExistingItems
) {
3681 mock_server_
->set_conflict_all_commits(true);
3682 mock_server_
->AddUpdateBookmark(1, 0, "base", 10, 10,
3683 foreign_cache_guid(), "-1");
3684 mock_server_
->AddUpdateBookmark(2, 0, "base2", 10, 10,
3685 foreign_cache_guid(), "-2");
3686 EXPECT_TRUE(SyncShareNudge());
3688 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3689 MutableEntry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3690 ASSERT_TRUE(entry
.good());
3691 entry
.PutNonUniqueName("Copy of base");
3692 entry
.PutIsUnsynced(true);
3694 mock_server_
->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
3695 foreign_cache_guid(), "-1");
3696 EXPECT_FALSE(SyncShareNudge());
3698 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3699 Entry
entry1(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3700 EXPECT_FALSE(entry1
.GetIsUnappliedUpdate());
3701 EXPECT_FALSE(entry1
.GetIsUnsynced());
3702 EXPECT_FALSE(entry1
.GetIsDel());
3703 Entry
entry2(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3704 EXPECT_FALSE(entry2
.GetIsUnappliedUpdate());
3705 EXPECT_TRUE(entry2
.GetIsUnsynced());
3706 EXPECT_FALSE(entry2
.GetIsDel());
3707 EXPECT_EQ(entry1
.GetNonUniqueName(), entry2
.GetNonUniqueName());
3711 TEST_F(SyncerTest
, TestUndeleteUpdate
) {
3712 mock_server_
->set_conflict_all_commits(true);
3713 mock_server_
->AddUpdateDirectory(1, 0, "foo", 1, 1,
3714 foreign_cache_guid(), "-1");
3715 mock_server_
->AddUpdateDirectory(2, 1, "bar", 1, 2,
3716 foreign_cache_guid(), "-2");
3717 EXPECT_TRUE(SyncShareNudge());
3718 mock_server_
->AddUpdateDirectory(2, 1, "bar", 2, 3,
3719 foreign_cache_guid(), "-2");
3720 mock_server_
->SetLastUpdateDeleted();
3721 EXPECT_TRUE(SyncShareNudge());
3725 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3726 Entry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3727 ASSERT_TRUE(entry
.good());
3728 EXPECT_TRUE(entry
.GetIsDel());
3729 metahandle
= entry
.GetMetahandle();
3731 mock_server_
->AddUpdateDirectory(1, 0, "foo", 2, 4,
3732 foreign_cache_guid(), "-1");
3733 mock_server_
->SetLastUpdateDeleted();
3734 EXPECT_TRUE(SyncShareNudge());
3735 // This used to be rejected as it's an undeletion. Now, it results in moving
3736 // the delete path aside.
3737 mock_server_
->AddUpdateDirectory(2, 1, "bar", 3, 5,
3738 foreign_cache_guid(), "-2");
3739 EXPECT_TRUE(SyncShareNudge());
3741 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3742 Entry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3743 ASSERT_TRUE(entry
.good());
3744 EXPECT_TRUE(entry
.GetIsDel());
3745 EXPECT_FALSE(entry
.GetServerIsDel());
3746 EXPECT_TRUE(entry
.GetIsUnappliedUpdate());
3747 EXPECT_NE(entry
.GetMetahandle(), metahandle
);
3751 TEST_F(SyncerTest
, TestMoveSanitizedNamedFolder
) {
3752 mock_server_
->AddUpdateDirectory(1, 0, "foo", 1, 1,
3753 foreign_cache_guid(), "-1");
3754 mock_server_
->AddUpdateDirectory(2, 0, ":::", 1, 2,
3755 foreign_cache_guid(), "-2");
3756 EXPECT_TRUE(SyncShareNudge());
3758 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3759 MutableEntry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3760 ASSERT_TRUE(entry
.good());
3761 entry
.PutParentId(ids_
.FromNumber(1));
3762 EXPECT_TRUE(entry
.PutIsUnsynced(true));
3764 EXPECT_TRUE(SyncShareNudge());
3765 // We use the same sync ts as before so our times match up.
3766 mock_server_
->AddUpdateDirectory(2, 1, ":::", 2, 2,
3767 foreign_cache_guid(), "-2");
3768 EXPECT_TRUE(SyncShareNudge());
3771 // Don't crash when this occurs.
3772 TEST_F(SyncerTest
, UpdateWhereParentIsNotAFolder
) {
3773 mock_server_
->AddUpdateBookmark(1, 0, "B", 10, 10,
3774 foreign_cache_guid(), "-1");
3775 mock_server_
->AddUpdateDirectory(2, 1, "BookmarkParent", 10, 10,
3776 foreign_cache_guid(), "-2");
3777 // Used to cause a CHECK
3778 EXPECT_TRUE(SyncShareNudge());
3780 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
3781 Entry
good_entry(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
3782 ASSERT_TRUE(good_entry
.good());
3783 EXPECT_FALSE(good_entry
.GetIsUnappliedUpdate());
3784 Entry
bad_parent(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(2));
3785 ASSERT_TRUE(bad_parent
.good());
3786 EXPECT_TRUE(bad_parent
.GetIsUnappliedUpdate());
3790 TEST_F(SyncerTest
, DirectoryUpdateTest
) {
3791 Id in_root_id
= ids_
.NewServerId();
3792 Id in_in_root_id
= ids_
.NewServerId();
3794 mock_server_
->AddUpdateDirectory(in_root_id
, TestIdFactory::root(),
3795 "in_root_name", 2, 2,
3796 foreign_cache_guid(), "-1");
3797 mock_server_
->AddUpdateDirectory(in_in_root_id
, in_root_id
,
3798 "in_in_root_name", 3, 3,
3799 foreign_cache_guid(), "-2");
3800 EXPECT_TRUE(SyncShareNudge());
3802 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3803 Entry
in_root(&trans
, GET_BY_ID
, in_root_id
);
3804 ASSERT_TRUE(in_root
.good());
3805 EXPECT_EQ("in_root_name", in_root
.GetNonUniqueName());
3806 EXPECT_EQ(TestIdFactory::root(), in_root
.GetParentId());
3808 Entry
in_in_root(&trans
, GET_BY_ID
, in_in_root_id
);
3809 ASSERT_TRUE(in_in_root
.good());
3810 EXPECT_EQ("in_in_root_name", in_in_root
.GetNonUniqueName());
3811 EXPECT_EQ(in_root_id
, in_in_root
.GetParentId());
3815 TEST_F(SyncerTest
, DirectoryCommitTest
) {
3816 syncable::Id in_root_id
, in_dir_id
;
3817 int64 foo_metahandle
;
3818 int64 bar_metahandle
;
3821 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3822 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "foo");
3823 ASSERT_TRUE(parent
.good());
3824 parent
.PutIsUnsynced(true);
3825 parent
.PutIsDir(true);
3826 parent
.PutSpecifics(DefaultBookmarkSpecifics());
3827 in_root_id
= parent
.GetId();
3828 foo_metahandle
= parent
.GetMetahandle();
3830 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent
.GetId(), "bar");
3831 ASSERT_TRUE(child
.good());
3832 child
.PutIsUnsynced(true);
3833 child
.PutIsDir(true);
3834 child
.PutSpecifics(DefaultBookmarkSpecifics());
3835 bar_metahandle
= child
.GetMetahandle();
3836 in_dir_id
= parent
.GetId();
3838 EXPECT_TRUE(SyncShareNudge());
3840 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3841 Entry
fail_by_old_id_entry(&trans
, GET_BY_ID
, in_root_id
);
3842 ASSERT_FALSE(fail_by_old_id_entry
.good());
3844 Entry
foo_entry(&trans
, GET_BY_HANDLE
, foo_metahandle
);
3845 ASSERT_TRUE(foo_entry
.good());
3846 EXPECT_EQ("foo", foo_entry
.GetNonUniqueName());
3847 EXPECT_NE(foo_entry
.GetId(), in_root_id
);
3849 Entry
bar_entry(&trans
, GET_BY_HANDLE
, bar_metahandle
);
3850 ASSERT_TRUE(bar_entry
.good());
3851 EXPECT_EQ("bar", bar_entry
.GetNonUniqueName());
3852 EXPECT_NE(bar_entry
.GetId(), in_dir_id
);
3853 EXPECT_EQ(foo_entry
.GetId(), bar_entry
.GetParentId());
3857 TEST_F(SyncerTest
, TestClientCommandDuringUpdate
) {
3858 using sync_pb::ClientCommand
;
3860 ClientCommand
* command
= new ClientCommand();
3861 command
->set_set_sync_poll_interval(8);
3862 command
->set_set_sync_long_poll_interval(800);
3863 command
->set_sessions_commit_delay_seconds(3141);
3864 sync_pb::CustomNudgeDelay
* bookmark_delay
=
3865 command
->add_custom_nudge_delays();
3866 bookmark_delay
->set_datatype_id(
3867 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3868 bookmark_delay
->set_delay_ms(950);
3869 command
->set_client_invalidation_hint_buffer_size(11);
3870 mock_server_
->AddUpdateDirectory(1, 0, "in_root", 1, 1,
3871 foreign_cache_guid(), "-1");
3872 mock_server_
->SetGUClientCommand(command
);
3873 EXPECT_TRUE(SyncShareNudge());
3875 EXPECT_EQ(TimeDelta::FromSeconds(8), last_short_poll_interval_received_
);
3876 EXPECT_EQ(TimeDelta::FromSeconds(800), last_long_poll_interval_received_
);
3877 EXPECT_EQ(TimeDelta::FromSeconds(3141), last_sessions_commit_delay_
);
3878 EXPECT_EQ(TimeDelta::FromMilliseconds(950), last_bookmarks_commit_delay_
);
3879 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_
);
3881 command
= new ClientCommand();
3882 command
->set_set_sync_poll_interval(180);
3883 command
->set_set_sync_long_poll_interval(190);
3884 command
->set_sessions_commit_delay_seconds(2718);
3885 bookmark_delay
= command
->add_custom_nudge_delays();
3886 bookmark_delay
->set_datatype_id(
3887 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3888 bookmark_delay
->set_delay_ms(1050);
3889 command
->set_client_invalidation_hint_buffer_size(9);
3890 mock_server_
->AddUpdateDirectory(
3891 1, 0, "in_root", 1, 1, foreign_cache_guid(), "-1");
3892 mock_server_
->SetGUClientCommand(command
);
3893 EXPECT_TRUE(SyncShareNudge());
3895 EXPECT_EQ(TimeDelta::FromSeconds(180), last_short_poll_interval_received_
);
3896 EXPECT_EQ(TimeDelta::FromSeconds(190), last_long_poll_interval_received_
);
3897 EXPECT_EQ(TimeDelta::FromSeconds(2718), last_sessions_commit_delay_
);
3898 EXPECT_EQ(TimeDelta::FromMilliseconds(1050), last_bookmarks_commit_delay_
);
3899 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_
);
3902 TEST_F(SyncerTest
, TestClientCommandDuringCommit
) {
3903 using sync_pb::ClientCommand
;
3905 ClientCommand
* command
= new ClientCommand();
3906 command
->set_set_sync_poll_interval(8);
3907 command
->set_set_sync_long_poll_interval(800);
3908 command
->set_sessions_commit_delay_seconds(3141);
3909 sync_pb::CustomNudgeDelay
* bookmark_delay
=
3910 command
->add_custom_nudge_delays();
3911 bookmark_delay
->set_datatype_id(
3912 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3913 bookmark_delay
->set_delay_ms(950);
3914 command
->set_client_invalidation_hint_buffer_size(11);
3915 CreateUnsyncedDirectory("X", "id_X");
3916 mock_server_
->SetCommitClientCommand(command
);
3917 EXPECT_TRUE(SyncShareNudge());
3919 EXPECT_EQ(TimeDelta::FromSeconds(8), last_short_poll_interval_received_
);
3920 EXPECT_EQ(TimeDelta::FromSeconds(800), last_long_poll_interval_received_
);
3921 EXPECT_EQ(TimeDelta::FromSeconds(3141), last_sessions_commit_delay_
);
3922 EXPECT_EQ(TimeDelta::FromMilliseconds(950), last_bookmarks_commit_delay_
);
3923 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_
);
3925 command
= new ClientCommand();
3926 command
->set_set_sync_poll_interval(180);
3927 command
->set_set_sync_long_poll_interval(190);
3928 command
->set_sessions_commit_delay_seconds(2718);
3929 bookmark_delay
= command
->add_custom_nudge_delays();
3930 bookmark_delay
->set_datatype_id(
3931 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3932 bookmark_delay
->set_delay_ms(1050);
3933 command
->set_client_invalidation_hint_buffer_size(9);
3934 CreateUnsyncedDirectory("Y", "id_Y");
3935 mock_server_
->SetCommitClientCommand(command
);
3936 EXPECT_TRUE(SyncShareNudge());
3938 EXPECT_EQ(TimeDelta::FromSeconds(180), last_short_poll_interval_received_
);
3939 EXPECT_EQ(TimeDelta::FromSeconds(190), last_long_poll_interval_received_
);
3940 EXPECT_EQ(TimeDelta::FromSeconds(2718), last_sessions_commit_delay_
);
3941 EXPECT_EQ(TimeDelta::FromMilliseconds(1050), last_bookmarks_commit_delay_
);
3942 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_
);
3945 TEST_F(SyncerTest
, EnsureWeSendUpOldParent
) {
3946 syncable::Id folder_one_id
= ids_
.FromNumber(1);
3947 syncable::Id folder_two_id
= ids_
.FromNumber(2);
3949 mock_server_
->AddUpdateDirectory(folder_one_id
, TestIdFactory::root(),
3950 "folder_one", 1, 1, foreign_cache_guid(), "-1");
3951 mock_server_
->AddUpdateDirectory(folder_two_id
, TestIdFactory::root(),
3952 "folder_two", 1, 1, foreign_cache_guid(), "-2");
3953 EXPECT_TRUE(SyncShareNudge());
3955 // A moved entry should send an "old parent."
3956 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3957 MutableEntry
entry(&trans
, GET_BY_ID
, folder_one_id
);
3958 ASSERT_TRUE(entry
.good());
3959 entry
.PutParentId(folder_two_id
);
3960 entry
.PutIsUnsynced(true);
3961 // A new entry should send no "old parent."
3962 MutableEntry
create(
3963 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_folder");
3964 create
.PutIsUnsynced(true);
3965 create
.PutSpecifics(DefaultBookmarkSpecifics());
3967 EXPECT_TRUE(SyncShareNudge());
3968 const sync_pb::CommitMessage
& commit
= mock_server_
->last_sent_commit();
3969 ASSERT_EQ(2, commit
.entries_size());
3970 EXPECT_TRUE(commit
.entries(0).parent_id_string() == "2");
3971 EXPECT_TRUE(commit
.entries(0).old_parent_id() == "0");
3972 EXPECT_FALSE(commit
.entries(1).has_old_parent_id());
3975 TEST_F(SyncerTest
, Test64BitVersionSupport
) {
3976 int64 really_big_int
= std::numeric_limits
<int64
>::max() - 12;
3977 const string
name("ringo's dang orang ran rings around my o-ring");
3978 int64 item_metahandle
;
3980 // Try writing max int64 to the version fields of a meta entry.
3982 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3983 MutableEntry
entry(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), name
);
3984 ASSERT_TRUE(entry
.good());
3985 entry
.PutBaseVersion(really_big_int
);
3986 entry
.PutServerVersion(really_big_int
);
3987 entry
.PutId(ids_
.NewServerId());
3988 item_metahandle
= entry
.GetMetahandle();
3990 // Now read it back out and make sure the value is max int64.
3991 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
3992 Entry
entry(&rtrans
, syncable::GET_BY_HANDLE
, item_metahandle
);
3993 ASSERT_TRUE(entry
.good());
3994 EXPECT_TRUE(really_big_int
== entry
.GetBaseVersion());
3997 TEST_F(SyncerTest
, TestSimpleUndelete
) {
3998 Id id
= ids_
.MakeServer("undeletion item"), root
= TestIdFactory::root();
3999 mock_server_
->set_conflict_all_commits(true);
4000 // Let there be an entry from the server.
4001 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 1, 10,
4002 foreign_cache_guid(), "-1");
4003 EXPECT_TRUE(SyncShareNudge());
4004 // Check it out and delete it.
4006 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
4007 MutableEntry
entry(&wtrans
, GET_BY_ID
, id
);
4008 ASSERT_TRUE(entry
.good());
4009 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4010 EXPECT_FALSE(entry
.GetIsUnsynced());
4011 EXPECT_FALSE(entry
.GetIsDel());
4012 // Delete it locally.
4013 entry
.PutIsDel(true);
4015 EXPECT_TRUE(SyncShareNudge());
4016 // Confirm we see IS_DEL and not SERVER_IS_DEL.
4018 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4019 Entry
entry(&trans
, GET_BY_ID
, id
);
4020 ASSERT_TRUE(entry
.good());
4021 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4022 EXPECT_FALSE(entry
.GetIsUnsynced());
4023 EXPECT_TRUE(entry
.GetIsDel());
4024 EXPECT_FALSE(entry
.GetServerIsDel());
4026 EXPECT_TRUE(SyncShareNudge());
4027 // Update from server confirming deletion.
4028 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 11,
4029 foreign_cache_guid(), "-1");
4030 mock_server_
->SetLastUpdateDeleted();
4031 EXPECT_TRUE(SyncShareNudge());
4032 // IS_DEL AND SERVER_IS_DEL now both true.
4034 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4035 Entry
entry(&trans
, GET_BY_ID
, id
);
4036 ASSERT_TRUE(entry
.good());
4037 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4038 EXPECT_FALSE(entry
.GetIsUnsynced());
4039 EXPECT_TRUE(entry
.GetIsDel());
4040 EXPECT_TRUE(entry
.GetServerIsDel());
4042 // Undelete from server.
4043 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 12,
4044 foreign_cache_guid(), "-1");
4045 EXPECT_TRUE(SyncShareNudge());
4046 // IS_DEL and SERVER_IS_DEL now both false.
4048 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4049 Entry
entry(&trans
, GET_BY_ID
, id
);
4050 ASSERT_TRUE(entry
.good());
4051 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4052 EXPECT_FALSE(entry
.GetIsUnsynced());
4053 EXPECT_FALSE(entry
.GetIsDel());
4054 EXPECT_FALSE(entry
.GetServerIsDel());
4058 TEST_F(SyncerTest
, TestUndeleteWithMissingDeleteUpdate
) {
4059 Id id
= ids_
.MakeServer("undeletion item"), root
= TestIdFactory::root();
4060 // Let there be a entry, from the server.
4061 mock_server_
->set_conflict_all_commits(true);
4062 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 1, 10,
4063 foreign_cache_guid(), "-1");
4064 EXPECT_TRUE(SyncShareNudge());
4065 // Check it out and delete it.
4067 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
4068 MutableEntry
entry(&wtrans
, GET_BY_ID
, id
);
4069 ASSERT_TRUE(entry
.good());
4070 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4071 EXPECT_FALSE(entry
.GetIsUnsynced());
4072 EXPECT_FALSE(entry
.GetIsDel());
4073 // Delete it locally.
4074 entry
.PutIsDel(true);
4076 EXPECT_TRUE(SyncShareNudge());
4077 // Confirm we see IS_DEL and not SERVER_IS_DEL.
4079 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4080 Entry
entry(&trans
, GET_BY_ID
, id
);
4081 ASSERT_TRUE(entry
.good());
4082 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4083 EXPECT_FALSE(entry
.GetIsUnsynced());
4084 EXPECT_TRUE(entry
.GetIsDel());
4085 EXPECT_FALSE(entry
.GetServerIsDel());
4087 EXPECT_TRUE(SyncShareNudge());
4088 // Say we do not get an update from server confirming deletion. Undelete
4090 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 12,
4091 foreign_cache_guid(), "-1");
4092 EXPECT_TRUE(SyncShareNudge());
4093 // IS_DEL and SERVER_IS_DEL now both false.
4095 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4096 Entry
entry(&trans
, GET_BY_ID
, id
);
4097 ASSERT_TRUE(entry
.good());
4098 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4099 EXPECT_FALSE(entry
.GetIsUnsynced());
4100 EXPECT_FALSE(entry
.GetIsDel());
4101 EXPECT_FALSE(entry
.GetServerIsDel());
4105 TEST_F(SyncerTest
, TestUndeleteIgnoreCorrectlyUnappliedUpdate
) {
4106 Id id1
= ids_
.MakeServer("first"), id2
= ids_
.MakeServer("second");
4107 Id root
= TestIdFactory::root();
4108 // Duplicate! expect path clashing!
4109 mock_server_
->set_conflict_all_commits(true);
4110 mock_server_
->AddUpdateBookmark(id1
, root
, "foo", 1, 10,
4111 foreign_cache_guid(), "-1");
4112 mock_server_
->AddUpdateBookmark(id2
, root
, "foo", 1, 10,
4113 foreign_cache_guid(), "-2");
4114 EXPECT_TRUE(SyncShareNudge());
4115 mock_server_
->AddUpdateBookmark(id2
, root
, "foo2", 2, 20,
4116 foreign_cache_guid(), "-2");
4117 EXPECT_TRUE(SyncShareNudge()); // Now just don't explode.
4120 TEST_F(SyncerTest
, ClientTagServerCreatedUpdatesWork
) {
4121 mock_server_
->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
4122 foreign_cache_guid(), "-1");
4123 mock_server_
->SetLastUpdateClientTag("permfolder");
4125 EXPECT_TRUE(SyncShareNudge());
4128 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4129 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
4130 ASSERT_TRUE(perm_folder
.good());
4131 EXPECT_FALSE(perm_folder
.GetIsDel());
4132 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
4133 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
4134 EXPECT_EQ(perm_folder
.GetUniqueClientTag(), "permfolder");
4135 EXPECT_EQ(perm_folder
.GetNonUniqueName(), "permitem1");
4138 mock_server_
->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
4139 foreign_cache_guid(), "-1");
4140 mock_server_
->SetLastUpdateClientTag("permfolder");
4141 EXPECT_TRUE(SyncShareNudge());
4144 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4146 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
4147 ASSERT_TRUE(perm_folder
.good());
4148 EXPECT_FALSE(perm_folder
.GetIsDel());
4149 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
4150 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
4151 EXPECT_EQ(perm_folder
.GetUniqueClientTag(), "permfolder");
4152 EXPECT_EQ(perm_folder
.GetNonUniqueName(), "permitem_renamed");
4156 TEST_F(SyncerTest
, ClientTagIllegalUpdateIgnored
) {
4157 mock_server_
->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
4158 foreign_cache_guid(), "-1");
4159 mock_server_
->SetLastUpdateClientTag("permfolder");
4161 EXPECT_TRUE(SyncShareNudge());
4164 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4165 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
4166 ASSERT_TRUE(perm_folder
.good());
4167 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
4168 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
4169 EXPECT_EQ(perm_folder
.GetUniqueClientTag(), "permfolder");
4170 EXPECT_TRUE(perm_folder
.GetNonUniqueName()== "permitem1");
4171 EXPECT_TRUE(perm_folder
.GetId().ServerKnows());
4174 mock_server_
->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
4175 foreign_cache_guid(), "-1");
4176 mock_server_
->SetLastUpdateClientTag("wrongtag");
4177 EXPECT_TRUE(SyncShareNudge());
4180 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4182 // This update is rejected because it has the same ID, but a
4183 // different tag than one that is already on the client.
4184 // The client has a ServerKnows ID, which cannot be overwritten.
4185 Entry
rejected_update(&trans
, GET_BY_CLIENT_TAG
, "wrongtag");
4186 EXPECT_FALSE(rejected_update
.good());
4188 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
4189 ASSERT_TRUE(perm_folder
.good());
4190 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
4191 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
4192 EXPECT_EQ(perm_folder
.GetNonUniqueName(), "permitem1");
4196 TEST_F(SyncerTest
, ClientTagUncommittedTagMatchesUpdate
) {
4197 int64 original_metahandle
= 0;
4200 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4202 &trans
, CREATE
, PREFERENCES
, ids_
.root(), "name");
4203 ASSERT_TRUE(pref
.good());
4204 pref
.PutUniqueClientTag("tag");
4205 pref
.PutIsUnsynced(true);
4206 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
4207 EXPECT_FALSE(pref
.GetId().ServerKnows());
4208 original_metahandle
= pref
.GetMetahandle();
4211 syncable::Id server_id
= TestIdFactory::MakeServer("id");
4212 mock_server_
->AddUpdatePref(server_id
.GetServerId(),
4213 ids_
.root().GetServerId(),
4215 mock_server_
->set_conflict_all_commits(true);
4217 EXPECT_FALSE(SyncShareNudge());
4218 // This should cause client tag reunion, preserving the metahandle.
4220 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4222 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
4223 ASSERT_TRUE(pref
.good());
4224 EXPECT_FALSE(pref
.GetIsDel());
4225 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
4226 EXPECT_TRUE(pref
.GetIsUnsynced());
4227 EXPECT_EQ(10, pref
.GetBaseVersion());
4228 // Entry should have been given the new ID while preserving the
4229 // metahandle; client should have won the conflict resolution.
4230 EXPECT_EQ(original_metahandle
, pref
.GetMetahandle());
4231 EXPECT_EQ("tag", pref
.GetUniqueClientTag());
4232 EXPECT_TRUE(pref
.GetId().ServerKnows());
4235 mock_server_
->set_conflict_all_commits(false);
4236 EXPECT_TRUE(SyncShareNudge());
4238 // The resolved entry ought to commit cleanly.
4240 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4242 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
4243 ASSERT_TRUE(pref
.good());
4244 EXPECT_FALSE(pref
.GetIsDel());
4245 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
4246 EXPECT_FALSE(pref
.GetIsUnsynced());
4247 EXPECT_TRUE(10 < pref
.GetBaseVersion());
4248 // Entry should have been given the new ID while preserving the
4249 // metahandle; client should have won the conflict resolution.
4250 EXPECT_EQ(original_metahandle
, pref
.GetMetahandle());
4251 EXPECT_EQ("tag", pref
.GetUniqueClientTag());
4252 EXPECT_TRUE(pref
.GetId().ServerKnows());
4256 TEST_F(SyncerTest
, ClientTagConflictWithDeletedLocalEntry
) {
4258 // Create a deleted local entry with a unique client tag.
4259 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4261 &trans
, CREATE
, PREFERENCES
, ids_
.root(), "name");
4262 ASSERT_TRUE(pref
.good());
4263 ASSERT_FALSE(pref
.GetId().ServerKnows());
4264 pref
.PutUniqueClientTag("tag");
4265 pref
.PutIsUnsynced(true);
4267 // Note: IS_DEL && !ServerKnows() will clear the UNSYNCED bit.
4268 // (We never attempt to commit server-unknown deleted items, so this
4269 // helps us clean up those entries).
4270 pref
.PutIsDel(true);
4273 // Prepare an update with the same unique client tag.
4274 syncable::Id server_id
= TestIdFactory::MakeServer("id");
4275 mock_server_
->AddUpdatePref(server_id
.GetServerId(),
4276 ids_
.root().GetServerId(),
4279 EXPECT_TRUE(SyncShareNudge());
4280 // The local entry will be overwritten.
4282 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4284 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
4285 ASSERT_TRUE(pref
.good());
4286 ASSERT_TRUE(pref
.GetId().ServerKnows());
4287 EXPECT_FALSE(pref
.GetIsDel());
4288 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
4289 EXPECT_FALSE(pref
.GetIsUnsynced());
4290 EXPECT_EQ(pref
.GetBaseVersion(), 10);
4291 EXPECT_EQ(pref
.GetUniqueClientTag(), "tag");
4295 TEST_F(SyncerTest
, ClientTagUpdateClashesWithLocalEntry
) {
4296 // This test is written assuming that ID comparison
4297 // will work out in a particular way.
4298 EXPECT_TRUE(ids_
.FromNumber(1) < ids_
.FromNumber(2));
4299 EXPECT_TRUE(ids_
.FromNumber(3) < ids_
.FromNumber(4));
4301 syncable::Id id1
= TestIdFactory::MakeServer("1");
4302 mock_server_
->AddUpdatePref(id1
.GetServerId(), "", "tag1", 10, 100);
4304 syncable::Id id4
= TestIdFactory::MakeServer("4");
4305 mock_server_
->AddUpdatePref(id4
.GetServerId(), "", "tag2", 11, 110);
4307 mock_server_
->set_conflict_all_commits(true);
4309 EXPECT_TRUE(SyncShareNudge());
4310 int64 tag1_metahandle
= syncable::kInvalidMetaHandle
;
4311 int64 tag2_metahandle
= syncable::kInvalidMetaHandle
;
4312 // This should cause client tag overwrite.
4314 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4316 Entry
tag1(&trans
, GET_BY_CLIENT_TAG
, "tag1");
4317 ASSERT_TRUE(tag1
.good());
4318 ASSERT_TRUE(tag1
.GetId().ServerKnows());
4319 ASSERT_TRUE(id1
== tag1
.GetId());
4320 EXPECT_FALSE(tag1
.GetIsDel());
4321 EXPECT_FALSE(tag1
.GetIsUnappliedUpdate());
4322 EXPECT_FALSE(tag1
.GetIsUnsynced());
4323 EXPECT_EQ(10, tag1
.GetBaseVersion());
4324 EXPECT_EQ("tag1", tag1
.GetUniqueClientTag());
4325 tag1_metahandle
= tag1
.GetMetahandle();
4327 Entry
tag2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
4328 ASSERT_TRUE(tag2
.good());
4329 ASSERT_TRUE(tag2
.GetId().ServerKnows());
4330 ASSERT_TRUE(id4
== tag2
.GetId());
4331 EXPECT_FALSE(tag2
.GetIsDel());
4332 EXPECT_FALSE(tag2
.GetIsUnappliedUpdate());
4333 EXPECT_FALSE(tag2
.GetIsUnsynced());
4334 EXPECT_EQ(11, tag2
.GetBaseVersion());
4335 EXPECT_EQ("tag2", tag2
.GetUniqueClientTag());
4336 tag2_metahandle
= tag2
.GetMetahandle();
4338 // Preferences type root should have been created by the updates above.
4339 Entry
pref_root(&trans
, GET_TYPE_ROOT
, PREFERENCES
);
4340 ASSERT_TRUE(pref_root
.good());
4342 syncable::Directory::Metahandles children
;
4343 directory()->GetChildHandlesById(&trans
, pref_root
.GetId(), &children
);
4344 ASSERT_EQ(2U, children
.size());
4347 syncable::Id id2
= TestIdFactory::MakeServer("2");
4348 mock_server_
->AddUpdatePref(id2
.GetServerId(), "", "tag1", 12, 120);
4349 syncable::Id id3
= TestIdFactory::MakeServer("3");
4350 mock_server_
->AddUpdatePref(id3
.GetServerId(), "", "tag2", 13, 130);
4351 EXPECT_TRUE(SyncShareNudge());
4354 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4356 Entry
tag1(&trans
, GET_BY_CLIENT_TAG
, "tag1");
4357 ASSERT_TRUE(tag1
.good());
4358 ASSERT_TRUE(tag1
.GetId().ServerKnows());
4359 ASSERT_EQ(id1
, tag1
.GetId())
4360 << "ID 1 should be kept, since it was less than ID 2.";
4361 EXPECT_FALSE(tag1
.GetIsDel());
4362 EXPECT_FALSE(tag1
.GetIsUnappliedUpdate());
4363 EXPECT_FALSE(tag1
.GetIsUnsynced());
4364 EXPECT_EQ(10, tag1
.GetBaseVersion());
4365 EXPECT_EQ("tag1", tag1
.GetUniqueClientTag());
4366 EXPECT_EQ(tag1_metahandle
, tag1
.GetMetahandle());
4368 Entry
tag2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
4369 ASSERT_TRUE(tag2
.good());
4370 ASSERT_TRUE(tag2
.GetId().ServerKnows());
4371 ASSERT_EQ(id3
, tag2
.GetId())
4372 << "ID 3 should be kept, since it was less than ID 4.";
4373 EXPECT_FALSE(tag2
.GetIsDel());
4374 EXPECT_FALSE(tag2
.GetIsUnappliedUpdate());
4375 EXPECT_FALSE(tag2
.GetIsUnsynced());
4376 EXPECT_EQ(13, tag2
.GetBaseVersion());
4377 EXPECT_EQ("tag2", tag2
.GetUniqueClientTag());
4378 EXPECT_EQ(tag2_metahandle
, tag2
.GetMetahandle());
4380 // Preferences type root should have been created by the updates above.
4381 Entry
pref_root(&trans
, GET_TYPE_ROOT
, PREFERENCES
);
4382 ASSERT_TRUE(pref_root
.good());
4384 syncable::Directory::Metahandles children
;
4385 directory()->GetChildHandlesById(&trans
, pref_root
.GetId(), &children
);
4386 ASSERT_EQ(2U, children
.size());
4390 TEST_F(SyncerTest
, ClientTagClashWithinBatchOfUpdates
) {
4391 // This test is written assuming that ID comparison
4392 // will work out in a particular way.
4393 EXPECT_TRUE(ids_
.FromNumber(1) < ids_
.FromNumber(4));
4394 EXPECT_TRUE(ids_
.FromNumber(201) < ids_
.FromNumber(205));
4396 // Least ID: winner.
4397 mock_server_
->AddUpdatePref(ids_
.FromNumber(1).GetServerId(), "", "tag a", 1,
4399 mock_server_
->AddUpdatePref(ids_
.FromNumber(2).GetServerId(), "", "tag a", 11,
4401 mock_server_
->AddUpdatePref(ids_
.FromNumber(3).GetServerId(), "", "tag a", 12,
4403 mock_server_
->AddUpdatePref(ids_
.FromNumber(4).GetServerId(), "", "tag a", 13,
4405 mock_server_
->AddUpdatePref(ids_
.FromNumber(105).GetServerId(), "", "tag b",
4407 mock_server_
->AddUpdatePref(ids_
.FromNumber(102).GetServerId(), "", "tag b",
4409 // Least ID: winner.
4410 mock_server_
->AddUpdatePref(ids_
.FromNumber(101).GetServerId(), "", "tag b",
4412 mock_server_
->AddUpdatePref(ids_
.FromNumber(104).GetServerId(), "", "tag b",
4415 mock_server_
->AddUpdatePref(ids_
.FromNumber(205).GetServerId(), "", "tag c",
4417 mock_server_
->AddUpdatePref(ids_
.FromNumber(202).GetServerId(), "", "tag c",
4419 mock_server_
->AddUpdatePref(ids_
.FromNumber(204).GetServerId(), "", "tag c",
4421 // Least ID: winner.
4422 mock_server_
->AddUpdatePref(ids_
.FromNumber(201).GetServerId(), "", "tag c",
4425 mock_server_
->set_conflict_all_commits(true);
4427 EXPECT_TRUE(SyncShareNudge());
4428 // This should cause client tag overwrite.
4430 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4432 Entry
tag_a(&trans
, GET_BY_CLIENT_TAG
, "tag a");
4433 ASSERT_TRUE(tag_a
.good());
4434 EXPECT_TRUE(tag_a
.GetId().ServerKnows());
4435 EXPECT_EQ(ids_
.FromNumber(1), tag_a
.GetId());
4436 EXPECT_FALSE(tag_a
.GetIsDel());
4437 EXPECT_FALSE(tag_a
.GetIsUnappliedUpdate());
4438 EXPECT_FALSE(tag_a
.GetIsUnsynced());
4439 EXPECT_EQ(1, tag_a
.GetBaseVersion());
4440 EXPECT_EQ("tag a", tag_a
.GetUniqueClientTag());
4442 Entry
tag_b(&trans
, GET_BY_CLIENT_TAG
, "tag b");
4443 ASSERT_TRUE(tag_b
.good());
4444 EXPECT_TRUE(tag_b
.GetId().ServerKnows());
4445 EXPECT_EQ(ids_
.FromNumber(101), tag_b
.GetId());
4446 EXPECT_FALSE(tag_b
.GetIsDel());
4447 EXPECT_FALSE(tag_b
.GetIsUnappliedUpdate());
4448 EXPECT_FALSE(tag_b
.GetIsUnsynced());
4449 EXPECT_EQ(16, tag_b
.GetBaseVersion());
4450 EXPECT_EQ("tag b", tag_b
.GetUniqueClientTag());
4452 Entry
tag_c(&trans
, GET_BY_CLIENT_TAG
, "tag c");
4453 ASSERT_TRUE(tag_c
.good());
4454 EXPECT_TRUE(tag_c
.GetId().ServerKnows());
4455 EXPECT_EQ(ids_
.FromNumber(201), tag_c
.GetId());
4456 EXPECT_FALSE(tag_c
.GetIsDel());
4457 EXPECT_FALSE(tag_c
.GetIsUnappliedUpdate());
4458 EXPECT_FALSE(tag_c
.GetIsUnsynced());
4459 EXPECT_EQ(21, tag_c
.GetBaseVersion());
4460 EXPECT_EQ("tag c", tag_c
.GetUniqueClientTag());
4462 // Preferences type root should have been created by the updates above.
4463 Entry
pref_root(&trans
, GET_TYPE_ROOT
, PREFERENCES
);
4464 ASSERT_TRUE(pref_root
.good());
4466 // Verify that we have exactly 3 tagged nodes under the type root.
4467 syncable::Directory::Metahandles children
;
4468 directory()->GetChildHandlesById(&trans
, pref_root
.GetId(), &children
);
4469 ASSERT_EQ(3U, children
.size());
4473 // This verifies transition to implicit permanent folders.
4474 TEST_F(SyncerTest
, EntryWithParentIdUpdatedWithEntryWithoutParentId
) {
4475 // Make sure SPECIFICS root exists so that we can get its parent ID.
4476 mock_server_
->AddUpdateSpecifics(1, 0, "Folder", 10, 10, true, 1,
4477 DefaultPreferencesSpecifics());
4478 mock_server_
->SetLastUpdateServerTag(ModelTypeToRootTag(PREFERENCES
));
4479 EXPECT_TRUE(SyncShareNudge());
4483 // Preferences type root should have been created by the update above.
4484 // We need it in order to get its ID.
4485 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4486 Entry
pref_root(&trans
, GET_TYPE_ROOT
, PREFERENCES
);
4487 ASSERT_TRUE(pref_root
.good());
4488 pref_root_id
= pref_root
.GetId();
4491 // Add a preference item with explicit parent ID.
4493 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4494 MutableEntry
entry(&trans
, CREATE
, PREFERENCES
, pref_root_id
, "tag");
4495 ASSERT_TRUE(entry
.good());
4496 entry
.PutIsDir(false);
4497 entry
.PutBaseVersion(1);
4498 entry
.PutUniqueClientTag("tag");
4499 entry
.PutId(ids_
.FromNumber(2));
4502 // Verify the entry above.
4504 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4505 Entry
pref_entry(&trans
, GET_BY_CLIENT_TAG
, "tag");
4506 ASSERT_TRUE(pref_entry
.good());
4507 ASSERT_EQ(pref_root_id
, pref_entry
.GetParentId());
4510 // Make another update where the same item get updated, this time
4511 // with implicit parent ID.
4512 mock_server_
->AddUpdatePref(ids_
.FromNumber(2).GetServerId(), "", "tag", 2,
4515 EXPECT_TRUE(SyncShareNudge());
4518 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4519 Entry
pref_entry(&trans
, GET_BY_CLIENT_TAG
, "tag");
4520 ASSERT_TRUE(pref_entry
.good());
4521 ASSERT_TRUE(pref_entry
.GetParentId().IsNull());
4523 // Verify that there is still one node under the type root.
4524 syncable::Directory::Metahandles children
;
4525 directory()->GetChildHandlesById(&trans
, pref_root_id
, &children
);
4526 ASSERT_EQ(1U, children
.size());
4530 TEST_F(SyncerTest
, UniqueServerTagUpdates
) {
4531 // As a hurdle, introduce an item whose name is the same as the tag value
4533 int64 hurdle_handle
= CreateUnsyncedDirectory("bob", "id_bob");
4535 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4536 Entry
hurdle(&trans
, GET_BY_HANDLE
, hurdle_handle
);
4537 ASSERT_TRUE(hurdle
.good());
4538 ASSERT_TRUE(!hurdle
.GetIsDel());
4539 ASSERT_TRUE(hurdle
.GetUniqueServerTag().empty());
4540 ASSERT_TRUE(hurdle
.GetNonUniqueName()== "bob");
4542 // Try to lookup by the tagname. These should fail.
4543 Entry
tag_alpha(&trans
, GET_BY_SERVER_TAG
, "alpha");
4544 EXPECT_FALSE(tag_alpha
.good());
4545 Entry
tag_bob(&trans
, GET_BY_SERVER_TAG
, "bob");
4546 EXPECT_FALSE(tag_bob
.good());
4549 // Now download some tagged items as updates.
4550 mock_server_
->AddUpdateDirectory(
4551 1, 0, "update1", 1, 10, std::string(), std::string());
4552 mock_server_
->SetLastUpdateServerTag("alpha");
4553 mock_server_
->AddUpdateDirectory(
4554 2, 0, "update2", 2, 20, std::string(), std::string());
4555 mock_server_
->SetLastUpdateServerTag("bob");
4556 EXPECT_TRUE(SyncShareNudge());
4559 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4561 // The new items should be applied as new entries, and we should be able
4562 // to look them up by their tag values.
4563 Entry
tag_alpha(&trans
, GET_BY_SERVER_TAG
, "alpha");
4564 ASSERT_TRUE(tag_alpha
.good());
4565 ASSERT_TRUE(!tag_alpha
.GetIsDel());
4566 ASSERT_TRUE(tag_alpha
.GetUniqueServerTag()== "alpha");
4567 ASSERT_TRUE(tag_alpha
.GetNonUniqueName()== "update1");
4568 Entry
tag_bob(&trans
, GET_BY_SERVER_TAG
, "bob");
4569 ASSERT_TRUE(tag_bob
.good());
4570 ASSERT_TRUE(!tag_bob
.GetIsDel());
4571 ASSERT_TRUE(tag_bob
.GetUniqueServerTag()== "bob");
4572 ASSERT_TRUE(tag_bob
.GetNonUniqueName()== "update2");
4573 // The old item should be unchanged.
4574 Entry
hurdle(&trans
, GET_BY_HANDLE
, hurdle_handle
);
4575 ASSERT_TRUE(hurdle
.good());
4576 ASSERT_TRUE(!hurdle
.GetIsDel());
4577 ASSERT_TRUE(hurdle
.GetUniqueServerTag().empty());
4578 ASSERT_TRUE(hurdle
.GetNonUniqueName()== "bob");
4582 TEST_F(SyncerTest
, GetUpdatesSetsRequestedTypes
) {
4583 // The expectations of this test happen in the MockConnectionManager's
4584 // GetUpdates handler. EnableDatatype sets the expectation value from our
4585 // set of enabled/disabled datatypes.
4586 EnableDatatype(BOOKMARKS
);
4587 EXPECT_TRUE(SyncShareNudge());
4588 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4590 EnableDatatype(AUTOFILL
);
4591 EXPECT_TRUE(SyncShareNudge());
4592 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4594 EnableDatatype(PREFERENCES
);
4595 EXPECT_TRUE(SyncShareNudge());
4596 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4598 DisableDatatype(BOOKMARKS
);
4599 EXPECT_TRUE(SyncShareNudge());
4600 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4602 DisableDatatype(AUTOFILL
);
4603 EXPECT_TRUE(SyncShareNudge());
4604 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4606 DisableDatatype(PREFERENCES
);
4607 EnableDatatype(AUTOFILL
);
4608 EXPECT_TRUE(SyncShareNudge());
4609 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4612 // A typical scenario: server and client each have one update for the other.
4613 // This is the "happy path" alternative to UpdateFailsThenDontCommit.
4614 TEST_F(SyncerTest
, UpdateThenCommit
) {
4615 syncable::Id to_receive
= ids_
.NewServerId();
4616 syncable::Id to_commit
= ids_
.NewLocalId();
4618 mock_server_
->AddUpdateDirectory(to_receive
, ids_
.root(), "x", 1, 10,
4619 foreign_cache_guid(), "-1");
4620 int64 commit_handle
= CreateUnsyncedDirectory("y", to_commit
);
4621 EXPECT_TRUE(SyncShareNudge());
4623 // The sync cycle should have included a GetUpdate, then a commit. By the
4624 // time the commit happened, we should have known for sure that there were no
4625 // hierarchy conflicts, and reported this fact to the server.
4626 ASSERT_TRUE(mock_server_
->last_request().has_commit());
4627 VerifyNoHierarchyConflictsReported(mock_server_
->last_request());
4629 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4631 Entry
received(&trans
, GET_BY_ID
, to_receive
);
4632 ASSERT_TRUE(received
.good());
4633 EXPECT_FALSE(received
.GetIsUnsynced());
4634 EXPECT_FALSE(received
.GetIsUnappliedUpdate());
4636 Entry
committed(&trans
, GET_BY_HANDLE
, commit_handle
);
4637 ASSERT_TRUE(committed
.good());
4638 EXPECT_FALSE(committed
.GetIsUnsynced());
4639 EXPECT_FALSE(committed
.GetIsUnappliedUpdate());
4642 // Same as above, but this time we fail to download updates.
4643 // We should not attempt to commit anything unless we successfully downloaded
4644 // updates, otherwise we risk causing a server-side conflict.
4645 TEST_F(SyncerTest
, UpdateFailsThenDontCommit
) {
4646 syncable::Id to_receive
= ids_
.NewServerId();
4647 syncable::Id to_commit
= ids_
.NewLocalId();
4649 mock_server_
->AddUpdateDirectory(to_receive
, ids_
.root(), "x", 1, 10,
4650 foreign_cache_guid(), "-1");
4651 int64 commit_handle
= CreateUnsyncedDirectory("y", to_commit
);
4652 mock_server_
->FailNextPostBufferToPathCall();
4653 EXPECT_FALSE(SyncShareNudge());
4655 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4657 // We did not receive this update.
4658 Entry
received(&trans
, GET_BY_ID
, to_receive
);
4659 ASSERT_FALSE(received
.good());
4661 // And our local update remains unapplied.
4662 Entry
committed(&trans
, GET_BY_HANDLE
, commit_handle
);
4663 ASSERT_TRUE(committed
.good());
4664 EXPECT_TRUE(committed
.GetIsUnsynced());
4665 EXPECT_FALSE(committed
.GetIsUnappliedUpdate());
4667 // Inform the Mock we won't be fetching all updates.
4668 mock_server_
->ClearUpdatesQueue();
4671 // Downloads two updates and applies them successfully.
4672 // This is the "happy path" alternative to ConfigureFailsDontApplyUpdates.
4673 TEST_F(SyncerTest
, ConfigureDownloadsTwoBatchesSuccess
) {
4674 syncable::Id node1
= ids_
.NewServerId();
4675 syncable::Id node2
= ids_
.NewServerId();
4677 // Construct the first GetUpdates response.
4678 mock_server_
->AddUpdateDirectory(node1
, ids_
.root(), "one", 1, 10,
4679 foreign_cache_guid(), "-2");
4680 mock_server_
->SetChangesRemaining(1);
4681 mock_server_
->NextUpdateBatch();
4683 // Construct the second GetUpdates response.
4684 mock_server_
->AddUpdateDirectory(node2
, ids_
.root(), "two", 1, 20,
4685 foreign_cache_guid(), "-2");
4687 SyncShareConfigure();
4689 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4690 // Both nodes should be downloaded and applied.
4692 Entry
n1(&trans
, GET_BY_ID
, node1
);
4693 ASSERT_TRUE(n1
.good());
4694 EXPECT_FALSE(n1
.GetIsUnappliedUpdate());
4696 Entry
n2(&trans
, GET_BY_ID
, node2
);
4697 ASSERT_TRUE(n2
.good());
4698 EXPECT_FALSE(n2
.GetIsUnappliedUpdate());
4701 // Same as the above case, but this time the second batch fails to download.
4702 TEST_F(SyncerTest
, ConfigureFailsDontApplyUpdates
) {
4703 syncable::Id node1
= ids_
.NewServerId();
4704 syncable::Id node2
= ids_
.NewServerId();
4706 // The scenario: we have two batches of updates with one update each. A
4707 // normal confgure step would download all the updates one batch at a time and
4708 // apply them. This configure will succeed in downloading the first batch
4709 // then fail when downloading the second.
4710 mock_server_
->FailNthPostBufferToPathCall(2);
4712 // Construct the first GetUpdates response.
4713 mock_server_
->AddUpdateDirectory(node1
, ids_
.root(), "one", 1, 10,
4714 foreign_cache_guid(), "-1");
4715 mock_server_
->SetChangesRemaining(1);
4716 mock_server_
->NextUpdateBatch();
4718 // Consutrct the second GetUpdates response.
4719 mock_server_
->AddUpdateDirectory(node2
, ids_
.root(), "two", 1, 20,
4720 foreign_cache_guid(), "-2");
4722 SyncShareConfigure();
4724 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4726 // The first node was downloaded, but not applied.
4727 Entry
n1(&trans
, GET_BY_ID
, node1
);
4728 ASSERT_TRUE(n1
.good());
4729 EXPECT_TRUE(n1
.GetIsUnappliedUpdate());
4731 // The second node was not downloaded.
4732 Entry
n2(&trans
, GET_BY_ID
, node2
);
4733 EXPECT_FALSE(n2
.good());
4735 // One update remains undownloaded.
4736 mock_server_
->ClearUpdatesQueue();
4739 TEST_F(SyncerTest
, GetKeySuccess
) {
4741 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4742 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4745 SyncShareConfigure();
4747 EXPECT_EQ(session_
->status_controller().last_get_key_result(), SYNCER_OK
);
4749 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4750 EXPECT_FALSE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4754 TEST_F(SyncerTest
, GetKeyEmpty
) {
4756 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4757 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4760 mock_server_
->SetKeystoreKey(std::string());
4761 SyncShareConfigure();
4763 EXPECT_NE(session_
->status_controller().last_get_key_result(), SYNCER_OK
);
4765 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4766 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4770 // Tests specifically related to bookmark (and therefore no client tags) sync
4771 // logic. Entities without client tags have custom logic in parts of the code,
4772 // and hence are not covered by e.g. the Undeletion tests below.
4773 class SyncerBookmarksTest
: public SyncerTest
{
4775 SyncerBookmarksTest() : metahandle_(syncable::kInvalidMetaHandle
) {
4779 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4780 MutableEntry
bookmark(
4781 &trans
, CREATE
, BOOKMARKS
, ids_
.root(), "clientname");
4782 ASSERT_TRUE(bookmark
.good());
4783 bookmark
.PutSpecifics(DefaultBookmarkSpecifics());
4784 EXPECT_FALSE(bookmark
.GetIsUnappliedUpdate());
4785 EXPECT_FALSE(bookmark
.GetId().ServerKnows());
4786 metahandle_
= bookmark
.GetMetahandle();
4787 local_id_
= bookmark
.GetId();
4788 bookmark
.PutIsUnsynced(true);
4792 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4793 MutableEntry
bookmark(&trans
, GET_BY_ID
, local_id_
);
4794 ASSERT_TRUE(bookmark
.good());
4795 bookmark
.PutSpecifics(DefaultBookmarkSpecifics());
4796 EXPECT_FALSE(bookmark
.GetIsUnappliedUpdate());
4797 bookmark
.PutIsUnsynced(true);
4798 if (bookmark
.GetSyncing())
4799 bookmark
.PutDirtySync(true);
4803 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4804 MutableEntry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4805 ASSERT_TRUE(entry
.good());
4806 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4807 // The order of setting IS_UNSYNCED vs IS_DEL matters. See
4808 // WriteNode::Tombstone().
4809 entry
.PutIsUnsynced(true);
4810 if (entry
.GetSyncing())
4811 entry
.PutDirtySync(true);
4812 entry
.PutIsDel(true);
4815 void UpdateAndDelete() {
4821 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4822 MutableEntry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4823 ASSERT_TRUE(entry
.good());
4824 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4825 EXPECT_TRUE(entry
.GetIsDel());
4826 entry
.PutIsDel(false);
4827 entry
.PutIsUnsynced(true);
4828 if (entry
.GetSyncing())
4829 entry
.PutDirtySync(true);
4832 int64
GetMetahandleOfTag() {
4833 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4834 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4835 EXPECT_TRUE(entry
.good());
4836 if (!entry
.good()) {
4837 return syncable::kInvalidMetaHandle
;
4839 return entry
.GetMetahandle();
4843 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4844 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4845 EXPECT_TRUE(entry
.good());
4846 if (!entry
.good()) {
4849 return entry
.GetId();
4852 void ExpectUnsyncedCreation() {
4853 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4854 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4856 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4857 EXPECT_FALSE(entry
.GetIsDel());
4858 EXPECT_FALSE(entry
.GetServerIsDel()); // Never been committed.
4859 EXPECT_LT(entry
.GetBaseVersion(), 0);
4860 EXPECT_TRUE(entry
.GetIsUnsynced());
4861 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4864 void ExpectUnsyncedUndeletion() {
4865 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4866 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4868 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4869 EXPECT_FALSE(entry
.GetIsDel());
4870 EXPECT_TRUE(entry
.GetServerIsDel());
4871 EXPECT_GE(entry
.GetBaseVersion(), 0);
4872 EXPECT_TRUE(entry
.GetIsUnsynced());
4873 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4874 EXPECT_TRUE(entry
.GetId().ServerKnows());
4877 void ExpectUnsyncedEdit() {
4878 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4879 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4881 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4882 EXPECT_FALSE(entry
.GetIsDel());
4883 EXPECT_FALSE(entry
.GetServerIsDel());
4884 EXPECT_GE(entry
.GetBaseVersion(), 0);
4885 EXPECT_TRUE(entry
.GetIsUnsynced());
4886 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4887 EXPECT_TRUE(entry
.GetId().ServerKnows());
4890 void ExpectUnsyncedDeletion() {
4891 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4892 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4894 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4895 EXPECT_TRUE(entry
.GetIsDel());
4896 EXPECT_FALSE(entry
.GetServerIsDel());
4897 EXPECT_TRUE(entry
.GetIsUnsynced());
4898 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4899 EXPECT_GE(entry
.GetBaseVersion(), 0);
4900 EXPECT_GE(entry
.GetServerVersion(), 0);
4903 void ExpectSyncedAndCreated() {
4904 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4905 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4907 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4908 EXPECT_FALSE(entry
.GetIsDel());
4909 EXPECT_FALSE(entry
.GetServerIsDel());
4910 EXPECT_GE(entry
.GetBaseVersion(), 0);
4911 EXPECT_EQ(entry
.GetBaseVersion(), entry
.GetServerVersion());
4912 EXPECT_FALSE(entry
.GetIsUnsynced());
4913 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4916 void ExpectSyncedAndDeleted() {
4917 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4918 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4920 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4921 EXPECT_TRUE(entry
.GetIsDel());
4922 EXPECT_TRUE(entry
.GetServerIsDel());
4923 EXPECT_FALSE(entry
.GetIsUnsynced());
4924 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4925 EXPECT_GE(entry
.GetBaseVersion(), 0);
4926 EXPECT_GE(entry
.GetServerVersion(), 0);
4930 syncable::Id local_id_
;
4934 TEST_F(SyncerBookmarksTest
, CreateSyncThenDeleteSync
) {
4936 ExpectUnsyncedCreation();
4937 EXPECT_TRUE(SyncShareNudge());
4938 ExpectSyncedAndCreated();
4940 ExpectUnsyncedDeletion();
4941 EXPECT_TRUE(SyncShareNudge());
4942 ExpectSyncedAndDeleted();
4945 TEST_F(SyncerBookmarksTest
, CreateThenDeleteBeforeSync
) {
4947 ExpectUnsyncedCreation();
4950 // Deleting before the initial commit should result in not needing to send
4951 // the delete to the server. It will still be in an unsynced state, but with
4952 // IS_UNSYNCED set to false.
4954 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4955 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4957 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4958 EXPECT_TRUE(entry
.GetIsDel());
4959 EXPECT_FALSE(entry
.GetServerIsDel());
4960 EXPECT_FALSE(entry
.GetIsUnsynced());
4961 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4962 EXPECT_EQ(entry
.GetBaseVersion(), -1);
4963 EXPECT_EQ(entry
.GetServerVersion(), 0);
4967 TEST_F(SyncerBookmarksTest
, LocalDeleteRemoteChangeConflict
) {
4969 ExpectUnsyncedCreation();
4970 EXPECT_TRUE(SyncShareNudge());
4971 ExpectSyncedAndCreated();
4973 ExpectUnsyncedDeletion();
4975 // Trigger a getupdates that modifies the bookmark. The update should be
4976 // clobbered by the local delete.
4977 mock_server_
->AddUpdateBookmark(GetServerId(), Id::GetRoot(), "dummy", 10, 10,
4978 local_cache_guid(), local_id_
.GetServerId());
4980 EXPECT_TRUE(SyncShareNudge());
4981 ExpectSyncedAndDeleted();
4984 TEST_F(SyncerBookmarksTest
, CreateThenDeleteDuringCommit
) {
4986 ExpectUnsyncedCreation();
4988 // In the middle of the initial creation commit, perform a deletion.
4989 // This should trigger performing two consecutive commit cycles, resulting
4990 // in the bookmark being both deleted and synced.
4991 mock_server_
->SetMidCommitCallback(
4992 base::Bind(&SyncerBookmarksTest::Delete
, base::Unretained(this)));
4994 EXPECT_TRUE(SyncShareNudge());
4995 ExpectSyncedAndDeleted();
4998 TEST_F(SyncerBookmarksTest
, CreateThenUpdateAndDeleteDuringCommit
) {
5000 ExpectUnsyncedCreation();
5002 // In the middle of the initial creation commit, perform an updated followed
5003 // by a deletion. This should trigger performing two consecutive commit
5004 // cycles, resulting in the bookmark being both deleted and synced.
5005 mock_server_
->SetMidCommitCallback(base::Bind(
5006 &SyncerBookmarksTest::UpdateAndDelete
, base::Unretained(this)));
5008 EXPECT_TRUE(SyncShareNudge());
5009 ExpectSyncedAndDeleted();
5012 // Test what happens if a client deletes, then recreates, an object very
5013 // quickly. It is possible that the deletion gets sent as a commit, and
5014 // the undelete happens during the commit request. The principle here
5015 // is that with a single committing client, conflicts should never
5016 // be encountered, and a client encountering its past actions during
5017 // getupdates should never feed back to override later actions.
5019 // In cases of ordering A-F below, the outcome should be the same.
5020 // Exercised by UndeleteDuringCommit:
5021 // A. Delete - commit - undelete - commitresponse.
5022 // B. Delete - commit - undelete - commitresponse - getupdates.
5023 // Exercised by UndeleteBeforeCommit:
5024 // C. Delete - undelete - commit - commitresponse.
5025 // D. Delete - undelete - commit - commitresponse - getupdates.
5026 // Exercised by UndeleteAfterCommit:
5027 // E. Delete - commit - commitresponse - undelete - commit
5028 // - commitresponse.
5029 // F. Delete - commit - commitresponse - undelete - commit -
5030 // - commitresponse - getupdates.
5031 class SyncerUndeletionTest
: public SyncerTest
{
5033 SyncerUndeletionTest()
5034 : client_tag_("foobar"),
5035 metahandle_(syncable::kInvalidMetaHandle
) {
5039 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
5040 MutableEntry
perm_folder(
5041 &trans
, CREATE
, PREFERENCES
, ids_
.root(), "clientname");
5042 ASSERT_TRUE(perm_folder
.good());
5043 perm_folder
.PutUniqueClientTag(client_tag_
);
5044 perm_folder
.PutIsUnsynced(true);
5045 if (perm_folder
.GetSyncing())
5046 perm_folder
.PutDirtySync(true);
5047 perm_folder
.PutSpecifics(DefaultPreferencesSpecifics());
5048 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
5049 EXPECT_FALSE(perm_folder
.GetId().ServerKnows());
5050 metahandle_
= perm_folder
.GetMetahandle();
5051 local_id_
= perm_folder
.GetId();
5055 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
5056 MutableEntry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
5057 ASSERT_TRUE(entry
.good());
5058 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
5059 // The order of setting IS_UNSYNCED vs IS_DEL matters. See
5060 // WriteNode::Tombstone().
5061 entry
.PutIsUnsynced(true);
5062 if (entry
.GetSyncing())
5063 entry
.PutDirtySync(true);
5064 entry
.PutIsDel(true);
5068 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
5069 MutableEntry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
5070 ASSERT_TRUE(entry
.good());
5071 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
5072 EXPECT_TRUE(entry
.GetIsDel());
5073 entry
.PutIsDel(false);
5074 entry
.PutIsUnsynced(true);
5075 if (entry
.GetSyncing())
5076 entry
.PutDirtySync(true);
5079 int64
GetMetahandleOfTag() {
5080 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5081 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
5082 EXPECT_TRUE(entry
.good());
5083 if (!entry
.good()) {
5084 return syncable::kInvalidMetaHandle
;
5086 return entry
.GetMetahandle();
5089 void ExpectUnsyncedCreation() {
5090 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5091 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
5093 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
5094 EXPECT_FALSE(entry
.GetIsDel());
5095 EXPECT_FALSE(entry
.GetServerIsDel()); // Never been committed.
5096 EXPECT_LT(entry
.GetBaseVersion(), 0);
5097 EXPECT_TRUE(entry
.GetIsUnsynced());
5098 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
5101 void ExpectUnsyncedUndeletion() {
5102 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5103 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
5105 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
5106 EXPECT_FALSE(entry
.GetIsDel());
5107 EXPECT_TRUE(entry
.GetServerIsDel());
5108 EXPECT_GE(entry
.GetBaseVersion(), 0);
5109 EXPECT_TRUE(entry
.GetIsUnsynced());
5110 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
5111 EXPECT_TRUE(entry
.GetId().ServerKnows());
5114 void ExpectUnsyncedEdit() {
5115 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5116 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
5118 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
5119 EXPECT_FALSE(entry
.GetIsDel());
5120 EXPECT_FALSE(entry
.GetServerIsDel());
5121 EXPECT_GE(entry
.GetBaseVersion(), 0);
5122 EXPECT_TRUE(entry
.GetIsUnsynced());
5123 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
5124 EXPECT_TRUE(entry
.GetId().ServerKnows());
5127 void ExpectUnsyncedDeletion() {
5128 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5129 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
5131 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
5132 EXPECT_TRUE(entry
.GetIsDel());
5133 EXPECT_FALSE(entry
.GetServerIsDel());
5134 EXPECT_TRUE(entry
.GetIsUnsynced());
5135 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
5136 EXPECT_GE(entry
.GetBaseVersion(), 0);
5137 EXPECT_GE(entry
.GetServerVersion(), 0);
5140 void ExpectSyncedAndCreated() {
5141 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5142 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
5144 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
5145 EXPECT_FALSE(entry
.GetIsDel());
5146 EXPECT_FALSE(entry
.GetServerIsDel());
5147 EXPECT_GE(entry
.GetBaseVersion(), 0);
5148 EXPECT_EQ(entry
.GetBaseVersion(), entry
.GetServerVersion());
5149 EXPECT_FALSE(entry
.GetIsUnsynced());
5150 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
5153 void ExpectSyncedAndDeleted() {
5154 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5155 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
5157 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
5158 EXPECT_TRUE(entry
.GetIsDel());
5159 EXPECT_TRUE(entry
.GetServerIsDel());
5160 EXPECT_FALSE(entry
.GetIsUnsynced());
5161 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
5162 EXPECT_GE(entry
.GetBaseVersion(), 0);
5163 EXPECT_GE(entry
.GetServerVersion(), 0);
5167 const std::string client_tag_
;
5168 syncable::Id local_id_
;
5172 TEST_F(SyncerUndeletionTest
, UndeleteDuringCommit
) {
5174 ExpectUnsyncedCreation();
5175 EXPECT_TRUE(SyncShareNudge());
5177 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5178 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5179 ExpectSyncedAndCreated();
5181 // Delete, begin committing the delete, then undelete while committing.
5183 ExpectUnsyncedDeletion();
5184 mock_server_
->SetMidCommitCallback(
5185 base::Bind(&SyncerUndeletionTest::Undelete
, base::Unretained(this)));
5186 EXPECT_TRUE(SyncShareNudge());
5188 // We will continue to commit until all nodes are synced, so we expect
5189 // that both the delete and following undelete were committed. We haven't
5190 // downloaded any updates, though, so the SERVER fields will be the same
5191 // as they were at the start of the cycle.
5192 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5193 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5196 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5197 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5199 // Server fields lag behind.
5200 EXPECT_FALSE(entry
.GetServerIsDel());
5202 // We have committed the second (undelete) update.
5203 EXPECT_FALSE(entry
.GetIsDel());
5204 EXPECT_FALSE(entry
.GetIsUnsynced());
5205 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
5208 // Now, encounter a GetUpdates corresponding to the deletion from
5209 // the server. The undeletion should prevail again and be committed.
5210 // None of this should trigger any conflict detection -- it is perfectly
5211 // normal to recieve updates from our own commits.
5212 mock_server_
->SetMidCommitCallback(base::Closure());
5213 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5214 update
->set_originator_cache_guid(local_cache_guid());
5215 update
->set_originator_client_item_id(local_id_
.GetServerId());
5217 EXPECT_TRUE(SyncShareNudge());
5218 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5219 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5220 ExpectSyncedAndCreated();
5223 TEST_F(SyncerUndeletionTest
, UndeleteBeforeCommit
) {
5225 ExpectUnsyncedCreation();
5226 EXPECT_TRUE(SyncShareNudge());
5228 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5229 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5230 ExpectSyncedAndCreated();
5232 // Delete and undelete, then sync to pick up the result.
5234 ExpectUnsyncedDeletion();
5236 ExpectUnsyncedEdit(); // Edit, not undelete: server thinks it exists.
5237 EXPECT_TRUE(SyncShareNudge());
5239 // The item ought to have committed successfully.
5240 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5241 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5242 ExpectSyncedAndCreated();
5244 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5245 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5246 EXPECT_EQ(2, entry
.GetBaseVersion());
5249 // Now, encounter a GetUpdates corresponding to the just-committed
5251 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5252 update
->set_originator_cache_guid(local_cache_guid());
5253 update
->set_originator_client_item_id(local_id_
.GetServerId());
5254 EXPECT_TRUE(SyncShareNudge());
5255 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5256 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5257 ExpectSyncedAndCreated();
5260 TEST_F(SyncerUndeletionTest
, UndeleteAfterCommitButBeforeGetUpdates
) {
5262 ExpectUnsyncedCreation();
5263 EXPECT_TRUE(SyncShareNudge());
5265 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5266 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5267 ExpectSyncedAndCreated();
5269 // Delete and commit.
5271 ExpectUnsyncedDeletion();
5272 EXPECT_TRUE(SyncShareNudge());
5274 // The item ought to have committed successfully.
5275 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5276 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5277 ExpectSyncedAndDeleted();
5279 // Before the GetUpdates, the item is locally undeleted.
5281 ExpectUnsyncedUndeletion();
5283 // Now, encounter a GetUpdates corresponding to the just-committed
5284 // deletion update. The undeletion should prevail.
5285 mock_server_
->AddUpdateFromLastCommit();
5286 EXPECT_TRUE(SyncShareNudge());
5287 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5288 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5289 ExpectSyncedAndCreated();
5292 TEST_F(SyncerUndeletionTest
, UndeleteAfterDeleteAndGetUpdates
) {
5294 ExpectUnsyncedCreation();
5295 EXPECT_TRUE(SyncShareNudge());
5297 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5298 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5299 ExpectSyncedAndCreated();
5301 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5302 update
->set_originator_cache_guid(local_cache_guid());
5303 update
->set_originator_client_item_id(local_id_
.GetServerId());
5304 EXPECT_TRUE(SyncShareNudge());
5305 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5306 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5307 ExpectSyncedAndCreated();
5309 // Delete and commit.
5311 ExpectUnsyncedDeletion();
5312 EXPECT_TRUE(SyncShareNudge());
5314 // The item ought to have committed successfully.
5315 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5316 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5317 ExpectSyncedAndDeleted();
5319 // Now, encounter a GetUpdates corresponding to the just-committed
5320 // deletion update. Should be consistent.
5321 mock_server_
->AddUpdateFromLastCommit();
5322 EXPECT_TRUE(SyncShareNudge());
5323 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5324 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5325 ExpectSyncedAndDeleted();
5327 // After the GetUpdates, the item is locally undeleted.
5329 ExpectUnsyncedUndeletion();
5331 // Now, encounter a GetUpdates corresponding to the just-committed
5332 // deletion update. The undeletion should prevail.
5333 EXPECT_TRUE(SyncShareNudge());
5334 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5335 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5336 ExpectSyncedAndCreated();
5339 // Test processing of undeletion GetUpdateses.
5340 TEST_F(SyncerUndeletionTest
, UndeleteAfterOtherClientDeletes
) {
5342 ExpectUnsyncedCreation();
5343 EXPECT_TRUE(SyncShareNudge());
5345 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5346 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5347 ExpectSyncedAndCreated();
5349 // Add a delete from the server.
5350 sync_pb::SyncEntity
* update1
= mock_server_
->AddUpdateFromLastCommit();
5351 update1
->set_originator_cache_guid(local_cache_guid());
5352 update1
->set_originator_client_item_id(local_id_
.GetServerId());
5353 EXPECT_TRUE(SyncShareNudge());
5354 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5355 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5356 ExpectSyncedAndCreated();
5358 // Some other client deletes the item.
5360 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5361 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5362 mock_server_
->AddUpdateTombstone(entry
.GetId(), PREFERENCES
);
5364 EXPECT_TRUE(SyncShareNudge());
5366 // The update ought to have applied successfully.
5367 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5368 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5369 ExpectSyncedAndDeleted();
5371 // Undelete it locally.
5373 ExpectUnsyncedUndeletion();
5374 EXPECT_TRUE(SyncShareNudge());
5375 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5376 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5377 ExpectSyncedAndCreated();
5379 // Now, encounter a GetUpdates corresponding to the just-committed
5380 // deletion update. The undeletion should prevail.
5381 sync_pb::SyncEntity
* update2
= mock_server_
->AddUpdateFromLastCommit();
5382 update2
->set_originator_cache_guid(local_cache_guid());
5383 update2
->set_originator_client_item_id(local_id_
.GetServerId());
5384 EXPECT_TRUE(SyncShareNudge());
5385 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5386 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5387 ExpectSyncedAndCreated();
5390 TEST_F(SyncerUndeletionTest
, UndeleteAfterOtherClientDeletesImmediately
) {
5392 ExpectUnsyncedCreation();
5393 EXPECT_TRUE(SyncShareNudge());
5395 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5396 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5397 ExpectSyncedAndCreated();
5399 // Some other client deletes the item before we get a chance
5400 // to GetUpdates our original request.
5402 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5403 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5404 mock_server_
->AddUpdateTombstone(entry
.GetId(), PREFERENCES
);
5406 EXPECT_TRUE(SyncShareNudge());
5408 // The update ought to have applied successfully.
5409 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5410 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5411 ExpectSyncedAndDeleted();
5413 // Undelete it locally.
5415 ExpectUnsyncedUndeletion();
5416 EXPECT_TRUE(SyncShareNudge());
5417 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5418 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5419 ExpectSyncedAndCreated();
5421 // Now, encounter a GetUpdates corresponding to the just-committed
5422 // deletion update. The undeletion should prevail.
5423 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5424 update
->set_originator_cache_guid(local_cache_guid());
5425 update
->set_originator_client_item_id(local_id_
.GetServerId());
5426 EXPECT_TRUE(SyncShareNudge());
5427 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5428 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5429 ExpectSyncedAndCreated();
5432 TEST_F(SyncerUndeletionTest
, OtherClientUndeletes
) {
5434 ExpectUnsyncedCreation();
5435 EXPECT_TRUE(SyncShareNudge());
5437 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5438 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5439 ExpectSyncedAndCreated();
5441 // Get the updates of our just-committed entry.
5442 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5443 update
->set_originator_cache_guid(local_cache_guid());
5444 update
->set_originator_client_item_id(local_id_
.GetServerId());
5445 EXPECT_TRUE(SyncShareNudge());
5446 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5447 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5448 ExpectSyncedAndCreated();
5450 // We delete the item.
5452 ExpectUnsyncedDeletion();
5453 EXPECT_TRUE(SyncShareNudge());
5455 // The update ought to have applied successfully.
5456 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5457 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5458 ExpectSyncedAndDeleted();
5460 // Now, encounter a GetUpdates corresponding to the just-committed
5462 mock_server_
->AddUpdateFromLastCommit();
5463 EXPECT_TRUE(SyncShareNudge());
5464 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5465 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5466 ExpectSyncedAndDeleted();
5468 // Some other client undeletes the item.
5470 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5471 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5472 mock_server_
->AddUpdatePref(
5473 entry
.GetId().GetServerId(),
5474 entry
.GetParentId().GetServerId(),
5475 client_tag_
, 100, 1000);
5477 mock_server_
->SetLastUpdateClientTag(client_tag_
);
5478 EXPECT_TRUE(SyncShareNudge());
5479 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5480 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5481 ExpectSyncedAndCreated();
5484 TEST_F(SyncerUndeletionTest
, OtherClientUndeletesImmediately
) {
5486 ExpectUnsyncedCreation();
5487 EXPECT_TRUE(SyncShareNudge());
5489 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5490 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5491 ExpectSyncedAndCreated();
5493 // Get the updates of our just-committed entry.
5494 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5495 update
->set_originator_cache_guid(local_cache_guid());
5497 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5498 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5499 update
->set_originator_client_item_id(local_id_
.GetServerId());
5501 EXPECT_TRUE(SyncShareNudge());
5502 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5503 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5504 ExpectSyncedAndCreated();
5506 // We delete the item.
5508 ExpectUnsyncedDeletion();
5509 EXPECT_TRUE(SyncShareNudge());
5511 // The update ought to have applied successfully.
5512 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5513 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5514 ExpectSyncedAndDeleted();
5516 // Some other client undeletes before we see the update from our
5519 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5520 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5521 mock_server_
->AddUpdatePref(
5522 entry
.GetId().GetServerId(),
5523 entry
.GetParentId().GetServerId(),
5524 client_tag_
, 100, 1000);
5526 mock_server_
->SetLastUpdateClientTag(client_tag_
);
5527 EXPECT_TRUE(SyncShareNudge());
5528 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5529 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5530 ExpectSyncedAndCreated();
5534 TEST_PARAM_BOOKMARK_ENABLE_BIT
,
5535 TEST_PARAM_AUTOFILL_ENABLE_BIT
,
5536 TEST_PARAM_BIT_COUNT
5541 public ::testing::WithParamInterface
<int> {
5543 bool ShouldFailBookmarkCommit() {
5544 return (GetParam() & (1 << TEST_PARAM_BOOKMARK_ENABLE_BIT
)) == 0;
5546 bool ShouldFailAutofillCommit() {
5547 return (GetParam() & (1 << TEST_PARAM_AUTOFILL_ENABLE_BIT
)) == 0;
5551 INSTANTIATE_TEST_CASE_P(ExtensionsActivity
,
5553 testing::Range(0, 1 << TEST_PARAM_BIT_COUNT
));
5555 TEST_P(MixedResult
, ExtensionsActivity
) {
5557 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
5559 MutableEntry
pref(&wtrans
, CREATE
, PREFERENCES
, wtrans
.root_id(), "pref");
5560 ASSERT_TRUE(pref
.good());
5561 pref
.PutIsUnsynced(true);
5563 MutableEntry
bookmark(
5564 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "bookmark");
5565 ASSERT_TRUE(bookmark
.good());
5566 bookmark
.PutIsUnsynced(true);
5568 if (ShouldFailBookmarkCommit()) {
5569 mock_server_
->SetTransientErrorId(bookmark
.GetId());
5572 if (ShouldFailAutofillCommit()) {
5573 mock_server_
->SetTransientErrorId(pref
.GetId());
5578 // Put some extenions activity records into the monitor.
5580 ExtensionsActivity::Records records
;
5581 records
["ABC"].extension_id
= "ABC";
5582 records
["ABC"].bookmark_write_count
= 2049U;
5583 records
["xyz"].extension_id
= "xyz";
5584 records
["xyz"].bookmark_write_count
= 4U;
5585 context_
->extensions_activity()->PutRecords(records
);
5588 EXPECT_EQ(!ShouldFailBookmarkCommit() && !ShouldFailAutofillCommit(),
5591 ExtensionsActivity::Records final_monitor_records
;
5592 context_
->extensions_activity()->GetAndClearRecords(&final_monitor_records
);
5593 if (ShouldFailBookmarkCommit()) {
5594 ASSERT_EQ(2U, final_monitor_records
.size())
5595 << "Should restore records after unsuccessful bookmark commit.";
5596 EXPECT_EQ("ABC", final_monitor_records
["ABC"].extension_id
);
5597 EXPECT_EQ("xyz", final_monitor_records
["xyz"].extension_id
);
5598 EXPECT_EQ(2049U, final_monitor_records
["ABC"].bookmark_write_count
);
5599 EXPECT_EQ(4U, final_monitor_records
["xyz"].bookmark_write_count
);
5601 EXPECT_TRUE(final_monitor_records
.empty())
5602 << "Should not restore records after successful bookmark commit.";
5606 } // namespace syncer