1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // Syncer unit tests. Unfortunately a lot of these tests
6 // are outdated and need to be reworked and updated.
15 #include "base/bind.h"
16 #include "base/bind_helpers.h"
17 #include "base/callback.h"
18 #include "base/compiler_specific.h"
19 #include "base/location.h"
20 #include "base/memory/scoped_ptr.h"
21 #include "base/message_loop/message_loop.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/stringprintf.h"
24 #include "base/time/time.h"
25 #include "build/build_config.h"
26 #include "sync/engine/get_commit_ids.h"
27 #include "sync/engine/net/server_connection_manager.h"
28 #include "sync/engine/sync_scheduler_impl.h"
29 #include "sync/engine/syncer.h"
30 #include "sync/engine/syncer_proto_util.h"
31 #include "sync/internal_api/public/base/cancelation_signal.h"
32 #include "sync/internal_api/public/base/model_type.h"
33 #include "sync/internal_api/public/engine/model_safe_worker.h"
34 #include "sync/internal_api/public/sessions/commit_counters.h"
35 #include "sync/internal_api/public/sessions/status_counters.h"
36 #include "sync/internal_api/public/sessions/update_counters.h"
37 #include "sync/protocol/bookmark_specifics.pb.h"
38 #include "sync/protocol/nigori_specifics.pb.h"
39 #include "sync/protocol/preference_specifics.pb.h"
40 #include "sync/protocol/sync.pb.h"
41 #include "sync/sessions/sync_session_context.h"
42 #include "sync/syncable/mutable_entry.h"
43 #include "sync/syncable/nigori_util.h"
44 #include "sync/syncable/syncable_delete_journal.h"
45 #include "sync/syncable/syncable_read_transaction.h"
46 #include "sync/syncable/syncable_util.h"
47 #include "sync/syncable/syncable_write_transaction.h"
48 #include "sync/test/engine/fake_model_worker.h"
49 #include "sync/test/engine/mock_connection_manager.h"
50 #include "sync/test/engine/mock_nudge_handler.h"
51 #include "sync/test/engine/test_directory_setter_upper.h"
52 #include "sync/test/engine/test_id_factory.h"
53 #include "sync/test/engine/test_syncable_utils.h"
54 #include "sync/test/fake_encryptor.h"
55 #include "sync/test/fake_sync_encryption_handler.h"
56 #include "sync/test/sessions/mock_debug_info_getter.h"
57 #include "sync/util/cryptographer.h"
58 #include "sync/util/extensions_activity.h"
59 #include "sync/util/time.h"
60 #include "testing/gtest/include/gtest/gtest.h"
62 using base::TimeDelta
;
73 using syncable::BaseTransaction
;
75 using syncable::CountEntriesWithName
;
76 using syncable::Directory
;
77 using syncable::Entry
;
78 using syncable::GetFirstEntryWithName
;
79 using syncable::GetOnlyEntryWithName
;
81 using syncable::kEncryptedString
;
82 using syncable::MutableEntry
;
83 using syncable::WriteTransaction
;
85 using syncable::CREATE
;
86 using syncable::GET_BY_HANDLE
;
87 using syncable::GET_BY_ID
;
88 using syncable::GET_BY_CLIENT_TAG
;
89 using syncable::GET_BY_SERVER_TAG
;
90 using syncable::GET_TYPE_ROOT
;
91 using syncable::UNITTEST
;
93 using sessions::MockDebugInfoGetter
;
94 using sessions::StatusController
;
95 using sessions::SyncSessionContext
;
96 using sessions::SyncSession
;
100 // A helper to hold on to the counters emitted by the sync engine.
101 class TypeDebugInfoCache
: public TypeDebugInfoObserver
{
103 TypeDebugInfoCache();
104 virtual ~TypeDebugInfoCache();
106 CommitCounters
GetLatestCommitCounters(ModelType type
) const;
107 UpdateCounters
GetLatestUpdateCounters(ModelType type
) const;
108 StatusCounters
GetLatestStatusCounters(ModelType type
) const;
110 // TypeDebugInfoObserver implementation.
111 virtual void OnCommitCountersUpdated(
112 syncer::ModelType type
,
113 const CommitCounters
& counters
) OVERRIDE
;
114 virtual void OnUpdateCountersUpdated(
115 syncer::ModelType type
,
116 const UpdateCounters
& counters
) OVERRIDE
;
117 virtual void OnStatusCountersUpdated(
118 syncer::ModelType type
,
119 const StatusCounters
& counters
) OVERRIDE
;
122 std::map
<ModelType
, CommitCounters
> commit_counters_map_
;
123 std::map
<ModelType
, UpdateCounters
> update_counters_map_
;
124 std::map
<ModelType
, StatusCounters
> status_counters_map_
;
127 TypeDebugInfoCache::TypeDebugInfoCache() {}
129 TypeDebugInfoCache::~TypeDebugInfoCache() {}
131 CommitCounters
TypeDebugInfoCache::GetLatestCommitCounters(
132 ModelType type
) const {
133 std::map
<ModelType
, CommitCounters
>::const_iterator it
=
134 commit_counters_map_
.find(type
);
135 if (it
== commit_counters_map_
.end()) {
136 return CommitCounters();
142 UpdateCounters
TypeDebugInfoCache::GetLatestUpdateCounters(
143 ModelType type
) const {
144 std::map
<ModelType
, UpdateCounters
>::const_iterator it
=
145 update_counters_map_
.find(type
);
146 if (it
== update_counters_map_
.end()) {
147 return UpdateCounters();
153 StatusCounters
TypeDebugInfoCache::GetLatestStatusCounters(
154 ModelType type
) const {
155 std::map
<ModelType
, StatusCounters
>::const_iterator it
=
156 status_counters_map_
.find(type
);
157 if (it
== status_counters_map_
.end()) {
158 return StatusCounters();
164 void TypeDebugInfoCache::OnCommitCountersUpdated(
165 syncer::ModelType type
,
166 const CommitCounters
& counters
) {
167 commit_counters_map_
[type
] = counters
;
170 void TypeDebugInfoCache::OnUpdateCountersUpdated(
171 syncer::ModelType type
,
172 const UpdateCounters
& counters
) {
173 update_counters_map_
[type
] = counters
;
176 void TypeDebugInfoCache::OnStatusCountersUpdated(
177 syncer::ModelType type
,
178 const StatusCounters
& counters
) {
179 status_counters_map_
[type
] = counters
;
184 class SyncerTest
: public testing::Test
,
185 public SyncSession::Delegate
,
186 public SyncEngineEventListener
{
189 : extensions_activity_(new ExtensionsActivity
),
191 saw_syncer_event_(false),
192 last_client_invalidation_hint_buffer_size_(10) {
195 // SyncSession::Delegate implementation.
196 virtual void OnThrottled(const base::TimeDelta
& throttle_duration
) OVERRIDE
{
197 FAIL() << "Should not get silenced.";
199 virtual void OnTypesThrottled(
201 const base::TimeDelta
& throttle_duration
) OVERRIDE
{
202 FAIL() << "Should not get silenced.";
204 virtual bool IsCurrentlyThrottled() OVERRIDE
{
207 virtual void OnReceivedLongPollIntervalUpdate(
208 const base::TimeDelta
& new_interval
) OVERRIDE
{
209 last_long_poll_interval_received_
= new_interval
;
211 virtual void OnReceivedShortPollIntervalUpdate(
212 const base::TimeDelta
& new_interval
) OVERRIDE
{
213 last_short_poll_interval_received_
= new_interval
;
215 virtual void OnReceivedCustomNudgeDelays(
216 const std::map
<ModelType
, base::TimeDelta
>& delay_map
) OVERRIDE
{
217 std::map
<ModelType
, base::TimeDelta
>::const_iterator iter
=
218 delay_map
.find(SESSIONS
);
219 if (iter
!= delay_map
.end() && iter
->second
> base::TimeDelta())
220 last_sessions_commit_delay_
= iter
->second
;
221 iter
= delay_map
.find(BOOKMARKS
);
222 if (iter
!= delay_map
.end() && iter
->second
> base::TimeDelta())
223 last_bookmarks_commit_delay_
= iter
->second
;
225 virtual void OnReceivedClientInvalidationHintBufferSize(
227 last_client_invalidation_hint_buffer_size_
= size
;
229 virtual void OnReceivedGuRetryDelay(const base::TimeDelta
& delay
) OVERRIDE
{}
230 virtual void OnReceivedMigrationRequest(ModelTypeSet types
) OVERRIDE
{}
231 virtual void OnProtocolEvent(const ProtocolEvent
& event
) OVERRIDE
{}
232 virtual void OnSyncProtocolError(const SyncProtocolError
& error
) OVERRIDE
{}
234 void GetModelSafeRoutingInfo(ModelSafeRoutingInfo
* out
) {
235 // We're just testing the sync engine here, so we shunt everything to
236 // the SyncerThread. Datatypes which aren't enabled aren't in the map.
237 for (ModelTypeSet::Iterator it
= enabled_datatypes_
.First();
238 it
.Good(); it
.Inc()) {
239 (*out
)[it
.Get()] = GROUP_PASSIVE
;
243 virtual void OnSyncCycleEvent(const SyncCycleEvent
& event
) OVERRIDE
{
244 DVLOG(1) << "HandleSyncEngineEvent in unittest " << event
.what_happened
;
245 // we only test for entry-specific events, not status changed ones.
246 switch (event
.what_happened
) {
247 case SyncCycleEvent::SYNC_CYCLE_BEGIN
: // Fall through.
248 case SyncCycleEvent::STATUS_CHANGED
:
249 case SyncCycleEvent::SYNC_CYCLE_ENDED
:
252 CHECK(false) << "Handling unknown error type in unit tests!!";
254 saw_syncer_event_
= true;
257 virtual void OnActionableError(const SyncProtocolError
& error
) OVERRIDE
{}
258 virtual void OnRetryTimeChanged(base::Time retry_time
) OVERRIDE
{}
259 virtual void OnThrottledTypesChanged(ModelTypeSet throttled_types
) OVERRIDE
{}
260 virtual void OnMigrationRequested(ModelTypeSet types
) OVERRIDE
{}
262 void ResetSession() {
263 session_
.reset(SyncSession::Build(context_
.get(), this));
266 void SyncShareNudge() {
269 // Pretend we've seen a local change, to make the nudge_tracker look normal.
270 nudge_tracker_
.RecordLocalChange(ModelTypeSet(BOOKMARKS
));
273 syncer_
->NormalSyncShare(
274 context_
->GetEnabledTypes(),
279 void SyncShareConfigure() {
281 EXPECT_TRUE(syncer_
->ConfigureSyncShare(
282 context_
->GetEnabledTypes(),
283 sync_pb::GetUpdatesCallerInfo::RECONFIGURATION
,
287 virtual void SetUp() {
289 mock_server_
.reset(new MockConnectionManager(directory(),
290 &cancelation_signal_
));
291 debug_info_getter_
.reset(new MockDebugInfoGetter
);
292 EnableDatatype(BOOKMARKS
);
293 EnableDatatype(NIGORI
);
294 EnableDatatype(PREFERENCES
);
295 EnableDatatype(NIGORI
);
296 workers_
.push_back(scoped_refptr
<ModelSafeWorker
>(
297 new FakeModelWorker(GROUP_PASSIVE
)));
298 std::vector
<SyncEngineEventListener
*> listeners
;
299 listeners
.push_back(this);
301 ModelSafeRoutingInfo routing_info
;
302 GetModelSafeRoutingInfo(&routing_info
);
304 model_type_registry_
.reset(
305 new ModelTypeRegistry(workers_
, directory(), &mock_nudge_handler_
));
306 model_type_registry_
->RegisterDirectoryTypeDebugInfoObserver(
309 context_
.reset(new SyncSessionContext(
312 extensions_activity_
.get(),
314 debug_info_getter_
.get(),
315 model_type_registry_
.get(),
316 true, // enable keystore encryption
317 false, // force enable pre-commit GU avoidance experiment
318 "fake_invalidator_client_id"));
319 context_
->SetRoutingInfo(routing_info
);
320 syncer_
= new Syncer(&cancelation_signal_
);
322 syncable::ReadTransaction
trans(FROM_HERE
, directory());
323 syncable::Directory::Metahandles children
;
324 directory()->GetChildHandlesById(&trans
, trans
.root_id(), &children
);
325 ASSERT_EQ(0u, children
.size());
326 saw_syncer_event_
= false;
327 root_id_
= TestIdFactory::root();
328 parent_id_
= ids_
.MakeServer("parent id");
329 child_id_
= ids_
.MakeServer("child id");
330 directory()->set_store_birthday(mock_server_
->store_birthday());
331 mock_server_
->SetKeystoreKey("encryption_key");
334 virtual void TearDown() {
335 model_type_registry_
->UnregisterDirectoryTypeDebugInfoObserver(
337 mock_server_
.reset();
340 dir_maker_
.TearDown();
343 void WriteTestDataToEntry(WriteTransaction
* trans
, MutableEntry
* entry
) {
344 EXPECT_FALSE(entry
->GetIsDir());
345 EXPECT_FALSE(entry
->GetIsDel());
346 sync_pb::EntitySpecifics specifics
;
347 specifics
.mutable_bookmark()->set_url("http://demo/");
348 specifics
.mutable_bookmark()->set_favicon("PNG");
349 entry
->PutSpecifics(specifics
);
350 entry
->PutIsUnsynced(true);
352 void VerifyTestDataInEntry(BaseTransaction
* trans
, Entry
* entry
) {
353 EXPECT_FALSE(entry
->GetIsDir());
354 EXPECT_FALSE(entry
->GetIsDel());
355 VerifyTestBookmarkDataInEntry(entry
);
357 void VerifyTestBookmarkDataInEntry(Entry
* entry
) {
358 const sync_pb::EntitySpecifics
& specifics
= entry
->GetSpecifics();
359 EXPECT_TRUE(specifics
.has_bookmark());
360 EXPECT_EQ("PNG", specifics
.bookmark().favicon());
361 EXPECT_EQ("http://demo/", specifics
.bookmark().url());
364 void VerifyHierarchyConflictsReported(
365 const sync_pb::ClientToServerMessage
& message
) {
366 // Our request should have included a warning about hierarchy conflicts.
367 const sync_pb::ClientStatus
& client_status
= message
.client_status();
368 EXPECT_TRUE(client_status
.has_hierarchy_conflict_detected());
369 EXPECT_TRUE(client_status
.hierarchy_conflict_detected());
372 void VerifyNoHierarchyConflictsReported(
373 const sync_pb::ClientToServerMessage
& message
) {
374 // Our request should have reported no hierarchy conflicts detected.
375 const sync_pb::ClientStatus
& client_status
= message
.client_status();
376 EXPECT_TRUE(client_status
.has_hierarchy_conflict_detected());
377 EXPECT_FALSE(client_status
.hierarchy_conflict_detected());
380 void VerifyHierarchyConflictsUnspecified(
381 const sync_pb::ClientToServerMessage
& message
) {
382 // Our request should have neither confirmed nor denied hierarchy conflicts.
383 const sync_pb::ClientStatus
& client_status
= message
.client_status();
384 EXPECT_FALSE(client_status
.has_hierarchy_conflict_detected());
387 sync_pb::EntitySpecifics
DefaultBookmarkSpecifics() {
388 sync_pb::EntitySpecifics result
;
389 AddDefaultFieldValue(BOOKMARKS
, &result
);
393 sync_pb::EntitySpecifics
DefaultPreferencesSpecifics() {
394 sync_pb::EntitySpecifics result
;
395 AddDefaultFieldValue(PREFERENCES
, &result
);
398 // Enumeration of alterations to entries for commit ordering tests.
400 LIST_END
= 0, // Denotes the end of the list of features from below.
401 SYNCED
, // Items are unsynced by default
407 struct CommitOrderingTest
{
408 // expected commit index.
410 // Details about the item
412 syncable::Id parent_id
;
413 EntryFeature features
[10];
415 static CommitOrderingTest
MakeLastCommitItem() {
416 CommitOrderingTest last_commit_item
;
417 last_commit_item
.commit_index
= -1;
418 last_commit_item
.id
= TestIdFactory::root();
419 return last_commit_item
;
423 void RunCommitOrderingTest(CommitOrderingTest
* test
) {
424 map
<int, syncable::Id
> expected_positions
;
425 { // Transaction scope.
426 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
427 while (!test
->id
.IsRoot()) {
428 if (test
->commit_index
>= 0) {
429 map
<int, syncable::Id
>::value_type
entry(test
->commit_index
,
431 bool double_position
= !expected_positions
.insert(entry
).second
;
432 ASSERT_FALSE(double_position
) << "Two id's expected at one position";
434 string utf8_name
= test
->id
.GetServerId();
435 string
name(utf8_name
.begin(), utf8_name
.end());
436 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, test
->parent_id
, name
);
438 entry
.PutId(test
->id
);
439 if (test
->id
.ServerKnows()) {
440 entry
.PutBaseVersion(5);
441 entry
.PutServerVersion(5);
442 entry
.PutServerParentId(test
->parent_id
);
444 entry
.PutIsDir(true);
445 entry
.PutIsUnsynced(true);
446 entry
.PutSpecifics(DefaultBookmarkSpecifics());
447 // Set the time to 30 seconds in the future to reduce the chance of
449 const base::Time
& now_plus_30s
=
450 base::Time::Now() + base::TimeDelta::FromSeconds(30);
451 const base::Time
& now_minus_2h
=
452 base::Time::Now() - base::TimeDelta::FromHours(2);
453 entry
.PutMtime(now_plus_30s
);
454 for (size_t i
= 0 ; i
< arraysize(test
->features
) ; ++i
) {
455 switch (test
->features
[i
]) {
459 entry
.PutIsUnsynced(false);
462 entry
.PutIsDel(true);
465 entry
.PutMtime(now_minus_2h
);
467 case MOVED_FROM_ROOT
:
468 entry
.PutServerParentId(trans
.root_id());
471 FAIL() << "Bad value in CommitOrderingTest list";
478 ASSERT_TRUE(expected_positions
.size() ==
479 mock_server_
->committed_ids().size());
480 // If this test starts failing, be aware other sort orders could be valid.
481 for (size_t i
= 0; i
< expected_positions
.size(); ++i
) {
483 EXPECT_EQ(1u, expected_positions
.count(i
));
484 EXPECT_EQ(expected_positions
[i
], mock_server_
->committed_ids()[i
]);
488 CommitCounters
GetCommitCounters(ModelType type
) {
489 return debug_info_cache_
.GetLatestCommitCounters(type
);
492 UpdateCounters
GetUpdateCounters(ModelType type
) {
493 return debug_info_cache_
.GetLatestUpdateCounters(type
);
496 StatusCounters
GetStatusCounters(ModelType type
) {
497 return debug_info_cache_
.GetLatestStatusCounters(type
);
500 Directory
* directory() {
501 return dir_maker_
.directory();
504 const std::string
local_cache_guid() {
505 return directory()->cache_guid();
508 const std::string
foreign_cache_guid() {
509 return "kqyg7097kro6GSUod+GSg==";
512 int64
CreateUnsyncedDirectory(const string
& entry_name
,
513 const string
& idstring
) {
514 return CreateUnsyncedDirectory(entry_name
,
515 syncable::Id::CreateFromServerId(idstring
));
518 int64
CreateUnsyncedDirectory(const string
& entry_name
,
519 const syncable::Id
& id
) {
520 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
522 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), entry_name
);
523 EXPECT_TRUE(entry
.good());
524 entry
.PutIsUnsynced(true);
525 entry
.PutIsDir(true);
526 entry
.PutSpecifics(DefaultBookmarkSpecifics());
527 entry
.PutBaseVersion(id
.ServerKnows() ? 1 : 0);
529 return entry
.GetMetahandle();
532 void EnableDatatype(ModelType model_type
) {
533 enabled_datatypes_
.Put(model_type
);
535 ModelSafeRoutingInfo routing_info
;
536 GetModelSafeRoutingInfo(&routing_info
);
539 context_
->SetRoutingInfo(routing_info
);
542 mock_server_
->ExpectGetUpdatesRequestTypes(enabled_datatypes_
);
545 void DisableDatatype(ModelType model_type
) {
546 enabled_datatypes_
.Remove(model_type
);
548 ModelSafeRoutingInfo routing_info
;
549 GetModelSafeRoutingInfo(&routing_info
);
552 context_
->SetRoutingInfo(routing_info
);
555 mock_server_
->ExpectGetUpdatesRequestTypes(enabled_datatypes_
);
558 Cryptographer
* GetCryptographer(syncable::BaseTransaction
* trans
) {
559 return directory()->GetCryptographer(trans
);
562 // Configures SyncSessionContext and NudgeTracker so Syncer won't call
563 // GetUpdates prior to Commit. This method can be used to ensure a Commit is
564 // not preceeded by GetUpdates.
565 void ConfigureNoGetUpdatesRequired() {
566 context_
->set_server_enabled_pre_commit_update_avoidance(true);
567 nudge_tracker_
.OnInvalidationsEnabled();
568 nudge_tracker_
.RecordSuccessfulSyncCycle();
570 ASSERT_FALSE(context_
->ShouldFetchUpdatesBeforeCommit());
571 ASSERT_FALSE(nudge_tracker_
.IsGetUpdatesRequired());
574 base::MessageLoop message_loop_
;
576 // Some ids to aid tests. Only the root one's value is specific. The rest
577 // are named for test clarity.
578 // TODO(chron): Get rid of these inbuilt IDs. They only make it
580 syncable::Id root_id_
;
581 syncable::Id parent_id_
;
582 syncable::Id child_id_
;
586 TestDirectorySetterUpper dir_maker_
;
587 FakeEncryptor encryptor_
;
588 scoped_refptr
<ExtensionsActivity
> extensions_activity_
;
589 scoped_ptr
<MockConnectionManager
> mock_server_
;
590 CancelationSignal cancelation_signal_
;
594 scoped_ptr
<SyncSession
> session_
;
595 TypeDebugInfoCache debug_info_cache_
;
596 MockNudgeHandler mock_nudge_handler_
;
597 scoped_ptr
<ModelTypeRegistry
> model_type_registry_
;
598 scoped_ptr
<SyncSessionContext
> context_
;
599 bool saw_syncer_event_
;
600 base::TimeDelta last_short_poll_interval_received_
;
601 base::TimeDelta last_long_poll_interval_received_
;
602 base::TimeDelta last_sessions_commit_delay_
;
603 base::TimeDelta last_bookmarks_commit_delay_
;
604 int last_client_invalidation_hint_buffer_size_
;
605 std::vector
<scoped_refptr
<ModelSafeWorker
> > workers_
;
607 ModelTypeSet enabled_datatypes_
;
608 sessions::NudgeTracker nudge_tracker_
;
609 scoped_ptr
<MockDebugInfoGetter
> debug_info_getter_
;
611 DISALLOW_COPY_AND_ASSIGN(SyncerTest
);
614 TEST_F(SyncerTest
, TestCallGatherUnsyncedEntries
) {
616 Syncer::UnsyncedMetaHandles handles
;
618 syncable::ReadTransaction
trans(FROM_HERE
, directory());
619 GetUnsyncedEntries(&trans
, &handles
);
621 ASSERT_EQ(0u, handles
.size());
623 // TODO(sync): When we can dynamically connect and disconnect the mock
624 // ServerConnectionManager test disconnected GetUnsyncedEntries here. It's a
625 // regression for a very old bug.
628 TEST_F(SyncerTest
, GetCommitIdsFiltersThrottledEntries
) {
629 const ModelTypeSet
throttled_types(BOOKMARKS
);
630 sync_pb::EntitySpecifics bookmark_data
;
631 AddDefaultFieldValue(BOOKMARKS
, &bookmark_data
);
633 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
634 foreign_cache_guid(), "-1");
638 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
639 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
640 ASSERT_TRUE(A
.good());
641 A
.PutIsUnsynced(true);
642 A
.PutSpecifics(bookmark_data
);
643 A
.PutNonUniqueName("bookmark");
646 // Now sync without enabling bookmarks.
647 mock_server_
->ExpectGetUpdatesRequestTypes(
648 Difference(context_
->GetEnabledTypes(), ModelTypeSet(BOOKMARKS
)));
650 syncer_
->NormalSyncShare(
651 Difference(context_
->GetEnabledTypes(), ModelTypeSet(BOOKMARKS
)),
656 // Nothing should have been committed as bookmarks is throttled.
657 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
658 Entry
entryA(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
659 ASSERT_TRUE(entryA
.good());
660 EXPECT_TRUE(entryA
.GetIsUnsynced());
663 // Sync again with bookmarks enabled.
664 mock_server_
->ExpectGetUpdatesRequestTypes(context_
->GetEnabledTypes());
667 // It should have been committed.
668 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
669 Entry
entryA(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
670 ASSERT_TRUE(entryA
.good());
671 EXPECT_FALSE(entryA
.GetIsUnsynced());
675 // We use a macro so we can preserve the error location.
676 #define VERIFY_ENTRY(id, is_unapplied, is_unsynced, prev_initialized, \
677 parent_id, version, server_version, id_fac, rtrans) \
679 Entry entryA(rtrans, syncable::GET_BY_ID, id_fac.FromNumber(id)); \
680 ASSERT_TRUE(entryA.good()); \
681 /* We don't use EXPECT_EQ here because when the left side param is false,
682 gcc 4.6 warns about converting 'false' to pointer type for argument 1. */ \
683 EXPECT_TRUE(is_unsynced == entryA.GetIsUnsynced()); \
684 EXPECT_TRUE(is_unapplied == entryA.GetIsUnappliedUpdate()); \
685 EXPECT_TRUE(prev_initialized == \
686 IsRealDataType(GetModelTypeFromSpecifics( \
687 entryA.GetBaseServerSpecifics()))); \
688 EXPECT_TRUE(parent_id == -1 || \
689 entryA.GetParentId()== id_fac.FromNumber(parent_id)); \
690 EXPECT_EQ(version, entryA.GetBaseVersion()); \
691 EXPECT_EQ(server_version, entryA.GetServerVersion()); \
694 TEST_F(SyncerTest
, GetCommitIdsFiltersUnreadyEntries
) {
695 KeyParams key_params
= {"localhost", "dummy", "foobar"};
696 KeyParams other_params
= {"localhost", "dummy", "foobar2"};
697 sync_pb::EntitySpecifics bookmark
, encrypted_bookmark
;
698 bookmark
.mutable_bookmark()->set_url("url");
699 bookmark
.mutable_bookmark()->set_title("title");
700 AddDefaultFieldValue(BOOKMARKS
, &encrypted_bookmark
);
701 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
702 foreign_cache_guid(), "-1");
703 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
704 foreign_cache_guid(), "-2");
705 mock_server_
->AddUpdateDirectory(3, 0, "C", 10, 10,
706 foreign_cache_guid(), "-3");
707 mock_server_
->AddUpdateDirectory(4, 0, "D", 10, 10,
708 foreign_cache_guid(), "-4");
710 // Server side change will put A in conflict.
711 mock_server_
->AddUpdateDirectory(1, 0, "A", 20, 20,
712 foreign_cache_guid(), "-1");
714 // Mark bookmarks as encrypted and set the cryptographer to have pending
716 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
717 Cryptographer
other_cryptographer(&encryptor_
);
718 other_cryptographer
.AddKey(other_params
);
719 sync_pb::EntitySpecifics specifics
;
720 sync_pb::NigoriSpecifics
* nigori
= specifics
.mutable_nigori();
721 other_cryptographer
.GetKeys(nigori
->mutable_encryption_keybag());
722 dir_maker_
.encryption_handler()->EnableEncryptEverything();
723 // Set up with an old passphrase, but have pending keys
724 GetCryptographer(&wtrans
)->AddKey(key_params
);
725 GetCryptographer(&wtrans
)->Encrypt(bookmark
,
726 encrypted_bookmark
.mutable_encrypted());
727 GetCryptographer(&wtrans
)->SetPendingKeys(nigori
->encryption_keybag());
729 // In conflict but properly encrypted.
730 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
731 ASSERT_TRUE(A
.good());
732 A
.PutIsUnsynced(true);
733 A
.PutSpecifics(encrypted_bookmark
);
734 A
.PutNonUniqueName(kEncryptedString
);
735 // Not in conflict and properly encrypted.
736 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
737 ASSERT_TRUE(B
.good());
738 B
.PutIsUnsynced(true);
739 B
.PutSpecifics(encrypted_bookmark
);
740 B
.PutNonUniqueName(kEncryptedString
);
741 // Unencrypted specifics.
742 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
743 ASSERT_TRUE(C
.good());
744 C
.PutIsUnsynced(true);
745 C
.PutNonUniqueName(kEncryptedString
);
746 // Unencrypted non_unique_name.
747 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
748 ASSERT_TRUE(D
.good());
749 D
.PutIsUnsynced(true);
750 D
.PutSpecifics(encrypted_bookmark
);
751 D
.PutNonUniqueName("not encrypted");
755 // Nothing should have commited due to bookmarks being encrypted and
756 // the cryptographer having pending keys. A would have been resolved
757 // as a simple conflict, but still be unsynced until the next sync cycle.
758 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
759 VERIFY_ENTRY(1, false, true, false, 0, 20, 20, ids_
, &rtrans
);
760 VERIFY_ENTRY(2, false, true, false, 0, 10, 10, ids_
, &rtrans
);
761 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_
, &rtrans
);
762 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_
, &rtrans
);
764 // Resolve the pending keys.
765 GetCryptographer(&rtrans
)->DecryptPendingKeys(other_params
);
769 // All properly encrypted and non-conflicting items should commit. "A" was
770 // conflicting, but last sync cycle resolved it as simple conflict, so on
771 // this sync cycle it committed succesfullly.
772 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
773 // Committed successfully.
774 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_
, &rtrans
);
775 // Committed successfully.
776 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_
, &rtrans
);
777 // Was not properly encrypted.
778 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_
, &rtrans
);
779 // Was not properly encrypted.
780 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_
, &rtrans
);
783 // Fix the remaining items.
784 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
785 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
786 ASSERT_TRUE(C
.good());
787 C
.PutSpecifics(encrypted_bookmark
);
788 C
.PutNonUniqueName(kEncryptedString
);
789 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
790 ASSERT_TRUE(D
.good());
791 D
.PutSpecifics(encrypted_bookmark
);
792 D
.PutNonUniqueName(kEncryptedString
);
796 const StatusController
& status_controller
= session_
->status_controller();
798 EXPECT_EQ(status_controller
.model_neutral_state().commit_result
, SYNCER_OK
);
799 // None should be unsynced anymore.
800 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
801 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_
, &rtrans
);
802 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_
, &rtrans
);
803 VERIFY_ENTRY(3, false, false, false, 0, 11, 11, ids_
, &rtrans
);
804 VERIFY_ENTRY(4, false, false, false, 0, 11, 11, ids_
, &rtrans
);
808 // This test uses internal knowledge of the directory to test correctness of
809 // GetCommitIds. In almost every other test, the hierarchy is created from
810 // parent to child order, and so parents always have metahandles that are
811 // smaller than those of their children. This makes it very difficult to test
812 // some GetCommitIds edge cases, since it uses metahandle ordering as
814 TEST_F(SyncerTest
, GetCommitIds_VerifyDeletionCommitOrder
) {
816 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
818 // Create four bookmarks folders at the root node.
819 for (int i
= 1; i
< 5; ++i
) {
820 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "");
821 entry
.PutId(ids_
.FromNumber(i
));
822 entry
.PutIsDir(true);
823 entry
.PutBaseVersion(5);
824 entry
.PutServerVersion(5);
825 entry
.PutServerParentId(trans
.root_id());
826 entry
.PutServerIsDir(true);
827 entry
.PutIsUnsynced(true);
828 entry
.PutSpecifics(DefaultBookmarkSpecifics());
831 // Now iterate in reverse order make a hierarchy of them.
832 // While we're at it, also mark them as deleted.
833 syncable::Id parent_id
= trans
.root_id();
834 for (int i
= 4; i
> 0; --i
) {
835 MutableEntry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(i
));
836 entry
.PutParentId(parent_id
);
837 entry
.PutServerParentId(parent_id
);
838 entry
.PutIsDel(true);
839 parent_id
= ids_
.FromNumber(i
);
844 // Run GetCommitIds, the function being tested.
845 syncable::Directory::Metahandles result_handles
;
846 syncable::ReadTransaction
trans(FROM_HERE
, directory());
847 GetCommitIdsForType(&trans
, BOOKMARKS
, 100, &result_handles
);
849 // Now verify the output. We expect four results in child to parent order.
850 ASSERT_EQ(4U, result_handles
.size());
852 Entry
entry0(&trans
, GET_BY_HANDLE
, result_handles
[0]);
853 EXPECT_EQ(ids_
.FromNumber(1), entry0
.GetId());
855 Entry
entry1(&trans
, GET_BY_HANDLE
, result_handles
[1]);
856 EXPECT_EQ(ids_
.FromNumber(2), entry1
.GetId());
858 Entry
entry2(&trans
, GET_BY_HANDLE
, result_handles
[2]);
859 EXPECT_EQ(ids_
.FromNumber(3), entry2
.GetId());
861 Entry
entry3(&trans
, GET_BY_HANDLE
, result_handles
[3]);
862 EXPECT_EQ(ids_
.FromNumber(4), entry3
.GetId());
866 TEST_F(SyncerTest
, EncryptionAwareConflicts
) {
867 KeyParams key_params
= {"localhost", "dummy", "foobar"};
868 Cryptographer
other_cryptographer(&encryptor_
);
869 other_cryptographer
.AddKey(key_params
);
870 sync_pb::EntitySpecifics bookmark
, encrypted_bookmark
, modified_bookmark
;
871 bookmark
.mutable_bookmark()->set_title("title");
872 other_cryptographer
.Encrypt(bookmark
,
873 encrypted_bookmark
.mutable_encrypted());
874 AddDefaultFieldValue(BOOKMARKS
, &encrypted_bookmark
);
875 modified_bookmark
.mutable_bookmark()->set_title("title2");
876 other_cryptographer
.Encrypt(modified_bookmark
,
877 modified_bookmark
.mutable_encrypted());
878 sync_pb::EntitySpecifics pref
, encrypted_pref
, modified_pref
;
879 pref
.mutable_preference()->set_name("name");
880 AddDefaultFieldValue(PREFERENCES
, &encrypted_pref
);
881 other_cryptographer
.Encrypt(pref
,
882 encrypted_pref
.mutable_encrypted());
883 modified_pref
.mutable_preference()->set_name("name2");
884 other_cryptographer
.Encrypt(modified_pref
,
885 modified_pref
.mutable_encrypted());
887 // Mark bookmarks and preferences as encrypted and set the cryptographer to
888 // have pending keys.
889 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
890 sync_pb::EntitySpecifics specifics
;
891 sync_pb::NigoriSpecifics
* nigori
= specifics
.mutable_nigori();
892 other_cryptographer
.GetKeys(nigori
->mutable_encryption_keybag());
893 dir_maker_
.encryption_handler()->EnableEncryptEverything();
894 GetCryptographer(&wtrans
)->SetPendingKeys(nigori
->encryption_keybag());
895 EXPECT_TRUE(GetCryptographer(&wtrans
)->has_pending_keys());
898 // We need to remember the exact position of our local items, so we can
899 // make updates that do not modify those positions.
904 mock_server_
->AddUpdateSpecifics(1, 0, "A", 10, 10, true, 0, bookmark
,
905 foreign_cache_guid(), "-1");
906 mock_server_
->AddUpdateSpecifics(2, 1, "B", 10, 10, false, 2, bookmark
,
907 foreign_cache_guid(), "-2");
908 mock_server_
->AddUpdateSpecifics(3, 1, "C", 10, 10, false, 1, bookmark
,
909 foreign_cache_guid(), "-3");
910 mock_server_
->AddUpdateSpecifics(4, 0, "D", 10, 10, false, 0, pref
);
913 // Initial state. Everything is normal.
914 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
915 VERIFY_ENTRY(1, false, false, false, 0, 10, 10, ids_
, &rtrans
);
916 VERIFY_ENTRY(2, false, false, false, 1, 10, 10, ids_
, &rtrans
);
917 VERIFY_ENTRY(3, false, false, false, 1, 10, 10, ids_
, &rtrans
);
918 VERIFY_ENTRY(4, false, false, false, 0, 10, 10, ids_
, &rtrans
);
920 Entry
entry1(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
921 ASSERT_TRUE(entry1
.GetUniquePosition().Equals(
922 entry1
.GetServerUniquePosition()));
923 pos1
= entry1
.GetUniquePosition();
924 Entry
entry2(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(2));
925 pos2
= entry2
.GetUniquePosition();
926 Entry
entry3(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(3));
927 pos3
= entry3
.GetUniquePosition();
930 // Server side encryption will not be applied due to undecryptable data.
931 // At this point, BASE_SERVER_SPECIFICS should be filled for all four items.
932 mock_server_
->AddUpdateSpecifics(1, 0, kEncryptedString
, 20, 20, true, 0,
934 foreign_cache_guid(), "-1");
935 mock_server_
->AddUpdateSpecifics(2, 1, kEncryptedString
, 20, 20, false, 2,
937 foreign_cache_guid(), "-2");
938 mock_server_
->AddUpdateSpecifics(3, 1, kEncryptedString
, 20, 20, false, 1,
940 foreign_cache_guid(), "-3");
941 mock_server_
->AddUpdateSpecifics(4, 0, kEncryptedString
, 20, 20, false, 0,
943 foreign_cache_guid(), "-4");
946 // All should be unapplied due to being undecryptable and have a valid
947 // BASE_SERVER_SPECIFICS.
948 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
949 VERIFY_ENTRY(1, true, false, true, 0, 10, 20, ids_
, &rtrans
);
950 VERIFY_ENTRY(2, true, false, true, 1, 10, 20, ids_
, &rtrans
);
951 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_
, &rtrans
);
952 VERIFY_ENTRY(4, true, false, true, 0, 10, 20, ids_
, &rtrans
);
955 // Server side change that don't modify anything should not affect
956 // BASE_SERVER_SPECIFICS (such as name changes and mtime changes).
957 mock_server_
->AddUpdateSpecifics(1, 0, kEncryptedString
, 30, 30, true, 0,
959 foreign_cache_guid(), "-1");
960 mock_server_
->AddUpdateSpecifics(2, 1, kEncryptedString
, 30, 30, false, 2,
962 foreign_cache_guid(), "-2");
963 // Item 3 doesn't change.
964 mock_server_
->AddUpdateSpecifics(4, 0, kEncryptedString
, 30, 30, false, 0,
966 foreign_cache_guid(), "-4");
969 // Items 1, 2, and 4 should have newer server versions, 3 remains the same.
970 // All should remain unapplied due to be undecryptable.
971 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
972 VERIFY_ENTRY(1, true, false, true, 0, 10, 30, ids_
, &rtrans
);
973 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_
, &rtrans
);
974 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_
, &rtrans
);
975 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_
, &rtrans
);
978 // Positional changes, parent changes, and specifics changes should reset
979 // BASE_SERVER_SPECIFICS.
980 // Became unencrypted.
981 mock_server_
->AddUpdateSpecifics(1, 0, "A", 40, 40, true, 0, bookmark
,
982 foreign_cache_guid(), "-1");
983 // Reordered to after item 2.
984 mock_server_
->AddUpdateSpecifics(3, 1, kEncryptedString
, 30, 30, false, 3,
986 foreign_cache_guid(), "-3");
989 // Items 2 and 4 should be the only ones with BASE_SERVER_SPECIFICS set.
990 // Items 1 is now unencrypted, so should have applied normally.
991 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
992 VERIFY_ENTRY(1, false, false, false, 0, 40, 40, ids_
, &rtrans
);
993 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_
, &rtrans
);
994 VERIFY_ENTRY(3, true, false, false, 1, 10, 30, ids_
, &rtrans
);
995 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_
, &rtrans
);
998 // Make local changes, which should remain unsynced for items 2, 3, 4.
1000 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1001 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
1002 ASSERT_TRUE(A
.good());
1003 A
.PutSpecifics(modified_bookmark
);
1004 A
.PutNonUniqueName(kEncryptedString
);
1005 A
.PutIsUnsynced(true);
1006 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
1007 ASSERT_TRUE(B
.good());
1008 B
.PutSpecifics(modified_bookmark
);
1009 B
.PutNonUniqueName(kEncryptedString
);
1010 B
.PutIsUnsynced(true);
1011 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
1012 ASSERT_TRUE(C
.good());
1013 C
.PutSpecifics(modified_bookmark
);
1014 C
.PutNonUniqueName(kEncryptedString
);
1015 C
.PutIsUnsynced(true);
1016 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
1017 ASSERT_TRUE(D
.good());
1018 D
.PutSpecifics(modified_pref
);
1019 D
.PutNonUniqueName(kEncryptedString
);
1020 D
.PutIsUnsynced(true);
1024 // Item 1 remains unsynced due to there being pending keys.
1025 // Items 2, 3, 4 should remain unsynced since they were not up to date.
1026 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1027 VERIFY_ENTRY(1, false, true, false, 0, 40, 40, ids_
, &rtrans
);
1028 VERIFY_ENTRY(2, true, true, true, 1, 10, 30, ids_
, &rtrans
);
1029 VERIFY_ENTRY(3, true, true, false, 1, 10, 30, ids_
, &rtrans
);
1030 VERIFY_ENTRY(4, true, true, true, 0, 10, 30, ids_
, &rtrans
);
1034 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1035 // Resolve the pending keys.
1036 GetCryptographer(&rtrans
)->DecryptPendingKeys(key_params
);
1038 // First cycle resolves conflicts, second cycle commits changes.
1040 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_server_overwrites
);
1041 EXPECT_EQ(1, GetUpdateCounters(PREFERENCES
).num_server_overwrites
);
1042 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_local_overwrites
);
1044 // We successfully commited item(s).
1045 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS
).num_commits_attempted
);
1046 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS
).num_commits_success
);
1047 EXPECT_EQ(1, GetCommitCounters(PREFERENCES
).num_commits_attempted
);
1048 EXPECT_EQ(1, GetCommitCounters(PREFERENCES
).num_commits_success
);
1052 // Everything should be resolved now. The local changes should have
1053 // overwritten the server changes for 2 and 4, while the server changes
1054 // overwrote the local for entry 3.
1056 // Expect there will be no new overwrites.
1057 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_server_overwrites
);
1058 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_local_overwrites
);
1060 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS
).num_commits_success
);
1061 EXPECT_EQ(1, GetCommitCounters(PREFERENCES
).num_commits_success
);
1063 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1064 VERIFY_ENTRY(1, false, false, false, 0, 41, 41, ids_
, &rtrans
);
1065 VERIFY_ENTRY(2, false, false, false, 1, 31, 31, ids_
, &rtrans
);
1066 VERIFY_ENTRY(3, false, false, false, 1, 30, 30, ids_
, &rtrans
);
1067 VERIFY_ENTRY(4, false, false, false, 0, 31, 31, ids_
, &rtrans
);
1072 TEST_F(SyncerTest
, TestGetUnsyncedAndSimpleCommit
) {
1074 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1075 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1076 ASSERT_TRUE(parent
.good());
1077 parent
.PutIsUnsynced(true);
1078 parent
.PutIsDir(true);
1079 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1080 parent
.PutBaseVersion(1);
1081 parent
.PutId(parent_id_
);
1082 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete");
1083 ASSERT_TRUE(child
.good());
1084 child
.PutId(child_id_
);
1085 child
.PutBaseVersion(1);
1086 WriteTestDataToEntry(&wtrans
, &child
);
1090 ASSERT_EQ(2u, mock_server_
->committed_ids().size());
1091 // If this test starts failing, be aware other sort orders could be valid.
1092 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1093 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1095 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1096 Entry
entry(&rt
, syncable::GET_BY_ID
, child_id_
);
1097 ASSERT_TRUE(entry
.good());
1098 VerifyTestDataInEntry(&rt
, &entry
);
1102 TEST_F(SyncerTest
, TestPurgeWhileUnsynced
) {
1103 // Similar to above, but throw a purge operation into the mix. Bug 49278.
1104 syncable::Id pref_node_id
= TestIdFactory::MakeServer("Tim");
1106 directory()->SetDownloadProgress(BOOKMARKS
,
1107 syncable::BuildProgress(BOOKMARKS
));
1108 directory()->SetDownloadProgress(PREFERENCES
,
1109 syncable::BuildProgress(PREFERENCES
));
1110 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1111 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1112 ASSERT_TRUE(parent
.good());
1113 parent
.PutIsUnsynced(true);
1114 parent
.PutIsDir(true);
1115 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1116 parent
.PutBaseVersion(1);
1117 parent
.PutId(parent_id_
);
1118 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete");
1119 ASSERT_TRUE(child
.good());
1120 child
.PutId(child_id_
);
1121 child
.PutBaseVersion(1);
1122 WriteTestDataToEntry(&wtrans
, &child
);
1124 MutableEntry
parent2(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Tim");
1125 ASSERT_TRUE(parent2
.good());
1126 parent2
.PutIsUnsynced(true);
1127 parent2
.PutIsDir(true);
1128 parent2
.PutSpecifics(DefaultPreferencesSpecifics());
1129 parent2
.PutBaseVersion(1);
1130 parent2
.PutId(pref_node_id
);
1133 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES
),
1138 ASSERT_EQ(2U, mock_server_
->committed_ids().size());
1139 // If this test starts failing, be aware other sort orders could be valid.
1140 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1141 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1143 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1144 Entry
entry(&rt
, syncable::GET_BY_ID
, child_id_
);
1145 ASSERT_TRUE(entry
.good());
1146 VerifyTestDataInEntry(&rt
, &entry
);
1148 directory()->SaveChanges();
1150 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1151 Entry
entry(&rt
, syncable::GET_BY_ID
, pref_node_id
);
1152 ASSERT_FALSE(entry
.good());
1156 TEST_F(SyncerTest
, TestPurgeWhileUnapplied
) {
1157 // Similar to above, but for unapplied items. Bug 49278.
1159 directory()->SetDownloadProgress(BOOKMARKS
,
1160 syncable::BuildProgress(BOOKMARKS
));
1161 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1162 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1163 ASSERT_TRUE(parent
.good());
1164 parent
.PutIsUnappliedUpdate(true);
1165 parent
.PutIsDir(true);
1166 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1167 parent
.PutBaseVersion(1);
1168 parent
.PutId(parent_id_
);
1171 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(BOOKMARKS
),
1176 directory()->SaveChanges();
1178 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1179 Entry
entry(&rt
, syncable::GET_BY_ID
, parent_id_
);
1180 ASSERT_FALSE(entry
.good());
1184 TEST_F(SyncerTest
, TestPurgeWithJournal
) {
1186 directory()->SetDownloadProgress(BOOKMARKS
,
1187 syncable::BuildProgress(BOOKMARKS
));
1188 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1189 MutableEntry
parent(&wtrans
, syncable::CREATE
, BOOKMARKS
, wtrans
.root_id(),
1191 ASSERT_TRUE(parent
.good());
1192 parent
.PutIsDir(true);
1193 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1194 parent
.PutBaseVersion(1);
1195 parent
.PutId(parent_id_
);
1196 MutableEntry
child(&wtrans
, syncable::CREATE
, BOOKMARKS
, parent_id_
,
1198 ASSERT_TRUE(child
.good());
1199 child
.PutId(child_id_
);
1200 child
.PutBaseVersion(1);
1201 WriteTestDataToEntry(&wtrans
, &child
);
1203 MutableEntry
parent2(&wtrans
, syncable::CREATE
, PREFERENCES
,
1204 wtrans
.root_id(), "Tim");
1205 ASSERT_TRUE(parent2
.good());
1206 parent2
.PutIsDir(true);
1207 parent2
.PutSpecifics(DefaultPreferencesSpecifics());
1208 parent2
.PutBaseVersion(1);
1209 parent2
.PutId(TestIdFactory::MakeServer("Tim"));
1212 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES
, BOOKMARKS
),
1213 ModelTypeSet(BOOKMARKS
),
1216 // Verify bookmark nodes are saved in delete journal but not preference
1218 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1219 syncable::DeleteJournal
* delete_journal
= directory()->delete_journal();
1220 EXPECT_EQ(2u, delete_journal
->GetDeleteJournalSize(&rt
));
1221 syncable::EntryKernelSet journal_entries
;
1222 directory()->delete_journal()->GetDeleteJournals(&rt
, BOOKMARKS
,
1224 EXPECT_EQ(parent_id_
, (*journal_entries
.begin())->ref(syncable::ID
));
1225 EXPECT_EQ(child_id_
, (*journal_entries
.rbegin())->ref(syncable::ID
));
1229 TEST_F(SyncerTest
, ResetVersions
) {
1230 // Download the top level pref node and some pref items.
1231 mock_server_
->AddUpdateDirectory(
1232 parent_id_
, root_id_
, "prefs", 1, 10, std::string(), std::string());
1233 mock_server_
->SetLastUpdateServerTag(ModelTypeToRootTag(PREFERENCES
));
1234 mock_server_
->AddUpdatePref("id1", parent_id_
.GetServerId(), "tag1", 20, 20);
1235 mock_server_
->AddUpdatePref("id2", parent_id_
.GetServerId(), "tag2", 30, 30);
1236 mock_server_
->AddUpdatePref("id3", parent_id_
.GetServerId(), "tag3", 40, 40);
1240 // Modify one of the preferences locally, mark another one as unapplied,
1241 // and create another unsynced preference.
1242 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1243 MutableEntry
entry(&wtrans
, GET_BY_CLIENT_TAG
, "tag1");
1244 entry
.PutIsUnsynced(true);
1246 MutableEntry
entry2(&wtrans
, GET_BY_CLIENT_TAG
, "tag2");
1247 entry2
.PutIsUnappliedUpdate(true);
1249 MutableEntry
entry4(&wtrans
, CREATE
, PREFERENCES
, parent_id_
, "name");
1250 entry4
.PutUniqueClientTag("tag4");
1251 entry4
.PutIsUnsynced(true);
1255 // Reset the versions.
1256 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1257 ASSERT_TRUE(directory()->ResetVersionsForType(&wtrans
, PREFERENCES
));
1261 // Verify the synced items are all with version 1 now, with
1262 // unsynced/unapplied state preserved.
1263 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1264 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, "tag1");
1265 EXPECT_EQ(1, entry
.GetBaseVersion());
1266 EXPECT_EQ(1, entry
.GetServerVersion());
1267 EXPECT_TRUE(entry
.GetIsUnsynced());
1268 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
1269 Entry
entry2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
1270 EXPECT_EQ(1, entry2
.GetBaseVersion());
1271 EXPECT_EQ(1, entry2
.GetServerVersion());
1272 EXPECT_FALSE(entry2
.GetIsUnsynced());
1273 EXPECT_TRUE(entry2
.GetIsUnappliedUpdate());
1274 Entry
entry3(&trans
, GET_BY_CLIENT_TAG
, "tag3");
1275 EXPECT_EQ(1, entry3
.GetBaseVersion());
1276 EXPECT_EQ(1, entry3
.GetServerVersion());
1277 EXPECT_FALSE(entry3
.GetIsUnsynced());
1278 EXPECT_FALSE(entry3
.GetIsUnappliedUpdate());
1280 // Entry 4 (the locally created one) should remain the same.
1281 Entry
entry4(&trans
, GET_BY_CLIENT_TAG
, "tag4");
1282 EXPECT_EQ(-1, entry4
.GetBaseVersion());
1283 EXPECT_EQ(0, entry4
.GetServerVersion());
1284 EXPECT_TRUE(entry4
.GetIsUnsynced());
1285 EXPECT_FALSE(entry4
.GetIsUnappliedUpdate());
1289 TEST_F(SyncerTest
, TestCommitListOrderingTwoItemsTall
) {
1290 CommitOrderingTest items
[] = {
1291 {1, ids_
.FromNumber(-1001), ids_
.FromNumber(-1000)},
1292 {0, ids_
.FromNumber(-1000), ids_
.FromNumber(0)},
1293 CommitOrderingTest::MakeLastCommitItem(),
1295 RunCommitOrderingTest(items
);
1298 TEST_F(SyncerTest
, TestCommitListOrderingThreeItemsTall
) {
1299 CommitOrderingTest items
[] = {
1300 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1301 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1302 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1303 CommitOrderingTest::MakeLastCommitItem(),
1305 RunCommitOrderingTest(items
);
1308 TEST_F(SyncerTest
, TestCommitListOrderingFourItemsTall
) {
1309 CommitOrderingTest items
[] = {
1310 {3, ids_
.FromNumber(-2003), ids_
.FromNumber(-2002)},
1311 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1312 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1313 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1314 CommitOrderingTest::MakeLastCommitItem(),
1316 RunCommitOrderingTest(items
);
1319 TEST_F(SyncerTest
, TestCommitListOrderingThreeItemsTallLimitedSize
) {
1320 context_
->set_max_commit_batch_size(2);
1321 CommitOrderingTest items
[] = {
1322 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1323 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1324 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1325 CommitOrderingTest::MakeLastCommitItem(),
1327 RunCommitOrderingTest(items
);
1330 TEST_F(SyncerTest
, TestCommitListOrderingSingleDeletedItem
) {
1331 CommitOrderingTest items
[] = {
1332 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1333 CommitOrderingTest::MakeLastCommitItem(),
1335 RunCommitOrderingTest(items
);
1338 TEST_F(SyncerTest
, TestCommitListOrderingSingleUncommittedDeletedItem
) {
1339 CommitOrderingTest items
[] = {
1340 {-1, ids_
.FromNumber(-1000), ids_
.FromNumber(0), {DELETED
}},
1341 CommitOrderingTest::MakeLastCommitItem(),
1343 RunCommitOrderingTest(items
);
1346 TEST_F(SyncerTest
, TestCommitListOrderingSingleDeletedItemWithUnroll
) {
1347 CommitOrderingTest items
[] = {
1348 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1349 CommitOrderingTest::MakeLastCommitItem(),
1351 RunCommitOrderingTest(items
);
1355 TestCommitListOrderingSingleLongDeletedItemWithUnroll
) {
1356 CommitOrderingTest items
[] = {
1357 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1358 CommitOrderingTest::MakeLastCommitItem(),
1360 RunCommitOrderingTest(items
);
1363 TEST_F(SyncerTest
, TestCommitListOrderingTwoLongDeletedItemWithUnroll
) {
1364 CommitOrderingTest items
[] = {
1365 {1, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1366 {0, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
, OLD_MTIME
}},
1367 CommitOrderingTest::MakeLastCommitItem(),
1369 RunCommitOrderingTest(items
);
1372 TEST_F(SyncerTest
, TestCommitListOrdering3LongDeletedItemsWithSizeLimit
) {
1373 context_
->set_max_commit_batch_size(2);
1374 CommitOrderingTest items
[] = {
1375 {2, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1376 {1, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
, OLD_MTIME
}},
1377 {0, ids_
.FromNumber(1002), ids_
.FromNumber(1001), {DELETED
, OLD_MTIME
}},
1378 CommitOrderingTest::MakeLastCommitItem(),
1380 RunCommitOrderingTest(items
);
1383 TEST_F(SyncerTest
, TestCommitListOrderingTwoDeletedItemsWithUnroll
) {
1384 CommitOrderingTest items
[] = {
1385 {1, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1386 {0, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
}},
1387 CommitOrderingTest::MakeLastCommitItem(),
1389 RunCommitOrderingTest(items
);
1392 TEST_F(SyncerTest
, TestCommitListOrderingComplexDeletionScenario
) {
1393 CommitOrderingTest items
[] = {
1394 {2, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1395 {-1, ids_
.FromNumber(1001), ids_
.FromNumber(0), {SYNCED
}},
1396 {1, ids_
.FromNumber(1002), ids_
.FromNumber(1001), {DELETED
, OLD_MTIME
}},
1397 {-1, ids_
.FromNumber(1003), ids_
.FromNumber(1001), {SYNCED
}},
1398 {0, ids_
.FromNumber(1004), ids_
.FromNumber(1003), {DELETED
}},
1399 CommitOrderingTest::MakeLastCommitItem(),
1401 RunCommitOrderingTest(items
);
1405 TestCommitListOrderingComplexDeletionScenarioWith2RecentDeletes
) {
1406 CommitOrderingTest items
[] = {
1407 {3, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1408 {-1, ids_
.FromNumber(1001), ids_
.FromNumber(0), {SYNCED
}},
1409 {2, ids_
.FromNumber(1002), ids_
.FromNumber(1001), {DELETED
, OLD_MTIME
}},
1410 {-1, ids_
.FromNumber(1003), ids_
.FromNumber(1001), {SYNCED
}},
1411 {1, ids_
.FromNumber(1004), ids_
.FromNumber(1003), {DELETED
}},
1412 {0, ids_
.FromNumber(1005), ids_
.FromNumber(1003), {DELETED
}},
1413 CommitOrderingTest::MakeLastCommitItem(),
1415 RunCommitOrderingTest(items
);
1418 TEST_F(SyncerTest
, TestCommitListOrderingDeleteMovedItems
) {
1419 CommitOrderingTest items
[] = {
1420 {1, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1421 {0, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
, OLD_MTIME
,
1423 CommitOrderingTest::MakeLastCommitItem(),
1425 RunCommitOrderingTest(items
);
1428 TEST_F(SyncerTest
, TestCommitListOrderingWithNesting
) {
1429 const base::Time
& now_minus_2h
=
1430 base::Time::Now() - base::TimeDelta::FromHours(2);
1432 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1434 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bob");
1435 ASSERT_TRUE(parent
.good());
1436 parent
.PutIsUnsynced(true);
1437 parent
.PutIsDir(true);
1438 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1439 parent
.PutId(ids_
.FromNumber(100));
1440 parent
.PutBaseVersion(1);
1442 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(100), "Bob");
1443 ASSERT_TRUE(child
.good());
1444 child
.PutIsUnsynced(true);
1445 child
.PutIsDir(true);
1446 child
.PutSpecifics(DefaultBookmarkSpecifics());
1447 child
.PutId(ids_
.FromNumber(101));
1448 child
.PutBaseVersion(1);
1449 MutableEntry
grandchild(
1450 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(101), "Bob");
1451 ASSERT_TRUE(grandchild
.good());
1452 grandchild
.PutId(ids_
.FromNumber(102));
1453 grandchild
.PutIsUnsynced(true);
1454 grandchild
.PutSpecifics(DefaultBookmarkSpecifics());
1455 grandchild
.PutBaseVersion(1);
1458 // Create three deleted items which deletions we expect to be sent to the
1460 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1461 ASSERT_TRUE(parent
.good());
1462 parent
.PutId(ids_
.FromNumber(103));
1463 parent
.PutIsUnsynced(true);
1464 parent
.PutIsDir(true);
1465 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1466 parent
.PutIsDel(true);
1467 parent
.PutBaseVersion(1);
1468 parent
.PutMtime(now_minus_2h
);
1470 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(103), "Pete");
1471 ASSERT_TRUE(child
.good());
1472 child
.PutId(ids_
.FromNumber(104));
1473 child
.PutIsUnsynced(true);
1474 child
.PutIsDir(true);
1475 child
.PutSpecifics(DefaultBookmarkSpecifics());
1476 child
.PutIsDel(true);
1477 child
.PutBaseVersion(1);
1478 child
.PutMtime(now_minus_2h
);
1479 MutableEntry
grandchild(
1480 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(104), "Pete");
1481 ASSERT_TRUE(grandchild
.good());
1482 grandchild
.PutId(ids_
.FromNumber(105));
1483 grandchild
.PutIsUnsynced(true);
1484 grandchild
.PutIsDel(true);
1485 grandchild
.PutIsDir(false);
1486 grandchild
.PutSpecifics(DefaultBookmarkSpecifics());
1487 grandchild
.PutBaseVersion(1);
1488 grandchild
.PutMtime(now_minus_2h
);
1493 ASSERT_EQ(6u, mock_server_
->committed_ids().size());
1494 // This test will NOT unroll deletes because SERVER_PARENT_ID is not set.
1495 // It will treat these like moves.
1496 vector
<syncable::Id
> commit_ids(mock_server_
->committed_ids());
1497 EXPECT_TRUE(ids_
.FromNumber(100) == commit_ids
[0]);
1498 EXPECT_TRUE(ids_
.FromNumber(101) == commit_ids
[1]);
1499 EXPECT_TRUE(ids_
.FromNumber(102) == commit_ids
[2]);
1500 // We don't guarantee the delete orders in this test, only that they occur
1502 std::sort(commit_ids
.begin() + 3, commit_ids
.end());
1503 EXPECT_TRUE(ids_
.FromNumber(103) == commit_ids
[3]);
1504 EXPECT_TRUE(ids_
.FromNumber(104) == commit_ids
[4]);
1505 EXPECT_TRUE(ids_
.FromNumber(105) == commit_ids
[5]);
1508 TEST_F(SyncerTest
, TestCommitListOrderingWithNewItems
) {
1509 syncable::Id parent1_id
= ids_
.MakeServer("p1");
1510 syncable::Id parent2_id
= ids_
.MakeServer("p2");
1513 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1514 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "1");
1515 ASSERT_TRUE(parent
.good());
1516 parent
.PutIsUnsynced(true);
1517 parent
.PutIsDir(true);
1518 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1519 parent
.PutId(parent1_id
);
1520 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "2");
1521 ASSERT_TRUE(child
.good());
1522 child
.PutIsUnsynced(true);
1523 child
.PutIsDir(true);
1524 child
.PutSpecifics(DefaultBookmarkSpecifics());
1525 child
.PutId(parent2_id
);
1526 parent
.PutBaseVersion(1);
1527 child
.PutBaseVersion(1);
1530 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1531 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, parent1_id
, "A");
1532 ASSERT_TRUE(parent
.good());
1533 parent
.PutIsUnsynced(true);
1534 parent
.PutIsDir(true);
1535 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1536 parent
.PutId(ids_
.FromNumber(102));
1537 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent1_id
, "B");
1538 ASSERT_TRUE(child
.good());
1539 child
.PutIsUnsynced(true);
1540 child
.PutIsDir(true);
1541 child
.PutSpecifics(DefaultBookmarkSpecifics());
1542 child
.PutId(ids_
.FromNumber(-103));
1543 parent
.PutBaseVersion(1);
1546 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1547 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, parent2_id
, "A");
1548 ASSERT_TRUE(parent
.good());
1549 parent
.PutIsUnsynced(true);
1550 parent
.PutIsDir(true);
1551 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1552 parent
.PutId(ids_
.FromNumber(-104));
1553 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent2_id
, "B");
1554 ASSERT_TRUE(child
.good());
1555 child
.PutIsUnsynced(true);
1556 child
.PutIsDir(true);
1557 child
.PutSpecifics(DefaultBookmarkSpecifics());
1558 child
.PutId(ids_
.FromNumber(105));
1559 child
.PutBaseVersion(1);
1563 ASSERT_EQ(6u, mock_server_
->committed_ids().size());
1565 // This strange iteration and std::count() usage is to allow the order to
1566 // vary. All we really care about is that parent1_id and parent2_id are the
1567 // first two IDs, and that the children make up the next four. Other than
1568 // that, ordering doesn't matter.
1570 vector
<syncable::Id
>::const_iterator i
=
1571 mock_server_
->committed_ids().begin();
1572 vector
<syncable::Id
>::const_iterator parents_begin
= i
;
1575 vector
<syncable::Id
>::const_iterator parents_end
= i
;
1576 vector
<syncable::Id
>::const_iterator children_begin
= i
;
1577 vector
<syncable::Id
>::const_iterator children_end
=
1578 mock_server_
->committed_ids().end();
1580 EXPECT_EQ(1, count(parents_begin
, parents_end
, parent1_id
));
1581 EXPECT_EQ(1, count(parents_begin
, parents_end
, parent2_id
));
1583 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(-103)));
1584 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(102)));
1585 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(105)));
1586 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(-104)));
1589 TEST_F(SyncerTest
, TestCommitListOrderingCounterexample
) {
1590 syncable::Id child2_id
= ids_
.NewServerId();
1593 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1594 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "P");
1595 ASSERT_TRUE(parent
.good());
1596 parent
.PutIsUnsynced(true);
1597 parent
.PutIsDir(true);
1598 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1599 parent
.PutId(parent_id_
);
1600 MutableEntry
child1(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "1");
1601 ASSERT_TRUE(child1
.good());
1602 child1
.PutIsUnsynced(true);
1603 child1
.PutId(child_id_
);
1604 child1
.PutSpecifics(DefaultBookmarkSpecifics());
1605 MutableEntry
child2(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "2");
1606 ASSERT_TRUE(child2
.good());
1607 child2
.PutIsUnsynced(true);
1608 child2
.PutSpecifics(DefaultBookmarkSpecifics());
1609 child2
.PutId(child2_id
);
1611 parent
.PutBaseVersion(1);
1612 child1
.PutBaseVersion(1);
1613 child2
.PutBaseVersion(1);
1617 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1618 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1619 // There are two possible valid orderings.
1620 if (child2_id
== mock_server_
->committed_ids()[1]) {
1621 EXPECT_TRUE(child2_id
== mock_server_
->committed_ids()[1]);
1622 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[2]);
1624 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1625 EXPECT_TRUE(child2_id
== mock_server_
->committed_ids()[2]);
1629 TEST_F(SyncerTest
, TestCommitListOrderingAndNewParent
) {
1630 string parent1_name
= "1";
1631 string parent2_name
= "A";
1632 string child_name
= "B";
1635 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1636 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(),
1638 ASSERT_TRUE(parent
.good());
1639 parent
.PutIsUnsynced(true);
1640 parent
.PutIsDir(true);
1641 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1642 parent
.PutId(parent_id_
);
1643 parent
.PutBaseVersion(1);
1646 syncable::Id parent2_id
= ids_
.NewLocalId();
1647 syncable::Id child_id
= ids_
.NewServerId();
1649 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1650 MutableEntry
parent2(
1651 &wtrans
, CREATE
, BOOKMARKS
, parent_id_
, parent2_name
);
1652 ASSERT_TRUE(parent2
.good());
1653 parent2
.PutIsUnsynced(true);
1654 parent2
.PutIsDir(true);
1655 parent2
.PutSpecifics(DefaultBookmarkSpecifics());
1656 parent2
.PutId(parent2_id
);
1659 &wtrans
, CREATE
, BOOKMARKS
, parent2_id
, child_name
);
1660 ASSERT_TRUE(child
.good());
1661 child
.PutIsUnsynced(true);
1662 child
.PutIsDir(true);
1663 child
.PutSpecifics(DefaultBookmarkSpecifics());
1664 child
.PutId(child_id
);
1665 child
.PutBaseVersion(1);
1669 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1670 // If this test starts failing, be aware other sort orders could be valid.
1671 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1672 EXPECT_TRUE(parent2_id
== mock_server_
->committed_ids()[1]);
1673 EXPECT_TRUE(child_id
== mock_server_
->committed_ids()[2]);
1675 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1676 // Check that things committed correctly.
1677 Entry
entry_1(&rtrans
, syncable::GET_BY_ID
, parent_id_
);
1678 EXPECT_EQ(entry_1
.GetNonUniqueName(), parent1_name
);
1679 // Check that parent2 is a subfolder of parent1.
1680 EXPECT_EQ(1, CountEntriesWithName(&rtrans
,
1684 // Parent2 was a local ID and thus should have changed on commit!
1685 Entry
pre_commit_entry_parent2(&rtrans
, syncable::GET_BY_ID
, parent2_id
);
1686 ASSERT_FALSE(pre_commit_entry_parent2
.good());
1688 // Look up the new ID.
1689 Id parent2_committed_id
=
1690 GetOnlyEntryWithName(&rtrans
, parent_id_
, parent2_name
);
1691 EXPECT_TRUE(parent2_committed_id
.ServerKnows());
1693 Entry
child(&rtrans
, syncable::GET_BY_ID
, child_id
);
1694 EXPECT_EQ(parent2_committed_id
, child
.GetParentId());
1698 TEST_F(SyncerTest
, TestCommitListOrderingAndNewParentAndChild
) {
1699 string parent_name
= "1";
1700 string parent2_name
= "A";
1701 string child_name
= "B";
1704 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1705 MutableEntry
parent(&wtrans
,
1709 ASSERT_TRUE(parent
.good());
1710 parent
.PutIsUnsynced(true);
1711 parent
.PutIsDir(true);
1712 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1713 parent
.PutId(parent_id_
);
1714 parent
.PutBaseVersion(1);
1717 int64 meta_handle_b
;
1718 const Id parent2_local_id
= ids_
.NewLocalId();
1719 const Id child_local_id
= ids_
.NewLocalId();
1721 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1722 MutableEntry
parent2(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, parent2_name
);
1723 ASSERT_TRUE(parent2
.good());
1724 parent2
.PutIsUnsynced(true);
1725 parent2
.PutIsDir(true);
1726 parent2
.PutSpecifics(DefaultBookmarkSpecifics());
1728 parent2
.PutId(parent2_local_id
);
1730 &wtrans
, CREATE
, BOOKMARKS
, parent2_local_id
, child_name
);
1731 ASSERT_TRUE(child
.good());
1732 child
.PutIsUnsynced(true);
1733 child
.PutIsDir(true);
1734 child
.PutSpecifics(DefaultBookmarkSpecifics());
1735 child
.PutId(child_local_id
);
1736 meta_handle_b
= child
.GetMetahandle();
1740 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1741 // If this test starts failing, be aware other sort orders could be valid.
1742 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1743 EXPECT_TRUE(parent2_local_id
== mock_server_
->committed_ids()[1]);
1744 EXPECT_TRUE(child_local_id
== mock_server_
->committed_ids()[2]);
1746 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1748 Entry
parent(&rtrans
, syncable::GET_BY_ID
,
1749 GetOnlyEntryWithName(&rtrans
, rtrans
.root_id(), parent_name
));
1750 ASSERT_TRUE(parent
.good());
1751 EXPECT_TRUE(parent
.GetId().ServerKnows());
1753 Entry
parent2(&rtrans
, syncable::GET_BY_ID
,
1754 GetOnlyEntryWithName(&rtrans
, parent
.GetId(), parent2_name
));
1755 ASSERT_TRUE(parent2
.good());
1756 EXPECT_TRUE(parent2
.GetId().ServerKnows());
1758 // Id changed on commit, so this should fail.
1759 Entry
local_parent2_id_entry(&rtrans
,
1760 syncable::GET_BY_ID
,
1762 ASSERT_FALSE(local_parent2_id_entry
.good());
1764 Entry
entry_b(&rtrans
, syncable::GET_BY_HANDLE
, meta_handle_b
);
1765 EXPECT_TRUE(entry_b
.GetId().ServerKnows());
1766 EXPECT_TRUE(parent2
.GetId()== entry_b
.GetParentId());
1770 TEST_F(SyncerTest
, UpdateWithZeroLengthName
) {
1771 // One illegal update
1772 mock_server_
->AddUpdateDirectory(
1773 1, 0, std::string(), 1, 10, foreign_cache_guid(), "-1");
1774 // And one legal one that we're going to delete.
1775 mock_server_
->AddUpdateDirectory(2, 0, "FOO", 1, 10,
1776 foreign_cache_guid(), "-2");
1778 // Delete the legal one. The new update has a null name.
1779 mock_server_
->AddUpdateDirectory(
1780 2, 0, std::string(), 2, 20, foreign_cache_guid(), "-2");
1781 mock_server_
->SetLastUpdateDeleted();
1785 TEST_F(SyncerTest
, TestBasicUpdate
) {
1786 string id
= "some_id";
1787 string parent_id
= "0";
1788 string name
= "in_root";
1790 int64 timestamp
= 10;
1791 mock_server_
->AddUpdateDirectory(id
, parent_id
, name
, version
, timestamp
,
1792 foreign_cache_guid(), "-1");
1796 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1797 Entry
entry(&trans
, GET_BY_ID
,
1798 syncable::Id::CreateFromServerId("some_id"));
1799 ASSERT_TRUE(entry
.good());
1800 EXPECT_TRUE(entry
.GetIsDir());
1801 EXPECT_TRUE(entry
.GetServerVersion()== version
);
1802 EXPECT_TRUE(entry
.GetBaseVersion()== version
);
1803 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
1804 EXPECT_FALSE(entry
.GetIsUnsynced());
1805 EXPECT_FALSE(entry
.GetServerIsDel());
1806 EXPECT_FALSE(entry
.GetIsDel());
1810 TEST_F(SyncerTest
, IllegalAndLegalUpdates
) {
1811 Id root
= TestIdFactory::root();
1812 // Should apply just fine.
1813 mock_server_
->AddUpdateDirectory(1, 0, "in_root", 10, 10,
1814 foreign_cache_guid(), "-1");
1816 // Same name. But this SHOULD work.
1817 mock_server_
->AddUpdateDirectory(2, 0, "in_root", 10, 10,
1818 foreign_cache_guid(), "-2");
1820 // Unknown parent: should never be applied. "-80" is a legal server ID,
1821 // because any string sent by the server is a legal server ID in the sync
1822 // protocol, but it's not the ID of any item known to the client. This
1823 // update should succeed validation, but be stuck in the unapplied state
1824 // until an item with the server ID "-80" arrives.
1825 mock_server_
->AddUpdateDirectory(3, -80, "bad_parent", 10, 10,
1826 foreign_cache_guid(), "-3");
1830 // Id 3 should be in conflict now.
1833 GetUpdateCounters(BOOKMARKS
).num_hierarchy_conflict_application_failures
);
1835 // The only request in that loop should have been a GetUpdate.
1836 // At that point, we didn't know whether or not we had conflicts.
1837 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
1838 VerifyHierarchyConflictsUnspecified(mock_server_
->last_request());
1840 // These entries will be used in the second set of updates.
1841 mock_server_
->AddUpdateDirectory(4, 0, "newer_version", 20, 10,
1842 foreign_cache_guid(), "-4");
1843 mock_server_
->AddUpdateDirectory(5, 0, "circular1", 10, 10,
1844 foreign_cache_guid(), "-5");
1845 mock_server_
->AddUpdateDirectory(6, 5, "circular2", 10, 10,
1846 foreign_cache_guid(), "-6");
1847 mock_server_
->AddUpdateDirectory(9, 3, "bad_parent_child", 10, 10,
1848 foreign_cache_guid(), "-9");
1849 mock_server_
->AddUpdateDirectory(100, 9, "bad_parent_child2", 10, 10,
1850 foreign_cache_guid(), "-100");
1851 mock_server_
->AddUpdateDirectory(10, 0, "dir_to_bookmark", 10, 10,
1852 foreign_cache_guid(), "-10");
1855 // The three items with an unresolved parent should be unapplied (3, 9, 100).
1856 // The name clash should also still be in conflict.
1859 GetUpdateCounters(BOOKMARKS
).num_hierarchy_conflict_application_failures
);
1861 // This time around, we knew that there were conflicts.
1862 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
1863 VerifyHierarchyConflictsReported(mock_server_
->last_request());
1866 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1867 // Even though it has the same name, it should work.
1868 Entry
name_clash(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
1869 ASSERT_TRUE(name_clash
.good());
1870 EXPECT_FALSE(name_clash
.GetIsUnappliedUpdate())
1871 << "Duplicate name SHOULD be OK.";
1873 Entry
bad_parent(&trans
, GET_BY_ID
, ids_
.FromNumber(3));
1874 ASSERT_TRUE(bad_parent
.good());
1875 EXPECT_TRUE(bad_parent
.GetIsUnappliedUpdate())
1876 << "child of unknown parent should be in conflict";
1878 Entry
bad_parent_child(&trans
, GET_BY_ID
, ids_
.FromNumber(9));
1879 ASSERT_TRUE(bad_parent_child
.good());
1880 EXPECT_TRUE(bad_parent_child
.GetIsUnappliedUpdate())
1881 << "grandchild of unknown parent should be in conflict";
1883 Entry
bad_parent_child2(&trans
, GET_BY_ID
, ids_
.FromNumber(100));
1884 ASSERT_TRUE(bad_parent_child2
.good());
1885 EXPECT_TRUE(bad_parent_child2
.GetIsUnappliedUpdate())
1886 << "great-grandchild of unknown parent should be in conflict";
1889 // Updating 1 should not affect item 2 of the same name.
1890 mock_server_
->AddUpdateDirectory(1, 0, "new_name", 20, 20,
1891 foreign_cache_guid(), "-1");
1893 // Moving 5 under 6 will create a cycle: a conflict.
1894 mock_server_
->AddUpdateDirectory(5, 6, "circular3", 20, 20,
1895 foreign_cache_guid(), "-5");
1897 // Flip the is_dir bit: should fail verify & be dropped.
1898 mock_server_
->AddUpdateBookmark(10, 0, "dir_to_bookmark", 20, 20,
1899 foreign_cache_guid(), "-10");
1902 // Version number older than last known: should fail verify & be dropped.
1903 mock_server_
->AddUpdateDirectory(4, 0, "old_version", 10, 10,
1904 foreign_cache_guid(), "-4");
1907 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1909 Entry
still_a_dir(&trans
, GET_BY_ID
, ids_
.FromNumber(10));
1910 ASSERT_TRUE(still_a_dir
.good());
1911 EXPECT_FALSE(still_a_dir
.GetIsUnappliedUpdate());
1912 EXPECT_EQ(10u, still_a_dir
.GetBaseVersion());
1913 EXPECT_EQ(10u, still_a_dir
.GetServerVersion());
1914 EXPECT_TRUE(still_a_dir
.GetIsDir());
1916 Entry
rename(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
1917 ASSERT_TRUE(rename
.good());
1918 EXPECT_EQ(root
, rename
.GetParentId());
1919 EXPECT_EQ("new_name", rename
.GetNonUniqueName());
1920 EXPECT_FALSE(rename
.GetIsUnappliedUpdate());
1921 EXPECT_TRUE(ids_
.FromNumber(1) == rename
.GetId());
1922 EXPECT_EQ(20u, rename
.GetBaseVersion());
1924 Entry
name_clash(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
1925 ASSERT_TRUE(name_clash
.good());
1926 EXPECT_EQ(root
, name_clash
.GetParentId());
1927 EXPECT_TRUE(ids_
.FromNumber(2) == name_clash
.GetId());
1928 EXPECT_EQ(10u, name_clash
.GetBaseVersion());
1929 EXPECT_EQ("in_root", name_clash
.GetNonUniqueName());
1931 Entry
ignored_old_version(&trans
, GET_BY_ID
, ids_
.FromNumber(4));
1932 ASSERT_TRUE(ignored_old_version
.good());
1934 ignored_old_version
.GetNonUniqueName()== "newer_version");
1935 EXPECT_FALSE(ignored_old_version
.GetIsUnappliedUpdate());
1936 EXPECT_EQ(20u, ignored_old_version
.GetBaseVersion());
1938 Entry
circular_parent_issue(&trans
, GET_BY_ID
, ids_
.FromNumber(5));
1939 ASSERT_TRUE(circular_parent_issue
.good());
1940 EXPECT_TRUE(circular_parent_issue
.GetIsUnappliedUpdate())
1941 << "circular move should be in conflict";
1942 EXPECT_TRUE(circular_parent_issue
.GetParentId()== root_id_
);
1943 EXPECT_TRUE(circular_parent_issue
.GetServerParentId()==
1944 ids_
.FromNumber(6));
1945 EXPECT_EQ(10u, circular_parent_issue
.GetBaseVersion());
1947 Entry
circular_parent_target(&trans
, GET_BY_ID
, ids_
.FromNumber(6));
1948 ASSERT_TRUE(circular_parent_target
.good());
1949 EXPECT_FALSE(circular_parent_target
.GetIsUnappliedUpdate());
1950 EXPECT_TRUE(circular_parent_issue
.GetId()==
1951 circular_parent_target
.GetParentId());
1952 EXPECT_EQ(10u, circular_parent_target
.GetBaseVersion());
1955 EXPECT_FALSE(saw_syncer_event_
);
1958 GetUpdateCounters(BOOKMARKS
).num_hierarchy_conflict_application_failures
);
1961 // A commit with a lost response produces an update that has to be reunited with
1963 TEST_F(SyncerTest
, CommitReuniteUpdateAdjustsChildren
) {
1964 // Create a folder in the root.
1965 int64 metahandle_folder
;
1967 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1969 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_folder");
1970 ASSERT_TRUE(entry
.good());
1971 entry
.PutIsDir(true);
1972 entry
.PutSpecifics(DefaultBookmarkSpecifics());
1973 entry
.PutIsUnsynced(true);
1974 metahandle_folder
= entry
.GetMetahandle();
1977 // Verify it and pull the ID out of the folder.
1978 syncable::Id folder_id
;
1979 int64 metahandle_entry
;
1981 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1982 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_folder
);
1983 ASSERT_TRUE(entry
.good());
1984 folder_id
= entry
.GetId();
1985 ASSERT_TRUE(!folder_id
.ServerKnows());
1988 // Create an entry in the newly created folder.
1990 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1991 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, folder_id
, "new_entry");
1992 ASSERT_TRUE(entry
.good());
1993 metahandle_entry
= entry
.GetMetahandle();
1994 WriteTestDataToEntry(&trans
, &entry
);
1997 // Verify it and pull the ID out of the entry.
1998 syncable::Id entry_id
;
2000 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2001 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, metahandle_entry
);
2002 ASSERT_TRUE(entry
.good());
2003 EXPECT_EQ(folder_id
, entry
.GetParentId());
2004 EXPECT_EQ("new_entry", entry
.GetNonUniqueName());
2005 entry_id
= entry
.GetId();
2006 EXPECT_TRUE(!entry_id
.ServerKnows());
2007 VerifyTestDataInEntry(&trans
, &entry
);
2010 // Now, to emulate a commit response failure, we just don't commit it.
2011 int64 new_version
= 150; // any larger value.
2012 int64 timestamp
= 20; // arbitrary value.
2013 syncable::Id new_folder_id
=
2014 syncable::Id::CreateFromServerId("folder_server_id");
2016 // The following update should cause the folder to both apply the update, as
2017 // well as reassociate the id.
2018 mock_server_
->AddUpdateDirectory(new_folder_id
, root_id_
,
2019 "new_folder", new_version
, timestamp
,
2020 local_cache_guid(), folder_id
.GetServerId());
2022 // We don't want it accidentally committed, just the update applied.
2023 mock_server_
->set_conflict_all_commits(true);
2025 // Alright! Apply that update!
2028 // The folder's ID should have been updated.
2029 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2030 Entry
folder(&trans
, GET_BY_HANDLE
, metahandle_folder
);
2031 ASSERT_TRUE(folder
.good());
2032 EXPECT_EQ("new_folder", folder
.GetNonUniqueName());
2033 EXPECT_TRUE(new_version
== folder
.GetBaseVersion());
2034 EXPECT_TRUE(new_folder_id
== folder
.GetId());
2035 EXPECT_TRUE(folder
.GetId().ServerKnows());
2036 EXPECT_EQ(trans
.root_id(), folder
.GetParentId());
2038 // Since it was updated, the old folder should not exist.
2039 Entry
old_dead_folder(&trans
, GET_BY_ID
, folder_id
);
2040 EXPECT_FALSE(old_dead_folder
.good());
2042 // The child's parent should have changed.
2043 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, metahandle_entry
);
2044 ASSERT_TRUE(entry
.good());
2045 EXPECT_EQ("new_entry", entry
.GetNonUniqueName());
2046 EXPECT_EQ(new_folder_id
, entry
.GetParentId());
2047 EXPECT_TRUE(!entry
.GetId().ServerKnows());
2048 VerifyTestDataInEntry(&trans
, &entry
);
2052 // A commit with a lost response produces an update that has to be reunited with
2054 TEST_F(SyncerTest
, CommitReuniteUpdate
) {
2055 // Create an entry in the root.
2056 int64 entry_metahandle
;
2058 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2059 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_entry");
2060 ASSERT_TRUE(entry
.good());
2061 entry_metahandle
= entry
.GetMetahandle();
2062 WriteTestDataToEntry(&trans
, &entry
);
2065 // Verify it and pull the ID out.
2066 syncable::Id entry_id
;
2068 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2070 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2071 ASSERT_TRUE(entry
.good());
2072 entry_id
= entry
.GetId();
2073 EXPECT_TRUE(!entry_id
.ServerKnows());
2074 VerifyTestDataInEntry(&trans
, &entry
);
2077 // Now, to emulate a commit response failure, we just don't commit it.
2078 int64 new_version
= 150; // any larger value.
2079 int64 timestamp
= 20; // arbitrary value.
2080 syncable::Id new_entry_id
= syncable::Id::CreateFromServerId("server_id");
2082 // Generate an update from the server with a relevant ID reassignment.
2083 mock_server_
->AddUpdateBookmark(new_entry_id
, root_id_
,
2084 "new_entry", new_version
, timestamp
,
2085 local_cache_guid(), entry_id
.GetServerId());
2087 // We don't want it accidentally committed, just the update applied.
2088 mock_server_
->set_conflict_all_commits(true);
2090 // Alright! Apply that update!
2093 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2094 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2095 ASSERT_TRUE(entry
.good());
2096 EXPECT_TRUE(new_version
== entry
.GetBaseVersion());
2097 EXPECT_TRUE(new_entry_id
== entry
.GetId());
2098 EXPECT_EQ("new_entry", entry
.GetNonUniqueName());
2102 // A commit with a lost response must work even if the local entry was deleted
2103 // before the update is applied. We should not duplicate the local entry in
2104 // this case, but just create another one alongside. We may wish to examine
2105 // this behavior in the future as it can create hanging uploads that never
2106 // finish, that must be cleaned up on the server side after some time.
2107 TEST_F(SyncerTest
, CommitReuniteUpdateDoesNotChokeOnDeletedLocalEntry
) {
2108 // Create a entry in the root.
2109 int64 entry_metahandle
;
2111 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2112 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_entry");
2113 ASSERT_TRUE(entry
.good());
2114 entry_metahandle
= entry
.GetMetahandle();
2115 WriteTestDataToEntry(&trans
, &entry
);
2117 // Verify it and pull the ID out.
2118 syncable::Id entry_id
;
2120 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2121 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2122 ASSERT_TRUE(entry
.good());
2123 entry_id
= entry
.GetId();
2124 EXPECT_TRUE(!entry_id
.ServerKnows());
2125 VerifyTestDataInEntry(&trans
, &entry
);
2128 // Now, to emulate a commit response failure, we just don't commit it.
2129 int64 new_version
= 150; // any larger value.
2130 int64 timestamp
= 20; // arbitrary value.
2131 syncable::Id new_entry_id
= syncable::Id::CreateFromServerId("server_id");
2133 // Generate an update from the server with a relevant ID reassignment.
2134 mock_server_
->AddUpdateBookmark(new_entry_id
, root_id_
,
2135 "new_entry", new_version
, timestamp
,
2136 local_cache_guid(), entry_id
.GetServerId());
2138 // We don't want it accidentally committed, just the update applied.
2139 mock_server_
->set_conflict_all_commits(true);
2141 // Purposefully delete the entry now before the update application finishes.
2143 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2144 Id new_entry_id
= GetOnlyEntryWithName(
2145 &trans
, trans
.root_id(), "new_entry");
2146 MutableEntry
entry(&trans
, GET_BY_ID
, new_entry_id
);
2147 ASSERT_TRUE(entry
.good());
2148 entry
.PutIsDel(true);
2151 // Just don't CHECK fail in sync, have the update split.
2154 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2155 Id new_entry_id
= GetOnlyEntryWithName(
2156 &trans
, trans
.root_id(), "new_entry");
2157 Entry
entry(&trans
, GET_BY_ID
, new_entry_id
);
2158 ASSERT_TRUE(entry
.good());
2159 EXPECT_FALSE(entry
.GetIsDel());
2161 Entry
old_entry(&trans
, GET_BY_ID
, entry_id
);
2162 ASSERT_TRUE(old_entry
.good());
2163 EXPECT_TRUE(old_entry
.GetIsDel());
2167 // TODO(chron): Add more unsanitized name tests.
2168 TEST_F(SyncerTest
, ConflictMatchingEntryHandlesUnsanitizedNames
) {
2169 mock_server_
->AddUpdateDirectory(1, 0, "A/A", 10, 10,
2170 foreign_cache_guid(), "-1");
2171 mock_server_
->AddUpdateDirectory(2, 0, "B/B", 10, 10,
2172 foreign_cache_guid(), "-2");
2173 mock_server_
->set_conflict_all_commits(true);
2176 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2178 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2179 ASSERT_TRUE(A
.good());
2180 A
.PutIsUnsynced(true);
2181 A
.PutIsUnappliedUpdate(true);
2182 A
.PutServerVersion(20);
2184 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
2185 ASSERT_TRUE(B
.good());
2186 B
.PutIsUnappliedUpdate(true);
2187 B
.PutServerVersion(20);
2190 saw_syncer_event_
= false;
2191 mock_server_
->set_conflict_all_commits(false);
2194 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2196 Entry
A(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2197 ASSERT_TRUE(A
.good());
2198 EXPECT_TRUE(A
.GetIsUnsynced()== false);
2199 EXPECT_TRUE(A
.GetIsUnappliedUpdate()== false);
2200 EXPECT_TRUE(A
.GetServerVersion()== 20);
2202 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
2203 ASSERT_TRUE(B
.good());
2204 EXPECT_TRUE(B
.GetIsUnsynced()== false);
2205 EXPECT_TRUE(B
.GetIsUnappliedUpdate()== false);
2206 EXPECT_TRUE(B
.GetServerVersion()== 20);
2210 TEST_F(SyncerTest
, ConflictMatchingEntryHandlesNormalNames
) {
2211 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
2212 foreign_cache_guid(), "-1");
2213 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
2214 foreign_cache_guid(), "-2");
2215 mock_server_
->set_conflict_all_commits(true);
2218 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2220 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2221 ASSERT_TRUE(A
.good());
2222 A
.PutIsUnsynced(true);
2223 A
.PutIsUnappliedUpdate(true);
2224 A
.PutServerVersion(20);
2226 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
2227 ASSERT_TRUE(B
.good());
2228 B
.PutIsUnappliedUpdate(true);
2229 B
.PutServerVersion(20);
2232 saw_syncer_event_
= false;
2233 mock_server_
->set_conflict_all_commits(false);
2236 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2238 Entry
A(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2239 ASSERT_TRUE(A
.good());
2240 EXPECT_TRUE(A
.GetIsUnsynced()== false);
2241 EXPECT_TRUE(A
.GetIsUnappliedUpdate()== false);
2242 EXPECT_TRUE(A
.GetServerVersion()== 20);
2244 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
2245 ASSERT_TRUE(B
.good());
2246 EXPECT_TRUE(B
.GetIsUnsynced()== false);
2247 EXPECT_TRUE(B
.GetIsUnappliedUpdate()== false);
2248 EXPECT_TRUE(B
.GetServerVersion()== 20);
2252 TEST_F(SyncerTest
, ReverseFolderOrderingTest
) {
2253 mock_server_
->AddUpdateDirectory(4, 3, "ggchild", 10, 10,
2254 foreign_cache_guid(), "-4");
2255 mock_server_
->AddUpdateDirectory(3, 2, "gchild", 10, 10,
2256 foreign_cache_guid(), "-3");
2257 mock_server_
->AddUpdateDirectory(5, 4, "gggchild", 10, 10,
2258 foreign_cache_guid(), "-5");
2259 mock_server_
->AddUpdateDirectory(2, 1, "child", 10, 10,
2260 foreign_cache_guid(), "-2");
2261 mock_server_
->AddUpdateDirectory(1, 0, "parent", 10, 10,
2262 foreign_cache_guid(), "-1");
2264 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2266 Id child_id
= GetOnlyEntryWithName(
2267 &trans
, ids_
.FromNumber(4), "gggchild");
2268 Entry
child(&trans
, GET_BY_ID
, child_id
);
2269 ASSERT_TRUE(child
.good());
2272 class EntryCreatedInNewFolderTest
: public SyncerTest
{
2274 void CreateFolderInBob() {
2275 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2276 MutableEntry
bob(&trans
,
2277 syncable::GET_BY_ID
,
2278 GetOnlyEntryWithName(&trans
,
2279 TestIdFactory::root(),
2283 MutableEntry
entry2(
2284 &trans
, CREATE
, BOOKMARKS
, bob
.GetId(), "bob");
2285 CHECK(entry2
.good());
2286 entry2
.PutIsDir(true);
2287 entry2
.PutIsUnsynced(true);
2288 entry2
.PutSpecifics(DefaultBookmarkSpecifics());
2292 TEST_F(EntryCreatedInNewFolderTest
, EntryCreatedInNewFolderMidSync
) {
2294 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2295 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2296 ASSERT_TRUE(entry
.good());
2297 entry
.PutIsDir(true);
2298 entry
.PutIsUnsynced(true);
2299 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2302 mock_server_
->SetMidCommitCallback(
2303 base::Bind(&EntryCreatedInNewFolderTest::CreateFolderInBob
,
2304 base::Unretained(this)));
2306 // We loop until no unsynced handles remain, so we will commit both ids.
2307 EXPECT_EQ(2u, mock_server_
->committed_ids().size());
2309 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2310 Entry
parent_entry(&trans
, syncable::GET_BY_ID
,
2311 GetOnlyEntryWithName(&trans
, TestIdFactory::root(), "bob"));
2312 ASSERT_TRUE(parent_entry
.good());
2315 GetOnlyEntryWithName(&trans
, parent_entry
.GetId(), "bob");
2316 Entry
child(&trans
, syncable::GET_BY_ID
, child_id
);
2317 ASSERT_TRUE(child
.good());
2318 EXPECT_EQ(parent_entry
.GetId(), child
.GetParentId());
2322 TEST_F(SyncerTest
, NegativeIDInUpdate
) {
2323 mock_server_
->AddUpdateBookmark(-10, 0, "bad", 40, 40,
2324 foreign_cache_guid(), "-100");
2326 // The negative id would make us CHECK!
2329 TEST_F(SyncerTest
, UnappliedUpdateOnCreatedItemItemDoesNotCrash
) {
2330 int64 metahandle_fred
;
2331 syncable::Id orig_id
;
2334 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2335 MutableEntry
fred_match(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(),
2337 ASSERT_TRUE(fred_match
.good());
2338 metahandle_fred
= fred_match
.GetMetahandle();
2339 orig_id
= fred_match
.GetId();
2340 WriteTestDataToEntry(&trans
, &fred_match
);
2344 EXPECT_EQ(1u, mock_server_
->committed_ids().size());
2345 mock_server_
->set_conflict_all_commits(true);
2346 syncable::Id fred_match_id
;
2348 // Now receive a change from outside.
2349 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2350 MutableEntry
fred_match(&trans
, GET_BY_HANDLE
, metahandle_fred
);
2351 ASSERT_TRUE(fred_match
.good());
2352 EXPECT_TRUE(fred_match
.GetId().ServerKnows());
2353 fred_match_id
= fred_match
.GetId();
2354 mock_server_
->AddUpdateBookmark(fred_match_id
, trans
.root_id(),
2355 "fred_match", 40, 40, local_cache_guid(), orig_id
.GetServerId());
2358 for (int i
= 0 ; i
< 30 ; ++i
) {
2364 * In the event that we have a double changed entry, that is changed on both
2365 * the client and the server, the conflict resolver should just drop one of
2366 * them and accept the other.
2369 TEST_F(SyncerTest
, DoublyChangedWithResolver
) {
2370 syncable::Id local_id
;
2372 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2373 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Folder");
2374 ASSERT_TRUE(parent
.good());
2375 parent
.PutIsDir(true);
2376 parent
.PutId(parent_id_
);
2377 parent
.PutBaseVersion(5);
2378 parent
.PutSpecifics(DefaultBookmarkSpecifics());
2379 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete.htm");
2380 ASSERT_TRUE(child
.good());
2381 local_id
= child
.GetId();
2382 child
.PutId(child_id_
);
2383 child
.PutBaseVersion(10);
2384 WriteTestDataToEntry(&wtrans
, &child
);
2386 mock_server_
->AddUpdateBookmark(child_id_
, parent_id_
, "Pete2.htm", 11, 10,
2387 local_cache_guid(), local_id
.GetServerId());
2388 mock_server_
->set_conflict_all_commits(true);
2390 syncable::Directory::Metahandles children
;
2392 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2393 directory()->GetChildHandlesById(&trans
, parent_id_
, &children
);
2394 // We expect the conflict resolver to preserve the local entry.
2395 Entry
child(&trans
, syncable::GET_BY_ID
, child_id_
);
2396 ASSERT_TRUE(child
.good());
2397 EXPECT_TRUE(child
.GetIsUnsynced());
2398 EXPECT_FALSE(child
.GetIsUnappliedUpdate());
2399 EXPECT_TRUE(child
.GetSpecifics().has_bookmark());
2400 EXPECT_EQ("Pete.htm", child
.GetNonUniqueName());
2401 VerifyTestBookmarkDataInEntry(&child
);
2404 // Only one entry, since we just overwrite one.
2405 EXPECT_EQ(1u, children
.size());
2406 saw_syncer_event_
= false;
2409 // We got this repro case when someone was editing bookmarks while sync was
2410 // occuring. The entry had changed out underneath the user.
2411 TEST_F(SyncerTest
, CommitsUpdateDoesntAlterEntry
) {
2412 const base::Time
& test_time
= ProtoTimeToTime(123456);
2413 syncable::Id local_id
;
2414 int64 entry_metahandle
;
2416 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2417 MutableEntry
entry(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Pete");
2418 ASSERT_TRUE(entry
.good());
2419 EXPECT_FALSE(entry
.GetId().ServerKnows());
2420 local_id
= entry
.GetId();
2421 entry
.PutIsDir(true);
2422 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2423 entry
.PutIsUnsynced(true);
2424 entry
.PutMtime(test_time
);
2425 entry_metahandle
= entry
.GetMetahandle();
2431 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2432 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, entry_metahandle
);
2433 ASSERT_TRUE(entry
.good());
2435 EXPECT_TRUE(id
.ServerKnows());
2436 version
= entry
.GetBaseVersion();
2438 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
2439 update
->set_originator_cache_guid(local_cache_guid());
2440 update
->set_originator_client_item_id(local_id
.GetServerId());
2441 EXPECT_EQ("Pete", update
->name());
2442 EXPECT_EQ(id
.GetServerId(), update
->id_string());
2443 EXPECT_EQ(root_id_
.GetServerId(), update
->parent_id_string());
2444 EXPECT_EQ(version
, update
->version());
2447 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2448 Entry
entry(&trans
, syncable::GET_BY_ID
, id
);
2449 ASSERT_TRUE(entry
.good());
2450 EXPECT_TRUE(entry
.GetMtime()== test_time
);
2454 TEST_F(SyncerTest
, ParentAndChildBothMatch
) {
2455 const FullModelTypeSet all_types
= FullModelTypeSet::All();
2456 syncable::Id parent_id
= ids_
.NewServerId();
2457 syncable::Id child_id
= ids_
.NewServerId();
2458 syncable::Id parent_local_id
;
2459 syncable::Id child_local_id
;
2463 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2464 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Folder");
2465 ASSERT_TRUE(parent
.good());
2466 parent_local_id
= parent
.GetId();
2467 parent
.PutIsDir(true);
2468 parent
.PutIsUnsynced(true);
2469 parent
.PutId(parent_id
);
2470 parent
.PutBaseVersion(1);
2471 parent
.PutSpecifics(DefaultBookmarkSpecifics());
2473 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent
.GetId(), "test.htm");
2474 ASSERT_TRUE(child
.good());
2475 child_local_id
= child
.GetId();
2476 child
.PutId(child_id
);
2477 child
.PutBaseVersion(1);
2478 child
.PutSpecifics(DefaultBookmarkSpecifics());
2479 WriteTestDataToEntry(&wtrans
, &child
);
2481 mock_server_
->AddUpdateDirectory(parent_id
, root_id_
, "Folder", 10, 10,
2483 parent_local_id
.GetServerId());
2484 mock_server_
->AddUpdateBookmark(child_id
, parent_id
, "test.htm", 10, 10,
2486 child_local_id
.GetServerId());
2487 mock_server_
->set_conflict_all_commits(true);
2492 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2493 Directory::Metahandles children
;
2494 directory()->GetChildHandlesById(&trans
, root_id_
, &children
);
2495 EXPECT_EQ(1u, children
.size());
2496 directory()->GetChildHandlesById(&trans
, parent_id
, &children
);
2497 EXPECT_EQ(1u, children
.size());
2498 std::vector
<int64
> unapplied
;
2499 directory()->GetUnappliedUpdateMetaHandles(&trans
, all_types
, &unapplied
);
2500 EXPECT_EQ(0u, unapplied
.size());
2501 syncable::Directory::Metahandles unsynced
;
2502 directory()->GetUnsyncedMetaHandles(&trans
, &unsynced
);
2503 EXPECT_EQ(0u, unsynced
.size());
2504 saw_syncer_event_
= false;
2508 TEST_F(SyncerTest
, CommittingNewDeleted
) {
2510 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2511 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2512 entry
.PutIsUnsynced(true);
2513 entry
.PutIsDel(true);
2516 EXPECT_EQ(0u, mock_server_
->committed_ids().size());
2519 // Original problem synopsis:
2520 // Check failed: entry->GetBaseVersion()<= entry->GetServerVersion()
2521 // Client creates entry, client finishes committing entry. Between
2522 // commit and getting update back, we delete the entry.
2523 // We get the update for the entry, but the local one was modified
2524 // so we store the entry but don't apply it. IS_UNAPPLIED_UPDATE is set.
2525 // We commit deletion and get a new version number.
2526 // We apply unapplied updates again before we get the update about the deletion.
2527 // This means we have an unapplied update where server_version < base_version.
2528 TEST_F(SyncerTest
, UnappliedUpdateDuringCommit
) {
2529 // This test is a little fake.
2531 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2532 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2533 entry
.PutId(ids_
.FromNumber(20));
2534 entry
.PutBaseVersion(1);
2535 entry
.PutServerVersion(1);
2536 entry
.PutServerParentId(ids_
.FromNumber(9999)); // Bad parent.
2537 entry
.PutIsUnsynced(true);
2538 entry
.PutIsUnappliedUpdate(true);
2539 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2540 entry
.PutServerSpecifics(DefaultBookmarkSpecifics());
2541 entry
.PutIsDel(false);
2544 EXPECT_EQ(1, session_
->status_controller().TotalNumConflictingItems());
2545 saw_syncer_event_
= false;
2548 // Original problem synopsis:
2550 // Unexpected error during sync if we:
2551 // make a new folder bob
2553 // make a new folder fred
2554 // move bob into fred
2557 // if no syncing occured midway, bob will have an illegal parent
2558 TEST_F(SyncerTest
, DeletingEntryInFolder
) {
2559 // This test is a little fake.
2560 int64 existing_metahandle
;
2562 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2563 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "existing");
2564 ASSERT_TRUE(entry
.good());
2565 entry
.PutIsDir(true);
2566 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2567 entry
.PutIsUnsynced(true);
2568 existing_metahandle
= entry
.GetMetahandle();
2572 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2573 MutableEntry
newfolder(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new");
2574 ASSERT_TRUE(newfolder
.good());
2575 newfolder
.PutIsDir(true);
2576 newfolder
.PutSpecifics(DefaultBookmarkSpecifics());
2577 newfolder
.PutIsUnsynced(true);
2579 MutableEntry
existing(&trans
, GET_BY_HANDLE
, existing_metahandle
);
2580 ASSERT_TRUE(existing
.good());
2581 existing
.PutParentId(newfolder
.GetId());
2582 existing
.PutIsUnsynced(true);
2583 EXPECT_TRUE(existing
.GetId().ServerKnows());
2585 newfolder
.PutIsDel(true);
2586 existing
.PutIsDel(true);
2589 EXPECT_EQ(0, GetCommitCounters(BOOKMARKS
).num_commits_conflict
);
2592 TEST_F(SyncerTest
, DeletingEntryWithLocalEdits
) {
2593 int64 newfolder_metahandle
;
2595 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
2596 foreign_cache_guid(), "-1");
2599 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2600 MutableEntry
newfolder(
2601 &trans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(1), "local");
2602 ASSERT_TRUE(newfolder
.good());
2603 newfolder
.PutIsUnsynced(true);
2604 newfolder
.PutIsDir(true);
2605 newfolder
.PutSpecifics(DefaultBookmarkSpecifics());
2606 newfolder_metahandle
= newfolder
.GetMetahandle();
2608 mock_server_
->AddUpdateDirectory(1, 0, "bob", 2, 20,
2609 foreign_cache_guid(), "-1");
2610 mock_server_
->SetLastUpdateDeleted();
2611 SyncShareConfigure();
2613 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2614 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, newfolder_metahandle
);
2615 ASSERT_TRUE(entry
.good());
2619 TEST_F(SyncerTest
, FolderSwapUpdate
) {
2620 mock_server_
->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2621 foreign_cache_guid(), "-7801");
2622 mock_server_
->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2623 foreign_cache_guid(), "-1024");
2625 mock_server_
->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2626 foreign_cache_guid(), "-1024");
2627 mock_server_
->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2628 foreign_cache_guid(), "-7801");
2631 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2632 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2633 ASSERT_TRUE(id1
.good());
2634 EXPECT_TRUE("fred" == id1
.GetNonUniqueName());
2635 EXPECT_TRUE(root_id_
== id1
.GetParentId());
2636 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2637 ASSERT_TRUE(id2
.good());
2638 EXPECT_TRUE("bob" == id2
.GetNonUniqueName());
2639 EXPECT_TRUE(root_id_
== id2
.GetParentId());
2641 saw_syncer_event_
= false;
2644 TEST_F(SyncerTest
, NameCollidingFolderSwapWorksFine
) {
2645 mock_server_
->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2646 foreign_cache_guid(), "-7801");
2647 mock_server_
->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2648 foreign_cache_guid(), "-1024");
2649 mock_server_
->AddUpdateDirectory(4096, 0, "alice", 1, 10,
2650 foreign_cache_guid(), "-4096");
2653 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2654 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2655 ASSERT_TRUE(id1
.good());
2656 EXPECT_TRUE("bob" == id1
.GetNonUniqueName());
2657 EXPECT_TRUE(root_id_
== id1
.GetParentId());
2658 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2659 ASSERT_TRUE(id2
.good());
2660 EXPECT_TRUE("fred" == id2
.GetNonUniqueName());
2661 EXPECT_TRUE(root_id_
== id2
.GetParentId());
2662 Entry
id3(&trans
, GET_BY_ID
, ids_
.FromNumber(4096));
2663 ASSERT_TRUE(id3
.good());
2664 EXPECT_TRUE("alice" == id3
.GetNonUniqueName());
2665 EXPECT_TRUE(root_id_
== id3
.GetParentId());
2667 mock_server_
->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2668 foreign_cache_guid(), "-1024");
2669 mock_server_
->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2670 foreign_cache_guid(), "-7801");
2671 mock_server_
->AddUpdateDirectory(4096, 0, "bob", 2, 20,
2672 foreign_cache_guid(), "-4096");
2675 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2676 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2677 ASSERT_TRUE(id1
.good());
2678 EXPECT_TRUE("fred" == id1
.GetNonUniqueName());
2679 EXPECT_TRUE(root_id_
== id1
.GetParentId());
2680 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2681 ASSERT_TRUE(id2
.good());
2682 EXPECT_TRUE("bob" == id2
.GetNonUniqueName());
2683 EXPECT_TRUE(root_id_
== id2
.GetParentId());
2684 Entry
id3(&trans
, GET_BY_ID
, ids_
.FromNumber(4096));
2685 ASSERT_TRUE(id3
.good());
2686 EXPECT_TRUE("bob" == id3
.GetNonUniqueName());
2687 EXPECT_TRUE(root_id_
== id3
.GetParentId());
2689 saw_syncer_event_
= false;
2692 // Committing more than kDefaultMaxCommitBatchSize items requires that
2693 // we post more than one commit command to the server. This test makes
2694 // sure that scenario works as expected.
2695 TEST_F(SyncerTest
, CommitManyItemsInOneGo_Success
) {
2696 uint32 num_batches
= 3;
2697 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2699 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2700 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2701 string nameutf8
= base::StringPrintf("%d", i
);
2702 string
name(nameutf8
.begin(), nameutf8
.end());
2703 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2704 e
.PutIsUnsynced(true);
2706 e
.PutSpecifics(DefaultBookmarkSpecifics());
2709 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2712 EXPECT_EQ(num_batches
, mock_server_
->commit_messages().size());
2713 EXPECT_EQ(0, directory()->unsynced_entity_count());
2716 // Test that a single failure to contact the server will cause us to exit the
2717 // commit loop immediately.
2718 TEST_F(SyncerTest
, CommitManyItemsInOneGo_PostBufferFail
) {
2719 uint32 num_batches
= 3;
2720 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2722 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2723 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2724 string nameutf8
= base::StringPrintf("%d", i
);
2725 string
name(nameutf8
.begin(), nameutf8
.end());
2726 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2727 e
.PutIsUnsynced(true);
2729 e
.PutSpecifics(DefaultBookmarkSpecifics());
2732 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2734 // The second commit should fail. It will be preceded by one successful
2735 // GetUpdate and one succesful commit.
2736 mock_server_
->FailNthPostBufferToPathCall(3);
2739 EXPECT_EQ(1U, mock_server_
->commit_messages().size());
2740 EXPECT_EQ(SYNC_SERVER_ERROR
,
2741 session_
->status_controller().model_neutral_state().commit_result
);
2742 EXPECT_EQ(items_to_commit
- kDefaultMaxCommitBatchSize
,
2743 directory()->unsynced_entity_count());
2746 // Test that a single conflict response from the server will cause us to exit
2747 // the commit loop immediately.
2748 TEST_F(SyncerTest
, CommitManyItemsInOneGo_CommitConflict
) {
2749 uint32 num_batches
= 2;
2750 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2752 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2753 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2754 string nameutf8
= base::StringPrintf("%d", i
);
2755 string
name(nameutf8
.begin(), nameutf8
.end());
2756 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2757 e
.PutIsUnsynced(true);
2759 e
.PutSpecifics(DefaultBookmarkSpecifics());
2762 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2764 // Return a CONFLICT response for the first item.
2765 mock_server_
->set_conflict_n_commits(1);
2768 // We should stop looping at the first sign of trouble.
2769 EXPECT_EQ(1U, mock_server_
->commit_messages().size());
2770 EXPECT_EQ(items_to_commit
- (kDefaultMaxCommitBatchSize
- 1),
2771 directory()->unsynced_entity_count());
2774 // Tests that sending debug info events works.
2775 TEST_F(SyncerTest
, SendDebugInfoEventsOnGetUpdates_HappyCase
) {
2776 debug_info_getter_
->AddDebugEvent();
2777 debug_info_getter_
->AddDebugEvent();
2781 // Verify we received one GetUpdates request with two debug info events.
2782 EXPECT_EQ(1U, mock_server_
->requests().size());
2783 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2784 EXPECT_EQ(2, mock_server_
->last_request().debug_info().events_size());
2788 // See that we received another GetUpdates request, but that it contains no
2789 // debug info events.
2790 EXPECT_EQ(2U, mock_server_
->requests().size());
2791 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2792 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
2794 debug_info_getter_
->AddDebugEvent();
2798 // See that we received another GetUpdates request and it contains one debug
2800 EXPECT_EQ(3U, mock_server_
->requests().size());
2801 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2802 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
2805 // Tests that debug info events are dropped on server error.
2806 TEST_F(SyncerTest
, SendDebugInfoEventsOnGetUpdates_PostFailsDontDrop
) {
2807 debug_info_getter_
->AddDebugEvent();
2808 debug_info_getter_
->AddDebugEvent();
2810 mock_server_
->FailNextPostBufferToPathCall();
2813 // Verify we attempted to send one GetUpdates request with two debug info
2815 EXPECT_EQ(1U, mock_server_
->requests().size());
2816 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2817 EXPECT_EQ(2, mock_server_
->last_request().debug_info().events_size());
2821 // See that the client resent the two debug info events.
2822 EXPECT_EQ(2U, mock_server_
->requests().size());
2823 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2824 EXPECT_EQ(2, mock_server_
->last_request().debug_info().events_size());
2826 // The previous send was successful so this next one shouldn't generate any
2827 // debug info events.
2829 EXPECT_EQ(3U, mock_server_
->requests().size());
2830 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2831 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
2834 // Tests that sending debug info events on Commit works.
2835 TEST_F(SyncerTest
, SendDebugInfoEventsOnCommit_HappyCase
) {
2836 // Make sure GetUpdate isn't call as it would "steal" debug info events before
2837 // Commit has a chance to send them.
2838 ConfigureNoGetUpdatesRequired();
2840 // Generate a debug info event and trigger a commit.
2841 debug_info_getter_
->AddDebugEvent();
2842 CreateUnsyncedDirectory("X", "id_X");
2845 // Verify that the last request received is a Commit and that it contains a
2846 // debug info event.
2847 EXPECT_EQ(1U, mock_server_
->requests().size());
2848 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2849 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
2851 // Generate another commit, but no debug info event.
2852 CreateUnsyncedDirectory("Y", "id_Y");
2855 // See that it was received and contains no debug info events.
2856 EXPECT_EQ(2U, mock_server_
->requests().size());
2857 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2858 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
2861 // Tests that debug info events are not dropped on server error.
2862 TEST_F(SyncerTest
, SendDebugInfoEventsOnCommit_PostFailsDontDrop
) {
2863 // Make sure GetUpdate isn't call as it would "steal" debug info events before
2864 // Commit has a chance to send them.
2865 ConfigureNoGetUpdatesRequired();
2867 mock_server_
->FailNextPostBufferToPathCall();
2869 // Generate a debug info event and trigger a commit.
2870 debug_info_getter_
->AddDebugEvent();
2871 CreateUnsyncedDirectory("X", "id_X");
2874 // Verify that the last request sent is a Commit and that it contains a debug
2876 EXPECT_EQ(1U, mock_server_
->requests().size());
2877 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2878 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
2883 // Verify that we've received another Commit and that it contains a debug info
2884 // event (just like the previous one).
2885 EXPECT_EQ(2U, mock_server_
->requests().size());
2886 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2887 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
2889 // Generate another commit and try again.
2890 CreateUnsyncedDirectory("Y", "id_Y");
2893 // See that it was received and contains no debug info events.
2894 EXPECT_EQ(3U, mock_server_
->requests().size());
2895 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2896 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
2899 TEST_F(SyncerTest
, HugeConflict
) {
2900 int item_count
= 300; // We should be able to do 300 or 3000 w/o issue.
2902 syncable::Id parent_id
= ids_
.NewServerId();
2903 syncable::Id last_id
= parent_id
;
2904 vector
<syncable::Id
> tree_ids
;
2906 // Create a lot of updates for which the parent does not exist yet.
2907 // Generate a huge deep tree which should all fail to apply at first.
2909 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2910 for (int i
= 0; i
< item_count
; i
++) {
2911 syncable::Id next_id
= ids_
.NewServerId();
2912 syncable::Id local_id
= ids_
.NewLocalId();
2913 tree_ids
.push_back(next_id
);
2914 mock_server_
->AddUpdateDirectory(next_id
, last_id
, "BOB", 2, 20,
2915 foreign_cache_guid(),
2916 local_id
.GetServerId());
2922 // Check they're in the expected conflict state.
2924 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2925 for (int i
= 0; i
< item_count
; i
++) {
2926 Entry
e(&trans
, GET_BY_ID
, tree_ids
[i
]);
2927 // They should all exist but none should be applied.
2928 ASSERT_TRUE(e
.good());
2929 EXPECT_TRUE(e
.GetIsDel());
2930 EXPECT_TRUE(e
.GetIsUnappliedUpdate());
2934 // Add the missing parent directory.
2935 mock_server_
->AddUpdateDirectory(parent_id
, TestIdFactory::root(),
2936 "BOB", 2, 20, foreign_cache_guid(), "-3500");
2939 // Now they should all be OK.
2941 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2942 for (int i
= 0; i
< item_count
; i
++) {
2943 Entry
e(&trans
, GET_BY_ID
, tree_ids
[i
]);
2944 ASSERT_TRUE(e
.good());
2945 EXPECT_FALSE(e
.GetIsDel());
2946 EXPECT_FALSE(e
.GetIsUnappliedUpdate());
2951 TEST_F(SyncerTest
, DontCrashOnCaseChange
) {
2952 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
2953 foreign_cache_guid(), "-1");
2956 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2957 MutableEntry
e(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2958 ASSERT_TRUE(e
.good());
2959 e
.PutIsUnsynced(true);
2961 mock_server_
->set_conflict_all_commits(true);
2962 mock_server_
->AddUpdateDirectory(1, 0, "BOB", 2, 20,
2963 foreign_cache_guid(), "-1");
2964 SyncShareNudge(); // USED TO CAUSE AN ASSERT
2965 saw_syncer_event_
= false;
2968 TEST_F(SyncerTest
, UnsyncedItemAndUpdate
) {
2969 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
2970 foreign_cache_guid(), "-1");
2972 mock_server_
->set_conflict_all_commits(true);
2973 mock_server_
->AddUpdateDirectory(2, 0, "bob", 2, 20,
2974 foreign_cache_guid(), "-2");
2975 SyncShareNudge(); // USED TO CAUSE AN ASSERT
2976 saw_syncer_event_
= false;
2979 TEST_F(SyncerTest
, NewEntryAndAlteredServerEntrySharePath
) {
2980 mock_server_
->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
2981 foreign_cache_guid(), "-1");
2983 int64 local_folder_handle
;
2984 syncable::Id local_folder_id
;
2986 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2987 MutableEntry
new_entry(
2988 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bar.htm");
2989 ASSERT_TRUE(new_entry
.good());
2990 local_folder_id
= new_entry
.GetId();
2991 local_folder_handle
= new_entry
.GetMetahandle();
2992 new_entry
.PutIsUnsynced(true);
2993 new_entry
.PutSpecifics(DefaultBookmarkSpecifics());
2994 MutableEntry
old(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2995 ASSERT_TRUE(old
.good());
2996 WriteTestDataToEntry(&wtrans
, &old
);
2998 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
2999 foreign_cache_guid(), "-1");
3000 mock_server_
->set_conflict_all_commits(true);
3002 saw_syncer_event_
= false;
3004 // Update #20 should have been dropped in favor of the local version.
3005 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3006 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3007 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3008 ASSERT_TRUE(server
.good());
3009 ASSERT_TRUE(local
.good());
3010 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3011 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3012 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3013 EXPECT_TRUE(server
.GetIsUnsynced());
3014 EXPECT_TRUE(local
.GetIsUnsynced());
3015 EXPECT_EQ("Foo.htm", server
.GetNonUniqueName());
3016 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3018 // Allow local changes to commit.
3019 mock_server_
->set_conflict_all_commits(false);
3021 saw_syncer_event_
= false;
3023 // Now add a server change to make the two names equal. There should
3024 // be no conflict with that, since names are not unique.
3025 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
3026 foreign_cache_guid(), "-1");
3028 saw_syncer_event_
= false;
3030 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3031 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3032 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3033 ASSERT_TRUE(server
.good());
3034 ASSERT_TRUE(local
.good());
3035 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3036 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3037 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3038 EXPECT_FALSE(server
.GetIsUnsynced());
3039 EXPECT_FALSE(local
.GetIsUnsynced());
3040 EXPECT_EQ("Bar.htm", server
.GetNonUniqueName());
3041 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3042 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
3043 server
.GetSpecifics().bookmark().url());
3047 // Same as NewEntryAnddServerEntrySharePath, but using the old-style protocol.
3048 TEST_F(SyncerTest
, NewEntryAndAlteredServerEntrySharePath_OldBookmarksProto
) {
3049 mock_server_
->set_use_legacy_bookmarks_protocol(true);
3050 mock_server_
->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
3051 foreign_cache_guid(), "-1");
3053 int64 local_folder_handle
;
3054 syncable::Id local_folder_id
;
3056 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3057 MutableEntry
new_entry(
3058 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bar.htm");
3059 ASSERT_TRUE(new_entry
.good());
3060 local_folder_id
= new_entry
.GetId();
3061 local_folder_handle
= new_entry
.GetMetahandle();
3062 new_entry
.PutIsUnsynced(true);
3063 new_entry
.PutSpecifics(DefaultBookmarkSpecifics());
3064 MutableEntry
old(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3065 ASSERT_TRUE(old
.good());
3066 WriteTestDataToEntry(&wtrans
, &old
);
3068 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
3069 foreign_cache_guid(), "-1");
3070 mock_server_
->set_conflict_all_commits(true);
3072 saw_syncer_event_
= false;
3074 // Update #20 should have been dropped in favor of the local version.
3075 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3076 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3077 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3078 ASSERT_TRUE(server
.good());
3079 ASSERT_TRUE(local
.good());
3080 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3081 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3082 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3083 EXPECT_TRUE(server
.GetIsUnsynced());
3084 EXPECT_TRUE(local
.GetIsUnsynced());
3085 EXPECT_EQ("Foo.htm", server
.GetNonUniqueName());
3086 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3088 // Allow local changes to commit.
3089 mock_server_
->set_conflict_all_commits(false);
3091 saw_syncer_event_
= false;
3093 // Now add a server change to make the two names equal. There should
3094 // be no conflict with that, since names are not unique.
3095 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
3096 foreign_cache_guid(), "-1");
3098 saw_syncer_event_
= false;
3100 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3101 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3102 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3103 ASSERT_TRUE(server
.good());
3104 ASSERT_TRUE(local
.good());
3105 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3106 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3107 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3108 EXPECT_FALSE(server
.GetIsUnsynced());
3109 EXPECT_FALSE(local
.GetIsUnsynced());
3110 EXPECT_EQ("Bar.htm", server
.GetNonUniqueName());
3111 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3112 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
3113 server
.GetSpecifics().bookmark().url());
3117 // Circular links should be resolved by the server.
3118 TEST_F(SyncerTest
, SiblingDirectoriesBecomeCircular
) {
3119 // we don't currently resolve this. This test ensures we don't.
3120 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
3121 foreign_cache_guid(), "-1");
3122 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
3123 foreign_cache_guid(), "-2");
3126 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3127 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3128 ASSERT_TRUE(A
.good());
3129 A
.PutIsUnsynced(true);
3130 A
.PutParentId(ids_
.FromNumber(2));
3131 A
.PutNonUniqueName("B");
3133 mock_server_
->AddUpdateDirectory(2, 1, "A", 20, 20,
3134 foreign_cache_guid(), "-2");
3135 mock_server_
->set_conflict_all_commits(true);
3137 saw_syncer_event_
= false;
3139 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3140 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3141 ASSERT_TRUE(A
.good());
3142 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
3143 ASSERT_TRUE(B
.good());
3144 EXPECT_TRUE(A
.GetNonUniqueName()== "B");
3145 EXPECT_TRUE(B
.GetNonUniqueName()== "B");
3149 TEST_F(SyncerTest
, SwapEntryNames
) {
3150 // Simple transaction test.
3151 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
3152 foreign_cache_guid(), "-1");
3153 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
3154 foreign_cache_guid(), "-2");
3155 mock_server_
->set_conflict_all_commits(true);
3158 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3159 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3160 ASSERT_TRUE(A
.good());
3161 A
.PutIsUnsynced(true);
3162 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
3163 ASSERT_TRUE(B
.good());
3164 B
.PutIsUnsynced(true);
3165 A
.PutNonUniqueName("C");
3166 B
.PutNonUniqueName("A");
3167 A
.PutNonUniqueName("B");
3170 saw_syncer_event_
= false;
3173 TEST_F(SyncerTest
, DualDeletionWithNewItemNameClash
) {
3174 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
3175 foreign_cache_guid(), "-1");
3176 mock_server_
->AddUpdateBookmark(2, 0, "B", 10, 10,
3177 foreign_cache_guid(), "-2");
3178 mock_server_
->set_conflict_all_commits(true);
3181 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3182 MutableEntry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3183 ASSERT_TRUE(B
.good());
3184 WriteTestDataToEntry(&trans
, &B
);
3187 mock_server_
->AddUpdateBookmark(2, 0, "A", 11, 11,
3188 foreign_cache_guid(), "-2");
3189 mock_server_
->SetLastUpdateDeleted();
3192 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3193 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3194 ASSERT_TRUE(B
.good());
3195 EXPECT_FALSE(B
.GetIsUnsynced());
3196 EXPECT_FALSE(B
.GetIsUnappliedUpdate());
3198 saw_syncer_event_
= false;
3201 // When we undelete an entity as a result of conflict resolution, we reuse the
3202 // existing server id and preserve the old version, simply updating the server
3203 // version with the new non-deleted entity.
3204 TEST_F(SyncerTest
, ResolveWeWroteTheyDeleted
) {
3205 int64 bob_metahandle
;
3207 mock_server_
->AddUpdateBookmark(1, 0, "bob", 1, 10,
3208 foreign_cache_guid(), "-1");
3211 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3212 MutableEntry
bob(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3213 ASSERT_TRUE(bob
.good());
3214 bob_metahandle
= bob
.GetMetahandle();
3215 WriteTestDataToEntry(&trans
, &bob
);
3217 mock_server_
->AddUpdateBookmark(1, 0, "bob", 2, 10,
3218 foreign_cache_guid(), "-1");
3219 mock_server_
->SetLastUpdateDeleted();
3220 mock_server_
->set_conflict_all_commits(true);
3224 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3225 Entry
bob(&trans
, GET_BY_HANDLE
, bob_metahandle
);
3226 ASSERT_TRUE(bob
.good());
3227 EXPECT_TRUE(bob
.GetIsUnsynced());
3228 EXPECT_TRUE(bob
.GetId().ServerKnows());
3229 EXPECT_FALSE(bob
.GetIsUnappliedUpdate());
3230 EXPECT_FALSE(bob
.GetIsDel());
3231 EXPECT_EQ(2, bob
.GetServerVersion());
3232 EXPECT_EQ(2, bob
.GetBaseVersion());
3234 saw_syncer_event_
= false;
3237 // This test is to reproduce a check failure. Sometimes we would get a bad ID
3238 // back when creating an entry.
3239 TEST_F(SyncerTest
, DuplicateIDReturn
) {
3241 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3242 MutableEntry
folder(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
3243 ASSERT_TRUE(folder
.good());
3244 folder
.PutIsUnsynced(true);
3245 folder
.PutIsDir(true);
3246 folder
.PutSpecifics(DefaultBookmarkSpecifics());
3247 MutableEntry
folder2(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "fred");
3248 ASSERT_TRUE(folder2
.good());
3249 folder2
.PutIsUnsynced(false);
3250 folder2
.PutIsDir(true);
3251 folder2
.PutSpecifics(DefaultBookmarkSpecifics());
3252 folder2
.PutBaseVersion(3);
3253 folder2
.PutId(syncable::Id::CreateFromServerId("mock_server:10000"));
3255 mock_server_
->set_next_new_id(10000);
3256 EXPECT_EQ(1u, directory()->unsynced_entity_count());
3257 // we get back a bad id in here (should never happen).
3259 EXPECT_EQ(1u, directory()->unsynced_entity_count());
3260 SyncShareNudge(); // another bad id in here.
3261 EXPECT_EQ(0u, directory()->unsynced_entity_count());
3262 saw_syncer_event_
= false;
3265 TEST_F(SyncerTest
, DeletedEntryWithBadParentInLoopCalculation
) {
3266 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
3267 foreign_cache_guid(), "-1");
3270 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3271 MutableEntry
bob(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3272 ASSERT_TRUE(bob
.good());
3273 // This is valid, because the parent could have gone away a long time ago.
3274 bob
.PutParentId(ids_
.FromNumber(54));
3276 bob
.PutIsUnsynced(true);
3278 mock_server_
->AddUpdateDirectory(2, 1, "fred", 1, 10,
3279 foreign_cache_guid(), "-2");
3284 TEST_F(SyncerTest
, ConflictResolverMergesLocalDeleteAndServerUpdate
) {
3285 syncable::Id local_id
;
3287 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3289 MutableEntry
local_deleted(
3290 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "name");
3291 local_id
= local_deleted
.GetId();
3292 local_deleted
.PutId(ids_
.FromNumber(1));
3293 local_deleted
.PutBaseVersion(1);
3294 local_deleted
.PutIsDel(true);
3295 local_deleted
.PutIsDir(false);
3296 local_deleted
.PutIsUnsynced(true);
3297 local_deleted
.PutSpecifics(DefaultBookmarkSpecifics());
3300 mock_server_
->AddUpdateBookmark(ids_
.FromNumber(1), root_id_
, "name", 10, 10,
3302 local_id
.GetServerId());
3304 // We don't care about actually committing, just the resolution.
3305 mock_server_
->set_conflict_all_commits(true);
3309 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3310 Entry
local_deleted(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3311 EXPECT_TRUE(local_deleted
.GetBaseVersion()== 10);
3312 EXPECT_TRUE(local_deleted
.GetIsUnappliedUpdate()== false);
3313 EXPECT_TRUE(local_deleted
.GetIsUnsynced()== true);
3314 EXPECT_TRUE(local_deleted
.GetIsDel()== true);
3315 EXPECT_TRUE(local_deleted
.GetIsDir()== false);
3319 // See what happens if the IS_DIR bit gets flipped. This can cause us
3320 // all kinds of disasters.
3321 TEST_F(SyncerTest
, UpdateFlipsTheFolderBit
) {
3322 // Local object: a deleted directory (container), revision 1, unsynced.
3324 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3326 MutableEntry
local_deleted(
3327 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "name");
3328 local_deleted
.PutId(ids_
.FromNumber(1));
3329 local_deleted
.PutBaseVersion(1);
3330 local_deleted
.PutIsDel(true);
3331 local_deleted
.PutIsDir(true);
3332 local_deleted
.PutIsUnsynced(true);
3333 local_deleted
.PutSpecifics(DefaultBookmarkSpecifics());
3336 // Server update: entry-type object (not a container), revision 10.
3337 mock_server_
->AddUpdateBookmark(ids_
.FromNumber(1), root_id_
, "name", 10, 10,
3339 ids_
.FromNumber(1).GetServerId());
3341 // Don't attempt to commit.
3342 mock_server_
->set_conflict_all_commits(true);
3344 // The syncer should not attempt to apply the invalid update.
3348 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3349 Entry
local_deleted(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3350 EXPECT_TRUE(local_deleted
.GetBaseVersion()== 1);
3351 EXPECT_TRUE(local_deleted
.GetIsUnappliedUpdate()== false);
3352 EXPECT_TRUE(local_deleted
.GetIsUnsynced()== true);
3353 EXPECT_TRUE(local_deleted
.GetIsDel()== true);
3354 EXPECT_TRUE(local_deleted
.GetIsDir()== true);
3359 // Merge conflict resolution will merge a new local entry with another entry
3360 // that needs updates, resulting in CHECK.
3361 TEST_F(SyncerTest
, MergingExistingItems
) {
3362 mock_server_
->set_conflict_all_commits(true);
3363 mock_server_
->AddUpdateBookmark(1, 0, "base", 10, 10,
3364 local_cache_guid(), "-1");
3367 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3369 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "Copy of base");
3370 WriteTestDataToEntry(&trans
, &entry
);
3372 mock_server_
->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
3373 local_cache_guid(), "-1");
3377 // In this test a long changelog contains a child at the start of the changelog
3378 // and a parent at the end. While these updates are in progress the client would
3380 TEST_F(SyncerTest
, LongChangelistWithApplicationConflict
) {
3381 const int depth
= 400;
3382 syncable::Id folder_id
= ids_
.FromNumber(1);
3384 // First we an item in a folder in the root. However the folder won't come
3386 syncable::Id stuck_entry_id
= TestIdFactory::FromNumber(99999);
3387 mock_server_
->AddUpdateDirectory(stuck_entry_id
,
3388 folder_id
, "stuck", 1, 1,
3389 foreign_cache_guid(), "-99999");
3390 mock_server_
->SetChangesRemaining(depth
- 1);
3393 // Buffer up a very long series of downloads.
3394 // We should never be stuck (conflict resolution shouldn't
3395 // kick in so long as we're making forward progress).
3396 for (int i
= 0; i
< depth
; i
++) {
3397 mock_server_
->NextUpdateBatch();
3398 mock_server_
->SetNewTimestamp(i
+ 1);
3399 mock_server_
->SetChangesRemaining(depth
- i
);
3404 // Ensure our folder hasn't somehow applied.
3406 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3407 Entry
child(&trans
, GET_BY_ID
, stuck_entry_id
);
3408 EXPECT_TRUE(child
.good());
3409 EXPECT_TRUE(child
.GetIsUnappliedUpdate());
3410 EXPECT_TRUE(child
.GetIsDel());
3411 EXPECT_FALSE(child
.GetIsUnsynced());
3414 // And finally the folder.
3415 mock_server_
->AddUpdateDirectory(folder_id
,
3416 TestIdFactory::root(), "folder", 1, 1,
3417 foreign_cache_guid(), "-1");
3418 mock_server_
->SetChangesRemaining(0);
3421 // Check that everything is as expected after the commit.
3423 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3424 Entry
entry(&trans
, GET_BY_ID
, folder_id
);
3425 ASSERT_TRUE(entry
.good());
3426 Entry
child(&trans
, GET_BY_ID
, stuck_entry_id
);
3427 EXPECT_EQ(entry
.GetId(), child
.GetParentId());
3428 EXPECT_EQ("stuck", child
.GetNonUniqueName());
3429 EXPECT_TRUE(child
.good());
3433 TEST_F(SyncerTest
, DontMergeTwoExistingItems
) {
3434 mock_server_
->set_conflict_all_commits(true);
3435 mock_server_
->AddUpdateBookmark(1, 0, "base", 10, 10,
3436 foreign_cache_guid(), "-1");
3437 mock_server_
->AddUpdateBookmark(2, 0, "base2", 10, 10,
3438 foreign_cache_guid(), "-2");
3441 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3442 MutableEntry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3443 ASSERT_TRUE(entry
.good());
3444 entry
.PutNonUniqueName("Copy of base");
3445 entry
.PutIsUnsynced(true);
3447 mock_server_
->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
3448 foreign_cache_guid(), "-1");
3451 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3452 Entry
entry1(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3453 EXPECT_FALSE(entry1
.GetIsUnappliedUpdate());
3454 EXPECT_FALSE(entry1
.GetIsUnsynced());
3455 EXPECT_FALSE(entry1
.GetIsDel());
3456 Entry
entry2(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3457 EXPECT_FALSE(entry2
.GetIsUnappliedUpdate());
3458 EXPECT_TRUE(entry2
.GetIsUnsynced());
3459 EXPECT_FALSE(entry2
.GetIsDel());
3460 EXPECT_EQ(entry1
.GetNonUniqueName(), entry2
.GetNonUniqueName());
3464 TEST_F(SyncerTest
, TestUndeleteUpdate
) {
3465 mock_server_
->set_conflict_all_commits(true);
3466 mock_server_
->AddUpdateDirectory(1, 0, "foo", 1, 1,
3467 foreign_cache_guid(), "-1");
3468 mock_server_
->AddUpdateDirectory(2, 1, "bar", 1, 2,
3469 foreign_cache_guid(), "-2");
3471 mock_server_
->AddUpdateDirectory(2, 1, "bar", 2, 3,
3472 foreign_cache_guid(), "-2");
3473 mock_server_
->SetLastUpdateDeleted();
3478 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3479 Entry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3480 ASSERT_TRUE(entry
.good());
3481 EXPECT_TRUE(entry
.GetIsDel());
3482 metahandle
= entry
.GetMetahandle();
3484 mock_server_
->AddUpdateDirectory(1, 0, "foo", 2, 4,
3485 foreign_cache_guid(), "-1");
3486 mock_server_
->SetLastUpdateDeleted();
3488 // This used to be rejected as it's an undeletion. Now, it results in moving
3489 // the delete path aside.
3490 mock_server_
->AddUpdateDirectory(2, 1, "bar", 3, 5,
3491 foreign_cache_guid(), "-2");
3494 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3495 Entry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3496 ASSERT_TRUE(entry
.good());
3497 EXPECT_TRUE(entry
.GetIsDel());
3498 EXPECT_FALSE(entry
.GetServerIsDel());
3499 EXPECT_TRUE(entry
.GetIsUnappliedUpdate());
3500 EXPECT_NE(entry
.GetMetahandle(), metahandle
);
3504 TEST_F(SyncerTest
, TestMoveSanitizedNamedFolder
) {
3505 mock_server_
->AddUpdateDirectory(1, 0, "foo", 1, 1,
3506 foreign_cache_guid(), "-1");
3507 mock_server_
->AddUpdateDirectory(2, 0, ":::", 1, 2,
3508 foreign_cache_guid(), "-2");
3511 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3512 MutableEntry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3513 ASSERT_TRUE(entry
.good());
3514 entry
.PutParentId(ids_
.FromNumber(1));
3515 EXPECT_TRUE(entry
.PutIsUnsynced(true));
3518 // We use the same sync ts as before so our times match up.
3519 mock_server_
->AddUpdateDirectory(2, 1, ":::", 2, 2,
3520 foreign_cache_guid(), "-2");
3524 // Don't crash when this occurs.
3525 TEST_F(SyncerTest
, UpdateWhereParentIsNotAFolder
) {
3526 mock_server_
->AddUpdateBookmark(1, 0, "B", 10, 10,
3527 foreign_cache_guid(), "-1");
3528 mock_server_
->AddUpdateDirectory(2, 1, "BookmarkParent", 10, 10,
3529 foreign_cache_guid(), "-2");
3530 // Used to cause a CHECK
3533 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
3534 Entry
good_entry(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
3535 ASSERT_TRUE(good_entry
.good());
3536 EXPECT_FALSE(good_entry
.GetIsUnappliedUpdate());
3537 Entry
bad_parent(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(2));
3538 ASSERT_TRUE(bad_parent
.good());
3539 EXPECT_TRUE(bad_parent
.GetIsUnappliedUpdate());
3543 TEST_F(SyncerTest
, DirectoryUpdateTest
) {
3544 Id in_root_id
= ids_
.NewServerId();
3545 Id in_in_root_id
= ids_
.NewServerId();
3547 mock_server_
->AddUpdateDirectory(in_root_id
, TestIdFactory::root(),
3548 "in_root_name", 2, 2,
3549 foreign_cache_guid(), "-1");
3550 mock_server_
->AddUpdateDirectory(in_in_root_id
, in_root_id
,
3551 "in_in_root_name", 3, 3,
3552 foreign_cache_guid(), "-2");
3555 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3556 Entry
in_root(&trans
, GET_BY_ID
, in_root_id
);
3557 ASSERT_TRUE(in_root
.good());
3558 EXPECT_EQ("in_root_name", in_root
.GetNonUniqueName());
3559 EXPECT_EQ(TestIdFactory::root(), in_root
.GetParentId());
3561 Entry
in_in_root(&trans
, GET_BY_ID
, in_in_root_id
);
3562 ASSERT_TRUE(in_in_root
.good());
3563 EXPECT_EQ("in_in_root_name", in_in_root
.GetNonUniqueName());
3564 EXPECT_EQ(in_root_id
, in_in_root
.GetParentId());
3568 TEST_F(SyncerTest
, DirectoryCommitTest
) {
3569 syncable::Id in_root_id
, in_dir_id
;
3570 int64 foo_metahandle
;
3571 int64 bar_metahandle
;
3574 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3575 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "foo");
3576 ASSERT_TRUE(parent
.good());
3577 parent
.PutIsUnsynced(true);
3578 parent
.PutIsDir(true);
3579 parent
.PutSpecifics(DefaultBookmarkSpecifics());
3580 in_root_id
= parent
.GetId();
3581 foo_metahandle
= parent
.GetMetahandle();
3583 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent
.GetId(), "bar");
3584 ASSERT_TRUE(child
.good());
3585 child
.PutIsUnsynced(true);
3586 child
.PutIsDir(true);
3587 child
.PutSpecifics(DefaultBookmarkSpecifics());
3588 bar_metahandle
= child
.GetMetahandle();
3589 in_dir_id
= parent
.GetId();
3593 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3594 Entry
fail_by_old_id_entry(&trans
, GET_BY_ID
, in_root_id
);
3595 ASSERT_FALSE(fail_by_old_id_entry
.good());
3597 Entry
foo_entry(&trans
, GET_BY_HANDLE
, foo_metahandle
);
3598 ASSERT_TRUE(foo_entry
.good());
3599 EXPECT_EQ("foo", foo_entry
.GetNonUniqueName());
3600 EXPECT_NE(foo_entry
.GetId(), in_root_id
);
3602 Entry
bar_entry(&trans
, GET_BY_HANDLE
, bar_metahandle
);
3603 ASSERT_TRUE(bar_entry
.good());
3604 EXPECT_EQ("bar", bar_entry
.GetNonUniqueName());
3605 EXPECT_NE(bar_entry
.GetId(), in_dir_id
);
3606 EXPECT_EQ(foo_entry
.GetId(), bar_entry
.GetParentId());
3610 TEST_F(SyncerTest
, TestClientCommandDuringUpdate
) {
3611 using sync_pb::ClientCommand
;
3613 ClientCommand
* command
= new ClientCommand();
3614 command
->set_set_sync_poll_interval(8);
3615 command
->set_set_sync_long_poll_interval(800);
3616 command
->set_sessions_commit_delay_seconds(3141);
3617 sync_pb::CustomNudgeDelay
* bookmark_delay
=
3618 command
->add_custom_nudge_delays();
3619 bookmark_delay
->set_datatype_id(
3620 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3621 bookmark_delay
->set_delay_ms(950);
3622 command
->set_client_invalidation_hint_buffer_size(11);
3623 mock_server_
->AddUpdateDirectory(1, 0, "in_root", 1, 1,
3624 foreign_cache_guid(), "-1");
3625 mock_server_
->SetGUClientCommand(command
);
3628 EXPECT_EQ(TimeDelta::FromSeconds(8), last_short_poll_interval_received_
);
3629 EXPECT_EQ(TimeDelta::FromSeconds(800), last_long_poll_interval_received_
);
3630 EXPECT_EQ(TimeDelta::FromSeconds(3141), last_sessions_commit_delay_
);
3631 EXPECT_EQ(TimeDelta::FromMilliseconds(950), last_bookmarks_commit_delay_
);
3632 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_
);
3634 command
= new ClientCommand();
3635 command
->set_set_sync_poll_interval(180);
3636 command
->set_set_sync_long_poll_interval(190);
3637 command
->set_sessions_commit_delay_seconds(2718);
3638 bookmark_delay
= command
->add_custom_nudge_delays();
3639 bookmark_delay
->set_datatype_id(
3640 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3641 bookmark_delay
->set_delay_ms(1050);
3642 command
->set_client_invalidation_hint_buffer_size(9);
3643 mock_server_
->AddUpdateDirectory(
3644 1, 0, "in_root", 1, 1, foreign_cache_guid(), "-1");
3645 mock_server_
->SetGUClientCommand(command
);
3648 EXPECT_EQ(TimeDelta::FromSeconds(180), last_short_poll_interval_received_
);
3649 EXPECT_EQ(TimeDelta::FromSeconds(190), last_long_poll_interval_received_
);
3650 EXPECT_EQ(TimeDelta::FromSeconds(2718), last_sessions_commit_delay_
);
3651 EXPECT_EQ(TimeDelta::FromMilliseconds(1050), last_bookmarks_commit_delay_
);
3652 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_
);
3655 TEST_F(SyncerTest
, TestClientCommandDuringCommit
) {
3656 using sync_pb::ClientCommand
;
3658 ClientCommand
* command
= new ClientCommand();
3659 command
->set_set_sync_poll_interval(8);
3660 command
->set_set_sync_long_poll_interval(800);
3661 command
->set_sessions_commit_delay_seconds(3141);
3662 sync_pb::CustomNudgeDelay
* bookmark_delay
=
3663 command
->add_custom_nudge_delays();
3664 bookmark_delay
->set_datatype_id(
3665 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3666 bookmark_delay
->set_delay_ms(950);
3667 command
->set_client_invalidation_hint_buffer_size(11);
3668 CreateUnsyncedDirectory("X", "id_X");
3669 mock_server_
->SetCommitClientCommand(command
);
3672 EXPECT_EQ(TimeDelta::FromSeconds(8), last_short_poll_interval_received_
);
3673 EXPECT_EQ(TimeDelta::FromSeconds(800), last_long_poll_interval_received_
);
3674 EXPECT_EQ(TimeDelta::FromSeconds(3141), last_sessions_commit_delay_
);
3675 EXPECT_EQ(TimeDelta::FromMilliseconds(950), last_bookmarks_commit_delay_
);
3676 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_
);
3678 command
= new ClientCommand();
3679 command
->set_set_sync_poll_interval(180);
3680 command
->set_set_sync_long_poll_interval(190);
3681 command
->set_sessions_commit_delay_seconds(2718);
3682 bookmark_delay
= command
->add_custom_nudge_delays();
3683 bookmark_delay
->set_datatype_id(
3684 GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
3685 bookmark_delay
->set_delay_ms(1050);
3686 command
->set_client_invalidation_hint_buffer_size(9);
3687 CreateUnsyncedDirectory("Y", "id_Y");
3688 mock_server_
->SetCommitClientCommand(command
);
3691 EXPECT_EQ(TimeDelta::FromSeconds(180), last_short_poll_interval_received_
);
3692 EXPECT_EQ(TimeDelta::FromSeconds(190), last_long_poll_interval_received_
);
3693 EXPECT_EQ(TimeDelta::FromSeconds(2718), last_sessions_commit_delay_
);
3694 EXPECT_EQ(TimeDelta::FromMilliseconds(1050), last_bookmarks_commit_delay_
);
3695 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_
);
3698 TEST_F(SyncerTest
, EnsureWeSendUpOldParent
) {
3699 syncable::Id folder_one_id
= ids_
.FromNumber(1);
3700 syncable::Id folder_two_id
= ids_
.FromNumber(2);
3702 mock_server_
->AddUpdateDirectory(folder_one_id
, TestIdFactory::root(),
3703 "folder_one", 1, 1, foreign_cache_guid(), "-1");
3704 mock_server_
->AddUpdateDirectory(folder_two_id
, TestIdFactory::root(),
3705 "folder_two", 1, 1, foreign_cache_guid(), "-2");
3708 // A moved entry should send an "old parent."
3709 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3710 MutableEntry
entry(&trans
, GET_BY_ID
, folder_one_id
);
3711 ASSERT_TRUE(entry
.good());
3712 entry
.PutParentId(folder_two_id
);
3713 entry
.PutIsUnsynced(true);
3714 // A new entry should send no "old parent."
3715 MutableEntry
create(
3716 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_folder");
3717 create
.PutIsUnsynced(true);
3718 create
.PutSpecifics(DefaultBookmarkSpecifics());
3721 const sync_pb::CommitMessage
& commit
= mock_server_
->last_sent_commit();
3722 ASSERT_EQ(2, commit
.entries_size());
3723 EXPECT_TRUE(commit
.entries(0).parent_id_string() == "2");
3724 EXPECT_TRUE(commit
.entries(0).old_parent_id() == "0");
3725 EXPECT_FALSE(commit
.entries(1).has_old_parent_id());
3728 TEST_F(SyncerTest
, Test64BitVersionSupport
) {
3729 int64 really_big_int
= std::numeric_limits
<int64
>::max() - 12;
3730 const string
name("ringo's dang orang ran rings around my o-ring");
3731 int64 item_metahandle
;
3733 // Try writing max int64 to the version fields of a meta entry.
3735 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3736 MutableEntry
entry(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), name
);
3737 ASSERT_TRUE(entry
.good());
3738 entry
.PutBaseVersion(really_big_int
);
3739 entry
.PutServerVersion(really_big_int
);
3740 entry
.PutId(ids_
.NewServerId());
3741 item_metahandle
= entry
.GetMetahandle();
3743 // Now read it back out and make sure the value is max int64.
3744 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
3745 Entry
entry(&rtrans
, syncable::GET_BY_HANDLE
, item_metahandle
);
3746 ASSERT_TRUE(entry
.good());
3747 EXPECT_TRUE(really_big_int
== entry
.GetBaseVersion());
3750 TEST_F(SyncerTest
, TestSimpleUndelete
) {
3751 Id id
= ids_
.MakeServer("undeletion item"), root
= TestIdFactory::root();
3752 mock_server_
->set_conflict_all_commits(true);
3753 // Let there be an entry from the server.
3754 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 1, 10,
3755 foreign_cache_guid(), "-1");
3757 // Check it out and delete it.
3759 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3760 MutableEntry
entry(&wtrans
, GET_BY_ID
, id
);
3761 ASSERT_TRUE(entry
.good());
3762 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3763 EXPECT_FALSE(entry
.GetIsUnsynced());
3764 EXPECT_FALSE(entry
.GetIsDel());
3765 // Delete it locally.
3766 entry
.PutIsDel(true);
3769 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3771 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3772 Entry
entry(&trans
, GET_BY_ID
, id
);
3773 ASSERT_TRUE(entry
.good());
3774 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3775 EXPECT_FALSE(entry
.GetIsUnsynced());
3776 EXPECT_TRUE(entry
.GetIsDel());
3777 EXPECT_FALSE(entry
.GetServerIsDel());
3780 // Update from server confirming deletion.
3781 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 11,
3782 foreign_cache_guid(), "-1");
3783 mock_server_
->SetLastUpdateDeleted();
3785 // IS_DEL AND SERVER_IS_DEL now both true.
3787 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3788 Entry
entry(&trans
, GET_BY_ID
, id
);
3789 ASSERT_TRUE(entry
.good());
3790 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3791 EXPECT_FALSE(entry
.GetIsUnsynced());
3792 EXPECT_TRUE(entry
.GetIsDel());
3793 EXPECT_TRUE(entry
.GetServerIsDel());
3795 // Undelete from server.
3796 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 12,
3797 foreign_cache_guid(), "-1");
3799 // IS_DEL and SERVER_IS_DEL now both false.
3801 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3802 Entry
entry(&trans
, GET_BY_ID
, id
);
3803 ASSERT_TRUE(entry
.good());
3804 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3805 EXPECT_FALSE(entry
.GetIsUnsynced());
3806 EXPECT_FALSE(entry
.GetIsDel());
3807 EXPECT_FALSE(entry
.GetServerIsDel());
3811 TEST_F(SyncerTest
, TestUndeleteWithMissingDeleteUpdate
) {
3812 Id id
= ids_
.MakeServer("undeletion item"), root
= TestIdFactory::root();
3813 // Let there be a entry, from the server.
3814 mock_server_
->set_conflict_all_commits(true);
3815 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 1, 10,
3816 foreign_cache_guid(), "-1");
3818 // Check it out and delete it.
3820 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3821 MutableEntry
entry(&wtrans
, GET_BY_ID
, id
);
3822 ASSERT_TRUE(entry
.good());
3823 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3824 EXPECT_FALSE(entry
.GetIsUnsynced());
3825 EXPECT_FALSE(entry
.GetIsDel());
3826 // Delete it locally.
3827 entry
.PutIsDel(true);
3830 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3832 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3833 Entry
entry(&trans
, GET_BY_ID
, id
);
3834 ASSERT_TRUE(entry
.good());
3835 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3836 EXPECT_FALSE(entry
.GetIsUnsynced());
3837 EXPECT_TRUE(entry
.GetIsDel());
3838 EXPECT_FALSE(entry
.GetServerIsDel());
3841 // Say we do not get an update from server confirming deletion. Undelete
3843 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 12,
3844 foreign_cache_guid(), "-1");
3846 // IS_DEL and SERVER_IS_DEL now both false.
3848 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3849 Entry
entry(&trans
, GET_BY_ID
, id
);
3850 ASSERT_TRUE(entry
.good());
3851 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3852 EXPECT_FALSE(entry
.GetIsUnsynced());
3853 EXPECT_FALSE(entry
.GetIsDel());
3854 EXPECT_FALSE(entry
.GetServerIsDel());
3858 TEST_F(SyncerTest
, TestUndeleteIgnoreCorrectlyUnappliedUpdate
) {
3859 Id id1
= ids_
.MakeServer("first"), id2
= ids_
.MakeServer("second");
3860 Id root
= TestIdFactory::root();
3861 // Duplicate! expect path clashing!
3862 mock_server_
->set_conflict_all_commits(true);
3863 mock_server_
->AddUpdateBookmark(id1
, root
, "foo", 1, 10,
3864 foreign_cache_guid(), "-1");
3865 mock_server_
->AddUpdateBookmark(id2
, root
, "foo", 1, 10,
3866 foreign_cache_guid(), "-2");
3868 mock_server_
->AddUpdateBookmark(id2
, root
, "foo2", 2, 20,
3869 foreign_cache_guid(), "-2");
3870 SyncShareNudge(); // Now just don't explode.
3873 TEST_F(SyncerTest
, ClientTagServerCreatedUpdatesWork
) {
3874 mock_server_
->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
3875 foreign_cache_guid(), "-1");
3876 mock_server_
->SetLastUpdateClientTag("permfolder");
3881 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3882 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
3883 ASSERT_TRUE(perm_folder
.good());
3884 EXPECT_FALSE(perm_folder
.GetIsDel());
3885 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
3886 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
3887 EXPECT_EQ(perm_folder
.GetUniqueClientTag(), "permfolder");
3888 EXPECT_EQ(perm_folder
.GetNonUniqueName(), "permitem1");
3891 mock_server_
->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
3892 foreign_cache_guid(), "-1");
3893 mock_server_
->SetLastUpdateClientTag("permfolder");
3897 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3899 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
3900 ASSERT_TRUE(perm_folder
.good());
3901 EXPECT_FALSE(perm_folder
.GetIsDel());
3902 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
3903 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
3904 EXPECT_EQ(perm_folder
.GetUniqueClientTag(), "permfolder");
3905 EXPECT_EQ(perm_folder
.GetNonUniqueName(), "permitem_renamed");
3909 TEST_F(SyncerTest
, ClientTagIllegalUpdateIgnored
) {
3910 mock_server_
->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
3911 foreign_cache_guid(), "-1");
3912 mock_server_
->SetLastUpdateClientTag("permfolder");
3917 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3918 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
3919 ASSERT_TRUE(perm_folder
.good());
3920 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
3921 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
3922 EXPECT_EQ(perm_folder
.GetUniqueClientTag(), "permfolder");
3923 EXPECT_TRUE(perm_folder
.GetNonUniqueName()== "permitem1");
3924 EXPECT_TRUE(perm_folder
.GetId().ServerKnows());
3927 mock_server_
->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
3928 foreign_cache_guid(), "-1");
3929 mock_server_
->SetLastUpdateClientTag("wrongtag");
3933 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3935 // This update is rejected because it has the same ID, but a
3936 // different tag than one that is already on the client.
3937 // The client has a ServerKnows ID, which cannot be overwritten.
3938 Entry
rejected_update(&trans
, GET_BY_CLIENT_TAG
, "wrongtag");
3939 EXPECT_FALSE(rejected_update
.good());
3941 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
3942 ASSERT_TRUE(perm_folder
.good());
3943 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
3944 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
3945 EXPECT_EQ(perm_folder
.GetNonUniqueName(), "permitem1");
3949 TEST_F(SyncerTest
, ClientTagUncommittedTagMatchesUpdate
) {
3950 int64 original_metahandle
= 0;
3953 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3955 &trans
, CREATE
, PREFERENCES
, ids_
.root(), "name");
3956 ASSERT_TRUE(pref
.good());
3957 pref
.PutUniqueClientTag("tag");
3958 pref
.PutIsUnsynced(true);
3959 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
3960 EXPECT_FALSE(pref
.GetId().ServerKnows());
3961 original_metahandle
= pref
.GetMetahandle();
3964 syncable::Id server_id
= TestIdFactory::MakeServer("id");
3965 mock_server_
->AddUpdatePref(server_id
.GetServerId(),
3966 ids_
.root().GetServerId(),
3968 mock_server_
->set_conflict_all_commits(true);
3971 // This should cause client tag reunion, preserving the metahandle.
3973 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3975 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
3976 ASSERT_TRUE(pref
.good());
3977 EXPECT_FALSE(pref
.GetIsDel());
3978 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
3979 EXPECT_TRUE(pref
.GetIsUnsynced());
3980 EXPECT_EQ(10, pref
.GetBaseVersion());
3981 // Entry should have been given the new ID while preserving the
3982 // metahandle; client should have won the conflict resolution.
3983 EXPECT_EQ(original_metahandle
, pref
.GetMetahandle());
3984 EXPECT_EQ("tag", pref
.GetUniqueClientTag());
3985 EXPECT_TRUE(pref
.GetId().ServerKnows());
3988 mock_server_
->set_conflict_all_commits(false);
3991 // The resolved entry ought to commit cleanly.
3993 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3995 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
3996 ASSERT_TRUE(pref
.good());
3997 EXPECT_FALSE(pref
.GetIsDel());
3998 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
3999 EXPECT_FALSE(pref
.GetIsUnsynced());
4000 EXPECT_TRUE(10 < pref
.GetBaseVersion());
4001 // Entry should have been given the new ID while preserving the
4002 // metahandle; client should have won the conflict resolution.
4003 EXPECT_EQ(original_metahandle
, pref
.GetMetahandle());
4004 EXPECT_EQ("tag", pref
.GetUniqueClientTag());
4005 EXPECT_TRUE(pref
.GetId().ServerKnows());
4009 TEST_F(SyncerTest
, ClientTagConflictWithDeletedLocalEntry
) {
4011 // Create a deleted local entry with a unique client tag.
4012 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4014 &trans
, CREATE
, PREFERENCES
, ids_
.root(), "name");
4015 ASSERT_TRUE(pref
.good());
4016 ASSERT_FALSE(pref
.GetId().ServerKnows());
4017 pref
.PutUniqueClientTag("tag");
4018 pref
.PutIsUnsynced(true);
4020 // Note: IS_DEL && !ServerKnows() will clear the UNSYNCED bit.
4021 // (We never attempt to commit server-unknown deleted items, so this
4022 // helps us clean up those entries).
4023 pref
.PutIsDel(true);
4026 // Prepare an update with the same unique client tag.
4027 syncable::Id server_id
= TestIdFactory::MakeServer("id");
4028 mock_server_
->AddUpdatePref(server_id
.GetServerId(),
4029 ids_
.root().GetServerId(),
4033 // The local entry will be overwritten.
4035 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4037 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
4038 ASSERT_TRUE(pref
.good());
4039 ASSERT_TRUE(pref
.GetId().ServerKnows());
4040 EXPECT_FALSE(pref
.GetIsDel());
4041 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
4042 EXPECT_FALSE(pref
.GetIsUnsynced());
4043 EXPECT_EQ(pref
.GetBaseVersion(), 10);
4044 EXPECT_EQ(pref
.GetUniqueClientTag(), "tag");
4048 TEST_F(SyncerTest
, ClientTagUpdateClashesWithLocalEntry
) {
4049 // This test is written assuming that ID comparison
4050 // will work out in a particular way.
4051 EXPECT_TRUE(ids_
.FromNumber(1) < ids_
.FromNumber(2));
4052 EXPECT_TRUE(ids_
.FromNumber(3) < ids_
.FromNumber(4));
4054 syncable::Id id1
= TestIdFactory::MakeServer("1");
4055 mock_server_
->AddUpdatePref(id1
.GetServerId(), ids_
.root().GetServerId(),
4058 syncable::Id id4
= TestIdFactory::MakeServer("4");
4059 mock_server_
->AddUpdatePref(id4
.GetServerId(), ids_
.root().GetServerId(),
4062 mock_server_
->set_conflict_all_commits(true);
4065 int64 tag1_metahandle
= syncable::kInvalidMetaHandle
;
4066 int64 tag2_metahandle
= syncable::kInvalidMetaHandle
;
4067 // This should cause client tag overwrite.
4069 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4071 Entry
tag1(&trans
, GET_BY_CLIENT_TAG
, "tag1");
4072 ASSERT_TRUE(tag1
.good());
4073 ASSERT_TRUE(tag1
.GetId().ServerKnows());
4074 ASSERT_TRUE(id1
== tag1
.GetId());
4075 EXPECT_FALSE(tag1
.GetIsDel());
4076 EXPECT_FALSE(tag1
.GetIsUnappliedUpdate());
4077 EXPECT_FALSE(tag1
.GetIsUnsynced());
4078 EXPECT_EQ(10, tag1
.GetBaseVersion());
4079 EXPECT_EQ("tag1", tag1
.GetUniqueClientTag());
4080 tag1_metahandle
= tag1
.GetMetahandle();
4082 Entry
tag2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
4083 ASSERT_TRUE(tag2
.good());
4084 ASSERT_TRUE(tag2
.GetId().ServerKnows());
4085 ASSERT_TRUE(id4
== tag2
.GetId());
4086 EXPECT_FALSE(tag2
.GetIsDel());
4087 EXPECT_FALSE(tag2
.GetIsUnappliedUpdate());
4088 EXPECT_FALSE(tag2
.GetIsUnsynced());
4089 EXPECT_EQ(11, tag2
.GetBaseVersion());
4090 EXPECT_EQ("tag2", tag2
.GetUniqueClientTag());
4091 tag2_metahandle
= tag2
.GetMetahandle();
4093 syncable::Directory::Metahandles children
;
4094 directory()->GetChildHandlesById(&trans
, trans
.root_id(), &children
);
4095 ASSERT_EQ(2U, children
.size());
4098 syncable::Id id2
= TestIdFactory::MakeServer("2");
4099 mock_server_
->AddUpdatePref(id2
.GetServerId(), ids_
.root().GetServerId(),
4101 syncable::Id id3
= TestIdFactory::MakeServer("3");
4102 mock_server_
->AddUpdatePref(id3
.GetServerId(), ids_
.root().GetServerId(),
4107 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4109 Entry
tag1(&trans
, GET_BY_CLIENT_TAG
, "tag1");
4110 ASSERT_TRUE(tag1
.good());
4111 ASSERT_TRUE(tag1
.GetId().ServerKnows());
4112 ASSERT_EQ(id1
, tag1
.GetId())
4113 << "ID 1 should be kept, since it was less than ID 2.";
4114 EXPECT_FALSE(tag1
.GetIsDel());
4115 EXPECT_FALSE(tag1
.GetIsUnappliedUpdate());
4116 EXPECT_FALSE(tag1
.GetIsUnsynced());
4117 EXPECT_EQ(10, tag1
.GetBaseVersion());
4118 EXPECT_EQ("tag1", tag1
.GetUniqueClientTag());
4119 EXPECT_EQ(tag1_metahandle
, tag1
.GetMetahandle());
4121 Entry
tag2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
4122 ASSERT_TRUE(tag2
.good());
4123 ASSERT_TRUE(tag2
.GetId().ServerKnows());
4124 ASSERT_EQ(id3
, tag2
.GetId())
4125 << "ID 3 should be kept, since it was less than ID 4.";
4126 EXPECT_FALSE(tag2
.GetIsDel());
4127 EXPECT_FALSE(tag2
.GetIsUnappliedUpdate());
4128 EXPECT_FALSE(tag2
.GetIsUnsynced());
4129 EXPECT_EQ(13, tag2
.GetBaseVersion());
4130 EXPECT_EQ("tag2", tag2
.GetUniqueClientTag());
4131 EXPECT_EQ(tag2_metahandle
, tag2
.GetMetahandle());
4133 syncable::Directory::Metahandles children
;
4134 directory()->GetChildHandlesById(&trans
, trans
.root_id(), &children
);
4135 ASSERT_EQ(2U, children
.size());
4139 TEST_F(SyncerTest
, ClientTagClashWithinBatchOfUpdates
) {
4140 // This test is written assuming that ID comparison
4141 // will work out in a particular way.
4142 EXPECT_TRUE(ids_
.FromNumber(1) < ids_
.FromNumber(4));
4143 EXPECT_TRUE(ids_
.FromNumber(201) < ids_
.FromNumber(205));
4145 // Least ID: winner.
4146 mock_server_
->AddUpdatePref(ids_
.FromNumber(1).GetServerId(),
4147 ids_
.root().GetServerId(), "tag a", 1, 10);
4148 mock_server_
->AddUpdatePref(ids_
.FromNumber(2).GetServerId(),
4149 ids_
.root().GetServerId(), "tag a", 11, 110);
4150 mock_server_
->AddUpdatePref(ids_
.FromNumber(3).GetServerId(),
4151 ids_
.root().GetServerId(), "tag a", 12, 120);
4152 mock_server_
->AddUpdatePref(ids_
.FromNumber(4).GetServerId(),
4153 ids_
.root().GetServerId(), "tag a", 13, 130);
4155 mock_server_
->AddUpdatePref(ids_
.FromNumber(105).GetServerId(),
4156 ids_
.root().GetServerId(), "tag b", 14, 140);
4157 mock_server_
->AddUpdatePref(ids_
.FromNumber(102).GetServerId(),
4158 ids_
.root().GetServerId(), "tag b", 15, 150);
4159 // Least ID: winner.
4160 mock_server_
->AddUpdatePref(ids_
.FromNumber(101).GetServerId(),
4161 ids_
.root().GetServerId(), "tag b", 16, 160);
4162 mock_server_
->AddUpdatePref(ids_
.FromNumber(104).GetServerId(),
4163 ids_
.root().GetServerId(), "tag b", 17, 170);
4165 mock_server_
->AddUpdatePref(ids_
.FromNumber(205).GetServerId(),
4166 ids_
.root().GetServerId(), "tag c", 18, 180);
4167 mock_server_
->AddUpdatePref(ids_
.FromNumber(202).GetServerId(),
4168 ids_
.root().GetServerId(), "tag c", 19, 190);
4169 mock_server_
->AddUpdatePref(ids_
.FromNumber(204).GetServerId(),
4170 ids_
.root().GetServerId(), "tag c", 20, 200);
4171 // Least ID: winner.
4172 mock_server_
->AddUpdatePref(ids_
.FromNumber(201).GetServerId(),
4173 ids_
.root().GetServerId(), "tag c", 21, 210);
4175 mock_server_
->set_conflict_all_commits(true);
4178 // This should cause client tag overwrite.
4180 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4182 Entry
tag_a(&trans
, GET_BY_CLIENT_TAG
, "tag a");
4183 ASSERT_TRUE(tag_a
.good());
4184 EXPECT_TRUE(tag_a
.GetId().ServerKnows());
4185 EXPECT_EQ(ids_
.FromNumber(1), tag_a
.GetId());
4186 EXPECT_FALSE(tag_a
.GetIsDel());
4187 EXPECT_FALSE(tag_a
.GetIsUnappliedUpdate());
4188 EXPECT_FALSE(tag_a
.GetIsUnsynced());
4189 EXPECT_EQ(1, tag_a
.GetBaseVersion());
4190 EXPECT_EQ("tag a", tag_a
.GetUniqueClientTag());
4192 Entry
tag_b(&trans
, GET_BY_CLIENT_TAG
, "tag b");
4193 ASSERT_TRUE(tag_b
.good());
4194 EXPECT_TRUE(tag_b
.GetId().ServerKnows());
4195 EXPECT_EQ(ids_
.FromNumber(101), tag_b
.GetId());
4196 EXPECT_FALSE(tag_b
.GetIsDel());
4197 EXPECT_FALSE(tag_b
.GetIsUnappliedUpdate());
4198 EXPECT_FALSE(tag_b
.GetIsUnsynced());
4199 EXPECT_EQ(16, tag_b
.GetBaseVersion());
4200 EXPECT_EQ("tag b", tag_b
.GetUniqueClientTag());
4202 Entry
tag_c(&trans
, GET_BY_CLIENT_TAG
, "tag c");
4203 ASSERT_TRUE(tag_c
.good());
4204 EXPECT_TRUE(tag_c
.GetId().ServerKnows());
4205 EXPECT_EQ(ids_
.FromNumber(201), tag_c
.GetId());
4206 EXPECT_FALSE(tag_c
.GetIsDel());
4207 EXPECT_FALSE(tag_c
.GetIsUnappliedUpdate());
4208 EXPECT_FALSE(tag_c
.GetIsUnsynced());
4209 EXPECT_EQ(21, tag_c
.GetBaseVersion());
4210 EXPECT_EQ("tag c", tag_c
.GetUniqueClientTag());
4212 syncable::Directory::Metahandles children
;
4213 directory()->GetChildHandlesById(&trans
, trans
.root_id(), &children
);
4214 ASSERT_EQ(3U, children
.size());
4218 TEST_F(SyncerTest
, UniqueServerTagUpdates
) {
4219 // As a hurdle, introduce an item whose name is the same as the tag value
4221 int64 hurdle_handle
= CreateUnsyncedDirectory("bob", "id_bob");
4223 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4224 Entry
hurdle(&trans
, GET_BY_HANDLE
, hurdle_handle
);
4225 ASSERT_TRUE(hurdle
.good());
4226 ASSERT_TRUE(!hurdle
.GetIsDel());
4227 ASSERT_TRUE(hurdle
.GetUniqueServerTag().empty());
4228 ASSERT_TRUE(hurdle
.GetNonUniqueName()== "bob");
4230 // Try to lookup by the tagname. These should fail.
4231 Entry
tag_alpha(&trans
, GET_BY_SERVER_TAG
, "alpha");
4232 EXPECT_FALSE(tag_alpha
.good());
4233 Entry
tag_bob(&trans
, GET_BY_SERVER_TAG
, "bob");
4234 EXPECT_FALSE(tag_bob
.good());
4237 // Now download some tagged items as updates.
4238 mock_server_
->AddUpdateDirectory(
4239 1, 0, "update1", 1, 10, std::string(), std::string());
4240 mock_server_
->SetLastUpdateServerTag("alpha");
4241 mock_server_
->AddUpdateDirectory(
4242 2, 0, "update2", 2, 20, std::string(), std::string());
4243 mock_server_
->SetLastUpdateServerTag("bob");
4247 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4249 // The new items should be applied as new entries, and we should be able
4250 // to look them up by their tag values.
4251 Entry
tag_alpha(&trans
, GET_BY_SERVER_TAG
, "alpha");
4252 ASSERT_TRUE(tag_alpha
.good());
4253 ASSERT_TRUE(!tag_alpha
.GetIsDel());
4254 ASSERT_TRUE(tag_alpha
.GetUniqueServerTag()== "alpha");
4255 ASSERT_TRUE(tag_alpha
.GetNonUniqueName()== "update1");
4256 Entry
tag_bob(&trans
, GET_BY_SERVER_TAG
, "bob");
4257 ASSERT_TRUE(tag_bob
.good());
4258 ASSERT_TRUE(!tag_bob
.GetIsDel());
4259 ASSERT_TRUE(tag_bob
.GetUniqueServerTag()== "bob");
4260 ASSERT_TRUE(tag_bob
.GetNonUniqueName()== "update2");
4261 // The old item should be unchanged.
4262 Entry
hurdle(&trans
, GET_BY_HANDLE
, hurdle_handle
);
4263 ASSERT_TRUE(hurdle
.good());
4264 ASSERT_TRUE(!hurdle
.GetIsDel());
4265 ASSERT_TRUE(hurdle
.GetUniqueServerTag().empty());
4266 ASSERT_TRUE(hurdle
.GetNonUniqueName()== "bob");
4270 TEST_F(SyncerTest
, GetUpdatesSetsRequestedTypes
) {
4271 // The expectations of this test happen in the MockConnectionManager's
4272 // GetUpdates handler. EnableDatatype sets the expectation value from our
4273 // set of enabled/disabled datatypes.
4274 EnableDatatype(BOOKMARKS
);
4276 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4278 EnableDatatype(AUTOFILL
);
4280 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4282 EnableDatatype(PREFERENCES
);
4284 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4286 DisableDatatype(BOOKMARKS
);
4288 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4290 DisableDatatype(AUTOFILL
);
4292 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4294 DisableDatatype(PREFERENCES
);
4295 EnableDatatype(AUTOFILL
);
4297 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4300 // A typical scenario: server and client each have one update for the other.
4301 // This is the "happy path" alternative to UpdateFailsThenDontCommit.
4302 TEST_F(SyncerTest
, UpdateThenCommit
) {
4303 syncable::Id to_receive
= ids_
.NewServerId();
4304 syncable::Id to_commit
= ids_
.NewLocalId();
4306 mock_server_
->AddUpdateDirectory(to_receive
, ids_
.root(), "x", 1, 10,
4307 foreign_cache_guid(), "-1");
4308 int64 commit_handle
= CreateUnsyncedDirectory("y", to_commit
);
4311 // The sync cycle should have included a GetUpdate, then a commit. By the
4312 // time the commit happened, we should have known for sure that there were no
4313 // hierarchy conflicts, and reported this fact to the server.
4314 ASSERT_TRUE(mock_server_
->last_request().has_commit());
4315 VerifyNoHierarchyConflictsReported(mock_server_
->last_request());
4317 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4319 Entry
received(&trans
, GET_BY_ID
, to_receive
);
4320 ASSERT_TRUE(received
.good());
4321 EXPECT_FALSE(received
.GetIsUnsynced());
4322 EXPECT_FALSE(received
.GetIsUnappliedUpdate());
4324 Entry
committed(&trans
, GET_BY_HANDLE
, commit_handle
);
4325 ASSERT_TRUE(committed
.good());
4326 EXPECT_FALSE(committed
.GetIsUnsynced());
4327 EXPECT_FALSE(committed
.GetIsUnappliedUpdate());
4330 // Same as above, but this time we fail to download updates.
4331 // We should not attempt to commit anything unless we successfully downloaded
4332 // updates, otherwise we risk causing a server-side conflict.
4333 TEST_F(SyncerTest
, UpdateFailsThenDontCommit
) {
4334 syncable::Id to_receive
= ids_
.NewServerId();
4335 syncable::Id to_commit
= ids_
.NewLocalId();
4337 mock_server_
->AddUpdateDirectory(to_receive
, ids_
.root(), "x", 1, 10,
4338 foreign_cache_guid(), "-1");
4339 int64 commit_handle
= CreateUnsyncedDirectory("y", to_commit
);
4340 mock_server_
->FailNextPostBufferToPathCall();
4343 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4345 // We did not receive this update.
4346 Entry
received(&trans
, GET_BY_ID
, to_receive
);
4347 ASSERT_FALSE(received
.good());
4349 // And our local update remains unapplied.
4350 Entry
committed(&trans
, GET_BY_HANDLE
, commit_handle
);
4351 ASSERT_TRUE(committed
.good());
4352 EXPECT_TRUE(committed
.GetIsUnsynced());
4353 EXPECT_FALSE(committed
.GetIsUnappliedUpdate());
4355 // Inform the Mock we won't be fetching all updates.
4356 mock_server_
->ClearUpdatesQueue();
4359 // Downloads two updates and applies them successfully.
4360 // This is the "happy path" alternative to ConfigureFailsDontApplyUpdates.
4361 TEST_F(SyncerTest
, ConfigureDownloadsTwoBatchesSuccess
) {
4362 syncable::Id node1
= ids_
.NewServerId();
4363 syncable::Id node2
= ids_
.NewServerId();
4365 // Construct the first GetUpdates response.
4366 mock_server_
->AddUpdateDirectory(node1
, ids_
.root(), "one", 1, 10,
4367 foreign_cache_guid(), "-2");
4368 mock_server_
->SetChangesRemaining(1);
4369 mock_server_
->NextUpdateBatch();
4371 // Construct the second GetUpdates response.
4372 mock_server_
->AddUpdateDirectory(node2
, ids_
.root(), "two", 1, 20,
4373 foreign_cache_guid(), "-2");
4375 SyncShareConfigure();
4377 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4378 // Both nodes should be downloaded and applied.
4380 Entry
n1(&trans
, GET_BY_ID
, node1
);
4381 ASSERT_TRUE(n1
.good());
4382 EXPECT_FALSE(n1
.GetIsUnappliedUpdate());
4384 Entry
n2(&trans
, GET_BY_ID
, node2
);
4385 ASSERT_TRUE(n2
.good());
4386 EXPECT_FALSE(n2
.GetIsUnappliedUpdate());
4389 // Same as the above case, but this time the second batch fails to download.
4390 TEST_F(SyncerTest
, ConfigureFailsDontApplyUpdates
) {
4391 syncable::Id node1
= ids_
.NewServerId();
4392 syncable::Id node2
= ids_
.NewServerId();
4394 // The scenario: we have two batches of updates with one update each. A
4395 // normal confgure step would download all the updates one batch at a time and
4396 // apply them. This configure will succeed in downloading the first batch
4397 // then fail when downloading the second.
4398 mock_server_
->FailNthPostBufferToPathCall(2);
4400 // Construct the first GetUpdates response.
4401 mock_server_
->AddUpdateDirectory(node1
, ids_
.root(), "one", 1, 10,
4402 foreign_cache_guid(), "-1");
4403 mock_server_
->SetChangesRemaining(1);
4404 mock_server_
->NextUpdateBatch();
4406 // Consutrct the second GetUpdates response.
4407 mock_server_
->AddUpdateDirectory(node2
, ids_
.root(), "two", 1, 20,
4408 foreign_cache_guid(), "-2");
4410 SyncShareConfigure();
4412 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4414 // The first node was downloaded, but not applied.
4415 Entry
n1(&trans
, GET_BY_ID
, node1
);
4416 ASSERT_TRUE(n1
.good());
4417 EXPECT_TRUE(n1
.GetIsUnappliedUpdate());
4419 // The second node was not downloaded.
4420 Entry
n2(&trans
, GET_BY_ID
, node2
);
4421 EXPECT_FALSE(n2
.good());
4423 // One update remains undownloaded.
4424 mock_server_
->ClearUpdatesQueue();
4427 TEST_F(SyncerTest
, GetKeySuccess
) {
4429 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4430 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4433 SyncShareConfigure();
4435 EXPECT_EQ(session_
->status_controller().last_get_key_result(), SYNCER_OK
);
4437 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4438 EXPECT_FALSE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4442 TEST_F(SyncerTest
, GetKeyEmpty
) {
4444 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4445 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4448 mock_server_
->SetKeystoreKey(std::string());
4449 SyncShareConfigure();
4451 EXPECT_NE(session_
->status_controller().last_get_key_result(), SYNCER_OK
);
4453 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4454 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4458 // Test what happens if a client deletes, then recreates, an object very
4459 // quickly. It is possible that the deletion gets sent as a commit, and
4460 // the undelete happens during the commit request. The principle here
4461 // is that with a single committing client, conflicts should never
4462 // be encountered, and a client encountering its past actions during
4463 // getupdates should never feed back to override later actions.
4465 // In cases of ordering A-F below, the outcome should be the same.
4466 // Exercised by UndeleteDuringCommit:
4467 // A. Delete - commit - undelete - commitresponse.
4468 // B. Delete - commit - undelete - commitresponse - getupdates.
4469 // Exercised by UndeleteBeforeCommit:
4470 // C. Delete - undelete - commit - commitresponse.
4471 // D. Delete - undelete - commit - commitresponse - getupdates.
4472 // Exercised by UndeleteAfterCommit:
4473 // E. Delete - commit - commitresponse - undelete - commit
4474 // - commitresponse.
4475 // F. Delete - commit - commitresponse - undelete - commit -
4476 // - commitresponse - getupdates.
4477 class SyncerUndeletionTest
: public SyncerTest
{
4479 SyncerUndeletionTest()
4480 : client_tag_("foobar"),
4481 metahandle_(syncable::kInvalidMetaHandle
) {
4485 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4486 MutableEntry
perm_folder(
4487 &trans
, CREATE
, BOOKMARKS
, ids_
.root(), "clientname");
4488 ASSERT_TRUE(perm_folder
.good());
4489 perm_folder
.PutUniqueClientTag(client_tag_
);
4490 perm_folder
.PutIsUnsynced(true);
4491 perm_folder
.PutSyncing(false);
4492 perm_folder
.PutSpecifics(DefaultBookmarkSpecifics());
4493 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
4494 EXPECT_FALSE(perm_folder
.GetId().ServerKnows());
4495 metahandle_
= perm_folder
.GetMetahandle();
4496 local_id_
= perm_folder
.GetId();
4500 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4501 MutableEntry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4502 ASSERT_TRUE(entry
.good());
4503 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4504 entry
.PutIsDel(true);
4505 entry
.PutIsUnsynced(true);
4506 entry
.PutSyncing(false);
4510 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4511 MutableEntry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4512 ASSERT_TRUE(entry
.good());
4513 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4514 EXPECT_TRUE(entry
.GetIsDel());
4515 entry
.PutIsDel(false);
4516 entry
.PutIsUnsynced(true);
4517 entry
.PutSyncing(false);
4520 int64
GetMetahandleOfTag() {
4521 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4522 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4523 EXPECT_TRUE(entry
.good());
4524 if (!entry
.good()) {
4525 return syncable::kInvalidMetaHandle
;
4527 return entry
.GetMetahandle();
4530 void ExpectUnsyncedCreation() {
4531 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4532 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4534 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4535 EXPECT_FALSE(entry
.GetIsDel());
4536 EXPECT_FALSE(entry
.GetServerIsDel()); // Never been committed.
4537 EXPECT_GE(0, entry
.GetBaseVersion());
4538 EXPECT_TRUE(entry
.GetIsUnsynced());
4539 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4542 void ExpectUnsyncedUndeletion() {
4543 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4544 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4546 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4547 EXPECT_FALSE(entry
.GetIsDel());
4548 EXPECT_TRUE(entry
.GetServerIsDel());
4549 EXPECT_EQ(0, entry
.GetBaseVersion());
4550 EXPECT_TRUE(entry
.GetIsUnsynced());
4551 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4552 EXPECT_TRUE(entry
.GetId().ServerKnows());
4555 void ExpectUnsyncedEdit() {
4556 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4557 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4559 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4560 EXPECT_FALSE(entry
.GetIsDel());
4561 EXPECT_FALSE(entry
.GetServerIsDel());
4562 EXPECT_LT(0, entry
.GetBaseVersion());
4563 EXPECT_TRUE(entry
.GetIsUnsynced());
4564 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4565 EXPECT_TRUE(entry
.GetId().ServerKnows());
4568 void ExpectUnsyncedDeletion() {
4569 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4570 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4572 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4573 EXPECT_TRUE(entry
.GetIsDel());
4574 EXPECT_FALSE(entry
.GetServerIsDel());
4575 EXPECT_TRUE(entry
.GetIsUnsynced());
4576 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4577 EXPECT_LT(0, entry
.GetBaseVersion());
4578 EXPECT_LT(0, entry
.GetServerVersion());
4581 void ExpectSyncedAndCreated() {
4582 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4583 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4585 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4586 EXPECT_FALSE(entry
.GetIsDel());
4587 EXPECT_FALSE(entry
.GetServerIsDel());
4588 EXPECT_LT(0, entry
.GetBaseVersion());
4589 EXPECT_EQ(entry
.GetBaseVersion(), entry
.GetServerVersion());
4590 EXPECT_FALSE(entry
.GetIsUnsynced());
4591 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4594 void ExpectSyncedAndDeleted() {
4595 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4596 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4598 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4599 EXPECT_TRUE(entry
.GetIsDel());
4600 EXPECT_TRUE(entry
.GetServerIsDel());
4601 EXPECT_FALSE(entry
.GetIsUnsynced());
4602 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4603 EXPECT_GE(0, entry
.GetBaseVersion());
4604 EXPECT_GE(0, entry
.GetServerVersion());
4608 const std::string client_tag_
;
4609 syncable::Id local_id_
;
4613 TEST_F(SyncerUndeletionTest
, UndeleteDuringCommit
) {
4615 ExpectUnsyncedCreation();
4618 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4619 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4620 ExpectSyncedAndCreated();
4622 // Delete, begin committing the delete, then undelete while committing.
4624 ExpectUnsyncedDeletion();
4625 mock_server_
->SetMidCommitCallback(
4626 base::Bind(&SyncerUndeletionTest::Undelete
, base::Unretained(this)));
4629 // We will continue to commit until all nodes are synced, so we expect
4630 // that both the delete and following undelete were committed. We haven't
4631 // downloaded any updates, though, so the SERVER fields will be the same
4632 // as they were at the start of the cycle.
4633 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4634 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4637 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4638 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4640 // Server fields lag behind.
4641 EXPECT_FALSE(entry
.GetServerIsDel());
4643 // We have committed the second (undelete) update.
4644 EXPECT_FALSE(entry
.GetIsDel());
4645 EXPECT_FALSE(entry
.GetIsUnsynced());
4646 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4649 // Now, encounter a GetUpdates corresponding to the deletion from
4650 // the server. The undeletion should prevail again and be committed.
4651 // None of this should trigger any conflict detection -- it is perfectly
4652 // normal to recieve updates from our own commits.
4653 mock_server_
->SetMidCommitCallback(base::Closure());
4654 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
4655 update
->set_originator_cache_guid(local_cache_guid());
4656 update
->set_originator_client_item_id(local_id_
.GetServerId());
4659 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4660 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4661 ExpectSyncedAndCreated();
4664 TEST_F(SyncerUndeletionTest
, UndeleteBeforeCommit
) {
4666 ExpectUnsyncedCreation();
4669 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4670 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4671 ExpectSyncedAndCreated();
4673 // Delete and undelete, then sync to pick up the result.
4675 ExpectUnsyncedDeletion();
4677 ExpectUnsyncedEdit(); // Edit, not undelete: server thinks it exists.
4680 // The item ought to have committed successfully.
4681 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4682 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4683 ExpectSyncedAndCreated();
4685 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4686 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4687 EXPECT_EQ(2, entry
.GetBaseVersion());
4690 // Now, encounter a GetUpdates corresponding to the just-committed
4692 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
4693 update
->set_originator_cache_guid(local_cache_guid());
4694 update
->set_originator_client_item_id(local_id_
.GetServerId());
4696 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4697 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4698 ExpectSyncedAndCreated();
4701 TEST_F(SyncerUndeletionTest
, UndeleteAfterCommitButBeforeGetUpdates
) {
4703 ExpectUnsyncedCreation();
4706 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4707 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4708 ExpectSyncedAndCreated();
4710 // Delete and commit.
4712 ExpectUnsyncedDeletion();
4715 // The item ought to have committed successfully.
4716 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4717 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4718 ExpectSyncedAndDeleted();
4720 // Before the GetUpdates, the item is locally undeleted.
4722 ExpectUnsyncedUndeletion();
4724 // Now, encounter a GetUpdates corresponding to the just-committed
4725 // deletion update. The undeletion should prevail.
4726 mock_server_
->AddUpdateFromLastCommit();
4728 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4729 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4730 ExpectSyncedAndCreated();
4733 TEST_F(SyncerUndeletionTest
, UndeleteAfterDeleteAndGetUpdates
) {
4735 ExpectUnsyncedCreation();
4738 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4739 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4740 ExpectSyncedAndCreated();
4742 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
4743 update
->set_originator_cache_guid(local_cache_guid());
4744 update
->set_originator_client_item_id(local_id_
.GetServerId());
4746 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4747 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4748 ExpectSyncedAndCreated();
4750 // Delete and commit.
4752 ExpectUnsyncedDeletion();
4755 // The item ought to have committed successfully.
4756 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4757 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4758 ExpectSyncedAndDeleted();
4760 // Now, encounter a GetUpdates corresponding to the just-committed
4761 // deletion update. Should be consistent.
4762 mock_server_
->AddUpdateFromLastCommit();
4764 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4765 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4766 ExpectSyncedAndDeleted();
4768 // After the GetUpdates, the item is locally undeleted.
4770 ExpectUnsyncedUndeletion();
4772 // Now, encounter a GetUpdates corresponding to the just-committed
4773 // deletion update. The undeletion should prevail.
4775 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4776 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4777 ExpectSyncedAndCreated();
4780 // Test processing of undeletion GetUpdateses.
4781 TEST_F(SyncerUndeletionTest
, UndeleteAfterOtherClientDeletes
) {
4783 ExpectUnsyncedCreation();
4786 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4787 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4788 ExpectSyncedAndCreated();
4790 // Add a delete from the server.
4791 sync_pb::SyncEntity
* update1
= mock_server_
->AddUpdateFromLastCommit();
4792 update1
->set_originator_cache_guid(local_cache_guid());
4793 update1
->set_originator_client_item_id(local_id_
.GetServerId());
4795 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4796 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4797 ExpectSyncedAndCreated();
4799 // Some other client deletes the item.
4801 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4802 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4803 mock_server_
->AddUpdateTombstone(entry
.GetId());
4807 // The update ought to have applied successfully.
4808 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4809 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4810 ExpectSyncedAndDeleted();
4812 // Undelete it locally.
4814 ExpectUnsyncedUndeletion();
4816 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4817 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4818 ExpectSyncedAndCreated();
4820 // Now, encounter a GetUpdates corresponding to the just-committed
4821 // deletion update. The undeletion should prevail.
4822 sync_pb::SyncEntity
* update2
= mock_server_
->AddUpdateFromLastCommit();
4823 update2
->set_originator_cache_guid(local_cache_guid());
4824 update2
->set_originator_client_item_id(local_id_
.GetServerId());
4826 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4827 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4828 ExpectSyncedAndCreated();
4831 TEST_F(SyncerUndeletionTest
, UndeleteAfterOtherClientDeletesImmediately
) {
4833 ExpectUnsyncedCreation();
4836 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4837 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4838 ExpectSyncedAndCreated();
4840 // Some other client deletes the item before we get a chance
4841 // to GetUpdates our original request.
4843 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4844 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4845 mock_server_
->AddUpdateTombstone(entry
.GetId());
4849 // The update ought to have applied successfully.
4850 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4851 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4852 ExpectSyncedAndDeleted();
4854 // Undelete it locally.
4856 ExpectUnsyncedUndeletion();
4858 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4859 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4860 ExpectSyncedAndCreated();
4862 // Now, encounter a GetUpdates corresponding to the just-committed
4863 // deletion update. The undeletion should prevail.
4864 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
4865 update
->set_originator_cache_guid(local_cache_guid());
4866 update
->set_originator_client_item_id(local_id_
.GetServerId());
4868 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4869 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4870 ExpectSyncedAndCreated();
4873 TEST_F(SyncerUndeletionTest
, OtherClientUndeletes
) {
4875 ExpectUnsyncedCreation();
4878 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4879 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4880 ExpectSyncedAndCreated();
4882 // Get the updates of our just-committed entry.
4883 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
4884 update
->set_originator_cache_guid(local_cache_guid());
4885 update
->set_originator_client_item_id(local_id_
.GetServerId());
4887 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4888 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4889 ExpectSyncedAndCreated();
4891 // We delete the item.
4893 ExpectUnsyncedDeletion();
4896 // The update ought to have applied successfully.
4897 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4898 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4899 ExpectSyncedAndDeleted();
4901 // Now, encounter a GetUpdates corresponding to the just-committed
4903 mock_server_
->AddUpdateFromLastCommit();
4905 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4906 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4907 ExpectSyncedAndDeleted();
4909 // Some other client undeletes the item.
4911 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4912 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4913 mock_server_
->AddUpdateBookmark(
4915 entry
.GetParentId(),
4916 "Thadeusz", 100, 1000,
4917 local_cache_guid(), local_id_
.GetServerId());
4919 mock_server_
->SetLastUpdateClientTag(client_tag_
);
4921 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4922 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4923 ExpectSyncedAndCreated();
4925 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4926 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4927 EXPECT_EQ("Thadeusz", entry
.GetNonUniqueName());
4931 TEST_F(SyncerUndeletionTest
, OtherClientUndeletesImmediately
) {
4933 ExpectUnsyncedCreation();
4936 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4937 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4938 ExpectSyncedAndCreated();
4940 // Get the updates of our just-committed entry.
4941 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
4942 update
->set_originator_cache_guid(local_cache_guid());
4944 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4945 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4946 update
->set_originator_client_item_id(local_id_
.GetServerId());
4949 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4950 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4951 ExpectSyncedAndCreated();
4953 // We delete the item.
4955 ExpectUnsyncedDeletion();
4958 // The update ought to have applied successfully.
4959 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4960 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4961 ExpectSyncedAndDeleted();
4963 // Some other client undeletes before we see the update from our
4966 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4967 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4968 mock_server_
->AddUpdateBookmark(
4970 entry
.GetParentId(),
4971 "Thadeusz", 100, 1000,
4972 local_cache_guid(), local_id_
.GetServerId());
4974 mock_server_
->SetLastUpdateClientTag(client_tag_
);
4976 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4977 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4978 ExpectSyncedAndCreated();
4980 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4981 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4982 EXPECT_EQ("Thadeusz", entry
.GetNonUniqueName());
4987 TEST_PARAM_BOOKMARK_ENABLE_BIT
,
4988 TEST_PARAM_AUTOFILL_ENABLE_BIT
,
4989 TEST_PARAM_BIT_COUNT
4994 public ::testing::WithParamInterface
<int> {
4996 bool ShouldFailBookmarkCommit() {
4997 return (GetParam() & (1 << TEST_PARAM_BOOKMARK_ENABLE_BIT
)) == 0;
4999 bool ShouldFailAutofillCommit() {
5000 return (GetParam() & (1 << TEST_PARAM_AUTOFILL_ENABLE_BIT
)) == 0;
5004 INSTANTIATE_TEST_CASE_P(ExtensionsActivity
,
5006 testing::Range(0, 1 << TEST_PARAM_BIT_COUNT
));
5008 TEST_P(MixedResult
, ExtensionsActivity
) {
5010 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
5012 MutableEntry
pref(&wtrans
, CREATE
, PREFERENCES
, wtrans
.root_id(), "pref");
5013 ASSERT_TRUE(pref
.good());
5014 pref
.PutIsUnsynced(true);
5016 MutableEntry
bookmark(
5017 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "bookmark");
5018 ASSERT_TRUE(bookmark
.good());
5019 bookmark
.PutIsUnsynced(true);
5021 if (ShouldFailBookmarkCommit()) {
5022 mock_server_
->SetTransientErrorId(bookmark
.GetId());
5025 if (ShouldFailAutofillCommit()) {
5026 mock_server_
->SetTransientErrorId(pref
.GetId());
5031 // Put some extenions activity records into the monitor.
5033 ExtensionsActivity::Records records
;
5034 records
["ABC"].extension_id
= "ABC";
5035 records
["ABC"].bookmark_write_count
= 2049U;
5036 records
["xyz"].extension_id
= "xyz";
5037 records
["xyz"].bookmark_write_count
= 4U;
5038 context_
->extensions_activity()->PutRecords(records
);
5043 ExtensionsActivity::Records final_monitor_records
;
5044 context_
->extensions_activity()->GetAndClearRecords(&final_monitor_records
);
5045 if (ShouldFailBookmarkCommit()) {
5046 ASSERT_EQ(2U, final_monitor_records
.size())
5047 << "Should restore records after unsuccessful bookmark commit.";
5048 EXPECT_EQ("ABC", final_monitor_records
["ABC"].extension_id
);
5049 EXPECT_EQ("xyz", final_monitor_records
["xyz"].extension_id
);
5050 EXPECT_EQ(2049U, final_monitor_records
["ABC"].bookmark_write_count
);
5051 EXPECT_EQ(4U, final_monitor_records
["xyz"].bookmark_write_count
);
5053 EXPECT_TRUE(final_monitor_records
.empty())
5054 << "Should not restore records after successful bookmark commit.";
5058 } // namespace syncer