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/time/time.h"
24 #include "build/build_config.h"
25 #include "sync/engine/backoff_delay_provider.h"
26 #include "sync/engine/get_commit_ids.h"
27 #include "sync/engine/net/server_connection_manager.h"
28 #include "sync/engine/sync_scheduler_impl.h"
29 #include "sync/engine/syncer.h"
30 #include "sync/engine/syncer_proto_util.h"
31 #include "sync/internal_api/public/base/cancelation_signal.h"
32 #include "sync/internal_api/public/base/model_type.h"
33 #include "sync/internal_api/public/engine/model_safe_worker.h"
34 #include "sync/internal_api/public/sessions/commit_counters.h"
35 #include "sync/internal_api/public/sessions/status_counters.h"
36 #include "sync/internal_api/public/sessions/update_counters.h"
37 #include "sync/protocol/bookmark_specifics.pb.h"
38 #include "sync/protocol/nigori_specifics.pb.h"
39 #include "sync/protocol/preference_specifics.pb.h"
40 #include "sync/protocol/sync.pb.h"
41 #include "sync/sessions/sync_session_context.h"
42 #include "sync/syncable/mutable_entry.h"
43 #include "sync/syncable/nigori_util.h"
44 #include "sync/syncable/syncable_delete_journal.h"
45 #include "sync/syncable/syncable_read_transaction.h"
46 #include "sync/syncable/syncable_util.h"
47 #include "sync/syncable/syncable_write_transaction.h"
48 #include "sync/test/engine/fake_model_worker.h"
49 #include "sync/test/engine/mock_connection_manager.h"
50 #include "sync/test/engine/mock_nudge_handler.h"
51 #include "sync/test/engine/test_directory_setter_upper.h"
52 #include "sync/test/engine/test_id_factory.h"
53 #include "sync/test/engine/test_syncable_utils.h"
54 #include "sync/test/fake_encryptor.h"
55 #include "sync/test/fake_sync_encryption_handler.h"
56 #include "sync/test/sessions/mock_debug_info_getter.h"
57 #include "sync/util/cryptographer.h"
58 #include "sync/util/extensions_activity.h"
59 #include "sync/util/time.h"
60 #include "testing/gtest/include/gtest/gtest.h"
62 using base::TimeDelta
;
73 using syncable::BaseTransaction
;
74 using syncable::CountEntriesWithName
;
75 using syncable::Directory
;
76 using syncable::Entry
;
77 using syncable::GetFirstEntryWithName
;
78 using syncable::GetOnlyEntryWithName
;
80 using syncable::kEncryptedString
;
81 using syncable::MutableEntry
;
82 using syncable::WriteTransaction
;
84 using syncable::CREATE
;
85 using syncable::GET_BY_HANDLE
;
86 using syncable::GET_BY_ID
;
87 using syncable::GET_BY_CLIENT_TAG
;
88 using syncable::GET_BY_SERVER_TAG
;
89 using syncable::GET_TYPE_ROOT
;
90 using syncable::UNITTEST
;
92 using sessions::MockDebugInfoGetter
;
93 using sessions::StatusController
;
94 using sessions::SyncSessionContext
;
95 using sessions::SyncSession
;
99 // A helper to hold on to the counters emitted by the sync engine.
100 class TypeDebugInfoCache
: public TypeDebugInfoObserver
{
102 TypeDebugInfoCache();
103 ~TypeDebugInfoCache() override
;
105 CommitCounters
GetLatestCommitCounters(ModelType type
) const;
106 UpdateCounters
GetLatestUpdateCounters(ModelType type
) const;
107 StatusCounters
GetLatestStatusCounters(ModelType type
) const;
109 // TypeDebugInfoObserver implementation.
110 void OnCommitCountersUpdated(syncer::ModelType type
,
111 const CommitCounters
& counters
) override
;
112 void OnUpdateCountersUpdated(syncer::ModelType type
,
113 const UpdateCounters
& counters
) override
;
114 void OnStatusCountersUpdated(syncer::ModelType type
,
115 const StatusCounters
& counters
) override
;
118 std::map
<ModelType
, CommitCounters
> commit_counters_map_
;
119 std::map
<ModelType
, UpdateCounters
> update_counters_map_
;
120 std::map
<ModelType
, StatusCounters
> status_counters_map_
;
123 TypeDebugInfoCache::TypeDebugInfoCache() {}
125 TypeDebugInfoCache::~TypeDebugInfoCache() {}
127 CommitCounters
TypeDebugInfoCache::GetLatestCommitCounters(
128 ModelType type
) const {
129 std::map
<ModelType
, CommitCounters
>::const_iterator it
=
130 commit_counters_map_
.find(type
);
131 if (it
== commit_counters_map_
.end()) {
132 return CommitCounters();
138 UpdateCounters
TypeDebugInfoCache::GetLatestUpdateCounters(
139 ModelType type
) const {
140 std::map
<ModelType
, UpdateCounters
>::const_iterator it
=
141 update_counters_map_
.find(type
);
142 if (it
== update_counters_map_
.end()) {
143 return UpdateCounters();
149 StatusCounters
TypeDebugInfoCache::GetLatestStatusCounters(
150 ModelType type
) const {
151 std::map
<ModelType
, StatusCounters
>::const_iterator it
=
152 status_counters_map_
.find(type
);
153 if (it
== status_counters_map_
.end()) {
154 return StatusCounters();
160 void TypeDebugInfoCache::OnCommitCountersUpdated(
161 syncer::ModelType type
,
162 const CommitCounters
& counters
) {
163 commit_counters_map_
[type
] = counters
;
166 void TypeDebugInfoCache::OnUpdateCountersUpdated(
167 syncer::ModelType type
,
168 const UpdateCounters
& counters
) {
169 update_counters_map_
[type
] = counters
;
172 void TypeDebugInfoCache::OnStatusCountersUpdated(
173 syncer::ModelType type
,
174 const StatusCounters
& counters
) {
175 status_counters_map_
[type
] = counters
;
180 class SyncerTest
: public testing::Test
,
181 public SyncSession::Delegate
,
182 public SyncEngineEventListener
{
185 : extensions_activity_(new ExtensionsActivity
),
187 saw_syncer_event_(false),
188 last_client_invalidation_hint_buffer_size_(10) {
191 // SyncSession::Delegate implementation.
192 void OnThrottled(const base::TimeDelta
& throttle_duration
) override
{
193 FAIL() << "Should not get silenced.";
195 void OnTypesThrottled(ModelTypeSet types
,
196 const base::TimeDelta
& throttle_duration
) override
{
197 scheduler_
->OnTypesThrottled(types
, throttle_duration
);
199 bool IsCurrentlyThrottled() override
{ return false; }
200 void OnReceivedLongPollIntervalUpdate(
201 const base::TimeDelta
& new_interval
) override
{
202 last_long_poll_interval_received_
= new_interval
;
204 void OnReceivedShortPollIntervalUpdate(
205 const base::TimeDelta
& new_interval
) override
{
206 last_short_poll_interval_received_
= new_interval
;
208 void OnReceivedCustomNudgeDelays(
209 const std::map
<ModelType
, base::TimeDelta
>& delay_map
) override
{
210 std::map
<ModelType
, base::TimeDelta
>::const_iterator iter
=
211 delay_map
.find(SESSIONS
);
212 if (iter
!= delay_map
.end() && iter
->second
> base::TimeDelta())
213 last_sessions_commit_delay_
= iter
->second
;
214 iter
= delay_map
.find(BOOKMARKS
);
215 if (iter
!= delay_map
.end() && iter
->second
> base::TimeDelta())
216 last_bookmarks_commit_delay_
= iter
->second
;
218 void OnReceivedClientInvalidationHintBufferSize(int size
) override
{
219 last_client_invalidation_hint_buffer_size_
= size
;
221 void OnReceivedGuRetryDelay(const base::TimeDelta
& delay
) override
{}
222 void OnReceivedMigrationRequest(ModelTypeSet types
) override
{}
223 void OnProtocolEvent(const ProtocolEvent
& event
) override
{}
224 void OnSyncProtocolError(const SyncProtocolError
& error
) override
{}
226 void GetModelSafeRoutingInfo(ModelSafeRoutingInfo
* out
) {
227 // We're just testing the sync engine here, so we shunt everything to
228 // the SyncerThread. Datatypes which aren't enabled aren't in the map.
229 for (ModelTypeSet::Iterator it
= enabled_datatypes_
.First();
230 it
.Good(); it
.Inc()) {
231 (*out
)[it
.Get()] = GROUP_PASSIVE
;
235 void OnSyncCycleEvent(const SyncCycleEvent
& event
) override
{
236 DVLOG(1) << "HandleSyncEngineEvent in unittest " << event
.what_happened
;
237 // we only test for entry-specific events, not status changed ones.
238 switch (event
.what_happened
) {
239 case SyncCycleEvent::SYNC_CYCLE_BEGIN
: // Fall through.
240 case SyncCycleEvent::STATUS_CHANGED
:
241 case SyncCycleEvent::SYNC_CYCLE_ENDED
:
244 CHECK(false) << "Handling unknown error type in unit tests!!";
246 saw_syncer_event_
= true;
249 void OnActionableError(const SyncProtocolError
& error
) override
{}
250 void OnRetryTimeChanged(base::Time retry_time
) override
{}
251 void OnThrottledTypesChanged(ModelTypeSet throttled_types
) override
{}
252 void OnMigrationRequested(ModelTypeSet types
) override
{}
254 void ResetSession() {
255 session_
.reset(SyncSession::Build(context_
.get(), this));
258 bool SyncShareNudge() {
261 // Pretend we've seen a local change, to make the nudge_tracker look normal.
262 nudge_tracker_
.RecordLocalChange(ModelTypeSet(BOOKMARKS
));
264 return syncer_
->NormalSyncShare(context_
->GetEnabledTypes(),
265 &nudge_tracker_
, session_
.get());
268 bool SyncShareConfigure() {
270 return syncer_
->ConfigureSyncShare(
271 context_
->GetEnabledTypes(),
272 sync_pb::GetUpdatesCallerInfo::RECONFIGURATION
,
276 void SetUp() override
{
278 mock_server_
.reset(new MockConnectionManager(directory(),
279 &cancelation_signal_
));
280 debug_info_getter_
.reset(new MockDebugInfoGetter
);
281 EnableDatatype(BOOKMARKS
);
282 EnableDatatype(NIGORI
);
283 EnableDatatype(PREFERENCES
);
284 EnableDatatype(NIGORI
);
285 workers_
.push_back(scoped_refptr
<ModelSafeWorker
>(
286 new FakeModelWorker(GROUP_PASSIVE
)));
287 std::vector
<SyncEngineEventListener
*> listeners
;
288 listeners
.push_back(this);
290 ModelSafeRoutingInfo routing_info
;
291 GetModelSafeRoutingInfo(&routing_info
);
293 model_type_registry_
.reset(
294 new ModelTypeRegistry(workers_
, directory(), &mock_nudge_handler_
));
295 model_type_registry_
->RegisterDirectoryTypeDebugInfoObserver(
298 context_
.reset(new SyncSessionContext(
301 extensions_activity_
.get(),
303 debug_info_getter_
.get(),
304 model_type_registry_
.get(),
305 true, // enable keystore encryption
306 false, // force enable pre-commit GU avoidance experiment
307 "fake_invalidator_client_id"));
308 context_
->SetRoutingInfo(routing_info
);
309 syncer_
= new Syncer(&cancelation_signal_
);
310 scheduler_
.reset(new SyncSchedulerImpl(
312 BackoffDelayProvider::FromDefaults(),
314 // scheduler_ owned syncer_ now and will manage the memory of syncer_
317 syncable::ReadTransaction
trans(FROM_HERE
, directory());
318 syncable::Directory::Metahandles children
;
319 directory()->GetChildHandlesById(&trans
, trans
.root_id(), &children
);
320 ASSERT_EQ(0u, children
.size());
321 saw_syncer_event_
= false;
322 root_id_
= TestIdFactory::root();
323 parent_id_
= ids_
.MakeServer("parent id");
324 child_id_
= ids_
.MakeServer("child id");
325 directory()->set_store_birthday(mock_server_
->store_birthday());
326 mock_server_
->SetKeystoreKey("encryption_key");
329 void TearDown() override
{
330 model_type_registry_
->UnregisterDirectoryTypeDebugInfoObserver(
332 mock_server_
.reset();
334 dir_maker_
.TearDown();
337 void WriteTestDataToEntry(WriteTransaction
* trans
, MutableEntry
* entry
) {
338 EXPECT_FALSE(entry
->GetIsDir());
339 EXPECT_FALSE(entry
->GetIsDel());
340 sync_pb::EntitySpecifics specifics
;
341 specifics
.mutable_bookmark()->set_url("http://demo/");
342 specifics
.mutable_bookmark()->set_favicon("PNG");
343 entry
->PutSpecifics(specifics
);
344 entry
->PutIsUnsynced(true);
346 void VerifyTestDataInEntry(BaseTransaction
* trans
, Entry
* entry
) {
347 EXPECT_FALSE(entry
->GetIsDir());
348 EXPECT_FALSE(entry
->GetIsDel());
349 VerifyTestBookmarkDataInEntry(entry
);
351 void VerifyTestBookmarkDataInEntry(Entry
* entry
) {
352 const sync_pb::EntitySpecifics
& specifics
= entry
->GetSpecifics();
353 EXPECT_TRUE(specifics
.has_bookmark());
354 EXPECT_EQ("PNG", specifics
.bookmark().favicon());
355 EXPECT_EQ("http://demo/", specifics
.bookmark().url());
358 void VerifyHierarchyConflictsReported(
359 const sync_pb::ClientToServerMessage
& message
) {
360 // Our request should have included a warning about hierarchy conflicts.
361 const sync_pb::ClientStatus
& client_status
= message
.client_status();
362 EXPECT_TRUE(client_status
.has_hierarchy_conflict_detected());
363 EXPECT_TRUE(client_status
.hierarchy_conflict_detected());
366 void VerifyNoHierarchyConflictsReported(
367 const sync_pb::ClientToServerMessage
& message
) {
368 // Our request should have reported no hierarchy conflicts detected.
369 const sync_pb::ClientStatus
& client_status
= message
.client_status();
370 EXPECT_TRUE(client_status
.has_hierarchy_conflict_detected());
371 EXPECT_FALSE(client_status
.hierarchy_conflict_detected());
374 void VerifyHierarchyConflictsUnspecified(
375 const sync_pb::ClientToServerMessage
& message
) {
376 // Our request should have neither confirmed nor denied hierarchy conflicts.
377 const sync_pb::ClientStatus
& client_status
= message
.client_status();
378 EXPECT_FALSE(client_status
.has_hierarchy_conflict_detected());
381 sync_pb::EntitySpecifics
DefaultBookmarkSpecifics() {
382 sync_pb::EntitySpecifics result
;
383 AddDefaultFieldValue(BOOKMARKS
, &result
);
387 sync_pb::EntitySpecifics
DefaultPreferencesSpecifics() {
388 sync_pb::EntitySpecifics result
;
389 AddDefaultFieldValue(PREFERENCES
, &result
);
392 // Enumeration of alterations to entries for commit ordering tests.
394 LIST_END
= 0, // Denotes the end of the list of features from below.
395 SYNCED
, // Items are unsynced by default
401 struct CommitOrderingTest
{
402 // expected commit index.
404 // Details about the item
406 syncable::Id parent_id
;
407 EntryFeature features
[10];
409 static CommitOrderingTest
MakeLastCommitItem() {
410 CommitOrderingTest last_commit_item
;
411 last_commit_item
.commit_index
= -1;
412 last_commit_item
.id
= TestIdFactory::root();
413 return last_commit_item
;
417 void RunCommitOrderingTest(CommitOrderingTest
* test
) {
418 map
<int, syncable::Id
> expected_positions
;
419 { // Transaction scope.
420 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
421 while (!test
->id
.IsRoot()) {
422 if (test
->commit_index
>= 0) {
423 map
<int, syncable::Id
>::value_type
entry(test
->commit_index
,
425 bool double_position
= !expected_positions
.insert(entry
).second
;
426 ASSERT_FALSE(double_position
) << "Two id's expected at one position";
428 string utf8_name
= test
->id
.GetServerId();
429 string
name(utf8_name
.begin(), utf8_name
.end());
430 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, test
->parent_id
, name
);
432 entry
.PutId(test
->id
);
433 if (test
->id
.ServerKnows()) {
434 entry
.PutBaseVersion(5);
435 entry
.PutServerVersion(5);
436 entry
.PutServerParentId(test
->parent_id
);
438 entry
.PutIsDir(true);
439 entry
.PutIsUnsynced(true);
440 entry
.PutSpecifics(DefaultBookmarkSpecifics());
441 // Set the time to 30 seconds in the future to reduce the chance of
443 const base::Time
& now_plus_30s
=
444 base::Time::Now() + base::TimeDelta::FromSeconds(30);
445 const base::Time
& now_minus_2h
=
446 base::Time::Now() - base::TimeDelta::FromHours(2);
447 entry
.PutMtime(now_plus_30s
);
448 for (size_t i
= 0 ; i
< arraysize(test
->features
) ; ++i
) {
449 switch (test
->features
[i
]) {
453 entry
.PutIsUnsynced(false);
456 entry
.PutIsDel(true);
459 entry
.PutMtime(now_minus_2h
);
461 case MOVED_FROM_ROOT
:
462 entry
.PutServerParentId(trans
.root_id());
465 FAIL() << "Bad value in CommitOrderingTest list";
471 EXPECT_TRUE(SyncShareNudge());
472 ASSERT_TRUE(expected_positions
.size() ==
473 mock_server_
->committed_ids().size());
474 // If this test starts failing, be aware other sort orders could be valid.
475 for (size_t i
= 0; i
< expected_positions
.size(); ++i
) {
477 EXPECT_EQ(1u, expected_positions
.count(i
));
478 EXPECT_EQ(expected_positions
[i
], mock_server_
->committed_ids()[i
]);
482 CommitCounters
GetCommitCounters(ModelType type
) {
483 return debug_info_cache_
.GetLatestCommitCounters(type
);
486 UpdateCounters
GetUpdateCounters(ModelType type
) {
487 return debug_info_cache_
.GetLatestUpdateCounters(type
);
490 StatusCounters
GetStatusCounters(ModelType type
) {
491 return debug_info_cache_
.GetLatestStatusCounters(type
);
494 Directory
* directory() {
495 return dir_maker_
.directory();
498 const std::string
local_cache_guid() {
499 return directory()->cache_guid();
502 const std::string
foreign_cache_guid() {
503 return "kqyg7097kro6GSUod+GSg==";
506 int64
CreateUnsyncedDirectory(const string
& entry_name
,
507 const string
& idstring
) {
508 return CreateUnsyncedDirectory(entry_name
,
509 syncable::Id::CreateFromServerId(idstring
));
512 int64
CreateUnsyncedDirectory(const string
& entry_name
,
513 const syncable::Id
& id
) {
514 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
516 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), entry_name
);
517 EXPECT_TRUE(entry
.good());
518 entry
.PutIsUnsynced(true);
519 entry
.PutIsDir(true);
520 entry
.PutSpecifics(DefaultBookmarkSpecifics());
521 entry
.PutBaseVersion(id
.ServerKnows() ? 1 : 0);
523 return entry
.GetMetahandle();
526 void EnableDatatype(ModelType model_type
) {
527 enabled_datatypes_
.Put(model_type
);
529 ModelSafeRoutingInfo routing_info
;
530 GetModelSafeRoutingInfo(&routing_info
);
533 context_
->SetRoutingInfo(routing_info
);
536 mock_server_
->ExpectGetUpdatesRequestTypes(enabled_datatypes_
);
539 void DisableDatatype(ModelType model_type
) {
540 enabled_datatypes_
.Remove(model_type
);
542 ModelSafeRoutingInfo routing_info
;
543 GetModelSafeRoutingInfo(&routing_info
);
546 context_
->SetRoutingInfo(routing_info
);
549 mock_server_
->ExpectGetUpdatesRequestTypes(enabled_datatypes_
);
552 Cryptographer
* GetCryptographer(syncable::BaseTransaction
* trans
) {
553 return directory()->GetCryptographer(trans
);
556 // Configures SyncSessionContext and NudgeTracker so Syncer won't call
557 // GetUpdates prior to Commit. This method can be used to ensure a Commit is
558 // not preceeded by GetUpdates.
559 void ConfigureNoGetUpdatesRequired() {
560 context_
->set_server_enabled_pre_commit_update_avoidance(true);
561 nudge_tracker_
.OnInvalidationsEnabled();
562 nudge_tracker_
.RecordSuccessfulSyncCycle();
564 ASSERT_FALSE(context_
->ShouldFetchUpdatesBeforeCommit());
565 ASSERT_FALSE(nudge_tracker_
.IsGetUpdatesRequired());
568 base::MessageLoop message_loop_
;
570 // Some ids to aid tests. Only the root one's value is specific. The rest
571 // are named for test clarity.
572 // TODO(chron): Get rid of these inbuilt IDs. They only make it
574 syncable::Id root_id_
;
575 syncable::Id parent_id_
;
576 syncable::Id child_id_
;
580 TestDirectorySetterUpper dir_maker_
;
581 FakeEncryptor encryptor_
;
582 scoped_refptr
<ExtensionsActivity
> extensions_activity_
;
583 scoped_ptr
<MockConnectionManager
> mock_server_
;
584 CancelationSignal cancelation_signal_
;
588 scoped_ptr
<SyncSession
> session_
;
589 TypeDebugInfoCache debug_info_cache_
;
590 MockNudgeHandler mock_nudge_handler_
;
591 scoped_ptr
<ModelTypeRegistry
> model_type_registry_
;
592 scoped_ptr
<SyncSchedulerImpl
> scheduler_
;
593 scoped_ptr
<SyncSessionContext
> context_
;
594 bool saw_syncer_event_
;
595 base::TimeDelta last_short_poll_interval_received_
;
596 base::TimeDelta last_long_poll_interval_received_
;
597 base::TimeDelta last_sessions_commit_delay_
;
598 base::TimeDelta last_bookmarks_commit_delay_
;
599 int last_client_invalidation_hint_buffer_size_
;
600 std::vector
<scoped_refptr
<ModelSafeWorker
> > workers_
;
602 ModelTypeSet enabled_datatypes_
;
603 sessions::NudgeTracker nudge_tracker_
;
604 scoped_ptr
<MockDebugInfoGetter
> debug_info_getter_
;
606 DISALLOW_COPY_AND_ASSIGN(SyncerTest
);
609 TEST_F(SyncerTest
, TestCallGatherUnsyncedEntries
) {
611 Syncer::UnsyncedMetaHandles handles
;
613 syncable::ReadTransaction
trans(FROM_HERE
, directory());
614 GetUnsyncedEntries(&trans
, &handles
);
616 ASSERT_EQ(0u, handles
.size());
618 // TODO(sync): When we can dynamically connect and disconnect the mock
619 // ServerConnectionManager test disconnected GetUnsyncedEntries here. It's a
620 // regression for a very old bug.
623 TEST_F(SyncerTest
, GetCommitIdsFiltersThrottledEntries
) {
624 const ModelTypeSet
throttled_types(BOOKMARKS
);
625 sync_pb::EntitySpecifics bookmark_data
;
626 AddDefaultFieldValue(BOOKMARKS
, &bookmark_data
);
628 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
629 foreign_cache_guid(), "-1");
630 EXPECT_TRUE(SyncShareNudge());
633 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
634 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
635 ASSERT_TRUE(A
.good());
636 A
.PutIsUnsynced(true);
637 A
.PutSpecifics(bookmark_data
);
638 A
.PutNonUniqueName("bookmark");
641 // Now sync without enabling bookmarks.
642 mock_server_
->ExpectGetUpdatesRequestTypes(
643 Difference(context_
->GetEnabledTypes(), ModelTypeSet(BOOKMARKS
)));
645 syncer_
->NormalSyncShare(
646 Difference(context_
->GetEnabledTypes(), ModelTypeSet(BOOKMARKS
)),
647 &nudge_tracker_
, session_
.get());
650 // Nothing should have been committed as bookmarks is throttled.
651 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
652 Entry
entryA(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
653 ASSERT_TRUE(entryA
.good());
654 EXPECT_TRUE(entryA
.GetIsUnsynced());
657 // Sync again with bookmarks enabled.
658 mock_server_
->ExpectGetUpdatesRequestTypes(context_
->GetEnabledTypes());
659 EXPECT_TRUE(SyncShareNudge());
661 // It should have been committed.
662 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
663 Entry
entryA(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
664 ASSERT_TRUE(entryA
.good());
665 EXPECT_FALSE(entryA
.GetIsUnsynced());
669 // We use a macro so we can preserve the error location.
670 #define VERIFY_ENTRY(id, is_unapplied, is_unsynced, prev_initialized, \
671 parent_id, version, server_version, id_fac, rtrans) \
673 Entry entryA(rtrans, syncable::GET_BY_ID, id_fac.FromNumber(id)); \
674 ASSERT_TRUE(entryA.good()); \
675 /* We don't use EXPECT_EQ here because when the left side param is false,
676 gcc 4.6 warns about converting 'false' to pointer type for argument 1. */ \
677 EXPECT_TRUE(is_unsynced == entryA.GetIsUnsynced()); \
678 EXPECT_TRUE(is_unapplied == entryA.GetIsUnappliedUpdate()); \
679 EXPECT_TRUE(prev_initialized == \
680 IsRealDataType(GetModelTypeFromSpecifics( \
681 entryA.GetBaseServerSpecifics()))); \
682 EXPECT_TRUE(parent_id == -1 || \
683 entryA.GetParentId()== id_fac.FromNumber(parent_id)); \
684 EXPECT_EQ(version, entryA.GetBaseVersion()); \
685 EXPECT_EQ(server_version, entryA.GetServerVersion()); \
688 TEST_F(SyncerTest
, GetCommitIdsFiltersUnreadyEntries
) {
689 KeyParams key_params
= {"localhost", "dummy", "foobar"};
690 KeyParams other_params
= {"localhost", "dummy", "foobar2"};
691 sync_pb::EntitySpecifics bookmark
, encrypted_bookmark
;
692 bookmark
.mutable_bookmark()->set_url("url");
693 bookmark
.mutable_bookmark()->set_title("title");
694 AddDefaultFieldValue(BOOKMARKS
, &encrypted_bookmark
);
695 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
696 foreign_cache_guid(), "-1");
697 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
698 foreign_cache_guid(), "-2");
699 mock_server_
->AddUpdateDirectory(3, 0, "C", 10, 10,
700 foreign_cache_guid(), "-3");
701 mock_server_
->AddUpdateDirectory(4, 0, "D", 10, 10,
702 foreign_cache_guid(), "-4");
703 EXPECT_TRUE(SyncShareNudge());
704 // Server side change will put A in conflict.
705 mock_server_
->AddUpdateDirectory(1, 0, "A", 20, 20,
706 foreign_cache_guid(), "-1");
708 // Mark bookmarks as encrypted and set the cryptographer to have pending
710 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
711 Cryptographer
other_cryptographer(&encryptor_
);
712 other_cryptographer
.AddKey(other_params
);
713 sync_pb::EntitySpecifics specifics
;
714 sync_pb::NigoriSpecifics
* nigori
= specifics
.mutable_nigori();
715 other_cryptographer
.GetKeys(nigori
->mutable_encryption_keybag());
716 dir_maker_
.encryption_handler()->EnableEncryptEverything();
717 // Set up with an old passphrase, but have pending keys
718 GetCryptographer(&wtrans
)->AddKey(key_params
);
719 GetCryptographer(&wtrans
)->Encrypt(bookmark
,
720 encrypted_bookmark
.mutable_encrypted());
721 GetCryptographer(&wtrans
)->SetPendingKeys(nigori
->encryption_keybag());
723 // In conflict but properly encrypted.
724 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
725 ASSERT_TRUE(A
.good());
726 A
.PutIsUnsynced(true);
727 A
.PutSpecifics(encrypted_bookmark
);
728 A
.PutNonUniqueName(kEncryptedString
);
729 // Not in conflict and properly encrypted.
730 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
731 ASSERT_TRUE(B
.good());
732 B
.PutIsUnsynced(true);
733 B
.PutSpecifics(encrypted_bookmark
);
734 B
.PutNonUniqueName(kEncryptedString
);
735 // Unencrypted specifics.
736 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
737 ASSERT_TRUE(C
.good());
738 C
.PutIsUnsynced(true);
739 C
.PutNonUniqueName(kEncryptedString
);
740 // Unencrypted non_unique_name.
741 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
742 ASSERT_TRUE(D
.good());
743 D
.PutIsUnsynced(true);
744 D
.PutSpecifics(encrypted_bookmark
);
745 D
.PutNonUniqueName("not encrypted");
747 EXPECT_TRUE(SyncShareNudge());
749 // Nothing should have commited due to bookmarks being encrypted and
750 // the cryptographer having pending keys. A would have been resolved
751 // as a simple conflict, but still be unsynced until the next sync cycle.
752 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
753 VERIFY_ENTRY(1, false, true, false, 0, 20, 20, ids_
, &rtrans
);
754 VERIFY_ENTRY(2, false, true, false, 0, 10, 10, ids_
, &rtrans
);
755 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_
, &rtrans
);
756 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_
, &rtrans
);
758 // Resolve the pending keys.
759 GetCryptographer(&rtrans
)->DecryptPendingKeys(other_params
);
761 EXPECT_TRUE(SyncShareNudge());
763 // All properly encrypted and non-conflicting items should commit. "A" was
764 // conflicting, but last sync cycle resolved it as simple conflict, so on
765 // this sync cycle it committed succesfullly.
766 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
767 // Committed successfully.
768 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_
, &rtrans
);
769 // Committed successfully.
770 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_
, &rtrans
);
771 // Was not properly encrypted.
772 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_
, &rtrans
);
773 // Was not properly encrypted.
774 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_
, &rtrans
);
777 // Fix the remaining items.
778 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
779 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
780 ASSERT_TRUE(C
.good());
781 C
.PutSpecifics(encrypted_bookmark
);
782 C
.PutNonUniqueName(kEncryptedString
);
783 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
784 ASSERT_TRUE(D
.good());
785 D
.PutSpecifics(encrypted_bookmark
);
786 D
.PutNonUniqueName(kEncryptedString
);
788 EXPECT_TRUE(SyncShareNudge());
790 const StatusController
& status_controller
= session_
->status_controller();
792 EXPECT_EQ(status_controller
.model_neutral_state().commit_result
, SYNCER_OK
);
793 // None should be unsynced anymore.
794 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
795 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_
, &rtrans
);
796 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_
, &rtrans
);
797 VERIFY_ENTRY(3, false, false, false, 0, 11, 11, ids_
, &rtrans
);
798 VERIFY_ENTRY(4, false, false, false, 0, 11, 11, ids_
, &rtrans
);
802 TEST_F(SyncerTest
, GetUpdatesPartialThrottled
) {
803 sync_pb::EntitySpecifics bookmark
, pref
;
804 bookmark
.mutable_bookmark()->set_title("title");
805 pref
.mutable_preference()->set_name("name");
806 AddDefaultFieldValue(BOOKMARKS
, &bookmark
);
807 AddDefaultFieldValue(PREFERENCES
, &pref
);
809 // Normal sync, all the data types should get synced.
810 mock_server_
->AddUpdateSpecifics(1, 0, "A", 10, 10, true, 0, bookmark
,
811 foreign_cache_guid(), "-1");
812 mock_server_
->AddUpdateSpecifics(2, 1, "B", 10, 10, false, 2, bookmark
,
813 foreign_cache_guid(), "-2");
814 mock_server_
->AddUpdateSpecifics(3, 1, "C", 10, 10, false, 1, bookmark
,
815 foreign_cache_guid(), "-3");
816 mock_server_
->AddUpdateSpecifics(4, 0, "D", 10, 10, false, 0, pref
);
818 EXPECT_TRUE(SyncShareNudge());
820 // Initial state. Everything is normal.
821 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
822 VERIFY_ENTRY(1, false, false, false, 0, 10, 10, ids_
, &rtrans
);
823 VERIFY_ENTRY(2, false, false, false, 1, 10, 10, ids_
, &rtrans
);
824 VERIFY_ENTRY(3, false, false, false, 1, 10, 10, ids_
, &rtrans
);
825 VERIFY_ENTRY(4, false, false, false, 0, 10, 10, ids_
, &rtrans
);
828 // Set BOOKMARKS throttled but PREFERENCES not,
829 // then BOOKMARKS should not get synced but PREFERENCES should.
830 ModelTypeSet
throttled_types(BOOKMARKS
);
831 mock_server_
->set_partial_throttling(true);
832 mock_server_
->SetThrottledTypes(throttled_types
);
834 mock_server_
->AddUpdateSpecifics(1, 0, "E", 20, 20, true, 0, bookmark
,
835 foreign_cache_guid(), "-1");
836 mock_server_
->AddUpdateSpecifics(2, 1, "F", 20, 20, false, 2, bookmark
,
837 foreign_cache_guid(), "-2");
838 mock_server_
->AddUpdateSpecifics(3, 1, "G", 20, 20, false, 1, bookmark
,
839 foreign_cache_guid(), "-3");
840 mock_server_
->AddUpdateSpecifics(4, 0, "H", 20, 20, false, 0, pref
);
842 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
843 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
844 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
845 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
846 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
847 A
.PutIsUnsynced(true);
848 B
.PutIsUnsynced(true);
849 C
.PutIsUnsynced(true);
850 D
.PutIsUnsynced(true);
852 EXPECT_TRUE(SyncShareNudge());
854 // BOOKMARKS throttled.
855 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
856 VERIFY_ENTRY(1, false, true, false, 0, 10, 10, ids_
, &rtrans
);
857 VERIFY_ENTRY(2, false, true, false, 1, 10, 10, ids_
, &rtrans
);
858 VERIFY_ENTRY(3, false, true, false, 1, 10, 10, ids_
, &rtrans
);
859 VERIFY_ENTRY(4, false, false, false, 0, 21, 21, ids_
, &rtrans
);
862 // Unthrottled BOOKMARKS, then BOOKMARKS should get synced now.
863 mock_server_
->set_partial_throttling(false);
865 mock_server_
->AddUpdateSpecifics(1, 0, "E", 30, 30, true, 0, bookmark
,
866 foreign_cache_guid(), "-1");
867 mock_server_
->AddUpdateSpecifics(2, 1, "F", 30, 30, false, 2, bookmark
,
868 foreign_cache_guid(), "-2");
869 mock_server_
->AddUpdateSpecifics(3, 1, "G", 30, 30, false, 1, bookmark
,
870 foreign_cache_guid(), "-3");
871 mock_server_
->AddUpdateSpecifics(4, 0, "H", 30, 30, false, 0, pref
);
872 EXPECT_TRUE(SyncShareNudge());
874 // BOOKMARKS unthrottled.
875 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
876 VERIFY_ENTRY(1, false, false, false, 0, 31, 31, ids_
, &rtrans
);
877 VERIFY_ENTRY(2, false, false, false, 1, 31, 31, ids_
, &rtrans
);
878 VERIFY_ENTRY(3, false, false, false, 1, 31, 31, ids_
, &rtrans
);
879 VERIFY_ENTRY(4, false, false, false, 0, 30, 30, ids_
, &rtrans
);
883 // This test uses internal knowledge of the directory to test correctness of
884 // GetCommitIds. In almost every other test, the hierarchy is created from
885 // parent to child order, and so parents always have metahandles that are
886 // smaller than those of their children. This makes it very difficult to test
887 // some GetCommitIds edge cases, since it uses metahandle ordering as
889 TEST_F(SyncerTest
, GetCommitIds_VerifyDeletionCommitOrder
) {
891 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
893 // Create four bookmarks folders at the root node.
894 for (int i
= 1; i
< 5; ++i
) {
895 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "");
896 entry
.PutId(ids_
.FromNumber(i
));
897 entry
.PutIsDir(true);
898 entry
.PutBaseVersion(5);
899 entry
.PutServerVersion(5);
900 entry
.PutServerParentId(trans
.root_id());
901 entry
.PutServerIsDir(true);
902 entry
.PutIsUnsynced(true);
903 entry
.PutSpecifics(DefaultBookmarkSpecifics());
906 // Now iterate in reverse order make a hierarchy of them.
907 // While we're at it, also mark them as deleted.
908 syncable::Id parent_id
= trans
.root_id();
909 for (int i
= 4; i
> 0; --i
) {
910 MutableEntry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(i
));
911 entry
.PutParentId(parent_id
);
912 entry
.PutServerParentId(parent_id
);
913 entry
.PutIsDel(true);
914 parent_id
= ids_
.FromNumber(i
);
919 // Run GetCommitIds, the function being tested.
920 syncable::Directory::Metahandles result_handles
;
921 syncable::ReadTransaction
trans(FROM_HERE
, directory());
922 GetCommitIdsForType(&trans
, BOOKMARKS
, 100, &result_handles
);
924 // Now verify the output. We expect four results in child to parent order.
925 ASSERT_EQ(4U, result_handles
.size());
927 Entry
entry0(&trans
, GET_BY_HANDLE
, result_handles
[0]);
928 EXPECT_EQ(ids_
.FromNumber(1), entry0
.GetId());
930 Entry
entry1(&trans
, GET_BY_HANDLE
, result_handles
[1]);
931 EXPECT_EQ(ids_
.FromNumber(2), entry1
.GetId());
933 Entry
entry2(&trans
, GET_BY_HANDLE
, result_handles
[2]);
934 EXPECT_EQ(ids_
.FromNumber(3), entry2
.GetId());
936 Entry
entry3(&trans
, GET_BY_HANDLE
, result_handles
[3]);
937 EXPECT_EQ(ids_
.FromNumber(4), entry3
.GetId());
941 // Verify that if there are more deleted items than the maximum number of
942 // entries, child to parent order is still preserved.
943 TEST_F(SyncerTest
, GetCommitIds_VerifyDeletionCommitOrderMaxEntries
) {
945 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
947 // Create a bookmark tree with one root, two second level, and three third
948 // level bookmarks, all folders.
949 for (int i
= 1; i
<= 6; ++i
) {
950 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "");
951 entry
.PutId(ids_
.FromNumber(i
));
952 entry
.PutIsDir(true);
953 entry
.PutBaseVersion(5);
954 entry
.PutServerVersion(5);
955 entry
.PutParentId(ids_
.FromNumber(i
/2));
956 entry
.PutServerParentId(ids_
.FromNumber(i
/2));
957 entry
.PutServerIsDir(true);
958 entry
.PutIsUnsynced(true);
959 entry
.PutSpecifics(DefaultBookmarkSpecifics());
960 entry
.PutIsDel(true);
965 // Run GetCommitIds with a limit of 2 entries to commit.
966 syncable::Directory::Metahandles result_handles
;
967 syncable::ReadTransaction
trans(FROM_HERE
, directory());
968 GetCommitIdsForType(&trans
, BOOKMARKS
, 2, &result_handles
);
970 // Now verify the output. We expect two results in child to parent order
971 // (descending id order).
972 ASSERT_EQ(2U, result_handles
.size());
974 Entry
entry0(&trans
, GET_BY_HANDLE
, result_handles
[0]);
975 EXPECT_EQ(ids_
.FromNumber(6), entry0
.GetId());
977 Entry
entry1(&trans
, GET_BY_HANDLE
, result_handles
[1]);
978 EXPECT_EQ(ids_
.FromNumber(5), entry1
.GetId());
982 TEST_F(SyncerTest
, EncryptionAwareConflicts
) {
983 KeyParams key_params
= {"localhost", "dummy", "foobar"};
984 Cryptographer
other_cryptographer(&encryptor_
);
985 other_cryptographer
.AddKey(key_params
);
986 sync_pb::EntitySpecifics bookmark
, encrypted_bookmark
, modified_bookmark
;
987 bookmark
.mutable_bookmark()->set_title("title");
988 other_cryptographer
.Encrypt(bookmark
,
989 encrypted_bookmark
.mutable_encrypted());
990 AddDefaultFieldValue(BOOKMARKS
, &encrypted_bookmark
);
991 modified_bookmark
.mutable_bookmark()->set_title("title2");
992 other_cryptographer
.Encrypt(modified_bookmark
,
993 modified_bookmark
.mutable_encrypted());
994 sync_pb::EntitySpecifics pref
, encrypted_pref
, modified_pref
;
995 pref
.mutable_preference()->set_name("name");
996 AddDefaultFieldValue(PREFERENCES
, &encrypted_pref
);
997 other_cryptographer
.Encrypt(pref
,
998 encrypted_pref
.mutable_encrypted());
999 modified_pref
.mutable_preference()->set_name("name2");
1000 other_cryptographer
.Encrypt(modified_pref
,
1001 modified_pref
.mutable_encrypted());
1003 // Mark bookmarks and preferences as encrypted and set the cryptographer to
1004 // have pending keys.
1005 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1006 sync_pb::EntitySpecifics specifics
;
1007 sync_pb::NigoriSpecifics
* nigori
= specifics
.mutable_nigori();
1008 other_cryptographer
.GetKeys(nigori
->mutable_encryption_keybag());
1009 dir_maker_
.encryption_handler()->EnableEncryptEverything();
1010 GetCryptographer(&wtrans
)->SetPendingKeys(nigori
->encryption_keybag());
1011 EXPECT_TRUE(GetCryptographer(&wtrans
)->has_pending_keys());
1014 // We need to remember the exact position of our local items, so we can
1015 // make updates that do not modify those positions.
1016 UniquePosition pos1
;
1017 UniquePosition pos2
;
1018 UniquePosition pos3
;
1020 mock_server_
->AddUpdateSpecifics(1, 0, "A", 10, 10, true, 0, bookmark
,
1021 foreign_cache_guid(), "-1");
1022 mock_server_
->AddUpdateSpecifics(2, 1, "B", 10, 10, false, 2, bookmark
,
1023 foreign_cache_guid(), "-2");
1024 mock_server_
->AddUpdateSpecifics(3, 1, "C", 10, 10, false, 1, bookmark
,
1025 foreign_cache_guid(), "-3");
1026 mock_server_
->AddUpdateSpecifics(4, 0, "D", 10, 10, false, 0, pref
);
1027 EXPECT_TRUE(SyncShareNudge());
1029 // Initial state. Everything is normal.
1030 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1031 VERIFY_ENTRY(1, false, false, false, 0, 10, 10, ids_
, &rtrans
);
1032 VERIFY_ENTRY(2, false, false, false, 1, 10, 10, ids_
, &rtrans
);
1033 VERIFY_ENTRY(3, false, false, false, 1, 10, 10, ids_
, &rtrans
);
1034 VERIFY_ENTRY(4, false, false, false, 0, 10, 10, ids_
, &rtrans
);
1036 Entry
entry1(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
1037 ASSERT_TRUE(entry1
.GetUniquePosition().Equals(
1038 entry1
.GetServerUniquePosition()));
1039 pos1
= entry1
.GetUniquePosition();
1040 Entry
entry2(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(2));
1041 pos2
= entry2
.GetUniquePosition();
1042 Entry
entry3(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(3));
1043 pos3
= entry3
.GetUniquePosition();
1046 // Server side encryption will not be applied due to undecryptable data.
1047 // At this point, BASE_SERVER_SPECIFICS should be filled for all four items.
1048 mock_server_
->AddUpdateSpecifics(1, 0, kEncryptedString
, 20, 20, true, 0,
1050 foreign_cache_guid(), "-1");
1051 mock_server_
->AddUpdateSpecifics(2, 1, kEncryptedString
, 20, 20, false, 2,
1053 foreign_cache_guid(), "-2");
1054 mock_server_
->AddUpdateSpecifics(3, 1, kEncryptedString
, 20, 20, false, 1,
1056 foreign_cache_guid(), "-3");
1057 mock_server_
->AddUpdateSpecifics(4, 0, kEncryptedString
, 20, 20, false, 0,
1059 foreign_cache_guid(), "-4");
1060 EXPECT_TRUE(SyncShareNudge());
1062 // All should be unapplied due to being undecryptable and have a valid
1063 // BASE_SERVER_SPECIFICS.
1064 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1065 VERIFY_ENTRY(1, true, false, true, 0, 10, 20, ids_
, &rtrans
);
1066 VERIFY_ENTRY(2, true, false, true, 1, 10, 20, ids_
, &rtrans
);
1067 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_
, &rtrans
);
1068 VERIFY_ENTRY(4, true, false, true, 0, 10, 20, ids_
, &rtrans
);
1071 // Server side change that don't modify anything should not affect
1072 // BASE_SERVER_SPECIFICS (such as name changes and mtime changes).
1073 mock_server_
->AddUpdateSpecifics(1, 0, kEncryptedString
, 30, 30, true, 0,
1075 foreign_cache_guid(), "-1");
1076 mock_server_
->AddUpdateSpecifics(2, 1, kEncryptedString
, 30, 30, false, 2,
1078 foreign_cache_guid(), "-2");
1079 // Item 3 doesn't change.
1080 mock_server_
->AddUpdateSpecifics(4, 0, kEncryptedString
, 30, 30, false, 0,
1082 foreign_cache_guid(), "-4");
1083 EXPECT_TRUE(SyncShareNudge());
1085 // Items 1, 2, and 4 should have newer server versions, 3 remains the same.
1086 // All should remain unapplied due to be undecryptable.
1087 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1088 VERIFY_ENTRY(1, true, false, true, 0, 10, 30, ids_
, &rtrans
);
1089 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_
, &rtrans
);
1090 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_
, &rtrans
);
1091 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_
, &rtrans
);
1094 // Positional changes, parent changes, and specifics changes should reset
1095 // BASE_SERVER_SPECIFICS.
1096 // Became unencrypted.
1097 mock_server_
->AddUpdateSpecifics(1, 0, "A", 40, 40, true, 0, bookmark
,
1098 foreign_cache_guid(), "-1");
1099 // Reordered to after item 2.
1100 mock_server_
->AddUpdateSpecifics(3, 1, kEncryptedString
, 30, 30, false, 3,
1102 foreign_cache_guid(), "-3");
1103 EXPECT_TRUE(SyncShareNudge());
1105 // Items 2 and 4 should be the only ones with BASE_SERVER_SPECIFICS set.
1106 // Items 1 is now unencrypted, so should have applied normally.
1107 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1108 VERIFY_ENTRY(1, false, false, false, 0, 40, 40, ids_
, &rtrans
);
1109 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_
, &rtrans
);
1110 VERIFY_ENTRY(3, true, false, false, 1, 10, 30, ids_
, &rtrans
);
1111 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_
, &rtrans
);
1114 // Make local changes, which should remain unsynced for items 2, 3, 4.
1116 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1117 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
1118 ASSERT_TRUE(A
.good());
1119 A
.PutSpecifics(modified_bookmark
);
1120 A
.PutNonUniqueName(kEncryptedString
);
1121 A
.PutIsUnsynced(true);
1122 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
1123 ASSERT_TRUE(B
.good());
1124 B
.PutSpecifics(modified_bookmark
);
1125 B
.PutNonUniqueName(kEncryptedString
);
1126 B
.PutIsUnsynced(true);
1127 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
1128 ASSERT_TRUE(C
.good());
1129 C
.PutSpecifics(modified_bookmark
);
1130 C
.PutNonUniqueName(kEncryptedString
);
1131 C
.PutIsUnsynced(true);
1132 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
1133 ASSERT_TRUE(D
.good());
1134 D
.PutSpecifics(modified_pref
);
1135 D
.PutNonUniqueName(kEncryptedString
);
1136 D
.PutIsUnsynced(true);
1138 EXPECT_TRUE(SyncShareNudge());
1140 // Item 1 remains unsynced due to there being pending keys.
1141 // Items 2, 3, 4 should remain unsynced since they were not up to date.
1142 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1143 VERIFY_ENTRY(1, false, true, false, 0, 40, 40, ids_
, &rtrans
);
1144 VERIFY_ENTRY(2, true, true, true, 1, 10, 30, ids_
, &rtrans
);
1145 VERIFY_ENTRY(3, true, true, false, 1, 10, 30, ids_
, &rtrans
);
1146 VERIFY_ENTRY(4, true, true, true, 0, 10, 30, ids_
, &rtrans
);
1150 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1151 // Resolve the pending keys.
1152 GetCryptographer(&rtrans
)->DecryptPendingKeys(key_params
);
1154 // First cycle resolves conflicts, second cycle commits changes.
1155 EXPECT_TRUE(SyncShareNudge());
1156 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_server_overwrites
);
1157 EXPECT_EQ(1, GetUpdateCounters(PREFERENCES
).num_server_overwrites
);
1158 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_local_overwrites
);
1160 // We successfully commited item(s).
1161 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS
).num_commits_attempted
);
1162 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS
).num_commits_success
);
1163 EXPECT_EQ(1, GetCommitCounters(PREFERENCES
).num_commits_attempted
);
1164 EXPECT_EQ(1, GetCommitCounters(PREFERENCES
).num_commits_success
);
1166 EXPECT_TRUE(SyncShareNudge());
1168 // Everything should be resolved now. The local changes should have
1169 // overwritten the server changes for 2 and 4, while the server changes
1170 // overwrote the local for entry 3.
1172 // Expect there will be no new overwrites.
1173 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_server_overwrites
);
1174 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_local_overwrites
);
1176 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS
).num_commits_success
);
1177 EXPECT_EQ(1, GetCommitCounters(PREFERENCES
).num_commits_success
);
1179 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1180 VERIFY_ENTRY(1, false, false, false, 0, 41, 41, ids_
, &rtrans
);
1181 VERIFY_ENTRY(2, false, false, false, 1, 31, 31, ids_
, &rtrans
);
1182 VERIFY_ENTRY(3, false, false, false, 1, 30, 30, ids_
, &rtrans
);
1183 VERIFY_ENTRY(4, false, false, false, 0, 31, 31, ids_
, &rtrans
);
1188 TEST_F(SyncerTest
, TestGetUnsyncedAndSimpleCommit
) {
1190 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1191 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1192 ASSERT_TRUE(parent
.good());
1193 parent
.PutIsUnsynced(true);
1194 parent
.PutIsDir(true);
1195 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1196 parent
.PutBaseVersion(1);
1197 parent
.PutId(parent_id_
);
1198 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete");
1199 ASSERT_TRUE(child
.good());
1200 child
.PutId(child_id_
);
1201 child
.PutBaseVersion(1);
1202 WriteTestDataToEntry(&wtrans
, &child
);
1205 EXPECT_TRUE(SyncShareNudge());
1206 ASSERT_EQ(2u, mock_server_
->committed_ids().size());
1207 // If this test starts failing, be aware other sort orders could be valid.
1208 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1209 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1211 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1212 Entry
entry(&rt
, syncable::GET_BY_ID
, child_id_
);
1213 ASSERT_TRUE(entry
.good());
1214 VerifyTestDataInEntry(&rt
, &entry
);
1218 TEST_F(SyncerTest
, TestPurgeWhileUnsynced
) {
1219 // Similar to above, but throw a purge operation into the mix. Bug 49278.
1220 syncable::Id pref_node_id
= TestIdFactory::MakeServer("Tim");
1222 directory()->SetDownloadProgress(BOOKMARKS
,
1223 syncable::BuildProgress(BOOKMARKS
));
1224 directory()->SetDownloadProgress(PREFERENCES
,
1225 syncable::BuildProgress(PREFERENCES
));
1226 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1227 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1228 ASSERT_TRUE(parent
.good());
1229 parent
.PutIsUnsynced(true);
1230 parent
.PutIsDir(true);
1231 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1232 parent
.PutBaseVersion(1);
1233 parent
.PutId(parent_id_
);
1234 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete");
1235 ASSERT_TRUE(child
.good());
1236 child
.PutId(child_id_
);
1237 child
.PutBaseVersion(1);
1238 WriteTestDataToEntry(&wtrans
, &child
);
1240 MutableEntry
parent2(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Tim");
1241 ASSERT_TRUE(parent2
.good());
1242 parent2
.PutIsUnsynced(true);
1243 parent2
.PutIsDir(true);
1244 parent2
.PutSpecifics(DefaultPreferencesSpecifics());
1245 parent2
.PutBaseVersion(1);
1246 parent2
.PutId(pref_node_id
);
1249 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES
),
1253 EXPECT_TRUE(SyncShareNudge());
1254 ASSERT_EQ(2U, mock_server_
->committed_ids().size());
1255 // If this test starts failing, be aware other sort orders could be valid.
1256 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1257 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1259 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1260 Entry
entry(&rt
, syncable::GET_BY_ID
, child_id_
);
1261 ASSERT_TRUE(entry
.good());
1262 VerifyTestDataInEntry(&rt
, &entry
);
1264 directory()->SaveChanges();
1266 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1267 Entry
entry(&rt
, syncable::GET_BY_ID
, pref_node_id
);
1268 ASSERT_FALSE(entry
.good());
1272 TEST_F(SyncerTest
, TestPurgeWhileUnapplied
) {
1273 // Similar to above, but for unapplied items. Bug 49278.
1275 directory()->SetDownloadProgress(BOOKMARKS
,
1276 syncable::BuildProgress(BOOKMARKS
));
1277 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1278 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1279 ASSERT_TRUE(parent
.good());
1280 parent
.PutIsUnappliedUpdate(true);
1281 parent
.PutIsDir(true);
1282 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1283 parent
.PutBaseVersion(1);
1284 parent
.PutId(parent_id_
);
1287 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(BOOKMARKS
),
1291 EXPECT_TRUE(SyncShareNudge());
1292 directory()->SaveChanges();
1294 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1295 Entry
entry(&rt
, syncable::GET_BY_ID
, parent_id_
);
1296 ASSERT_FALSE(entry
.good());
1300 TEST_F(SyncerTest
, TestPurgeWithJournal
) {
1302 directory()->SetDownloadProgress(BOOKMARKS
,
1303 syncable::BuildProgress(BOOKMARKS
));
1304 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1305 MutableEntry
parent(&wtrans
, syncable::CREATE
, BOOKMARKS
, wtrans
.root_id(),
1307 ASSERT_TRUE(parent
.good());
1308 parent
.PutIsDir(true);
1309 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1310 parent
.PutBaseVersion(1);
1311 parent
.PutId(parent_id_
);
1312 MutableEntry
child(&wtrans
, syncable::CREATE
, BOOKMARKS
, parent_id_
,
1314 ASSERT_TRUE(child
.good());
1315 child
.PutId(child_id_
);
1316 child
.PutBaseVersion(1);
1317 WriteTestDataToEntry(&wtrans
, &child
);
1319 MutableEntry
parent2(&wtrans
, syncable::CREATE
, PREFERENCES
,
1320 wtrans
.root_id(), "Tim");
1321 ASSERT_TRUE(parent2
.good());
1322 parent2
.PutIsDir(true);
1323 parent2
.PutSpecifics(DefaultPreferencesSpecifics());
1324 parent2
.PutBaseVersion(1);
1325 parent2
.PutId(TestIdFactory::MakeServer("Tim"));
1328 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES
, BOOKMARKS
),
1329 ModelTypeSet(BOOKMARKS
),
1332 // Verify bookmark nodes are saved in delete journal but not preference
1334 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1335 syncable::DeleteJournal
* delete_journal
= directory()->delete_journal();
1336 EXPECT_EQ(2u, delete_journal
->GetDeleteJournalSize(&rt
));
1337 syncable::EntryKernelSet journal_entries
;
1338 directory()->delete_journal()->GetDeleteJournals(&rt
, BOOKMARKS
,
1340 EXPECT_EQ(parent_id_
, (*journal_entries
.begin())->ref(syncable::ID
));
1341 EXPECT_EQ(child_id_
, (*journal_entries
.rbegin())->ref(syncable::ID
));
1345 TEST_F(SyncerTest
, ResetVersions
) {
1346 // Download some pref items.
1347 mock_server_
->AddUpdatePref("id1", "", "tag1", 20, 20);
1348 mock_server_
->AddUpdatePref("id2", "", "tag2", 30, 30);
1349 mock_server_
->AddUpdatePref("id3", "", "tag3", 40, 40);
1350 EXPECT_TRUE(SyncShareNudge());
1353 // Modify one of the preferences locally, mark another one as unapplied,
1354 // and create another unsynced preference.
1355 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1356 MutableEntry
entry(&wtrans
, GET_BY_CLIENT_TAG
, "tag1");
1357 entry
.PutIsUnsynced(true);
1359 MutableEntry
entry2(&wtrans
, GET_BY_CLIENT_TAG
, "tag2");
1360 entry2
.PutIsUnappliedUpdate(true);
1362 MutableEntry
entry4(&wtrans
, CREATE
, PREFERENCES
, "name");
1363 entry4
.PutUniqueClientTag("tag4");
1364 entry4
.PutIsUnsynced(true);
1368 // Reset the versions.
1369 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1370 ASSERT_TRUE(directory()->ResetVersionsForType(&wtrans
, PREFERENCES
));
1374 // Verify the synced items are all with version 1 now, with
1375 // unsynced/unapplied state preserved.
1376 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1377 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, "tag1");
1378 EXPECT_EQ(1, entry
.GetBaseVersion());
1379 EXPECT_EQ(1, entry
.GetServerVersion());
1380 EXPECT_TRUE(entry
.GetIsUnsynced());
1381 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
1382 Entry
entry2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
1383 EXPECT_EQ(1, entry2
.GetBaseVersion());
1384 EXPECT_EQ(1, entry2
.GetServerVersion());
1385 EXPECT_FALSE(entry2
.GetIsUnsynced());
1386 EXPECT_TRUE(entry2
.GetIsUnappliedUpdate());
1387 Entry
entry3(&trans
, GET_BY_CLIENT_TAG
, "tag3");
1388 EXPECT_EQ(1, entry3
.GetBaseVersion());
1389 EXPECT_EQ(1, entry3
.GetServerVersion());
1390 EXPECT_FALSE(entry3
.GetIsUnsynced());
1391 EXPECT_FALSE(entry3
.GetIsUnappliedUpdate());
1393 // Entry 4 (the locally created one) should remain the same.
1394 Entry
entry4(&trans
, GET_BY_CLIENT_TAG
, "tag4");
1395 EXPECT_EQ(-1, entry4
.GetBaseVersion());
1396 EXPECT_EQ(0, entry4
.GetServerVersion());
1397 EXPECT_TRUE(entry4
.GetIsUnsynced());
1398 EXPECT_FALSE(entry4
.GetIsUnappliedUpdate());
1402 TEST_F(SyncerTest
, TestCommitListOrderingTwoItemsTall
) {
1403 CommitOrderingTest items
[] = {
1404 {1, ids_
.FromNumber(-1001), ids_
.FromNumber(-1000)},
1405 {0, ids_
.FromNumber(-1000), ids_
.FromNumber(0)},
1406 CommitOrderingTest::MakeLastCommitItem(),
1408 RunCommitOrderingTest(items
);
1411 TEST_F(SyncerTest
, TestCommitListOrderingThreeItemsTall
) {
1412 CommitOrderingTest items
[] = {
1413 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1414 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1415 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1416 CommitOrderingTest::MakeLastCommitItem(),
1418 RunCommitOrderingTest(items
);
1421 TEST_F(SyncerTest
, TestCommitListOrderingFourItemsTall
) {
1422 CommitOrderingTest items
[] = {
1423 {3, ids_
.FromNumber(-2003), ids_
.FromNumber(-2002)},
1424 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1425 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1426 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1427 CommitOrderingTest::MakeLastCommitItem(),
1429 RunCommitOrderingTest(items
);
1432 TEST_F(SyncerTest
, TestCommitListOrderingThreeItemsTallLimitedSize
) {
1433 context_
->set_max_commit_batch_size(2);
1434 CommitOrderingTest items
[] = {
1435 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1436 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1437 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1438 CommitOrderingTest::MakeLastCommitItem(),
1440 RunCommitOrderingTest(items
);
1443 TEST_F(SyncerTest
, TestCommitListOrderingSingleDeletedItem
) {
1444 CommitOrderingTest items
[] = {
1445 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1446 CommitOrderingTest::MakeLastCommitItem(),
1448 RunCommitOrderingTest(items
);
1451 TEST_F(SyncerTest
, TestCommitListOrderingSingleUncommittedDeletedItem
) {
1452 CommitOrderingTest items
[] = {
1453 {-1, ids_
.FromNumber(-1000), ids_
.FromNumber(0), {DELETED
}},
1454 CommitOrderingTest::MakeLastCommitItem(),
1456 RunCommitOrderingTest(items
);
1459 TEST_F(SyncerTest
, TestCommitListOrderingSingleDeletedItemWithUnroll
) {
1460 CommitOrderingTest items
[] = {
1461 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1462 CommitOrderingTest::MakeLastCommitItem(),
1464 RunCommitOrderingTest(items
);
1468 TestCommitListOrderingSingleLongDeletedItemWithUnroll
) {
1469 CommitOrderingTest items
[] = {
1470 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1471 CommitOrderingTest::MakeLastCommitItem(),
1473 RunCommitOrderingTest(items
);
1476 TEST_F(SyncerTest
, TestCommitListOrderingTwoLongDeletedItemWithUnroll
) {
1477 CommitOrderingTest items
[] = {
1478 {1, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1479 {0, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
, OLD_MTIME
}},
1480 CommitOrderingTest::MakeLastCommitItem(),
1482 RunCommitOrderingTest(items
);
1485 TEST_F(SyncerTest
, TestCommitListOrdering3LongDeletedItemsWithSizeLimit
) {
1486 context_
->set_max_commit_batch_size(2);
1487 CommitOrderingTest items
[] = {
1488 {2, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1489 {1, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
, OLD_MTIME
}},
1490 {0, ids_
.FromNumber(1002), ids_
.FromNumber(1001), {DELETED
, OLD_MTIME
}},
1491 CommitOrderingTest::MakeLastCommitItem(),
1493 RunCommitOrderingTest(items
);
1496 TEST_F(SyncerTest
, TestCommitListOrderingTwoDeletedItemsWithUnroll
) {
1497 CommitOrderingTest items
[] = {
1498 {1, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1499 {0, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
}},
1500 CommitOrderingTest::MakeLastCommitItem(),
1502 RunCommitOrderingTest(items
);
1505 TEST_F(SyncerTest
, TestCommitListOrderingComplexDeletionScenario
) {
1506 CommitOrderingTest items
[] = {
1507 {2, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1508 {-1, ids_
.FromNumber(1001), ids_
.FromNumber(0), {SYNCED
}},
1509 {1, ids_
.FromNumber(1002), ids_
.FromNumber(1001), {DELETED
, OLD_MTIME
}},
1510 {-1, ids_
.FromNumber(1003), ids_
.FromNumber(1001), {SYNCED
}},
1511 {0, ids_
.FromNumber(1004), ids_
.FromNumber(1003), {DELETED
}},
1512 CommitOrderingTest::MakeLastCommitItem(),
1514 RunCommitOrderingTest(items
);
1518 TestCommitListOrderingComplexDeletionScenarioWith2RecentDeletes
) {
1519 CommitOrderingTest items
[] = {
1520 {3, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1521 {-1, ids_
.FromNumber(1001), ids_
.FromNumber(0), {SYNCED
}},
1522 {2, ids_
.FromNumber(1002), ids_
.FromNumber(1001), {DELETED
, OLD_MTIME
}},
1523 {-1, ids_
.FromNumber(1003), ids_
.FromNumber(1001), {SYNCED
}},
1524 {1, ids_
.FromNumber(1004), ids_
.FromNumber(1003), {DELETED
}},
1525 {0, ids_
.FromNumber(1005), ids_
.FromNumber(1003), {DELETED
}},
1526 CommitOrderingTest::MakeLastCommitItem(),
1528 RunCommitOrderingTest(items
);
1531 TEST_F(SyncerTest
, TestCommitListOrderingDeleteMovedItems
) {
1532 CommitOrderingTest items
[] = {
1533 {1, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1534 {0, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
, OLD_MTIME
,
1536 CommitOrderingTest::MakeLastCommitItem(),
1538 RunCommitOrderingTest(items
);
1541 TEST_F(SyncerTest
, TestCommitListOrderingWithNesting
) {
1542 const base::Time
& now_minus_2h
=
1543 base::Time::Now() - base::TimeDelta::FromHours(2);
1545 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1547 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bob");
1548 ASSERT_TRUE(parent
.good());
1549 parent
.PutIsUnsynced(true);
1550 parent
.PutIsDir(true);
1551 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1552 parent
.PutId(ids_
.FromNumber(100));
1553 parent
.PutBaseVersion(1);
1555 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(100), "Bob");
1556 ASSERT_TRUE(child
.good());
1557 child
.PutIsUnsynced(true);
1558 child
.PutIsDir(true);
1559 child
.PutSpecifics(DefaultBookmarkSpecifics());
1560 child
.PutId(ids_
.FromNumber(101));
1561 child
.PutBaseVersion(1);
1562 MutableEntry
grandchild(
1563 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(101), "Bob");
1564 ASSERT_TRUE(grandchild
.good());
1565 grandchild
.PutId(ids_
.FromNumber(102));
1566 grandchild
.PutIsUnsynced(true);
1567 grandchild
.PutSpecifics(DefaultBookmarkSpecifics());
1568 grandchild
.PutBaseVersion(1);
1571 // Create three deleted items which deletions we expect to be sent to the
1573 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1574 ASSERT_TRUE(parent
.good());
1575 parent
.PutId(ids_
.FromNumber(103));
1576 parent
.PutIsUnsynced(true);
1577 parent
.PutIsDir(true);
1578 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1579 parent
.PutIsDel(true);
1580 parent
.PutBaseVersion(1);
1581 parent
.PutMtime(now_minus_2h
);
1583 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(103), "Pete");
1584 ASSERT_TRUE(child
.good());
1585 child
.PutId(ids_
.FromNumber(104));
1586 child
.PutIsUnsynced(true);
1587 child
.PutIsDir(true);
1588 child
.PutSpecifics(DefaultBookmarkSpecifics());
1589 child
.PutIsDel(true);
1590 child
.PutBaseVersion(1);
1591 child
.PutMtime(now_minus_2h
);
1592 MutableEntry
grandchild(
1593 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(104), "Pete");
1594 ASSERT_TRUE(grandchild
.good());
1595 grandchild
.PutId(ids_
.FromNumber(105));
1596 grandchild
.PutIsUnsynced(true);
1597 grandchild
.PutIsDel(true);
1598 grandchild
.PutIsDir(false);
1599 grandchild
.PutSpecifics(DefaultBookmarkSpecifics());
1600 grandchild
.PutBaseVersion(1);
1601 grandchild
.PutMtime(now_minus_2h
);
1605 EXPECT_TRUE(SyncShareNudge());
1606 ASSERT_EQ(6u, mock_server_
->committed_ids().size());
1607 // This test will NOT unroll deletes because SERVER_PARENT_ID is not set.
1608 // It will treat these like moves.
1609 vector
<syncable::Id
> commit_ids(mock_server_
->committed_ids());
1610 EXPECT_TRUE(ids_
.FromNumber(100) == commit_ids
[0]);
1611 EXPECT_TRUE(ids_
.FromNumber(101) == commit_ids
[1]);
1612 EXPECT_TRUE(ids_
.FromNumber(102) == commit_ids
[2]);
1613 // We don't guarantee the delete orders in this test, only that they occur
1615 std::sort(commit_ids
.begin() + 3, commit_ids
.end());
1616 EXPECT_TRUE(ids_
.FromNumber(103) == commit_ids
[3]);
1617 EXPECT_TRUE(ids_
.FromNumber(104) == commit_ids
[4]);
1618 EXPECT_TRUE(ids_
.FromNumber(105) == commit_ids
[5]);
1621 TEST_F(SyncerTest
, TestCommitListOrderingWithNewItems
) {
1622 syncable::Id parent1_id
= ids_
.MakeServer("p1");
1623 syncable::Id parent2_id
= ids_
.MakeServer("p2");
1626 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1627 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "1");
1628 ASSERT_TRUE(parent
.good());
1629 parent
.PutIsUnsynced(true);
1630 parent
.PutIsDir(true);
1631 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1632 parent
.PutId(parent1_id
);
1633 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "2");
1634 ASSERT_TRUE(child
.good());
1635 child
.PutIsUnsynced(true);
1636 child
.PutIsDir(true);
1637 child
.PutSpecifics(DefaultBookmarkSpecifics());
1638 child
.PutId(parent2_id
);
1639 parent
.PutBaseVersion(1);
1640 child
.PutBaseVersion(1);
1643 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1644 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, parent1_id
, "A");
1645 ASSERT_TRUE(parent
.good());
1646 parent
.PutIsUnsynced(true);
1647 parent
.PutIsDir(true);
1648 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1649 parent
.PutId(ids_
.FromNumber(102));
1650 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent1_id
, "B");
1651 ASSERT_TRUE(child
.good());
1652 child
.PutIsUnsynced(true);
1653 child
.PutIsDir(true);
1654 child
.PutSpecifics(DefaultBookmarkSpecifics());
1655 child
.PutId(ids_
.FromNumber(-103));
1656 parent
.PutBaseVersion(1);
1659 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1660 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, parent2_id
, "A");
1661 ASSERT_TRUE(parent
.good());
1662 parent
.PutIsUnsynced(true);
1663 parent
.PutIsDir(true);
1664 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1665 parent
.PutId(ids_
.FromNumber(-104));
1666 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent2_id
, "B");
1667 ASSERT_TRUE(child
.good());
1668 child
.PutIsUnsynced(true);
1669 child
.PutIsDir(true);
1670 child
.PutSpecifics(DefaultBookmarkSpecifics());
1671 child
.PutId(ids_
.FromNumber(105));
1672 child
.PutBaseVersion(1);
1675 EXPECT_TRUE(SyncShareNudge());
1676 ASSERT_EQ(6u, mock_server_
->committed_ids().size());
1678 // This strange iteration and std::count() usage is to allow the order to
1679 // vary. All we really care about is that parent1_id and parent2_id are the
1680 // first two IDs, and that the children make up the next four. Other than
1681 // that, ordering doesn't matter.
1683 vector
<syncable::Id
>::const_iterator i
=
1684 mock_server_
->committed_ids().begin();
1685 vector
<syncable::Id
>::const_iterator parents_begin
= i
;
1688 vector
<syncable::Id
>::const_iterator parents_end
= i
;
1689 vector
<syncable::Id
>::const_iterator children_begin
= i
;
1690 vector
<syncable::Id
>::const_iterator children_end
=
1691 mock_server_
->committed_ids().end();
1693 EXPECT_EQ(1, count(parents_begin
, parents_end
, parent1_id
));
1694 EXPECT_EQ(1, count(parents_begin
, parents_end
, parent2_id
));
1696 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(-103)));
1697 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(102)));
1698 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(105)));
1699 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(-104)));
1702 TEST_F(SyncerTest
, TestCommitListOrderingCounterexample
) {
1703 syncable::Id child2_id
= ids_
.NewServerId();
1706 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1707 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "P");
1708 ASSERT_TRUE(parent
.good());
1709 parent
.PutIsUnsynced(true);
1710 parent
.PutIsDir(true);
1711 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1712 parent
.PutId(parent_id_
);
1713 MutableEntry
child1(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "1");
1714 ASSERT_TRUE(child1
.good());
1715 child1
.PutIsUnsynced(true);
1716 child1
.PutId(child_id_
);
1717 child1
.PutSpecifics(DefaultBookmarkSpecifics());
1718 MutableEntry
child2(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "2");
1719 ASSERT_TRUE(child2
.good());
1720 child2
.PutIsUnsynced(true);
1721 child2
.PutSpecifics(DefaultBookmarkSpecifics());
1722 child2
.PutId(child2_id
);
1724 parent
.PutBaseVersion(1);
1725 child1
.PutBaseVersion(1);
1726 child2
.PutBaseVersion(1);
1729 EXPECT_TRUE(SyncShareNudge());
1730 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1731 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1732 // There are two possible valid orderings.
1733 if (child2_id
== mock_server_
->committed_ids()[1]) {
1734 EXPECT_TRUE(child2_id
== mock_server_
->committed_ids()[1]);
1735 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[2]);
1737 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1738 EXPECT_TRUE(child2_id
== mock_server_
->committed_ids()[2]);
1742 TEST_F(SyncerTest
, TestCommitListOrderingAndNewParent
) {
1743 string parent1_name
= "1";
1744 string parent2_name
= "A";
1745 string child_name
= "B";
1748 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1749 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(),
1751 ASSERT_TRUE(parent
.good());
1752 parent
.PutIsUnsynced(true);
1753 parent
.PutIsDir(true);
1754 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1755 parent
.PutId(parent_id_
);
1756 parent
.PutBaseVersion(1);
1759 syncable::Id parent2_id
= ids_
.NewLocalId();
1760 syncable::Id child_id
= ids_
.NewServerId();
1762 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1763 MutableEntry
parent2(
1764 &wtrans
, CREATE
, BOOKMARKS
, parent_id_
, parent2_name
);
1765 ASSERT_TRUE(parent2
.good());
1766 parent2
.PutIsUnsynced(true);
1767 parent2
.PutIsDir(true);
1768 parent2
.PutSpecifics(DefaultBookmarkSpecifics());
1769 parent2
.PutId(parent2_id
);
1772 &wtrans
, CREATE
, BOOKMARKS
, parent2_id
, child_name
);
1773 ASSERT_TRUE(child
.good());
1774 child
.PutIsUnsynced(true);
1775 child
.PutIsDir(true);
1776 child
.PutSpecifics(DefaultBookmarkSpecifics());
1777 child
.PutId(child_id
);
1778 child
.PutBaseVersion(1);
1781 EXPECT_TRUE(SyncShareNudge());
1782 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1783 // If this test starts failing, be aware other sort orders could be valid.
1784 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1785 EXPECT_TRUE(parent2_id
== mock_server_
->committed_ids()[1]);
1786 EXPECT_TRUE(child_id
== mock_server_
->committed_ids()[2]);
1788 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1789 // Check that things committed correctly.
1790 Entry
entry_1(&rtrans
, syncable::GET_BY_ID
, parent_id_
);
1791 EXPECT_EQ(entry_1
.GetNonUniqueName(), parent1_name
);
1792 // Check that parent2 is a subfolder of parent1.
1793 EXPECT_EQ(1, CountEntriesWithName(&rtrans
,
1797 // Parent2 was a local ID and thus should have changed on commit!
1798 Entry
pre_commit_entry_parent2(&rtrans
, syncable::GET_BY_ID
, parent2_id
);
1799 ASSERT_FALSE(pre_commit_entry_parent2
.good());
1801 // Look up the new ID.
1802 Id parent2_committed_id
=
1803 GetOnlyEntryWithName(&rtrans
, parent_id_
, parent2_name
);
1804 EXPECT_TRUE(parent2_committed_id
.ServerKnows());
1806 Entry
child(&rtrans
, syncable::GET_BY_ID
, child_id
);
1807 EXPECT_EQ(parent2_committed_id
, child
.GetParentId());
1811 TEST_F(SyncerTest
, TestCommitListOrderingAndNewParentAndChild
) {
1812 string parent_name
= "1";
1813 string parent2_name
= "A";
1814 string child_name
= "B";
1817 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1818 MutableEntry
parent(&wtrans
,
1822 ASSERT_TRUE(parent
.good());
1823 parent
.PutIsUnsynced(true);
1824 parent
.PutIsDir(true);
1825 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1826 parent
.PutId(parent_id_
);
1827 parent
.PutBaseVersion(1);
1830 int64 meta_handle_b
;
1831 const Id parent2_local_id
= ids_
.NewLocalId();
1832 const Id child_local_id
= ids_
.NewLocalId();
1834 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1835 MutableEntry
parent2(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, parent2_name
);
1836 ASSERT_TRUE(parent2
.good());
1837 parent2
.PutIsUnsynced(true);
1838 parent2
.PutIsDir(true);
1839 parent2
.PutSpecifics(DefaultBookmarkSpecifics());
1841 parent2
.PutId(parent2_local_id
);
1843 &wtrans
, CREATE
, BOOKMARKS
, parent2_local_id
, child_name
);
1844 ASSERT_TRUE(child
.good());
1845 child
.PutIsUnsynced(true);
1846 child
.PutIsDir(true);
1847 child
.PutSpecifics(DefaultBookmarkSpecifics());
1848 child
.PutId(child_local_id
);
1849 meta_handle_b
= child
.GetMetahandle();
1852 EXPECT_TRUE(SyncShareNudge());
1853 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1854 // If this test starts failing, be aware other sort orders could be valid.
1855 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1856 EXPECT_TRUE(parent2_local_id
== mock_server_
->committed_ids()[1]);
1857 EXPECT_TRUE(child_local_id
== mock_server_
->committed_ids()[2]);
1859 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1861 Entry
parent(&rtrans
, syncable::GET_BY_ID
,
1862 GetOnlyEntryWithName(&rtrans
, rtrans
.root_id(), parent_name
));
1863 ASSERT_TRUE(parent
.good());
1864 EXPECT_TRUE(parent
.GetId().ServerKnows());
1866 Entry
parent2(&rtrans
, syncable::GET_BY_ID
,
1867 GetOnlyEntryWithName(&rtrans
, parent
.GetId(), parent2_name
));
1868 ASSERT_TRUE(parent2
.good());
1869 EXPECT_TRUE(parent2
.GetId().ServerKnows());
1871 // Id changed on commit, so this should fail.
1872 Entry
local_parent2_id_entry(&rtrans
,
1873 syncable::GET_BY_ID
,
1875 ASSERT_FALSE(local_parent2_id_entry
.good());
1877 Entry
entry_b(&rtrans
, syncable::GET_BY_HANDLE
, meta_handle_b
);
1878 EXPECT_TRUE(entry_b
.GetId().ServerKnows());
1879 EXPECT_TRUE(parent2
.GetId()== entry_b
.GetParentId());
1883 TEST_F(SyncerTest
, UpdateWithZeroLengthName
) {
1884 // One illegal update
1885 mock_server_
->AddUpdateDirectory(
1886 1, 0, std::string(), 1, 10, foreign_cache_guid(), "-1");
1887 // And one legal one that we're going to delete.
1888 mock_server_
->AddUpdateDirectory(2, 0, "FOO", 1, 10,
1889 foreign_cache_guid(), "-2");
1890 EXPECT_TRUE(SyncShareNudge());
1891 // Delete the legal one. The new update has a null name.
1892 mock_server_
->AddUpdateDirectory(
1893 2, 0, std::string(), 2, 20, foreign_cache_guid(), "-2");
1894 mock_server_
->SetLastUpdateDeleted();
1895 EXPECT_TRUE(SyncShareNudge());
1898 TEST_F(SyncerTest
, TestBasicUpdate
) {
1899 string id
= "some_id";
1900 string parent_id
= "0";
1901 string name
= "in_root";
1903 int64 timestamp
= 10;
1904 mock_server_
->AddUpdateDirectory(id
, parent_id
, name
, version
, timestamp
,
1905 foreign_cache_guid(), "-1");
1907 EXPECT_TRUE(SyncShareNudge());
1909 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1910 Entry
entry(&trans
, GET_BY_ID
,
1911 syncable::Id::CreateFromServerId("some_id"));
1912 ASSERT_TRUE(entry
.good());
1913 EXPECT_TRUE(entry
.GetIsDir());
1914 EXPECT_TRUE(entry
.GetServerVersion()== version
);
1915 EXPECT_TRUE(entry
.GetBaseVersion()== version
);
1916 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
1917 EXPECT_FALSE(entry
.GetIsUnsynced());
1918 EXPECT_FALSE(entry
.GetServerIsDel());
1919 EXPECT_FALSE(entry
.GetIsDel());
1923 TEST_F(SyncerTest
, IllegalAndLegalUpdates
) {
1924 Id root
= TestIdFactory::root();
1925 // Should apply just fine.
1926 mock_server_
->AddUpdateDirectory(1, 0, "in_root", 10, 10,
1927 foreign_cache_guid(), "-1");
1929 // Same name. But this SHOULD work.
1930 mock_server_
->AddUpdateDirectory(2, 0, "in_root", 10, 10,
1931 foreign_cache_guid(), "-2");
1933 // Unknown parent: should never be applied. "-80" is a legal server ID,
1934 // because any string sent by the server is a legal server ID in the sync
1935 // protocol, but it's not the ID of any item known to the client. This
1936 // update should succeed validation, but be stuck in the unapplied state
1937 // until an item with the server ID "-80" arrives.
1938 mock_server_
->AddUpdateDirectory(3, -80, "bad_parent", 10, 10,
1939 foreign_cache_guid(), "-3");
1941 EXPECT_TRUE(SyncShareNudge());
1943 // Id 3 should be in conflict now.
1946 GetUpdateCounters(BOOKMARKS
).num_hierarchy_conflict_application_failures
);
1948 // The only request in that loop should have been a GetUpdate.
1949 // At that point, we didn't know whether or not we had conflicts.
1950 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
1951 VerifyHierarchyConflictsUnspecified(mock_server_
->last_request());
1953 // These entries will be used in the second set of updates.
1954 mock_server_
->AddUpdateDirectory(4, 0, "newer_version", 20, 10,
1955 foreign_cache_guid(), "-4");
1956 mock_server_
->AddUpdateDirectory(5, 0, "circular1", 10, 10,
1957 foreign_cache_guid(), "-5");
1958 mock_server_
->AddUpdateDirectory(6, 5, "circular2", 10, 10,
1959 foreign_cache_guid(), "-6");
1960 mock_server_
->AddUpdateDirectory(9, 3, "bad_parent_child", 10, 10,
1961 foreign_cache_guid(), "-9");
1962 mock_server_
->AddUpdateDirectory(100, 9, "bad_parent_child2", 10, 10,
1963 foreign_cache_guid(), "-100");
1964 mock_server_
->AddUpdateDirectory(10, 0, "dir_to_bookmark", 10, 10,
1965 foreign_cache_guid(), "-10");
1967 EXPECT_TRUE(SyncShareNudge());
1968 // The three items with an unresolved parent should be unapplied (3, 9, 100).
1969 // The name clash should also still be in conflict.
1972 GetUpdateCounters(BOOKMARKS
).num_hierarchy_conflict_application_failures
);
1974 // This time around, we knew that there were conflicts.
1975 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
1976 VerifyHierarchyConflictsReported(mock_server_
->last_request());
1979 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1980 // Even though it has the same name, it should work.
1981 Entry
name_clash(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
1982 ASSERT_TRUE(name_clash
.good());
1983 EXPECT_FALSE(name_clash
.GetIsUnappliedUpdate())
1984 << "Duplicate name SHOULD be OK.";
1986 Entry
bad_parent(&trans
, GET_BY_ID
, ids_
.FromNumber(3));
1987 ASSERT_TRUE(bad_parent
.good());
1988 EXPECT_TRUE(bad_parent
.GetIsUnappliedUpdate())
1989 << "child of unknown parent should be in conflict";
1991 Entry
bad_parent_child(&trans
, GET_BY_ID
, ids_
.FromNumber(9));
1992 ASSERT_TRUE(bad_parent_child
.good());
1993 EXPECT_TRUE(bad_parent_child
.GetIsUnappliedUpdate())
1994 << "grandchild of unknown parent should be in conflict";
1996 Entry
bad_parent_child2(&trans
, GET_BY_ID
, ids_
.FromNumber(100));
1997 ASSERT_TRUE(bad_parent_child2
.good());
1998 EXPECT_TRUE(bad_parent_child2
.GetIsUnappliedUpdate())
1999 << "great-grandchild of unknown parent should be in conflict";
2002 // Updating 1 should not affect item 2 of the same name.
2003 mock_server_
->AddUpdateDirectory(1, 0, "new_name", 20, 20,
2004 foreign_cache_guid(), "-1");
2006 // Moving 5 under 6 will create a cycle: a conflict.
2007 mock_server_
->AddUpdateDirectory(5, 6, "circular3", 20, 20,
2008 foreign_cache_guid(), "-5");
2010 // Flip the is_dir bit: should fail verify & be dropped.
2011 mock_server_
->AddUpdateBookmark(10, 0, "dir_to_bookmark", 20, 20,
2012 foreign_cache_guid(), "-10");
2013 EXPECT_TRUE(SyncShareNudge());
2015 // Version number older than last known: should fail verify & be dropped.
2016 mock_server_
->AddUpdateDirectory(4, 0, "old_version", 10, 10,
2017 foreign_cache_guid(), "-4");
2018 EXPECT_TRUE(SyncShareNudge());
2020 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2022 Entry
still_a_dir(&trans
, GET_BY_ID
, ids_
.FromNumber(10));
2023 ASSERT_TRUE(still_a_dir
.good());
2024 EXPECT_FALSE(still_a_dir
.GetIsUnappliedUpdate());
2025 EXPECT_EQ(10u, still_a_dir
.GetBaseVersion());
2026 EXPECT_EQ(10u, still_a_dir
.GetServerVersion());
2027 EXPECT_TRUE(still_a_dir
.GetIsDir());
2029 Entry
rename(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2030 ASSERT_TRUE(rename
.good());
2031 EXPECT_EQ(root
, rename
.GetParentId());
2032 EXPECT_EQ("new_name", rename
.GetNonUniqueName());
2033 EXPECT_FALSE(rename
.GetIsUnappliedUpdate());
2034 EXPECT_TRUE(ids_
.FromNumber(1) == rename
.GetId());
2035 EXPECT_EQ(20u, rename
.GetBaseVersion());
2037 Entry
name_clash(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
2038 ASSERT_TRUE(name_clash
.good());
2039 EXPECT_EQ(root
, name_clash
.GetParentId());
2040 EXPECT_TRUE(ids_
.FromNumber(2) == name_clash
.GetId());
2041 EXPECT_EQ(10u, name_clash
.GetBaseVersion());
2042 EXPECT_EQ("in_root", name_clash
.GetNonUniqueName());
2044 Entry
ignored_old_version(&trans
, GET_BY_ID
, ids_
.FromNumber(4));
2045 ASSERT_TRUE(ignored_old_version
.good());
2047 ignored_old_version
.GetNonUniqueName()== "newer_version");
2048 EXPECT_FALSE(ignored_old_version
.GetIsUnappliedUpdate());
2049 EXPECT_EQ(20u, ignored_old_version
.GetBaseVersion());
2051 Entry
circular_parent_issue(&trans
, GET_BY_ID
, ids_
.FromNumber(5));
2052 ASSERT_TRUE(circular_parent_issue
.good());
2053 EXPECT_TRUE(circular_parent_issue
.GetIsUnappliedUpdate())
2054 << "circular move should be in conflict";
2055 EXPECT_TRUE(circular_parent_issue
.GetParentId()== root_id_
);
2056 EXPECT_TRUE(circular_parent_issue
.GetServerParentId()==
2057 ids_
.FromNumber(6));
2058 EXPECT_EQ(10u, circular_parent_issue
.GetBaseVersion());
2060 Entry
circular_parent_target(&trans
, GET_BY_ID
, ids_
.FromNumber(6));
2061 ASSERT_TRUE(circular_parent_target
.good());
2062 EXPECT_FALSE(circular_parent_target
.GetIsUnappliedUpdate());
2063 EXPECT_TRUE(circular_parent_issue
.GetId()==
2064 circular_parent_target
.GetParentId());
2065 EXPECT_EQ(10u, circular_parent_target
.GetBaseVersion());
2068 EXPECT_FALSE(saw_syncer_event_
);
2071 GetUpdateCounters(BOOKMARKS
).num_hierarchy_conflict_application_failures
);
2074 // A commit with a lost response produces an update that has to be reunited with
2076 TEST_F(SyncerTest
, CommitReuniteUpdateAdjustsChildren
) {
2077 // Create a folder in the root.
2078 int64 metahandle_folder
;
2080 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2082 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_folder");
2083 ASSERT_TRUE(entry
.good());
2084 entry
.PutIsDir(true);
2085 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2086 entry
.PutIsUnsynced(true);
2087 metahandle_folder
= entry
.GetMetahandle();
2090 // Verify it and pull the ID out of the folder.
2091 syncable::Id folder_id
;
2092 int64 metahandle_entry
;
2094 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2095 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_folder
);
2096 ASSERT_TRUE(entry
.good());
2097 folder_id
= entry
.GetId();
2098 ASSERT_TRUE(!folder_id
.ServerKnows());
2101 // Create an entry in the newly created folder.
2103 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2104 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, folder_id
, "new_entry");
2105 ASSERT_TRUE(entry
.good());
2106 metahandle_entry
= entry
.GetMetahandle();
2107 WriteTestDataToEntry(&trans
, &entry
);
2110 // Verify it and pull the ID out of the entry.
2111 syncable::Id entry_id
;
2113 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2114 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, metahandle_entry
);
2115 ASSERT_TRUE(entry
.good());
2116 EXPECT_EQ(folder_id
, entry
.GetParentId());
2117 EXPECT_EQ("new_entry", entry
.GetNonUniqueName());
2118 entry_id
= entry
.GetId();
2119 EXPECT_TRUE(!entry_id
.ServerKnows());
2120 VerifyTestDataInEntry(&trans
, &entry
);
2123 // Now, to emulate a commit response failure, we just don't commit it.
2124 int64 new_version
= 150; // any larger value.
2125 int64 timestamp
= 20; // arbitrary value.
2126 syncable::Id new_folder_id
=
2127 syncable::Id::CreateFromServerId("folder_server_id");
2129 // The following update should cause the folder to both apply the update, as
2130 // well as reassociate the id.
2131 mock_server_
->AddUpdateDirectory(new_folder_id
, root_id_
,
2132 "new_folder", new_version
, timestamp
,
2133 local_cache_guid(), folder_id
.GetServerId());
2135 // We don't want it accidentally committed, just the update applied.
2136 mock_server_
->set_conflict_all_commits(true);
2138 // Alright! Apply that update!
2139 EXPECT_FALSE(SyncShareNudge());
2141 // The folder's ID should have been updated.
2142 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2143 Entry
folder(&trans
, GET_BY_HANDLE
, metahandle_folder
);
2144 ASSERT_TRUE(folder
.good());
2145 EXPECT_EQ("new_folder", folder
.GetNonUniqueName());
2146 EXPECT_TRUE(new_version
== folder
.GetBaseVersion());
2147 EXPECT_TRUE(new_folder_id
== folder
.GetId());
2148 EXPECT_TRUE(folder
.GetId().ServerKnows());
2149 EXPECT_EQ(trans
.root_id(), folder
.GetParentId());
2151 // Since it was updated, the old folder should not exist.
2152 Entry
old_dead_folder(&trans
, GET_BY_ID
, folder_id
);
2153 EXPECT_FALSE(old_dead_folder
.good());
2155 // The child's parent should have changed.
2156 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, metahandle_entry
);
2157 ASSERT_TRUE(entry
.good());
2158 EXPECT_EQ("new_entry", entry
.GetNonUniqueName());
2159 EXPECT_EQ(new_folder_id
, entry
.GetParentId());
2160 EXPECT_TRUE(!entry
.GetId().ServerKnows());
2161 VerifyTestDataInEntry(&trans
, &entry
);
2165 // A commit with a lost response produces an update that has to be reunited with
2167 TEST_F(SyncerTest
, CommitReuniteUpdate
) {
2168 // Create an entry in the root.
2169 int64 entry_metahandle
;
2171 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2172 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_entry");
2173 ASSERT_TRUE(entry
.good());
2174 entry_metahandle
= entry
.GetMetahandle();
2175 WriteTestDataToEntry(&trans
, &entry
);
2178 // Verify it and pull the ID out.
2179 syncable::Id entry_id
;
2181 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2183 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2184 ASSERT_TRUE(entry
.good());
2185 entry_id
= entry
.GetId();
2186 EXPECT_TRUE(!entry_id
.ServerKnows());
2187 VerifyTestDataInEntry(&trans
, &entry
);
2190 // Now, to emulate a commit response failure, we just don't commit it.
2191 int64 new_version
= 150; // any larger value.
2192 int64 timestamp
= 20; // arbitrary value.
2193 syncable::Id new_entry_id
= syncable::Id::CreateFromServerId("server_id");
2195 // Generate an update from the server with a relevant ID reassignment.
2196 mock_server_
->AddUpdateBookmark(new_entry_id
, root_id_
,
2197 "new_entry", new_version
, timestamp
,
2198 local_cache_guid(), entry_id
.GetServerId());
2200 // We don't want it accidentally committed, just the update applied.
2201 mock_server_
->set_conflict_all_commits(true);
2203 // Alright! Apply that update!
2204 EXPECT_TRUE(SyncShareNudge());
2206 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2207 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2208 ASSERT_TRUE(entry
.good());
2209 EXPECT_TRUE(new_version
== entry
.GetBaseVersion());
2210 EXPECT_TRUE(new_entry_id
== entry
.GetId());
2211 EXPECT_EQ("new_entry", entry
.GetNonUniqueName());
2215 // A commit with a lost response must work even if the local entry was deleted
2216 // before the update is applied. We should not duplicate the local entry in
2217 // this case, but just create another one alongside. We may wish to examine
2218 // this behavior in the future as it can create hanging uploads that never
2219 // finish, that must be cleaned up on the server side after some time.
2220 TEST_F(SyncerTest
, CommitReuniteUpdateDoesNotChokeOnDeletedLocalEntry
) {
2221 // Create a entry in the root.
2222 int64 entry_metahandle
;
2224 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2225 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_entry");
2226 ASSERT_TRUE(entry
.good());
2227 entry_metahandle
= entry
.GetMetahandle();
2228 WriteTestDataToEntry(&trans
, &entry
);
2230 // Verify it and pull the ID out.
2231 syncable::Id entry_id
;
2233 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2234 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2235 ASSERT_TRUE(entry
.good());
2236 entry_id
= entry
.GetId();
2237 EXPECT_TRUE(!entry_id
.ServerKnows());
2238 VerifyTestDataInEntry(&trans
, &entry
);
2241 // Now, to emulate a commit response failure, we just don't commit it.
2242 int64 new_version
= 150; // any larger value.
2243 int64 timestamp
= 20; // arbitrary value.
2244 syncable::Id new_entry_id
= syncable::Id::CreateFromServerId("server_id");
2246 // Generate an update from the server with a relevant ID reassignment.
2247 mock_server_
->AddUpdateBookmark(new_entry_id
, root_id_
,
2248 "new_entry", new_version
, timestamp
,
2249 local_cache_guid(), entry_id
.GetServerId());
2251 // We don't want it accidentally committed, just the update applied.
2252 mock_server_
->set_conflict_all_commits(true);
2254 // Purposefully delete the entry now before the update application finishes.
2256 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2257 Id new_entry_id
= GetOnlyEntryWithName(
2258 &trans
, trans
.root_id(), "new_entry");
2259 MutableEntry
entry(&trans
, GET_BY_ID
, new_entry_id
);
2260 ASSERT_TRUE(entry
.good());
2261 entry
.PutIsDel(true);
2264 // Just don't CHECK fail in sync, have the update split.
2265 EXPECT_TRUE(SyncShareNudge());
2267 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2268 Id new_entry_id
= GetOnlyEntryWithName(
2269 &trans
, trans
.root_id(), "new_entry");
2270 Entry
entry(&trans
, GET_BY_ID
, new_entry_id
);
2271 ASSERT_TRUE(entry
.good());
2272 EXPECT_FALSE(entry
.GetIsDel());
2274 Entry
old_entry(&trans
, GET_BY_ID
, entry_id
);
2275 ASSERT_TRUE(old_entry
.good());
2276 EXPECT_TRUE(old_entry
.GetIsDel());
2280 // TODO(chron): Add more unsanitized name tests.
2281 TEST_F(SyncerTest
, ConflictMatchingEntryHandlesUnsanitizedNames
) {
2282 mock_server_
->AddUpdateDirectory(1, 0, "A/A", 10, 10,
2283 foreign_cache_guid(), "-1");
2284 mock_server_
->AddUpdateDirectory(2, 0, "B/B", 10, 10,
2285 foreign_cache_guid(), "-2");
2286 mock_server_
->set_conflict_all_commits(true);
2287 EXPECT_TRUE(SyncShareNudge());
2289 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2291 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2292 ASSERT_TRUE(A
.good());
2293 A
.PutIsUnsynced(true);
2294 A
.PutIsUnappliedUpdate(true);
2295 A
.PutServerVersion(20);
2297 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
2298 ASSERT_TRUE(B
.good());
2299 B
.PutIsUnappliedUpdate(true);
2300 B
.PutServerVersion(20);
2302 EXPECT_TRUE(SyncShareNudge());
2303 saw_syncer_event_
= false;
2304 mock_server_
->set_conflict_all_commits(false);
2307 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2309 Entry
A(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2310 ASSERT_TRUE(A
.good());
2311 EXPECT_TRUE(A
.GetIsUnsynced()== false);
2312 EXPECT_TRUE(A
.GetIsUnappliedUpdate()== false);
2313 EXPECT_TRUE(A
.GetServerVersion()== 20);
2315 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
2316 ASSERT_TRUE(B
.good());
2317 EXPECT_TRUE(B
.GetIsUnsynced()== false);
2318 EXPECT_TRUE(B
.GetIsUnappliedUpdate()== false);
2319 EXPECT_TRUE(B
.GetServerVersion()== 20);
2323 TEST_F(SyncerTest
, ConflictMatchingEntryHandlesNormalNames
) {
2324 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
2325 foreign_cache_guid(), "-1");
2326 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
2327 foreign_cache_guid(), "-2");
2328 mock_server_
->set_conflict_all_commits(true);
2329 EXPECT_TRUE(SyncShareNudge());
2331 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2333 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2334 ASSERT_TRUE(A
.good());
2335 A
.PutIsUnsynced(true);
2336 A
.PutIsUnappliedUpdate(true);
2337 A
.PutServerVersion(20);
2339 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
2340 ASSERT_TRUE(B
.good());
2341 B
.PutIsUnappliedUpdate(true);
2342 B
.PutServerVersion(20);
2344 EXPECT_TRUE(SyncShareNudge());
2345 saw_syncer_event_
= false;
2346 mock_server_
->set_conflict_all_commits(false);
2349 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2351 Entry
A(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2352 ASSERT_TRUE(A
.good());
2353 EXPECT_TRUE(A
.GetIsUnsynced()== false);
2354 EXPECT_TRUE(A
.GetIsUnappliedUpdate()== false);
2355 EXPECT_TRUE(A
.GetServerVersion()== 20);
2357 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
2358 ASSERT_TRUE(B
.good());
2359 EXPECT_TRUE(B
.GetIsUnsynced()== false);
2360 EXPECT_TRUE(B
.GetIsUnappliedUpdate()== false);
2361 EXPECT_TRUE(B
.GetServerVersion()== 20);
2365 TEST_F(SyncerTest
, ReverseFolderOrderingTest
) {
2366 mock_server_
->AddUpdateDirectory(4, 3, "ggchild", 10, 10,
2367 foreign_cache_guid(), "-4");
2368 mock_server_
->AddUpdateDirectory(3, 2, "gchild", 10, 10,
2369 foreign_cache_guid(), "-3");
2370 mock_server_
->AddUpdateDirectory(5, 4, "gggchild", 10, 10,
2371 foreign_cache_guid(), "-5");
2372 mock_server_
->AddUpdateDirectory(2, 1, "child", 10, 10,
2373 foreign_cache_guid(), "-2");
2374 mock_server_
->AddUpdateDirectory(1, 0, "parent", 10, 10,
2375 foreign_cache_guid(), "-1");
2376 EXPECT_TRUE(SyncShareNudge());
2377 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2379 Id child_id
= GetOnlyEntryWithName(
2380 &trans
, ids_
.FromNumber(4), "gggchild");
2381 Entry
child(&trans
, GET_BY_ID
, child_id
);
2382 ASSERT_TRUE(child
.good());
2385 class EntryCreatedInNewFolderTest
: public SyncerTest
{
2387 void CreateFolderInBob() {
2388 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2389 MutableEntry
bob(&trans
,
2390 syncable::GET_BY_ID
,
2391 GetOnlyEntryWithName(&trans
,
2392 TestIdFactory::root(),
2396 MutableEntry
entry2(
2397 &trans
, CREATE
, BOOKMARKS
, bob
.GetId(), "bob");
2398 CHECK(entry2
.good());
2399 entry2
.PutIsDir(true);
2400 entry2
.PutIsUnsynced(true);
2401 entry2
.PutSpecifics(DefaultBookmarkSpecifics());
2405 TEST_F(EntryCreatedInNewFolderTest
, EntryCreatedInNewFolderMidSync
) {
2407 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2408 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2409 ASSERT_TRUE(entry
.good());
2410 entry
.PutIsDir(true);
2411 entry
.PutIsUnsynced(true);
2412 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2415 mock_server_
->SetMidCommitCallback(
2416 base::Bind(&EntryCreatedInNewFolderTest::CreateFolderInBob
,
2417 base::Unretained(this)));
2418 EXPECT_TRUE(SyncShareNudge());
2419 // We loop until no unsynced handles remain, so we will commit both ids.
2420 EXPECT_EQ(2u, mock_server_
->committed_ids().size());
2422 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2423 Entry
parent_entry(&trans
, syncable::GET_BY_ID
,
2424 GetOnlyEntryWithName(&trans
, TestIdFactory::root(), "bob"));
2425 ASSERT_TRUE(parent_entry
.good());
2428 GetOnlyEntryWithName(&trans
, parent_entry
.GetId(), "bob");
2429 Entry
child(&trans
, syncable::GET_BY_ID
, child_id
);
2430 ASSERT_TRUE(child
.good());
2431 EXPECT_EQ(parent_entry
.GetId(), child
.GetParentId());
2435 TEST_F(SyncerTest
, NegativeIDInUpdate
) {
2436 mock_server_
->AddUpdateBookmark(-10, 0, "bad", 40, 40,
2437 foreign_cache_guid(), "-100");
2438 EXPECT_TRUE(SyncShareNudge());
2439 // The negative id would make us CHECK!
2442 TEST_F(SyncerTest
, UnappliedUpdateOnCreatedItemItemDoesNotCrash
) {
2443 int64 metahandle_fred
;
2444 syncable::Id orig_id
;
2447 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2448 MutableEntry
fred_match(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(),
2450 ASSERT_TRUE(fred_match
.good());
2451 metahandle_fred
= fred_match
.GetMetahandle();
2452 orig_id
= fred_match
.GetId();
2453 WriteTestDataToEntry(&trans
, &fred_match
);
2456 EXPECT_TRUE(SyncShareNudge());
2457 EXPECT_EQ(1u, mock_server_
->committed_ids().size());
2458 mock_server_
->set_conflict_all_commits(true);
2459 syncable::Id fred_match_id
;
2461 // Now receive a change from outside.
2462 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2463 MutableEntry
fred_match(&trans
, GET_BY_HANDLE
, metahandle_fred
);
2464 ASSERT_TRUE(fred_match
.good());
2465 EXPECT_TRUE(fred_match
.GetId().ServerKnows());
2466 fred_match_id
= fred_match
.GetId();
2467 mock_server_
->AddUpdateBookmark(fred_match_id
, trans
.root_id(),
2468 "fred_match", 40, 40, local_cache_guid(), orig_id
.GetServerId());
2471 for (int i
= 0 ; i
< 30 ; ++i
) {
2472 EXPECT_TRUE(SyncShareNudge());
2477 * In the event that we have a double changed entry, that is changed on both
2478 * the client and the server, the conflict resolver should just drop one of
2479 * them and accept the other.
2482 TEST_F(SyncerTest
, DoublyChangedWithResolver
) {
2483 syncable::Id local_id
;
2485 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2486 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Folder");
2487 ASSERT_TRUE(parent
.good());
2488 parent
.PutIsDir(true);
2489 parent
.PutId(parent_id_
);
2490 parent
.PutBaseVersion(5);
2491 parent
.PutSpecifics(DefaultBookmarkSpecifics());
2492 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete.htm");
2493 ASSERT_TRUE(child
.good());
2494 local_id
= child
.GetId();
2495 child
.PutId(child_id_
);
2496 child
.PutBaseVersion(10);
2497 WriteTestDataToEntry(&wtrans
, &child
);
2499 mock_server_
->AddUpdateBookmark(child_id_
, parent_id_
, "Pete2.htm", 11, 10,
2500 local_cache_guid(), local_id
.GetServerId());
2501 mock_server_
->set_conflict_all_commits(true);
2502 EXPECT_FALSE(SyncShareNudge());
2503 syncable::Directory::Metahandles children
;
2505 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2506 directory()->GetChildHandlesById(&trans
, parent_id_
, &children
);
2507 // We expect the conflict resolver to preserve the local entry.
2508 Entry
child(&trans
, syncable::GET_BY_ID
, child_id_
);
2509 ASSERT_TRUE(child
.good());
2510 EXPECT_TRUE(child
.GetIsUnsynced());
2511 EXPECT_FALSE(child
.GetIsUnappliedUpdate());
2512 EXPECT_TRUE(child
.GetSpecifics().has_bookmark());
2513 EXPECT_EQ("Pete.htm", child
.GetNonUniqueName());
2514 VerifyTestBookmarkDataInEntry(&child
);
2517 // Only one entry, since we just overwrite one.
2518 EXPECT_EQ(1u, children
.size());
2519 saw_syncer_event_
= false;
2522 // We got this repro case when someone was editing bookmarks while sync was
2523 // occuring. The entry had changed out underneath the user.
2524 TEST_F(SyncerTest
, CommitsUpdateDoesntAlterEntry
) {
2525 const base::Time
& test_time
= ProtoTimeToTime(123456);
2526 syncable::Id local_id
;
2527 int64 entry_metahandle
;
2529 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2530 MutableEntry
entry(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Pete");
2531 ASSERT_TRUE(entry
.good());
2532 EXPECT_FALSE(entry
.GetId().ServerKnows());
2533 local_id
= entry
.GetId();
2534 entry
.PutIsDir(true);
2535 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2536 entry
.PutIsUnsynced(true);
2537 entry
.PutMtime(test_time
);
2538 entry_metahandle
= entry
.GetMetahandle();
2540 EXPECT_TRUE(SyncShareNudge());
2544 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2545 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, entry_metahandle
);
2546 ASSERT_TRUE(entry
.good());
2548 EXPECT_TRUE(id
.ServerKnows());
2549 version
= entry
.GetBaseVersion();
2551 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
2552 update
->set_originator_cache_guid(local_cache_guid());
2553 update
->set_originator_client_item_id(local_id
.GetServerId());
2554 EXPECT_EQ("Pete", update
->name());
2555 EXPECT_EQ(id
.GetServerId(), update
->id_string());
2556 EXPECT_EQ(root_id_
.GetServerId(), update
->parent_id_string());
2557 EXPECT_EQ(version
, update
->version());
2558 EXPECT_TRUE(SyncShareNudge());
2560 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2561 Entry
entry(&trans
, syncable::GET_BY_ID
, id
);
2562 ASSERT_TRUE(entry
.good());
2563 EXPECT_TRUE(entry
.GetMtime()== test_time
);
2567 TEST_F(SyncerTest
, ParentAndChildBothMatch
) {
2568 // Disable PREFERENCES which is enabled at the setup step to avoid
2570 // PREFERENCES root folder and failing the test below that verifies the number
2571 // of children at the root.
2572 DisableDatatype(PREFERENCES
);
2574 const FullModelTypeSet all_types
= FullModelTypeSet::All();
2575 syncable::Id parent_id
= ids_
.NewServerId();
2576 syncable::Id child_id
= ids_
.NewServerId();
2577 syncable::Id parent_local_id
;
2578 syncable::Id child_local_id
;
2581 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2582 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Folder");
2583 ASSERT_TRUE(parent
.good());
2584 parent_local_id
= parent
.GetId();
2585 parent
.PutIsDir(true);
2586 parent
.PutIsUnsynced(true);
2587 parent
.PutId(parent_id
);
2588 parent
.PutBaseVersion(1);
2589 parent
.PutSpecifics(DefaultBookmarkSpecifics());
2591 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent
.GetId(), "test.htm");
2592 ASSERT_TRUE(child
.good());
2593 child_local_id
= child
.GetId();
2594 child
.PutId(child_id
);
2595 child
.PutBaseVersion(1);
2596 child
.PutSpecifics(DefaultBookmarkSpecifics());
2597 WriteTestDataToEntry(&wtrans
, &child
);
2599 mock_server_
->AddUpdateDirectory(parent_id
, root_id_
, "Folder", 10, 10,
2601 parent_local_id
.GetServerId());
2602 mock_server_
->AddUpdateBookmark(child_id
, parent_id
, "test.htm", 10, 10,
2604 child_local_id
.GetServerId());
2605 mock_server_
->set_conflict_all_commits(true);
2606 EXPECT_TRUE(SyncShareNudge());
2607 EXPECT_TRUE(SyncShareNudge());
2608 EXPECT_TRUE(SyncShareNudge());
2610 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2611 Directory::Metahandles children
;
2612 directory()->GetChildHandlesById(&trans
, root_id_
, &children
);
2613 EXPECT_EQ(1u, children
.size());
2614 directory()->GetChildHandlesById(&trans
, parent_id
, &children
);
2615 EXPECT_EQ(1u, children
.size());
2616 std::vector
<int64
> unapplied
;
2617 directory()->GetUnappliedUpdateMetaHandles(&trans
, all_types
, &unapplied
);
2618 EXPECT_EQ(0u, unapplied
.size());
2619 syncable::Directory::Metahandles unsynced
;
2620 directory()->GetUnsyncedMetaHandles(&trans
, &unsynced
);
2621 EXPECT_EQ(0u, unsynced
.size());
2622 saw_syncer_event_
= false;
2626 TEST_F(SyncerTest
, CommittingNewDeleted
) {
2628 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2629 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2630 entry
.PutIsUnsynced(true);
2631 entry
.PutIsDel(true);
2633 EXPECT_TRUE(SyncShareNudge());
2634 EXPECT_EQ(0u, mock_server_
->committed_ids().size());
2637 // Original problem synopsis:
2638 // Check failed: entry->GetBaseVersion()<= entry->GetServerVersion()
2639 // Client creates entry, client finishes committing entry. Between
2640 // commit and getting update back, we delete the entry.
2641 // We get the update for the entry, but the local one was modified
2642 // so we store the entry but don't apply it. IS_UNAPPLIED_UPDATE is set.
2643 // We commit deletion and get a new version number.
2644 // We apply unapplied updates again before we get the update about the deletion.
2645 // This means we have an unapplied update where server_version < base_version.
2646 TEST_F(SyncerTest
, UnappliedUpdateDuringCommit
) {
2647 // This test is a little fake.
2649 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2650 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2651 entry
.PutId(ids_
.FromNumber(20));
2652 entry
.PutBaseVersion(1);
2653 entry
.PutServerVersion(1);
2654 entry
.PutServerParentId(ids_
.FromNumber(9999)); // Bad parent.
2655 entry
.PutIsUnsynced(true);
2656 entry
.PutIsUnappliedUpdate(true);
2657 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2658 entry
.PutServerSpecifics(DefaultBookmarkSpecifics());
2659 entry
.PutIsDel(false);
2661 EXPECT_TRUE(SyncShareNudge());
2662 EXPECT_EQ(1, session_
->status_controller().TotalNumConflictingItems());
2663 saw_syncer_event_
= false;
2666 // Original problem synopsis:
2668 // Unexpected error during sync if we:
2669 // make a new folder bob
2671 // make a new folder fred
2672 // move bob into fred
2675 // if no syncing occured midway, bob will have an illegal parent
2676 TEST_F(SyncerTest
, DeletingEntryInFolder
) {
2677 // This test is a little fake.
2678 int64 existing_metahandle
;
2680 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2681 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "existing");
2682 ASSERT_TRUE(entry
.good());
2683 entry
.PutIsDir(true);
2684 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2685 entry
.PutIsUnsynced(true);
2686 existing_metahandle
= entry
.GetMetahandle();
2688 EXPECT_TRUE(SyncShareNudge());
2690 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2691 MutableEntry
newfolder(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new");
2692 ASSERT_TRUE(newfolder
.good());
2693 newfolder
.PutIsDir(true);
2694 newfolder
.PutSpecifics(DefaultBookmarkSpecifics());
2695 newfolder
.PutIsUnsynced(true);
2697 MutableEntry
existing(&trans
, GET_BY_HANDLE
, existing_metahandle
);
2698 ASSERT_TRUE(existing
.good());
2699 existing
.PutParentId(newfolder
.GetId());
2700 existing
.PutIsUnsynced(true);
2701 EXPECT_TRUE(existing
.GetId().ServerKnows());
2703 newfolder
.PutIsDel(true);
2704 existing
.PutIsDel(true);
2706 EXPECT_TRUE(SyncShareNudge());
2707 EXPECT_EQ(0, GetCommitCounters(BOOKMARKS
).num_commits_conflict
);
2710 TEST_F(SyncerTest
, DeletingEntryWithLocalEdits
) {
2711 int64 newfolder_metahandle
;
2713 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
2714 foreign_cache_guid(), "-1");
2715 EXPECT_TRUE(SyncShareNudge());
2717 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2718 MutableEntry
newfolder(
2719 &trans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(1), "local");
2720 ASSERT_TRUE(newfolder
.good());
2721 newfolder
.PutIsUnsynced(true);
2722 newfolder
.PutIsDir(true);
2723 newfolder
.PutSpecifics(DefaultBookmarkSpecifics());
2724 newfolder_metahandle
= newfolder
.GetMetahandle();
2726 mock_server_
->AddUpdateDirectory(1, 0, "bob", 2, 20,
2727 foreign_cache_guid(), "-1");
2728 mock_server_
->SetLastUpdateDeleted();
2729 SyncShareConfigure();
2731 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2732 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, newfolder_metahandle
);
2733 ASSERT_TRUE(entry
.good());
2737 TEST_F(SyncerTest
, FolderSwapUpdate
) {
2738 mock_server_
->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2739 foreign_cache_guid(), "-7801");
2740 mock_server_
->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2741 foreign_cache_guid(), "-1024");
2742 EXPECT_TRUE(SyncShareNudge());
2743 mock_server_
->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2744 foreign_cache_guid(), "-1024");
2745 mock_server_
->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2746 foreign_cache_guid(), "-7801");
2747 EXPECT_TRUE(SyncShareNudge());
2749 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2750 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2751 ASSERT_TRUE(id1
.good());
2752 EXPECT_TRUE("fred" == id1
.GetNonUniqueName());
2753 EXPECT_TRUE(root_id_
== id1
.GetParentId());
2754 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2755 ASSERT_TRUE(id2
.good());
2756 EXPECT_TRUE("bob" == id2
.GetNonUniqueName());
2757 EXPECT_TRUE(root_id_
== id2
.GetParentId());
2759 saw_syncer_event_
= false;
2762 TEST_F(SyncerTest
, NameCollidingFolderSwapWorksFine
) {
2763 mock_server_
->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2764 foreign_cache_guid(), "-7801");
2765 mock_server_
->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2766 foreign_cache_guid(), "-1024");
2767 mock_server_
->AddUpdateDirectory(4096, 0, "alice", 1, 10,
2768 foreign_cache_guid(), "-4096");
2769 EXPECT_TRUE(SyncShareNudge());
2771 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2772 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2773 ASSERT_TRUE(id1
.good());
2774 EXPECT_TRUE("bob" == id1
.GetNonUniqueName());
2775 EXPECT_TRUE(root_id_
== id1
.GetParentId());
2776 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2777 ASSERT_TRUE(id2
.good());
2778 EXPECT_TRUE("fred" == id2
.GetNonUniqueName());
2779 EXPECT_TRUE(root_id_
== id2
.GetParentId());
2780 Entry
id3(&trans
, GET_BY_ID
, ids_
.FromNumber(4096));
2781 ASSERT_TRUE(id3
.good());
2782 EXPECT_TRUE("alice" == id3
.GetNonUniqueName());
2783 EXPECT_TRUE(root_id_
== id3
.GetParentId());
2785 mock_server_
->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2786 foreign_cache_guid(), "-1024");
2787 mock_server_
->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2788 foreign_cache_guid(), "-7801");
2789 mock_server_
->AddUpdateDirectory(4096, 0, "bob", 2, 20,
2790 foreign_cache_guid(), "-4096");
2791 EXPECT_TRUE(SyncShareNudge());
2793 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2794 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2795 ASSERT_TRUE(id1
.good());
2796 EXPECT_TRUE("fred" == id1
.GetNonUniqueName());
2797 EXPECT_TRUE(root_id_
== id1
.GetParentId());
2798 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2799 ASSERT_TRUE(id2
.good());
2800 EXPECT_TRUE("bob" == id2
.GetNonUniqueName());
2801 EXPECT_TRUE(root_id_
== id2
.GetParentId());
2802 Entry
id3(&trans
, GET_BY_ID
, ids_
.FromNumber(4096));
2803 ASSERT_TRUE(id3
.good());
2804 EXPECT_TRUE("bob" == id3
.GetNonUniqueName());
2805 EXPECT_TRUE(root_id_
== id3
.GetParentId());
2807 saw_syncer_event_
= false;
2810 // Committing more than kDefaultMaxCommitBatchSize items requires that
2811 // we post more than one commit command to the server. This test makes
2812 // sure that scenario works as expected.
2813 TEST_F(SyncerTest
, CommitManyItemsInOneGo_Success
) {
2814 uint32 num_batches
= 3;
2815 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2817 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2818 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2819 string nameutf8
= base::UintToString(i
);
2820 string
name(nameutf8
.begin(), nameutf8
.end());
2821 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2822 e
.PutIsUnsynced(true);
2824 e
.PutSpecifics(DefaultBookmarkSpecifics());
2827 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2829 EXPECT_TRUE(SyncShareNudge());
2830 EXPECT_EQ(num_batches
, mock_server_
->commit_messages().size());
2831 EXPECT_EQ(0, directory()->unsynced_entity_count());
2834 // Test that a single failure to contact the server will cause us to exit the
2835 // commit loop immediately.
2836 TEST_F(SyncerTest
, CommitManyItemsInOneGo_PostBufferFail
) {
2837 uint32 num_batches
= 3;
2838 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2840 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2841 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2842 string nameutf8
= base::UintToString(i
);
2843 string
name(nameutf8
.begin(), nameutf8
.end());
2844 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2845 e
.PutIsUnsynced(true);
2847 e
.PutSpecifics(DefaultBookmarkSpecifics());
2850 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2852 // The second commit should fail. It will be preceded by one successful
2853 // GetUpdate and one succesful commit.
2854 mock_server_
->FailNthPostBufferToPathCall(3);
2855 EXPECT_FALSE(SyncShareNudge());
2857 EXPECT_EQ(1U, mock_server_
->commit_messages().size());
2858 EXPECT_EQ(SYNC_SERVER_ERROR
,
2859 session_
->status_controller().model_neutral_state().commit_result
);
2860 EXPECT_EQ(items_to_commit
- kDefaultMaxCommitBatchSize
,
2861 directory()->unsynced_entity_count());
2864 // Test that a single conflict response from the server will cause us to exit
2865 // the commit loop immediately.
2866 TEST_F(SyncerTest
, CommitManyItemsInOneGo_CommitConflict
) {
2867 uint32 num_batches
= 2;
2868 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2870 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2871 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2872 string nameutf8
= base::UintToString(i
);
2873 string
name(nameutf8
.begin(), nameutf8
.end());
2874 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2875 e
.PutIsUnsynced(true);
2877 e
.PutSpecifics(DefaultBookmarkSpecifics());
2880 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2882 // Return a CONFLICT response for the first item.
2883 mock_server_
->set_conflict_n_commits(1);
2884 EXPECT_FALSE(SyncShareNudge());
2886 // We should stop looping at the first sign of trouble.
2887 EXPECT_EQ(1U, mock_server_
->commit_messages().size());
2888 EXPECT_EQ(items_to_commit
- (kDefaultMaxCommitBatchSize
- 1),
2889 directory()->unsynced_entity_count());
2892 // Tests that sending debug info events works.
2893 TEST_F(SyncerTest
, SendDebugInfoEventsOnGetUpdates_HappyCase
) {
2894 debug_info_getter_
->AddDebugEvent();
2895 debug_info_getter_
->AddDebugEvent();
2897 EXPECT_TRUE(SyncShareNudge());
2899 // Verify we received one GetUpdates request with two debug info events.
2900 EXPECT_EQ(1U, mock_server_
->requests().size());
2901 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2902 EXPECT_EQ(2, mock_server_
->last_request().debug_info().events_size());
2904 EXPECT_TRUE(SyncShareNudge());
2906 // See that we received another GetUpdates request, but that it contains no
2907 // debug info events.
2908 EXPECT_EQ(2U, mock_server_
->requests().size());
2909 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2910 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
2912 debug_info_getter_
->AddDebugEvent();
2914 EXPECT_TRUE(SyncShareNudge());
2916 // See that we received another GetUpdates request and it contains one debug
2918 EXPECT_EQ(3U, mock_server_
->requests().size());
2919 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2920 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
2923 // Tests that debug info events are dropped on server error.
2924 TEST_F(SyncerTest
, SendDebugInfoEventsOnGetUpdates_PostFailsDontDrop
) {
2925 debug_info_getter_
->AddDebugEvent();
2926 debug_info_getter_
->AddDebugEvent();
2928 mock_server_
->FailNextPostBufferToPathCall();
2929 EXPECT_FALSE(SyncShareNudge());
2931 // Verify we attempted to send one GetUpdates request with two debug info
2933 EXPECT_EQ(1U, mock_server_
->requests().size());
2934 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2935 EXPECT_EQ(2, mock_server_
->last_request().debug_info().events_size());
2937 EXPECT_TRUE(SyncShareNudge());
2939 // See that the client resent the two debug info events.
2940 EXPECT_EQ(2U, mock_server_
->requests().size());
2941 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2942 EXPECT_EQ(2, mock_server_
->last_request().debug_info().events_size());
2944 // The previous send was successful so this next one shouldn't generate any
2945 // debug info events.
2946 EXPECT_TRUE(SyncShareNudge());
2947 EXPECT_EQ(3U, mock_server_
->requests().size());
2948 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2949 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
2952 // Tests that commit failure with conflict will trigger GetUpdates for next
2954 TEST_F(SyncerTest
, CommitFailureWithConflict
) {
2955 ConfigureNoGetUpdatesRequired();
2956 CreateUnsyncedDirectory("X", "id_X");
2957 EXPECT_FALSE(nudge_tracker_
.IsGetUpdatesRequired());
2959 EXPECT_TRUE(SyncShareNudge());
2960 EXPECT_FALSE(nudge_tracker_
.IsGetUpdatesRequired());
2962 CreateUnsyncedDirectory("Y", "id_Y");
2963 mock_server_
->set_conflict_n_commits(1);
2964 EXPECT_FALSE(SyncShareNudge());
2965 EXPECT_TRUE(nudge_tracker_
.IsGetUpdatesRequired());
2967 nudge_tracker_
.RecordSuccessfulSyncCycle();
2968 EXPECT_FALSE(nudge_tracker_
.IsGetUpdatesRequired());
2971 // Tests that sending debug info events on Commit works.
2972 TEST_F(SyncerTest
, SendDebugInfoEventsOnCommit_HappyCase
) {
2973 // Make sure GetUpdate isn't call as it would "steal" debug info events before
2974 // Commit has a chance to send them.
2975 ConfigureNoGetUpdatesRequired();
2977 // Generate a debug info event and trigger a commit.
2978 debug_info_getter_
->AddDebugEvent();
2979 CreateUnsyncedDirectory("X", "id_X");
2980 EXPECT_TRUE(SyncShareNudge());
2982 // Verify that the last request received is a Commit and that it contains a
2983 // debug info event.
2984 EXPECT_EQ(1U, mock_server_
->requests().size());
2985 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2986 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
2988 // Generate another commit, but no debug info event.
2989 CreateUnsyncedDirectory("Y", "id_Y");
2990 EXPECT_TRUE(SyncShareNudge());
2992 // See that it was received and contains no debug info events.
2993 EXPECT_EQ(2U, mock_server_
->requests().size());
2994 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2995 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
2998 // Tests that debug info events are not dropped on server error.
2999 TEST_F(SyncerTest
, SendDebugInfoEventsOnCommit_PostFailsDontDrop
) {
3000 // Make sure GetUpdate isn't call as it would "steal" debug info events before
3001 // Commit has a chance to send them.
3002 ConfigureNoGetUpdatesRequired();
3004 mock_server_
->FailNextPostBufferToPathCall();
3006 // Generate a debug info event and trigger a commit.
3007 debug_info_getter_
->AddDebugEvent();
3008 CreateUnsyncedDirectory("X", "id_X");
3009 EXPECT_FALSE(SyncShareNudge());
3011 // Verify that the last request sent is a Commit and that it contains a debug
3013 EXPECT_EQ(1U, mock_server_
->requests().size());
3014 ASSERT_TRUE(mock_server_
->last_request().has_commit());
3015 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
3018 EXPECT_TRUE(SyncShareNudge());
3020 // Verify that we've received another Commit and that it contains a debug info
3021 // event (just like the previous one).
3022 EXPECT_EQ(2U, mock_server_
->requests().size());
3023 ASSERT_TRUE(mock_server_
->last_request().has_commit());
3024 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
3026 // Generate another commit and try again.
3027 CreateUnsyncedDirectory("Y", "id_Y");
3028 EXPECT_TRUE(SyncShareNudge());
3030 // See that it was received and contains no debug info events.
3031 EXPECT_EQ(3U, mock_server_
->requests().size());
3032 ASSERT_TRUE(mock_server_
->last_request().has_commit());
3033 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
3036 TEST_F(SyncerTest
, HugeConflict
) {
3037 int item_count
= 300; // We should be able to do 300 or 3000 w/o issue.
3039 syncable::Id parent_id
= ids_
.NewServerId();
3040 syncable::Id last_id
= parent_id
;
3041 vector
<syncable::Id
> tree_ids
;
3043 // Create a lot of updates for which the parent does not exist yet.
3044 // Generate a huge deep tree which should all fail to apply at first.
3046 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3047 for (int i
= 0; i
< item_count
; i
++) {
3048 syncable::Id next_id
= ids_
.NewServerId();
3049 syncable::Id local_id
= ids_
.NewLocalId();
3050 tree_ids
.push_back(next_id
);
3051 mock_server_
->AddUpdateDirectory(next_id
, last_id
, "BOB", 2, 20,
3052 foreign_cache_guid(),
3053 local_id
.GetServerId());
3057 EXPECT_TRUE(SyncShareNudge());
3059 // Check they're in the expected conflict state.
3061 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3062 for (int i
= 0; i
< item_count
; i
++) {
3063 Entry
e(&trans
, GET_BY_ID
, tree_ids
[i
]);
3064 // They should all exist but none should be applied.
3065 ASSERT_TRUE(e
.good());
3066 EXPECT_TRUE(e
.GetIsDel());
3067 EXPECT_TRUE(e
.GetIsUnappliedUpdate());
3071 // Add the missing parent directory.
3072 mock_server_
->AddUpdateDirectory(parent_id
, TestIdFactory::root(),
3073 "BOB", 2, 20, foreign_cache_guid(), "-3500");
3074 EXPECT_TRUE(SyncShareNudge());
3076 // Now they should all be OK.
3078 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3079 for (int i
= 0; i
< item_count
; i
++) {
3080 Entry
e(&trans
, GET_BY_ID
, tree_ids
[i
]);
3081 ASSERT_TRUE(e
.good());
3082 EXPECT_FALSE(e
.GetIsDel());
3083 EXPECT_FALSE(e
.GetIsUnappliedUpdate());
3088 TEST_F(SyncerTest
, DontCrashOnCaseChange
) {
3089 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
3090 foreign_cache_guid(), "-1");
3091 EXPECT_TRUE(SyncShareNudge());
3093 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3094 MutableEntry
e(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3095 ASSERT_TRUE(e
.good());
3096 e
.PutIsUnsynced(true);
3098 mock_server_
->set_conflict_all_commits(true);
3099 mock_server_
->AddUpdateDirectory(1, 0, "BOB", 2, 20,
3100 foreign_cache_guid(), "-1");
3101 EXPECT_FALSE(SyncShareNudge()); // USED TO CAUSE AN ASSERT
3102 saw_syncer_event_
= false;
3105 TEST_F(SyncerTest
, UnsyncedItemAndUpdate
) {
3106 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
3107 foreign_cache_guid(), "-1");
3108 EXPECT_TRUE(SyncShareNudge());
3109 mock_server_
->set_conflict_all_commits(true);
3110 mock_server_
->AddUpdateDirectory(2, 0, "bob", 2, 20,
3111 foreign_cache_guid(), "-2");
3112 EXPECT_TRUE(SyncShareNudge()); // USED TO CAUSE AN ASSERT
3113 saw_syncer_event_
= false;
3116 TEST_F(SyncerTest
, NewEntryAndAlteredServerEntrySharePath
) {
3117 mock_server_
->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
3118 foreign_cache_guid(), "-1");
3119 EXPECT_TRUE(SyncShareNudge());
3120 int64 local_folder_handle
;
3121 syncable::Id local_folder_id
;
3123 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3124 MutableEntry
new_entry(
3125 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bar.htm");
3126 ASSERT_TRUE(new_entry
.good());
3127 local_folder_id
= new_entry
.GetId();
3128 local_folder_handle
= new_entry
.GetMetahandle();
3129 new_entry
.PutIsUnsynced(true);
3130 new_entry
.PutSpecifics(DefaultBookmarkSpecifics());
3131 MutableEntry
old(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3132 ASSERT_TRUE(old
.good());
3133 WriteTestDataToEntry(&wtrans
, &old
);
3135 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
3136 foreign_cache_guid(), "-1");
3137 mock_server_
->set_conflict_all_commits(true);
3138 EXPECT_FALSE(SyncShareNudge());
3139 saw_syncer_event_
= false;
3141 // Update #20 should have been dropped in favor of the local version.
3142 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3143 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3144 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3145 ASSERT_TRUE(server
.good());
3146 ASSERT_TRUE(local
.good());
3147 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3148 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3149 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3150 EXPECT_TRUE(server
.GetIsUnsynced());
3151 EXPECT_TRUE(local
.GetIsUnsynced());
3152 EXPECT_EQ("Foo.htm", server
.GetNonUniqueName());
3153 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3155 // Allow local changes to commit.
3156 mock_server_
->set_conflict_all_commits(false);
3157 EXPECT_TRUE(SyncShareNudge());
3158 saw_syncer_event_
= false;
3160 // Now add a server change to make the two names equal. There should
3161 // be no conflict with that, since names are not unique.
3162 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
3163 foreign_cache_guid(), "-1");
3164 EXPECT_TRUE(SyncShareNudge());
3165 saw_syncer_event_
= false;
3167 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3168 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3169 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3170 ASSERT_TRUE(server
.good());
3171 ASSERT_TRUE(local
.good());
3172 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3173 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3174 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3175 EXPECT_FALSE(server
.GetIsUnsynced());
3176 EXPECT_FALSE(local
.GetIsUnsynced());
3177 EXPECT_EQ("Bar.htm", server
.GetNonUniqueName());
3178 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3179 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
3180 server
.GetSpecifics().bookmark().url());
3184 // Same as NewEntryAnddServerEntrySharePath, but using the old-style protocol.
3185 TEST_F(SyncerTest
, NewEntryAndAlteredServerEntrySharePath_OldBookmarksProto
) {
3186 mock_server_
->set_use_legacy_bookmarks_protocol(true);
3187 mock_server_
->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
3188 foreign_cache_guid(), "-1");
3189 EXPECT_TRUE(SyncShareNudge());
3190 int64 local_folder_handle
;
3191 syncable::Id local_folder_id
;
3193 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3194 MutableEntry
new_entry(
3195 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bar.htm");
3196 ASSERT_TRUE(new_entry
.good());
3197 local_folder_id
= new_entry
.GetId();
3198 local_folder_handle
= new_entry
.GetMetahandle();
3199 new_entry
.PutIsUnsynced(true);
3200 new_entry
.PutSpecifics(DefaultBookmarkSpecifics());
3201 MutableEntry
old(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3202 ASSERT_TRUE(old
.good());
3203 WriteTestDataToEntry(&wtrans
, &old
);
3205 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
3206 foreign_cache_guid(), "-1");
3207 mock_server_
->set_conflict_all_commits(true);
3208 EXPECT_FALSE(SyncShareNudge());
3209 saw_syncer_event_
= false;
3211 // Update #20 should have been dropped in favor of the local version.
3212 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3213 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3214 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3215 ASSERT_TRUE(server
.good());
3216 ASSERT_TRUE(local
.good());
3217 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3218 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3219 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3220 EXPECT_TRUE(server
.GetIsUnsynced());
3221 EXPECT_TRUE(local
.GetIsUnsynced());
3222 EXPECT_EQ("Foo.htm", server
.GetNonUniqueName());
3223 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3225 // Allow local changes to commit.
3226 mock_server_
->set_conflict_all_commits(false);
3227 EXPECT_TRUE(SyncShareNudge());
3228 saw_syncer_event_
= false;
3230 // Now add a server change to make the two names equal. There should
3231 // be no conflict with that, since names are not unique.
3232 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
3233 foreign_cache_guid(), "-1");
3234 EXPECT_TRUE(SyncShareNudge());
3235 saw_syncer_event_
= false;
3237 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3238 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3239 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3240 ASSERT_TRUE(server
.good());
3241 ASSERT_TRUE(local
.good());
3242 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3243 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3244 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3245 EXPECT_FALSE(server
.GetIsUnsynced());
3246 EXPECT_FALSE(local
.GetIsUnsynced());
3247 EXPECT_EQ("Bar.htm", server
.GetNonUniqueName());
3248 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3249 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
3250 server
.GetSpecifics().bookmark().url());
3254 // Circular links should be resolved by the server.
3255 TEST_F(SyncerTest
, SiblingDirectoriesBecomeCircular
) {
3256 // we don't currently resolve this. This test ensures we don't.
3257 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
3258 foreign_cache_guid(), "-1");
3259 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
3260 foreign_cache_guid(), "-2");
3261 EXPECT_TRUE(SyncShareNudge());
3263 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3264 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3265 ASSERT_TRUE(A
.good());
3266 A
.PutIsUnsynced(true);
3267 A
.PutParentId(ids_
.FromNumber(2));
3268 A
.PutNonUniqueName("B");
3270 mock_server_
->AddUpdateDirectory(2, 1, "A", 20, 20,
3271 foreign_cache_guid(), "-2");
3272 mock_server_
->set_conflict_all_commits(true);
3273 EXPECT_FALSE(SyncShareNudge());
3274 saw_syncer_event_
= false;
3276 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3277 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3278 ASSERT_TRUE(A
.good());
3279 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
3280 ASSERT_TRUE(B
.good());
3281 EXPECT_TRUE(A
.GetNonUniqueName()== "B");
3282 EXPECT_TRUE(B
.GetNonUniqueName()== "B");
3286 TEST_F(SyncerTest
, SwapEntryNames
) {
3287 // Simple transaction test.
3288 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
3289 foreign_cache_guid(), "-1");
3290 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
3291 foreign_cache_guid(), "-2");
3292 mock_server_
->set_conflict_all_commits(true);
3293 EXPECT_TRUE(SyncShareNudge());
3295 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3296 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3297 ASSERT_TRUE(A
.good());
3298 A
.PutIsUnsynced(true);
3299 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
3300 ASSERT_TRUE(B
.good());
3301 B
.PutIsUnsynced(true);
3302 A
.PutNonUniqueName("C");
3303 B
.PutNonUniqueName("A");
3304 A
.PutNonUniqueName("B");
3306 EXPECT_FALSE(SyncShareNudge());
3307 saw_syncer_event_
= false;
3310 TEST_F(SyncerTest
, DualDeletionWithNewItemNameClash
) {
3311 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
3312 foreign_cache_guid(), "-1");
3313 mock_server_
->AddUpdateBookmark(2, 0, "B", 10, 10,
3314 foreign_cache_guid(), "-2");
3315 mock_server_
->set_conflict_all_commits(true);
3316 EXPECT_TRUE(SyncShareNudge());
3318 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3319 MutableEntry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3320 ASSERT_TRUE(B
.good());
3321 WriteTestDataToEntry(&trans
, &B
);
3324 mock_server_
->AddUpdateBookmark(2, 0, "A", 11, 11,
3325 foreign_cache_guid(), "-2");
3326 mock_server_
->SetLastUpdateDeleted();
3327 EXPECT_TRUE(SyncShareNudge());
3329 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3330 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3331 ASSERT_TRUE(B
.good());
3332 EXPECT_FALSE(B
.GetIsUnsynced());
3333 EXPECT_FALSE(B
.GetIsUnappliedUpdate());
3335 saw_syncer_event_
= false;
3338 // When we undelete an entity as a result of conflict resolution, we reuse the
3339 // existing server id and preserve the old version, simply updating the server
3340 // version with the new non-deleted entity.
3341 TEST_F(SyncerTest
, ResolveWeWroteTheyDeleted
) {
3342 int64 bob_metahandle
;
3344 mock_server_
->AddUpdateBookmark(1, 0, "bob", 1, 10,
3345 foreign_cache_guid(), "-1");
3346 EXPECT_TRUE(SyncShareNudge());
3348 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3349 MutableEntry
bob(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3350 ASSERT_TRUE(bob
.good());
3351 bob_metahandle
= bob
.GetMetahandle();
3352 WriteTestDataToEntry(&trans
, &bob
);
3354 mock_server_
->AddUpdateBookmark(1, 0, "bob", 2, 10,
3355 foreign_cache_guid(), "-1");
3356 mock_server_
->SetLastUpdateDeleted();
3357 mock_server_
->set_conflict_all_commits(true);
3358 EXPECT_FALSE(SyncShareNudge());
3359 EXPECT_FALSE(SyncShareNudge());
3361 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3362 Entry
bob(&trans
, GET_BY_HANDLE
, bob_metahandle
);
3363 ASSERT_TRUE(bob
.good());
3364 EXPECT_TRUE(bob
.GetIsUnsynced());
3365 EXPECT_TRUE(bob
.GetId().ServerKnows());
3366 EXPECT_FALSE(bob
.GetIsUnappliedUpdate());
3367 EXPECT_FALSE(bob
.GetIsDel());
3368 EXPECT_EQ(2, bob
.GetServerVersion());
3369 EXPECT_EQ(2, bob
.GetBaseVersion());
3371 saw_syncer_event_
= false;
3374 // This test is to reproduce a check failure. Sometimes we would get a bad ID
3375 // back when creating an entry.
3376 TEST_F(SyncerTest
, DuplicateIDReturn
) {
3378 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3379 MutableEntry
folder(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
3380 ASSERT_TRUE(folder
.good());
3381 folder
.PutIsUnsynced(true);
3382 folder
.PutIsDir(true);
3383 folder
.PutSpecifics(DefaultBookmarkSpecifics());
3384 MutableEntry
folder2(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "fred");
3385 ASSERT_TRUE(folder2
.good());
3386 folder2
.PutIsUnsynced(false);
3387 folder2
.PutIsDir(true);
3388 folder2
.PutSpecifics(DefaultBookmarkSpecifics());
3389 folder2
.PutBaseVersion(3);
3390 folder2
.PutId(syncable::Id::CreateFromServerId("mock_server:10000"));
3392 mock_server_
->set_next_new_id(10000);
3393 EXPECT_EQ(1u, directory()->unsynced_entity_count());
3394 // we get back a bad id in here (should never happen).
3395 EXPECT_FALSE(SyncShareNudge());
3396 EXPECT_EQ(1u, directory()->unsynced_entity_count());
3397 EXPECT_TRUE(SyncShareNudge()); // another bad id in here.
3398 EXPECT_EQ(0u, directory()->unsynced_entity_count());
3399 saw_syncer_event_
= false;
3402 TEST_F(SyncerTest
, DeletedEntryWithBadParentInLoopCalculation
) {
3403 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
3404 foreign_cache_guid(), "-1");
3405 EXPECT_TRUE(SyncShareNudge());
3407 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3408 MutableEntry
bob(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3409 ASSERT_TRUE(bob
.good());
3410 // This is valid, because the parent could have gone away a long time ago.
3411 bob
.PutParentId(ids_
.FromNumber(54));
3413 bob
.PutIsUnsynced(true);
3415 mock_server_
->AddUpdateDirectory(2, 1, "fred", 1, 10,
3416 foreign_cache_guid(), "-2");
3417 EXPECT_TRUE(SyncShareNudge());
3418 EXPECT_TRUE(SyncShareNudge());
3421 TEST_F(SyncerTest
, ConflictResolverMergesLocalDeleteAndServerUpdate
) {
3422 syncable::Id local_id
;
3424 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3426 MutableEntry
local_deleted(
3427 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "name");
3428 local_id
= local_deleted
.GetId();
3429 local_deleted
.PutId(ids_
.FromNumber(1));
3430 local_deleted
.PutBaseVersion(1);
3431 local_deleted
.PutIsDel(true);
3432 local_deleted
.PutIsDir(false);
3433 local_deleted
.PutIsUnsynced(true);
3434 local_deleted
.PutSpecifics(DefaultBookmarkSpecifics());
3437 mock_server_
->AddUpdateBookmark(ids_
.FromNumber(1), root_id_
, "name", 10, 10,
3439 local_id
.GetServerId());
3441 // We don't care about actually committing, just the resolution.
3442 mock_server_
->set_conflict_all_commits(true);
3443 EXPECT_FALSE(SyncShareNudge());
3446 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3447 Entry
local_deleted(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3448 EXPECT_TRUE(local_deleted
.GetBaseVersion()== 10);
3449 EXPECT_TRUE(local_deleted
.GetIsUnappliedUpdate()== false);
3450 EXPECT_TRUE(local_deleted
.GetIsUnsynced()== true);
3451 EXPECT_TRUE(local_deleted
.GetIsDel()== true);
3452 EXPECT_TRUE(local_deleted
.GetIsDir()== false);
3456 // See what happens if the IS_DIR bit gets flipped. This can cause us
3457 // all kinds of disasters.
3458 TEST_F(SyncerTest
, UpdateFlipsTheFolderBit
) {
3459 // Local object: a deleted directory (container), revision 1, unsynced.
3461 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3463 MutableEntry
local_deleted(
3464 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "name");
3465 local_deleted
.PutId(ids_
.FromNumber(1));
3466 local_deleted
.PutBaseVersion(1);
3467 local_deleted
.PutIsDel(true);
3468 local_deleted
.PutIsDir(true);
3469 local_deleted
.PutIsUnsynced(true);
3470 local_deleted
.PutSpecifics(DefaultBookmarkSpecifics());
3473 // Server update: entry-type object (not a container), revision 10.
3474 mock_server_
->AddUpdateBookmark(ids_
.FromNumber(1), root_id_
, "name", 10, 10,
3476 ids_
.FromNumber(1).GetServerId());
3478 // Don't attempt to commit.
3479 mock_server_
->set_conflict_all_commits(true);
3481 // The syncer should not attempt to apply the invalid update.
3482 EXPECT_FALSE(SyncShareNudge());
3485 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3486 Entry
local_deleted(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3487 EXPECT_TRUE(local_deleted
.GetBaseVersion()== 1);
3488 EXPECT_TRUE(local_deleted
.GetIsUnappliedUpdate()== false);
3489 EXPECT_TRUE(local_deleted
.GetIsUnsynced()== true);
3490 EXPECT_TRUE(local_deleted
.GetIsDel()== true);
3491 EXPECT_TRUE(local_deleted
.GetIsDir()== true);
3496 // Merge conflict resolution will merge a new local entry with another entry
3497 // that needs updates, resulting in CHECK.
3498 TEST_F(SyncerTest
, MergingExistingItems
) {
3499 mock_server_
->set_conflict_all_commits(true);
3500 mock_server_
->AddUpdateBookmark(1, 0, "base", 10, 10,
3501 local_cache_guid(), "-1");
3502 EXPECT_TRUE(SyncShareNudge());
3504 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3506 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "Copy of base");
3507 WriteTestDataToEntry(&trans
, &entry
);
3509 mock_server_
->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
3510 local_cache_guid(), "-1");
3511 EXPECT_FALSE(SyncShareNudge());
3514 // In this test a long changelog contains a child at the start of the changelog
3515 // and a parent at the end. While these updates are in progress the client would
3517 TEST_F(SyncerTest
, LongChangelistWithApplicationConflict
) {
3518 const int depth
= 400;
3519 syncable::Id folder_id
= ids_
.FromNumber(1);
3521 // First we an item in a folder in the root. However the folder won't come
3523 syncable::Id stuck_entry_id
= TestIdFactory::FromNumber(99999);
3524 mock_server_
->AddUpdateDirectory(stuck_entry_id
,
3525 folder_id
, "stuck", 1, 1,
3526 foreign_cache_guid(), "-99999");
3527 mock_server_
->SetChangesRemaining(depth
- 1);
3528 EXPECT_TRUE(SyncShareNudge());
3530 // Buffer up a very long series of downloads.
3531 // We should never be stuck (conflict resolution shouldn't
3532 // kick in so long as we're making forward progress).
3533 for (int i
= 0; i
< depth
; i
++) {
3534 mock_server_
->NextUpdateBatch();
3535 mock_server_
->SetNewTimestamp(i
+ 1);
3536 mock_server_
->SetChangesRemaining(depth
- i
);
3539 EXPECT_TRUE(SyncShareNudge());
3541 // Ensure our folder hasn't somehow applied.
3543 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3544 Entry
child(&trans
, GET_BY_ID
, stuck_entry_id
);
3545 EXPECT_TRUE(child
.good());
3546 EXPECT_TRUE(child
.GetIsUnappliedUpdate());
3547 EXPECT_TRUE(child
.GetIsDel());
3548 EXPECT_FALSE(child
.GetIsUnsynced());
3551 // And finally the folder.
3552 mock_server_
->AddUpdateDirectory(folder_id
,
3553 TestIdFactory::root(), "folder", 1, 1,
3554 foreign_cache_guid(), "-1");
3555 mock_server_
->SetChangesRemaining(0);
3556 EXPECT_TRUE(SyncShareNudge());
3557 EXPECT_TRUE(SyncShareNudge());
3558 // Check that everything is as expected after the commit.
3560 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3561 Entry
entry(&trans
, GET_BY_ID
, folder_id
);
3562 ASSERT_TRUE(entry
.good());
3563 Entry
child(&trans
, GET_BY_ID
, stuck_entry_id
);
3564 EXPECT_EQ(entry
.GetId(), child
.GetParentId());
3565 EXPECT_EQ("stuck", child
.GetNonUniqueName());
3566 EXPECT_TRUE(child
.good());
3570 TEST_F(SyncerTest
, DontMergeTwoExistingItems
) {
3571 mock_server_
->set_conflict_all_commits(true);
3572 mock_server_
->AddUpdateBookmark(1, 0, "base", 10, 10,
3573 foreign_cache_guid(), "-1");
3574 mock_server_
->AddUpdateBookmark(2, 0, "base2", 10, 10,
3575 foreign_cache_guid(), "-2");
3576 EXPECT_TRUE(SyncShareNudge());
3578 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3579 MutableEntry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3580 ASSERT_TRUE(entry
.good());
3581 entry
.PutNonUniqueName("Copy of base");
3582 entry
.PutIsUnsynced(true);
3584 mock_server_
->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
3585 foreign_cache_guid(), "-1");
3586 EXPECT_FALSE(SyncShareNudge());
3588 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3589 Entry
entry1(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3590 EXPECT_FALSE(entry1
.GetIsUnappliedUpdate());
3591 EXPECT_FALSE(entry1
.GetIsUnsynced());
3592 EXPECT_FALSE(entry1
.GetIsDel());
3593 Entry
entry2(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3594 EXPECT_FALSE(entry2
.GetIsUnappliedUpdate());
3595 EXPECT_TRUE(entry2
.GetIsUnsynced());
3596 EXPECT_FALSE(entry2
.GetIsDel());
3597 EXPECT_EQ(entry1
.GetNonUniqueName(), entry2
.GetNonUniqueName());
3601 TEST_F(SyncerTest
, TestUndeleteUpdate
) {
3602 mock_server_
->set_conflict_all_commits(true);
3603 mock_server_
->AddUpdateDirectory(1, 0, "foo", 1, 1,
3604 foreign_cache_guid(), "-1");
3605 mock_server_
->AddUpdateDirectory(2, 1, "bar", 1, 2,
3606 foreign_cache_guid(), "-2");
3607 EXPECT_TRUE(SyncShareNudge());
3608 mock_server_
->AddUpdateDirectory(2, 1, "bar", 2, 3,
3609 foreign_cache_guid(), "-2");
3610 mock_server_
->SetLastUpdateDeleted();
3611 EXPECT_TRUE(SyncShareNudge());
3615 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3616 Entry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3617 ASSERT_TRUE(entry
.good());
3618 EXPECT_TRUE(entry
.GetIsDel());
3619 metahandle
= entry
.GetMetahandle();
3621 mock_server_
->AddUpdateDirectory(1, 0, "foo", 2, 4,
3622 foreign_cache_guid(), "-1");
3623 mock_server_
->SetLastUpdateDeleted();
3624 EXPECT_TRUE(SyncShareNudge());
3625 // This used to be rejected as it's an undeletion. Now, it results in moving
3626 // the delete path aside.
3627 mock_server_
->AddUpdateDirectory(2, 1, "bar", 3, 5,
3628 foreign_cache_guid(), "-2");
3629 EXPECT_TRUE(SyncShareNudge());
3631 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3632 Entry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3633 ASSERT_TRUE(entry
.good());
3634 EXPECT_TRUE(entry
.GetIsDel());
3635 EXPECT_FALSE(entry
.GetServerIsDel());
3636 EXPECT_TRUE(entry
.GetIsUnappliedUpdate());
3637 EXPECT_NE(entry
.GetMetahandle(), metahandle
);
3641 TEST_F(SyncerTest
, TestMoveSanitizedNamedFolder
) {
3642 mock_server_
->AddUpdateDirectory(1, 0, "foo", 1, 1,
3643 foreign_cache_guid(), "-1");
3644 mock_server_
->AddUpdateDirectory(2, 0, ":::", 1, 2,
3645 foreign_cache_guid(), "-2");
3646 EXPECT_TRUE(SyncShareNudge());
3648 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3649 MutableEntry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3650 ASSERT_TRUE(entry
.good());
3651 entry
.PutParentId(ids_
.FromNumber(1));
3652 EXPECT_TRUE(entry
.PutIsUnsynced(true));
3654 EXPECT_TRUE(SyncShareNudge());
3655 // We use the same sync ts as before so our times match up.
3656 mock_server_
->AddUpdateDirectory(2, 1, ":::", 2, 2,
3657 foreign_cache_guid(), "-2");
3658 EXPECT_TRUE(SyncShareNudge());
3661 // Don't crash when this occurs.
3662 TEST_F(SyncerTest
, UpdateWhereParentIsNotAFolder
) {
3663 mock_server_
->AddUpdateBookmark(1, 0, "B", 10, 10,
3664 foreign_cache_guid(), "-1");
3665 mock_server_
->AddUpdateDirectory(2, 1, "BookmarkParent", 10, 10,
3666 foreign_cache_guid(), "-2");
3667 // Used to cause a CHECK
3668 EXPECT_TRUE(SyncShareNudge());
3670 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
3671 Entry
good_entry(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
3672 ASSERT_TRUE(good_entry
.good());
3673 EXPECT_FALSE(good_entry
.GetIsUnappliedUpdate());
3674 Entry
bad_parent(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(2));
3675 ASSERT_TRUE(bad_parent
.good());
3676 EXPECT_TRUE(bad_parent
.GetIsUnappliedUpdate());
3680 TEST_F(SyncerTest
, DirectoryUpdateTest
) {
3681 Id in_root_id
= ids_
.NewServerId();
3682 Id in_in_root_id
= ids_
.NewServerId();
3684 mock_server_
->AddUpdateDirectory(in_root_id
, TestIdFactory::root(),
3685 "in_root_name", 2, 2,
3686 foreign_cache_guid(), "-1");
3687 mock_server_
->AddUpdateDirectory(in_in_root_id
, in_root_id
,
3688 "in_in_root_name", 3, 3,
3689 foreign_cache_guid(), "-2");
3690 EXPECT_TRUE(SyncShareNudge());
3692 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3693 Entry
in_root(&trans
, GET_BY_ID
, in_root_id
);
3694 ASSERT_TRUE(in_root
.good());
3695 EXPECT_EQ("in_root_name", in_root
.GetNonUniqueName());
3696 EXPECT_EQ(TestIdFactory::root(), in_root
.GetParentId());
3698 Entry
in_in_root(&trans
, GET_BY_ID
, in_in_root_id
);
3699 ASSERT_TRUE(in_in_root
.good());
3700 EXPECT_EQ("in_in_root_name", in_in_root
.GetNonUniqueName());
3701 EXPECT_EQ(in_root_id
, in_in_root
.GetParentId());
3705 TEST_F(SyncerTest
, DirectoryCommitTest
) {
3706 syncable::Id in_root_id
, in_dir_id
;
3707 int64 foo_metahandle
;
3708 int64 bar_metahandle
;
3711 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3712 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "foo");
3713 ASSERT_TRUE(parent
.good());
3714 parent
.PutIsUnsynced(true);
3715 parent
.PutIsDir(true);
3716 parent
.PutSpecifics(DefaultBookmarkSpecifics());
3717 in_root_id
= parent
.GetId();
3718 foo_metahandle
= parent
.GetMetahandle();
3720 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent
.GetId(), "bar");
3721 ASSERT_TRUE(child
.good());
3722 child
.PutIsUnsynced(true);
3723 child
.PutIsDir(true);
3724 child
.PutSpecifics(DefaultBookmarkSpecifics());
3725 bar_metahandle
= child
.GetMetahandle();
3726 in_dir_id
= parent
.GetId();
3728 EXPECT_TRUE(SyncShareNudge());
3730 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3731 Entry
fail_by_old_id_entry(&trans
, GET_BY_ID
, in_root_id
);
3732 ASSERT_FALSE(fail_by_old_id_entry
.good());
3734 Entry
foo_entry(&trans
, GET_BY_HANDLE
, foo_metahandle
);
3735 ASSERT_TRUE(foo_entry
.good());
3736 EXPECT_EQ("foo", foo_entry
.GetNonUniqueName());
3737 EXPECT_NE(foo_entry
.GetId(), in_root_id
);
3739 Entry
bar_entry(&trans
, GET_BY_HANDLE
, bar_metahandle
);
3740 ASSERT_TRUE(bar_entry
.good());
3741 EXPECT_EQ("bar", bar_entry
.GetNonUniqueName());
3742 EXPECT_NE(bar_entry
.GetId(), in_dir_id
);
3743 EXPECT_EQ(foo_entry
.GetId(), bar_entry
.GetParentId());
3747 TEST_F(SyncerTest
, TestClientCommandDuringUpdate
) {
3748 using sync_pb::ClientCommand
;
3750 ClientCommand
* command
= new ClientCommand();
3751 command
->set_set_sync_poll_interval(8);
3752 command
->set_set_sync_long_poll_interval(800);
3753 command
->set_sessions_commit_delay_seconds(3141);
3754 sync_pb::CustomNudgeDelay
* bookmark_delay
=
3755 command
->add_custom_nudge_delays();
3756 bookmark_delay
->set_datatype_id(
3757 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3758 bookmark_delay
->set_delay_ms(950);
3759 command
->set_client_invalidation_hint_buffer_size(11);
3760 mock_server_
->AddUpdateDirectory(1, 0, "in_root", 1, 1,
3761 foreign_cache_guid(), "-1");
3762 mock_server_
->SetGUClientCommand(command
);
3763 EXPECT_TRUE(SyncShareNudge());
3765 EXPECT_EQ(TimeDelta::FromSeconds(8), last_short_poll_interval_received_
);
3766 EXPECT_EQ(TimeDelta::FromSeconds(800), last_long_poll_interval_received_
);
3767 EXPECT_EQ(TimeDelta::FromSeconds(3141), last_sessions_commit_delay_
);
3768 EXPECT_EQ(TimeDelta::FromMilliseconds(950), last_bookmarks_commit_delay_
);
3769 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_
);
3771 command
= new ClientCommand();
3772 command
->set_set_sync_poll_interval(180);
3773 command
->set_set_sync_long_poll_interval(190);
3774 command
->set_sessions_commit_delay_seconds(2718);
3775 bookmark_delay
= command
->add_custom_nudge_delays();
3776 bookmark_delay
->set_datatype_id(
3777 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3778 bookmark_delay
->set_delay_ms(1050);
3779 command
->set_client_invalidation_hint_buffer_size(9);
3780 mock_server_
->AddUpdateDirectory(
3781 1, 0, "in_root", 1, 1, foreign_cache_guid(), "-1");
3782 mock_server_
->SetGUClientCommand(command
);
3783 EXPECT_TRUE(SyncShareNudge());
3785 EXPECT_EQ(TimeDelta::FromSeconds(180), last_short_poll_interval_received_
);
3786 EXPECT_EQ(TimeDelta::FromSeconds(190), last_long_poll_interval_received_
);
3787 EXPECT_EQ(TimeDelta::FromSeconds(2718), last_sessions_commit_delay_
);
3788 EXPECT_EQ(TimeDelta::FromMilliseconds(1050), last_bookmarks_commit_delay_
);
3789 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_
);
3792 TEST_F(SyncerTest
, TestClientCommandDuringCommit
) {
3793 using sync_pb::ClientCommand
;
3795 ClientCommand
* command
= new ClientCommand();
3796 command
->set_set_sync_poll_interval(8);
3797 command
->set_set_sync_long_poll_interval(800);
3798 command
->set_sessions_commit_delay_seconds(3141);
3799 sync_pb::CustomNudgeDelay
* bookmark_delay
=
3800 command
->add_custom_nudge_delays();
3801 bookmark_delay
->set_datatype_id(
3802 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3803 bookmark_delay
->set_delay_ms(950);
3804 command
->set_client_invalidation_hint_buffer_size(11);
3805 CreateUnsyncedDirectory("X", "id_X");
3806 mock_server_
->SetCommitClientCommand(command
);
3807 EXPECT_TRUE(SyncShareNudge());
3809 EXPECT_EQ(TimeDelta::FromSeconds(8), last_short_poll_interval_received_
);
3810 EXPECT_EQ(TimeDelta::FromSeconds(800), last_long_poll_interval_received_
);
3811 EXPECT_EQ(TimeDelta::FromSeconds(3141), last_sessions_commit_delay_
);
3812 EXPECT_EQ(TimeDelta::FromMilliseconds(950), last_bookmarks_commit_delay_
);
3813 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_
);
3815 command
= new ClientCommand();
3816 command
->set_set_sync_poll_interval(180);
3817 command
->set_set_sync_long_poll_interval(190);
3818 command
->set_sessions_commit_delay_seconds(2718);
3819 bookmark_delay
= command
->add_custom_nudge_delays();
3820 bookmark_delay
->set_datatype_id(
3821 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3822 bookmark_delay
->set_delay_ms(1050);
3823 command
->set_client_invalidation_hint_buffer_size(9);
3824 CreateUnsyncedDirectory("Y", "id_Y");
3825 mock_server_
->SetCommitClientCommand(command
);
3826 EXPECT_TRUE(SyncShareNudge());
3828 EXPECT_EQ(TimeDelta::FromSeconds(180), last_short_poll_interval_received_
);
3829 EXPECT_EQ(TimeDelta::FromSeconds(190), last_long_poll_interval_received_
);
3830 EXPECT_EQ(TimeDelta::FromSeconds(2718), last_sessions_commit_delay_
);
3831 EXPECT_EQ(TimeDelta::FromMilliseconds(1050), last_bookmarks_commit_delay_
);
3832 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_
);
3835 TEST_F(SyncerTest
, EnsureWeSendUpOldParent
) {
3836 syncable::Id folder_one_id
= ids_
.FromNumber(1);
3837 syncable::Id folder_two_id
= ids_
.FromNumber(2);
3839 mock_server_
->AddUpdateDirectory(folder_one_id
, TestIdFactory::root(),
3840 "folder_one", 1, 1, foreign_cache_guid(), "-1");
3841 mock_server_
->AddUpdateDirectory(folder_two_id
, TestIdFactory::root(),
3842 "folder_two", 1, 1, foreign_cache_guid(), "-2");
3843 EXPECT_TRUE(SyncShareNudge());
3845 // A moved entry should send an "old parent."
3846 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3847 MutableEntry
entry(&trans
, GET_BY_ID
, folder_one_id
);
3848 ASSERT_TRUE(entry
.good());
3849 entry
.PutParentId(folder_two_id
);
3850 entry
.PutIsUnsynced(true);
3851 // A new entry should send no "old parent."
3852 MutableEntry
create(
3853 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_folder");
3854 create
.PutIsUnsynced(true);
3855 create
.PutSpecifics(DefaultBookmarkSpecifics());
3857 EXPECT_TRUE(SyncShareNudge());
3858 const sync_pb::CommitMessage
& commit
= mock_server_
->last_sent_commit();
3859 ASSERT_EQ(2, commit
.entries_size());
3860 EXPECT_TRUE(commit
.entries(0).parent_id_string() == "2");
3861 EXPECT_TRUE(commit
.entries(0).old_parent_id() == "0");
3862 EXPECT_FALSE(commit
.entries(1).has_old_parent_id());
3865 TEST_F(SyncerTest
, Test64BitVersionSupport
) {
3866 int64 really_big_int
= std::numeric_limits
<int64
>::max() - 12;
3867 const string
name("ringo's dang orang ran rings around my o-ring");
3868 int64 item_metahandle
;
3870 // Try writing max int64 to the version fields of a meta entry.
3872 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3873 MutableEntry
entry(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), name
);
3874 ASSERT_TRUE(entry
.good());
3875 entry
.PutBaseVersion(really_big_int
);
3876 entry
.PutServerVersion(really_big_int
);
3877 entry
.PutId(ids_
.NewServerId());
3878 item_metahandle
= entry
.GetMetahandle();
3880 // Now read it back out and make sure the value is max int64.
3881 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
3882 Entry
entry(&rtrans
, syncable::GET_BY_HANDLE
, item_metahandle
);
3883 ASSERT_TRUE(entry
.good());
3884 EXPECT_TRUE(really_big_int
== entry
.GetBaseVersion());
3887 TEST_F(SyncerTest
, TestSimpleUndelete
) {
3888 Id id
= ids_
.MakeServer("undeletion item"), root
= TestIdFactory::root();
3889 mock_server_
->set_conflict_all_commits(true);
3890 // Let there be an entry from the server.
3891 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 1, 10,
3892 foreign_cache_guid(), "-1");
3893 EXPECT_TRUE(SyncShareNudge());
3894 // Check it out and delete it.
3896 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3897 MutableEntry
entry(&wtrans
, GET_BY_ID
, id
);
3898 ASSERT_TRUE(entry
.good());
3899 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3900 EXPECT_FALSE(entry
.GetIsUnsynced());
3901 EXPECT_FALSE(entry
.GetIsDel());
3902 // Delete it locally.
3903 entry
.PutIsDel(true);
3905 EXPECT_TRUE(SyncShareNudge());
3906 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3908 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3909 Entry
entry(&trans
, GET_BY_ID
, id
);
3910 ASSERT_TRUE(entry
.good());
3911 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3912 EXPECT_FALSE(entry
.GetIsUnsynced());
3913 EXPECT_TRUE(entry
.GetIsDel());
3914 EXPECT_FALSE(entry
.GetServerIsDel());
3916 EXPECT_TRUE(SyncShareNudge());
3917 // Update from server confirming deletion.
3918 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 11,
3919 foreign_cache_guid(), "-1");
3920 mock_server_
->SetLastUpdateDeleted();
3921 EXPECT_TRUE(SyncShareNudge());
3922 // IS_DEL AND SERVER_IS_DEL now both true.
3924 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3925 Entry
entry(&trans
, GET_BY_ID
, id
);
3926 ASSERT_TRUE(entry
.good());
3927 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3928 EXPECT_FALSE(entry
.GetIsUnsynced());
3929 EXPECT_TRUE(entry
.GetIsDel());
3930 EXPECT_TRUE(entry
.GetServerIsDel());
3932 // Undelete from server.
3933 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 12,
3934 foreign_cache_guid(), "-1");
3935 EXPECT_TRUE(SyncShareNudge());
3936 // IS_DEL and SERVER_IS_DEL now both false.
3938 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3939 Entry
entry(&trans
, GET_BY_ID
, id
);
3940 ASSERT_TRUE(entry
.good());
3941 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3942 EXPECT_FALSE(entry
.GetIsUnsynced());
3943 EXPECT_FALSE(entry
.GetIsDel());
3944 EXPECT_FALSE(entry
.GetServerIsDel());
3948 TEST_F(SyncerTest
, TestUndeleteWithMissingDeleteUpdate
) {
3949 Id id
= ids_
.MakeServer("undeletion item"), root
= TestIdFactory::root();
3950 // Let there be a entry, from the server.
3951 mock_server_
->set_conflict_all_commits(true);
3952 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 1, 10,
3953 foreign_cache_guid(), "-1");
3954 EXPECT_TRUE(SyncShareNudge());
3955 // Check it out and delete it.
3957 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3958 MutableEntry
entry(&wtrans
, GET_BY_ID
, id
);
3959 ASSERT_TRUE(entry
.good());
3960 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3961 EXPECT_FALSE(entry
.GetIsUnsynced());
3962 EXPECT_FALSE(entry
.GetIsDel());
3963 // Delete it locally.
3964 entry
.PutIsDel(true);
3966 EXPECT_TRUE(SyncShareNudge());
3967 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3969 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3970 Entry
entry(&trans
, GET_BY_ID
, id
);
3971 ASSERT_TRUE(entry
.good());
3972 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3973 EXPECT_FALSE(entry
.GetIsUnsynced());
3974 EXPECT_TRUE(entry
.GetIsDel());
3975 EXPECT_FALSE(entry
.GetServerIsDel());
3977 EXPECT_TRUE(SyncShareNudge());
3978 // Say we do not get an update from server confirming deletion. Undelete
3980 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 12,
3981 foreign_cache_guid(), "-1");
3982 EXPECT_TRUE(SyncShareNudge());
3983 // IS_DEL and SERVER_IS_DEL now both false.
3985 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3986 Entry
entry(&trans
, GET_BY_ID
, id
);
3987 ASSERT_TRUE(entry
.good());
3988 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3989 EXPECT_FALSE(entry
.GetIsUnsynced());
3990 EXPECT_FALSE(entry
.GetIsDel());
3991 EXPECT_FALSE(entry
.GetServerIsDel());
3995 TEST_F(SyncerTest
, TestUndeleteIgnoreCorrectlyUnappliedUpdate
) {
3996 Id id1
= ids_
.MakeServer("first"), id2
= ids_
.MakeServer("second");
3997 Id root
= TestIdFactory::root();
3998 // Duplicate! expect path clashing!
3999 mock_server_
->set_conflict_all_commits(true);
4000 mock_server_
->AddUpdateBookmark(id1
, root
, "foo", 1, 10,
4001 foreign_cache_guid(), "-1");
4002 mock_server_
->AddUpdateBookmark(id2
, root
, "foo", 1, 10,
4003 foreign_cache_guid(), "-2");
4004 EXPECT_TRUE(SyncShareNudge());
4005 mock_server_
->AddUpdateBookmark(id2
, root
, "foo2", 2, 20,
4006 foreign_cache_guid(), "-2");
4007 EXPECT_TRUE(SyncShareNudge()); // Now just don't explode.
4010 TEST_F(SyncerTest
, ClientTagServerCreatedUpdatesWork
) {
4011 mock_server_
->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
4012 foreign_cache_guid(), "-1");
4013 mock_server_
->SetLastUpdateClientTag("permfolder");
4015 EXPECT_TRUE(SyncShareNudge());
4018 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4019 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
4020 ASSERT_TRUE(perm_folder
.good());
4021 EXPECT_FALSE(perm_folder
.GetIsDel());
4022 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
4023 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
4024 EXPECT_EQ(perm_folder
.GetUniqueClientTag(), "permfolder");
4025 EXPECT_EQ(perm_folder
.GetNonUniqueName(), "permitem1");
4028 mock_server_
->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
4029 foreign_cache_guid(), "-1");
4030 mock_server_
->SetLastUpdateClientTag("permfolder");
4031 EXPECT_TRUE(SyncShareNudge());
4034 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4036 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
4037 ASSERT_TRUE(perm_folder
.good());
4038 EXPECT_FALSE(perm_folder
.GetIsDel());
4039 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
4040 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
4041 EXPECT_EQ(perm_folder
.GetUniqueClientTag(), "permfolder");
4042 EXPECT_EQ(perm_folder
.GetNonUniqueName(), "permitem_renamed");
4046 TEST_F(SyncerTest
, ClientTagIllegalUpdateIgnored
) {
4047 mock_server_
->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
4048 foreign_cache_guid(), "-1");
4049 mock_server_
->SetLastUpdateClientTag("permfolder");
4051 EXPECT_TRUE(SyncShareNudge());
4054 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4055 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
4056 ASSERT_TRUE(perm_folder
.good());
4057 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
4058 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
4059 EXPECT_EQ(perm_folder
.GetUniqueClientTag(), "permfolder");
4060 EXPECT_TRUE(perm_folder
.GetNonUniqueName()== "permitem1");
4061 EXPECT_TRUE(perm_folder
.GetId().ServerKnows());
4064 mock_server_
->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
4065 foreign_cache_guid(), "-1");
4066 mock_server_
->SetLastUpdateClientTag("wrongtag");
4067 EXPECT_TRUE(SyncShareNudge());
4070 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4072 // This update is rejected because it has the same ID, but a
4073 // different tag than one that is already on the client.
4074 // The client has a ServerKnows ID, which cannot be overwritten.
4075 Entry
rejected_update(&trans
, GET_BY_CLIENT_TAG
, "wrongtag");
4076 EXPECT_FALSE(rejected_update
.good());
4078 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
4079 ASSERT_TRUE(perm_folder
.good());
4080 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
4081 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
4082 EXPECT_EQ(perm_folder
.GetNonUniqueName(), "permitem1");
4086 TEST_F(SyncerTest
, ClientTagUncommittedTagMatchesUpdate
) {
4087 int64 original_metahandle
= 0;
4090 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4092 &trans
, CREATE
, PREFERENCES
, ids_
.root(), "name");
4093 ASSERT_TRUE(pref
.good());
4094 pref
.PutUniqueClientTag("tag");
4095 pref
.PutIsUnsynced(true);
4096 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
4097 EXPECT_FALSE(pref
.GetId().ServerKnows());
4098 original_metahandle
= pref
.GetMetahandle();
4101 syncable::Id server_id
= TestIdFactory::MakeServer("id");
4102 mock_server_
->AddUpdatePref(server_id
.GetServerId(),
4103 ids_
.root().GetServerId(),
4105 mock_server_
->set_conflict_all_commits(true);
4107 EXPECT_FALSE(SyncShareNudge());
4108 // This should cause client tag reunion, preserving the metahandle.
4110 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4112 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
4113 ASSERT_TRUE(pref
.good());
4114 EXPECT_FALSE(pref
.GetIsDel());
4115 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
4116 EXPECT_TRUE(pref
.GetIsUnsynced());
4117 EXPECT_EQ(10, pref
.GetBaseVersion());
4118 // Entry should have been given the new ID while preserving the
4119 // metahandle; client should have won the conflict resolution.
4120 EXPECT_EQ(original_metahandle
, pref
.GetMetahandle());
4121 EXPECT_EQ("tag", pref
.GetUniqueClientTag());
4122 EXPECT_TRUE(pref
.GetId().ServerKnows());
4125 mock_server_
->set_conflict_all_commits(false);
4126 EXPECT_TRUE(SyncShareNudge());
4128 // The resolved entry ought to commit cleanly.
4130 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4132 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
4133 ASSERT_TRUE(pref
.good());
4134 EXPECT_FALSE(pref
.GetIsDel());
4135 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
4136 EXPECT_FALSE(pref
.GetIsUnsynced());
4137 EXPECT_TRUE(10 < pref
.GetBaseVersion());
4138 // Entry should have been given the new ID while preserving the
4139 // metahandle; client should have won the conflict resolution.
4140 EXPECT_EQ(original_metahandle
, pref
.GetMetahandle());
4141 EXPECT_EQ("tag", pref
.GetUniqueClientTag());
4142 EXPECT_TRUE(pref
.GetId().ServerKnows());
4146 TEST_F(SyncerTest
, ClientTagConflictWithDeletedLocalEntry
) {
4148 // Create a deleted local entry with a unique client tag.
4149 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4151 &trans
, CREATE
, PREFERENCES
, ids_
.root(), "name");
4152 ASSERT_TRUE(pref
.good());
4153 ASSERT_FALSE(pref
.GetId().ServerKnows());
4154 pref
.PutUniqueClientTag("tag");
4155 pref
.PutIsUnsynced(true);
4157 // Note: IS_DEL && !ServerKnows() will clear the UNSYNCED bit.
4158 // (We never attempt to commit server-unknown deleted items, so this
4159 // helps us clean up those entries).
4160 pref
.PutIsDel(true);
4163 // Prepare an update with the same unique client tag.
4164 syncable::Id server_id
= TestIdFactory::MakeServer("id");
4165 mock_server_
->AddUpdatePref(server_id
.GetServerId(),
4166 ids_
.root().GetServerId(),
4169 EXPECT_TRUE(SyncShareNudge());
4170 // The local entry will be overwritten.
4172 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4174 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
4175 ASSERT_TRUE(pref
.good());
4176 ASSERT_TRUE(pref
.GetId().ServerKnows());
4177 EXPECT_FALSE(pref
.GetIsDel());
4178 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
4179 EXPECT_FALSE(pref
.GetIsUnsynced());
4180 EXPECT_EQ(pref
.GetBaseVersion(), 10);
4181 EXPECT_EQ(pref
.GetUniqueClientTag(), "tag");
4185 TEST_F(SyncerTest
, ClientTagUpdateClashesWithLocalEntry
) {
4186 // This test is written assuming that ID comparison
4187 // will work out in a particular way.
4188 EXPECT_TRUE(ids_
.FromNumber(1) < ids_
.FromNumber(2));
4189 EXPECT_TRUE(ids_
.FromNumber(3) < ids_
.FromNumber(4));
4191 syncable::Id id1
= TestIdFactory::MakeServer("1");
4192 mock_server_
->AddUpdatePref(id1
.GetServerId(), "", "tag1", 10, 100);
4194 syncable::Id id4
= TestIdFactory::MakeServer("4");
4195 mock_server_
->AddUpdatePref(id4
.GetServerId(), "", "tag2", 11, 110);
4197 mock_server_
->set_conflict_all_commits(true);
4199 EXPECT_TRUE(SyncShareNudge());
4200 int64 tag1_metahandle
= syncable::kInvalidMetaHandle
;
4201 int64 tag2_metahandle
= syncable::kInvalidMetaHandle
;
4202 // This should cause client tag overwrite.
4204 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4206 Entry
tag1(&trans
, GET_BY_CLIENT_TAG
, "tag1");
4207 ASSERT_TRUE(tag1
.good());
4208 ASSERT_TRUE(tag1
.GetId().ServerKnows());
4209 ASSERT_TRUE(id1
== tag1
.GetId());
4210 EXPECT_FALSE(tag1
.GetIsDel());
4211 EXPECT_FALSE(tag1
.GetIsUnappliedUpdate());
4212 EXPECT_FALSE(tag1
.GetIsUnsynced());
4213 EXPECT_EQ(10, tag1
.GetBaseVersion());
4214 EXPECT_EQ("tag1", tag1
.GetUniqueClientTag());
4215 tag1_metahandle
= tag1
.GetMetahandle();
4217 Entry
tag2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
4218 ASSERT_TRUE(tag2
.good());
4219 ASSERT_TRUE(tag2
.GetId().ServerKnows());
4220 ASSERT_TRUE(id4
== tag2
.GetId());
4221 EXPECT_FALSE(tag2
.GetIsDel());
4222 EXPECT_FALSE(tag2
.GetIsUnappliedUpdate());
4223 EXPECT_FALSE(tag2
.GetIsUnsynced());
4224 EXPECT_EQ(11, tag2
.GetBaseVersion());
4225 EXPECT_EQ("tag2", tag2
.GetUniqueClientTag());
4226 tag2_metahandle
= tag2
.GetMetahandle();
4228 // Preferences type root should have been created by the updates above.
4229 Entry
pref_root(&trans
, GET_TYPE_ROOT
, PREFERENCES
);
4230 ASSERT_TRUE(pref_root
.good());
4232 syncable::Directory::Metahandles children
;
4233 directory()->GetChildHandlesById(&trans
, pref_root
.GetId(), &children
);
4234 ASSERT_EQ(2U, children
.size());
4237 syncable::Id id2
= TestIdFactory::MakeServer("2");
4238 mock_server_
->AddUpdatePref(id2
.GetServerId(), "", "tag1", 12, 120);
4239 syncable::Id id3
= TestIdFactory::MakeServer("3");
4240 mock_server_
->AddUpdatePref(id3
.GetServerId(), "", "tag2", 13, 130);
4241 EXPECT_TRUE(SyncShareNudge());
4244 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4246 Entry
tag1(&trans
, GET_BY_CLIENT_TAG
, "tag1");
4247 ASSERT_TRUE(tag1
.good());
4248 ASSERT_TRUE(tag1
.GetId().ServerKnows());
4249 ASSERT_EQ(id1
, tag1
.GetId())
4250 << "ID 1 should be kept, since it was less than ID 2.";
4251 EXPECT_FALSE(tag1
.GetIsDel());
4252 EXPECT_FALSE(tag1
.GetIsUnappliedUpdate());
4253 EXPECT_FALSE(tag1
.GetIsUnsynced());
4254 EXPECT_EQ(10, tag1
.GetBaseVersion());
4255 EXPECT_EQ("tag1", tag1
.GetUniqueClientTag());
4256 EXPECT_EQ(tag1_metahandle
, tag1
.GetMetahandle());
4258 Entry
tag2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
4259 ASSERT_TRUE(tag2
.good());
4260 ASSERT_TRUE(tag2
.GetId().ServerKnows());
4261 ASSERT_EQ(id3
, tag2
.GetId())
4262 << "ID 3 should be kept, since it was less than ID 4.";
4263 EXPECT_FALSE(tag2
.GetIsDel());
4264 EXPECT_FALSE(tag2
.GetIsUnappliedUpdate());
4265 EXPECT_FALSE(tag2
.GetIsUnsynced());
4266 EXPECT_EQ(13, tag2
.GetBaseVersion());
4267 EXPECT_EQ("tag2", tag2
.GetUniqueClientTag());
4268 EXPECT_EQ(tag2_metahandle
, tag2
.GetMetahandle());
4270 // Preferences type root should have been created by the updates above.
4271 Entry
pref_root(&trans
, GET_TYPE_ROOT
, PREFERENCES
);
4272 ASSERT_TRUE(pref_root
.good());
4274 syncable::Directory::Metahandles children
;
4275 directory()->GetChildHandlesById(&trans
, pref_root
.GetId(), &children
);
4276 ASSERT_EQ(2U, children
.size());
4280 TEST_F(SyncerTest
, ClientTagClashWithinBatchOfUpdates
) {
4281 // This test is written assuming that ID comparison
4282 // will work out in a particular way.
4283 EXPECT_TRUE(ids_
.FromNumber(1) < ids_
.FromNumber(4));
4284 EXPECT_TRUE(ids_
.FromNumber(201) < ids_
.FromNumber(205));
4286 // Least ID: winner.
4287 mock_server_
->AddUpdatePref(ids_
.FromNumber(1).GetServerId(), "", "tag a", 1,
4289 mock_server_
->AddUpdatePref(ids_
.FromNumber(2).GetServerId(), "", "tag a", 11,
4291 mock_server_
->AddUpdatePref(ids_
.FromNumber(3).GetServerId(), "", "tag a", 12,
4293 mock_server_
->AddUpdatePref(ids_
.FromNumber(4).GetServerId(), "", "tag a", 13,
4295 mock_server_
->AddUpdatePref(ids_
.FromNumber(105).GetServerId(), "", "tag b",
4297 mock_server_
->AddUpdatePref(ids_
.FromNumber(102).GetServerId(), "", "tag b",
4299 // Least ID: winner.
4300 mock_server_
->AddUpdatePref(ids_
.FromNumber(101).GetServerId(), "", "tag b",
4302 mock_server_
->AddUpdatePref(ids_
.FromNumber(104).GetServerId(), "", "tag b",
4305 mock_server_
->AddUpdatePref(ids_
.FromNumber(205).GetServerId(), "", "tag c",
4307 mock_server_
->AddUpdatePref(ids_
.FromNumber(202).GetServerId(), "", "tag c",
4309 mock_server_
->AddUpdatePref(ids_
.FromNumber(204).GetServerId(), "", "tag c",
4311 // Least ID: winner.
4312 mock_server_
->AddUpdatePref(ids_
.FromNumber(201).GetServerId(), "", "tag c",
4315 mock_server_
->set_conflict_all_commits(true);
4317 EXPECT_TRUE(SyncShareNudge());
4318 // This should cause client tag overwrite.
4320 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4322 Entry
tag_a(&trans
, GET_BY_CLIENT_TAG
, "tag a");
4323 ASSERT_TRUE(tag_a
.good());
4324 EXPECT_TRUE(tag_a
.GetId().ServerKnows());
4325 EXPECT_EQ(ids_
.FromNumber(1), tag_a
.GetId());
4326 EXPECT_FALSE(tag_a
.GetIsDel());
4327 EXPECT_FALSE(tag_a
.GetIsUnappliedUpdate());
4328 EXPECT_FALSE(tag_a
.GetIsUnsynced());
4329 EXPECT_EQ(1, tag_a
.GetBaseVersion());
4330 EXPECT_EQ("tag a", tag_a
.GetUniqueClientTag());
4332 Entry
tag_b(&trans
, GET_BY_CLIENT_TAG
, "tag b");
4333 ASSERT_TRUE(tag_b
.good());
4334 EXPECT_TRUE(tag_b
.GetId().ServerKnows());
4335 EXPECT_EQ(ids_
.FromNumber(101), tag_b
.GetId());
4336 EXPECT_FALSE(tag_b
.GetIsDel());
4337 EXPECT_FALSE(tag_b
.GetIsUnappliedUpdate());
4338 EXPECT_FALSE(tag_b
.GetIsUnsynced());
4339 EXPECT_EQ(16, tag_b
.GetBaseVersion());
4340 EXPECT_EQ("tag b", tag_b
.GetUniqueClientTag());
4342 Entry
tag_c(&trans
, GET_BY_CLIENT_TAG
, "tag c");
4343 ASSERT_TRUE(tag_c
.good());
4344 EXPECT_TRUE(tag_c
.GetId().ServerKnows());
4345 EXPECT_EQ(ids_
.FromNumber(201), tag_c
.GetId());
4346 EXPECT_FALSE(tag_c
.GetIsDel());
4347 EXPECT_FALSE(tag_c
.GetIsUnappliedUpdate());
4348 EXPECT_FALSE(tag_c
.GetIsUnsynced());
4349 EXPECT_EQ(21, tag_c
.GetBaseVersion());
4350 EXPECT_EQ("tag c", tag_c
.GetUniqueClientTag());
4352 // Preferences type root should have been created by the updates above.
4353 Entry
pref_root(&trans
, GET_TYPE_ROOT
, PREFERENCES
);
4354 ASSERT_TRUE(pref_root
.good());
4356 // Verify that we have exactly 3 tagged nodes under the type root.
4357 syncable::Directory::Metahandles children
;
4358 directory()->GetChildHandlesById(&trans
, pref_root
.GetId(), &children
);
4359 ASSERT_EQ(3U, children
.size());
4363 // This verifies transition to implicit permanent folders.
4364 TEST_F(SyncerTest
, EntryWithParentIdUpdatedWithEntryWithoutParentId
) {
4365 // Make sure SPECIFICS root exists so that we can get its parent ID.
4366 mock_server_
->AddUpdateSpecifics(1, 0, "Folder", 10, 10, true, 1,
4367 DefaultPreferencesSpecifics());
4368 mock_server_
->SetLastUpdateServerTag(ModelTypeToRootTag(PREFERENCES
));
4369 EXPECT_TRUE(SyncShareNudge());
4373 // Preferences type root should have been created by the update above.
4374 // We need it in order to get its ID.
4375 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4376 Entry
pref_root(&trans
, GET_TYPE_ROOT
, PREFERENCES
);
4377 ASSERT_TRUE(pref_root
.good());
4378 pref_root_id
= pref_root
.GetId();
4381 // Add a preference item with explicit parent ID.
4383 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4384 MutableEntry
entry(&trans
, CREATE
, PREFERENCES
, pref_root_id
, "tag");
4385 ASSERT_TRUE(entry
.good());
4386 entry
.PutIsDir(false);
4387 entry
.PutBaseVersion(1);
4388 entry
.PutUniqueClientTag("tag");
4389 entry
.PutId(ids_
.FromNumber(2));
4392 // Verify the entry above.
4394 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4395 Entry
pref_entry(&trans
, GET_BY_CLIENT_TAG
, "tag");
4396 ASSERT_TRUE(pref_entry
.good());
4397 ASSERT_EQ(pref_root_id
, pref_entry
.GetParentId());
4400 // Make another update where the same item get updated, this time
4401 // with implicit parent ID.
4402 mock_server_
->AddUpdatePref(ids_
.FromNumber(2).GetServerId(), "", "tag", 2,
4405 EXPECT_TRUE(SyncShareNudge());
4408 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4409 Entry
pref_entry(&trans
, GET_BY_CLIENT_TAG
, "tag");
4410 ASSERT_TRUE(pref_entry
.good());
4411 ASSERT_TRUE(pref_entry
.GetParentId().IsNull());
4413 // Verify that there is still one node under the type root.
4414 syncable::Directory::Metahandles children
;
4415 directory()->GetChildHandlesById(&trans
, pref_root_id
, &children
);
4416 ASSERT_EQ(1U, children
.size());
4420 TEST_F(SyncerTest
, UniqueServerTagUpdates
) {
4421 // As a hurdle, introduce an item whose name is the same as the tag value
4423 int64 hurdle_handle
= CreateUnsyncedDirectory("bob", "id_bob");
4425 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4426 Entry
hurdle(&trans
, GET_BY_HANDLE
, hurdle_handle
);
4427 ASSERT_TRUE(hurdle
.good());
4428 ASSERT_TRUE(!hurdle
.GetIsDel());
4429 ASSERT_TRUE(hurdle
.GetUniqueServerTag().empty());
4430 ASSERT_TRUE(hurdle
.GetNonUniqueName()== "bob");
4432 // Try to lookup by the tagname. These should fail.
4433 Entry
tag_alpha(&trans
, GET_BY_SERVER_TAG
, "alpha");
4434 EXPECT_FALSE(tag_alpha
.good());
4435 Entry
tag_bob(&trans
, GET_BY_SERVER_TAG
, "bob");
4436 EXPECT_FALSE(tag_bob
.good());
4439 // Now download some tagged items as updates.
4440 mock_server_
->AddUpdateDirectory(
4441 1, 0, "update1", 1, 10, std::string(), std::string());
4442 mock_server_
->SetLastUpdateServerTag("alpha");
4443 mock_server_
->AddUpdateDirectory(
4444 2, 0, "update2", 2, 20, std::string(), std::string());
4445 mock_server_
->SetLastUpdateServerTag("bob");
4446 EXPECT_TRUE(SyncShareNudge());
4449 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4451 // The new items should be applied as new entries, and we should be able
4452 // to look them up by their tag values.
4453 Entry
tag_alpha(&trans
, GET_BY_SERVER_TAG
, "alpha");
4454 ASSERT_TRUE(tag_alpha
.good());
4455 ASSERT_TRUE(!tag_alpha
.GetIsDel());
4456 ASSERT_TRUE(tag_alpha
.GetUniqueServerTag()== "alpha");
4457 ASSERT_TRUE(tag_alpha
.GetNonUniqueName()== "update1");
4458 Entry
tag_bob(&trans
, GET_BY_SERVER_TAG
, "bob");
4459 ASSERT_TRUE(tag_bob
.good());
4460 ASSERT_TRUE(!tag_bob
.GetIsDel());
4461 ASSERT_TRUE(tag_bob
.GetUniqueServerTag()== "bob");
4462 ASSERT_TRUE(tag_bob
.GetNonUniqueName()== "update2");
4463 // The old item should be unchanged.
4464 Entry
hurdle(&trans
, GET_BY_HANDLE
, hurdle_handle
);
4465 ASSERT_TRUE(hurdle
.good());
4466 ASSERT_TRUE(!hurdle
.GetIsDel());
4467 ASSERT_TRUE(hurdle
.GetUniqueServerTag().empty());
4468 ASSERT_TRUE(hurdle
.GetNonUniqueName()== "bob");
4472 TEST_F(SyncerTest
, GetUpdatesSetsRequestedTypes
) {
4473 // The expectations of this test happen in the MockConnectionManager's
4474 // GetUpdates handler. EnableDatatype sets the expectation value from our
4475 // set of enabled/disabled datatypes.
4476 EnableDatatype(BOOKMARKS
);
4477 EXPECT_TRUE(SyncShareNudge());
4478 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4480 EnableDatatype(AUTOFILL
);
4481 EXPECT_TRUE(SyncShareNudge());
4482 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4484 EnableDatatype(PREFERENCES
);
4485 EXPECT_TRUE(SyncShareNudge());
4486 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4488 DisableDatatype(BOOKMARKS
);
4489 EXPECT_TRUE(SyncShareNudge());
4490 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4492 DisableDatatype(AUTOFILL
);
4493 EXPECT_TRUE(SyncShareNudge());
4494 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4496 DisableDatatype(PREFERENCES
);
4497 EnableDatatype(AUTOFILL
);
4498 EXPECT_TRUE(SyncShareNudge());
4499 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4502 // A typical scenario: server and client each have one update for the other.
4503 // This is the "happy path" alternative to UpdateFailsThenDontCommit.
4504 TEST_F(SyncerTest
, UpdateThenCommit
) {
4505 syncable::Id to_receive
= ids_
.NewServerId();
4506 syncable::Id to_commit
= ids_
.NewLocalId();
4508 mock_server_
->AddUpdateDirectory(to_receive
, ids_
.root(), "x", 1, 10,
4509 foreign_cache_guid(), "-1");
4510 int64 commit_handle
= CreateUnsyncedDirectory("y", to_commit
);
4511 EXPECT_TRUE(SyncShareNudge());
4513 // The sync cycle should have included a GetUpdate, then a commit. By the
4514 // time the commit happened, we should have known for sure that there were no
4515 // hierarchy conflicts, and reported this fact to the server.
4516 ASSERT_TRUE(mock_server_
->last_request().has_commit());
4517 VerifyNoHierarchyConflictsReported(mock_server_
->last_request());
4519 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4521 Entry
received(&trans
, GET_BY_ID
, to_receive
);
4522 ASSERT_TRUE(received
.good());
4523 EXPECT_FALSE(received
.GetIsUnsynced());
4524 EXPECT_FALSE(received
.GetIsUnappliedUpdate());
4526 Entry
committed(&trans
, GET_BY_HANDLE
, commit_handle
);
4527 ASSERT_TRUE(committed
.good());
4528 EXPECT_FALSE(committed
.GetIsUnsynced());
4529 EXPECT_FALSE(committed
.GetIsUnappliedUpdate());
4532 // Same as above, but this time we fail to download updates.
4533 // We should not attempt to commit anything unless we successfully downloaded
4534 // updates, otherwise we risk causing a server-side conflict.
4535 TEST_F(SyncerTest
, UpdateFailsThenDontCommit
) {
4536 syncable::Id to_receive
= ids_
.NewServerId();
4537 syncable::Id to_commit
= ids_
.NewLocalId();
4539 mock_server_
->AddUpdateDirectory(to_receive
, ids_
.root(), "x", 1, 10,
4540 foreign_cache_guid(), "-1");
4541 int64 commit_handle
= CreateUnsyncedDirectory("y", to_commit
);
4542 mock_server_
->FailNextPostBufferToPathCall();
4543 EXPECT_FALSE(SyncShareNudge());
4545 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4547 // We did not receive this update.
4548 Entry
received(&trans
, GET_BY_ID
, to_receive
);
4549 ASSERT_FALSE(received
.good());
4551 // And our local update remains unapplied.
4552 Entry
committed(&trans
, GET_BY_HANDLE
, commit_handle
);
4553 ASSERT_TRUE(committed
.good());
4554 EXPECT_TRUE(committed
.GetIsUnsynced());
4555 EXPECT_FALSE(committed
.GetIsUnappliedUpdate());
4557 // Inform the Mock we won't be fetching all updates.
4558 mock_server_
->ClearUpdatesQueue();
4561 // Downloads two updates and applies them successfully.
4562 // This is the "happy path" alternative to ConfigureFailsDontApplyUpdates.
4563 TEST_F(SyncerTest
, ConfigureDownloadsTwoBatchesSuccess
) {
4564 syncable::Id node1
= ids_
.NewServerId();
4565 syncable::Id node2
= ids_
.NewServerId();
4567 // Construct the first GetUpdates response.
4568 mock_server_
->AddUpdateDirectory(node1
, ids_
.root(), "one", 1, 10,
4569 foreign_cache_guid(), "-2");
4570 mock_server_
->SetChangesRemaining(1);
4571 mock_server_
->NextUpdateBatch();
4573 // Construct the second GetUpdates response.
4574 mock_server_
->AddUpdateDirectory(node2
, ids_
.root(), "two", 1, 20,
4575 foreign_cache_guid(), "-2");
4577 SyncShareConfigure();
4579 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4580 // Both nodes should be downloaded and applied.
4582 Entry
n1(&trans
, GET_BY_ID
, node1
);
4583 ASSERT_TRUE(n1
.good());
4584 EXPECT_FALSE(n1
.GetIsUnappliedUpdate());
4586 Entry
n2(&trans
, GET_BY_ID
, node2
);
4587 ASSERT_TRUE(n2
.good());
4588 EXPECT_FALSE(n2
.GetIsUnappliedUpdate());
4591 // Same as the above case, but this time the second batch fails to download.
4592 TEST_F(SyncerTest
, ConfigureFailsDontApplyUpdates
) {
4593 syncable::Id node1
= ids_
.NewServerId();
4594 syncable::Id node2
= ids_
.NewServerId();
4596 // The scenario: we have two batches of updates with one update each. A
4597 // normal confgure step would download all the updates one batch at a time and
4598 // apply them. This configure will succeed in downloading the first batch
4599 // then fail when downloading the second.
4600 mock_server_
->FailNthPostBufferToPathCall(2);
4602 // Construct the first GetUpdates response.
4603 mock_server_
->AddUpdateDirectory(node1
, ids_
.root(), "one", 1, 10,
4604 foreign_cache_guid(), "-1");
4605 mock_server_
->SetChangesRemaining(1);
4606 mock_server_
->NextUpdateBatch();
4608 // Consutrct the second GetUpdates response.
4609 mock_server_
->AddUpdateDirectory(node2
, ids_
.root(), "two", 1, 20,
4610 foreign_cache_guid(), "-2");
4612 SyncShareConfigure();
4614 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4616 // The first node was downloaded, but not applied.
4617 Entry
n1(&trans
, GET_BY_ID
, node1
);
4618 ASSERT_TRUE(n1
.good());
4619 EXPECT_TRUE(n1
.GetIsUnappliedUpdate());
4621 // The second node was not downloaded.
4622 Entry
n2(&trans
, GET_BY_ID
, node2
);
4623 EXPECT_FALSE(n2
.good());
4625 // One update remains undownloaded.
4626 mock_server_
->ClearUpdatesQueue();
4629 TEST_F(SyncerTest
, GetKeySuccess
) {
4631 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4632 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4635 SyncShareConfigure();
4637 EXPECT_EQ(session_
->status_controller().last_get_key_result(), SYNCER_OK
);
4639 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4640 EXPECT_FALSE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4644 TEST_F(SyncerTest
, GetKeyEmpty
) {
4646 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4647 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4650 mock_server_
->SetKeystoreKey(std::string());
4651 SyncShareConfigure();
4653 EXPECT_NE(session_
->status_controller().last_get_key_result(), SYNCER_OK
);
4655 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4656 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4660 // Tests specifically related to bookmark (and therefore no client tags) sync
4661 // logic. Entities without client tags have custom logic in parts of the code,
4662 // and hence are not covered by e.g. the Undeletion tests below.
4663 class SyncerBookmarksTest
: public SyncerTest
{
4665 SyncerBookmarksTest() : metahandle_(syncable::kInvalidMetaHandle
) {
4669 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4670 MutableEntry
bookmark(
4671 &trans
, CREATE
, BOOKMARKS
, ids_
.root(), "clientname");
4672 ASSERT_TRUE(bookmark
.good());
4673 bookmark
.PutSpecifics(DefaultBookmarkSpecifics());
4674 EXPECT_FALSE(bookmark
.GetIsUnappliedUpdate());
4675 EXPECT_FALSE(bookmark
.GetId().ServerKnows());
4676 metahandle_
= bookmark
.GetMetahandle();
4677 local_id_
= bookmark
.GetId();
4678 bookmark
.PutIsUnsynced(true);
4682 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4683 MutableEntry
bookmark(&trans
, GET_BY_ID
, local_id_
);
4684 ASSERT_TRUE(bookmark
.good());
4685 bookmark
.PutSpecifics(DefaultBookmarkSpecifics());
4686 EXPECT_FALSE(bookmark
.GetIsUnappliedUpdate());
4687 bookmark
.PutIsUnsynced(true);
4688 if (bookmark
.GetSyncing())
4689 bookmark
.PutDirtySync(true);
4693 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4694 MutableEntry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4695 ASSERT_TRUE(entry
.good());
4696 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4697 // The order of setting IS_UNSYNCED vs IS_DEL matters. See
4698 // WriteNode::Tombstone().
4699 entry
.PutIsUnsynced(true);
4700 if (entry
.GetSyncing())
4701 entry
.PutDirtySync(true);
4702 entry
.PutIsDel(true);
4705 void UpdateAndDelete() {
4711 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4712 MutableEntry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4713 ASSERT_TRUE(entry
.good());
4714 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4715 EXPECT_TRUE(entry
.GetIsDel());
4716 entry
.PutIsDel(false);
4717 entry
.PutIsUnsynced(true);
4718 if (entry
.GetSyncing())
4719 entry
.PutDirtySync(true);
4722 int64
GetMetahandleOfTag() {
4723 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4724 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4725 EXPECT_TRUE(entry
.good());
4726 if (!entry
.good()) {
4727 return syncable::kInvalidMetaHandle
;
4729 return entry
.GetMetahandle();
4733 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4734 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4735 EXPECT_TRUE(entry
.good());
4736 if (!entry
.good()) {
4739 return entry
.GetId();
4742 void ExpectUnsyncedCreation() {
4743 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4744 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4746 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4747 EXPECT_FALSE(entry
.GetIsDel());
4748 EXPECT_FALSE(entry
.GetServerIsDel()); // Never been committed.
4749 EXPECT_LT(entry
.GetBaseVersion(), 0);
4750 EXPECT_TRUE(entry
.GetIsUnsynced());
4751 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4754 void ExpectUnsyncedUndeletion() {
4755 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4756 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4758 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4759 EXPECT_FALSE(entry
.GetIsDel());
4760 EXPECT_TRUE(entry
.GetServerIsDel());
4761 EXPECT_GE(entry
.GetBaseVersion(), 0);
4762 EXPECT_TRUE(entry
.GetIsUnsynced());
4763 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4764 EXPECT_TRUE(entry
.GetId().ServerKnows());
4767 void ExpectUnsyncedEdit() {
4768 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4769 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4771 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4772 EXPECT_FALSE(entry
.GetIsDel());
4773 EXPECT_FALSE(entry
.GetServerIsDel());
4774 EXPECT_GE(entry
.GetBaseVersion(), 0);
4775 EXPECT_TRUE(entry
.GetIsUnsynced());
4776 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4777 EXPECT_TRUE(entry
.GetId().ServerKnows());
4780 void ExpectUnsyncedDeletion() {
4781 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4782 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4784 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4785 EXPECT_TRUE(entry
.GetIsDel());
4786 EXPECT_FALSE(entry
.GetServerIsDel());
4787 EXPECT_TRUE(entry
.GetIsUnsynced());
4788 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4789 EXPECT_GE(entry
.GetBaseVersion(), 0);
4790 EXPECT_GE(entry
.GetServerVersion(), 0);
4793 void ExpectSyncedAndCreated() {
4794 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4795 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4797 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4798 EXPECT_FALSE(entry
.GetIsDel());
4799 EXPECT_FALSE(entry
.GetServerIsDel());
4800 EXPECT_GE(entry
.GetBaseVersion(), 0);
4801 EXPECT_EQ(entry
.GetBaseVersion(), entry
.GetServerVersion());
4802 EXPECT_FALSE(entry
.GetIsUnsynced());
4803 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4806 void ExpectSyncedAndDeleted() {
4807 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4808 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4810 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4811 EXPECT_TRUE(entry
.GetIsDel());
4812 EXPECT_TRUE(entry
.GetServerIsDel());
4813 EXPECT_FALSE(entry
.GetIsUnsynced());
4814 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4815 EXPECT_GE(entry
.GetBaseVersion(), 0);
4816 EXPECT_GE(entry
.GetServerVersion(), 0);
4820 syncable::Id local_id_
;
4824 TEST_F(SyncerBookmarksTest
, CreateSyncThenDeleteSync
) {
4826 ExpectUnsyncedCreation();
4827 EXPECT_TRUE(SyncShareNudge());
4828 ExpectSyncedAndCreated();
4830 ExpectUnsyncedDeletion();
4831 EXPECT_TRUE(SyncShareNudge());
4832 ExpectSyncedAndDeleted();
4835 TEST_F(SyncerBookmarksTest
, CreateThenDeleteBeforeSync
) {
4837 ExpectUnsyncedCreation();
4840 // Deleting before the initial commit should result in not needing to send
4841 // the delete to the server. It will still be in an unsynced state, but with
4842 // IS_UNSYNCED set to false.
4844 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4845 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4847 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4848 EXPECT_TRUE(entry
.GetIsDel());
4849 EXPECT_FALSE(entry
.GetServerIsDel());
4850 EXPECT_FALSE(entry
.GetIsUnsynced());
4851 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4852 EXPECT_EQ(entry
.GetBaseVersion(), -1);
4853 EXPECT_EQ(entry
.GetServerVersion(), 0);
4857 TEST_F(SyncerBookmarksTest
, LocalDeleteRemoteChangeConflict
) {
4859 ExpectUnsyncedCreation();
4860 EXPECT_TRUE(SyncShareNudge());
4861 ExpectSyncedAndCreated();
4863 ExpectUnsyncedDeletion();
4865 // Trigger a getupdates that modifies the bookmark. The update should be
4866 // clobbered by the local delete.
4867 mock_server_
->AddUpdateBookmark(GetServerId(), Id::GetRoot(), "dummy", 10, 10,
4868 local_cache_guid(), local_id_
.GetServerId());
4870 EXPECT_TRUE(SyncShareNudge());
4871 ExpectSyncedAndDeleted();
4874 TEST_F(SyncerBookmarksTest
, CreateThenDeleteDuringCommit
) {
4876 ExpectUnsyncedCreation();
4878 // In the middle of the initial creation commit, perform a deletion.
4879 // This should trigger performing two consecutive commit cycles, resulting
4880 // in the bookmark being both deleted and synced.
4881 mock_server_
->SetMidCommitCallback(
4882 base::Bind(&SyncerBookmarksTest::Delete
, base::Unretained(this)));
4884 EXPECT_TRUE(SyncShareNudge());
4885 ExpectSyncedAndDeleted();
4888 TEST_F(SyncerBookmarksTest
, CreateThenUpdateAndDeleteDuringCommit
) {
4890 ExpectUnsyncedCreation();
4892 // In the middle of the initial creation commit, perform an updated followed
4893 // by a deletion. This should trigger performing two consecutive commit
4894 // cycles, resulting in the bookmark being both deleted and synced.
4895 mock_server_
->SetMidCommitCallback(base::Bind(
4896 &SyncerBookmarksTest::UpdateAndDelete
, base::Unretained(this)));
4898 EXPECT_TRUE(SyncShareNudge());
4899 ExpectSyncedAndDeleted();
4902 // Test what happens if a client deletes, then recreates, an object very
4903 // quickly. It is possible that the deletion gets sent as a commit, and
4904 // the undelete happens during the commit request. The principle here
4905 // is that with a single committing client, conflicts should never
4906 // be encountered, and a client encountering its past actions during
4907 // getupdates should never feed back to override later actions.
4909 // In cases of ordering A-F below, the outcome should be the same.
4910 // Exercised by UndeleteDuringCommit:
4911 // A. Delete - commit - undelete - commitresponse.
4912 // B. Delete - commit - undelete - commitresponse - getupdates.
4913 // Exercised by UndeleteBeforeCommit:
4914 // C. Delete - undelete - commit - commitresponse.
4915 // D. Delete - undelete - commit - commitresponse - getupdates.
4916 // Exercised by UndeleteAfterCommit:
4917 // E. Delete - commit - commitresponse - undelete - commit
4918 // - commitresponse.
4919 // F. Delete - commit - commitresponse - undelete - commit -
4920 // - commitresponse - getupdates.
4921 class SyncerUndeletionTest
: public SyncerTest
{
4923 SyncerUndeletionTest()
4924 : client_tag_("foobar"),
4925 metahandle_(syncable::kInvalidMetaHandle
) {
4929 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4930 MutableEntry
perm_folder(
4931 &trans
, CREATE
, PREFERENCES
, ids_
.root(), "clientname");
4932 ASSERT_TRUE(perm_folder
.good());
4933 perm_folder
.PutUniqueClientTag(client_tag_
);
4934 perm_folder
.PutIsUnsynced(true);
4935 if (perm_folder
.GetSyncing())
4936 perm_folder
.PutDirtySync(true);
4937 perm_folder
.PutSpecifics(DefaultPreferencesSpecifics());
4938 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
4939 EXPECT_FALSE(perm_folder
.GetId().ServerKnows());
4940 metahandle_
= perm_folder
.GetMetahandle();
4941 local_id_
= perm_folder
.GetId();
4945 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4946 MutableEntry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4947 ASSERT_TRUE(entry
.good());
4948 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4949 // The order of setting IS_UNSYNCED vs IS_DEL matters. See
4950 // WriteNode::Tombstone().
4951 entry
.PutIsUnsynced(true);
4952 if (entry
.GetSyncing())
4953 entry
.PutDirtySync(true);
4954 entry
.PutIsDel(true);
4958 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4959 MutableEntry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4960 ASSERT_TRUE(entry
.good());
4961 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4962 EXPECT_TRUE(entry
.GetIsDel());
4963 entry
.PutIsDel(false);
4964 entry
.PutIsUnsynced(true);
4965 if (entry
.GetSyncing())
4966 entry
.PutDirtySync(true);
4969 int64
GetMetahandleOfTag() {
4970 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4971 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4972 EXPECT_TRUE(entry
.good());
4973 if (!entry
.good()) {
4974 return syncable::kInvalidMetaHandle
;
4976 return entry
.GetMetahandle();
4979 void ExpectUnsyncedCreation() {
4980 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4981 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4983 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4984 EXPECT_FALSE(entry
.GetIsDel());
4985 EXPECT_FALSE(entry
.GetServerIsDel()); // Never been committed.
4986 EXPECT_LT(entry
.GetBaseVersion(), 0);
4987 EXPECT_TRUE(entry
.GetIsUnsynced());
4988 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4991 void ExpectUnsyncedUndeletion() {
4992 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4993 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4995 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4996 EXPECT_FALSE(entry
.GetIsDel());
4997 EXPECT_TRUE(entry
.GetServerIsDel());
4998 EXPECT_GE(entry
.GetBaseVersion(), 0);
4999 EXPECT_TRUE(entry
.GetIsUnsynced());
5000 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
5001 EXPECT_TRUE(entry
.GetId().ServerKnows());
5004 void ExpectUnsyncedEdit() {
5005 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5006 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
5008 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
5009 EXPECT_FALSE(entry
.GetIsDel());
5010 EXPECT_FALSE(entry
.GetServerIsDel());
5011 EXPECT_GE(entry
.GetBaseVersion(), 0);
5012 EXPECT_TRUE(entry
.GetIsUnsynced());
5013 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
5014 EXPECT_TRUE(entry
.GetId().ServerKnows());
5017 void ExpectUnsyncedDeletion() {
5018 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5019 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
5021 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
5022 EXPECT_TRUE(entry
.GetIsDel());
5023 EXPECT_FALSE(entry
.GetServerIsDel());
5024 EXPECT_TRUE(entry
.GetIsUnsynced());
5025 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
5026 EXPECT_GE(entry
.GetBaseVersion(), 0);
5027 EXPECT_GE(entry
.GetServerVersion(), 0);
5030 void ExpectSyncedAndCreated() {
5031 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5032 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
5034 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
5035 EXPECT_FALSE(entry
.GetIsDel());
5036 EXPECT_FALSE(entry
.GetServerIsDel());
5037 EXPECT_GE(entry
.GetBaseVersion(), 0);
5038 EXPECT_EQ(entry
.GetBaseVersion(), entry
.GetServerVersion());
5039 EXPECT_FALSE(entry
.GetIsUnsynced());
5040 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
5043 void ExpectSyncedAndDeleted() {
5044 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5045 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
5047 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
5048 EXPECT_TRUE(entry
.GetIsDel());
5049 EXPECT_TRUE(entry
.GetServerIsDel());
5050 EXPECT_FALSE(entry
.GetIsUnsynced());
5051 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
5052 EXPECT_GE(entry
.GetBaseVersion(), 0);
5053 EXPECT_GE(entry
.GetServerVersion(), 0);
5057 const std::string client_tag_
;
5058 syncable::Id local_id_
;
5062 TEST_F(SyncerUndeletionTest
, UndeleteDuringCommit
) {
5064 ExpectUnsyncedCreation();
5065 EXPECT_TRUE(SyncShareNudge());
5067 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5068 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5069 ExpectSyncedAndCreated();
5071 // Delete, begin committing the delete, then undelete while committing.
5073 ExpectUnsyncedDeletion();
5074 mock_server_
->SetMidCommitCallback(
5075 base::Bind(&SyncerUndeletionTest::Undelete
, base::Unretained(this)));
5076 EXPECT_TRUE(SyncShareNudge());
5078 // We will continue to commit until all nodes are synced, so we expect
5079 // that both the delete and following undelete were committed. We haven't
5080 // downloaded any updates, though, so the SERVER fields will be the same
5081 // as they were at the start of the cycle.
5082 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5083 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5086 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5087 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5089 // Server fields lag behind.
5090 EXPECT_FALSE(entry
.GetServerIsDel());
5092 // We have committed the second (undelete) update.
5093 EXPECT_FALSE(entry
.GetIsDel());
5094 EXPECT_FALSE(entry
.GetIsUnsynced());
5095 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
5098 // Now, encounter a GetUpdates corresponding to the deletion from
5099 // the server. The undeletion should prevail again and be committed.
5100 // None of this should trigger any conflict detection -- it is perfectly
5101 // normal to recieve updates from our own commits.
5102 mock_server_
->SetMidCommitCallback(base::Closure());
5103 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5104 update
->set_originator_cache_guid(local_cache_guid());
5105 update
->set_originator_client_item_id(local_id_
.GetServerId());
5107 EXPECT_TRUE(SyncShareNudge());
5108 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5109 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5110 ExpectSyncedAndCreated();
5113 TEST_F(SyncerUndeletionTest
, UndeleteBeforeCommit
) {
5115 ExpectUnsyncedCreation();
5116 EXPECT_TRUE(SyncShareNudge());
5118 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5119 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5120 ExpectSyncedAndCreated();
5122 // Delete and undelete, then sync to pick up the result.
5124 ExpectUnsyncedDeletion();
5126 ExpectUnsyncedEdit(); // Edit, not undelete: server thinks it exists.
5127 EXPECT_TRUE(SyncShareNudge());
5129 // The item ought to have committed successfully.
5130 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5131 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5132 ExpectSyncedAndCreated();
5134 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5135 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5136 EXPECT_EQ(2, entry
.GetBaseVersion());
5139 // Now, encounter a GetUpdates corresponding to the just-committed
5141 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5142 update
->set_originator_cache_guid(local_cache_guid());
5143 update
->set_originator_client_item_id(local_id_
.GetServerId());
5144 EXPECT_TRUE(SyncShareNudge());
5145 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5146 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5147 ExpectSyncedAndCreated();
5150 TEST_F(SyncerUndeletionTest
, UndeleteAfterCommitButBeforeGetUpdates
) {
5152 ExpectUnsyncedCreation();
5153 EXPECT_TRUE(SyncShareNudge());
5155 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5156 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5157 ExpectSyncedAndCreated();
5159 // Delete and commit.
5161 ExpectUnsyncedDeletion();
5162 EXPECT_TRUE(SyncShareNudge());
5164 // The item ought to have committed successfully.
5165 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5166 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5167 ExpectSyncedAndDeleted();
5169 // Before the GetUpdates, the item is locally undeleted.
5171 ExpectUnsyncedUndeletion();
5173 // Now, encounter a GetUpdates corresponding to the just-committed
5174 // deletion update. The undeletion should prevail.
5175 mock_server_
->AddUpdateFromLastCommit();
5176 EXPECT_TRUE(SyncShareNudge());
5177 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5178 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5179 ExpectSyncedAndCreated();
5182 TEST_F(SyncerUndeletionTest
, UndeleteAfterDeleteAndGetUpdates
) {
5184 ExpectUnsyncedCreation();
5185 EXPECT_TRUE(SyncShareNudge());
5187 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5188 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5189 ExpectSyncedAndCreated();
5191 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5192 update
->set_originator_cache_guid(local_cache_guid());
5193 update
->set_originator_client_item_id(local_id_
.GetServerId());
5194 EXPECT_TRUE(SyncShareNudge());
5195 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5196 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5197 ExpectSyncedAndCreated();
5199 // Delete and commit.
5201 ExpectUnsyncedDeletion();
5202 EXPECT_TRUE(SyncShareNudge());
5204 // The item ought to have committed successfully.
5205 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5206 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5207 ExpectSyncedAndDeleted();
5209 // Now, encounter a GetUpdates corresponding to the just-committed
5210 // deletion update. Should be consistent.
5211 mock_server_
->AddUpdateFromLastCommit();
5212 EXPECT_TRUE(SyncShareNudge());
5213 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5214 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5215 ExpectSyncedAndDeleted();
5217 // After the GetUpdates, the item is locally undeleted.
5219 ExpectUnsyncedUndeletion();
5221 // Now, encounter a GetUpdates corresponding to the just-committed
5222 // deletion update. The undeletion should prevail.
5223 EXPECT_TRUE(SyncShareNudge());
5224 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5225 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5226 ExpectSyncedAndCreated();
5229 // Test processing of undeletion GetUpdateses.
5230 TEST_F(SyncerUndeletionTest
, UndeleteAfterOtherClientDeletes
) {
5232 ExpectUnsyncedCreation();
5233 EXPECT_TRUE(SyncShareNudge());
5235 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5236 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5237 ExpectSyncedAndCreated();
5239 // Add a delete from the server.
5240 sync_pb::SyncEntity
* update1
= mock_server_
->AddUpdateFromLastCommit();
5241 update1
->set_originator_cache_guid(local_cache_guid());
5242 update1
->set_originator_client_item_id(local_id_
.GetServerId());
5243 EXPECT_TRUE(SyncShareNudge());
5244 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5245 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5246 ExpectSyncedAndCreated();
5248 // Some other client deletes the item.
5250 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5251 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5252 mock_server_
->AddUpdateTombstone(entry
.GetId(), PREFERENCES
);
5254 EXPECT_TRUE(SyncShareNudge());
5256 // The update ought to have applied successfully.
5257 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5258 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5259 ExpectSyncedAndDeleted();
5261 // Undelete it locally.
5263 ExpectUnsyncedUndeletion();
5264 EXPECT_TRUE(SyncShareNudge());
5265 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5266 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5267 ExpectSyncedAndCreated();
5269 // Now, encounter a GetUpdates corresponding to the just-committed
5270 // deletion update. The undeletion should prevail.
5271 sync_pb::SyncEntity
* update2
= mock_server_
->AddUpdateFromLastCommit();
5272 update2
->set_originator_cache_guid(local_cache_guid());
5273 update2
->set_originator_client_item_id(local_id_
.GetServerId());
5274 EXPECT_TRUE(SyncShareNudge());
5275 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5276 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5277 ExpectSyncedAndCreated();
5280 TEST_F(SyncerUndeletionTest
, UndeleteAfterOtherClientDeletesImmediately
) {
5282 ExpectUnsyncedCreation();
5283 EXPECT_TRUE(SyncShareNudge());
5285 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5286 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5287 ExpectSyncedAndCreated();
5289 // Some other client deletes the item before we get a chance
5290 // to GetUpdates our original request.
5292 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5293 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5294 mock_server_
->AddUpdateTombstone(entry
.GetId(), PREFERENCES
);
5296 EXPECT_TRUE(SyncShareNudge());
5298 // The update ought to have applied successfully.
5299 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5300 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5301 ExpectSyncedAndDeleted();
5303 // Undelete it locally.
5305 ExpectUnsyncedUndeletion();
5306 EXPECT_TRUE(SyncShareNudge());
5307 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5308 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5309 ExpectSyncedAndCreated();
5311 // Now, encounter a GetUpdates corresponding to the just-committed
5312 // deletion update. The undeletion should prevail.
5313 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5314 update
->set_originator_cache_guid(local_cache_guid());
5315 update
->set_originator_client_item_id(local_id_
.GetServerId());
5316 EXPECT_TRUE(SyncShareNudge());
5317 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5318 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5319 ExpectSyncedAndCreated();
5322 TEST_F(SyncerUndeletionTest
, OtherClientUndeletes
) {
5324 ExpectUnsyncedCreation();
5325 EXPECT_TRUE(SyncShareNudge());
5327 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5328 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5329 ExpectSyncedAndCreated();
5331 // Get the updates of our just-committed entry.
5332 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5333 update
->set_originator_cache_guid(local_cache_guid());
5334 update
->set_originator_client_item_id(local_id_
.GetServerId());
5335 EXPECT_TRUE(SyncShareNudge());
5336 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5337 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5338 ExpectSyncedAndCreated();
5340 // We delete the item.
5342 ExpectUnsyncedDeletion();
5343 EXPECT_TRUE(SyncShareNudge());
5345 // The update ought to have applied successfully.
5346 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5347 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5348 ExpectSyncedAndDeleted();
5350 // Now, encounter a GetUpdates corresponding to the just-committed
5352 mock_server_
->AddUpdateFromLastCommit();
5353 EXPECT_TRUE(SyncShareNudge());
5354 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5355 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5356 ExpectSyncedAndDeleted();
5358 // Some other client undeletes the item.
5360 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5361 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5362 mock_server_
->AddUpdatePref(
5363 entry
.GetId().GetServerId(),
5364 entry
.GetParentId().GetServerId(),
5365 client_tag_
, 100, 1000);
5367 mock_server_
->SetLastUpdateClientTag(client_tag_
);
5368 EXPECT_TRUE(SyncShareNudge());
5369 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5370 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5371 ExpectSyncedAndCreated();
5374 TEST_F(SyncerUndeletionTest
, OtherClientUndeletesImmediately
) {
5376 ExpectUnsyncedCreation();
5377 EXPECT_TRUE(SyncShareNudge());
5379 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5380 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5381 ExpectSyncedAndCreated();
5383 // Get the updates of our just-committed entry.
5384 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
5385 update
->set_originator_cache_guid(local_cache_guid());
5387 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5388 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5389 update
->set_originator_client_item_id(local_id_
.GetServerId());
5391 EXPECT_TRUE(SyncShareNudge());
5392 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5393 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5394 ExpectSyncedAndCreated();
5396 // We delete the item.
5398 ExpectUnsyncedDeletion();
5399 EXPECT_TRUE(SyncShareNudge());
5401 // The update ought to have applied successfully.
5402 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5403 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5404 ExpectSyncedAndDeleted();
5406 // Some other client undeletes before we see the update from our
5409 syncable::ReadTransaction
trans(FROM_HERE
, directory());
5410 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
5411 mock_server_
->AddUpdatePref(
5412 entry
.GetId().GetServerId(),
5413 entry
.GetParentId().GetServerId(),
5414 client_tag_
, 100, 1000);
5416 mock_server_
->SetLastUpdateClientTag(client_tag_
);
5417 EXPECT_TRUE(SyncShareNudge());
5418 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
5419 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
5420 ExpectSyncedAndCreated();
5424 TEST_PARAM_BOOKMARK_ENABLE_BIT
,
5425 TEST_PARAM_AUTOFILL_ENABLE_BIT
,
5426 TEST_PARAM_BIT_COUNT
5431 public ::testing::WithParamInterface
<int> {
5433 bool ShouldFailBookmarkCommit() {
5434 return (GetParam() & (1 << TEST_PARAM_BOOKMARK_ENABLE_BIT
)) == 0;
5436 bool ShouldFailAutofillCommit() {
5437 return (GetParam() & (1 << TEST_PARAM_AUTOFILL_ENABLE_BIT
)) == 0;
5441 INSTANTIATE_TEST_CASE_P(ExtensionsActivity
,
5443 testing::Range(0, 1 << TEST_PARAM_BIT_COUNT
));
5445 TEST_P(MixedResult
, ExtensionsActivity
) {
5447 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
5449 MutableEntry
pref(&wtrans
, CREATE
, PREFERENCES
, wtrans
.root_id(), "pref");
5450 ASSERT_TRUE(pref
.good());
5451 pref
.PutIsUnsynced(true);
5453 MutableEntry
bookmark(
5454 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "bookmark");
5455 ASSERT_TRUE(bookmark
.good());
5456 bookmark
.PutIsUnsynced(true);
5458 if (ShouldFailBookmarkCommit()) {
5459 mock_server_
->SetTransientErrorId(bookmark
.GetId());
5462 if (ShouldFailAutofillCommit()) {
5463 mock_server_
->SetTransientErrorId(pref
.GetId());
5468 // Put some extenions activity records into the monitor.
5470 ExtensionsActivity::Records records
;
5471 records
["ABC"].extension_id
= "ABC";
5472 records
["ABC"].bookmark_write_count
= 2049U;
5473 records
["xyz"].extension_id
= "xyz";
5474 records
["xyz"].bookmark_write_count
= 4U;
5475 context_
->extensions_activity()->PutRecords(records
);
5478 EXPECT_EQ(!ShouldFailBookmarkCommit() && !ShouldFailAutofillCommit(),
5481 ExtensionsActivity::Records final_monitor_records
;
5482 context_
->extensions_activity()->GetAndClearRecords(&final_monitor_records
);
5483 if (ShouldFailBookmarkCommit()) {
5484 ASSERT_EQ(2U, final_monitor_records
.size())
5485 << "Should restore records after unsuccessful bookmark commit.";
5486 EXPECT_EQ("ABC", final_monitor_records
["ABC"].extension_id
);
5487 EXPECT_EQ("xyz", final_monitor_records
["xyz"].extension_id
);
5488 EXPECT_EQ(2049U, final_monitor_records
["ABC"].bookmark_write_count
);
5489 EXPECT_EQ(4U, final_monitor_records
["xyz"].bookmark_write_count
);
5491 EXPECT_TRUE(final_monitor_records
.empty())
5492 << "Should not restore records after successful bookmark commit.";
5496 } // namespace syncer