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 OnReceivedSessionsCommitDelay(
216 const base::TimeDelta
& new_delay
) OVERRIDE
{
217 last_sessions_commit_delay_seconds_
= new_delay
;
219 virtual void OnReceivedClientInvalidationHintBufferSize(
221 last_client_invalidation_hint_buffer_size_
= size
;
223 virtual void OnReceivedGuRetryDelay(const base::TimeDelta
& delay
) OVERRIDE
{}
224 virtual void OnReceivedMigrationRequest(ModelTypeSet types
) OVERRIDE
{}
225 virtual void OnProtocolEvent(const ProtocolEvent
& event
) OVERRIDE
{}
226 virtual void OnSyncProtocolError(const SyncProtocolError
& error
) OVERRIDE
{}
228 void GetModelSafeRoutingInfo(ModelSafeRoutingInfo
* out
) {
229 // We're just testing the sync engine here, so we shunt everything to
230 // the SyncerThread. Datatypes which aren't enabled aren't in the map.
231 for (ModelTypeSet::Iterator it
= enabled_datatypes_
.First();
232 it
.Good(); it
.Inc()) {
233 (*out
)[it
.Get()] = GROUP_PASSIVE
;
237 virtual void OnSyncCycleEvent(const SyncCycleEvent
& event
) OVERRIDE
{
238 DVLOG(1) << "HandleSyncEngineEvent in unittest " << event
.what_happened
;
239 // we only test for entry-specific events, not status changed ones.
240 switch (event
.what_happened
) {
241 case SyncCycleEvent::SYNC_CYCLE_BEGIN
: // Fall through.
242 case SyncCycleEvent::STATUS_CHANGED
:
243 case SyncCycleEvent::SYNC_CYCLE_ENDED
:
246 CHECK(false) << "Handling unknown error type in unit tests!!";
248 saw_syncer_event_
= true;
251 virtual void OnActionableError(const SyncProtocolError
& error
) OVERRIDE
{}
252 virtual void OnRetryTimeChanged(base::Time retry_time
) OVERRIDE
{}
253 virtual void OnThrottledTypesChanged(ModelTypeSet throttled_types
) OVERRIDE
{}
254 virtual void OnMigrationRequested(ModelTypeSet types
) OVERRIDE
{}
256 void ResetSession() {
257 session_
.reset(SyncSession::Build(context_
.get(), this));
260 void SyncShareNudge() {
263 // Pretend we've seen a local change, to make the nudge_tracker look normal.
264 nudge_tracker_
.RecordLocalChange(ModelTypeSet(BOOKMARKS
));
267 syncer_
->NormalSyncShare(
268 context_
->GetEnabledTypes(),
273 void SyncShareConfigure() {
275 EXPECT_TRUE(syncer_
->ConfigureSyncShare(
276 context_
->GetEnabledTypes(),
277 sync_pb::GetUpdatesCallerInfo::RECONFIGURATION
,
281 virtual void SetUp() {
283 mock_server_
.reset(new MockConnectionManager(directory(),
284 &cancelation_signal_
));
285 debug_info_getter_
.reset(new MockDebugInfoGetter
);
286 EnableDatatype(BOOKMARKS
);
287 EnableDatatype(NIGORI
);
288 EnableDatatype(PREFERENCES
);
289 EnableDatatype(NIGORI
);
290 workers_
.push_back(scoped_refptr
<ModelSafeWorker
>(
291 new FakeModelWorker(GROUP_PASSIVE
)));
292 std::vector
<SyncEngineEventListener
*> listeners
;
293 listeners
.push_back(this);
295 ModelSafeRoutingInfo routing_info
;
296 GetModelSafeRoutingInfo(&routing_info
);
298 model_type_registry_
.reset(
299 new ModelTypeRegistry(workers_
, directory(), &mock_nudge_handler_
));
300 model_type_registry_
->RegisterDirectoryTypeDebugInfoObserver(
304 new SyncSessionContext(
305 mock_server_
.get(), directory(),
306 extensions_activity_
,
307 listeners
, debug_info_getter_
.get(),
308 model_type_registry_
.get(),
309 true, // enable keystore encryption
310 false, // force enable pre-commit GU avoidance experiment
311 "fake_invalidator_client_id"));
312 context_
->SetRoutingInfo(routing_info
);
313 syncer_
= new Syncer(&cancelation_signal_
);
315 syncable::ReadTransaction
trans(FROM_HERE
, directory());
316 syncable::Directory::Metahandles children
;
317 directory()->GetChildHandlesById(&trans
, trans
.root_id(), &children
);
318 ASSERT_EQ(0u, children
.size());
319 saw_syncer_event_
= false;
320 root_id_
= TestIdFactory::root();
321 parent_id_
= ids_
.MakeServer("parent id");
322 child_id_
= ids_
.MakeServer("child id");
323 directory()->set_store_birthday(mock_server_
->store_birthday());
324 mock_server_
->SetKeystoreKey("encryption_key");
327 virtual void TearDown() {
328 model_type_registry_
->UnregisterDirectoryTypeDebugInfoObserver(
330 mock_server_
.reset();
333 dir_maker_
.TearDown();
336 void WriteTestDataToEntry(WriteTransaction
* trans
, MutableEntry
* entry
) {
337 EXPECT_FALSE(entry
->GetIsDir());
338 EXPECT_FALSE(entry
->GetIsDel());
339 sync_pb::EntitySpecifics specifics
;
340 specifics
.mutable_bookmark()->set_url("http://demo/");
341 specifics
.mutable_bookmark()->set_favicon("PNG");
342 entry
->PutSpecifics(specifics
);
343 entry
->PutIsUnsynced(true);
345 void VerifyTestDataInEntry(BaseTransaction
* trans
, Entry
* entry
) {
346 EXPECT_FALSE(entry
->GetIsDir());
347 EXPECT_FALSE(entry
->GetIsDel());
348 VerifyTestBookmarkDataInEntry(entry
);
350 void VerifyTestBookmarkDataInEntry(Entry
* entry
) {
351 const sync_pb::EntitySpecifics
& specifics
= entry
->GetSpecifics();
352 EXPECT_TRUE(specifics
.has_bookmark());
353 EXPECT_EQ("PNG", specifics
.bookmark().favicon());
354 EXPECT_EQ("http://demo/", specifics
.bookmark().url());
357 void VerifyHierarchyConflictsReported(
358 const sync_pb::ClientToServerMessage
& message
) {
359 // Our request should have included a warning about hierarchy conflicts.
360 const sync_pb::ClientStatus
& client_status
= message
.client_status();
361 EXPECT_TRUE(client_status
.has_hierarchy_conflict_detected());
362 EXPECT_TRUE(client_status
.hierarchy_conflict_detected());
365 void VerifyNoHierarchyConflictsReported(
366 const sync_pb::ClientToServerMessage
& message
) {
367 // Our request should have reported no hierarchy conflicts detected.
368 const sync_pb::ClientStatus
& client_status
= message
.client_status();
369 EXPECT_TRUE(client_status
.has_hierarchy_conflict_detected());
370 EXPECT_FALSE(client_status
.hierarchy_conflict_detected());
373 void VerifyHierarchyConflictsUnspecified(
374 const sync_pb::ClientToServerMessage
& message
) {
375 // Our request should have neither confirmed nor denied hierarchy conflicts.
376 const sync_pb::ClientStatus
& client_status
= message
.client_status();
377 EXPECT_FALSE(client_status
.has_hierarchy_conflict_detected());
380 sync_pb::EntitySpecifics
DefaultBookmarkSpecifics() {
381 sync_pb::EntitySpecifics result
;
382 AddDefaultFieldValue(BOOKMARKS
, &result
);
386 sync_pb::EntitySpecifics
DefaultPreferencesSpecifics() {
387 sync_pb::EntitySpecifics result
;
388 AddDefaultFieldValue(PREFERENCES
, &result
);
391 // Enumeration of alterations to entries for commit ordering tests.
393 LIST_END
= 0, // Denotes the end of the list of features from below.
394 SYNCED
, // Items are unsynced by default
400 struct CommitOrderingTest
{
401 // expected commit index.
403 // Details about the item
405 syncable::Id parent_id
;
406 EntryFeature features
[10];
408 static CommitOrderingTest
MakeLastCommitItem() {
409 CommitOrderingTest last_commit_item
;
410 last_commit_item
.commit_index
= -1;
411 last_commit_item
.id
= TestIdFactory::root();
412 return last_commit_item
;
416 void RunCommitOrderingTest(CommitOrderingTest
* test
) {
417 map
<int, syncable::Id
> expected_positions
;
418 { // Transaction scope.
419 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
420 while (!test
->id
.IsRoot()) {
421 if (test
->commit_index
>= 0) {
422 map
<int, syncable::Id
>::value_type
entry(test
->commit_index
,
424 bool double_position
= !expected_positions
.insert(entry
).second
;
425 ASSERT_FALSE(double_position
) << "Two id's expected at one position";
427 string utf8_name
= test
->id
.GetServerId();
428 string
name(utf8_name
.begin(), utf8_name
.end());
429 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, test
->parent_id
, name
);
431 entry
.PutId(test
->id
);
432 if (test
->id
.ServerKnows()) {
433 entry
.PutBaseVersion(5);
434 entry
.PutServerVersion(5);
435 entry
.PutServerParentId(test
->parent_id
);
437 entry
.PutIsDir(true);
438 entry
.PutIsUnsynced(true);
439 entry
.PutSpecifics(DefaultBookmarkSpecifics());
440 // Set the time to 30 seconds in the future to reduce the chance of
442 const base::Time
& now_plus_30s
=
443 base::Time::Now() + base::TimeDelta::FromSeconds(30);
444 const base::Time
& now_minus_2h
=
445 base::Time::Now() - base::TimeDelta::FromHours(2);
446 entry
.PutMtime(now_plus_30s
);
447 for (size_t i
= 0 ; i
< arraysize(test
->features
) ; ++i
) {
448 switch (test
->features
[i
]) {
452 entry
.PutIsUnsynced(false);
455 entry
.PutIsDel(true);
458 entry
.PutMtime(now_minus_2h
);
460 case MOVED_FROM_ROOT
:
461 entry
.PutServerParentId(trans
.root_id());
464 FAIL() << "Bad value in CommitOrderingTest list";
471 ASSERT_TRUE(expected_positions
.size() ==
472 mock_server_
->committed_ids().size());
473 // If this test starts failing, be aware other sort orders could be valid.
474 for (size_t i
= 0; i
< expected_positions
.size(); ++i
) {
476 EXPECT_EQ(1u, expected_positions
.count(i
));
477 EXPECT_EQ(expected_positions
[i
], mock_server_
->committed_ids()[i
]);
481 CommitCounters
GetCommitCounters(ModelType type
) {
482 return debug_info_cache_
.GetLatestCommitCounters(type
);
485 UpdateCounters
GetUpdateCounters(ModelType type
) {
486 return debug_info_cache_
.GetLatestUpdateCounters(type
);
489 StatusCounters
GetStatusCounters(ModelType type
) {
490 return debug_info_cache_
.GetLatestStatusCounters(type
);
493 Directory
* directory() {
494 return dir_maker_
.directory();
497 const std::string
local_cache_guid() {
498 return directory()->cache_guid();
501 const std::string
foreign_cache_guid() {
502 return "kqyg7097kro6GSUod+GSg==";
505 int64
CreateUnsyncedDirectory(const string
& entry_name
,
506 const string
& idstring
) {
507 return CreateUnsyncedDirectory(entry_name
,
508 syncable::Id::CreateFromServerId(idstring
));
511 int64
CreateUnsyncedDirectory(const string
& entry_name
,
512 const syncable::Id
& id
) {
513 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
515 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), entry_name
);
516 EXPECT_TRUE(entry
.good());
517 entry
.PutIsUnsynced(true);
518 entry
.PutIsDir(true);
519 entry
.PutSpecifics(DefaultBookmarkSpecifics());
520 entry
.PutBaseVersion(id
.ServerKnows() ? 1 : 0);
522 return entry
.GetMetahandle();
525 void EnableDatatype(ModelType model_type
) {
526 enabled_datatypes_
.Put(model_type
);
528 ModelSafeRoutingInfo routing_info
;
529 GetModelSafeRoutingInfo(&routing_info
);
532 context_
->SetRoutingInfo(routing_info
);
535 mock_server_
->ExpectGetUpdatesRequestTypes(enabled_datatypes_
);
538 void DisableDatatype(ModelType model_type
) {
539 enabled_datatypes_
.Remove(model_type
);
541 ModelSafeRoutingInfo routing_info
;
542 GetModelSafeRoutingInfo(&routing_info
);
545 context_
->SetRoutingInfo(routing_info
);
548 mock_server_
->ExpectGetUpdatesRequestTypes(enabled_datatypes_
);
551 Cryptographer
* GetCryptographer(syncable::BaseTransaction
* trans
) {
552 return directory()->GetCryptographer(trans
);
555 // Configures SyncSessionContext and NudgeTracker so Syncer won't call
556 // GetUpdates prior to Commit. This method can be used to ensure a Commit is
557 // not preceeded by GetUpdates.
558 void ConfigureNoGetUpdatesRequired() {
559 context_
->set_server_enabled_pre_commit_update_avoidance(true);
560 nudge_tracker_
.OnInvalidationsEnabled();
561 nudge_tracker_
.RecordSuccessfulSyncCycle();
563 ASSERT_FALSE(context_
->ShouldFetchUpdatesBeforeCommit());
564 ASSERT_FALSE(nudge_tracker_
.IsGetUpdatesRequired());
567 base::MessageLoop message_loop_
;
569 // Some ids to aid tests. Only the root one's value is specific. The rest
570 // are named for test clarity.
571 // TODO(chron): Get rid of these inbuilt IDs. They only make it
573 syncable::Id root_id_
;
574 syncable::Id parent_id_
;
575 syncable::Id child_id_
;
579 TestDirectorySetterUpper dir_maker_
;
580 FakeEncryptor encryptor_
;
581 scoped_refptr
<ExtensionsActivity
> extensions_activity_
;
582 scoped_ptr
<MockConnectionManager
> mock_server_
;
583 CancelationSignal cancelation_signal_
;
587 scoped_ptr
<SyncSession
> session_
;
588 TypeDebugInfoCache debug_info_cache_
;
589 MockNudgeHandler mock_nudge_handler_
;
590 scoped_ptr
<ModelTypeRegistry
> model_type_registry_
;
591 scoped_ptr
<SyncSessionContext
> context_
;
592 bool saw_syncer_event_
;
593 base::TimeDelta last_short_poll_interval_received_
;
594 base::TimeDelta last_long_poll_interval_received_
;
595 base::TimeDelta last_sessions_commit_delay_seconds_
;
596 int last_client_invalidation_hint_buffer_size_
;
597 std::vector
<scoped_refptr
<ModelSafeWorker
> > workers_
;
599 ModelTypeSet enabled_datatypes_
;
600 sessions::NudgeTracker nudge_tracker_
;
601 scoped_ptr
<MockDebugInfoGetter
> debug_info_getter_
;
603 DISALLOW_COPY_AND_ASSIGN(SyncerTest
);
606 TEST_F(SyncerTest
, TestCallGatherUnsyncedEntries
) {
608 Syncer::UnsyncedMetaHandles handles
;
610 syncable::ReadTransaction
trans(FROM_HERE
, directory());
611 GetUnsyncedEntries(&trans
, &handles
);
613 ASSERT_EQ(0u, handles
.size());
615 // TODO(sync): When we can dynamically connect and disconnect the mock
616 // ServerConnectionManager test disconnected GetUnsyncedEntries here. It's a
617 // regression for a very old bug.
620 TEST_F(SyncerTest
, GetCommitIdsFiltersThrottledEntries
) {
621 const ModelTypeSet
throttled_types(BOOKMARKS
);
622 sync_pb::EntitySpecifics bookmark_data
;
623 AddDefaultFieldValue(BOOKMARKS
, &bookmark_data
);
625 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
626 foreign_cache_guid(), "-1");
630 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
631 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
632 ASSERT_TRUE(A
.good());
633 A
.PutIsUnsynced(true);
634 A
.PutSpecifics(bookmark_data
);
635 A
.PutNonUniqueName("bookmark");
638 // Now sync without enabling bookmarks.
639 mock_server_
->ExpectGetUpdatesRequestTypes(
640 Difference(context_
->GetEnabledTypes(), ModelTypeSet(BOOKMARKS
)));
642 syncer_
->NormalSyncShare(
643 Difference(context_
->GetEnabledTypes(), ModelTypeSet(BOOKMARKS
)),
648 // Nothing should have been committed as bookmarks is throttled.
649 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
650 Entry
entryA(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
651 ASSERT_TRUE(entryA
.good());
652 EXPECT_TRUE(entryA
.GetIsUnsynced());
655 // Sync again with bookmarks enabled.
656 mock_server_
->ExpectGetUpdatesRequestTypes(context_
->GetEnabledTypes());
659 // It should have been committed.
660 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
661 Entry
entryA(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
662 ASSERT_TRUE(entryA
.good());
663 EXPECT_FALSE(entryA
.GetIsUnsynced());
667 // We use a macro so we can preserve the error location.
668 #define VERIFY_ENTRY(id, is_unapplied, is_unsynced, prev_initialized, \
669 parent_id, version, server_version, id_fac, rtrans) \
671 Entry entryA(rtrans, syncable::GET_BY_ID, id_fac.FromNumber(id)); \
672 ASSERT_TRUE(entryA.good()); \
673 /* We don't use EXPECT_EQ here because when the left side param is false,
674 gcc 4.6 warns about converting 'false' to pointer type for argument 1. */ \
675 EXPECT_TRUE(is_unsynced == entryA.GetIsUnsynced()); \
676 EXPECT_TRUE(is_unapplied == entryA.GetIsUnappliedUpdate()); \
677 EXPECT_TRUE(prev_initialized == \
678 IsRealDataType(GetModelTypeFromSpecifics( \
679 entryA.GetBaseServerSpecifics()))); \
680 EXPECT_TRUE(parent_id == -1 || \
681 entryA.GetParentId()== id_fac.FromNumber(parent_id)); \
682 EXPECT_EQ(version, entryA.GetBaseVersion()); \
683 EXPECT_EQ(server_version, entryA.GetServerVersion()); \
686 TEST_F(SyncerTest
, GetCommitIdsFiltersUnreadyEntries
) {
687 KeyParams key_params
= {"localhost", "dummy", "foobar"};
688 KeyParams other_params
= {"localhost", "dummy", "foobar2"};
689 sync_pb::EntitySpecifics bookmark
, encrypted_bookmark
;
690 bookmark
.mutable_bookmark()->set_url("url");
691 bookmark
.mutable_bookmark()->set_title("title");
692 AddDefaultFieldValue(BOOKMARKS
, &encrypted_bookmark
);
693 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
694 foreign_cache_guid(), "-1");
695 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
696 foreign_cache_guid(), "-2");
697 mock_server_
->AddUpdateDirectory(3, 0, "C", 10, 10,
698 foreign_cache_guid(), "-3");
699 mock_server_
->AddUpdateDirectory(4, 0, "D", 10, 10,
700 foreign_cache_guid(), "-4");
702 // Server side change will put A in conflict.
703 mock_server_
->AddUpdateDirectory(1, 0, "A", 20, 20,
704 foreign_cache_guid(), "-1");
706 // Mark bookmarks as encrypted and set the cryptographer to have pending
708 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
709 Cryptographer
other_cryptographer(&encryptor_
);
710 other_cryptographer
.AddKey(other_params
);
711 sync_pb::EntitySpecifics specifics
;
712 sync_pb::NigoriSpecifics
* nigori
= specifics
.mutable_nigori();
713 other_cryptographer
.GetKeys(nigori
->mutable_encryption_keybag());
714 dir_maker_
.encryption_handler()->EnableEncryptEverything();
715 // Set up with an old passphrase, but have pending keys
716 GetCryptographer(&wtrans
)->AddKey(key_params
);
717 GetCryptographer(&wtrans
)->Encrypt(bookmark
,
718 encrypted_bookmark
.mutable_encrypted());
719 GetCryptographer(&wtrans
)->SetPendingKeys(nigori
->encryption_keybag());
721 // In conflict but properly encrypted.
722 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
723 ASSERT_TRUE(A
.good());
724 A
.PutIsUnsynced(true);
725 A
.PutSpecifics(encrypted_bookmark
);
726 A
.PutNonUniqueName(kEncryptedString
);
727 // Not in conflict and properly encrypted.
728 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
729 ASSERT_TRUE(B
.good());
730 B
.PutIsUnsynced(true);
731 B
.PutSpecifics(encrypted_bookmark
);
732 B
.PutNonUniqueName(kEncryptedString
);
733 // Unencrypted specifics.
734 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
735 ASSERT_TRUE(C
.good());
736 C
.PutIsUnsynced(true);
737 C
.PutNonUniqueName(kEncryptedString
);
738 // Unencrypted non_unique_name.
739 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
740 ASSERT_TRUE(D
.good());
741 D
.PutIsUnsynced(true);
742 D
.PutSpecifics(encrypted_bookmark
);
743 D
.PutNonUniqueName("not encrypted");
747 // Nothing should have commited due to bookmarks being encrypted and
748 // the cryptographer having pending keys. A would have been resolved
749 // as a simple conflict, but still be unsynced until the next sync cycle.
750 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
751 VERIFY_ENTRY(1, false, true, false, 0, 20, 20, ids_
, &rtrans
);
752 VERIFY_ENTRY(2, false, true, false, 0, 10, 10, ids_
, &rtrans
);
753 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_
, &rtrans
);
754 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_
, &rtrans
);
756 // Resolve the pending keys.
757 GetCryptographer(&rtrans
)->DecryptPendingKeys(other_params
);
761 // All properly encrypted and non-conflicting items should commit. "A" was
762 // conflicting, but last sync cycle resolved it as simple conflict, so on
763 // this sync cycle it committed succesfullly.
764 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
765 // Committed successfully.
766 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_
, &rtrans
);
767 // Committed successfully.
768 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_
, &rtrans
);
769 // Was not properly encrypted.
770 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_
, &rtrans
);
771 // Was not properly encrypted.
772 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_
, &rtrans
);
775 // Fix the remaining items.
776 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
777 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
778 ASSERT_TRUE(C
.good());
779 C
.PutSpecifics(encrypted_bookmark
);
780 C
.PutNonUniqueName(kEncryptedString
);
781 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
782 ASSERT_TRUE(D
.good());
783 D
.PutSpecifics(encrypted_bookmark
);
784 D
.PutNonUniqueName(kEncryptedString
);
788 const StatusController
& status_controller
= session_
->status_controller();
790 EXPECT_EQ(status_controller
.model_neutral_state().commit_result
, SYNCER_OK
);
791 // None should be unsynced anymore.
792 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
793 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_
, &rtrans
);
794 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_
, &rtrans
);
795 VERIFY_ENTRY(3, false, false, false, 0, 11, 11, ids_
, &rtrans
);
796 VERIFY_ENTRY(4, false, false, false, 0, 11, 11, ids_
, &rtrans
);
800 TEST_F(SyncerTest
, EncryptionAwareConflicts
) {
801 KeyParams key_params
= {"localhost", "dummy", "foobar"};
802 Cryptographer
other_cryptographer(&encryptor_
);
803 other_cryptographer
.AddKey(key_params
);
804 sync_pb::EntitySpecifics bookmark
, encrypted_bookmark
, modified_bookmark
;
805 bookmark
.mutable_bookmark()->set_title("title");
806 other_cryptographer
.Encrypt(bookmark
,
807 encrypted_bookmark
.mutable_encrypted());
808 AddDefaultFieldValue(BOOKMARKS
, &encrypted_bookmark
);
809 modified_bookmark
.mutable_bookmark()->set_title("title2");
810 other_cryptographer
.Encrypt(modified_bookmark
,
811 modified_bookmark
.mutable_encrypted());
812 sync_pb::EntitySpecifics pref
, encrypted_pref
, modified_pref
;
813 pref
.mutable_preference()->set_name("name");
814 AddDefaultFieldValue(PREFERENCES
, &encrypted_pref
);
815 other_cryptographer
.Encrypt(pref
,
816 encrypted_pref
.mutable_encrypted());
817 modified_pref
.mutable_preference()->set_name("name2");
818 other_cryptographer
.Encrypt(modified_pref
,
819 modified_pref
.mutable_encrypted());
821 // Mark bookmarks and preferences as encrypted and set the cryptographer to
822 // have pending keys.
823 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
824 sync_pb::EntitySpecifics specifics
;
825 sync_pb::NigoriSpecifics
* nigori
= specifics
.mutable_nigori();
826 other_cryptographer
.GetKeys(nigori
->mutable_encryption_keybag());
827 dir_maker_
.encryption_handler()->EnableEncryptEverything();
828 GetCryptographer(&wtrans
)->SetPendingKeys(nigori
->encryption_keybag());
829 EXPECT_TRUE(GetCryptographer(&wtrans
)->has_pending_keys());
832 // We need to remember the exact position of our local items, so we can
833 // make updates that do not modify those positions.
838 mock_server_
->AddUpdateSpecifics(1, 0, "A", 10, 10, true, 0, bookmark
,
839 foreign_cache_guid(), "-1");
840 mock_server_
->AddUpdateSpecifics(2, 1, "B", 10, 10, false, 2, bookmark
,
841 foreign_cache_guid(), "-2");
842 mock_server_
->AddUpdateSpecifics(3, 1, "C", 10, 10, false, 1, bookmark
,
843 foreign_cache_guid(), "-3");
844 mock_server_
->AddUpdateSpecifics(4, 0, "D", 10, 10, false, 0, pref
);
847 // Initial state. Everything is normal.
848 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
849 VERIFY_ENTRY(1, false, false, false, 0, 10, 10, ids_
, &rtrans
);
850 VERIFY_ENTRY(2, false, false, false, 1, 10, 10, ids_
, &rtrans
);
851 VERIFY_ENTRY(3, false, false, false, 1, 10, 10, ids_
, &rtrans
);
852 VERIFY_ENTRY(4, false, false, false, 0, 10, 10, ids_
, &rtrans
);
854 Entry
entry1(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
855 ASSERT_TRUE(entry1
.GetUniquePosition().Equals(
856 entry1
.GetServerUniquePosition()));
857 pos1
= entry1
.GetUniquePosition();
858 Entry
entry2(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(2));
859 pos2
= entry2
.GetUniquePosition();
860 Entry
entry3(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(3));
861 pos3
= entry3
.GetUniquePosition();
864 // Server side encryption will not be applied due to undecryptable data.
865 // At this point, BASE_SERVER_SPECIFICS should be filled for all four items.
866 mock_server_
->AddUpdateSpecifics(1, 0, kEncryptedString
, 20, 20, true, 0,
868 foreign_cache_guid(), "-1");
869 mock_server_
->AddUpdateSpecifics(2, 1, kEncryptedString
, 20, 20, false, 2,
871 foreign_cache_guid(), "-2");
872 mock_server_
->AddUpdateSpecifics(3, 1, kEncryptedString
, 20, 20, false, 1,
874 foreign_cache_guid(), "-3");
875 mock_server_
->AddUpdateSpecifics(4, 0, kEncryptedString
, 20, 20, false, 0,
877 foreign_cache_guid(), "-4");
880 // All should be unapplied due to being undecryptable and have a valid
881 // BASE_SERVER_SPECIFICS.
882 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
883 VERIFY_ENTRY(1, true, false, true, 0, 10, 20, ids_
, &rtrans
);
884 VERIFY_ENTRY(2, true, false, true, 1, 10, 20, ids_
, &rtrans
);
885 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_
, &rtrans
);
886 VERIFY_ENTRY(4, true, false, true, 0, 10, 20, ids_
, &rtrans
);
889 // Server side change that don't modify anything should not affect
890 // BASE_SERVER_SPECIFICS (such as name changes and mtime changes).
891 mock_server_
->AddUpdateSpecifics(1, 0, kEncryptedString
, 30, 30, true, 0,
893 foreign_cache_guid(), "-1");
894 mock_server_
->AddUpdateSpecifics(2, 1, kEncryptedString
, 30, 30, false, 2,
896 foreign_cache_guid(), "-2");
897 // Item 3 doesn't change.
898 mock_server_
->AddUpdateSpecifics(4, 0, kEncryptedString
, 30, 30, false, 0,
900 foreign_cache_guid(), "-4");
903 // Items 1, 2, and 4 should have newer server versions, 3 remains the same.
904 // All should remain unapplied due to be undecryptable.
905 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
906 VERIFY_ENTRY(1, true, false, true, 0, 10, 30, ids_
, &rtrans
);
907 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_
, &rtrans
);
908 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_
, &rtrans
);
909 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_
, &rtrans
);
912 // Positional changes, parent changes, and specifics changes should reset
913 // BASE_SERVER_SPECIFICS.
914 // Became unencrypted.
915 mock_server_
->AddUpdateSpecifics(1, 0, "A", 40, 40, true, 0, bookmark
,
916 foreign_cache_guid(), "-1");
917 // Reordered to after item 2.
918 mock_server_
->AddUpdateSpecifics(3, 1, kEncryptedString
, 30, 30, false, 3,
920 foreign_cache_guid(), "-3");
923 // Items 2 and 4 should be the only ones with BASE_SERVER_SPECIFICS set.
924 // Items 1 is now unencrypted, so should have applied normally.
925 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
926 VERIFY_ENTRY(1, false, false, false, 0, 40, 40, ids_
, &rtrans
);
927 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_
, &rtrans
);
928 VERIFY_ENTRY(3, true, false, false, 1, 10, 30, ids_
, &rtrans
);
929 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_
, &rtrans
);
932 // Make local changes, which should remain unsynced for items 2, 3, 4.
934 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
935 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
936 ASSERT_TRUE(A
.good());
937 A
.PutSpecifics(modified_bookmark
);
938 A
.PutNonUniqueName(kEncryptedString
);
939 A
.PutIsUnsynced(true);
940 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
941 ASSERT_TRUE(B
.good());
942 B
.PutSpecifics(modified_bookmark
);
943 B
.PutNonUniqueName(kEncryptedString
);
944 B
.PutIsUnsynced(true);
945 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
946 ASSERT_TRUE(C
.good());
947 C
.PutSpecifics(modified_bookmark
);
948 C
.PutNonUniqueName(kEncryptedString
);
949 C
.PutIsUnsynced(true);
950 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
951 ASSERT_TRUE(D
.good());
952 D
.PutSpecifics(modified_pref
);
953 D
.PutNonUniqueName(kEncryptedString
);
954 D
.PutIsUnsynced(true);
958 // Item 1 remains unsynced due to there being pending keys.
959 // Items 2, 3, 4 should remain unsynced since they were not up to date.
960 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
961 VERIFY_ENTRY(1, false, true, false, 0, 40, 40, ids_
, &rtrans
);
962 VERIFY_ENTRY(2, true, true, true, 1, 10, 30, ids_
, &rtrans
);
963 VERIFY_ENTRY(3, true, true, false, 1, 10, 30, ids_
, &rtrans
);
964 VERIFY_ENTRY(4, true, true, true, 0, 10, 30, ids_
, &rtrans
);
968 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
969 // Resolve the pending keys.
970 GetCryptographer(&rtrans
)->DecryptPendingKeys(key_params
);
972 // First cycle resolves conflicts, second cycle commits changes.
974 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_server_overwrites
);
975 EXPECT_EQ(1, GetUpdateCounters(PREFERENCES
).num_server_overwrites
);
976 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_local_overwrites
);
978 // We successfully commited item(s).
979 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS
).num_commits_attempted
);
980 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS
).num_commits_success
);
981 EXPECT_EQ(1, GetCommitCounters(PREFERENCES
).num_commits_attempted
);
982 EXPECT_EQ(1, GetCommitCounters(PREFERENCES
).num_commits_success
);
986 // Everything should be resolved now. The local changes should have
987 // overwritten the server changes for 2 and 4, while the server changes
988 // overwrote the local for entry 3.
990 // Expect there will be no new overwrites.
991 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_server_overwrites
);
992 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS
).num_local_overwrites
);
994 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS
).num_commits_success
);
995 EXPECT_EQ(1, GetCommitCounters(PREFERENCES
).num_commits_success
);
997 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
998 VERIFY_ENTRY(1, false, false, false, 0, 41, 41, ids_
, &rtrans
);
999 VERIFY_ENTRY(2, false, false, false, 1, 31, 31, ids_
, &rtrans
);
1000 VERIFY_ENTRY(3, false, false, false, 1, 30, 30, ids_
, &rtrans
);
1001 VERIFY_ENTRY(4, false, false, false, 0, 31, 31, ids_
, &rtrans
);
1006 TEST_F(SyncerTest
, TestGetUnsyncedAndSimpleCommit
) {
1008 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1009 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1010 ASSERT_TRUE(parent
.good());
1011 parent
.PutIsUnsynced(true);
1012 parent
.PutIsDir(true);
1013 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1014 parent
.PutBaseVersion(1);
1015 parent
.PutId(parent_id_
);
1016 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete");
1017 ASSERT_TRUE(child
.good());
1018 child
.PutId(child_id_
);
1019 child
.PutBaseVersion(1);
1020 WriteTestDataToEntry(&wtrans
, &child
);
1024 ASSERT_EQ(2u, mock_server_
->committed_ids().size());
1025 // If this test starts failing, be aware other sort orders could be valid.
1026 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1027 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1029 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1030 Entry
entry(&rt
, syncable::GET_BY_ID
, child_id_
);
1031 ASSERT_TRUE(entry
.good());
1032 VerifyTestDataInEntry(&rt
, &entry
);
1036 TEST_F(SyncerTest
, TestPurgeWhileUnsynced
) {
1037 // Similar to above, but throw a purge operation into the mix. Bug 49278.
1038 syncable::Id pref_node_id
= TestIdFactory::MakeServer("Tim");
1040 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1041 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1042 ASSERT_TRUE(parent
.good());
1043 parent
.PutIsUnsynced(true);
1044 parent
.PutIsDir(true);
1045 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1046 parent
.PutBaseVersion(1);
1047 parent
.PutId(parent_id_
);
1048 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete");
1049 ASSERT_TRUE(child
.good());
1050 child
.PutId(child_id_
);
1051 child
.PutBaseVersion(1);
1052 WriteTestDataToEntry(&wtrans
, &child
);
1054 MutableEntry
parent2(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Tim");
1055 ASSERT_TRUE(parent2
.good());
1056 parent2
.PutIsUnsynced(true);
1057 parent2
.PutIsDir(true);
1058 parent2
.PutSpecifics(DefaultPreferencesSpecifics());
1059 parent2
.PutBaseVersion(1);
1060 parent2
.PutId(pref_node_id
);
1063 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES
),
1068 ASSERT_EQ(2U, mock_server_
->committed_ids().size());
1069 // If this test starts failing, be aware other sort orders could be valid.
1070 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1071 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1073 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1074 Entry
entry(&rt
, syncable::GET_BY_ID
, child_id_
);
1075 ASSERT_TRUE(entry
.good());
1076 VerifyTestDataInEntry(&rt
, &entry
);
1078 directory()->SaveChanges();
1080 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1081 Entry
entry(&rt
, syncable::GET_BY_ID
, pref_node_id
);
1082 ASSERT_FALSE(entry
.good());
1086 TEST_F(SyncerTest
, TestPurgeWhileUnapplied
) {
1087 // Similar to above, but for unapplied items. Bug 49278.
1089 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1090 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1091 ASSERT_TRUE(parent
.good());
1092 parent
.PutIsUnappliedUpdate(true);
1093 parent
.PutIsDir(true);
1094 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1095 parent
.PutBaseVersion(1);
1096 parent
.PutId(parent_id_
);
1099 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(BOOKMARKS
),
1104 directory()->SaveChanges();
1106 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1107 Entry
entry(&rt
, syncable::GET_BY_ID
, parent_id_
);
1108 ASSERT_FALSE(entry
.good());
1112 TEST_F(SyncerTest
, TestPurgeWithJournal
) {
1114 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1115 MutableEntry
parent(&wtrans
, syncable::CREATE
, BOOKMARKS
, wtrans
.root_id(),
1117 ASSERT_TRUE(parent
.good());
1118 parent
.PutIsDir(true);
1119 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1120 parent
.PutBaseVersion(1);
1121 parent
.PutId(parent_id_
);
1122 MutableEntry
child(&wtrans
, syncable::CREATE
, BOOKMARKS
, parent_id_
,
1124 ASSERT_TRUE(child
.good());
1125 child
.PutId(child_id_
);
1126 child
.PutBaseVersion(1);
1127 WriteTestDataToEntry(&wtrans
, &child
);
1129 MutableEntry
parent2(&wtrans
, syncable::CREATE
, PREFERENCES
,
1130 wtrans
.root_id(), "Tim");
1131 ASSERT_TRUE(parent2
.good());
1132 parent2
.PutIsDir(true);
1133 parent2
.PutSpecifics(DefaultPreferencesSpecifics());
1134 parent2
.PutBaseVersion(1);
1135 parent2
.PutId(TestIdFactory::MakeServer("Tim"));
1138 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES
, BOOKMARKS
),
1139 ModelTypeSet(BOOKMARKS
),
1142 // Verify bookmark nodes are saved in delete journal but not preference
1144 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1145 syncable::DeleteJournal
* delete_journal
= directory()->delete_journal();
1146 EXPECT_EQ(2u, delete_journal
->GetDeleteJournalSize(&rt
));
1147 syncable::EntryKernelSet journal_entries
;
1148 directory()->delete_journal()->GetDeleteJournals(&rt
, BOOKMARKS
,
1150 EXPECT_EQ(parent_id_
, (*journal_entries
.begin())->ref(syncable::ID
));
1151 EXPECT_EQ(child_id_
, (*journal_entries
.rbegin())->ref(syncable::ID
));
1155 TEST_F(SyncerTest
, ResetVersions
) {
1156 // Download the top level pref node and some pref items.
1157 mock_server_
->AddUpdateDirectory(
1158 parent_id_
, root_id_
, "prefs", 1, 10, std::string(), std::string());
1159 mock_server_
->SetLastUpdateServerTag(ModelTypeToRootTag(PREFERENCES
));
1160 mock_server_
->AddUpdatePref("id1", parent_id_
.GetServerId(), "tag1", 20, 20);
1161 mock_server_
->AddUpdatePref("id2", parent_id_
.GetServerId(), "tag2", 30, 30);
1162 mock_server_
->AddUpdatePref("id3", parent_id_
.GetServerId(), "tag3", 40, 40);
1166 // Modify one of the preferences locally, mark another one as unapplied,
1167 // and create another unsynced preference.
1168 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1169 MutableEntry
entry(&wtrans
, GET_BY_CLIENT_TAG
, "tag1");
1170 entry
.PutIsUnsynced(true);
1172 MutableEntry
entry2(&wtrans
, GET_BY_CLIENT_TAG
, "tag2");
1173 entry2
.PutIsUnappliedUpdate(true);
1175 MutableEntry
entry4(&wtrans
, CREATE
, PREFERENCES
, parent_id_
, "name");
1176 entry4
.PutUniqueClientTag("tag4");
1177 entry4
.PutIsUnsynced(true);
1181 // Reset the versions.
1182 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1183 ASSERT_TRUE(directory()->ResetVersionsForType(&wtrans
, PREFERENCES
));
1187 // Verify the synced items are all with version 1 now, with
1188 // unsynced/unapplied state preserved.
1189 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1190 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, "tag1");
1191 EXPECT_EQ(1, entry
.GetBaseVersion());
1192 EXPECT_EQ(1, entry
.GetServerVersion());
1193 EXPECT_TRUE(entry
.GetIsUnsynced());
1194 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
1195 Entry
entry2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
1196 EXPECT_EQ(1, entry2
.GetBaseVersion());
1197 EXPECT_EQ(1, entry2
.GetServerVersion());
1198 EXPECT_FALSE(entry2
.GetIsUnsynced());
1199 EXPECT_TRUE(entry2
.GetIsUnappliedUpdate());
1200 Entry
entry3(&trans
, GET_BY_CLIENT_TAG
, "tag3");
1201 EXPECT_EQ(1, entry3
.GetBaseVersion());
1202 EXPECT_EQ(1, entry3
.GetServerVersion());
1203 EXPECT_FALSE(entry3
.GetIsUnsynced());
1204 EXPECT_FALSE(entry3
.GetIsUnappliedUpdate());
1206 // Entry 4 (the locally created one) should remain the same.
1207 Entry
entry4(&trans
, GET_BY_CLIENT_TAG
, "tag4");
1208 EXPECT_EQ(-1, entry4
.GetBaseVersion());
1209 EXPECT_EQ(0, entry4
.GetServerVersion());
1210 EXPECT_TRUE(entry4
.GetIsUnsynced());
1211 EXPECT_FALSE(entry4
.GetIsUnappliedUpdate());
1215 TEST_F(SyncerTest
, TestCommitListOrderingTwoItemsTall
) {
1216 CommitOrderingTest items
[] = {
1217 {1, ids_
.FromNumber(-1001), ids_
.FromNumber(-1000)},
1218 {0, ids_
.FromNumber(-1000), ids_
.FromNumber(0)},
1219 CommitOrderingTest::MakeLastCommitItem(),
1221 RunCommitOrderingTest(items
);
1224 TEST_F(SyncerTest
, TestCommitListOrderingThreeItemsTall
) {
1225 CommitOrderingTest items
[] = {
1226 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1227 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1228 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1229 CommitOrderingTest::MakeLastCommitItem(),
1231 RunCommitOrderingTest(items
);
1234 TEST_F(SyncerTest
, TestCommitListOrderingFourItemsTall
) {
1235 CommitOrderingTest items
[] = {
1236 {3, ids_
.FromNumber(-2003), ids_
.FromNumber(-2002)},
1237 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1238 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1239 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1240 CommitOrderingTest::MakeLastCommitItem(),
1242 RunCommitOrderingTest(items
);
1245 TEST_F(SyncerTest
, TestCommitListOrderingThreeItemsTallLimitedSize
) {
1246 context_
->set_max_commit_batch_size(2);
1247 CommitOrderingTest items
[] = {
1248 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1249 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1250 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1251 CommitOrderingTest::MakeLastCommitItem(),
1253 RunCommitOrderingTest(items
);
1256 TEST_F(SyncerTest
, TestCommitListOrderingSingleDeletedItem
) {
1257 CommitOrderingTest items
[] = {
1258 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1259 CommitOrderingTest::MakeLastCommitItem(),
1261 RunCommitOrderingTest(items
);
1264 TEST_F(SyncerTest
, TestCommitListOrderingSingleUncommittedDeletedItem
) {
1265 CommitOrderingTest items
[] = {
1266 {-1, ids_
.FromNumber(-1000), ids_
.FromNumber(0), {DELETED
}},
1267 CommitOrderingTest::MakeLastCommitItem(),
1269 RunCommitOrderingTest(items
);
1272 TEST_F(SyncerTest
, TestCommitListOrderingSingleDeletedItemWithUnroll
) {
1273 CommitOrderingTest items
[] = {
1274 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1275 CommitOrderingTest::MakeLastCommitItem(),
1277 RunCommitOrderingTest(items
);
1281 TestCommitListOrderingSingleLongDeletedItemWithUnroll
) {
1282 CommitOrderingTest items
[] = {
1283 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1284 CommitOrderingTest::MakeLastCommitItem(),
1286 RunCommitOrderingTest(items
);
1289 TEST_F(SyncerTest
, TestCommitListOrderingTwoLongDeletedItemWithUnroll
) {
1290 CommitOrderingTest items
[] = {
1291 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1292 {-1, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
, OLD_MTIME
}},
1293 CommitOrderingTest::MakeLastCommitItem(),
1295 RunCommitOrderingTest(items
);
1298 TEST_F(SyncerTest
, TestCommitListOrdering3LongDeletedItemsWithSizeLimit
) {
1299 context_
->set_max_commit_batch_size(2);
1300 CommitOrderingTest items
[] = {
1301 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1302 {1, ids_
.FromNumber(1001), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1303 {2, ids_
.FromNumber(1002), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1304 CommitOrderingTest::MakeLastCommitItem(),
1306 RunCommitOrderingTest(items
);
1309 TEST_F(SyncerTest
, TestCommitListOrderingTwoDeletedItemsWithUnroll
) {
1310 CommitOrderingTest items
[] = {
1311 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1312 {-1, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
}},
1313 CommitOrderingTest::MakeLastCommitItem(),
1315 RunCommitOrderingTest(items
);
1318 TEST_F(SyncerTest
, TestCommitListOrderingComplexDeletionScenario
) {
1319 CommitOrderingTest items
[] = {
1320 { 0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1321 {-1, ids_
.FromNumber(1001), ids_
.FromNumber(0), {SYNCED
}},
1322 {1, ids_
.FromNumber(1002), ids_
.FromNumber(1001), {DELETED
, OLD_MTIME
}},
1323 {-1, ids_
.FromNumber(1003), ids_
.FromNumber(1001), {SYNCED
}},
1324 {2, ids_
.FromNumber(1004), ids_
.FromNumber(1003), {DELETED
}},
1325 CommitOrderingTest::MakeLastCommitItem(),
1327 RunCommitOrderingTest(items
);
1331 TestCommitListOrderingComplexDeletionScenarioWith2RecentDeletes
) {
1332 CommitOrderingTest items
[] = {
1333 { 0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1334 {-1, ids_
.FromNumber(1001), ids_
.FromNumber(0), {SYNCED
}},
1335 {1, ids_
.FromNumber(1002), ids_
.FromNumber(1001), {DELETED
, OLD_MTIME
}},
1336 {-1, ids_
.FromNumber(1003), ids_
.FromNumber(1001), {SYNCED
}},
1337 {2, ids_
.FromNumber(1004), ids_
.FromNumber(1003), {DELETED
}},
1338 {3, ids_
.FromNumber(1005), ids_
.FromNumber(1003), {DELETED
}},
1339 CommitOrderingTest::MakeLastCommitItem(),
1341 RunCommitOrderingTest(items
);
1344 TEST_F(SyncerTest
, TestCommitListOrderingDeleteMovedItems
) {
1345 CommitOrderingTest items
[] = {
1346 {1, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1347 {0, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
, OLD_MTIME
,
1349 CommitOrderingTest::MakeLastCommitItem(),
1351 RunCommitOrderingTest(items
);
1354 TEST_F(SyncerTest
, TestCommitListOrderingWithNesting
) {
1355 const base::Time
& now_minus_2h
=
1356 base::Time::Now() - base::TimeDelta::FromHours(2);
1358 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1360 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bob");
1361 ASSERT_TRUE(parent
.good());
1362 parent
.PutIsUnsynced(true);
1363 parent
.PutIsDir(true);
1364 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1365 parent
.PutId(ids_
.FromNumber(100));
1366 parent
.PutBaseVersion(1);
1368 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(100), "Bob");
1369 ASSERT_TRUE(child
.good());
1370 child
.PutIsUnsynced(true);
1371 child
.PutIsDir(true);
1372 child
.PutSpecifics(DefaultBookmarkSpecifics());
1373 child
.PutId(ids_
.FromNumber(101));
1374 child
.PutBaseVersion(1);
1375 MutableEntry
grandchild(
1376 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(101), "Bob");
1377 ASSERT_TRUE(grandchild
.good());
1378 grandchild
.PutId(ids_
.FromNumber(102));
1379 grandchild
.PutIsUnsynced(true);
1380 grandchild
.PutSpecifics(DefaultBookmarkSpecifics());
1381 grandchild
.PutBaseVersion(1);
1384 // Create three deleted items which deletions we expect to be sent to the
1386 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1387 ASSERT_TRUE(parent
.good());
1388 parent
.PutId(ids_
.FromNumber(103));
1389 parent
.PutIsUnsynced(true);
1390 parent
.PutIsDir(true);
1391 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1392 parent
.PutIsDel(true);
1393 parent
.PutBaseVersion(1);
1394 parent
.PutMtime(now_minus_2h
);
1396 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(103), "Pete");
1397 ASSERT_TRUE(child
.good());
1398 child
.PutId(ids_
.FromNumber(104));
1399 child
.PutIsUnsynced(true);
1400 child
.PutIsDir(true);
1401 child
.PutSpecifics(DefaultBookmarkSpecifics());
1402 child
.PutIsDel(true);
1403 child
.PutBaseVersion(1);
1404 child
.PutMtime(now_minus_2h
);
1405 MutableEntry
grandchild(
1406 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(104), "Pete");
1407 ASSERT_TRUE(grandchild
.good());
1408 grandchild
.PutId(ids_
.FromNumber(105));
1409 grandchild
.PutIsUnsynced(true);
1410 grandchild
.PutIsDel(true);
1411 grandchild
.PutIsDir(false);
1412 grandchild
.PutSpecifics(DefaultBookmarkSpecifics());
1413 grandchild
.PutBaseVersion(1);
1414 grandchild
.PutMtime(now_minus_2h
);
1419 ASSERT_EQ(6u, mock_server_
->committed_ids().size());
1420 // This test will NOT unroll deletes because SERVER_PARENT_ID is not set.
1421 // It will treat these like moves.
1422 vector
<syncable::Id
> commit_ids(mock_server_
->committed_ids());
1423 EXPECT_TRUE(ids_
.FromNumber(100) == commit_ids
[0]);
1424 EXPECT_TRUE(ids_
.FromNumber(101) == commit_ids
[1]);
1425 EXPECT_TRUE(ids_
.FromNumber(102) == commit_ids
[2]);
1426 // We don't guarantee the delete orders in this test, only that they occur
1428 std::sort(commit_ids
.begin() + 3, commit_ids
.end());
1429 EXPECT_TRUE(ids_
.FromNumber(103) == commit_ids
[3]);
1430 EXPECT_TRUE(ids_
.FromNumber(104) == commit_ids
[4]);
1431 EXPECT_TRUE(ids_
.FromNumber(105) == commit_ids
[5]);
1434 TEST_F(SyncerTest
, TestCommitListOrderingWithNewItems
) {
1435 syncable::Id parent1_id
= ids_
.MakeServer("p1");
1436 syncable::Id parent2_id
= ids_
.MakeServer("p2");
1439 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1440 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "1");
1441 ASSERT_TRUE(parent
.good());
1442 parent
.PutIsUnsynced(true);
1443 parent
.PutIsDir(true);
1444 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1445 parent
.PutId(parent1_id
);
1446 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "2");
1447 ASSERT_TRUE(child
.good());
1448 child
.PutIsUnsynced(true);
1449 child
.PutIsDir(true);
1450 child
.PutSpecifics(DefaultBookmarkSpecifics());
1451 child
.PutId(parent2_id
);
1452 parent
.PutBaseVersion(1);
1453 child
.PutBaseVersion(1);
1456 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1457 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, parent1_id
, "A");
1458 ASSERT_TRUE(parent
.good());
1459 parent
.PutIsUnsynced(true);
1460 parent
.PutIsDir(true);
1461 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1462 parent
.PutId(ids_
.FromNumber(102));
1463 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent1_id
, "B");
1464 ASSERT_TRUE(child
.good());
1465 child
.PutIsUnsynced(true);
1466 child
.PutIsDir(true);
1467 child
.PutSpecifics(DefaultBookmarkSpecifics());
1468 child
.PutId(ids_
.FromNumber(-103));
1469 parent
.PutBaseVersion(1);
1472 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1473 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, parent2_id
, "A");
1474 ASSERT_TRUE(parent
.good());
1475 parent
.PutIsUnsynced(true);
1476 parent
.PutIsDir(true);
1477 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1478 parent
.PutId(ids_
.FromNumber(-104));
1479 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent2_id
, "B");
1480 ASSERT_TRUE(child
.good());
1481 child
.PutIsUnsynced(true);
1482 child
.PutIsDir(true);
1483 child
.PutSpecifics(DefaultBookmarkSpecifics());
1484 child
.PutId(ids_
.FromNumber(105));
1485 child
.PutBaseVersion(1);
1489 ASSERT_EQ(6u, mock_server_
->committed_ids().size());
1491 // This strange iteration and std::count() usage is to allow the order to
1492 // vary. All we really care about is that parent1_id and parent2_id are the
1493 // first two IDs, and that the children make up the next four. Other than
1494 // that, ordering doesn't matter.
1496 vector
<syncable::Id
>::const_iterator i
=
1497 mock_server_
->committed_ids().begin();
1498 vector
<syncable::Id
>::const_iterator parents_begin
= i
;
1501 vector
<syncable::Id
>::const_iterator parents_end
= i
;
1502 vector
<syncable::Id
>::const_iterator children_begin
= i
;
1503 vector
<syncable::Id
>::const_iterator children_end
=
1504 mock_server_
->committed_ids().end();
1506 EXPECT_EQ(1, count(parents_begin
, parents_end
, parent1_id
));
1507 EXPECT_EQ(1, count(parents_begin
, parents_end
, parent2_id
));
1509 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(-103)));
1510 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(102)));
1511 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(105)));
1512 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(-104)));
1515 TEST_F(SyncerTest
, TestCommitListOrderingCounterexample
) {
1516 syncable::Id child2_id
= ids_
.NewServerId();
1519 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1520 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "P");
1521 ASSERT_TRUE(parent
.good());
1522 parent
.PutIsUnsynced(true);
1523 parent
.PutIsDir(true);
1524 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1525 parent
.PutId(parent_id_
);
1526 MutableEntry
child1(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "1");
1527 ASSERT_TRUE(child1
.good());
1528 child1
.PutIsUnsynced(true);
1529 child1
.PutId(child_id_
);
1530 child1
.PutSpecifics(DefaultBookmarkSpecifics());
1531 MutableEntry
child2(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "2");
1532 ASSERT_TRUE(child2
.good());
1533 child2
.PutIsUnsynced(true);
1534 child2
.PutSpecifics(DefaultBookmarkSpecifics());
1535 child2
.PutId(child2_id
);
1537 parent
.PutBaseVersion(1);
1538 child1
.PutBaseVersion(1);
1539 child2
.PutBaseVersion(1);
1543 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1544 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1545 // There are two possible valid orderings.
1546 if (child2_id
== mock_server_
->committed_ids()[1]) {
1547 EXPECT_TRUE(child2_id
== mock_server_
->committed_ids()[1]);
1548 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[2]);
1550 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1551 EXPECT_TRUE(child2_id
== mock_server_
->committed_ids()[2]);
1555 TEST_F(SyncerTest
, TestCommitListOrderingAndNewParent
) {
1556 string parent1_name
= "1";
1557 string parent2_name
= "A";
1558 string child_name
= "B";
1561 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1562 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(),
1564 ASSERT_TRUE(parent
.good());
1565 parent
.PutIsUnsynced(true);
1566 parent
.PutIsDir(true);
1567 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1568 parent
.PutId(parent_id_
);
1569 parent
.PutBaseVersion(1);
1572 syncable::Id parent2_id
= ids_
.NewLocalId();
1573 syncable::Id child_id
= ids_
.NewServerId();
1575 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1576 MutableEntry
parent2(
1577 &wtrans
, CREATE
, BOOKMARKS
, parent_id_
, parent2_name
);
1578 ASSERT_TRUE(parent2
.good());
1579 parent2
.PutIsUnsynced(true);
1580 parent2
.PutIsDir(true);
1581 parent2
.PutSpecifics(DefaultBookmarkSpecifics());
1582 parent2
.PutId(parent2_id
);
1585 &wtrans
, CREATE
, BOOKMARKS
, parent2_id
, child_name
);
1586 ASSERT_TRUE(child
.good());
1587 child
.PutIsUnsynced(true);
1588 child
.PutIsDir(true);
1589 child
.PutSpecifics(DefaultBookmarkSpecifics());
1590 child
.PutId(child_id
);
1591 child
.PutBaseVersion(1);
1595 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1596 // If this test starts failing, be aware other sort orders could be valid.
1597 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1598 EXPECT_TRUE(parent2_id
== mock_server_
->committed_ids()[1]);
1599 EXPECT_TRUE(child_id
== mock_server_
->committed_ids()[2]);
1601 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1602 // Check that things committed correctly.
1603 Entry
entry_1(&rtrans
, syncable::GET_BY_ID
, parent_id_
);
1604 EXPECT_EQ(entry_1
.GetNonUniqueName(), parent1_name
);
1605 // Check that parent2 is a subfolder of parent1.
1606 EXPECT_EQ(1, CountEntriesWithName(&rtrans
,
1610 // Parent2 was a local ID and thus should have changed on commit!
1611 Entry
pre_commit_entry_parent2(&rtrans
, syncable::GET_BY_ID
, parent2_id
);
1612 ASSERT_FALSE(pre_commit_entry_parent2
.good());
1614 // Look up the new ID.
1615 Id parent2_committed_id
=
1616 GetOnlyEntryWithName(&rtrans
, parent_id_
, parent2_name
);
1617 EXPECT_TRUE(parent2_committed_id
.ServerKnows());
1619 Entry
child(&rtrans
, syncable::GET_BY_ID
, child_id
);
1620 EXPECT_EQ(parent2_committed_id
, child
.GetParentId());
1624 TEST_F(SyncerTest
, TestCommitListOrderingAndNewParentAndChild
) {
1625 string parent_name
= "1";
1626 string parent2_name
= "A";
1627 string child_name
= "B";
1630 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1631 MutableEntry
parent(&wtrans
,
1635 ASSERT_TRUE(parent
.good());
1636 parent
.PutIsUnsynced(true);
1637 parent
.PutIsDir(true);
1638 parent
.PutSpecifics(DefaultBookmarkSpecifics());
1639 parent
.PutId(parent_id_
);
1640 parent
.PutBaseVersion(1);
1643 int64 meta_handle_b
;
1644 const Id parent2_local_id
= ids_
.NewLocalId();
1645 const Id child_local_id
= ids_
.NewLocalId();
1647 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1648 MutableEntry
parent2(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, parent2_name
);
1649 ASSERT_TRUE(parent2
.good());
1650 parent2
.PutIsUnsynced(true);
1651 parent2
.PutIsDir(true);
1652 parent2
.PutSpecifics(DefaultBookmarkSpecifics());
1654 parent2
.PutId(parent2_local_id
);
1656 &wtrans
, CREATE
, BOOKMARKS
, parent2_local_id
, child_name
);
1657 ASSERT_TRUE(child
.good());
1658 child
.PutIsUnsynced(true);
1659 child
.PutIsDir(true);
1660 child
.PutSpecifics(DefaultBookmarkSpecifics());
1661 child
.PutId(child_local_id
);
1662 meta_handle_b
= child
.GetMetahandle();
1666 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1667 // If this test starts failing, be aware other sort orders could be valid.
1668 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1669 EXPECT_TRUE(parent2_local_id
== mock_server_
->committed_ids()[1]);
1670 EXPECT_TRUE(child_local_id
== mock_server_
->committed_ids()[2]);
1672 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1674 Entry
parent(&rtrans
, syncable::GET_BY_ID
,
1675 GetOnlyEntryWithName(&rtrans
, rtrans
.root_id(), parent_name
));
1676 ASSERT_TRUE(parent
.good());
1677 EXPECT_TRUE(parent
.GetId().ServerKnows());
1679 Entry
parent2(&rtrans
, syncable::GET_BY_ID
,
1680 GetOnlyEntryWithName(&rtrans
, parent
.GetId(), parent2_name
));
1681 ASSERT_TRUE(parent2
.good());
1682 EXPECT_TRUE(parent2
.GetId().ServerKnows());
1684 // Id changed on commit, so this should fail.
1685 Entry
local_parent2_id_entry(&rtrans
,
1686 syncable::GET_BY_ID
,
1688 ASSERT_FALSE(local_parent2_id_entry
.good());
1690 Entry
entry_b(&rtrans
, syncable::GET_BY_HANDLE
, meta_handle_b
);
1691 EXPECT_TRUE(entry_b
.GetId().ServerKnows());
1692 EXPECT_TRUE(parent2
.GetId()== entry_b
.GetParentId());
1696 TEST_F(SyncerTest
, UpdateWithZeroLengthName
) {
1697 // One illegal update
1698 mock_server_
->AddUpdateDirectory(
1699 1, 0, std::string(), 1, 10, foreign_cache_guid(), "-1");
1700 // And one legal one that we're going to delete.
1701 mock_server_
->AddUpdateDirectory(2, 0, "FOO", 1, 10,
1702 foreign_cache_guid(), "-2");
1704 // Delete the legal one. The new update has a null name.
1705 mock_server_
->AddUpdateDirectory(
1706 2, 0, std::string(), 2, 20, foreign_cache_guid(), "-2");
1707 mock_server_
->SetLastUpdateDeleted();
1711 TEST_F(SyncerTest
, TestBasicUpdate
) {
1712 string id
= "some_id";
1713 string parent_id
= "0";
1714 string name
= "in_root";
1716 int64 timestamp
= 10;
1717 mock_server_
->AddUpdateDirectory(id
, parent_id
, name
, version
, timestamp
,
1718 foreign_cache_guid(), "-1");
1722 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1723 Entry
entry(&trans
, GET_BY_ID
,
1724 syncable::Id::CreateFromServerId("some_id"));
1725 ASSERT_TRUE(entry
.good());
1726 EXPECT_TRUE(entry
.GetIsDir());
1727 EXPECT_TRUE(entry
.GetServerVersion()== version
);
1728 EXPECT_TRUE(entry
.GetBaseVersion()== version
);
1729 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
1730 EXPECT_FALSE(entry
.GetIsUnsynced());
1731 EXPECT_FALSE(entry
.GetServerIsDel());
1732 EXPECT_FALSE(entry
.GetIsDel());
1736 TEST_F(SyncerTest
, IllegalAndLegalUpdates
) {
1737 Id root
= TestIdFactory::root();
1738 // Should apply just fine.
1739 mock_server_
->AddUpdateDirectory(1, 0, "in_root", 10, 10,
1740 foreign_cache_guid(), "-1");
1742 // Same name. But this SHOULD work.
1743 mock_server_
->AddUpdateDirectory(2, 0, "in_root", 10, 10,
1744 foreign_cache_guid(), "-2");
1746 // Unknown parent: should never be applied. "-80" is a legal server ID,
1747 // because any string sent by the server is a legal server ID in the sync
1748 // protocol, but it's not the ID of any item known to the client. This
1749 // update should succeed validation, but be stuck in the unapplied state
1750 // until an item with the server ID "-80" arrives.
1751 mock_server_
->AddUpdateDirectory(3, -80, "bad_parent", 10, 10,
1752 foreign_cache_guid(), "-3");
1756 // Id 3 should be in conflict now.
1759 GetUpdateCounters(BOOKMARKS
).num_hierarchy_conflict_application_failures
);
1761 // The only request in that loop should have been a GetUpdate.
1762 // At that point, we didn't know whether or not we had conflicts.
1763 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
1764 VerifyHierarchyConflictsUnspecified(mock_server_
->last_request());
1766 // These entries will be used in the second set of updates.
1767 mock_server_
->AddUpdateDirectory(4, 0, "newer_version", 20, 10,
1768 foreign_cache_guid(), "-4");
1769 mock_server_
->AddUpdateDirectory(5, 0, "circular1", 10, 10,
1770 foreign_cache_guid(), "-5");
1771 mock_server_
->AddUpdateDirectory(6, 5, "circular2", 10, 10,
1772 foreign_cache_guid(), "-6");
1773 mock_server_
->AddUpdateDirectory(9, 3, "bad_parent_child", 10, 10,
1774 foreign_cache_guid(), "-9");
1775 mock_server_
->AddUpdateDirectory(100, 9, "bad_parent_child2", 10, 10,
1776 foreign_cache_guid(), "-100");
1777 mock_server_
->AddUpdateDirectory(10, 0, "dir_to_bookmark", 10, 10,
1778 foreign_cache_guid(), "-10");
1781 // The three items with an unresolved parent should be unapplied (3, 9, 100).
1782 // The name clash should also still be in conflict.
1785 GetUpdateCounters(BOOKMARKS
).num_hierarchy_conflict_application_failures
);
1787 // This time around, we knew that there were conflicts.
1788 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
1789 VerifyHierarchyConflictsReported(mock_server_
->last_request());
1792 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1793 // Even though it has the same name, it should work.
1794 Entry
name_clash(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
1795 ASSERT_TRUE(name_clash
.good());
1796 EXPECT_FALSE(name_clash
.GetIsUnappliedUpdate())
1797 << "Duplicate name SHOULD be OK.";
1799 Entry
bad_parent(&trans
, GET_BY_ID
, ids_
.FromNumber(3));
1800 ASSERT_TRUE(bad_parent
.good());
1801 EXPECT_TRUE(bad_parent
.GetIsUnappliedUpdate())
1802 << "child of unknown parent should be in conflict";
1804 Entry
bad_parent_child(&trans
, GET_BY_ID
, ids_
.FromNumber(9));
1805 ASSERT_TRUE(bad_parent_child
.good());
1806 EXPECT_TRUE(bad_parent_child
.GetIsUnappliedUpdate())
1807 << "grandchild of unknown parent should be in conflict";
1809 Entry
bad_parent_child2(&trans
, GET_BY_ID
, ids_
.FromNumber(100));
1810 ASSERT_TRUE(bad_parent_child2
.good());
1811 EXPECT_TRUE(bad_parent_child2
.GetIsUnappliedUpdate())
1812 << "great-grandchild of unknown parent should be in conflict";
1815 // Updating 1 should not affect item 2 of the same name.
1816 mock_server_
->AddUpdateDirectory(1, 0, "new_name", 20, 20,
1817 foreign_cache_guid(), "-1");
1819 // Moving 5 under 6 will create a cycle: a conflict.
1820 mock_server_
->AddUpdateDirectory(5, 6, "circular3", 20, 20,
1821 foreign_cache_guid(), "-5");
1823 // Flip the is_dir bit: should fail verify & be dropped.
1824 mock_server_
->AddUpdateBookmark(10, 0, "dir_to_bookmark", 20, 20,
1825 foreign_cache_guid(), "-10");
1828 // Version number older than last known: should fail verify & be dropped.
1829 mock_server_
->AddUpdateDirectory(4, 0, "old_version", 10, 10,
1830 foreign_cache_guid(), "-4");
1833 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1835 Entry
still_a_dir(&trans
, GET_BY_ID
, ids_
.FromNumber(10));
1836 ASSERT_TRUE(still_a_dir
.good());
1837 EXPECT_FALSE(still_a_dir
.GetIsUnappliedUpdate());
1838 EXPECT_EQ(10u, still_a_dir
.GetBaseVersion());
1839 EXPECT_EQ(10u, still_a_dir
.GetServerVersion());
1840 EXPECT_TRUE(still_a_dir
.GetIsDir());
1842 Entry
rename(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
1843 ASSERT_TRUE(rename
.good());
1844 EXPECT_EQ(root
, rename
.GetParentId());
1845 EXPECT_EQ("new_name", rename
.GetNonUniqueName());
1846 EXPECT_FALSE(rename
.GetIsUnappliedUpdate());
1847 EXPECT_TRUE(ids_
.FromNumber(1) == rename
.GetId());
1848 EXPECT_EQ(20u, rename
.GetBaseVersion());
1850 Entry
name_clash(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
1851 ASSERT_TRUE(name_clash
.good());
1852 EXPECT_EQ(root
, name_clash
.GetParentId());
1853 EXPECT_TRUE(ids_
.FromNumber(2) == name_clash
.GetId());
1854 EXPECT_EQ(10u, name_clash
.GetBaseVersion());
1855 EXPECT_EQ("in_root", name_clash
.GetNonUniqueName());
1857 Entry
ignored_old_version(&trans
, GET_BY_ID
, ids_
.FromNumber(4));
1858 ASSERT_TRUE(ignored_old_version
.good());
1860 ignored_old_version
.GetNonUniqueName()== "newer_version");
1861 EXPECT_FALSE(ignored_old_version
.GetIsUnappliedUpdate());
1862 EXPECT_EQ(20u, ignored_old_version
.GetBaseVersion());
1864 Entry
circular_parent_issue(&trans
, GET_BY_ID
, ids_
.FromNumber(5));
1865 ASSERT_TRUE(circular_parent_issue
.good());
1866 EXPECT_TRUE(circular_parent_issue
.GetIsUnappliedUpdate())
1867 << "circular move should be in conflict";
1868 EXPECT_TRUE(circular_parent_issue
.GetParentId()== root_id_
);
1869 EXPECT_TRUE(circular_parent_issue
.GetServerParentId()==
1870 ids_
.FromNumber(6));
1871 EXPECT_EQ(10u, circular_parent_issue
.GetBaseVersion());
1873 Entry
circular_parent_target(&trans
, GET_BY_ID
, ids_
.FromNumber(6));
1874 ASSERT_TRUE(circular_parent_target
.good());
1875 EXPECT_FALSE(circular_parent_target
.GetIsUnappliedUpdate());
1876 EXPECT_TRUE(circular_parent_issue
.GetId()==
1877 circular_parent_target
.GetParentId());
1878 EXPECT_EQ(10u, circular_parent_target
.GetBaseVersion());
1881 EXPECT_FALSE(saw_syncer_event_
);
1884 GetUpdateCounters(BOOKMARKS
).num_hierarchy_conflict_application_failures
);
1887 // A commit with a lost response produces an update that has to be reunited with
1889 TEST_F(SyncerTest
, CommitReuniteUpdateAdjustsChildren
) {
1890 // Create a folder in the root.
1891 int64 metahandle_folder
;
1893 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1895 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_folder");
1896 ASSERT_TRUE(entry
.good());
1897 entry
.PutIsDir(true);
1898 entry
.PutSpecifics(DefaultBookmarkSpecifics());
1899 entry
.PutIsUnsynced(true);
1900 metahandle_folder
= entry
.GetMetahandle();
1903 // Verify it and pull the ID out of the folder.
1904 syncable::Id folder_id
;
1905 int64 metahandle_entry
;
1907 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1908 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_folder
);
1909 ASSERT_TRUE(entry
.good());
1910 folder_id
= entry
.GetId();
1911 ASSERT_TRUE(!folder_id
.ServerKnows());
1914 // Create an entry in the newly created folder.
1916 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1917 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, folder_id
, "new_entry");
1918 ASSERT_TRUE(entry
.good());
1919 metahandle_entry
= entry
.GetMetahandle();
1920 WriteTestDataToEntry(&trans
, &entry
);
1923 // Verify it and pull the ID out of the entry.
1924 syncable::Id entry_id
;
1926 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1927 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, metahandle_entry
);
1928 ASSERT_TRUE(entry
.good());
1929 EXPECT_EQ(folder_id
, entry
.GetParentId());
1930 EXPECT_EQ("new_entry", entry
.GetNonUniqueName());
1931 entry_id
= entry
.GetId();
1932 EXPECT_TRUE(!entry_id
.ServerKnows());
1933 VerifyTestDataInEntry(&trans
, &entry
);
1936 // Now, to emulate a commit response failure, we just don't commit it.
1937 int64 new_version
= 150; // any larger value.
1938 int64 timestamp
= 20; // arbitrary value.
1939 syncable::Id new_folder_id
=
1940 syncable::Id::CreateFromServerId("folder_server_id");
1942 // The following update should cause the folder to both apply the update, as
1943 // well as reassociate the id.
1944 mock_server_
->AddUpdateDirectory(new_folder_id
, root_id_
,
1945 "new_folder", new_version
, timestamp
,
1946 local_cache_guid(), folder_id
.GetServerId());
1948 // We don't want it accidentally committed, just the update applied.
1949 mock_server_
->set_conflict_all_commits(true);
1951 // Alright! Apply that update!
1954 // The folder's ID should have been updated.
1955 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1956 Entry
folder(&trans
, GET_BY_HANDLE
, metahandle_folder
);
1957 ASSERT_TRUE(folder
.good());
1958 EXPECT_EQ("new_folder", folder
.GetNonUniqueName());
1959 EXPECT_TRUE(new_version
== folder
.GetBaseVersion());
1960 EXPECT_TRUE(new_folder_id
== folder
.GetId());
1961 EXPECT_TRUE(folder
.GetId().ServerKnows());
1962 EXPECT_EQ(trans
.root_id(), folder
.GetParentId());
1964 // Since it was updated, the old folder should not exist.
1965 Entry
old_dead_folder(&trans
, GET_BY_ID
, folder_id
);
1966 EXPECT_FALSE(old_dead_folder
.good());
1968 // The child's parent should have changed.
1969 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, metahandle_entry
);
1970 ASSERT_TRUE(entry
.good());
1971 EXPECT_EQ("new_entry", entry
.GetNonUniqueName());
1972 EXPECT_EQ(new_folder_id
, entry
.GetParentId());
1973 EXPECT_TRUE(!entry
.GetId().ServerKnows());
1974 VerifyTestDataInEntry(&trans
, &entry
);
1978 // A commit with a lost response produces an update that has to be reunited with
1980 TEST_F(SyncerTest
, CommitReuniteUpdate
) {
1981 // Create an entry in the root.
1982 int64 entry_metahandle
;
1984 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1985 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_entry");
1986 ASSERT_TRUE(entry
.good());
1987 entry_metahandle
= entry
.GetMetahandle();
1988 WriteTestDataToEntry(&trans
, &entry
);
1991 // Verify it and pull the ID out.
1992 syncable::Id entry_id
;
1994 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1996 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
1997 ASSERT_TRUE(entry
.good());
1998 entry_id
= entry
.GetId();
1999 EXPECT_TRUE(!entry_id
.ServerKnows());
2000 VerifyTestDataInEntry(&trans
, &entry
);
2003 // Now, to emulate a commit response failure, we just don't commit it.
2004 int64 new_version
= 150; // any larger value.
2005 int64 timestamp
= 20; // arbitrary value.
2006 syncable::Id new_entry_id
= syncable::Id::CreateFromServerId("server_id");
2008 // Generate an update from the server with a relevant ID reassignment.
2009 mock_server_
->AddUpdateBookmark(new_entry_id
, root_id_
,
2010 "new_entry", new_version
, timestamp
,
2011 local_cache_guid(), entry_id
.GetServerId());
2013 // We don't want it accidentally committed, just the update applied.
2014 mock_server_
->set_conflict_all_commits(true);
2016 // Alright! Apply that update!
2019 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2020 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2021 ASSERT_TRUE(entry
.good());
2022 EXPECT_TRUE(new_version
== entry
.GetBaseVersion());
2023 EXPECT_TRUE(new_entry_id
== entry
.GetId());
2024 EXPECT_EQ("new_entry", entry
.GetNonUniqueName());
2028 // A commit with a lost response must work even if the local entry was deleted
2029 // before the update is applied. We should not duplicate the local entry in
2030 // this case, but just create another one alongside. We may wish to examine
2031 // this behavior in the future as it can create hanging uploads that never
2032 // finish, that must be cleaned up on the server side after some time.
2033 TEST_F(SyncerTest
, CommitReuniteUpdateDoesNotChokeOnDeletedLocalEntry
) {
2034 // Create a entry in the root.
2035 int64 entry_metahandle
;
2037 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2038 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_entry");
2039 ASSERT_TRUE(entry
.good());
2040 entry_metahandle
= entry
.GetMetahandle();
2041 WriteTestDataToEntry(&trans
, &entry
);
2043 // Verify it and pull the ID out.
2044 syncable::Id entry_id
;
2046 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2047 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2048 ASSERT_TRUE(entry
.good());
2049 entry_id
= entry
.GetId();
2050 EXPECT_TRUE(!entry_id
.ServerKnows());
2051 VerifyTestDataInEntry(&trans
, &entry
);
2054 // Now, to emulate a commit response failure, we just don't commit it.
2055 int64 new_version
= 150; // any larger value.
2056 int64 timestamp
= 20; // arbitrary value.
2057 syncable::Id new_entry_id
= syncable::Id::CreateFromServerId("server_id");
2059 // Generate an update from the server with a relevant ID reassignment.
2060 mock_server_
->AddUpdateBookmark(new_entry_id
, root_id_
,
2061 "new_entry", new_version
, timestamp
,
2062 local_cache_guid(), entry_id
.GetServerId());
2064 // We don't want it accidentally committed, just the update applied.
2065 mock_server_
->set_conflict_all_commits(true);
2067 // Purposefully delete the entry now before the update application finishes.
2069 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2070 Id new_entry_id
= GetOnlyEntryWithName(
2071 &trans
, trans
.root_id(), "new_entry");
2072 MutableEntry
entry(&trans
, GET_BY_ID
, new_entry_id
);
2073 ASSERT_TRUE(entry
.good());
2074 entry
.PutIsDel(true);
2077 // Just don't CHECK fail in sync, have the update split.
2080 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2081 Id new_entry_id
= GetOnlyEntryWithName(
2082 &trans
, trans
.root_id(), "new_entry");
2083 Entry
entry(&trans
, GET_BY_ID
, new_entry_id
);
2084 ASSERT_TRUE(entry
.good());
2085 EXPECT_FALSE(entry
.GetIsDel());
2087 Entry
old_entry(&trans
, GET_BY_ID
, entry_id
);
2088 ASSERT_TRUE(old_entry
.good());
2089 EXPECT_TRUE(old_entry
.GetIsDel());
2093 // TODO(chron): Add more unsanitized name tests.
2094 TEST_F(SyncerTest
, ConflictMatchingEntryHandlesUnsanitizedNames
) {
2095 mock_server_
->AddUpdateDirectory(1, 0, "A/A", 10, 10,
2096 foreign_cache_guid(), "-1");
2097 mock_server_
->AddUpdateDirectory(2, 0, "B/B", 10, 10,
2098 foreign_cache_guid(), "-2");
2099 mock_server_
->set_conflict_all_commits(true);
2102 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2104 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2105 ASSERT_TRUE(A
.good());
2106 A
.PutIsUnsynced(true);
2107 A
.PutIsUnappliedUpdate(true);
2108 A
.PutServerVersion(20);
2110 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
2111 ASSERT_TRUE(B
.good());
2112 B
.PutIsUnappliedUpdate(true);
2113 B
.PutServerVersion(20);
2116 saw_syncer_event_
= false;
2117 mock_server_
->set_conflict_all_commits(false);
2120 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2122 Entry
A(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2123 ASSERT_TRUE(A
.good());
2124 EXPECT_TRUE(A
.GetIsUnsynced()== false);
2125 EXPECT_TRUE(A
.GetIsUnappliedUpdate()== false);
2126 EXPECT_TRUE(A
.GetServerVersion()== 20);
2128 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
2129 ASSERT_TRUE(B
.good());
2130 EXPECT_TRUE(B
.GetIsUnsynced()== false);
2131 EXPECT_TRUE(B
.GetIsUnappliedUpdate()== false);
2132 EXPECT_TRUE(B
.GetServerVersion()== 20);
2136 TEST_F(SyncerTest
, ConflictMatchingEntryHandlesNormalNames
) {
2137 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
2138 foreign_cache_guid(), "-1");
2139 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
2140 foreign_cache_guid(), "-2");
2141 mock_server_
->set_conflict_all_commits(true);
2144 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2146 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2147 ASSERT_TRUE(A
.good());
2148 A
.PutIsUnsynced(true);
2149 A
.PutIsUnappliedUpdate(true);
2150 A
.PutServerVersion(20);
2152 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
2153 ASSERT_TRUE(B
.good());
2154 B
.PutIsUnappliedUpdate(true);
2155 B
.PutServerVersion(20);
2158 saw_syncer_event_
= false;
2159 mock_server_
->set_conflict_all_commits(false);
2162 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2164 Entry
A(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2165 ASSERT_TRUE(A
.good());
2166 EXPECT_TRUE(A
.GetIsUnsynced()== false);
2167 EXPECT_TRUE(A
.GetIsUnappliedUpdate()== false);
2168 EXPECT_TRUE(A
.GetServerVersion()== 20);
2170 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
2171 ASSERT_TRUE(B
.good());
2172 EXPECT_TRUE(B
.GetIsUnsynced()== false);
2173 EXPECT_TRUE(B
.GetIsUnappliedUpdate()== false);
2174 EXPECT_TRUE(B
.GetServerVersion()== 20);
2178 TEST_F(SyncerTest
, ReverseFolderOrderingTest
) {
2179 mock_server_
->AddUpdateDirectory(4, 3, "ggchild", 10, 10,
2180 foreign_cache_guid(), "-4");
2181 mock_server_
->AddUpdateDirectory(3, 2, "gchild", 10, 10,
2182 foreign_cache_guid(), "-3");
2183 mock_server_
->AddUpdateDirectory(5, 4, "gggchild", 10, 10,
2184 foreign_cache_guid(), "-5");
2185 mock_server_
->AddUpdateDirectory(2, 1, "child", 10, 10,
2186 foreign_cache_guid(), "-2");
2187 mock_server_
->AddUpdateDirectory(1, 0, "parent", 10, 10,
2188 foreign_cache_guid(), "-1");
2190 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2192 Id child_id
= GetOnlyEntryWithName(
2193 &trans
, ids_
.FromNumber(4), "gggchild");
2194 Entry
child(&trans
, GET_BY_ID
, child_id
);
2195 ASSERT_TRUE(child
.good());
2198 class EntryCreatedInNewFolderTest
: public SyncerTest
{
2200 void CreateFolderInBob() {
2201 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2202 MutableEntry
bob(&trans
,
2203 syncable::GET_BY_ID
,
2204 GetOnlyEntryWithName(&trans
,
2205 TestIdFactory::root(),
2209 MutableEntry
entry2(
2210 &trans
, CREATE
, BOOKMARKS
, bob
.GetId(), "bob");
2211 CHECK(entry2
.good());
2212 entry2
.PutIsDir(true);
2213 entry2
.PutIsUnsynced(true);
2214 entry2
.PutSpecifics(DefaultBookmarkSpecifics());
2218 TEST_F(EntryCreatedInNewFolderTest
, EntryCreatedInNewFolderMidSync
) {
2220 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2221 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2222 ASSERT_TRUE(entry
.good());
2223 entry
.PutIsDir(true);
2224 entry
.PutIsUnsynced(true);
2225 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2228 mock_server_
->SetMidCommitCallback(
2229 base::Bind(&EntryCreatedInNewFolderTest::CreateFolderInBob
,
2230 base::Unretained(this)));
2232 // We loop until no unsynced handles remain, so we will commit both ids.
2233 EXPECT_EQ(2u, mock_server_
->committed_ids().size());
2235 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2236 Entry
parent_entry(&trans
, syncable::GET_BY_ID
,
2237 GetOnlyEntryWithName(&trans
, TestIdFactory::root(), "bob"));
2238 ASSERT_TRUE(parent_entry
.good());
2241 GetOnlyEntryWithName(&trans
, parent_entry
.GetId(), "bob");
2242 Entry
child(&trans
, syncable::GET_BY_ID
, child_id
);
2243 ASSERT_TRUE(child
.good());
2244 EXPECT_EQ(parent_entry
.GetId(), child
.GetParentId());
2248 TEST_F(SyncerTest
, NegativeIDInUpdate
) {
2249 mock_server_
->AddUpdateBookmark(-10, 0, "bad", 40, 40,
2250 foreign_cache_guid(), "-100");
2252 // The negative id would make us CHECK!
2255 TEST_F(SyncerTest
, UnappliedUpdateOnCreatedItemItemDoesNotCrash
) {
2256 int64 metahandle_fred
;
2257 syncable::Id orig_id
;
2260 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2261 MutableEntry
fred_match(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(),
2263 ASSERT_TRUE(fred_match
.good());
2264 metahandle_fred
= fred_match
.GetMetahandle();
2265 orig_id
= fred_match
.GetId();
2266 WriteTestDataToEntry(&trans
, &fred_match
);
2270 EXPECT_EQ(1u, mock_server_
->committed_ids().size());
2271 mock_server_
->set_conflict_all_commits(true);
2272 syncable::Id fred_match_id
;
2274 // Now receive a change from outside.
2275 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2276 MutableEntry
fred_match(&trans
, GET_BY_HANDLE
, metahandle_fred
);
2277 ASSERT_TRUE(fred_match
.good());
2278 EXPECT_TRUE(fred_match
.GetId().ServerKnows());
2279 fred_match_id
= fred_match
.GetId();
2280 mock_server_
->AddUpdateBookmark(fred_match_id
, trans
.root_id(),
2281 "fred_match", 40, 40, local_cache_guid(), orig_id
.GetServerId());
2284 for (int i
= 0 ; i
< 30 ; ++i
) {
2290 * In the event that we have a double changed entry, that is changed on both
2291 * the client and the server, the conflict resolver should just drop one of
2292 * them and accept the other.
2295 TEST_F(SyncerTest
, DoublyChangedWithResolver
) {
2296 syncable::Id local_id
;
2298 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2299 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Folder");
2300 ASSERT_TRUE(parent
.good());
2301 parent
.PutIsDir(true);
2302 parent
.PutId(parent_id_
);
2303 parent
.PutBaseVersion(5);
2304 parent
.PutSpecifics(DefaultBookmarkSpecifics());
2305 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete.htm");
2306 ASSERT_TRUE(child
.good());
2307 local_id
= child
.GetId();
2308 child
.PutId(child_id_
);
2309 child
.PutBaseVersion(10);
2310 WriteTestDataToEntry(&wtrans
, &child
);
2312 mock_server_
->AddUpdateBookmark(child_id_
, parent_id_
, "Pete2.htm", 11, 10,
2313 local_cache_guid(), local_id
.GetServerId());
2314 mock_server_
->set_conflict_all_commits(true);
2316 syncable::Directory::Metahandles children
;
2318 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2319 directory()->GetChildHandlesById(&trans
, parent_id_
, &children
);
2320 // We expect the conflict resolver to preserve the local entry.
2321 Entry
child(&trans
, syncable::GET_BY_ID
, child_id_
);
2322 ASSERT_TRUE(child
.good());
2323 EXPECT_TRUE(child
.GetIsUnsynced());
2324 EXPECT_FALSE(child
.GetIsUnappliedUpdate());
2325 EXPECT_TRUE(child
.GetSpecifics().has_bookmark());
2326 EXPECT_EQ("Pete.htm", child
.GetNonUniqueName());
2327 VerifyTestBookmarkDataInEntry(&child
);
2330 // Only one entry, since we just overwrite one.
2331 EXPECT_EQ(1u, children
.size());
2332 saw_syncer_event_
= false;
2335 // We got this repro case when someone was editing bookmarks while sync was
2336 // occuring. The entry had changed out underneath the user.
2337 TEST_F(SyncerTest
, CommitsUpdateDoesntAlterEntry
) {
2338 const base::Time
& test_time
= ProtoTimeToTime(123456);
2339 syncable::Id local_id
;
2340 int64 entry_metahandle
;
2342 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2343 MutableEntry
entry(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Pete");
2344 ASSERT_TRUE(entry
.good());
2345 EXPECT_FALSE(entry
.GetId().ServerKnows());
2346 local_id
= entry
.GetId();
2347 entry
.PutIsDir(true);
2348 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2349 entry
.PutIsUnsynced(true);
2350 entry
.PutMtime(test_time
);
2351 entry_metahandle
= entry
.GetMetahandle();
2357 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2358 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, entry_metahandle
);
2359 ASSERT_TRUE(entry
.good());
2361 EXPECT_TRUE(id
.ServerKnows());
2362 version
= entry
.GetBaseVersion();
2364 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
2365 update
->set_originator_cache_guid(local_cache_guid());
2366 update
->set_originator_client_item_id(local_id
.GetServerId());
2367 EXPECT_EQ("Pete", update
->name());
2368 EXPECT_EQ(id
.GetServerId(), update
->id_string());
2369 EXPECT_EQ(root_id_
.GetServerId(), update
->parent_id_string());
2370 EXPECT_EQ(version
, update
->version());
2373 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2374 Entry
entry(&trans
, syncable::GET_BY_ID
, id
);
2375 ASSERT_TRUE(entry
.good());
2376 EXPECT_TRUE(entry
.GetMtime()== test_time
);
2380 TEST_F(SyncerTest
, ParentAndChildBothMatch
) {
2381 const FullModelTypeSet all_types
= FullModelTypeSet::All();
2382 syncable::Id parent_id
= ids_
.NewServerId();
2383 syncable::Id child_id
= ids_
.NewServerId();
2384 syncable::Id parent_local_id
;
2385 syncable::Id child_local_id
;
2389 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2390 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Folder");
2391 ASSERT_TRUE(parent
.good());
2392 parent_local_id
= parent
.GetId();
2393 parent
.PutIsDir(true);
2394 parent
.PutIsUnsynced(true);
2395 parent
.PutId(parent_id
);
2396 parent
.PutBaseVersion(1);
2397 parent
.PutSpecifics(DefaultBookmarkSpecifics());
2399 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent
.GetId(), "test.htm");
2400 ASSERT_TRUE(child
.good());
2401 child_local_id
= child
.GetId();
2402 child
.PutId(child_id
);
2403 child
.PutBaseVersion(1);
2404 child
.PutSpecifics(DefaultBookmarkSpecifics());
2405 WriteTestDataToEntry(&wtrans
, &child
);
2407 mock_server_
->AddUpdateDirectory(parent_id
, root_id_
, "Folder", 10, 10,
2409 parent_local_id
.GetServerId());
2410 mock_server_
->AddUpdateBookmark(child_id
, parent_id
, "test.htm", 10, 10,
2412 child_local_id
.GetServerId());
2413 mock_server_
->set_conflict_all_commits(true);
2418 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2419 Directory::Metahandles children
;
2420 directory()->GetChildHandlesById(&trans
, root_id_
, &children
);
2421 EXPECT_EQ(1u, children
.size());
2422 directory()->GetChildHandlesById(&trans
, parent_id
, &children
);
2423 EXPECT_EQ(1u, children
.size());
2424 std::vector
<int64
> unapplied
;
2425 directory()->GetUnappliedUpdateMetaHandles(&trans
, all_types
, &unapplied
);
2426 EXPECT_EQ(0u, unapplied
.size());
2427 syncable::Directory::Metahandles unsynced
;
2428 directory()->GetUnsyncedMetaHandles(&trans
, &unsynced
);
2429 EXPECT_EQ(0u, unsynced
.size());
2430 saw_syncer_event_
= false;
2434 TEST_F(SyncerTest
, CommittingNewDeleted
) {
2436 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2437 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2438 entry
.PutIsUnsynced(true);
2439 entry
.PutIsDel(true);
2442 EXPECT_EQ(0u, mock_server_
->committed_ids().size());
2445 // Original problem synopsis:
2446 // Check failed: entry->GetBaseVersion()<= entry->GetServerVersion()
2447 // Client creates entry, client finishes committing entry. Between
2448 // commit and getting update back, we delete the entry.
2449 // We get the update for the entry, but the local one was modified
2450 // so we store the entry but don't apply it. IS_UNAPPLIED_UPDATE is set.
2451 // We commit deletion and get a new version number.
2452 // We apply unapplied updates again before we get the update about the deletion.
2453 // This means we have an unapplied update where server_version < base_version.
2454 TEST_F(SyncerTest
, UnappliedUpdateDuringCommit
) {
2455 // This test is a little fake.
2457 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2458 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2459 entry
.PutId(ids_
.FromNumber(20));
2460 entry
.PutBaseVersion(1);
2461 entry
.PutServerVersion(1);
2462 entry
.PutServerParentId(ids_
.FromNumber(9999)); // Bad parent.
2463 entry
.PutIsUnsynced(true);
2464 entry
.PutIsUnappliedUpdate(true);
2465 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2466 entry
.PutServerSpecifics(DefaultBookmarkSpecifics());
2467 entry
.PutIsDel(false);
2470 EXPECT_EQ(1, session_
->status_controller().TotalNumConflictingItems());
2471 saw_syncer_event_
= false;
2474 // Original problem synopsis:
2476 // Unexpected error during sync if we:
2477 // make a new folder bob
2479 // make a new folder fred
2480 // move bob into fred
2483 // if no syncing occured midway, bob will have an illegal parent
2484 TEST_F(SyncerTest
, DeletingEntryInFolder
) {
2485 // This test is a little fake.
2486 int64 existing_metahandle
;
2488 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2489 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "existing");
2490 ASSERT_TRUE(entry
.good());
2491 entry
.PutIsDir(true);
2492 entry
.PutSpecifics(DefaultBookmarkSpecifics());
2493 entry
.PutIsUnsynced(true);
2494 existing_metahandle
= entry
.GetMetahandle();
2498 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2499 MutableEntry
newfolder(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new");
2500 ASSERT_TRUE(newfolder
.good());
2501 newfolder
.PutIsDir(true);
2502 newfolder
.PutSpecifics(DefaultBookmarkSpecifics());
2503 newfolder
.PutIsUnsynced(true);
2505 MutableEntry
existing(&trans
, GET_BY_HANDLE
, existing_metahandle
);
2506 ASSERT_TRUE(existing
.good());
2507 existing
.PutParentId(newfolder
.GetId());
2508 existing
.PutIsUnsynced(true);
2509 EXPECT_TRUE(existing
.GetId().ServerKnows());
2511 newfolder
.PutIsDel(true);
2512 existing
.PutIsDel(true);
2515 EXPECT_EQ(0, GetCommitCounters(BOOKMARKS
).num_commits_conflict
);
2518 TEST_F(SyncerTest
, DeletingEntryWithLocalEdits
) {
2519 int64 newfolder_metahandle
;
2521 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
2522 foreign_cache_guid(), "-1");
2525 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2526 MutableEntry
newfolder(
2527 &trans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(1), "local");
2528 ASSERT_TRUE(newfolder
.good());
2529 newfolder
.PutIsUnsynced(true);
2530 newfolder
.PutIsDir(true);
2531 newfolder
.PutSpecifics(DefaultBookmarkSpecifics());
2532 newfolder_metahandle
= newfolder
.GetMetahandle();
2534 mock_server_
->AddUpdateDirectory(1, 0, "bob", 2, 20,
2535 foreign_cache_guid(), "-1");
2536 mock_server_
->SetLastUpdateDeleted();
2537 SyncShareConfigure();
2539 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2540 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, newfolder_metahandle
);
2541 ASSERT_TRUE(entry
.good());
2545 TEST_F(SyncerTest
, FolderSwapUpdate
) {
2546 mock_server_
->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2547 foreign_cache_guid(), "-7801");
2548 mock_server_
->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2549 foreign_cache_guid(), "-1024");
2551 mock_server_
->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2552 foreign_cache_guid(), "-1024");
2553 mock_server_
->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2554 foreign_cache_guid(), "-7801");
2557 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2558 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2559 ASSERT_TRUE(id1
.good());
2560 EXPECT_TRUE("fred" == id1
.GetNonUniqueName());
2561 EXPECT_TRUE(root_id_
== id1
.GetParentId());
2562 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2563 ASSERT_TRUE(id2
.good());
2564 EXPECT_TRUE("bob" == id2
.GetNonUniqueName());
2565 EXPECT_TRUE(root_id_
== id2
.GetParentId());
2567 saw_syncer_event_
= false;
2570 TEST_F(SyncerTest
, NameCollidingFolderSwapWorksFine
) {
2571 mock_server_
->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2572 foreign_cache_guid(), "-7801");
2573 mock_server_
->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2574 foreign_cache_guid(), "-1024");
2575 mock_server_
->AddUpdateDirectory(4096, 0, "alice", 1, 10,
2576 foreign_cache_guid(), "-4096");
2579 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2580 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2581 ASSERT_TRUE(id1
.good());
2582 EXPECT_TRUE("bob" == id1
.GetNonUniqueName());
2583 EXPECT_TRUE(root_id_
== id1
.GetParentId());
2584 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2585 ASSERT_TRUE(id2
.good());
2586 EXPECT_TRUE("fred" == id2
.GetNonUniqueName());
2587 EXPECT_TRUE(root_id_
== id2
.GetParentId());
2588 Entry
id3(&trans
, GET_BY_ID
, ids_
.FromNumber(4096));
2589 ASSERT_TRUE(id3
.good());
2590 EXPECT_TRUE("alice" == id3
.GetNonUniqueName());
2591 EXPECT_TRUE(root_id_
== id3
.GetParentId());
2593 mock_server_
->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2594 foreign_cache_guid(), "-1024");
2595 mock_server_
->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2596 foreign_cache_guid(), "-7801");
2597 mock_server_
->AddUpdateDirectory(4096, 0, "bob", 2, 20,
2598 foreign_cache_guid(), "-4096");
2601 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2602 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2603 ASSERT_TRUE(id1
.good());
2604 EXPECT_TRUE("fred" == id1
.GetNonUniqueName());
2605 EXPECT_TRUE(root_id_
== id1
.GetParentId());
2606 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2607 ASSERT_TRUE(id2
.good());
2608 EXPECT_TRUE("bob" == id2
.GetNonUniqueName());
2609 EXPECT_TRUE(root_id_
== id2
.GetParentId());
2610 Entry
id3(&trans
, GET_BY_ID
, ids_
.FromNumber(4096));
2611 ASSERT_TRUE(id3
.good());
2612 EXPECT_TRUE("bob" == id3
.GetNonUniqueName());
2613 EXPECT_TRUE(root_id_
== id3
.GetParentId());
2615 saw_syncer_event_
= false;
2618 // Committing more than kDefaultMaxCommitBatchSize items requires that
2619 // we post more than one commit command to the server. This test makes
2620 // sure that scenario works as expected.
2621 TEST_F(SyncerTest
, CommitManyItemsInOneGo_Success
) {
2622 uint32 num_batches
= 3;
2623 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2625 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2626 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2627 string nameutf8
= base::StringPrintf("%d", i
);
2628 string
name(nameutf8
.begin(), nameutf8
.end());
2629 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2630 e
.PutIsUnsynced(true);
2632 e
.PutSpecifics(DefaultBookmarkSpecifics());
2635 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2638 EXPECT_EQ(num_batches
, mock_server_
->commit_messages().size());
2639 EXPECT_EQ(0, directory()->unsynced_entity_count());
2642 // Test that a single failure to contact the server will cause us to exit the
2643 // commit loop immediately.
2644 TEST_F(SyncerTest
, CommitManyItemsInOneGo_PostBufferFail
) {
2645 uint32 num_batches
= 3;
2646 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2648 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2649 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2650 string nameutf8
= base::StringPrintf("%d", i
);
2651 string
name(nameutf8
.begin(), nameutf8
.end());
2652 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2653 e
.PutIsUnsynced(true);
2655 e
.PutSpecifics(DefaultBookmarkSpecifics());
2658 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2660 // The second commit should fail. It will be preceded by one successful
2661 // GetUpdate and one succesful commit.
2662 mock_server_
->FailNthPostBufferToPathCall(3);
2665 EXPECT_EQ(1U, mock_server_
->commit_messages().size());
2666 EXPECT_EQ(SYNC_SERVER_ERROR
,
2667 session_
->status_controller().model_neutral_state().commit_result
);
2668 EXPECT_EQ(items_to_commit
- kDefaultMaxCommitBatchSize
,
2669 directory()->unsynced_entity_count());
2672 // Test that a single conflict response from the server will cause us to exit
2673 // the commit loop immediately.
2674 TEST_F(SyncerTest
, CommitManyItemsInOneGo_CommitConflict
) {
2675 uint32 num_batches
= 2;
2676 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2678 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2679 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2680 string nameutf8
= base::StringPrintf("%d", i
);
2681 string
name(nameutf8
.begin(), nameutf8
.end());
2682 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2683 e
.PutIsUnsynced(true);
2685 e
.PutSpecifics(DefaultBookmarkSpecifics());
2688 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2690 // Return a CONFLICT response for the first item.
2691 mock_server_
->set_conflict_n_commits(1);
2694 // We should stop looping at the first sign of trouble.
2695 EXPECT_EQ(1U, mock_server_
->commit_messages().size());
2696 EXPECT_EQ(items_to_commit
- (kDefaultMaxCommitBatchSize
- 1),
2697 directory()->unsynced_entity_count());
2700 // Tests that sending debug info events works.
2701 TEST_F(SyncerTest
, SendDebugInfoEventsOnGetUpdates_HappyCase
) {
2702 debug_info_getter_
->AddDebugEvent();
2703 debug_info_getter_
->AddDebugEvent();
2707 // Verify we received one GetUpdates request with two debug info events.
2708 EXPECT_EQ(1U, mock_server_
->requests().size());
2709 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2710 EXPECT_EQ(2, mock_server_
->last_request().debug_info().events_size());
2714 // See that we received another GetUpdates request, but that it contains no
2715 // debug info events.
2716 EXPECT_EQ(2U, mock_server_
->requests().size());
2717 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2718 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
2720 debug_info_getter_
->AddDebugEvent();
2724 // See that we received another GetUpdates request and it contains one debug
2726 EXPECT_EQ(3U, mock_server_
->requests().size());
2727 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2728 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
2731 // Tests that debug info events are dropped on server error.
2732 TEST_F(SyncerTest
, SendDebugInfoEventsOnGetUpdates_PostFailsDontDrop
) {
2733 debug_info_getter_
->AddDebugEvent();
2734 debug_info_getter_
->AddDebugEvent();
2736 mock_server_
->FailNextPostBufferToPathCall();
2739 // Verify we attempted to send one GetUpdates request with two debug info
2741 EXPECT_EQ(1U, mock_server_
->requests().size());
2742 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2743 EXPECT_EQ(2, mock_server_
->last_request().debug_info().events_size());
2747 // See that the client resent the two debug info events.
2748 EXPECT_EQ(2U, mock_server_
->requests().size());
2749 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2750 EXPECT_EQ(2, mock_server_
->last_request().debug_info().events_size());
2752 // The previous send was successful so this next one shouldn't generate any
2753 // debug info events.
2755 EXPECT_EQ(3U, mock_server_
->requests().size());
2756 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
2757 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
2760 // Tests that sending debug info events on Commit works.
2761 TEST_F(SyncerTest
, SendDebugInfoEventsOnCommit_HappyCase
) {
2762 // Make sure GetUpdate isn't call as it would "steal" debug info events before
2763 // Commit has a chance to send them.
2764 ConfigureNoGetUpdatesRequired();
2766 // Generate a debug info event and trigger a commit.
2767 debug_info_getter_
->AddDebugEvent();
2768 CreateUnsyncedDirectory("X", "id_X");
2771 // Verify that the last request received is a Commit and that it contains a
2772 // debug info event.
2773 EXPECT_EQ(1U, mock_server_
->requests().size());
2774 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2775 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
2777 // Generate another commit, but no debug info event.
2778 CreateUnsyncedDirectory("Y", "id_Y");
2781 // See that it was received and contains no debug info events.
2782 EXPECT_EQ(2U, mock_server_
->requests().size());
2783 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2784 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
2787 // Tests that debug info events are not dropped on server error.
2788 TEST_F(SyncerTest
, SendDebugInfoEventsOnCommit_PostFailsDontDrop
) {
2789 // Make sure GetUpdate isn't call as it would "steal" debug info events before
2790 // Commit has a chance to send them.
2791 ConfigureNoGetUpdatesRequired();
2793 mock_server_
->FailNextPostBufferToPathCall();
2795 // Generate a debug info event and trigger a commit.
2796 debug_info_getter_
->AddDebugEvent();
2797 CreateUnsyncedDirectory("X", "id_X");
2800 // Verify that the last request sent is a Commit and that it contains a debug
2802 EXPECT_EQ(1U, mock_server_
->requests().size());
2803 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2804 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
2809 // Verify that we've received another Commit and that it contains a debug info
2810 // event (just like the previous one).
2811 EXPECT_EQ(2U, mock_server_
->requests().size());
2812 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2813 EXPECT_EQ(1, mock_server_
->last_request().debug_info().events_size());
2815 // Generate another commit and try again.
2816 CreateUnsyncedDirectory("Y", "id_Y");
2819 // See that it was received and contains no debug info events.
2820 EXPECT_EQ(3U, mock_server_
->requests().size());
2821 ASSERT_TRUE(mock_server_
->last_request().has_commit());
2822 EXPECT_EQ(0, mock_server_
->last_request().debug_info().events_size());
2825 TEST_F(SyncerTest
, HugeConflict
) {
2826 int item_count
= 300; // We should be able to do 300 or 3000 w/o issue.
2828 syncable::Id parent_id
= ids_
.NewServerId();
2829 syncable::Id last_id
= parent_id
;
2830 vector
<syncable::Id
> tree_ids
;
2832 // Create a lot of updates for which the parent does not exist yet.
2833 // Generate a huge deep tree which should all fail to apply at first.
2835 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2836 for (int i
= 0; i
< item_count
; i
++) {
2837 syncable::Id next_id
= ids_
.NewServerId();
2838 syncable::Id local_id
= ids_
.NewLocalId();
2839 tree_ids
.push_back(next_id
);
2840 mock_server_
->AddUpdateDirectory(next_id
, last_id
, "BOB", 2, 20,
2841 foreign_cache_guid(),
2842 local_id
.GetServerId());
2848 // Check they're in the expected conflict state.
2850 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2851 for (int i
= 0; i
< item_count
; i
++) {
2852 Entry
e(&trans
, GET_BY_ID
, tree_ids
[i
]);
2853 // They should all exist but none should be applied.
2854 ASSERT_TRUE(e
.good());
2855 EXPECT_TRUE(e
.GetIsDel());
2856 EXPECT_TRUE(e
.GetIsUnappliedUpdate());
2860 // Add the missing parent directory.
2861 mock_server_
->AddUpdateDirectory(parent_id
, TestIdFactory::root(),
2862 "BOB", 2, 20, foreign_cache_guid(), "-3500");
2865 // Now they should all be OK.
2867 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2868 for (int i
= 0; i
< item_count
; i
++) {
2869 Entry
e(&trans
, GET_BY_ID
, tree_ids
[i
]);
2870 ASSERT_TRUE(e
.good());
2871 EXPECT_FALSE(e
.GetIsDel());
2872 EXPECT_FALSE(e
.GetIsUnappliedUpdate());
2877 TEST_F(SyncerTest
, DontCrashOnCaseChange
) {
2878 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
2879 foreign_cache_guid(), "-1");
2882 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2883 MutableEntry
e(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2884 ASSERT_TRUE(e
.good());
2885 e
.PutIsUnsynced(true);
2887 mock_server_
->set_conflict_all_commits(true);
2888 mock_server_
->AddUpdateDirectory(1, 0, "BOB", 2, 20,
2889 foreign_cache_guid(), "-1");
2890 SyncShareNudge(); // USED TO CAUSE AN ASSERT
2891 saw_syncer_event_
= false;
2894 TEST_F(SyncerTest
, UnsyncedItemAndUpdate
) {
2895 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
2896 foreign_cache_guid(), "-1");
2898 mock_server_
->set_conflict_all_commits(true);
2899 mock_server_
->AddUpdateDirectory(2, 0, "bob", 2, 20,
2900 foreign_cache_guid(), "-2");
2901 SyncShareNudge(); // USED TO CAUSE AN ASSERT
2902 saw_syncer_event_
= false;
2905 TEST_F(SyncerTest
, NewEntryAndAlteredServerEntrySharePath
) {
2906 mock_server_
->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
2907 foreign_cache_guid(), "-1");
2909 int64 local_folder_handle
;
2910 syncable::Id local_folder_id
;
2912 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2913 MutableEntry
new_entry(
2914 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bar.htm");
2915 ASSERT_TRUE(new_entry
.good());
2916 local_folder_id
= new_entry
.GetId();
2917 local_folder_handle
= new_entry
.GetMetahandle();
2918 new_entry
.PutIsUnsynced(true);
2919 new_entry
.PutSpecifics(DefaultBookmarkSpecifics());
2920 MutableEntry
old(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2921 ASSERT_TRUE(old
.good());
2922 WriteTestDataToEntry(&wtrans
, &old
);
2924 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
2925 foreign_cache_guid(), "-1");
2926 mock_server_
->set_conflict_all_commits(true);
2928 saw_syncer_event_
= false;
2930 // Update #20 should have been dropped in favor of the local version.
2931 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2932 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2933 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
2934 ASSERT_TRUE(server
.good());
2935 ASSERT_TRUE(local
.good());
2936 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
2937 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
2938 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
2939 EXPECT_TRUE(server
.GetIsUnsynced());
2940 EXPECT_TRUE(local
.GetIsUnsynced());
2941 EXPECT_EQ("Foo.htm", server
.GetNonUniqueName());
2942 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
2944 // Allow local changes to commit.
2945 mock_server_
->set_conflict_all_commits(false);
2947 saw_syncer_event_
= false;
2949 // Now add a server change to make the two names equal. There should
2950 // be no conflict with that, since names are not unique.
2951 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
2952 foreign_cache_guid(), "-1");
2954 saw_syncer_event_
= false;
2956 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2957 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2958 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
2959 ASSERT_TRUE(server
.good());
2960 ASSERT_TRUE(local
.good());
2961 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
2962 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
2963 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
2964 EXPECT_FALSE(server
.GetIsUnsynced());
2965 EXPECT_FALSE(local
.GetIsUnsynced());
2966 EXPECT_EQ("Bar.htm", server
.GetNonUniqueName());
2967 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
2968 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
2969 server
.GetSpecifics().bookmark().url());
2973 // Same as NewEntryAnddServerEntrySharePath, but using the old-style protocol.
2974 TEST_F(SyncerTest
, NewEntryAndAlteredServerEntrySharePath_OldBookmarksProto
) {
2975 mock_server_
->set_use_legacy_bookmarks_protocol(true);
2976 mock_server_
->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
2977 foreign_cache_guid(), "-1");
2979 int64 local_folder_handle
;
2980 syncable::Id local_folder_id
;
2982 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2983 MutableEntry
new_entry(
2984 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bar.htm");
2985 ASSERT_TRUE(new_entry
.good());
2986 local_folder_id
= new_entry
.GetId();
2987 local_folder_handle
= new_entry
.GetMetahandle();
2988 new_entry
.PutIsUnsynced(true);
2989 new_entry
.PutSpecifics(DefaultBookmarkSpecifics());
2990 MutableEntry
old(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2991 ASSERT_TRUE(old
.good());
2992 WriteTestDataToEntry(&wtrans
, &old
);
2994 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
2995 foreign_cache_guid(), "-1");
2996 mock_server_
->set_conflict_all_commits(true);
2998 saw_syncer_event_
= false;
3000 // Update #20 should have been dropped in favor of the local version.
3001 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3002 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3003 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3004 ASSERT_TRUE(server
.good());
3005 ASSERT_TRUE(local
.good());
3006 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3007 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3008 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3009 EXPECT_TRUE(server
.GetIsUnsynced());
3010 EXPECT_TRUE(local
.GetIsUnsynced());
3011 EXPECT_EQ("Foo.htm", server
.GetNonUniqueName());
3012 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3014 // Allow local changes to commit.
3015 mock_server_
->set_conflict_all_commits(false);
3017 saw_syncer_event_
= false;
3019 // Now add a server change to make the two names equal. There should
3020 // be no conflict with that, since names are not unique.
3021 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
3022 foreign_cache_guid(), "-1");
3024 saw_syncer_event_
= false;
3026 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3027 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3028 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
3029 ASSERT_TRUE(server
.good());
3030 ASSERT_TRUE(local
.good());
3031 EXPECT_TRUE(local
.GetMetahandle()!= server
.GetMetahandle());
3032 EXPECT_FALSE(server
.GetIsUnappliedUpdate());
3033 EXPECT_FALSE(local
.GetIsUnappliedUpdate());
3034 EXPECT_FALSE(server
.GetIsUnsynced());
3035 EXPECT_FALSE(local
.GetIsUnsynced());
3036 EXPECT_EQ("Bar.htm", server
.GetNonUniqueName());
3037 EXPECT_EQ("Bar.htm", local
.GetNonUniqueName());
3038 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
3039 server
.GetSpecifics().bookmark().url());
3043 // Circular links should be resolved by the server.
3044 TEST_F(SyncerTest
, SiblingDirectoriesBecomeCircular
) {
3045 // we don't currently resolve this. This test ensures we don't.
3046 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
3047 foreign_cache_guid(), "-1");
3048 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
3049 foreign_cache_guid(), "-2");
3052 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3053 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3054 ASSERT_TRUE(A
.good());
3055 A
.PutIsUnsynced(true);
3056 A
.PutParentId(ids_
.FromNumber(2));
3057 A
.PutNonUniqueName("B");
3059 mock_server_
->AddUpdateDirectory(2, 1, "A", 20, 20,
3060 foreign_cache_guid(), "-2");
3061 mock_server_
->set_conflict_all_commits(true);
3063 saw_syncer_event_
= false;
3065 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3066 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3067 ASSERT_TRUE(A
.good());
3068 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
3069 ASSERT_TRUE(B
.good());
3070 EXPECT_TRUE(A
.GetNonUniqueName()== "B");
3071 EXPECT_TRUE(B
.GetNonUniqueName()== "B");
3075 TEST_F(SyncerTest
, SwapEntryNames
) {
3076 // Simple transaction test.
3077 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
3078 foreign_cache_guid(), "-1");
3079 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
3080 foreign_cache_guid(), "-2");
3081 mock_server_
->set_conflict_all_commits(true);
3084 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3085 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
3086 ASSERT_TRUE(A
.good());
3087 A
.PutIsUnsynced(true);
3088 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
3089 ASSERT_TRUE(B
.good());
3090 B
.PutIsUnsynced(true);
3091 A
.PutNonUniqueName("C");
3092 B
.PutNonUniqueName("A");
3093 A
.PutNonUniqueName("B");
3096 saw_syncer_event_
= false;
3099 TEST_F(SyncerTest
, DualDeletionWithNewItemNameClash
) {
3100 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
3101 foreign_cache_guid(), "-1");
3102 mock_server_
->AddUpdateBookmark(2, 0, "B", 10, 10,
3103 foreign_cache_guid(), "-2");
3104 mock_server_
->set_conflict_all_commits(true);
3107 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3108 MutableEntry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3109 ASSERT_TRUE(B
.good());
3110 WriteTestDataToEntry(&trans
, &B
);
3113 mock_server_
->AddUpdateBookmark(2, 0, "A", 11, 11,
3114 foreign_cache_guid(), "-2");
3115 mock_server_
->SetLastUpdateDeleted();
3118 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3119 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3120 ASSERT_TRUE(B
.good());
3121 EXPECT_FALSE(B
.GetIsUnsynced());
3122 EXPECT_FALSE(B
.GetIsUnappliedUpdate());
3124 saw_syncer_event_
= false;
3127 // When we undelete an entity as a result of conflict resolution, we reuse the
3128 // existing server id and preserve the old version, simply updating the server
3129 // version with the new non-deleted entity.
3130 TEST_F(SyncerTest
, ResolveWeWroteTheyDeleted
) {
3131 int64 bob_metahandle
;
3133 mock_server_
->AddUpdateBookmark(1, 0, "bob", 1, 10,
3134 foreign_cache_guid(), "-1");
3137 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3138 MutableEntry
bob(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3139 ASSERT_TRUE(bob
.good());
3140 bob_metahandle
= bob
.GetMetahandle();
3141 WriteTestDataToEntry(&trans
, &bob
);
3143 mock_server_
->AddUpdateBookmark(1, 0, "bob", 2, 10,
3144 foreign_cache_guid(), "-1");
3145 mock_server_
->SetLastUpdateDeleted();
3146 mock_server_
->set_conflict_all_commits(true);
3150 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3151 Entry
bob(&trans
, GET_BY_HANDLE
, bob_metahandle
);
3152 ASSERT_TRUE(bob
.good());
3153 EXPECT_TRUE(bob
.GetIsUnsynced());
3154 EXPECT_TRUE(bob
.GetId().ServerKnows());
3155 EXPECT_FALSE(bob
.GetIsUnappliedUpdate());
3156 EXPECT_FALSE(bob
.GetIsDel());
3157 EXPECT_EQ(2, bob
.GetServerVersion());
3158 EXPECT_EQ(2, bob
.GetBaseVersion());
3160 saw_syncer_event_
= false;
3163 // This test is to reproduce a check failure. Sometimes we would get a bad ID
3164 // back when creating an entry.
3165 TEST_F(SyncerTest
, DuplicateIDReturn
) {
3167 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3168 MutableEntry
folder(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
3169 ASSERT_TRUE(folder
.good());
3170 folder
.PutIsUnsynced(true);
3171 folder
.PutIsDir(true);
3172 folder
.PutSpecifics(DefaultBookmarkSpecifics());
3173 MutableEntry
folder2(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "fred");
3174 ASSERT_TRUE(folder2
.good());
3175 folder2
.PutIsUnsynced(false);
3176 folder2
.PutIsDir(true);
3177 folder2
.PutSpecifics(DefaultBookmarkSpecifics());
3178 folder2
.PutBaseVersion(3);
3179 folder2
.PutId(syncable::Id::CreateFromServerId("mock_server:10000"));
3181 mock_server_
->set_next_new_id(10000);
3182 EXPECT_EQ(1u, directory()->unsynced_entity_count());
3183 // we get back a bad id in here (should never happen).
3185 EXPECT_EQ(1u, directory()->unsynced_entity_count());
3186 SyncShareNudge(); // another bad id in here.
3187 EXPECT_EQ(0u, directory()->unsynced_entity_count());
3188 saw_syncer_event_
= false;
3191 TEST_F(SyncerTest
, DeletedEntryWithBadParentInLoopCalculation
) {
3192 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
3193 foreign_cache_guid(), "-1");
3196 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3197 MutableEntry
bob(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3198 ASSERT_TRUE(bob
.good());
3199 // This is valid, because the parent could have gone away a long time ago.
3200 bob
.PutParentId(ids_
.FromNumber(54));
3202 bob
.PutIsUnsynced(true);
3204 mock_server_
->AddUpdateDirectory(2, 1, "fred", 1, 10,
3205 foreign_cache_guid(), "-2");
3210 TEST_F(SyncerTest
, ConflictResolverMergesLocalDeleteAndServerUpdate
) {
3211 syncable::Id local_id
;
3213 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3215 MutableEntry
local_deleted(
3216 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "name");
3217 local_id
= local_deleted
.GetId();
3218 local_deleted
.PutId(ids_
.FromNumber(1));
3219 local_deleted
.PutBaseVersion(1);
3220 local_deleted
.PutIsDel(true);
3221 local_deleted
.PutIsDir(false);
3222 local_deleted
.PutIsUnsynced(true);
3223 local_deleted
.PutSpecifics(DefaultBookmarkSpecifics());
3226 mock_server_
->AddUpdateBookmark(ids_
.FromNumber(1), root_id_
, "name", 10, 10,
3228 local_id
.GetServerId());
3230 // We don't care about actually committing, just the resolution.
3231 mock_server_
->set_conflict_all_commits(true);
3235 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3236 Entry
local_deleted(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3237 EXPECT_TRUE(local_deleted
.GetBaseVersion()== 10);
3238 EXPECT_TRUE(local_deleted
.GetIsUnappliedUpdate()== false);
3239 EXPECT_TRUE(local_deleted
.GetIsUnsynced()== true);
3240 EXPECT_TRUE(local_deleted
.GetIsDel()== true);
3241 EXPECT_TRUE(local_deleted
.GetIsDir()== false);
3245 // See what happens if the IS_DIR bit gets flipped. This can cause us
3246 // all kinds of disasters.
3247 TEST_F(SyncerTest
, UpdateFlipsTheFolderBit
) {
3248 // Local object: a deleted directory (container), revision 1, unsynced.
3250 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3252 MutableEntry
local_deleted(
3253 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "name");
3254 local_deleted
.PutId(ids_
.FromNumber(1));
3255 local_deleted
.PutBaseVersion(1);
3256 local_deleted
.PutIsDel(true);
3257 local_deleted
.PutIsDir(true);
3258 local_deleted
.PutIsUnsynced(true);
3259 local_deleted
.PutSpecifics(DefaultBookmarkSpecifics());
3262 // Server update: entry-type object (not a container), revision 10.
3263 mock_server_
->AddUpdateBookmark(ids_
.FromNumber(1), root_id_
, "name", 10, 10,
3265 ids_
.FromNumber(1).GetServerId());
3267 // Don't attempt to commit.
3268 mock_server_
->set_conflict_all_commits(true);
3270 // The syncer should not attempt to apply the invalid update.
3274 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3275 Entry
local_deleted(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3276 EXPECT_TRUE(local_deleted
.GetBaseVersion()== 1);
3277 EXPECT_TRUE(local_deleted
.GetIsUnappliedUpdate()== false);
3278 EXPECT_TRUE(local_deleted
.GetIsUnsynced()== true);
3279 EXPECT_TRUE(local_deleted
.GetIsDel()== true);
3280 EXPECT_TRUE(local_deleted
.GetIsDir()== true);
3285 // Merge conflict resolution will merge a new local entry with another entry
3286 // that needs updates, resulting in CHECK.
3287 TEST_F(SyncerTest
, MergingExistingItems
) {
3288 mock_server_
->set_conflict_all_commits(true);
3289 mock_server_
->AddUpdateBookmark(1, 0, "base", 10, 10,
3290 local_cache_guid(), "-1");
3293 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3295 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "Copy of base");
3296 WriteTestDataToEntry(&trans
, &entry
);
3298 mock_server_
->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
3299 local_cache_guid(), "-1");
3303 // In this test a long changelog contains a child at the start of the changelog
3304 // and a parent at the end. While these updates are in progress the client would
3306 TEST_F(SyncerTest
, LongChangelistWithApplicationConflict
) {
3307 const int depth
= 400;
3308 syncable::Id folder_id
= ids_
.FromNumber(1);
3310 // First we an item in a folder in the root. However the folder won't come
3312 syncable::Id stuck_entry_id
= TestIdFactory::FromNumber(99999);
3313 mock_server_
->AddUpdateDirectory(stuck_entry_id
,
3314 folder_id
, "stuck", 1, 1,
3315 foreign_cache_guid(), "-99999");
3316 mock_server_
->SetChangesRemaining(depth
- 1);
3319 // Buffer up a very long series of downloads.
3320 // We should never be stuck (conflict resolution shouldn't
3321 // kick in so long as we're making forward progress).
3322 for (int i
= 0; i
< depth
; i
++) {
3323 mock_server_
->NextUpdateBatch();
3324 mock_server_
->SetNewTimestamp(i
+ 1);
3325 mock_server_
->SetChangesRemaining(depth
- i
);
3330 // Ensure our folder hasn't somehow applied.
3332 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3333 Entry
child(&trans
, GET_BY_ID
, stuck_entry_id
);
3334 EXPECT_TRUE(child
.good());
3335 EXPECT_TRUE(child
.GetIsUnappliedUpdate());
3336 EXPECT_TRUE(child
.GetIsDel());
3337 EXPECT_FALSE(child
.GetIsUnsynced());
3340 // And finally the folder.
3341 mock_server_
->AddUpdateDirectory(folder_id
,
3342 TestIdFactory::root(), "folder", 1, 1,
3343 foreign_cache_guid(), "-1");
3344 mock_server_
->SetChangesRemaining(0);
3347 // Check that everything is as expected after the commit.
3349 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3350 Entry
entry(&trans
, GET_BY_ID
, folder_id
);
3351 ASSERT_TRUE(entry
.good());
3352 Entry
child(&trans
, GET_BY_ID
, stuck_entry_id
);
3353 EXPECT_EQ(entry
.GetId(), child
.GetParentId());
3354 EXPECT_EQ("stuck", child
.GetNonUniqueName());
3355 EXPECT_TRUE(child
.good());
3359 TEST_F(SyncerTest
, DontMergeTwoExistingItems
) {
3360 mock_server_
->set_conflict_all_commits(true);
3361 mock_server_
->AddUpdateBookmark(1, 0, "base", 10, 10,
3362 foreign_cache_guid(), "-1");
3363 mock_server_
->AddUpdateBookmark(2, 0, "base2", 10, 10,
3364 foreign_cache_guid(), "-2");
3367 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3368 MutableEntry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3369 ASSERT_TRUE(entry
.good());
3370 entry
.PutNonUniqueName("Copy of base");
3371 entry
.PutIsUnsynced(true);
3373 mock_server_
->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
3374 foreign_cache_guid(), "-1");
3377 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3378 Entry
entry1(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3379 EXPECT_FALSE(entry1
.GetIsUnappliedUpdate());
3380 EXPECT_FALSE(entry1
.GetIsUnsynced());
3381 EXPECT_FALSE(entry1
.GetIsDel());
3382 Entry
entry2(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3383 EXPECT_FALSE(entry2
.GetIsUnappliedUpdate());
3384 EXPECT_TRUE(entry2
.GetIsUnsynced());
3385 EXPECT_FALSE(entry2
.GetIsDel());
3386 EXPECT_EQ(entry1
.GetNonUniqueName(), entry2
.GetNonUniqueName());
3390 TEST_F(SyncerTest
, TestUndeleteUpdate
) {
3391 mock_server_
->set_conflict_all_commits(true);
3392 mock_server_
->AddUpdateDirectory(1, 0, "foo", 1, 1,
3393 foreign_cache_guid(), "-1");
3394 mock_server_
->AddUpdateDirectory(2, 1, "bar", 1, 2,
3395 foreign_cache_guid(), "-2");
3397 mock_server_
->AddUpdateDirectory(2, 1, "bar", 2, 3,
3398 foreign_cache_guid(), "-2");
3399 mock_server_
->SetLastUpdateDeleted();
3404 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3405 Entry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3406 ASSERT_TRUE(entry
.good());
3407 EXPECT_TRUE(entry
.GetIsDel());
3408 metahandle
= entry
.GetMetahandle();
3410 mock_server_
->AddUpdateDirectory(1, 0, "foo", 2, 4,
3411 foreign_cache_guid(), "-1");
3412 mock_server_
->SetLastUpdateDeleted();
3414 // This used to be rejected as it's an undeletion. Now, it results in moving
3415 // the delete path aside.
3416 mock_server_
->AddUpdateDirectory(2, 1, "bar", 3, 5,
3417 foreign_cache_guid(), "-2");
3420 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3421 Entry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3422 ASSERT_TRUE(entry
.good());
3423 EXPECT_TRUE(entry
.GetIsDel());
3424 EXPECT_FALSE(entry
.GetServerIsDel());
3425 EXPECT_TRUE(entry
.GetIsUnappliedUpdate());
3426 EXPECT_NE(entry
.GetMetahandle(), metahandle
);
3430 TEST_F(SyncerTest
, TestMoveSanitizedNamedFolder
) {
3431 mock_server_
->AddUpdateDirectory(1, 0, "foo", 1, 1,
3432 foreign_cache_guid(), "-1");
3433 mock_server_
->AddUpdateDirectory(2, 0, ":::", 1, 2,
3434 foreign_cache_guid(), "-2");
3437 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3438 MutableEntry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3439 ASSERT_TRUE(entry
.good());
3440 entry
.PutParentId(ids_
.FromNumber(1));
3441 EXPECT_TRUE(entry
.PutIsUnsynced(true));
3444 // We use the same sync ts as before so our times match up.
3445 mock_server_
->AddUpdateDirectory(2, 1, ":::", 2, 2,
3446 foreign_cache_guid(), "-2");
3450 // Don't crash when this occurs.
3451 TEST_F(SyncerTest
, UpdateWhereParentIsNotAFolder
) {
3452 mock_server_
->AddUpdateBookmark(1, 0, "B", 10, 10,
3453 foreign_cache_guid(), "-1");
3454 mock_server_
->AddUpdateDirectory(2, 1, "BookmarkParent", 10, 10,
3455 foreign_cache_guid(), "-2");
3456 // Used to cause a CHECK
3459 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
3460 Entry
good_entry(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
3461 ASSERT_TRUE(good_entry
.good());
3462 EXPECT_FALSE(good_entry
.GetIsUnappliedUpdate());
3463 Entry
bad_parent(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(2));
3464 ASSERT_TRUE(bad_parent
.good());
3465 EXPECT_TRUE(bad_parent
.GetIsUnappliedUpdate());
3469 TEST_F(SyncerTest
, DirectoryUpdateTest
) {
3470 Id in_root_id
= ids_
.NewServerId();
3471 Id in_in_root_id
= ids_
.NewServerId();
3473 mock_server_
->AddUpdateDirectory(in_root_id
, TestIdFactory::root(),
3474 "in_root_name", 2, 2,
3475 foreign_cache_guid(), "-1");
3476 mock_server_
->AddUpdateDirectory(in_in_root_id
, in_root_id
,
3477 "in_in_root_name", 3, 3,
3478 foreign_cache_guid(), "-2");
3481 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3482 Entry
in_root(&trans
, GET_BY_ID
, in_root_id
);
3483 ASSERT_TRUE(in_root
.good());
3484 EXPECT_EQ("in_root_name", in_root
.GetNonUniqueName());
3485 EXPECT_EQ(TestIdFactory::root(), in_root
.GetParentId());
3487 Entry
in_in_root(&trans
, GET_BY_ID
, in_in_root_id
);
3488 ASSERT_TRUE(in_in_root
.good());
3489 EXPECT_EQ("in_in_root_name", in_in_root
.GetNonUniqueName());
3490 EXPECT_EQ(in_root_id
, in_in_root
.GetParentId());
3494 TEST_F(SyncerTest
, DirectoryCommitTest
) {
3495 syncable::Id in_root_id
, in_dir_id
;
3496 int64 foo_metahandle
;
3497 int64 bar_metahandle
;
3500 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3501 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "foo");
3502 ASSERT_TRUE(parent
.good());
3503 parent
.PutIsUnsynced(true);
3504 parent
.PutIsDir(true);
3505 parent
.PutSpecifics(DefaultBookmarkSpecifics());
3506 in_root_id
= parent
.GetId();
3507 foo_metahandle
= parent
.GetMetahandle();
3509 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent
.GetId(), "bar");
3510 ASSERT_TRUE(child
.good());
3511 child
.PutIsUnsynced(true);
3512 child
.PutIsDir(true);
3513 child
.PutSpecifics(DefaultBookmarkSpecifics());
3514 bar_metahandle
= child
.GetMetahandle();
3515 in_dir_id
= parent
.GetId();
3519 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3520 Entry
fail_by_old_id_entry(&trans
, GET_BY_ID
, in_root_id
);
3521 ASSERT_FALSE(fail_by_old_id_entry
.good());
3523 Entry
foo_entry(&trans
, GET_BY_HANDLE
, foo_metahandle
);
3524 ASSERT_TRUE(foo_entry
.good());
3525 EXPECT_EQ("foo", foo_entry
.GetNonUniqueName());
3526 EXPECT_NE(foo_entry
.GetId(), in_root_id
);
3528 Entry
bar_entry(&trans
, GET_BY_HANDLE
, bar_metahandle
);
3529 ASSERT_TRUE(bar_entry
.good());
3530 EXPECT_EQ("bar", bar_entry
.GetNonUniqueName());
3531 EXPECT_NE(bar_entry
.GetId(), in_dir_id
);
3532 EXPECT_EQ(foo_entry
.GetId(), bar_entry
.GetParentId());
3536 TEST_F(SyncerTest
, TestClientCommandDuringUpdate
) {
3537 using sync_pb::ClientCommand
;
3539 ClientCommand
* command
= new ClientCommand();
3540 command
->set_set_sync_poll_interval(8);
3541 command
->set_set_sync_long_poll_interval(800);
3542 command
->set_sessions_commit_delay_seconds(3141);
3543 command
->set_client_invalidation_hint_buffer_size(11);
3544 mock_server_
->AddUpdateDirectory(1, 0, "in_root", 1, 1,
3545 foreign_cache_guid(), "-1");
3546 mock_server_
->SetGUClientCommand(command
);
3549 EXPECT_TRUE(TimeDelta::FromSeconds(8) ==
3550 last_short_poll_interval_received_
);
3551 EXPECT_TRUE(TimeDelta::FromSeconds(800) ==
3552 last_long_poll_interval_received_
);
3553 EXPECT_TRUE(TimeDelta::FromSeconds(3141) ==
3554 last_sessions_commit_delay_seconds_
);
3555 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_
);
3557 command
= new ClientCommand();
3558 command
->set_set_sync_poll_interval(180);
3559 command
->set_set_sync_long_poll_interval(190);
3560 command
->set_sessions_commit_delay_seconds(2718);
3561 command
->set_client_invalidation_hint_buffer_size(9);
3562 mock_server_
->AddUpdateDirectory(1, 0, "in_root", 1, 1,
3563 foreign_cache_guid(), "-1");
3564 mock_server_
->SetGUClientCommand(command
);
3567 EXPECT_TRUE(TimeDelta::FromSeconds(180) ==
3568 last_short_poll_interval_received_
);
3569 EXPECT_TRUE(TimeDelta::FromSeconds(190) ==
3570 last_long_poll_interval_received_
);
3571 EXPECT_TRUE(TimeDelta::FromSeconds(2718) ==
3572 last_sessions_commit_delay_seconds_
);
3573 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_
);
3576 TEST_F(SyncerTest
, TestClientCommandDuringCommit
) {
3577 using sync_pb::ClientCommand
;
3579 ClientCommand
* command
= new ClientCommand();
3580 command
->set_set_sync_poll_interval(8);
3581 command
->set_set_sync_long_poll_interval(800);
3582 command
->set_sessions_commit_delay_seconds(3141);
3583 command
->set_client_invalidation_hint_buffer_size(11);
3584 CreateUnsyncedDirectory("X", "id_X");
3585 mock_server_
->SetCommitClientCommand(command
);
3588 EXPECT_TRUE(TimeDelta::FromSeconds(8) ==
3589 last_short_poll_interval_received_
);
3590 EXPECT_TRUE(TimeDelta::FromSeconds(800) ==
3591 last_long_poll_interval_received_
);
3592 EXPECT_TRUE(TimeDelta::FromSeconds(3141) ==
3593 last_sessions_commit_delay_seconds_
);
3594 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_
);
3596 command
= new ClientCommand();
3597 command
->set_set_sync_poll_interval(180);
3598 command
->set_set_sync_long_poll_interval(190);
3599 command
->set_sessions_commit_delay_seconds(2718);
3600 command
->set_client_invalidation_hint_buffer_size(9);
3601 CreateUnsyncedDirectory("Y", "id_Y");
3602 mock_server_
->SetCommitClientCommand(command
);
3605 EXPECT_TRUE(TimeDelta::FromSeconds(180) ==
3606 last_short_poll_interval_received_
);
3607 EXPECT_TRUE(TimeDelta::FromSeconds(190) ==
3608 last_long_poll_interval_received_
);
3609 EXPECT_TRUE(TimeDelta::FromSeconds(2718) ==
3610 last_sessions_commit_delay_seconds_
);
3611 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_
);
3614 TEST_F(SyncerTest
, EnsureWeSendUpOldParent
) {
3615 syncable::Id folder_one_id
= ids_
.FromNumber(1);
3616 syncable::Id folder_two_id
= ids_
.FromNumber(2);
3618 mock_server_
->AddUpdateDirectory(folder_one_id
, TestIdFactory::root(),
3619 "folder_one", 1, 1, foreign_cache_guid(), "-1");
3620 mock_server_
->AddUpdateDirectory(folder_two_id
, TestIdFactory::root(),
3621 "folder_two", 1, 1, foreign_cache_guid(), "-2");
3624 // A moved entry should send an "old parent."
3625 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3626 MutableEntry
entry(&trans
, GET_BY_ID
, folder_one_id
);
3627 ASSERT_TRUE(entry
.good());
3628 entry
.PutParentId(folder_two_id
);
3629 entry
.PutIsUnsynced(true);
3630 // A new entry should send no "old parent."
3631 MutableEntry
create(
3632 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_folder");
3633 create
.PutIsUnsynced(true);
3634 create
.PutSpecifics(DefaultBookmarkSpecifics());
3637 const sync_pb::CommitMessage
& commit
= mock_server_
->last_sent_commit();
3638 ASSERT_EQ(2, commit
.entries_size());
3639 EXPECT_TRUE(commit
.entries(0).parent_id_string() == "2");
3640 EXPECT_TRUE(commit
.entries(0).old_parent_id() == "0");
3641 EXPECT_FALSE(commit
.entries(1).has_old_parent_id());
3644 TEST_F(SyncerTest
, Test64BitVersionSupport
) {
3645 int64 really_big_int
= std::numeric_limits
<int64
>::max() - 12;
3646 const string
name("ringo's dang orang ran rings around my o-ring");
3647 int64 item_metahandle
;
3649 // Try writing max int64 to the version fields of a meta entry.
3651 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3652 MutableEntry
entry(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), name
);
3653 ASSERT_TRUE(entry
.good());
3654 entry
.PutBaseVersion(really_big_int
);
3655 entry
.PutServerVersion(really_big_int
);
3656 entry
.PutId(ids_
.NewServerId());
3657 item_metahandle
= entry
.GetMetahandle();
3659 // Now read it back out and make sure the value is max int64.
3660 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
3661 Entry
entry(&rtrans
, syncable::GET_BY_HANDLE
, item_metahandle
);
3662 ASSERT_TRUE(entry
.good());
3663 EXPECT_TRUE(really_big_int
== entry
.GetBaseVersion());
3666 TEST_F(SyncerTest
, TestSimpleUndelete
) {
3667 Id id
= ids_
.MakeServer("undeletion item"), root
= TestIdFactory::root();
3668 mock_server_
->set_conflict_all_commits(true);
3669 // Let there be an entry from the server.
3670 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 1, 10,
3671 foreign_cache_guid(), "-1");
3673 // Check it out and delete it.
3675 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3676 MutableEntry
entry(&wtrans
, GET_BY_ID
, id
);
3677 ASSERT_TRUE(entry
.good());
3678 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3679 EXPECT_FALSE(entry
.GetIsUnsynced());
3680 EXPECT_FALSE(entry
.GetIsDel());
3681 // Delete it locally.
3682 entry
.PutIsDel(true);
3685 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3687 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3688 Entry
entry(&trans
, GET_BY_ID
, id
);
3689 ASSERT_TRUE(entry
.good());
3690 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3691 EXPECT_FALSE(entry
.GetIsUnsynced());
3692 EXPECT_TRUE(entry
.GetIsDel());
3693 EXPECT_FALSE(entry
.GetServerIsDel());
3696 // Update from server confirming deletion.
3697 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 11,
3698 foreign_cache_guid(), "-1");
3699 mock_server_
->SetLastUpdateDeleted();
3701 // IS_DEL AND SERVER_IS_DEL now both true.
3703 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3704 Entry
entry(&trans
, GET_BY_ID
, id
);
3705 ASSERT_TRUE(entry
.good());
3706 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3707 EXPECT_FALSE(entry
.GetIsUnsynced());
3708 EXPECT_TRUE(entry
.GetIsDel());
3709 EXPECT_TRUE(entry
.GetServerIsDel());
3711 // Undelete from server.
3712 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 12,
3713 foreign_cache_guid(), "-1");
3715 // IS_DEL and SERVER_IS_DEL now both false.
3717 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3718 Entry
entry(&trans
, GET_BY_ID
, id
);
3719 ASSERT_TRUE(entry
.good());
3720 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3721 EXPECT_FALSE(entry
.GetIsUnsynced());
3722 EXPECT_FALSE(entry
.GetIsDel());
3723 EXPECT_FALSE(entry
.GetServerIsDel());
3727 TEST_F(SyncerTest
, TestUndeleteWithMissingDeleteUpdate
) {
3728 Id id
= ids_
.MakeServer("undeletion item"), root
= TestIdFactory::root();
3729 // Let there be a entry, from the server.
3730 mock_server_
->set_conflict_all_commits(true);
3731 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 1, 10,
3732 foreign_cache_guid(), "-1");
3734 // Check it out and delete it.
3736 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3737 MutableEntry
entry(&wtrans
, GET_BY_ID
, id
);
3738 ASSERT_TRUE(entry
.good());
3739 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3740 EXPECT_FALSE(entry
.GetIsUnsynced());
3741 EXPECT_FALSE(entry
.GetIsDel());
3742 // Delete it locally.
3743 entry
.PutIsDel(true);
3746 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3748 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3749 Entry
entry(&trans
, GET_BY_ID
, id
);
3750 ASSERT_TRUE(entry
.good());
3751 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3752 EXPECT_FALSE(entry
.GetIsUnsynced());
3753 EXPECT_TRUE(entry
.GetIsDel());
3754 EXPECT_FALSE(entry
.GetServerIsDel());
3757 // Say we do not get an update from server confirming deletion. Undelete
3759 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 12,
3760 foreign_cache_guid(), "-1");
3762 // IS_DEL and SERVER_IS_DEL now both false.
3764 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3765 Entry
entry(&trans
, GET_BY_ID
, id
);
3766 ASSERT_TRUE(entry
.good());
3767 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
3768 EXPECT_FALSE(entry
.GetIsUnsynced());
3769 EXPECT_FALSE(entry
.GetIsDel());
3770 EXPECT_FALSE(entry
.GetServerIsDel());
3774 TEST_F(SyncerTest
, TestUndeleteIgnoreCorrectlyUnappliedUpdate
) {
3775 Id id1
= ids_
.MakeServer("first"), id2
= ids_
.MakeServer("second");
3776 Id root
= TestIdFactory::root();
3777 // Duplicate! expect path clashing!
3778 mock_server_
->set_conflict_all_commits(true);
3779 mock_server_
->AddUpdateBookmark(id1
, root
, "foo", 1, 10,
3780 foreign_cache_guid(), "-1");
3781 mock_server_
->AddUpdateBookmark(id2
, root
, "foo", 1, 10,
3782 foreign_cache_guid(), "-2");
3784 mock_server_
->AddUpdateBookmark(id2
, root
, "foo2", 2, 20,
3785 foreign_cache_guid(), "-2");
3786 SyncShareNudge(); // Now just don't explode.
3789 TEST_F(SyncerTest
, ClientTagServerCreatedUpdatesWork
) {
3790 mock_server_
->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
3791 foreign_cache_guid(), "-1");
3792 mock_server_
->SetLastUpdateClientTag("permfolder");
3797 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3798 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
3799 ASSERT_TRUE(perm_folder
.good());
3800 EXPECT_FALSE(perm_folder
.GetIsDel());
3801 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
3802 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
3803 EXPECT_EQ(perm_folder
.GetUniqueClientTag(), "permfolder");
3804 EXPECT_EQ(perm_folder
.GetNonUniqueName(), "permitem1");
3807 mock_server_
->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
3808 foreign_cache_guid(), "-1");
3809 mock_server_
->SetLastUpdateClientTag("permfolder");
3813 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3815 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
3816 ASSERT_TRUE(perm_folder
.good());
3817 EXPECT_FALSE(perm_folder
.GetIsDel());
3818 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
3819 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
3820 EXPECT_EQ(perm_folder
.GetUniqueClientTag(), "permfolder");
3821 EXPECT_EQ(perm_folder
.GetNonUniqueName(), "permitem_renamed");
3825 TEST_F(SyncerTest
, ClientTagIllegalUpdateIgnored
) {
3826 mock_server_
->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
3827 foreign_cache_guid(), "-1");
3828 mock_server_
->SetLastUpdateClientTag("permfolder");
3833 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3834 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
3835 ASSERT_TRUE(perm_folder
.good());
3836 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
3837 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
3838 EXPECT_EQ(perm_folder
.GetUniqueClientTag(), "permfolder");
3839 EXPECT_TRUE(perm_folder
.GetNonUniqueName()== "permitem1");
3840 EXPECT_TRUE(perm_folder
.GetId().ServerKnows());
3843 mock_server_
->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
3844 foreign_cache_guid(), "-1");
3845 mock_server_
->SetLastUpdateClientTag("wrongtag");
3849 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3851 // This update is rejected because it has the same ID, but a
3852 // different tag than one that is already on the client.
3853 // The client has a ServerKnows ID, which cannot be overwritten.
3854 Entry
rejected_update(&trans
, GET_BY_CLIENT_TAG
, "wrongtag");
3855 EXPECT_FALSE(rejected_update
.good());
3857 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
3858 ASSERT_TRUE(perm_folder
.good());
3859 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
3860 EXPECT_FALSE(perm_folder
.GetIsUnsynced());
3861 EXPECT_EQ(perm_folder
.GetNonUniqueName(), "permitem1");
3865 TEST_F(SyncerTest
, ClientTagUncommittedTagMatchesUpdate
) {
3866 int64 original_metahandle
= 0;
3869 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3871 &trans
, CREATE
, PREFERENCES
, ids_
.root(), "name");
3872 ASSERT_TRUE(pref
.good());
3873 pref
.PutUniqueClientTag("tag");
3874 pref
.PutIsUnsynced(true);
3875 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
3876 EXPECT_FALSE(pref
.GetId().ServerKnows());
3877 original_metahandle
= pref
.GetMetahandle();
3880 syncable::Id server_id
= TestIdFactory::MakeServer("id");
3881 mock_server_
->AddUpdatePref(server_id
.GetServerId(),
3882 ids_
.root().GetServerId(),
3884 mock_server_
->set_conflict_all_commits(true);
3887 // This should cause client tag reunion, preserving the metahandle.
3889 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3891 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
3892 ASSERT_TRUE(pref
.good());
3893 EXPECT_FALSE(pref
.GetIsDel());
3894 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
3895 EXPECT_TRUE(pref
.GetIsUnsynced());
3896 EXPECT_EQ(10, pref
.GetBaseVersion());
3897 // Entry should have been given the new ID while preserving the
3898 // metahandle; client should have won the conflict resolution.
3899 EXPECT_EQ(original_metahandle
, pref
.GetMetahandle());
3900 EXPECT_EQ("tag", pref
.GetUniqueClientTag());
3901 EXPECT_TRUE(pref
.GetId().ServerKnows());
3904 mock_server_
->set_conflict_all_commits(false);
3907 // The resolved entry ought to commit cleanly.
3909 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3911 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
3912 ASSERT_TRUE(pref
.good());
3913 EXPECT_FALSE(pref
.GetIsDel());
3914 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
3915 EXPECT_FALSE(pref
.GetIsUnsynced());
3916 EXPECT_TRUE(10 < pref
.GetBaseVersion());
3917 // Entry should have been given the new ID while preserving the
3918 // metahandle; client should have won the conflict resolution.
3919 EXPECT_EQ(original_metahandle
, pref
.GetMetahandle());
3920 EXPECT_EQ("tag", pref
.GetUniqueClientTag());
3921 EXPECT_TRUE(pref
.GetId().ServerKnows());
3925 TEST_F(SyncerTest
, ClientTagConflictWithDeletedLocalEntry
) {
3927 // Create a deleted local entry with a unique client tag.
3928 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3930 &trans
, CREATE
, PREFERENCES
, ids_
.root(), "name");
3931 ASSERT_TRUE(pref
.good());
3932 ASSERT_FALSE(pref
.GetId().ServerKnows());
3933 pref
.PutUniqueClientTag("tag");
3934 pref
.PutIsUnsynced(true);
3936 // Note: IS_DEL && !ServerKnows() will clear the UNSYNCED bit.
3937 // (We never attempt to commit server-unknown deleted items, so this
3938 // helps us clean up those entries).
3939 pref
.PutIsDel(true);
3942 // Prepare an update with the same unique client tag.
3943 syncable::Id server_id
= TestIdFactory::MakeServer("id");
3944 mock_server_
->AddUpdatePref(server_id
.GetServerId(),
3945 ids_
.root().GetServerId(),
3949 // The local entry will be overwritten.
3951 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3953 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
3954 ASSERT_TRUE(pref
.good());
3955 ASSERT_TRUE(pref
.GetId().ServerKnows());
3956 EXPECT_FALSE(pref
.GetIsDel());
3957 EXPECT_FALSE(pref
.GetIsUnappliedUpdate());
3958 EXPECT_FALSE(pref
.GetIsUnsynced());
3959 EXPECT_EQ(pref
.GetBaseVersion(), 10);
3960 EXPECT_EQ(pref
.GetUniqueClientTag(), "tag");
3964 TEST_F(SyncerTest
, ClientTagUpdateClashesWithLocalEntry
) {
3965 // This test is written assuming that ID comparison
3966 // will work out in a particular way.
3967 EXPECT_TRUE(ids_
.FromNumber(1) < ids_
.FromNumber(2));
3968 EXPECT_TRUE(ids_
.FromNumber(3) < ids_
.FromNumber(4));
3970 syncable::Id id1
= TestIdFactory::MakeServer("1");
3971 mock_server_
->AddUpdatePref(id1
.GetServerId(), ids_
.root().GetServerId(),
3974 syncable::Id id4
= TestIdFactory::MakeServer("4");
3975 mock_server_
->AddUpdatePref(id4
.GetServerId(), ids_
.root().GetServerId(),
3978 mock_server_
->set_conflict_all_commits(true);
3981 int64 tag1_metahandle
= syncable::kInvalidMetaHandle
;
3982 int64 tag2_metahandle
= syncable::kInvalidMetaHandle
;
3983 // This should cause client tag overwrite.
3985 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3987 Entry
tag1(&trans
, GET_BY_CLIENT_TAG
, "tag1");
3988 ASSERT_TRUE(tag1
.good());
3989 ASSERT_TRUE(tag1
.GetId().ServerKnows());
3990 ASSERT_TRUE(id1
== tag1
.GetId());
3991 EXPECT_FALSE(tag1
.GetIsDel());
3992 EXPECT_FALSE(tag1
.GetIsUnappliedUpdate());
3993 EXPECT_FALSE(tag1
.GetIsUnsynced());
3994 EXPECT_EQ(10, tag1
.GetBaseVersion());
3995 EXPECT_EQ("tag1", tag1
.GetUniqueClientTag());
3996 tag1_metahandle
= tag1
.GetMetahandle();
3998 Entry
tag2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
3999 ASSERT_TRUE(tag2
.good());
4000 ASSERT_TRUE(tag2
.GetId().ServerKnows());
4001 ASSERT_TRUE(id4
== tag2
.GetId());
4002 EXPECT_FALSE(tag2
.GetIsDel());
4003 EXPECT_FALSE(tag2
.GetIsUnappliedUpdate());
4004 EXPECT_FALSE(tag2
.GetIsUnsynced());
4005 EXPECT_EQ(11, tag2
.GetBaseVersion());
4006 EXPECT_EQ("tag2", tag2
.GetUniqueClientTag());
4007 tag2_metahandle
= tag2
.GetMetahandle();
4009 syncable::Directory::Metahandles children
;
4010 directory()->GetChildHandlesById(&trans
, trans
.root_id(), &children
);
4011 ASSERT_EQ(2U, children
.size());
4014 syncable::Id id2
= TestIdFactory::MakeServer("2");
4015 mock_server_
->AddUpdatePref(id2
.GetServerId(), ids_
.root().GetServerId(),
4017 syncable::Id id3
= TestIdFactory::MakeServer("3");
4018 mock_server_
->AddUpdatePref(id3
.GetServerId(), ids_
.root().GetServerId(),
4023 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4025 Entry
tag1(&trans
, GET_BY_CLIENT_TAG
, "tag1");
4026 ASSERT_TRUE(tag1
.good());
4027 ASSERT_TRUE(tag1
.GetId().ServerKnows());
4028 ASSERT_EQ(id1
, tag1
.GetId())
4029 << "ID 1 should be kept, since it was less than ID 2.";
4030 EXPECT_FALSE(tag1
.GetIsDel());
4031 EXPECT_FALSE(tag1
.GetIsUnappliedUpdate());
4032 EXPECT_FALSE(tag1
.GetIsUnsynced());
4033 EXPECT_EQ(10, tag1
.GetBaseVersion());
4034 EXPECT_EQ("tag1", tag1
.GetUniqueClientTag());
4035 EXPECT_EQ(tag1_metahandle
, tag1
.GetMetahandle());
4037 Entry
tag2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
4038 ASSERT_TRUE(tag2
.good());
4039 ASSERT_TRUE(tag2
.GetId().ServerKnows());
4040 ASSERT_EQ(id3
, tag2
.GetId())
4041 << "ID 3 should be kept, since it was less than ID 4.";
4042 EXPECT_FALSE(tag2
.GetIsDel());
4043 EXPECT_FALSE(tag2
.GetIsUnappliedUpdate());
4044 EXPECT_FALSE(tag2
.GetIsUnsynced());
4045 EXPECT_EQ(13, tag2
.GetBaseVersion());
4046 EXPECT_EQ("tag2", tag2
.GetUniqueClientTag());
4047 EXPECT_EQ(tag2_metahandle
, tag2
.GetMetahandle());
4049 syncable::Directory::Metahandles children
;
4050 directory()->GetChildHandlesById(&trans
, trans
.root_id(), &children
);
4051 ASSERT_EQ(2U, children
.size());
4055 TEST_F(SyncerTest
, ClientTagClashWithinBatchOfUpdates
) {
4056 // This test is written assuming that ID comparison
4057 // will work out in a particular way.
4058 EXPECT_TRUE(ids_
.FromNumber(1) < ids_
.FromNumber(4));
4059 EXPECT_TRUE(ids_
.FromNumber(201) < ids_
.FromNumber(205));
4061 // Least ID: winner.
4062 mock_server_
->AddUpdatePref(ids_
.FromNumber(1).GetServerId(),
4063 ids_
.root().GetServerId(), "tag a", 1, 10);
4064 mock_server_
->AddUpdatePref(ids_
.FromNumber(2).GetServerId(),
4065 ids_
.root().GetServerId(), "tag a", 11, 110);
4066 mock_server_
->AddUpdatePref(ids_
.FromNumber(3).GetServerId(),
4067 ids_
.root().GetServerId(), "tag a", 12, 120);
4068 mock_server_
->AddUpdatePref(ids_
.FromNumber(4).GetServerId(),
4069 ids_
.root().GetServerId(), "tag a", 13, 130);
4071 mock_server_
->AddUpdatePref(ids_
.FromNumber(105).GetServerId(),
4072 ids_
.root().GetServerId(), "tag b", 14, 140);
4073 mock_server_
->AddUpdatePref(ids_
.FromNumber(102).GetServerId(),
4074 ids_
.root().GetServerId(), "tag b", 15, 150);
4075 // Least ID: winner.
4076 mock_server_
->AddUpdatePref(ids_
.FromNumber(101).GetServerId(),
4077 ids_
.root().GetServerId(), "tag b", 16, 160);
4078 mock_server_
->AddUpdatePref(ids_
.FromNumber(104).GetServerId(),
4079 ids_
.root().GetServerId(), "tag b", 17, 170);
4081 mock_server_
->AddUpdatePref(ids_
.FromNumber(205).GetServerId(),
4082 ids_
.root().GetServerId(), "tag c", 18, 180);
4083 mock_server_
->AddUpdatePref(ids_
.FromNumber(202).GetServerId(),
4084 ids_
.root().GetServerId(), "tag c", 19, 190);
4085 mock_server_
->AddUpdatePref(ids_
.FromNumber(204).GetServerId(),
4086 ids_
.root().GetServerId(), "tag c", 20, 200);
4087 // Least ID: winner.
4088 mock_server_
->AddUpdatePref(ids_
.FromNumber(201).GetServerId(),
4089 ids_
.root().GetServerId(), "tag c", 21, 210);
4091 mock_server_
->set_conflict_all_commits(true);
4094 // This should cause client tag overwrite.
4096 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4098 Entry
tag_a(&trans
, GET_BY_CLIENT_TAG
, "tag a");
4099 ASSERT_TRUE(tag_a
.good());
4100 EXPECT_TRUE(tag_a
.GetId().ServerKnows());
4101 EXPECT_EQ(ids_
.FromNumber(1), tag_a
.GetId());
4102 EXPECT_FALSE(tag_a
.GetIsDel());
4103 EXPECT_FALSE(tag_a
.GetIsUnappliedUpdate());
4104 EXPECT_FALSE(tag_a
.GetIsUnsynced());
4105 EXPECT_EQ(1, tag_a
.GetBaseVersion());
4106 EXPECT_EQ("tag a", tag_a
.GetUniqueClientTag());
4108 Entry
tag_b(&trans
, GET_BY_CLIENT_TAG
, "tag b");
4109 ASSERT_TRUE(tag_b
.good());
4110 EXPECT_TRUE(tag_b
.GetId().ServerKnows());
4111 EXPECT_EQ(ids_
.FromNumber(101), tag_b
.GetId());
4112 EXPECT_FALSE(tag_b
.GetIsDel());
4113 EXPECT_FALSE(tag_b
.GetIsUnappliedUpdate());
4114 EXPECT_FALSE(tag_b
.GetIsUnsynced());
4115 EXPECT_EQ(16, tag_b
.GetBaseVersion());
4116 EXPECT_EQ("tag b", tag_b
.GetUniqueClientTag());
4118 Entry
tag_c(&trans
, GET_BY_CLIENT_TAG
, "tag c");
4119 ASSERT_TRUE(tag_c
.good());
4120 EXPECT_TRUE(tag_c
.GetId().ServerKnows());
4121 EXPECT_EQ(ids_
.FromNumber(201), tag_c
.GetId());
4122 EXPECT_FALSE(tag_c
.GetIsDel());
4123 EXPECT_FALSE(tag_c
.GetIsUnappliedUpdate());
4124 EXPECT_FALSE(tag_c
.GetIsUnsynced());
4125 EXPECT_EQ(21, tag_c
.GetBaseVersion());
4126 EXPECT_EQ("tag c", tag_c
.GetUniqueClientTag());
4128 syncable::Directory::Metahandles children
;
4129 directory()->GetChildHandlesById(&trans
, trans
.root_id(), &children
);
4130 ASSERT_EQ(3U, children
.size());
4134 TEST_F(SyncerTest
, UniqueServerTagUpdates
) {
4135 // As a hurdle, introduce an item whose name is the same as the tag value
4137 int64 hurdle_handle
= CreateUnsyncedDirectory("bob", "id_bob");
4139 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4140 Entry
hurdle(&trans
, GET_BY_HANDLE
, hurdle_handle
);
4141 ASSERT_TRUE(hurdle
.good());
4142 ASSERT_TRUE(!hurdle
.GetIsDel());
4143 ASSERT_TRUE(hurdle
.GetUniqueServerTag().empty());
4144 ASSERT_TRUE(hurdle
.GetNonUniqueName()== "bob");
4146 // Try to lookup by the tagname. These should fail.
4147 Entry
tag_alpha(&trans
, GET_BY_SERVER_TAG
, "alpha");
4148 EXPECT_FALSE(tag_alpha
.good());
4149 Entry
tag_bob(&trans
, GET_BY_SERVER_TAG
, "bob");
4150 EXPECT_FALSE(tag_bob
.good());
4153 // Now download some tagged items as updates.
4154 mock_server_
->AddUpdateDirectory(
4155 1, 0, "update1", 1, 10, std::string(), std::string());
4156 mock_server_
->SetLastUpdateServerTag("alpha");
4157 mock_server_
->AddUpdateDirectory(
4158 2, 0, "update2", 2, 20, std::string(), std::string());
4159 mock_server_
->SetLastUpdateServerTag("bob");
4163 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4165 // The new items should be applied as new entries, and we should be able
4166 // to look them up by their tag values.
4167 Entry
tag_alpha(&trans
, GET_BY_SERVER_TAG
, "alpha");
4168 ASSERT_TRUE(tag_alpha
.good());
4169 ASSERT_TRUE(!tag_alpha
.GetIsDel());
4170 ASSERT_TRUE(tag_alpha
.GetUniqueServerTag()== "alpha");
4171 ASSERT_TRUE(tag_alpha
.GetNonUniqueName()== "update1");
4172 Entry
tag_bob(&trans
, GET_BY_SERVER_TAG
, "bob");
4173 ASSERT_TRUE(tag_bob
.good());
4174 ASSERT_TRUE(!tag_bob
.GetIsDel());
4175 ASSERT_TRUE(tag_bob
.GetUniqueServerTag()== "bob");
4176 ASSERT_TRUE(tag_bob
.GetNonUniqueName()== "update2");
4177 // The old item should be unchanged.
4178 Entry
hurdle(&trans
, GET_BY_HANDLE
, hurdle_handle
);
4179 ASSERT_TRUE(hurdle
.good());
4180 ASSERT_TRUE(!hurdle
.GetIsDel());
4181 ASSERT_TRUE(hurdle
.GetUniqueServerTag().empty());
4182 ASSERT_TRUE(hurdle
.GetNonUniqueName()== "bob");
4186 TEST_F(SyncerTest
, GetUpdatesSetsRequestedTypes
) {
4187 // The expectations of this test happen in the MockConnectionManager's
4188 // GetUpdates handler. EnableDatatype sets the expectation value from our
4189 // set of enabled/disabled datatypes.
4190 EnableDatatype(BOOKMARKS
);
4192 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4194 EnableDatatype(AUTOFILL
);
4196 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4198 EnableDatatype(PREFERENCES
);
4200 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4202 DisableDatatype(BOOKMARKS
);
4204 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4206 DisableDatatype(AUTOFILL
);
4208 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4210 DisableDatatype(PREFERENCES
);
4211 EnableDatatype(AUTOFILL
);
4213 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4216 // A typical scenario: server and client each have one update for the other.
4217 // This is the "happy path" alternative to UpdateFailsThenDontCommit.
4218 TEST_F(SyncerTest
, UpdateThenCommit
) {
4219 syncable::Id to_receive
= ids_
.NewServerId();
4220 syncable::Id to_commit
= ids_
.NewLocalId();
4222 mock_server_
->AddUpdateDirectory(to_receive
, ids_
.root(), "x", 1, 10,
4223 foreign_cache_guid(), "-1");
4224 int64 commit_handle
= CreateUnsyncedDirectory("y", to_commit
);
4227 // The sync cycle should have included a GetUpdate, then a commit. By the
4228 // time the commit happened, we should have known for sure that there were no
4229 // hierarchy conflicts, and reported this fact to the server.
4230 ASSERT_TRUE(mock_server_
->last_request().has_commit());
4231 VerifyNoHierarchyConflictsReported(mock_server_
->last_request());
4233 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4235 Entry
received(&trans
, GET_BY_ID
, to_receive
);
4236 ASSERT_TRUE(received
.good());
4237 EXPECT_FALSE(received
.GetIsUnsynced());
4238 EXPECT_FALSE(received
.GetIsUnappliedUpdate());
4240 Entry
committed(&trans
, GET_BY_HANDLE
, commit_handle
);
4241 ASSERT_TRUE(committed
.good());
4242 EXPECT_FALSE(committed
.GetIsUnsynced());
4243 EXPECT_FALSE(committed
.GetIsUnappliedUpdate());
4246 // Same as above, but this time we fail to download updates.
4247 // We should not attempt to commit anything unless we successfully downloaded
4248 // updates, otherwise we risk causing a server-side conflict.
4249 TEST_F(SyncerTest
, UpdateFailsThenDontCommit
) {
4250 syncable::Id to_receive
= ids_
.NewServerId();
4251 syncable::Id to_commit
= ids_
.NewLocalId();
4253 mock_server_
->AddUpdateDirectory(to_receive
, ids_
.root(), "x", 1, 10,
4254 foreign_cache_guid(), "-1");
4255 int64 commit_handle
= CreateUnsyncedDirectory("y", to_commit
);
4256 mock_server_
->FailNextPostBufferToPathCall();
4259 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4261 // We did not receive this update.
4262 Entry
received(&trans
, GET_BY_ID
, to_receive
);
4263 ASSERT_FALSE(received
.good());
4265 // And our local update remains unapplied.
4266 Entry
committed(&trans
, GET_BY_HANDLE
, commit_handle
);
4267 ASSERT_TRUE(committed
.good());
4268 EXPECT_TRUE(committed
.GetIsUnsynced());
4269 EXPECT_FALSE(committed
.GetIsUnappliedUpdate());
4271 // Inform the Mock we won't be fetching all updates.
4272 mock_server_
->ClearUpdatesQueue();
4275 // Downloads two updates and applies them successfully.
4276 // This is the "happy path" alternative to ConfigureFailsDontApplyUpdates.
4277 TEST_F(SyncerTest
, ConfigureDownloadsTwoBatchesSuccess
) {
4278 syncable::Id node1
= ids_
.NewServerId();
4279 syncable::Id node2
= ids_
.NewServerId();
4281 // Construct the first GetUpdates response.
4282 mock_server_
->AddUpdateDirectory(node1
, ids_
.root(), "one", 1, 10,
4283 foreign_cache_guid(), "-2");
4284 mock_server_
->SetChangesRemaining(1);
4285 mock_server_
->NextUpdateBatch();
4287 // Construct the second GetUpdates response.
4288 mock_server_
->AddUpdateDirectory(node2
, ids_
.root(), "two", 1, 20,
4289 foreign_cache_guid(), "-2");
4291 SyncShareConfigure();
4293 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4294 // Both nodes should be downloaded and applied.
4296 Entry
n1(&trans
, GET_BY_ID
, node1
);
4297 ASSERT_TRUE(n1
.good());
4298 EXPECT_FALSE(n1
.GetIsUnappliedUpdate());
4300 Entry
n2(&trans
, GET_BY_ID
, node2
);
4301 ASSERT_TRUE(n2
.good());
4302 EXPECT_FALSE(n2
.GetIsUnappliedUpdate());
4305 // Same as the above case, but this time the second batch fails to download.
4306 TEST_F(SyncerTest
, ConfigureFailsDontApplyUpdates
) {
4307 syncable::Id node1
= ids_
.NewServerId();
4308 syncable::Id node2
= ids_
.NewServerId();
4310 // The scenario: we have two batches of updates with one update each. A
4311 // normal confgure step would download all the updates one batch at a time and
4312 // apply them. This configure will succeed in downloading the first batch
4313 // then fail when downloading the second.
4314 mock_server_
->FailNthPostBufferToPathCall(2);
4316 // Construct the first GetUpdates response.
4317 mock_server_
->AddUpdateDirectory(node1
, ids_
.root(), "one", 1, 10,
4318 foreign_cache_guid(), "-1");
4319 mock_server_
->SetChangesRemaining(1);
4320 mock_server_
->NextUpdateBatch();
4322 // Consutrct the second GetUpdates response.
4323 mock_server_
->AddUpdateDirectory(node2
, ids_
.root(), "two", 1, 20,
4324 foreign_cache_guid(), "-2");
4326 SyncShareConfigure();
4328 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4330 // The first node was downloaded, but not applied.
4331 Entry
n1(&trans
, GET_BY_ID
, node1
);
4332 ASSERT_TRUE(n1
.good());
4333 EXPECT_TRUE(n1
.GetIsUnappliedUpdate());
4335 // The second node was not downloaded.
4336 Entry
n2(&trans
, GET_BY_ID
, node2
);
4337 EXPECT_FALSE(n2
.good());
4339 // One update remains undownloaded.
4340 mock_server_
->ClearUpdatesQueue();
4343 TEST_F(SyncerTest
, GetKeySuccess
) {
4345 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4346 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4349 SyncShareConfigure();
4351 EXPECT_EQ(session_
->status_controller().last_get_key_result(), SYNCER_OK
);
4353 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4354 EXPECT_FALSE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4358 TEST_F(SyncerTest
, GetKeyEmpty
) {
4360 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4361 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4364 mock_server_
->SetKeystoreKey(std::string());
4365 SyncShareConfigure();
4367 EXPECT_NE(session_
->status_controller().last_get_key_result(), SYNCER_OK
);
4369 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4370 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4374 // Test what happens if a client deletes, then recreates, an object very
4375 // quickly. It is possible that the deletion gets sent as a commit, and
4376 // the undelete happens during the commit request. The principle here
4377 // is that with a single committing client, conflicts should never
4378 // be encountered, and a client encountering its past actions during
4379 // getupdates should never feed back to override later actions.
4381 // In cases of ordering A-F below, the outcome should be the same.
4382 // Exercised by UndeleteDuringCommit:
4383 // A. Delete - commit - undelete - commitresponse.
4384 // B. Delete - commit - undelete - commitresponse - getupdates.
4385 // Exercised by UndeleteBeforeCommit:
4386 // C. Delete - undelete - commit - commitresponse.
4387 // D. Delete - undelete - commit - commitresponse - getupdates.
4388 // Exercised by UndeleteAfterCommit:
4389 // E. Delete - commit - commitresponse - undelete - commit
4390 // - commitresponse.
4391 // F. Delete - commit - commitresponse - undelete - commit -
4392 // - commitresponse - getupdates.
4393 class SyncerUndeletionTest
: public SyncerTest
{
4395 SyncerUndeletionTest()
4396 : client_tag_("foobar"),
4397 metahandle_(syncable::kInvalidMetaHandle
) {
4401 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4402 MutableEntry
perm_folder(
4403 &trans
, CREATE
, BOOKMARKS
, ids_
.root(), "clientname");
4404 ASSERT_TRUE(perm_folder
.good());
4405 perm_folder
.PutUniqueClientTag(client_tag_
);
4406 perm_folder
.PutIsUnsynced(true);
4407 perm_folder
.PutSyncing(false);
4408 perm_folder
.PutSpecifics(DefaultBookmarkSpecifics());
4409 EXPECT_FALSE(perm_folder
.GetIsUnappliedUpdate());
4410 EXPECT_FALSE(perm_folder
.GetId().ServerKnows());
4411 metahandle_
= perm_folder
.GetMetahandle();
4412 local_id_
= perm_folder
.GetId();
4416 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4417 MutableEntry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4418 ASSERT_TRUE(entry
.good());
4419 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4420 entry
.PutIsDel(true);
4421 entry
.PutIsUnsynced(true);
4422 entry
.PutSyncing(false);
4426 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4427 MutableEntry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4428 ASSERT_TRUE(entry
.good());
4429 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4430 EXPECT_TRUE(entry
.GetIsDel());
4431 entry
.PutIsDel(false);
4432 entry
.PutIsUnsynced(true);
4433 entry
.PutSyncing(false);
4436 int64
GetMetahandleOfTag() {
4437 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4438 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4439 EXPECT_TRUE(entry
.good());
4440 if (!entry
.good()) {
4441 return syncable::kInvalidMetaHandle
;
4443 return entry
.GetMetahandle();
4446 void ExpectUnsyncedCreation() {
4447 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4448 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4450 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4451 EXPECT_FALSE(entry
.GetIsDel());
4452 EXPECT_FALSE(entry
.GetServerIsDel()); // Never been committed.
4453 EXPECT_GE(0, entry
.GetBaseVersion());
4454 EXPECT_TRUE(entry
.GetIsUnsynced());
4455 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4458 void ExpectUnsyncedUndeletion() {
4459 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4460 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4462 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4463 EXPECT_FALSE(entry
.GetIsDel());
4464 EXPECT_TRUE(entry
.GetServerIsDel());
4465 EXPECT_EQ(0, entry
.GetBaseVersion());
4466 EXPECT_TRUE(entry
.GetIsUnsynced());
4467 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4468 EXPECT_TRUE(entry
.GetId().ServerKnows());
4471 void ExpectUnsyncedEdit() {
4472 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4473 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4475 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4476 EXPECT_FALSE(entry
.GetIsDel());
4477 EXPECT_FALSE(entry
.GetServerIsDel());
4478 EXPECT_LT(0, entry
.GetBaseVersion());
4479 EXPECT_TRUE(entry
.GetIsUnsynced());
4480 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4481 EXPECT_TRUE(entry
.GetId().ServerKnows());
4484 void ExpectUnsyncedDeletion() {
4485 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4486 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4488 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4489 EXPECT_TRUE(entry
.GetIsDel());
4490 EXPECT_FALSE(entry
.GetServerIsDel());
4491 EXPECT_TRUE(entry
.GetIsUnsynced());
4492 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4493 EXPECT_LT(0, entry
.GetBaseVersion());
4494 EXPECT_LT(0, entry
.GetServerVersion());
4497 void ExpectSyncedAndCreated() {
4498 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4499 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4501 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4502 EXPECT_FALSE(entry
.GetIsDel());
4503 EXPECT_FALSE(entry
.GetServerIsDel());
4504 EXPECT_LT(0, entry
.GetBaseVersion());
4505 EXPECT_EQ(entry
.GetBaseVersion(), entry
.GetServerVersion());
4506 EXPECT_FALSE(entry
.GetIsUnsynced());
4507 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4510 void ExpectSyncedAndDeleted() {
4511 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4512 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4514 EXPECT_EQ(metahandle_
, entry
.GetMetahandle());
4515 EXPECT_TRUE(entry
.GetIsDel());
4516 EXPECT_TRUE(entry
.GetServerIsDel());
4517 EXPECT_FALSE(entry
.GetIsUnsynced());
4518 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4519 EXPECT_GE(0, entry
.GetBaseVersion());
4520 EXPECT_GE(0, entry
.GetServerVersion());
4524 const std::string client_tag_
;
4525 syncable::Id local_id_
;
4529 TEST_F(SyncerUndeletionTest
, UndeleteDuringCommit
) {
4531 ExpectUnsyncedCreation();
4534 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4535 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4536 ExpectSyncedAndCreated();
4538 // Delete, begin committing the delete, then undelete while committing.
4540 ExpectUnsyncedDeletion();
4541 mock_server_
->SetMidCommitCallback(
4542 base::Bind(&SyncerUndeletionTest::Undelete
, base::Unretained(this)));
4545 // We will continue to commit until all nodes are synced, so we expect
4546 // that both the delete and following undelete were committed. We haven't
4547 // downloaded any updates, though, so the SERVER fields will be the same
4548 // as they were at the start of the cycle.
4549 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4550 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4553 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4554 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4556 // Server fields lag behind.
4557 EXPECT_FALSE(entry
.GetServerIsDel());
4559 // We have committed the second (undelete) update.
4560 EXPECT_FALSE(entry
.GetIsDel());
4561 EXPECT_FALSE(entry
.GetIsUnsynced());
4562 EXPECT_FALSE(entry
.GetIsUnappliedUpdate());
4565 // Now, encounter a GetUpdates corresponding to the deletion from
4566 // the server. The undeletion should prevail again and be committed.
4567 // None of this should trigger any conflict detection -- it is perfectly
4568 // normal to recieve updates from our own commits.
4569 mock_server_
->SetMidCommitCallback(base::Closure());
4570 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
4571 update
->set_originator_cache_guid(local_cache_guid());
4572 update
->set_originator_client_item_id(local_id_
.GetServerId());
4575 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4576 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4577 ExpectSyncedAndCreated();
4580 TEST_F(SyncerUndeletionTest
, UndeleteBeforeCommit
) {
4582 ExpectUnsyncedCreation();
4585 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4586 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4587 ExpectSyncedAndCreated();
4589 // Delete and undelete, then sync to pick up the result.
4591 ExpectUnsyncedDeletion();
4593 ExpectUnsyncedEdit(); // Edit, not undelete: server thinks it exists.
4596 // The item ought to have committed successfully.
4597 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4598 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4599 ExpectSyncedAndCreated();
4601 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4602 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4603 EXPECT_EQ(2, entry
.GetBaseVersion());
4606 // Now, encounter a GetUpdates corresponding to the just-committed
4608 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
4609 update
->set_originator_cache_guid(local_cache_guid());
4610 update
->set_originator_client_item_id(local_id_
.GetServerId());
4612 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4613 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4614 ExpectSyncedAndCreated();
4617 TEST_F(SyncerUndeletionTest
, UndeleteAfterCommitButBeforeGetUpdates
) {
4619 ExpectUnsyncedCreation();
4622 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4623 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4624 ExpectSyncedAndCreated();
4626 // Delete and commit.
4628 ExpectUnsyncedDeletion();
4631 // The item ought to have committed successfully.
4632 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4633 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4634 ExpectSyncedAndDeleted();
4636 // Before the GetUpdates, the item is locally undeleted.
4638 ExpectUnsyncedUndeletion();
4640 // Now, encounter a GetUpdates corresponding to the just-committed
4641 // deletion update. The undeletion should prevail.
4642 mock_server_
->AddUpdateFromLastCommit();
4644 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4645 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4646 ExpectSyncedAndCreated();
4649 TEST_F(SyncerUndeletionTest
, UndeleteAfterDeleteAndGetUpdates
) {
4651 ExpectUnsyncedCreation();
4654 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4655 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4656 ExpectSyncedAndCreated();
4658 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
4659 update
->set_originator_cache_guid(local_cache_guid());
4660 update
->set_originator_client_item_id(local_id_
.GetServerId());
4662 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4663 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4664 ExpectSyncedAndCreated();
4666 // Delete and commit.
4668 ExpectUnsyncedDeletion();
4671 // The item ought to have committed successfully.
4672 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4673 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4674 ExpectSyncedAndDeleted();
4676 // Now, encounter a GetUpdates corresponding to the just-committed
4677 // deletion update. Should be consistent.
4678 mock_server_
->AddUpdateFromLastCommit();
4680 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4681 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4682 ExpectSyncedAndDeleted();
4684 // After the GetUpdates, the item is locally undeleted.
4686 ExpectUnsyncedUndeletion();
4688 // Now, encounter a GetUpdates corresponding to the just-committed
4689 // deletion update. The undeletion should prevail.
4691 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4692 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4693 ExpectSyncedAndCreated();
4696 // Test processing of undeletion GetUpdateses.
4697 TEST_F(SyncerUndeletionTest
, UndeleteAfterOtherClientDeletes
) {
4699 ExpectUnsyncedCreation();
4702 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4703 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4704 ExpectSyncedAndCreated();
4706 // Add a delete from the server.
4707 sync_pb::SyncEntity
* update1
= mock_server_
->AddUpdateFromLastCommit();
4708 update1
->set_originator_cache_guid(local_cache_guid());
4709 update1
->set_originator_client_item_id(local_id_
.GetServerId());
4711 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4712 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4713 ExpectSyncedAndCreated();
4715 // Some other client deletes the item.
4717 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4718 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4719 mock_server_
->AddUpdateTombstone(entry
.GetId());
4723 // The update ought to have applied successfully.
4724 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4725 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4726 ExpectSyncedAndDeleted();
4728 // Undelete it locally.
4730 ExpectUnsyncedUndeletion();
4732 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4733 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4734 ExpectSyncedAndCreated();
4736 // Now, encounter a GetUpdates corresponding to the just-committed
4737 // deletion update. The undeletion should prevail.
4738 sync_pb::SyncEntity
* update2
= mock_server_
->AddUpdateFromLastCommit();
4739 update2
->set_originator_cache_guid(local_cache_guid());
4740 update2
->set_originator_client_item_id(local_id_
.GetServerId());
4742 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4743 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4744 ExpectSyncedAndCreated();
4747 TEST_F(SyncerUndeletionTest
, UndeleteAfterOtherClientDeletesImmediately
) {
4749 ExpectUnsyncedCreation();
4752 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4753 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4754 ExpectSyncedAndCreated();
4756 // Some other client deletes the item before we get a chance
4757 // to GetUpdates our original request.
4759 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4760 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4761 mock_server_
->AddUpdateTombstone(entry
.GetId());
4765 // The update ought to have applied successfully.
4766 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4767 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4768 ExpectSyncedAndDeleted();
4770 // Undelete it locally.
4772 ExpectUnsyncedUndeletion();
4774 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4775 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4776 ExpectSyncedAndCreated();
4778 // Now, encounter a GetUpdates corresponding to the just-committed
4779 // deletion update. The undeletion should prevail.
4780 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
4781 update
->set_originator_cache_guid(local_cache_guid());
4782 update
->set_originator_client_item_id(local_id_
.GetServerId());
4784 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4785 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4786 ExpectSyncedAndCreated();
4789 TEST_F(SyncerUndeletionTest
, OtherClientUndeletes
) {
4791 ExpectUnsyncedCreation();
4794 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4795 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4796 ExpectSyncedAndCreated();
4798 // Get the updates of our just-committed entry.
4799 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
4800 update
->set_originator_cache_guid(local_cache_guid());
4801 update
->set_originator_client_item_id(local_id_
.GetServerId());
4803 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4804 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4805 ExpectSyncedAndCreated();
4807 // We delete the item.
4809 ExpectUnsyncedDeletion();
4812 // The update ought to have applied successfully.
4813 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4814 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4815 ExpectSyncedAndDeleted();
4817 // Now, encounter a GetUpdates corresponding to the just-committed
4819 mock_server_
->AddUpdateFromLastCommit();
4821 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4822 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4823 ExpectSyncedAndDeleted();
4825 // Some other client undeletes the item.
4827 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4828 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4829 mock_server_
->AddUpdateBookmark(
4831 entry
.GetParentId(),
4832 "Thadeusz", 100, 1000,
4833 local_cache_guid(), local_id_
.GetServerId());
4835 mock_server_
->SetLastUpdateClientTag(client_tag_
);
4837 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4838 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4839 ExpectSyncedAndCreated();
4841 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4842 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4843 EXPECT_EQ("Thadeusz", entry
.GetNonUniqueName());
4847 TEST_F(SyncerUndeletionTest
, OtherClientUndeletesImmediately
) {
4849 ExpectUnsyncedCreation();
4852 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4853 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4854 ExpectSyncedAndCreated();
4856 // Get the updates of our just-committed entry.
4857 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
4858 update
->set_originator_cache_guid(local_cache_guid());
4860 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4861 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4862 update
->set_originator_client_item_id(local_id_
.GetServerId());
4865 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4866 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4867 ExpectSyncedAndCreated();
4869 // We delete the item.
4871 ExpectUnsyncedDeletion();
4874 // The update ought to have applied successfully.
4875 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4876 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4877 ExpectSyncedAndDeleted();
4879 // Some other client undeletes before we see the update from our
4882 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4883 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4884 mock_server_
->AddUpdateBookmark(
4886 entry
.GetParentId(),
4887 "Thadeusz", 100, 1000,
4888 local_cache_guid(), local_id_
.GetServerId());
4890 mock_server_
->SetLastUpdateClientTag(client_tag_
);
4892 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4893 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4894 ExpectSyncedAndCreated();
4896 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4897 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_
);
4898 EXPECT_EQ("Thadeusz", entry
.GetNonUniqueName());
4903 TEST_PARAM_BOOKMARK_ENABLE_BIT
,
4904 TEST_PARAM_AUTOFILL_ENABLE_BIT
,
4905 TEST_PARAM_BIT_COUNT
4910 public ::testing::WithParamInterface
<int> {
4912 bool ShouldFailBookmarkCommit() {
4913 return (GetParam() & (1 << TEST_PARAM_BOOKMARK_ENABLE_BIT
)) == 0;
4915 bool ShouldFailAutofillCommit() {
4916 return (GetParam() & (1 << TEST_PARAM_AUTOFILL_ENABLE_BIT
)) == 0;
4920 INSTANTIATE_TEST_CASE_P(ExtensionsActivity
,
4922 testing::Range(0, 1 << TEST_PARAM_BIT_COUNT
));
4924 TEST_P(MixedResult
, ExtensionsActivity
) {
4926 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
4928 MutableEntry
pref(&wtrans
, CREATE
, PREFERENCES
, wtrans
.root_id(), "pref");
4929 ASSERT_TRUE(pref
.good());
4930 pref
.PutIsUnsynced(true);
4932 MutableEntry
bookmark(
4933 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "bookmark");
4934 ASSERT_TRUE(bookmark
.good());
4935 bookmark
.PutIsUnsynced(true);
4937 if (ShouldFailBookmarkCommit()) {
4938 mock_server_
->SetTransientErrorId(bookmark
.GetId());
4941 if (ShouldFailAutofillCommit()) {
4942 mock_server_
->SetTransientErrorId(pref
.GetId());
4947 // Put some extenions activity records into the monitor.
4949 ExtensionsActivity::Records records
;
4950 records
["ABC"].extension_id
= "ABC";
4951 records
["ABC"].bookmark_write_count
= 2049U;
4952 records
["xyz"].extension_id
= "xyz";
4953 records
["xyz"].bookmark_write_count
= 4U;
4954 context_
->extensions_activity()->PutRecords(records
);
4959 ExtensionsActivity::Records final_monitor_records
;
4960 context_
->extensions_activity()->GetAndClearRecords(&final_monitor_records
);
4961 if (ShouldFailBookmarkCommit()) {
4962 ASSERT_EQ(2U, final_monitor_records
.size())
4963 << "Should restore records after unsuccessful bookmark commit.";
4964 EXPECT_EQ("ABC", final_monitor_records
["ABC"].extension_id
);
4965 EXPECT_EQ("xyz", final_monitor_records
["xyz"].extension_id
);
4966 EXPECT_EQ(2049U, final_monitor_records
["ABC"].bookmark_write_count
);
4967 EXPECT_EQ(4U, final_monitor_records
["xyz"].bookmark_write_count
);
4969 EXPECT_TRUE(final_monitor_records
.empty())
4970 << "Should not restore records after successful bookmark commit.";
4974 } // namespace syncer