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.h"
22 #include "base/string_number_conversions.h"
23 #include "base/stringprintf.h"
24 #include "base/time.h"
25 #include "build/build_config.h"
26 #include "sync/engine/get_commit_ids_command.h"
27 #include "sync/engine/net/server_connection_manager.h"
28 #include "sync/engine/process_updates_command.h"
29 #include "sync/engine/sync_scheduler_impl.h"
30 #include "sync/engine/syncer.h"
31 #include "sync/engine/syncer_proto_util.h"
32 #include "sync/engine/throttled_data_type_tracker.h"
33 #include "sync/engine/traffic_recorder.h"
34 #include "sync/internal_api/public/base/model_type.h"
35 #include "sync/internal_api/public/engine/model_safe_worker.h"
36 #include "sync/protocol/bookmark_specifics.pb.h"
37 #include "sync/protocol/nigori_specifics.pb.h"
38 #include "sync/protocol/preference_specifics.pb.h"
39 #include "sync/protocol/sync.pb.h"
40 #include "sync/sessions/sync_session_context.h"
41 #include "sync/syncable/mutable_entry.h"
42 #include "sync/syncable/nigori_util.h"
43 #include "sync/syncable/syncable_delete_journal.h"
44 #include "sync/syncable/syncable_read_transaction.h"
45 #include "sync/syncable/syncable_util.h"
46 #include "sync/syncable/syncable_write_transaction.h"
47 #include "sync/test/engine/fake_model_worker.h"
48 #include "sync/test/engine/mock_connection_manager.h"
49 #include "sync/test/engine/test_directory_setter_upper.h"
50 #include "sync/test/engine/test_id_factory.h"
51 #include "sync/test/engine/test_syncable_utils.h"
52 #include "sync/test/fake_encryptor.h"
53 #include "sync/test/fake_extensions_activity_monitor.h"
54 #include "sync/test/fake_sync_encryption_handler.h"
55 #include "sync/util/cryptographer.h"
56 #include "sync/util/time.h"
57 #include "testing/gtest/include/gtest/gtest.h"
59 using base::TimeDelta
;
70 using syncable::BaseTransaction
;
72 using syncable::CountEntriesWithName
;
73 using syncable::Directory
;
74 using syncable::Entry
;
75 using syncable::GetFirstEntryWithName
;
76 using syncable::GetOnlyEntryWithName
;
78 using syncable::kEncryptedString
;
79 using syncable::MutableEntry
;
80 using syncable::WriteTransaction
;
82 using syncable::BASE_VERSION
;
83 using syncable::CREATE
;
84 using syncable::GET_BY_HANDLE
;
85 using syncable::GET_BY_ID
;
86 using syncable::GET_BY_CLIENT_TAG
;
87 using syncable::GET_BY_SERVER_TAG
;
89 using syncable::IS_DEL
;
90 using syncable::IS_DIR
;
91 using syncable::IS_UNAPPLIED_UPDATE
;
92 using syncable::IS_UNSYNCED
;
93 using syncable::META_HANDLE
;
94 using syncable::MTIME
;
95 using syncable::NON_UNIQUE_NAME
;
96 using syncable::PARENT_ID
;
97 using syncable::BASE_SERVER_SPECIFICS
;
98 using syncable::SERVER_IS_DEL
;
99 using syncable::SERVER_PARENT_ID
;
100 using syncable::SERVER_SPECIFICS
;
101 using syncable::SERVER_VERSION
;
102 using syncable::UNIQUE_CLIENT_TAG
;
103 using syncable::UNIQUE_SERVER_TAG
;
104 using syncable::SPECIFICS
;
105 using syncable::SYNCING
;
106 using syncable::UNITTEST
;
108 using sessions::StatusController
;
109 using sessions::SyncSessionContext
;
110 using sessions::SyncSession
;
112 class SyncerTest
: public testing::Test
,
113 public SyncSession::Delegate
,
114 public SyncEngineEventListener
{
118 saw_syncer_event_(false),
119 traffic_recorder_(0, 0) {
122 // SyncSession::Delegate implementation.
123 virtual void OnSilencedUntil(const base::TimeTicks
& silenced_until
) OVERRIDE
{
124 FAIL() << "Should not get silenced.";
126 virtual bool IsSyncingCurrentlySilenced() OVERRIDE
{
129 virtual void OnReceivedLongPollIntervalUpdate(
130 const base::TimeDelta
& new_interval
) OVERRIDE
{
131 last_long_poll_interval_received_
= new_interval
;
133 virtual void OnReceivedShortPollIntervalUpdate(
134 const base::TimeDelta
& new_interval
) OVERRIDE
{
135 last_short_poll_interval_received_
= new_interval
;
137 virtual void OnReceivedSessionsCommitDelay(
138 const base::TimeDelta
& new_delay
) OVERRIDE
{
139 last_sessions_commit_delay_seconds_
= new_delay
;
141 virtual void OnShouldStopSyncingPermanently() OVERRIDE
{
143 virtual void OnSyncProtocolError(
144 const sessions::SyncSessionSnapshot
& snapshot
) OVERRIDE
{
147 void GetWorkers(std::vector
<ModelSafeWorker
*>* out
) {
148 out
->push_back(worker_
.get());
151 void GetModelSafeRoutingInfo(ModelSafeRoutingInfo
* out
) {
152 // We're just testing the sync engine here, so we shunt everything to
153 // the SyncerThread. Datatypes which aren't enabled aren't in the map.
154 for (ModelTypeSet::Iterator it
= enabled_datatypes_
.First();
155 it
.Good(); it
.Inc()) {
156 (*out
)[it
.Get()] = GROUP_PASSIVE
;
160 virtual void OnSyncEngineEvent(const SyncEngineEvent
& event
) OVERRIDE
{
161 DVLOG(1) << "HandleSyncEngineEvent in unittest " << event
.what_happened
;
162 // we only test for entry-specific events, not status changed ones.
163 switch (event
.what_happened
) {
164 case SyncEngineEvent::SYNC_CYCLE_BEGIN
: // Fall through.
165 case SyncEngineEvent::STATUS_CHANGED
:
166 case SyncEngineEvent::SYNC_CYCLE_ENDED
:
169 CHECK(false) << "Handling unknown error type in unit tests!!";
171 saw_syncer_event_
= true;
174 SyncSession
* MakeSession() {
175 ModelSafeRoutingInfo info
;
176 GetModelSafeRoutingInfo(&info
);
177 ModelTypeInvalidationMap invalidation_map
=
178 ModelSafeRoutingInfoToInvalidationMap(info
, std::string());
179 sessions::SyncSourceInfo
source_info(sync_pb::GetUpdatesCallerInfo::UNKNOWN
,
181 return new SyncSession(context_
.get(), this, source_info
);
184 void SyncShareNudge() {
185 session_
.reset(MakeSession());
186 EXPECT_TRUE(syncer_
->SyncShare(session_
.get(), SYNCER_BEGIN
, SYNCER_END
));
189 void SyncShareConfigure() {
190 session_
.reset(MakeSession());
192 syncer_
->SyncShare(session_
.get(), DOWNLOAD_UPDATES
, APPLY_UPDATES
));
195 virtual void SetUp() {
197 mock_server_
.reset(new MockConnectionManager(directory()));
198 EnableDatatype(BOOKMARKS
);
199 EnableDatatype(NIGORI
);
200 EnableDatatype(PREFERENCES
);
201 EnableDatatype(NIGORI
);
202 worker_
= new FakeModelWorker(GROUP_PASSIVE
);
203 std::vector
<SyncEngineEventListener
*> listeners
;
204 listeners
.push_back(this);
206 ModelSafeRoutingInfo routing_info
;
207 std::vector
<ModelSafeWorker
*> workers
;
209 GetModelSafeRoutingInfo(&routing_info
);
210 GetWorkers(&workers
);
212 throttled_data_type_tracker_
.reset(new ThrottledDataTypeTracker(NULL
));
215 new SyncSessionContext(
216 mock_server_
.get(), directory(), workers
,
217 &extensions_activity_monitor_
, throttled_data_type_tracker_
.get(),
218 listeners
, NULL
, &traffic_recorder_
,
219 true, // enable keystore encryption
220 "fake_invalidator_client_id"));
221 context_
->set_routing_info(routing_info
);
222 syncer_
= new Syncer();
223 session_
.reset(MakeSession());
225 syncable::ReadTransaction
trans(FROM_HERE
, directory());
226 syncable::Directory::ChildHandles children
;
227 directory()->GetChildHandlesById(&trans
, trans
.root_id(), &children
);
228 ASSERT_EQ(0u, children
.size());
229 saw_syncer_event_
= false;
230 root_id_
= TestIdFactory::root();
231 parent_id_
= ids_
.MakeServer("parent id");
232 child_id_
= ids_
.MakeServer("child id");
233 directory()->set_store_birthday(mock_server_
->store_birthday());
234 mock_server_
->SetKeystoreKey("encryption_key");
237 virtual void TearDown() {
238 mock_server_
.reset();
241 dir_maker_
.TearDown();
243 void WriteTestDataToEntry(WriteTransaction
* trans
, MutableEntry
* entry
) {
244 EXPECT_FALSE(entry
->Get(IS_DIR
));
245 EXPECT_FALSE(entry
->Get(IS_DEL
));
246 sync_pb::EntitySpecifics specifics
;
247 specifics
.mutable_bookmark()->set_url("http://demo/");
248 specifics
.mutable_bookmark()->set_favicon("PNG");
249 entry
->Put(syncable::SPECIFICS
, specifics
);
250 entry
->Put(syncable::IS_UNSYNCED
, true);
252 void VerifyTestDataInEntry(BaseTransaction
* trans
, Entry
* entry
) {
253 EXPECT_FALSE(entry
->Get(IS_DIR
));
254 EXPECT_FALSE(entry
->Get(IS_DEL
));
255 VerifyTestBookmarkDataInEntry(entry
);
257 void VerifyTestBookmarkDataInEntry(Entry
* entry
) {
258 const sync_pb::EntitySpecifics
& specifics
= entry
->Get(syncable::SPECIFICS
);
259 EXPECT_TRUE(specifics
.has_bookmark());
260 EXPECT_EQ("PNG", specifics
.bookmark().favicon());
261 EXPECT_EQ("http://demo/", specifics
.bookmark().url());
264 void VerifyHierarchyConflictsReported(
265 const sync_pb::ClientToServerMessage
& message
) {
266 // Our request should have included a warning about hierarchy conflicts.
267 const sync_pb::ClientStatus
& client_status
= message
.client_status();
268 EXPECT_TRUE(client_status
.has_hierarchy_conflict_detected());
269 EXPECT_TRUE(client_status
.hierarchy_conflict_detected());
272 void VerifyNoHierarchyConflictsReported(
273 const sync_pb::ClientToServerMessage
& message
) {
274 // Our request should have reported no hierarchy conflicts detected.
275 const sync_pb::ClientStatus
& client_status
= message
.client_status();
276 EXPECT_TRUE(client_status
.has_hierarchy_conflict_detected());
277 EXPECT_FALSE(client_status
.hierarchy_conflict_detected());
280 void VerifyHierarchyConflictsUnspecified(
281 const sync_pb::ClientToServerMessage
& message
) {
282 // Our request should have neither confirmed nor denied hierarchy conflicts.
283 const sync_pb::ClientStatus
& client_status
= message
.client_status();
284 EXPECT_FALSE(client_status
.has_hierarchy_conflict_detected());
287 void SyncRepeatedlyToTriggerConflictResolution(SyncSession
* session
) {
288 // We should trigger after less than 6 syncs, but extra does no harm.
289 for (int i
= 0 ; i
< 6 ; ++i
)
290 syncer_
->SyncShare(session
, SYNCER_BEGIN
, SYNCER_END
);
292 void SyncRepeatedlyToTriggerStuckSignal(SyncSession
* session
) {
293 // We should trigger after less than 10 syncs, but we want to avoid brittle
295 for (int i
= 0 ; i
< 12 ; ++i
)
296 syncer_
->SyncShare(session
, SYNCER_BEGIN
, SYNCER_END
);
298 sync_pb::EntitySpecifics
DefaultBookmarkSpecifics() {
299 sync_pb::EntitySpecifics result
;
300 AddDefaultFieldValue(BOOKMARKS
, &result
);
304 sync_pb::EntitySpecifics
DefaultPreferencesSpecifics() {
305 sync_pb::EntitySpecifics result
;
306 AddDefaultFieldValue(PREFERENCES
, &result
);
309 // Enumeration of alterations to entries for commit ordering tests.
311 LIST_END
= 0, // Denotes the end of the list of features from below.
312 SYNCED
, // Items are unsynced by default
318 struct CommitOrderingTest
{
319 // expected commit index.
321 // Details about the item
323 syncable::Id parent_id
;
324 EntryFeature features
[10];
326 static CommitOrderingTest
MakeLastCommitItem() {
327 CommitOrderingTest last_commit_item
;
328 last_commit_item
.commit_index
= -1;
329 last_commit_item
.id
= TestIdFactory::root();
330 return last_commit_item
;
334 void RunCommitOrderingTest(CommitOrderingTest
* test
) {
335 map
<int, syncable::Id
> expected_positions
;
336 { // Transaction scope.
337 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
338 while (!test
->id
.IsRoot()) {
339 if (test
->commit_index
>= 0) {
340 map
<int, syncable::Id
>::value_type
entry(test
->commit_index
,
342 bool double_position
= !expected_positions
.insert(entry
).second
;
343 ASSERT_FALSE(double_position
) << "Two id's expected at one position";
345 string utf8_name
= test
->id
.GetServerId();
346 string
name(utf8_name
.begin(), utf8_name
.end());
347 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, test
->parent_id
, name
);
349 entry
.Put(syncable::ID
, test
->id
);
350 if (test
->id
.ServerKnows()) {
351 entry
.Put(BASE_VERSION
, 5);
352 entry
.Put(SERVER_VERSION
, 5);
353 entry
.Put(SERVER_PARENT_ID
, test
->parent_id
);
355 entry
.Put(syncable::IS_DIR
, true);
356 entry
.Put(syncable::IS_UNSYNCED
, true);
357 entry
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
358 // Set the time to 30 seconds in the future to reduce the chance of
360 const base::Time
& now_plus_30s
=
361 base::Time::Now() + base::TimeDelta::FromSeconds(30);
362 const base::Time
& now_minus_2h
=
363 base::Time::Now() - base::TimeDelta::FromHours(2);
364 entry
.Put(syncable::MTIME
, now_plus_30s
);
365 for (size_t i
= 0 ; i
< arraysize(test
->features
) ; ++i
) {
366 switch (test
->features
[i
]) {
370 entry
.Put(syncable::IS_UNSYNCED
, false);
373 entry
.Put(syncable::IS_DEL
, true);
376 entry
.Put(MTIME
, now_minus_2h
);
378 case MOVED_FROM_ROOT
:
379 entry
.Put(SERVER_PARENT_ID
, trans
.root_id());
382 FAIL() << "Bad value in CommitOrderingTest list";
389 ASSERT_TRUE(expected_positions
.size() ==
390 mock_server_
->committed_ids().size());
391 // If this test starts failing, be aware other sort orders could be valid.
392 for (size_t i
= 0; i
< expected_positions
.size(); ++i
) {
393 EXPECT_EQ(1u, expected_positions
.count(i
));
394 EXPECT_TRUE(expected_positions
[i
] == mock_server_
->committed_ids()[i
]);
398 void DoTruncationTest(const vector
<int64
>& unsynced_handle_view
,
399 const vector
<syncable::Id
>& expected_id_order
) {
400 for (size_t limit
= expected_id_order
.size() + 2; limit
> 0; --limit
) {
401 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
403 ModelSafeRoutingInfo routes
;
404 GetModelSafeRoutingInfo(&routes
);
405 sessions::OrderedCommitSet
output_set(routes
);
406 GetCommitIdsCommand
command(&wtrans
, limit
, &output_set
);
407 std::set
<int64
> ready_unsynced_set
;
408 command
.FilterUnreadyEntries(&wtrans
, ModelTypeSet(),
409 ModelTypeSet(), false,
410 unsynced_handle_view
, &ready_unsynced_set
);
411 command
.BuildCommitIds(&wtrans
, routes
, ready_unsynced_set
);
412 size_t truncated_size
= std::min(limit
, expected_id_order
.size());
413 ASSERT_EQ(truncated_size
, output_set
.Size());
414 for (size_t i
= 0; i
< truncated_size
; ++i
) {
415 ASSERT_EQ(expected_id_order
[i
], output_set
.GetCommitIdAt(i
))
416 << "At index " << i
<< " with batch size limited to " << limit
;
418 sessions::OrderedCommitSet::Projection proj
;
419 proj
= output_set
.GetCommitIdProjection(GROUP_PASSIVE
);
420 ASSERT_EQ(truncated_size
, proj
.size());
421 for (size_t i
= 0; i
< truncated_size
; ++i
) {
422 SCOPED_TRACE(::testing::Message("Projection mismatch with i = ") << i
);
423 syncable::Id projected
= output_set
.GetCommitIdAt(proj
[i
]);
424 ASSERT_EQ(expected_id_order
[proj
[i
]], projected
);
425 // Since this projection is the identity, the following holds.
426 ASSERT_EQ(expected_id_order
[i
], projected
);
431 const StatusController
& status() {
432 return session_
->status_controller();
435 Directory
* directory() {
436 return dir_maker_
.directory();
439 const std::string
local_cache_guid() {
440 return directory()->cache_guid();
443 const std::string
foreign_cache_guid() {
444 return "kqyg7097kro6GSUod+GSg==";
447 int64
CreateUnsyncedDirectory(const string
& entry_name
,
448 const string
& idstring
) {
449 return CreateUnsyncedDirectory(entry_name
,
450 syncable::Id::CreateFromServerId(idstring
));
453 int64
CreateUnsyncedDirectory(const string
& entry_name
,
454 const syncable::Id
& id
) {
455 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
457 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), entry_name
);
458 EXPECT_TRUE(entry
.good());
459 entry
.Put(syncable::IS_UNSYNCED
, true);
460 entry
.Put(syncable::IS_DIR
, true);
461 entry
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
462 entry
.Put(syncable::BASE_VERSION
, id
.ServerKnows() ? 1 : 0);
463 entry
.Put(syncable::ID
, id
);
464 return entry
.Get(META_HANDLE
);
467 void EnableDatatype(ModelType model_type
) {
468 enabled_datatypes_
.Put(model_type
);
470 ModelSafeRoutingInfo routing_info
;
471 GetModelSafeRoutingInfo(&routing_info
);
474 context_
->set_routing_info(routing_info
);
477 mock_server_
->ExpectGetUpdatesRequestTypes(enabled_datatypes_
);
480 void DisableDatatype(ModelType model_type
) {
481 enabled_datatypes_
.Remove(model_type
);
483 ModelSafeRoutingInfo routing_info
;
484 GetModelSafeRoutingInfo(&routing_info
);
487 context_
->set_routing_info(routing_info
);
490 mock_server_
->ExpectGetUpdatesRequestTypes(enabled_datatypes_
);
493 template<typename FieldType
, typename ValueType
>
494 ValueType
GetField(int64 metahandle
, FieldType field
,
495 ValueType default_value
) {
496 syncable::ReadTransaction
trans(FROM_HERE
, directory());
497 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle
);
498 EXPECT_TRUE(entry
.good());
500 return default_value
;
502 EXPECT_EQ(metahandle
, entry
.Get(META_HANDLE
));
503 return entry
.Get(field
);
506 // Helper getters that work without a transaction, to reduce boilerplate.
507 Id
Get(int64 metahandle
, syncable::IdField field
) {
508 return GetField(metahandle
, field
, syncable::GetNullId());
511 string
Get(int64 metahandle
, syncable::StringField field
) {
512 return GetField(metahandle
, field
, string());
515 int64
Get(int64 metahandle
, syncable::Int64Field field
) {
516 return GetField(metahandle
, field
, syncable::kInvalidMetaHandle
);
519 int64
Get(int64 metahandle
, syncable::BaseVersion field
) {
520 const int64 kDefaultValue
= -100;
521 return GetField(metahandle
, field
, kDefaultValue
);
524 bool Get(int64 metahandle
, syncable::IndexedBitField field
) {
525 return GetField(metahandle
, field
, false);
528 bool Get(int64 metahandle
, syncable::IsDelField field
) {
529 return GetField(metahandle
, field
, false);
532 bool Get(int64 metahandle
, syncable::BitField field
) {
533 return GetField(metahandle
, field
, false);
536 Cryptographer
* GetCryptographer(syncable::BaseTransaction
* trans
) {
537 return directory()->GetCryptographer(trans
);
540 MessageLoop message_loop_
;
542 // Some ids to aid tests. Only the root one's value is specific. The rest
543 // are named for test clarity.
544 // TODO(chron): Get rid of these inbuilt IDs. They only make it
546 syncable::Id root_id_
;
547 syncable::Id parent_id_
;
548 syncable::Id child_id_
;
552 TestDirectorySetterUpper dir_maker_
;
553 FakeEncryptor encryptor_
;
554 FakeExtensionsActivityMonitor extensions_activity_monitor_
;
555 scoped_ptr
<ThrottledDataTypeTracker
> throttled_data_type_tracker_
;
556 scoped_ptr
<MockConnectionManager
> mock_server_
;
560 scoped_ptr
<SyncSession
> session_
;
561 scoped_ptr
<SyncSessionContext
> context_
;
562 bool saw_syncer_event_
;
563 base::TimeDelta last_short_poll_interval_received_
;
564 base::TimeDelta last_long_poll_interval_received_
;
565 base::TimeDelta last_sessions_commit_delay_seconds_
;
566 scoped_refptr
<ModelSafeWorker
> worker_
;
568 ModelTypeSet enabled_datatypes_
;
569 TrafficRecorder traffic_recorder_
;
571 DISALLOW_COPY_AND_ASSIGN(SyncerTest
);
574 TEST_F(SyncerTest
, TestCallGatherUnsyncedEntries
) {
576 Syncer::UnsyncedMetaHandles handles
;
578 syncable::ReadTransaction
trans(FROM_HERE
, directory());
579 GetUnsyncedEntries(&trans
, &handles
);
581 ASSERT_EQ(0u, handles
.size());
583 // TODO(sync): When we can dynamically connect and disconnect the mock
584 // ServerConnectionManager test disconnected GetUnsyncedEntries here. It's a
585 // regression for a very old bug.
588 TEST_F(SyncerTest
, GetCommitIdsCommandTruncates
) {
589 syncable::Id root
= ids_
.root();
590 // Create two server entries.
591 mock_server_
->AddUpdateDirectory(ids_
.MakeServer("x"), root
, "X", 10, 10,
592 foreign_cache_guid(), "-1");
593 mock_server_
->AddUpdateDirectory(ids_
.MakeServer("w"), root
, "W", 10, 10,
594 foreign_cache_guid(), "-2");
597 // Create some new client entries.
598 CreateUnsyncedDirectory("C", ids_
.MakeLocal("c"));
599 CreateUnsyncedDirectory("B", ids_
.MakeLocal("b"));
600 CreateUnsyncedDirectory("D", ids_
.MakeLocal("d"));
601 CreateUnsyncedDirectory("E", ids_
.MakeLocal("e"));
602 CreateUnsyncedDirectory("J", ids_
.MakeLocal("j"));
605 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
606 MutableEntry
entry_x(&wtrans
, GET_BY_ID
, ids_
.MakeServer("x"));
607 MutableEntry
entry_b(&wtrans
, GET_BY_ID
, ids_
.MakeLocal("b"));
608 MutableEntry
entry_c(&wtrans
, GET_BY_ID
, ids_
.MakeLocal("c"));
609 MutableEntry
entry_d(&wtrans
, GET_BY_ID
, ids_
.MakeLocal("d"));
610 MutableEntry
entry_e(&wtrans
, GET_BY_ID
, ids_
.MakeLocal("e"));
611 MutableEntry
entry_w(&wtrans
, GET_BY_ID
, ids_
.MakeServer("w"));
612 MutableEntry
entry_j(&wtrans
, GET_BY_ID
, ids_
.MakeLocal("j"));
613 entry_x
.Put(IS_UNSYNCED
, true);
614 entry_b
.Put(PARENT_ID
, entry_x
.Get(ID
));
615 entry_d
.Put(PARENT_ID
, entry_b
.Get(ID
));
616 entry_c
.Put(PARENT_ID
, entry_x
.Get(ID
));
617 entry_c
.PutPredecessor(entry_b
.Get(ID
));
618 entry_e
.Put(PARENT_ID
, entry_c
.Get(ID
));
619 entry_w
.PutPredecessor(entry_x
.Get(ID
));
620 entry_w
.Put(IS_UNSYNCED
, true);
621 entry_w
.Put(SERVER_VERSION
, 20);
622 entry_w
.Put(IS_UNAPPLIED_UPDATE
, true); // Fake a conflict.
623 entry_j
.PutPredecessor(entry_w
.Get(ID
));
626 // The arrangement is now: x (b (d) c (e)) w j
627 // Entry "w" is in conflict, making its sucessors unready to commit.
628 vector
<int64
> unsynced_handle_view
;
629 vector
<syncable::Id
> expected_order
;
631 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
632 GetUnsyncedEntries(&rtrans
, &unsynced_handle_view
);
634 // The expected order is "x", "b", "c", "d", "e", truncated appropriately.
635 expected_order
.push_back(ids_
.MakeServer("x"));
636 expected_order
.push_back(ids_
.MakeLocal("b"));
637 expected_order
.push_back(ids_
.MakeLocal("c"));
638 expected_order
.push_back(ids_
.MakeLocal("d"));
639 expected_order
.push_back(ids_
.MakeLocal("e"));
640 DoTruncationTest(unsynced_handle_view
, expected_order
);
643 TEST_F(SyncerTest
, GetCommitIdsFiltersThrottledEntries
) {
644 const ModelTypeSet
throttled_types(BOOKMARKS
);
645 sync_pb::EntitySpecifics bookmark_data
;
646 AddDefaultFieldValue(BOOKMARKS
, &bookmark_data
);
648 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
649 foreign_cache_guid(), "-1");
653 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
654 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
655 ASSERT_TRUE(A
.good());
656 A
.Put(IS_UNSYNCED
, true);
657 A
.Put(SPECIFICS
, bookmark_data
);
658 A
.Put(NON_UNIQUE_NAME
, "bookmark");
661 // Now set the throttled types.
662 context_
->throttled_data_type_tracker()->SetUnthrottleTime(
664 base::TimeTicks::Now() + base::TimeDelta::FromSeconds(1200));
668 // Nothing should have been committed as bookmarks is throttled.
669 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
670 Entry
entryA(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
671 ASSERT_TRUE(entryA
.good());
672 EXPECT_TRUE(entryA
.Get(IS_UNSYNCED
));
676 context_
->throttled_data_type_tracker()->SetUnthrottleTime(
678 base::TimeTicks::Now() - base::TimeDelta::FromSeconds(1200));
681 // It should have been committed.
682 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
683 Entry
entryA(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
684 ASSERT_TRUE(entryA
.good());
685 EXPECT_FALSE(entryA
.Get(IS_UNSYNCED
));
689 // We use a macro so we can preserve the error location.
690 #define VERIFY_ENTRY(id, is_unapplied, is_unsynced, prev_initialized, \
691 parent_id, version, server_version, id_fac, rtrans) \
693 Entry entryA(rtrans, syncable::GET_BY_ID, id_fac.FromNumber(id)); \
694 ASSERT_TRUE(entryA.good()); \
695 /* We don't use EXPECT_EQ here because when the left side param is false,
696 gcc 4.6 warns about converting 'false' to pointer type for argument 1. */ \
697 EXPECT_TRUE(is_unsynced == entryA.Get(IS_UNSYNCED)); \
698 EXPECT_TRUE(is_unapplied == entryA.Get(IS_UNAPPLIED_UPDATE)); \
699 EXPECT_TRUE(prev_initialized == \
700 IsRealDataType(GetModelTypeFromSpecifics( \
701 entryA.Get(BASE_SERVER_SPECIFICS)))); \
702 EXPECT_TRUE(parent_id == -1 || \
703 entryA.Get(PARENT_ID) == id_fac.FromNumber(parent_id)); \
704 EXPECT_EQ(version, entryA.Get(BASE_VERSION)); \
705 EXPECT_EQ(server_version, entryA.Get(SERVER_VERSION)); \
708 TEST_F(SyncerTest
, GetCommitIdsFiltersUnreadyEntries
) {
709 KeyParams key_params
= {"localhost", "dummy", "foobar"};
710 KeyParams other_params
= {"localhost", "dummy", "foobar2"};
711 sync_pb::EntitySpecifics bookmark
, encrypted_bookmark
;
712 bookmark
.mutable_bookmark()->set_url("url");
713 bookmark
.mutable_bookmark()->set_title("title");
714 AddDefaultFieldValue(BOOKMARKS
, &encrypted_bookmark
);
715 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
716 foreign_cache_guid(), "-1");
717 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
718 foreign_cache_guid(), "-2");
719 mock_server_
->AddUpdateDirectory(3, 0, "C", 10, 10,
720 foreign_cache_guid(), "-3");
721 mock_server_
->AddUpdateDirectory(4, 0, "D", 10, 10,
722 foreign_cache_guid(), "-4");
724 // Server side change will put A in conflict.
725 mock_server_
->AddUpdateDirectory(1, 0, "A", 20, 20,
726 foreign_cache_guid(), "-1");
728 // Mark bookmarks as encrypted and set the cryptographer to have pending
730 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
731 Cryptographer
other_cryptographer(&encryptor_
);
732 other_cryptographer
.AddKey(other_params
);
733 sync_pb::EntitySpecifics specifics
;
734 sync_pb::NigoriSpecifics
* nigori
= specifics
.mutable_nigori();
735 other_cryptographer
.GetKeys(nigori
->mutable_encryption_keybag());
736 dir_maker_
.encryption_handler()->EnableEncryptEverything();
737 // Set up with an old passphrase, but have pending keys
738 GetCryptographer(&wtrans
)->AddKey(key_params
);
739 GetCryptographer(&wtrans
)->Encrypt(bookmark
,
740 encrypted_bookmark
.mutable_encrypted());
741 GetCryptographer(&wtrans
)->SetPendingKeys(nigori
->encryption_keybag());
743 // In conflict but properly encrypted.
744 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
745 ASSERT_TRUE(A
.good());
746 A
.Put(IS_UNSYNCED
, true);
747 A
.Put(SPECIFICS
, encrypted_bookmark
);
748 A
.Put(NON_UNIQUE_NAME
, kEncryptedString
);
749 // Not in conflict and properly encrypted.
750 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
751 ASSERT_TRUE(B
.good());
752 B
.Put(IS_UNSYNCED
, true);
753 B
.Put(SPECIFICS
, encrypted_bookmark
);
754 B
.Put(NON_UNIQUE_NAME
, kEncryptedString
);
755 // Unencrypted specifics.
756 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
757 ASSERT_TRUE(C
.good());
758 C
.Put(IS_UNSYNCED
, true);
759 C
.Put(NON_UNIQUE_NAME
, kEncryptedString
);
760 // Unencrypted non_unique_name.
761 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
762 ASSERT_TRUE(D
.good());
763 D
.Put(IS_UNSYNCED
, true);
764 D
.Put(SPECIFICS
, encrypted_bookmark
);
765 D
.Put(NON_UNIQUE_NAME
, "not encrypted");
769 // Nothing should have commited due to bookmarks being encrypted and
770 // the cryptographer having pending keys. A would have been resolved
771 // as a simple conflict, but still be unsynced until the next sync cycle.
772 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
773 VERIFY_ENTRY(1, false, true, false, 0, 20, 20, ids_
, &rtrans
);
774 VERIFY_ENTRY(2, false, true, false, 0, 10, 10, ids_
, &rtrans
);
775 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_
, &rtrans
);
776 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_
, &rtrans
);
778 // Resolve the pending keys.
779 GetCryptographer(&rtrans
)->DecryptPendingKeys(other_params
);
783 // All properly encrypted and non-conflicting items should commit. "A" was
784 // conflicting, but last sync cycle resolved it as simple conflict, so on
785 // this sync cycle it committed succesfullly.
786 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
787 // Committed successfully.
788 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_
, &rtrans
);
789 // Committed successfully.
790 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_
, &rtrans
);
791 // Was not properly encrypted.
792 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_
, &rtrans
);
793 // Was not properly encrypted.
794 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_
, &rtrans
);
797 // Fix the remaining items.
798 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
799 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
800 ASSERT_TRUE(C
.good());
801 C
.Put(SPECIFICS
, encrypted_bookmark
);
802 C
.Put(NON_UNIQUE_NAME
, kEncryptedString
);
803 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
804 ASSERT_TRUE(D
.good());
805 D
.Put(SPECIFICS
, encrypted_bookmark
);
806 D
.Put(NON_UNIQUE_NAME
, kEncryptedString
);
810 const StatusController
& status_controller
= session_
->status_controller();
812 EXPECT_EQ(status_controller
.model_neutral_state().commit_result
, SYNCER_OK
);
813 // None should be unsynced anymore.
814 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
815 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_
, &rtrans
);
816 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_
, &rtrans
);
817 VERIFY_ENTRY(3, false, false, false, 0, 11, 11, ids_
, &rtrans
);
818 VERIFY_ENTRY(4, false, false, false, 0, 11, 11, ids_
, &rtrans
);
822 TEST_F(SyncerTest
, EncryptionAwareConflicts
) {
823 KeyParams key_params
= {"localhost", "dummy", "foobar"};
824 Cryptographer
other_cryptographer(&encryptor_
);
825 other_cryptographer
.AddKey(key_params
);
826 sync_pb::EntitySpecifics bookmark
, encrypted_bookmark
, modified_bookmark
;
827 bookmark
.mutable_bookmark()->set_title("title");
828 other_cryptographer
.Encrypt(bookmark
,
829 encrypted_bookmark
.mutable_encrypted());
830 AddDefaultFieldValue(BOOKMARKS
, &encrypted_bookmark
);
831 modified_bookmark
.mutable_bookmark()->set_title("title2");
832 other_cryptographer
.Encrypt(modified_bookmark
,
833 modified_bookmark
.mutable_encrypted());
834 sync_pb::EntitySpecifics pref
, encrypted_pref
, modified_pref
;
835 pref
.mutable_preference()->set_name("name");
836 AddDefaultFieldValue(PREFERENCES
, &encrypted_pref
);
837 other_cryptographer
.Encrypt(pref
,
838 encrypted_pref
.mutable_encrypted());
839 modified_pref
.mutable_preference()->set_name("name2");
840 other_cryptographer
.Encrypt(modified_pref
,
841 modified_pref
.mutable_encrypted());
843 // Mark bookmarks and preferences as encrypted and set the cryptographer to
844 // have pending keys.
845 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
846 sync_pb::EntitySpecifics specifics
;
847 sync_pb::NigoriSpecifics
* nigori
= specifics
.mutable_nigori();
848 other_cryptographer
.GetKeys(nigori
->mutable_encryption_keybag());
849 dir_maker_
.encryption_handler()->EnableEncryptEverything();
850 GetCryptographer(&wtrans
)->SetPendingKeys(nigori
->encryption_keybag());
851 EXPECT_TRUE(GetCryptographer(&wtrans
)->has_pending_keys());
854 // We need to remember the exact position of our local items, so we can
855 // make updates that do not modify those positions.
860 mock_server_
->AddUpdateSpecifics(1, 0, "A", 10, 10, true, 0, bookmark
,
861 foreign_cache_guid(), "-1");
862 mock_server_
->AddUpdateSpecifics(2, 1, "B", 10, 10, false, 2, bookmark
,
863 foreign_cache_guid(), "-2");
864 mock_server_
->AddUpdateSpecifics(3, 1, "C", 10, 10, false, 1, bookmark
,
865 foreign_cache_guid(), "-3");
866 mock_server_
->AddUpdateSpecifics(4, 0, "D", 10, 10, false, 0, pref
);
869 // Initial state. Everything is normal.
870 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
871 VERIFY_ENTRY(1, false, false, false, 0, 10, 10, ids_
, &rtrans
);
872 VERIFY_ENTRY(2, false, false, false, 1, 10, 10, ids_
, &rtrans
);
873 VERIFY_ENTRY(3, false, false, false, 1, 10, 10, ids_
, &rtrans
);
874 VERIFY_ENTRY(4, false, false, false, 0, 10, 10, ids_
, &rtrans
);
876 Entry
entry1(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
877 ASSERT_TRUE(entry1
.Get(syncable::UNIQUE_POSITION
).Equals(
878 entry1
.Get(syncable::SERVER_UNIQUE_POSITION
)));
879 pos1
= entry1
.Get(syncable::UNIQUE_POSITION
);
880 Entry
entry2(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(2));
881 pos2
= entry2
.Get(syncable::UNIQUE_POSITION
);
882 Entry
entry3(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(3));
883 pos3
= entry3
.Get(syncable::UNIQUE_POSITION
);
886 // Server side encryption will not be applied due to undecryptable data.
887 // At this point, BASE_SERVER_SPECIFICS should be filled for all four items.
888 mock_server_
->AddUpdateSpecifics(1, 0, kEncryptedString
, 20, 20, true, 0,
890 foreign_cache_guid(), "-1");
891 mock_server_
->AddUpdateSpecifics(2, 1, kEncryptedString
, 20, 20, false, 2,
893 foreign_cache_guid(), "-2");
894 mock_server_
->AddUpdateSpecifics(3, 1, kEncryptedString
, 20, 20, false, 1,
896 foreign_cache_guid(), "-3");
897 mock_server_
->AddUpdateSpecifics(4, 0, kEncryptedString
, 20, 20, false, 0,
899 foreign_cache_guid(), "-4");
902 // All should be unapplied due to being undecryptable and have a valid
903 // BASE_SERVER_SPECIFICS.
904 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
905 VERIFY_ENTRY(1, true, false, true, 0, 10, 20, ids_
, &rtrans
);
906 VERIFY_ENTRY(2, true, false, true, 1, 10, 20, ids_
, &rtrans
);
907 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_
, &rtrans
);
908 VERIFY_ENTRY(4, true, false, true, 0, 10, 20, ids_
, &rtrans
);
911 // Server side change that don't modify anything should not affect
912 // BASE_SERVER_SPECIFICS (such as name changes and mtime changes).
913 mock_server_
->AddUpdateSpecifics(1, 0, kEncryptedString
, 30, 30, true, 0,
915 foreign_cache_guid(), "-1");
916 mock_server_
->AddUpdateSpecifics(2, 1, kEncryptedString
, 30, 30, false, 2,
918 foreign_cache_guid(), "-2");
919 // Item 3 doesn't change.
920 mock_server_
->AddUpdateSpecifics(4, 0, kEncryptedString
, 30, 30, false, 0,
922 foreign_cache_guid(), "-4");
925 // Items 1, 2, and 4 should have newer server versions, 3 remains the same.
926 // All should remain unapplied due to be undecryptable.
927 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
928 VERIFY_ENTRY(1, true, false, true, 0, 10, 30, ids_
, &rtrans
);
929 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_
, &rtrans
);
930 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_
, &rtrans
);
931 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_
, &rtrans
);
934 // Positional changes, parent changes, and specifics changes should reset
935 // BASE_SERVER_SPECIFICS.
936 // Became unencrypted.
937 mock_server_
->AddUpdateSpecifics(1, 0, "A", 40, 40, true, 0, bookmark
,
938 foreign_cache_guid(), "-1");
939 // Reordered to after item 2.
940 mock_server_
->AddUpdateSpecifics(3, 1, kEncryptedString
, 30, 30, false, 3,
942 foreign_cache_guid(), "-3");
945 // Items 2 and 4 should be the only ones with BASE_SERVER_SPECIFICS set.
946 // Items 1 is now unencrypted, so should have applied normally.
947 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
948 VERIFY_ENTRY(1, false, false, false, 0, 40, 40, ids_
, &rtrans
);
949 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_
, &rtrans
);
950 VERIFY_ENTRY(3, true, false, false, 1, 10, 30, ids_
, &rtrans
);
951 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_
, &rtrans
);
954 // Make local changes, which should remain unsynced for items 2, 3, 4.
956 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
957 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
958 ASSERT_TRUE(A
.good());
959 A
.Put(SPECIFICS
, modified_bookmark
);
960 A
.Put(NON_UNIQUE_NAME
, kEncryptedString
);
961 A
.Put(IS_UNSYNCED
, true);
962 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
963 ASSERT_TRUE(B
.good());
964 B
.Put(SPECIFICS
, modified_bookmark
);
965 B
.Put(NON_UNIQUE_NAME
, kEncryptedString
);
966 B
.Put(IS_UNSYNCED
, true);
967 MutableEntry
C(&wtrans
, GET_BY_ID
, ids_
.FromNumber(3));
968 ASSERT_TRUE(C
.good());
969 C
.Put(SPECIFICS
, modified_bookmark
);
970 C
.Put(NON_UNIQUE_NAME
, kEncryptedString
);
971 C
.Put(IS_UNSYNCED
, true);
972 MutableEntry
D(&wtrans
, GET_BY_ID
, ids_
.FromNumber(4));
973 ASSERT_TRUE(D
.good());
974 D
.Put(SPECIFICS
, modified_pref
);
975 D
.Put(NON_UNIQUE_NAME
, kEncryptedString
);
976 D
.Put(IS_UNSYNCED
, true);
980 // Item 1 remains unsynced due to there being pending keys.
981 // Items 2, 3, 4 should remain unsynced since they were not up to date.
982 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
983 VERIFY_ENTRY(1, false, true, false, 0, 40, 40, ids_
, &rtrans
);
984 VERIFY_ENTRY(2, true, true, true, 1, 10, 30, ids_
, &rtrans
);
985 VERIFY_ENTRY(3, true, true, false, 1, 10, 30, ids_
, &rtrans
);
986 VERIFY_ENTRY(4, true, true, true, 0, 10, 30, ids_
, &rtrans
);
990 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
991 // Resolve the pending keys.
992 GetCryptographer(&rtrans
)->DecryptPendingKeys(key_params
);
994 // First cycle resolves conflicts, second cycle commits changes.
996 EXPECT_EQ(2, status().model_neutral_state().num_server_overwrites
);
997 EXPECT_EQ(1, status().model_neutral_state().num_local_overwrites
);
998 // We successfully commited item(s).
999 EXPECT_EQ(status().model_neutral_state().commit_result
, SYNCER_OK
);
1002 // Everything should be resolved now. The local changes should have
1003 // overwritten the server changes for 2 and 4, while the server changes
1004 // overwrote the local for entry 3.
1005 EXPECT_EQ(0, status().model_neutral_state().num_server_overwrites
);
1006 EXPECT_EQ(0, status().model_neutral_state().num_local_overwrites
);
1007 EXPECT_EQ(status().model_neutral_state().commit_result
, SYNCER_OK
);
1008 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1009 VERIFY_ENTRY(1, false, false, false, 0, 41, 41, ids_
, &rtrans
);
1010 VERIFY_ENTRY(2, false, false, false, 1, 31, 31, ids_
, &rtrans
);
1011 VERIFY_ENTRY(3, false, false, false, 1, 30, 30, ids_
, &rtrans
);
1012 VERIFY_ENTRY(4, false, false, false, 0, 31, 31, ids_
, &rtrans
);
1017 TEST_F(SyncerTest
, TestGetUnsyncedAndSimpleCommit
) {
1019 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1020 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1021 ASSERT_TRUE(parent
.good());
1022 parent
.Put(syncable::IS_UNSYNCED
, true);
1023 parent
.Put(syncable::IS_DIR
, true);
1024 parent
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1025 parent
.Put(syncable::BASE_VERSION
, 1);
1026 parent
.Put(syncable::ID
, parent_id_
);
1027 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete");
1028 ASSERT_TRUE(child
.good());
1029 child
.Put(syncable::ID
, child_id_
);
1030 child
.Put(syncable::BASE_VERSION
, 1);
1031 WriteTestDataToEntry(&wtrans
, &child
);
1035 ASSERT_EQ(2u, mock_server_
->committed_ids().size());
1036 // If this test starts failing, be aware other sort orders could be valid.
1037 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1038 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1040 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1041 Entry
entry(&rt
, syncable::GET_BY_ID
, child_id_
);
1042 ASSERT_TRUE(entry
.good());
1043 VerifyTestDataInEntry(&rt
, &entry
);
1047 TEST_F(SyncerTest
, TestPurgeWhileUnsynced
) {
1048 // Similar to above, but throw a purge operation into the mix. Bug 49278.
1049 syncable::Id pref_node_id
= TestIdFactory::MakeServer("Tim");
1051 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1052 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1053 ASSERT_TRUE(parent
.good());
1054 parent
.Put(syncable::IS_UNSYNCED
, true);
1055 parent
.Put(syncable::IS_DIR
, true);
1056 parent
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1057 parent
.Put(syncable::BASE_VERSION
, 1);
1058 parent
.Put(syncable::ID
, parent_id_
);
1059 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete");
1060 ASSERT_TRUE(child
.good());
1061 child
.Put(syncable::ID
, child_id_
);
1062 child
.Put(syncable::BASE_VERSION
, 1);
1063 WriteTestDataToEntry(&wtrans
, &child
);
1065 MutableEntry
parent2(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Tim");
1066 ASSERT_TRUE(parent2
.good());
1067 parent2
.Put(syncable::IS_UNSYNCED
, true);
1068 parent2
.Put(syncable::IS_DIR
, true);
1069 parent2
.Put(syncable::SPECIFICS
, DefaultPreferencesSpecifics());
1070 parent2
.Put(syncable::BASE_VERSION
, 1);
1071 parent2
.Put(syncable::ID
, pref_node_id
);
1074 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES
),
1078 ASSERT_EQ(2U, mock_server_
->committed_ids().size());
1079 // If this test starts failing, be aware other sort orders could be valid.
1080 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1081 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1083 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1084 Entry
entry(&rt
, syncable::GET_BY_ID
, child_id_
);
1085 ASSERT_TRUE(entry
.good());
1086 VerifyTestDataInEntry(&rt
, &entry
);
1088 directory()->SaveChanges();
1090 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1091 Entry
entry(&rt
, syncable::GET_BY_ID
, pref_node_id
);
1092 ASSERT_FALSE(entry
.good());
1096 TEST_F(SyncerTest
, TestPurgeWhileUnapplied
) {
1097 // Similar to above, but for unapplied items. Bug 49278.
1099 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1100 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1101 ASSERT_TRUE(parent
.good());
1102 parent
.Put(syncable::IS_UNAPPLIED_UPDATE
, true);
1103 parent
.Put(syncable::IS_DIR
, true);
1104 parent
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1105 parent
.Put(syncable::BASE_VERSION
, 1);
1106 parent
.Put(syncable::ID
, parent_id_
);
1109 directory()->PurgeEntriesWithTypeIn(
1110 ModelTypeSet(BOOKMARKS
), ModelTypeSet());
1113 directory()->SaveChanges();
1115 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1116 Entry
entry(&rt
, syncable::GET_BY_ID
, parent_id_
);
1117 ASSERT_FALSE(entry
.good());
1121 TEST_F(SyncerTest
, TestPurgeWithJournal
) {
1123 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1124 MutableEntry
parent(&wtrans
, syncable::CREATE
, BOOKMARKS
, wtrans
.root_id(),
1126 ASSERT_TRUE(parent
.good());
1127 parent
.Put(syncable::IS_DIR
, true);
1128 parent
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1129 parent
.Put(syncable::BASE_VERSION
, 1);
1130 parent
.Put(syncable::ID
, parent_id_
);
1131 MutableEntry
child(&wtrans
, syncable::CREATE
, BOOKMARKS
, parent_id_
,
1133 ASSERT_TRUE(child
.good());
1134 child
.Put(syncable::ID
, child_id_
);
1135 child
.Put(syncable::BASE_VERSION
, 1);
1136 WriteTestDataToEntry(&wtrans
, &child
);
1138 MutableEntry
parent2(&wtrans
, syncable::CREATE
, PREFERENCES
,
1139 wtrans
.root_id(), "Tim");
1140 ASSERT_TRUE(parent2
.good());
1141 parent2
.Put(syncable::IS_DIR
, true);
1142 parent2
.Put(syncable::SPECIFICS
, DefaultPreferencesSpecifics());
1143 parent2
.Put(syncable::BASE_VERSION
, 1);
1144 parent2
.Put(syncable::ID
, TestIdFactory::MakeServer("Tim"));
1147 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES
, BOOKMARKS
),
1148 ModelTypeSet(BOOKMARKS
));
1150 // Verify bookmark nodes are saved in delete journal but not preference
1152 syncable::ReadTransaction
rt(FROM_HERE
, directory());
1153 syncable::DeleteJournal
* delete_journal
= directory()->delete_journal();
1154 EXPECT_EQ(2u, delete_journal
->GetDeleteJournalSize(&rt
));
1155 syncable::EntryKernelSet journal_entries
;
1156 directory()->delete_journal()->GetDeleteJournals(&rt
, BOOKMARKS
,
1158 EXPECT_EQ(parent_id_
, (*journal_entries
.begin())->ref(syncable::ID
));
1159 EXPECT_EQ(child_id_
, (*journal_entries
.rbegin())->ref(syncable::ID
));
1163 TEST_F(SyncerTest
, TestCommitListOrderingTwoItemsTall
) {
1164 CommitOrderingTest items
[] = {
1165 {1, ids_
.FromNumber(-1001), ids_
.FromNumber(-1000)},
1166 {0, ids_
.FromNumber(-1000), ids_
.FromNumber(0)},
1167 CommitOrderingTest::MakeLastCommitItem(),
1169 RunCommitOrderingTest(items
);
1172 TEST_F(SyncerTest
, TestCommitListOrderingThreeItemsTall
) {
1173 CommitOrderingTest items
[] = {
1174 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1175 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1176 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1177 CommitOrderingTest::MakeLastCommitItem(),
1179 RunCommitOrderingTest(items
);
1182 TEST_F(SyncerTest
, TestCommitListOrderingThreeItemsTallLimitedSize
) {
1183 context_
->set_max_commit_batch_size(2);
1184 CommitOrderingTest items
[] = {
1185 {1, ids_
.FromNumber(-2001), ids_
.FromNumber(-2000)},
1186 {0, ids_
.FromNumber(-2000), ids_
.FromNumber(0)},
1187 {2, ids_
.FromNumber(-2002), ids_
.FromNumber(-2001)},
1188 CommitOrderingTest::MakeLastCommitItem(),
1190 RunCommitOrderingTest(items
);
1193 TEST_F(SyncerTest
, TestCommitListOrderingSingleDeletedItem
) {
1194 CommitOrderingTest items
[] = {
1195 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1196 CommitOrderingTest::MakeLastCommitItem(),
1198 RunCommitOrderingTest(items
);
1201 TEST_F(SyncerTest
, TestCommitListOrderingSingleUncommittedDeletedItem
) {
1202 CommitOrderingTest items
[] = {
1203 {-1, ids_
.FromNumber(-1000), ids_
.FromNumber(0), {DELETED
}},
1204 CommitOrderingTest::MakeLastCommitItem(),
1206 RunCommitOrderingTest(items
);
1209 TEST_F(SyncerTest
, TestCommitListOrderingSingleDeletedItemWithUnroll
) {
1210 CommitOrderingTest items
[] = {
1211 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1212 CommitOrderingTest::MakeLastCommitItem(),
1214 RunCommitOrderingTest(items
);
1218 TestCommitListOrderingSingleLongDeletedItemWithUnroll
) {
1219 CommitOrderingTest items
[] = {
1220 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1221 CommitOrderingTest::MakeLastCommitItem(),
1223 RunCommitOrderingTest(items
);
1226 TEST_F(SyncerTest
, TestCommitListOrderingTwoLongDeletedItemWithUnroll
) {
1227 CommitOrderingTest items
[] = {
1228 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1229 {-1, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
, OLD_MTIME
}},
1230 CommitOrderingTest::MakeLastCommitItem(),
1232 RunCommitOrderingTest(items
);
1235 TEST_F(SyncerTest
, TestCommitListOrdering3LongDeletedItemsWithSizeLimit
) {
1236 context_
->set_max_commit_batch_size(2);
1237 CommitOrderingTest items
[] = {
1238 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1239 {1, ids_
.FromNumber(1001), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1240 {2, ids_
.FromNumber(1002), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1241 CommitOrderingTest::MakeLastCommitItem(),
1243 RunCommitOrderingTest(items
);
1246 TEST_F(SyncerTest
, TestCommitListOrderingTwoDeletedItemsWithUnroll
) {
1247 CommitOrderingTest items
[] = {
1248 {0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
}},
1249 {-1, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
}},
1250 CommitOrderingTest::MakeLastCommitItem(),
1252 RunCommitOrderingTest(items
);
1255 TEST_F(SyncerTest
, TestCommitListOrderingComplexDeletionScenario
) {
1256 CommitOrderingTest items
[] = {
1257 { 0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1258 {-1, ids_
.FromNumber(1001), ids_
.FromNumber(0), {SYNCED
}},
1259 {1, ids_
.FromNumber(1002), ids_
.FromNumber(1001), {DELETED
, OLD_MTIME
}},
1260 {-1, ids_
.FromNumber(1003), ids_
.FromNumber(1001), {SYNCED
}},
1261 {2, ids_
.FromNumber(1004), ids_
.FromNumber(1003), {DELETED
}},
1262 CommitOrderingTest::MakeLastCommitItem(),
1264 RunCommitOrderingTest(items
);
1268 TestCommitListOrderingComplexDeletionScenarioWith2RecentDeletes
) {
1269 CommitOrderingTest items
[] = {
1270 { 0, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1271 {-1, ids_
.FromNumber(1001), ids_
.FromNumber(0), {SYNCED
}},
1272 {1, ids_
.FromNumber(1002), ids_
.FromNumber(1001), {DELETED
, OLD_MTIME
}},
1273 {-1, ids_
.FromNumber(1003), ids_
.FromNumber(1001), {SYNCED
}},
1274 {2, ids_
.FromNumber(1004), ids_
.FromNumber(1003), {DELETED
}},
1275 {3, ids_
.FromNumber(1005), ids_
.FromNumber(1003), {DELETED
}},
1276 CommitOrderingTest::MakeLastCommitItem(),
1278 RunCommitOrderingTest(items
);
1281 TEST_F(SyncerTest
, TestCommitListOrderingDeleteMovedItems
) {
1282 CommitOrderingTest items
[] = {
1283 {1, ids_
.FromNumber(1000), ids_
.FromNumber(0), {DELETED
, OLD_MTIME
}},
1284 {0, ids_
.FromNumber(1001), ids_
.FromNumber(1000), {DELETED
, OLD_MTIME
,
1286 CommitOrderingTest::MakeLastCommitItem(),
1288 RunCommitOrderingTest(items
);
1291 TEST_F(SyncerTest
, TestCommitListOrderingWithNesting
) {
1292 const base::Time
& now_minus_2h
=
1293 base::Time::Now() - base::TimeDelta::FromHours(2);
1295 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1297 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bob");
1298 ASSERT_TRUE(parent
.good());
1299 parent
.Put(syncable::IS_UNSYNCED
, true);
1300 parent
.Put(syncable::IS_DIR
, true);
1301 parent
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1302 parent
.Put(syncable::ID
, ids_
.FromNumber(100));
1303 parent
.Put(syncable::BASE_VERSION
, 1);
1305 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(100), "Bob");
1306 ASSERT_TRUE(child
.good());
1307 child
.Put(syncable::IS_UNSYNCED
, true);
1308 child
.Put(syncable::IS_DIR
, true);
1309 child
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1310 child
.Put(syncable::ID
, ids_
.FromNumber(101));
1311 child
.Put(syncable::BASE_VERSION
, 1);
1312 MutableEntry
grandchild(
1313 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(101), "Bob");
1314 ASSERT_TRUE(grandchild
.good());
1315 grandchild
.Put(syncable::ID
, ids_
.FromNumber(102));
1316 grandchild
.Put(syncable::IS_UNSYNCED
, true);
1317 grandchild
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1318 grandchild
.Put(syncable::BASE_VERSION
, 1);
1321 // Create three deleted items which deletions we expect to be sent to the
1323 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Pete");
1324 ASSERT_TRUE(parent
.good());
1325 parent
.Put(syncable::ID
, ids_
.FromNumber(103));
1326 parent
.Put(syncable::IS_UNSYNCED
, true);
1327 parent
.Put(syncable::IS_DIR
, true);
1328 parent
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1329 parent
.Put(syncable::IS_DEL
, true);
1330 parent
.Put(syncable::BASE_VERSION
, 1);
1331 parent
.Put(syncable::MTIME
, now_minus_2h
);
1333 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(103), "Pete");
1334 ASSERT_TRUE(child
.good());
1335 child
.Put(syncable::ID
, ids_
.FromNumber(104));
1336 child
.Put(syncable::IS_UNSYNCED
, true);
1337 child
.Put(syncable::IS_DIR
, true);
1338 child
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1339 child
.Put(syncable::IS_DEL
, true);
1340 child
.Put(syncable::BASE_VERSION
, 1);
1341 child
.Put(syncable::MTIME
, now_minus_2h
);
1342 MutableEntry
grandchild(
1343 &wtrans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(104), "Pete");
1344 ASSERT_TRUE(grandchild
.good());
1345 grandchild
.Put(syncable::ID
, ids_
.FromNumber(105));
1346 grandchild
.Put(syncable::IS_UNSYNCED
, true);
1347 grandchild
.Put(syncable::IS_DEL
, true);
1348 grandchild
.Put(syncable::IS_DIR
, false);
1349 grandchild
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1350 grandchild
.Put(syncable::BASE_VERSION
, 1);
1351 grandchild
.Put(syncable::MTIME
, now_minus_2h
);
1356 ASSERT_EQ(6u, mock_server_
->committed_ids().size());
1357 // This test will NOT unroll deletes because SERVER_PARENT_ID is not set.
1358 // It will treat these like moves.
1359 vector
<syncable::Id
> commit_ids(mock_server_
->committed_ids());
1360 EXPECT_TRUE(ids_
.FromNumber(100) == commit_ids
[0]);
1361 EXPECT_TRUE(ids_
.FromNumber(101) == commit_ids
[1]);
1362 EXPECT_TRUE(ids_
.FromNumber(102) == commit_ids
[2]);
1363 // We don't guarantee the delete orders in this test, only that they occur
1365 std::sort(commit_ids
.begin() + 3, commit_ids
.end());
1366 EXPECT_TRUE(ids_
.FromNumber(103) == commit_ids
[3]);
1367 EXPECT_TRUE(ids_
.FromNumber(104) == commit_ids
[4]);
1368 EXPECT_TRUE(ids_
.FromNumber(105) == commit_ids
[5]);
1371 TEST_F(SyncerTest
, TestCommitListOrderingWithNewItems
) {
1372 syncable::Id parent1_id
= ids_
.MakeServer("p1");
1373 syncable::Id parent2_id
= ids_
.MakeServer("p2");
1376 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1377 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "1");
1378 ASSERT_TRUE(parent
.good());
1379 parent
.Put(syncable::IS_UNSYNCED
, true);
1380 parent
.Put(syncable::IS_DIR
, true);
1381 parent
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1382 parent
.Put(syncable::ID
, parent1_id
);
1383 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "2");
1384 ASSERT_TRUE(child
.good());
1385 child
.Put(syncable::IS_UNSYNCED
, true);
1386 child
.Put(syncable::IS_DIR
, true);
1387 child
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1388 child
.Put(syncable::ID
, parent2_id
);
1389 parent
.Put(syncable::BASE_VERSION
, 1);
1390 child
.Put(syncable::BASE_VERSION
, 1);
1393 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1394 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, parent1_id
, "A");
1395 ASSERT_TRUE(parent
.good());
1396 parent
.Put(syncable::IS_UNSYNCED
, true);
1397 parent
.Put(syncable::IS_DIR
, true);
1398 parent
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1399 parent
.Put(syncable::ID
, ids_
.FromNumber(102));
1400 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent1_id
, "B");
1401 ASSERT_TRUE(child
.good());
1402 child
.Put(syncable::IS_UNSYNCED
, true);
1403 child
.Put(syncable::IS_DIR
, true);
1404 child
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1405 child
.Put(syncable::ID
, ids_
.FromNumber(-103));
1406 parent
.Put(syncable::BASE_VERSION
, 1);
1409 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1410 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, parent2_id
, "A");
1411 ASSERT_TRUE(parent
.good());
1412 parent
.Put(syncable::IS_UNSYNCED
, true);
1413 parent
.Put(syncable::IS_DIR
, true);
1414 parent
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1415 parent
.Put(syncable::ID
, ids_
.FromNumber(-104));
1416 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent2_id
, "B");
1417 ASSERT_TRUE(child
.good());
1418 child
.Put(syncable::IS_UNSYNCED
, true);
1419 child
.Put(syncable::IS_DIR
, true);
1420 child
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1421 child
.Put(syncable::ID
, ids_
.FromNumber(105));
1422 child
.Put(syncable::BASE_VERSION
, 1);
1426 ASSERT_EQ(6u, mock_server_
->committed_ids().size());
1428 // This strange iteration and std::count() usage is to allow the order to
1429 // vary. All we really care about is that parent1_id and parent2_id are the
1430 // first two IDs, and that the children make up the next four. Other than
1431 // that, ordering doesn't matter.
1433 vector
<syncable::Id
>::const_iterator i
=
1434 mock_server_
->committed_ids().begin();
1435 vector
<syncable::Id
>::const_iterator parents_begin
= i
;
1438 vector
<syncable::Id
>::const_iterator parents_end
= i
;
1439 vector
<syncable::Id
>::const_iterator children_begin
= i
;
1440 vector
<syncable::Id
>::const_iterator children_end
=
1441 mock_server_
->committed_ids().end();
1443 EXPECT_EQ(1, count(parents_begin
, parents_end
, parent1_id
));
1444 EXPECT_EQ(1, count(parents_begin
, parents_end
, parent2_id
));
1446 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(-103)));
1447 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(102)));
1448 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(105)));
1449 EXPECT_EQ(1, count(children_begin
, children_end
, ids_
.FromNumber(-104)));
1452 TEST_F(SyncerTest
, TestCommitListOrderingCounterexample
) {
1453 syncable::Id child2_id
= ids_
.NewServerId();
1456 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1457 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "P");
1458 ASSERT_TRUE(parent
.good());
1459 parent
.Put(syncable::IS_UNSYNCED
, true);
1460 parent
.Put(syncable::IS_DIR
, true);
1461 parent
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1462 parent
.Put(syncable::ID
, parent_id_
);
1463 MutableEntry
child1(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "1");
1464 ASSERT_TRUE(child1
.good());
1465 child1
.Put(syncable::IS_UNSYNCED
, true);
1466 child1
.Put(syncable::ID
, child_id_
);
1467 child1
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1468 MutableEntry
child2(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "2");
1469 ASSERT_TRUE(child2
.good());
1470 child2
.Put(syncable::IS_UNSYNCED
, true);
1471 child2
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1472 child2
.Put(syncable::ID
, child2_id
);
1474 parent
.Put(syncable::BASE_VERSION
, 1);
1475 child1
.Put(syncable::BASE_VERSION
, 1);
1476 child2
.Put(syncable::BASE_VERSION
, 1);
1480 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1481 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1482 // There are two possible valid orderings.
1483 if (child2_id
== mock_server_
->committed_ids()[1]) {
1484 EXPECT_TRUE(child2_id
== mock_server_
->committed_ids()[1]);
1485 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[2]);
1487 EXPECT_TRUE(child_id_
== mock_server_
->committed_ids()[1]);
1488 EXPECT_TRUE(child2_id
== mock_server_
->committed_ids()[2]);
1492 TEST_F(SyncerTest
, TestCommitListOrderingAndNewParent
) {
1493 string parent1_name
= "1";
1494 string parent2_name
= "A";
1495 string child_name
= "B";
1498 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1499 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(),
1501 ASSERT_TRUE(parent
.good());
1502 parent
.Put(syncable::IS_UNSYNCED
, true);
1503 parent
.Put(syncable::IS_DIR
, true);
1504 parent
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1505 parent
.Put(syncable::ID
, parent_id_
);
1506 parent
.Put(syncable::BASE_VERSION
, 1);
1509 syncable::Id parent2_id
= ids_
.NewLocalId();
1510 syncable::Id child_id
= ids_
.NewServerId();
1512 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1513 MutableEntry
parent2(
1514 &wtrans
, CREATE
, BOOKMARKS
, parent_id_
, parent2_name
);
1515 ASSERT_TRUE(parent2
.good());
1516 parent2
.Put(syncable::IS_UNSYNCED
, true);
1517 parent2
.Put(syncable::IS_DIR
, true);
1518 parent2
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1519 parent2
.Put(syncable::ID
, parent2_id
);
1522 &wtrans
, CREATE
, BOOKMARKS
, parent2_id
, child_name
);
1523 ASSERT_TRUE(child
.good());
1524 child
.Put(syncable::IS_UNSYNCED
, true);
1525 child
.Put(syncable::IS_DIR
, true);
1526 child
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1527 child
.Put(syncable::ID
, child_id
);
1528 child
.Put(syncable::BASE_VERSION
, 1);
1532 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1533 // If this test starts failing, be aware other sort orders could be valid.
1534 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1535 EXPECT_TRUE(parent2_id
== mock_server_
->committed_ids()[1]);
1536 EXPECT_TRUE(child_id
== mock_server_
->committed_ids()[2]);
1538 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1539 // Check that things committed correctly.
1540 Entry
entry_1(&rtrans
, syncable::GET_BY_ID
, parent_id_
);
1541 EXPECT_EQ(entry_1
.Get(NON_UNIQUE_NAME
), parent1_name
);
1542 // Check that parent2 is a subfolder of parent1.
1543 EXPECT_EQ(1, CountEntriesWithName(&rtrans
,
1547 // Parent2 was a local ID and thus should have changed on commit!
1548 Entry
pre_commit_entry_parent2(&rtrans
, syncable::GET_BY_ID
, parent2_id
);
1549 ASSERT_FALSE(pre_commit_entry_parent2
.good());
1551 // Look up the new ID.
1552 Id parent2_committed_id
=
1553 GetOnlyEntryWithName(&rtrans
, parent_id_
, parent2_name
);
1554 EXPECT_TRUE(parent2_committed_id
.ServerKnows());
1556 Entry
child(&rtrans
, syncable::GET_BY_ID
, child_id
);
1557 EXPECT_EQ(parent2_committed_id
, child
.Get(syncable::PARENT_ID
));
1561 TEST_F(SyncerTest
, TestCommitListOrderingAndNewParentAndChild
) {
1562 string parent_name
= "1";
1563 string parent2_name
= "A";
1564 string child_name
= "B";
1567 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1568 MutableEntry
parent(&wtrans
,
1572 ASSERT_TRUE(parent
.good());
1573 parent
.Put(syncable::IS_UNSYNCED
, true);
1574 parent
.Put(syncable::IS_DIR
, true);
1575 parent
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1576 parent
.Put(syncable::ID
, parent_id_
);
1577 parent
.Put(syncable::BASE_VERSION
, 1);
1580 int64 meta_handle_b
;
1581 const Id parent2_local_id
= ids_
.NewLocalId();
1582 const Id child_local_id
= ids_
.NewLocalId();
1584 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
1585 MutableEntry
parent2(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, parent2_name
);
1586 ASSERT_TRUE(parent2
.good());
1587 parent2
.Put(syncable::IS_UNSYNCED
, true);
1588 parent2
.Put(syncable::IS_DIR
, true);
1589 parent2
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1591 parent2
.Put(syncable::ID
, parent2_local_id
);
1593 &wtrans
, CREATE
, BOOKMARKS
, parent2_local_id
, child_name
);
1594 ASSERT_TRUE(child
.good());
1595 child
.Put(syncable::IS_UNSYNCED
, true);
1596 child
.Put(syncable::IS_DIR
, true);
1597 child
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1598 child
.Put(syncable::ID
, child_local_id
);
1599 meta_handle_b
= child
.Get(syncable::META_HANDLE
);
1603 ASSERT_EQ(3u, mock_server_
->committed_ids().size());
1604 // If this test starts failing, be aware other sort orders could be valid.
1605 EXPECT_TRUE(parent_id_
== mock_server_
->committed_ids()[0]);
1606 EXPECT_TRUE(parent2_local_id
== mock_server_
->committed_ids()[1]);
1607 EXPECT_TRUE(child_local_id
== mock_server_
->committed_ids()[2]);
1609 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
1611 Entry
parent(&rtrans
, syncable::GET_BY_ID
,
1612 GetOnlyEntryWithName(&rtrans
, rtrans
.root_id(), parent_name
));
1613 ASSERT_TRUE(parent
.good());
1614 EXPECT_TRUE(parent
.Get(syncable::ID
).ServerKnows());
1616 Entry
parent2(&rtrans
, syncable::GET_BY_ID
,
1617 GetOnlyEntryWithName(&rtrans
, parent
.Get(ID
), parent2_name
));
1618 ASSERT_TRUE(parent2
.good());
1619 EXPECT_TRUE(parent2
.Get(syncable::ID
).ServerKnows());
1621 // Id changed on commit, so this should fail.
1622 Entry
local_parent2_id_entry(&rtrans
,
1623 syncable::GET_BY_ID
,
1625 ASSERT_FALSE(local_parent2_id_entry
.good());
1627 Entry
entry_b(&rtrans
, syncable::GET_BY_HANDLE
, meta_handle_b
);
1628 EXPECT_TRUE(entry_b
.Get(syncable::ID
).ServerKnows());
1629 EXPECT_TRUE(parent2
.Get(syncable::ID
) == entry_b
.Get(syncable::PARENT_ID
));
1633 TEST_F(SyncerTest
, UpdateWithZeroLengthName
) {
1634 // One illegal update
1635 mock_server_
->AddUpdateDirectory(
1636 1, 0, std::string(), 1, 10, foreign_cache_guid(), "-1");
1637 // And one legal one that we're going to delete.
1638 mock_server_
->AddUpdateDirectory(2, 0, "FOO", 1, 10,
1639 foreign_cache_guid(), "-2");
1641 // Delete the legal one. The new update has a null name.
1642 mock_server_
->AddUpdateDirectory(
1643 2, 0, std::string(), 2, 20, foreign_cache_guid(), "-2");
1644 mock_server_
->SetLastUpdateDeleted();
1648 TEST_F(SyncerTest
, TestBasicUpdate
) {
1649 string id
= "some_id";
1650 string parent_id
= "0";
1651 string name
= "in_root";
1653 int64 timestamp
= 10;
1654 mock_server_
->AddUpdateDirectory(id
, parent_id
, name
, version
, timestamp
,
1655 foreign_cache_guid(), "-1");
1659 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1660 Entry
entry(&trans
, GET_BY_ID
,
1661 syncable::Id::CreateFromServerId("some_id"));
1662 ASSERT_TRUE(entry
.good());
1663 EXPECT_TRUE(entry
.Get(IS_DIR
));
1664 EXPECT_TRUE(entry
.Get(SERVER_VERSION
) == version
);
1665 EXPECT_TRUE(entry
.Get(BASE_VERSION
) == version
);
1666 EXPECT_FALSE(entry
.Get(IS_UNAPPLIED_UPDATE
));
1667 EXPECT_FALSE(entry
.Get(IS_UNSYNCED
));
1668 EXPECT_FALSE(entry
.Get(SERVER_IS_DEL
));
1669 EXPECT_FALSE(entry
.Get(IS_DEL
));
1673 TEST_F(SyncerTest
, IllegalAndLegalUpdates
) {
1674 Id root
= TestIdFactory::root();
1675 // Should apply just fine.
1676 mock_server_
->AddUpdateDirectory(1, 0, "in_root", 10, 10,
1677 foreign_cache_guid(), "-1");
1679 // Same name. But this SHOULD work.
1680 mock_server_
->AddUpdateDirectory(2, 0, "in_root", 10, 10,
1681 foreign_cache_guid(), "-2");
1683 // Unknown parent: should never be applied. "-80" is a legal server ID,
1684 // because any string sent by the server is a legal server ID in the sync
1685 // protocol, but it's not the ID of any item known to the client. This
1686 // update should succeed validation, but be stuck in the unapplied state
1687 // until an item with the server ID "-80" arrives.
1688 mock_server_
->AddUpdateDirectory(3, -80, "bad_parent", 10, 10,
1689 foreign_cache_guid(), "-3");
1693 // Id 3 should be in conflict now.
1694 EXPECT_EQ(1, status().TotalNumConflictingItems());
1695 EXPECT_EQ(1, status().num_hierarchy_conflicts());
1697 // The only request in that loop should have been a GetUpdate.
1698 // At that point, we didn't know whether or not we had conflicts.
1699 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
1700 VerifyHierarchyConflictsUnspecified(mock_server_
->last_request());
1702 // These entries will be used in the second set of updates.
1703 mock_server_
->AddUpdateDirectory(4, 0, "newer_version", 20, 10,
1704 foreign_cache_guid(), "-4");
1705 mock_server_
->AddUpdateDirectory(5, 0, "circular1", 10, 10,
1706 foreign_cache_guid(), "-5");
1707 mock_server_
->AddUpdateDirectory(6, 5, "circular2", 10, 10,
1708 foreign_cache_guid(), "-6");
1709 mock_server_
->AddUpdateDirectory(9, 3, "bad_parent_child", 10, 10,
1710 foreign_cache_guid(), "-9");
1711 mock_server_
->AddUpdateDirectory(100, 9, "bad_parent_child2", 10, 10,
1712 foreign_cache_guid(), "-100");
1713 mock_server_
->AddUpdateDirectory(10, 0, "dir_to_bookmark", 10, 10,
1714 foreign_cache_guid(), "-10");
1717 // The three items with an unresolved parent should be unapplied (3, 9, 100).
1718 // The name clash should also still be in conflict.
1719 EXPECT_EQ(3, status().TotalNumConflictingItems());
1720 EXPECT_EQ(3, status().num_hierarchy_conflicts());
1722 // This time around, we knew that there were conflicts.
1723 ASSERT_TRUE(mock_server_
->last_request().has_get_updates());
1724 VerifyHierarchyConflictsReported(mock_server_
->last_request());
1727 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1728 // Even though it has the same name, it should work.
1729 Entry
name_clash(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
1730 ASSERT_TRUE(name_clash
.good());
1731 EXPECT_FALSE(name_clash
.Get(IS_UNAPPLIED_UPDATE
))
1732 << "Duplicate name SHOULD be OK.";
1734 Entry
bad_parent(&trans
, GET_BY_ID
, ids_
.FromNumber(3));
1735 ASSERT_TRUE(bad_parent
.good());
1736 EXPECT_TRUE(bad_parent
.Get(IS_UNAPPLIED_UPDATE
))
1737 << "child of unknown parent should be in conflict";
1739 Entry
bad_parent_child(&trans
, GET_BY_ID
, ids_
.FromNumber(9));
1740 ASSERT_TRUE(bad_parent_child
.good());
1741 EXPECT_TRUE(bad_parent_child
.Get(IS_UNAPPLIED_UPDATE
))
1742 << "grandchild of unknown parent should be in conflict";
1744 Entry
bad_parent_child2(&trans
, GET_BY_ID
, ids_
.FromNumber(100));
1745 ASSERT_TRUE(bad_parent_child2
.good());
1746 EXPECT_TRUE(bad_parent_child2
.Get(IS_UNAPPLIED_UPDATE
))
1747 << "great-grandchild of unknown parent should be in conflict";
1750 // Updating 1 should not affect item 2 of the same name.
1751 mock_server_
->AddUpdateDirectory(1, 0, "new_name", 20, 20,
1752 foreign_cache_guid(), "-1");
1754 // Moving 5 under 6 will create a cycle: a conflict.
1755 mock_server_
->AddUpdateDirectory(5, 6, "circular3", 20, 20,
1756 foreign_cache_guid(), "-5");
1758 // Flip the is_dir bit: should fail verify & be dropped.
1759 mock_server_
->AddUpdateBookmark(10, 0, "dir_to_bookmark", 20, 20,
1760 foreign_cache_guid(), "-10");
1763 // Version number older than last known: should fail verify & be dropped.
1764 mock_server_
->AddUpdateDirectory(4, 0, "old_version", 10, 10,
1765 foreign_cache_guid(), "-4");
1768 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1770 Entry
still_a_dir(&trans
, GET_BY_ID
, ids_
.FromNumber(10));
1771 ASSERT_TRUE(still_a_dir
.good());
1772 EXPECT_FALSE(still_a_dir
.Get(IS_UNAPPLIED_UPDATE
));
1773 EXPECT_EQ(10u, still_a_dir
.Get(BASE_VERSION
));
1774 EXPECT_EQ(10u, still_a_dir
.Get(SERVER_VERSION
));
1775 EXPECT_TRUE(still_a_dir
.Get(IS_DIR
));
1777 Entry
rename(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
1778 ASSERT_TRUE(rename
.good());
1779 EXPECT_EQ(root
, rename
.Get(PARENT_ID
));
1780 EXPECT_EQ("new_name", rename
.Get(NON_UNIQUE_NAME
));
1781 EXPECT_FALSE(rename
.Get(IS_UNAPPLIED_UPDATE
));
1782 EXPECT_TRUE(ids_
.FromNumber(1) == rename
.Get(ID
));
1783 EXPECT_EQ(20u, rename
.Get(BASE_VERSION
));
1785 Entry
name_clash(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
1786 ASSERT_TRUE(name_clash
.good());
1787 EXPECT_EQ(root
, name_clash
.Get(PARENT_ID
));
1788 EXPECT_TRUE(ids_
.FromNumber(2) == name_clash
.Get(ID
));
1789 EXPECT_EQ(10u, name_clash
.Get(BASE_VERSION
));
1790 EXPECT_EQ("in_root", name_clash
.Get(NON_UNIQUE_NAME
));
1792 Entry
ignored_old_version(&trans
, GET_BY_ID
, ids_
.FromNumber(4));
1793 ASSERT_TRUE(ignored_old_version
.good());
1795 ignored_old_version
.Get(NON_UNIQUE_NAME
) == "newer_version");
1796 EXPECT_FALSE(ignored_old_version
.Get(IS_UNAPPLIED_UPDATE
));
1797 EXPECT_EQ(20u, ignored_old_version
.Get(BASE_VERSION
));
1799 Entry
circular_parent_issue(&trans
, GET_BY_ID
, ids_
.FromNumber(5));
1800 ASSERT_TRUE(circular_parent_issue
.good());
1801 EXPECT_TRUE(circular_parent_issue
.Get(IS_UNAPPLIED_UPDATE
))
1802 << "circular move should be in conflict";
1803 EXPECT_TRUE(circular_parent_issue
.Get(PARENT_ID
) == root_id_
);
1804 EXPECT_TRUE(circular_parent_issue
.Get(SERVER_PARENT_ID
) ==
1805 ids_
.FromNumber(6));
1806 EXPECT_EQ(10u, circular_parent_issue
.Get(BASE_VERSION
));
1808 Entry
circular_parent_target(&trans
, GET_BY_ID
, ids_
.FromNumber(6));
1809 ASSERT_TRUE(circular_parent_target
.good());
1810 EXPECT_FALSE(circular_parent_target
.Get(IS_UNAPPLIED_UPDATE
));
1811 EXPECT_TRUE(circular_parent_issue
.Get(ID
) ==
1812 circular_parent_target
.Get(PARENT_ID
));
1813 EXPECT_EQ(10u, circular_parent_target
.Get(BASE_VERSION
));
1816 EXPECT_FALSE(saw_syncer_event_
);
1817 EXPECT_EQ(4, status().TotalNumConflictingItems());
1818 EXPECT_EQ(4, status().num_hierarchy_conflicts());
1821 TEST_F(SyncerTest
, CommitTimeRename
) {
1822 int64 metahandle_folder
;
1823 int64 metahandle_new_entry
;
1825 // Create a folder and an entry.
1827 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1828 MutableEntry
parent(&trans
, CREATE
, BOOKMARKS
, root_id_
, "Folder");
1829 ASSERT_TRUE(parent
.good());
1830 parent
.Put(IS_DIR
, true);
1831 parent
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
1832 parent
.Put(IS_UNSYNCED
, true);
1833 metahandle_folder
= parent
.Get(META_HANDLE
);
1835 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, parent
.Get(ID
), "new_entry");
1836 ASSERT_TRUE(entry
.good());
1837 metahandle_new_entry
= entry
.Get(META_HANDLE
);
1838 WriteTestDataToEntry(&trans
, &entry
);
1841 // Mix in a directory creation too for later.
1842 mock_server_
->AddUpdateDirectory(2, 0, "dir_in_root", 10, 10,
1843 foreign_cache_guid(), "-2");
1844 mock_server_
->SetCommitTimeRename("renamed_");
1847 // Verify it was correctly renamed.
1849 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1850 Entry
entry_folder(&trans
, GET_BY_HANDLE
, metahandle_folder
);
1851 ASSERT_TRUE(entry_folder
.good());
1852 EXPECT_EQ("renamed_Folder", entry_folder
.Get(NON_UNIQUE_NAME
));
1854 Entry
entry_new(&trans
, GET_BY_HANDLE
, metahandle_new_entry
);
1855 ASSERT_TRUE(entry_new
.good());
1856 EXPECT_EQ(entry_folder
.Get(ID
), entry_new
.Get(PARENT_ID
));
1857 EXPECT_EQ("renamed_new_entry", entry_new
.Get(NON_UNIQUE_NAME
));
1859 // And that the unrelated directory creation worked without a rename.
1860 Entry
new_dir(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
1861 EXPECT_TRUE(new_dir
.good());
1862 EXPECT_EQ("dir_in_root", new_dir
.Get(NON_UNIQUE_NAME
));
1867 TEST_F(SyncerTest
, CommitTimeRenameI18N
) {
1868 // This is utf-8 for the diacritized Internationalization.
1869 const char* i18nString
= "\xc3\x8e\xc3\xb1\x74\xc3\xa9\x72\xc3\xb1"
1870 "\xc3\xa5\x74\xc3\xae\xc3\xb6\xc3\xb1\xc3\xa5\x6c\xc3\xae"
1871 "\xc2\x9e\xc3\xa5\x74\xc3\xae\xc3\xb6\xc3\xb1";
1874 // Create a folder, expect a commit time rename.
1876 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1877 MutableEntry
parent(&trans
, CREATE
, BOOKMARKS
, root_id_
, "Folder");
1878 ASSERT_TRUE(parent
.good());
1879 parent
.Put(IS_DIR
, true);
1880 parent
.Put(SPECIFICS
, DefaultBookmarkSpecifics());
1881 parent
.Put(IS_UNSYNCED
, true);
1882 metahandle
= parent
.Get(META_HANDLE
);
1885 mock_server_
->SetCommitTimeRename(i18nString
);
1888 // Verify it was correctly renamed.
1890 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1891 string
expected_folder_name(i18nString
);
1892 expected_folder_name
.append("Folder");
1895 Entry
entry_folder(&trans
, GET_BY_HANDLE
, metahandle
);
1896 ASSERT_TRUE(entry_folder
.good());
1897 EXPECT_EQ(expected_folder_name
, entry_folder
.Get(NON_UNIQUE_NAME
));
1901 // A commit with a lost response produces an update that has to be reunited with
1903 TEST_F(SyncerTest
, CommitReuniteUpdateAdjustsChildren
) {
1904 // Create a folder in the root.
1905 int64 metahandle_folder
;
1907 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1909 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_folder");
1910 ASSERT_TRUE(entry
.good());
1911 entry
.Put(IS_DIR
, true);
1912 entry
.Put(SPECIFICS
, DefaultBookmarkSpecifics());
1913 entry
.Put(IS_UNSYNCED
, true);
1914 metahandle_folder
= entry
.Get(META_HANDLE
);
1917 // Verify it and pull the ID out of the folder.
1918 syncable::Id folder_id
;
1919 int64 metahandle_entry
;
1921 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1922 Entry
entry(&trans
, GET_BY_HANDLE
, metahandle_folder
);
1923 ASSERT_TRUE(entry
.good());
1924 folder_id
= entry
.Get(ID
);
1925 ASSERT_TRUE(!folder_id
.ServerKnows());
1928 // Create an entry in the newly created folder.
1930 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1931 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, folder_id
, "new_entry");
1932 ASSERT_TRUE(entry
.good());
1933 metahandle_entry
= entry
.Get(META_HANDLE
);
1934 WriteTestDataToEntry(&trans
, &entry
);
1937 // Verify it and pull the ID out of the entry.
1938 syncable::Id entry_id
;
1940 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1941 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, metahandle_entry
);
1942 ASSERT_TRUE(entry
.good());
1943 EXPECT_EQ(folder_id
, entry
.Get(PARENT_ID
));
1944 EXPECT_EQ("new_entry", entry
.Get(NON_UNIQUE_NAME
));
1945 entry_id
= entry
.Get(ID
);
1946 EXPECT_TRUE(!entry_id
.ServerKnows());
1947 VerifyTestDataInEntry(&trans
, &entry
);
1950 // Now, to emulate a commit response failure, we just don't commit it.
1951 int64 new_version
= 150; // any larger value.
1952 int64 timestamp
= 20; // arbitrary value.
1953 syncable::Id new_folder_id
=
1954 syncable::Id::CreateFromServerId("folder_server_id");
1956 // The following update should cause the folder to both apply the update, as
1957 // well as reassociate the id.
1958 mock_server_
->AddUpdateDirectory(new_folder_id
, root_id_
,
1959 "new_folder", new_version
, timestamp
,
1960 local_cache_guid(), folder_id
.GetServerId());
1962 // We don't want it accidentally committed, just the update applied.
1963 mock_server_
->set_conflict_all_commits(true);
1965 // Alright! Apply that update!
1968 // The folder's ID should have been updated.
1969 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1970 Entry
folder(&trans
, GET_BY_HANDLE
, metahandle_folder
);
1971 ASSERT_TRUE(folder
.good());
1972 EXPECT_EQ("new_folder", folder
.Get(NON_UNIQUE_NAME
));
1973 EXPECT_TRUE(new_version
== folder
.Get(BASE_VERSION
));
1974 EXPECT_TRUE(new_folder_id
== folder
.Get(ID
));
1975 EXPECT_TRUE(folder
.Get(ID
).ServerKnows());
1976 EXPECT_EQ(trans
.root_id(), folder
.Get(PARENT_ID
));
1978 // Since it was updated, the old folder should not exist.
1979 Entry
old_dead_folder(&trans
, GET_BY_ID
, folder_id
);
1980 EXPECT_FALSE(old_dead_folder
.good());
1982 // The child's parent should have changed.
1983 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, metahandle_entry
);
1984 ASSERT_TRUE(entry
.good());
1985 EXPECT_EQ("new_entry", entry
.Get(NON_UNIQUE_NAME
));
1986 EXPECT_EQ(new_folder_id
, entry
.Get(PARENT_ID
));
1987 EXPECT_TRUE(!entry
.Get(ID
).ServerKnows());
1988 VerifyTestDataInEntry(&trans
, &entry
);
1992 // A commit with a lost response produces an update that has to be reunited with
1994 TEST_F(SyncerTest
, CommitReuniteUpdate
) {
1995 // Create an entry in the root.
1996 int64 entry_metahandle
;
1998 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
1999 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_entry");
2000 ASSERT_TRUE(entry
.good());
2001 entry_metahandle
= entry
.Get(META_HANDLE
);
2002 WriteTestDataToEntry(&trans
, &entry
);
2005 // Verify it and pull the ID out.
2006 syncable::Id entry_id
;
2008 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2010 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2011 ASSERT_TRUE(entry
.good());
2012 entry_id
= entry
.Get(ID
);
2013 EXPECT_TRUE(!entry_id
.ServerKnows());
2014 VerifyTestDataInEntry(&trans
, &entry
);
2017 // Now, to emulate a commit response failure, we just don't commit it.
2018 int64 new_version
= 150; // any larger value.
2019 int64 timestamp
= 20; // arbitrary value.
2020 syncable::Id new_entry_id
= syncable::Id::CreateFromServerId("server_id");
2022 // Generate an update from the server with a relevant ID reassignment.
2023 mock_server_
->AddUpdateBookmark(new_entry_id
, root_id_
,
2024 "new_entry", new_version
, timestamp
,
2025 local_cache_guid(), entry_id
.GetServerId());
2027 // We don't want it accidentally committed, just the update applied.
2028 mock_server_
->set_conflict_all_commits(true);
2030 // Alright! Apply that update!
2033 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2034 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2035 ASSERT_TRUE(entry
.good());
2036 EXPECT_TRUE(new_version
== entry
.Get(BASE_VERSION
));
2037 EXPECT_TRUE(new_entry_id
== entry
.Get(ID
));
2038 EXPECT_EQ("new_entry", entry
.Get(NON_UNIQUE_NAME
));
2042 // A commit with a lost response must work even if the local entry was deleted
2043 // before the update is applied. We should not duplicate the local entry in
2044 // this case, but just create another one alongside. We may wish to examine
2045 // this behavior in the future as it can create hanging uploads that never
2046 // finish, that must be cleaned up on the server side after some time.
2047 TEST_F(SyncerTest
, CommitReuniteUpdateDoesNotChokeOnDeletedLocalEntry
) {
2048 // Create a entry in the root.
2049 int64 entry_metahandle
;
2051 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2052 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_entry");
2053 ASSERT_TRUE(entry
.good());
2054 entry_metahandle
= entry
.Get(META_HANDLE
);
2055 WriteTestDataToEntry(&trans
, &entry
);
2057 // Verify it and pull the ID out.
2058 syncable::Id entry_id
;
2060 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2061 Entry
entry(&trans
, GET_BY_HANDLE
, entry_metahandle
);
2062 ASSERT_TRUE(entry
.good());
2063 entry_id
= entry
.Get(ID
);
2064 EXPECT_TRUE(!entry_id
.ServerKnows());
2065 VerifyTestDataInEntry(&trans
, &entry
);
2068 // Now, to emulate a commit response failure, we just don't commit it.
2069 int64 new_version
= 150; // any larger value.
2070 int64 timestamp
= 20; // arbitrary value.
2071 syncable::Id new_entry_id
= syncable::Id::CreateFromServerId("server_id");
2073 // Generate an update from the server with a relevant ID reassignment.
2074 mock_server_
->AddUpdateBookmark(new_entry_id
, root_id_
,
2075 "new_entry", new_version
, timestamp
,
2076 local_cache_guid(), entry_id
.GetServerId());
2078 // We don't want it accidentally committed, just the update applied.
2079 mock_server_
->set_conflict_all_commits(true);
2081 // Purposefully delete the entry now before the update application finishes.
2083 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2084 Id new_entry_id
= GetOnlyEntryWithName(
2085 &trans
, trans
.root_id(), "new_entry");
2086 MutableEntry
entry(&trans
, GET_BY_ID
, new_entry_id
);
2087 ASSERT_TRUE(entry
.good());
2088 entry
.Put(syncable::IS_DEL
, true);
2091 // Just don't CHECK fail in sync, have the update split.
2094 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2095 Id new_entry_id
= GetOnlyEntryWithName(
2096 &trans
, trans
.root_id(), "new_entry");
2097 Entry
entry(&trans
, GET_BY_ID
, new_entry_id
);
2098 ASSERT_TRUE(entry
.good());
2099 EXPECT_FALSE(entry
.Get(IS_DEL
));
2101 Entry
old_entry(&trans
, GET_BY_ID
, entry_id
);
2102 ASSERT_TRUE(old_entry
.good());
2103 EXPECT_TRUE(old_entry
.Get(IS_DEL
));
2107 // TODO(chron): Add more unsanitized name tests.
2108 TEST_F(SyncerTest
, ConflictMatchingEntryHandlesUnsanitizedNames
) {
2109 mock_server_
->AddUpdateDirectory(1, 0, "A/A", 10, 10,
2110 foreign_cache_guid(), "-1");
2111 mock_server_
->AddUpdateDirectory(2, 0, "B/B", 10, 10,
2112 foreign_cache_guid(), "-2");
2113 mock_server_
->set_conflict_all_commits(true);
2116 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2118 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2119 ASSERT_TRUE(A
.good());
2120 A
.Put(IS_UNSYNCED
, true);
2121 A
.Put(IS_UNAPPLIED_UPDATE
, true);
2122 A
.Put(SERVER_VERSION
, 20);
2124 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
2125 ASSERT_TRUE(B
.good());
2126 B
.Put(IS_UNAPPLIED_UPDATE
, true);
2127 B
.Put(SERVER_VERSION
, 20);
2130 saw_syncer_event_
= false;
2131 mock_server_
->set_conflict_all_commits(false);
2134 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2136 Entry
A(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2137 ASSERT_TRUE(A
.good());
2138 EXPECT_TRUE(A
.Get(IS_UNSYNCED
) == false);
2139 EXPECT_TRUE(A
.Get(IS_UNAPPLIED_UPDATE
) == false);
2140 EXPECT_TRUE(A
.Get(SERVER_VERSION
) == 20);
2142 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
2143 ASSERT_TRUE(B
.good());
2144 EXPECT_TRUE(B
.Get(IS_UNSYNCED
) == false);
2145 EXPECT_TRUE(B
.Get(IS_UNAPPLIED_UPDATE
) == false);
2146 EXPECT_TRUE(B
.Get(SERVER_VERSION
) == 20);
2150 TEST_F(SyncerTest
, ConflictMatchingEntryHandlesNormalNames
) {
2151 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
2152 foreign_cache_guid(), "-1");
2153 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
2154 foreign_cache_guid(), "-2");
2155 mock_server_
->set_conflict_all_commits(true);
2158 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2160 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2161 ASSERT_TRUE(A
.good());
2162 A
.Put(IS_UNSYNCED
, true);
2163 A
.Put(IS_UNAPPLIED_UPDATE
, true);
2164 A
.Put(SERVER_VERSION
, 20);
2166 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
2167 ASSERT_TRUE(B
.good());
2168 B
.Put(IS_UNAPPLIED_UPDATE
, true);
2169 B
.Put(SERVER_VERSION
, 20);
2172 saw_syncer_event_
= false;
2173 mock_server_
->set_conflict_all_commits(false);
2176 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2178 Entry
A(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2179 ASSERT_TRUE(A
.good());
2180 EXPECT_TRUE(A
.Get(IS_UNSYNCED
) == false);
2181 EXPECT_TRUE(A
.Get(IS_UNAPPLIED_UPDATE
) == false);
2182 EXPECT_TRUE(A
.Get(SERVER_VERSION
) == 20);
2184 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
2185 ASSERT_TRUE(B
.good());
2186 EXPECT_TRUE(B
.Get(IS_UNSYNCED
) == false);
2187 EXPECT_TRUE(B
.Get(IS_UNAPPLIED_UPDATE
) == false);
2188 EXPECT_TRUE(B
.Get(SERVER_VERSION
) == 20);
2192 TEST_F(SyncerTest
, ReverseFolderOrderingTest
) {
2193 mock_server_
->AddUpdateDirectory(4, 3, "ggchild", 10, 10,
2194 foreign_cache_guid(), "-4");
2195 mock_server_
->AddUpdateDirectory(3, 2, "gchild", 10, 10,
2196 foreign_cache_guid(), "-3");
2197 mock_server_
->AddUpdateDirectory(5, 4, "gggchild", 10, 10,
2198 foreign_cache_guid(), "-5");
2199 mock_server_
->AddUpdateDirectory(2, 1, "child", 10, 10,
2200 foreign_cache_guid(), "-2");
2201 mock_server_
->AddUpdateDirectory(1, 0, "parent", 10, 10,
2202 foreign_cache_guid(), "-1");
2204 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2206 Id child_id
= GetOnlyEntryWithName(
2207 &trans
, ids_
.FromNumber(4), "gggchild");
2208 Entry
child(&trans
, GET_BY_ID
, child_id
);
2209 ASSERT_TRUE(child
.good());
2212 class EntryCreatedInNewFolderTest
: public SyncerTest
{
2214 void CreateFolderInBob() {
2215 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2216 MutableEntry
bob(&trans
,
2217 syncable::GET_BY_ID
,
2218 GetOnlyEntryWithName(&trans
,
2219 TestIdFactory::root(),
2223 MutableEntry
entry2(
2224 &trans
, CREATE
, BOOKMARKS
, bob
.Get(syncable::ID
), "bob");
2225 CHECK(entry2
.good());
2226 entry2
.Put(syncable::IS_DIR
, true);
2227 entry2
.Put(syncable::IS_UNSYNCED
, true);
2228 entry2
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
2232 TEST_F(EntryCreatedInNewFolderTest
, EntryCreatedInNewFolderMidSync
) {
2234 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2235 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2236 ASSERT_TRUE(entry
.good());
2237 entry
.Put(syncable::IS_DIR
, true);
2238 entry
.Put(syncable::IS_UNSYNCED
, true);
2239 entry
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
2242 mock_server_
->SetMidCommitCallback(
2243 base::Bind(&EntryCreatedInNewFolderTest::CreateFolderInBob
,
2244 base::Unretained(this)));
2245 syncer_
->SyncShare(session_
.get(), COMMIT
, SYNCER_END
);
2246 // We loop until no unsynced handles remain, so we will commit both ids.
2247 EXPECT_EQ(2u, mock_server_
->committed_ids().size());
2249 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2250 Entry
parent_entry(&trans
, syncable::GET_BY_ID
,
2251 GetOnlyEntryWithName(&trans
, TestIdFactory::root(), "bob"));
2252 ASSERT_TRUE(parent_entry
.good());
2255 GetOnlyEntryWithName(&trans
, parent_entry
.Get(ID
), "bob");
2256 Entry
child(&trans
, syncable::GET_BY_ID
, child_id
);
2257 ASSERT_TRUE(child
.good());
2258 EXPECT_EQ(parent_entry
.Get(ID
), child
.Get(PARENT_ID
));
2262 TEST_F(SyncerTest
, NegativeIDInUpdate
) {
2263 mock_server_
->AddUpdateBookmark(-10, 0, "bad", 40, 40,
2264 foreign_cache_guid(), "-100");
2266 // The negative id would make us CHECK!
2269 TEST_F(SyncerTest
, UnappliedUpdateOnCreatedItemItemDoesNotCrash
) {
2270 int64 metahandle_fred
;
2271 syncable::Id orig_id
;
2274 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2275 MutableEntry
fred_match(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(),
2277 ASSERT_TRUE(fred_match
.good());
2278 metahandle_fred
= fred_match
.Get(META_HANDLE
);
2279 orig_id
= fred_match
.Get(ID
);
2280 WriteTestDataToEntry(&trans
, &fred_match
);
2284 EXPECT_EQ(1u, mock_server_
->committed_ids().size());
2285 mock_server_
->set_conflict_all_commits(true);
2286 syncable::Id fred_match_id
;
2288 // Now receive a change from outside.
2289 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2290 MutableEntry
fred_match(&trans
, GET_BY_HANDLE
, metahandle_fred
);
2291 ASSERT_TRUE(fred_match
.good());
2292 EXPECT_TRUE(fred_match
.Get(ID
).ServerKnows());
2293 fred_match_id
= fred_match
.Get(ID
);
2294 mock_server_
->AddUpdateBookmark(fred_match_id
, trans
.root_id(),
2295 "fred_match", 40, 40, local_cache_guid(), orig_id
.GetServerId());
2298 for (int i
= 0 ; i
< 30 ; ++i
) {
2304 * In the event that we have a double changed entry, that is changed on both
2305 * the client and the server, the conflict resolver should just drop one of
2306 * them and accept the other.
2309 TEST_F(SyncerTest
, DoublyChangedWithResolver
) {
2310 syncable::Id local_id
;
2312 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2313 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Folder");
2314 ASSERT_TRUE(parent
.good());
2315 parent
.Put(syncable::IS_DIR
, true);
2316 parent
.Put(syncable::ID
, parent_id_
);
2317 parent
.Put(syncable::BASE_VERSION
, 5);
2318 parent
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
2319 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent_id_
, "Pete.htm");
2320 ASSERT_TRUE(child
.good());
2321 local_id
= child
.Get(syncable::ID
);
2322 child
.Put(syncable::ID
, child_id_
);
2323 child
.Put(syncable::BASE_VERSION
, 10);
2324 WriteTestDataToEntry(&wtrans
, &child
);
2326 mock_server_
->AddUpdateBookmark(child_id_
, parent_id_
, "Pete2.htm", 11, 10,
2327 local_cache_guid(), local_id
.GetServerId());
2328 mock_server_
->set_conflict_all_commits(true);
2330 syncable::Directory::ChildHandles children
;
2332 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2333 directory()->GetChildHandlesById(&trans
, parent_id_
, &children
);
2334 // We expect the conflict resolver to preserve the local entry.
2335 Entry
child(&trans
, syncable::GET_BY_ID
, child_id_
);
2336 ASSERT_TRUE(child
.good());
2337 EXPECT_TRUE(child
.Get(syncable::IS_UNSYNCED
));
2338 EXPECT_FALSE(child
.Get(syncable::IS_UNAPPLIED_UPDATE
));
2339 EXPECT_TRUE(child
.Get(SPECIFICS
).has_bookmark());
2340 EXPECT_EQ("Pete.htm", child
.Get(NON_UNIQUE_NAME
));
2341 VerifyTestBookmarkDataInEntry(&child
);
2344 // Only one entry, since we just overwrite one.
2345 EXPECT_EQ(1u, children
.size());
2346 saw_syncer_event_
= false;
2349 // We got this repro case when someone was editing bookmarks while sync was
2350 // occuring. The entry had changed out underneath the user.
2351 TEST_F(SyncerTest
, CommitsUpdateDoesntAlterEntry
) {
2352 const base::Time
& test_time
= ProtoTimeToTime(123456);
2353 syncable::Id local_id
;
2354 int64 entry_metahandle
;
2356 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2357 MutableEntry
entry(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Pete");
2358 ASSERT_TRUE(entry
.good());
2359 EXPECT_FALSE(entry
.Get(ID
).ServerKnows());
2360 local_id
= entry
.Get(ID
);
2361 entry
.Put(syncable::IS_DIR
, true);
2362 entry
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
2363 entry
.Put(syncable::IS_UNSYNCED
, true);
2364 entry
.Put(syncable::MTIME
, test_time
);
2365 entry_metahandle
= entry
.Get(META_HANDLE
);
2371 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2372 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, entry_metahandle
);
2373 ASSERT_TRUE(entry
.good());
2375 EXPECT_TRUE(id
.ServerKnows());
2376 version
= entry
.Get(BASE_VERSION
);
2378 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
2379 update
->set_originator_cache_guid(local_cache_guid());
2380 update
->set_originator_client_item_id(local_id
.GetServerId());
2381 EXPECT_EQ("Pete", update
->name());
2382 EXPECT_EQ(id
.GetServerId(), update
->id_string());
2383 EXPECT_EQ(root_id_
.GetServerId(), update
->parent_id_string());
2384 EXPECT_EQ(version
, update
->version());
2387 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2388 Entry
entry(&trans
, syncable::GET_BY_ID
, id
);
2389 ASSERT_TRUE(entry
.good());
2390 EXPECT_TRUE(entry
.Get(MTIME
) == test_time
);
2394 TEST_F(SyncerTest
, ParentAndChildBothMatch
) {
2395 const FullModelTypeSet all_types
= FullModelTypeSet::All();
2396 syncable::Id parent_id
= ids_
.NewServerId();
2397 syncable::Id child_id
= ids_
.NewServerId();
2398 syncable::Id parent_local_id
;
2399 syncable::Id child_local_id
;
2403 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2404 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "Folder");
2405 ASSERT_TRUE(parent
.good());
2406 parent_local_id
= parent
.Get(ID
);
2407 parent
.Put(IS_DIR
, true);
2408 parent
.Put(IS_UNSYNCED
, true);
2409 parent
.Put(ID
, parent_id
);
2410 parent
.Put(BASE_VERSION
, 1);
2411 parent
.Put(SPECIFICS
, DefaultBookmarkSpecifics());
2413 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent
.Get(ID
), "test.htm");
2414 ASSERT_TRUE(child
.good());
2415 child_local_id
= child
.Get(ID
);
2416 child
.Put(ID
, child_id
);
2417 child
.Put(BASE_VERSION
, 1);
2418 child
.Put(SPECIFICS
, DefaultBookmarkSpecifics());
2419 WriteTestDataToEntry(&wtrans
, &child
);
2421 mock_server_
->AddUpdateDirectory(parent_id
, root_id_
, "Folder", 10, 10,
2423 parent_local_id
.GetServerId());
2424 mock_server_
->AddUpdateBookmark(child_id
, parent_id
, "test.htm", 10, 10,
2426 child_local_id
.GetServerId());
2427 mock_server_
->set_conflict_all_commits(true);
2432 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2433 Directory::ChildHandles children
;
2434 directory()->GetChildHandlesById(&trans
, root_id_
, &children
);
2435 EXPECT_EQ(1u, children
.size());
2436 directory()->GetChildHandlesById(&trans
, parent_id
, &children
);
2437 EXPECT_EQ(1u, children
.size());
2438 std::vector
<int64
> unapplied
;
2439 directory()->GetUnappliedUpdateMetaHandles(&trans
, all_types
, &unapplied
);
2440 EXPECT_EQ(0u, unapplied
.size());
2441 syncable::Directory::UnsyncedMetaHandles unsynced
;
2442 directory()->GetUnsyncedMetaHandles(&trans
, &unsynced
);
2443 EXPECT_EQ(0u, unsynced
.size());
2444 saw_syncer_event_
= false;
2448 TEST_F(SyncerTest
, CommittingNewDeleted
) {
2450 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2451 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2452 entry
.Put(IS_UNSYNCED
, true);
2453 entry
.Put(IS_DEL
, true);
2456 EXPECT_EQ(0u, mock_server_
->committed_ids().size());
2459 // Original problem synopsis:
2460 // Check failed: entry->Get(BASE_VERSION) <= entry->Get(SERVER_VERSION)
2461 // Client creates entry, client finishes committing entry. Between
2462 // commit and getting update back, we delete the entry.
2463 // We get the update for the entry, but the local one was modified
2464 // so we store the entry but don't apply it. IS_UNAPPLIED_UPDATE is set.
2465 // We commit deletion and get a new version number.
2466 // We apply unapplied updates again before we get the update about the deletion.
2467 // This means we have an unapplied update where server_version < base_version.
2468 TEST_F(SyncerTest
, UnappliedUpdateDuringCommit
) {
2469 // This test is a little fake.
2471 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2472 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
2473 entry
.Put(ID
, ids_
.FromNumber(20));
2474 entry
.Put(BASE_VERSION
, 1);
2475 entry
.Put(SERVER_VERSION
, 1);
2476 entry
.Put(SERVER_PARENT_ID
, ids_
.FromNumber(9999)); // Bad parent.
2477 entry
.Put(IS_UNSYNCED
, true);
2478 entry
.Put(IS_UNAPPLIED_UPDATE
, true);
2479 entry
.Put(SPECIFICS
, DefaultBookmarkSpecifics());
2480 entry
.Put(SERVER_SPECIFICS
, DefaultBookmarkSpecifics());
2481 entry
.Put(IS_DEL
, false);
2483 syncer_
->SyncShare(session_
.get(), SYNCER_BEGIN
, SYNCER_END
);
2484 syncer_
->SyncShare(session_
.get(), SYNCER_BEGIN
, SYNCER_END
);
2485 EXPECT_EQ(1, session_
->status_controller().TotalNumConflictingItems());
2486 saw_syncer_event_
= false;
2489 // Original problem synopsis:
2491 // Unexpected error during sync if we:
2492 // make a new folder bob
2494 // make a new folder fred
2495 // move bob into fred
2498 // if no syncing occured midway, bob will have an illegal parent
2499 TEST_F(SyncerTest
, DeletingEntryInFolder
) {
2500 // This test is a little fake.
2501 int64 existing_metahandle
;
2503 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2504 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "existing");
2505 ASSERT_TRUE(entry
.good());
2506 entry
.Put(IS_DIR
, true);
2507 entry
.Put(SPECIFICS
, DefaultBookmarkSpecifics());
2508 entry
.Put(IS_UNSYNCED
, true);
2509 existing_metahandle
= entry
.Get(META_HANDLE
);
2511 syncer_
->SyncShare(session_
.get(), SYNCER_BEGIN
, SYNCER_END
);
2513 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2514 MutableEntry
newfolder(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new");
2515 ASSERT_TRUE(newfolder
.good());
2516 newfolder
.Put(IS_DIR
, true);
2517 newfolder
.Put(SPECIFICS
, DefaultBookmarkSpecifics());
2518 newfolder
.Put(IS_UNSYNCED
, true);
2520 MutableEntry
existing(&trans
, GET_BY_HANDLE
, existing_metahandle
);
2521 ASSERT_TRUE(existing
.good());
2522 existing
.Put(PARENT_ID
, newfolder
.Get(ID
));
2523 existing
.Put(IS_UNSYNCED
, true);
2524 EXPECT_TRUE(existing
.Get(ID
).ServerKnows());
2526 newfolder
.Put(IS_DEL
, true);
2527 existing
.Put(IS_DEL
, true);
2529 syncer_
->SyncShare(session_
.get(), SYNCER_BEGIN
, SYNCER_END
);
2530 EXPECT_EQ(0, status().num_server_conflicts());
2533 TEST_F(SyncerTest
, DeletingEntryWithLocalEdits
) {
2534 int64 newfolder_metahandle
;
2536 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
2537 foreign_cache_guid(), "-1");
2540 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2541 MutableEntry
newfolder(
2542 &trans
, CREATE
, BOOKMARKS
, ids_
.FromNumber(1), "local");
2543 ASSERT_TRUE(newfolder
.good());
2544 newfolder
.Put(IS_UNSYNCED
, true);
2545 newfolder
.Put(IS_DIR
, true);
2546 newfolder
.Put(SPECIFICS
, DefaultBookmarkSpecifics());
2547 newfolder_metahandle
= newfolder
.Get(META_HANDLE
);
2549 mock_server_
->AddUpdateDirectory(1, 0, "bob", 2, 20,
2550 foreign_cache_guid(), "-1");
2551 mock_server_
->SetLastUpdateDeleted();
2552 syncer_
->SyncShare(session_
.get(), SYNCER_BEGIN
, APPLY_UPDATES
);
2554 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2555 Entry
entry(&trans
, syncable::GET_BY_HANDLE
, newfolder_metahandle
);
2556 ASSERT_TRUE(entry
.good());
2560 TEST_F(SyncerTest
, FolderSwapUpdate
) {
2561 mock_server_
->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2562 foreign_cache_guid(), "-7801");
2563 mock_server_
->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2564 foreign_cache_guid(), "-1024");
2566 mock_server_
->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2567 foreign_cache_guid(), "-1024");
2568 mock_server_
->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2569 foreign_cache_guid(), "-7801");
2572 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2573 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2574 ASSERT_TRUE(id1
.good());
2575 EXPECT_TRUE("fred" == id1
.Get(NON_UNIQUE_NAME
));
2576 EXPECT_TRUE(root_id_
== id1
.Get(PARENT_ID
));
2577 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2578 ASSERT_TRUE(id2
.good());
2579 EXPECT_TRUE("bob" == id2
.Get(NON_UNIQUE_NAME
));
2580 EXPECT_TRUE(root_id_
== id2
.Get(PARENT_ID
));
2582 saw_syncer_event_
= false;
2585 TEST_F(SyncerTest
, NameCollidingFolderSwapWorksFine
) {
2586 mock_server_
->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2587 foreign_cache_guid(), "-7801");
2588 mock_server_
->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2589 foreign_cache_guid(), "-1024");
2590 mock_server_
->AddUpdateDirectory(4096, 0, "alice", 1, 10,
2591 foreign_cache_guid(), "-4096");
2594 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2595 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2596 ASSERT_TRUE(id1
.good());
2597 EXPECT_TRUE("bob" == id1
.Get(NON_UNIQUE_NAME
));
2598 EXPECT_TRUE(root_id_
== id1
.Get(PARENT_ID
));
2599 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2600 ASSERT_TRUE(id2
.good());
2601 EXPECT_TRUE("fred" == id2
.Get(NON_UNIQUE_NAME
));
2602 EXPECT_TRUE(root_id_
== id2
.Get(PARENT_ID
));
2603 Entry
id3(&trans
, GET_BY_ID
, ids_
.FromNumber(4096));
2604 ASSERT_TRUE(id3
.good());
2605 EXPECT_TRUE("alice" == id3
.Get(NON_UNIQUE_NAME
));
2606 EXPECT_TRUE(root_id_
== id3
.Get(PARENT_ID
));
2608 mock_server_
->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2609 foreign_cache_guid(), "-1024");
2610 mock_server_
->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2611 foreign_cache_guid(), "-7801");
2612 mock_server_
->AddUpdateDirectory(4096, 0, "bob", 2, 20,
2613 foreign_cache_guid(), "-4096");
2616 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2617 Entry
id1(&trans
, GET_BY_ID
, ids_
.FromNumber(7801));
2618 ASSERT_TRUE(id1
.good());
2619 EXPECT_TRUE("fred" == id1
.Get(NON_UNIQUE_NAME
));
2620 EXPECT_TRUE(root_id_
== id1
.Get(PARENT_ID
));
2621 Entry
id2(&trans
, GET_BY_ID
, ids_
.FromNumber(1024));
2622 ASSERT_TRUE(id2
.good());
2623 EXPECT_TRUE("bob" == id2
.Get(NON_UNIQUE_NAME
));
2624 EXPECT_TRUE(root_id_
== id2
.Get(PARENT_ID
));
2625 Entry
id3(&trans
, GET_BY_ID
, ids_
.FromNumber(4096));
2626 ASSERT_TRUE(id3
.good());
2627 EXPECT_TRUE("bob" == id3
.Get(NON_UNIQUE_NAME
));
2628 EXPECT_TRUE(root_id_
== id3
.Get(PARENT_ID
));
2630 saw_syncer_event_
= false;
2633 // Committing more than kDefaultMaxCommitBatchSize items requires that
2634 // we post more than one commit command to the server. This test makes
2635 // sure that scenario works as expected.
2636 TEST_F(SyncerTest
, CommitManyItemsInOneGo_Success
) {
2637 uint32 num_batches
= 3;
2638 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2640 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2641 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2642 string nameutf8
= base::StringPrintf("%d", i
);
2643 string
name(nameutf8
.begin(), nameutf8
.end());
2644 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2645 e
.Put(IS_UNSYNCED
, true);
2646 e
.Put(IS_DIR
, true);
2647 e
.Put(SPECIFICS
, DefaultBookmarkSpecifics());
2650 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2653 EXPECT_EQ(num_batches
, mock_server_
->commit_messages().size());
2654 EXPECT_EQ(0, directory()->unsynced_entity_count());
2657 // Test that a single failure to contact the server will cause us to exit the
2658 // commit loop immediately.
2659 TEST_F(SyncerTest
, CommitManyItemsInOneGo_PostBufferFail
) {
2660 uint32 num_batches
= 3;
2661 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2663 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2664 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2665 string nameutf8
= base::StringPrintf("%d", i
);
2666 string
name(nameutf8
.begin(), nameutf8
.end());
2667 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2668 e
.Put(IS_UNSYNCED
, true);
2669 e
.Put(IS_DIR
, true);
2670 e
.Put(SPECIFICS
, DefaultBookmarkSpecifics());
2673 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2675 // The second commit should fail. It will be preceded by one successful
2676 // GetUpdate and one succesful commit.
2677 mock_server_
->FailNthPostBufferToPathCall(3);
2680 EXPECT_EQ(1U, mock_server_
->commit_messages().size());
2681 EXPECT_EQ(SYNC_SERVER_ERROR
,
2682 session_
->status_controller().model_neutral_state().commit_result
);
2683 EXPECT_EQ(items_to_commit
- kDefaultMaxCommitBatchSize
,
2684 directory()->unsynced_entity_count());
2687 // Test that a single conflict response from the server will cause us to exit
2688 // the commit loop immediately.
2689 TEST_F(SyncerTest
, CommitManyItemsInOneGo_CommitConflict
) {
2690 uint32 num_batches
= 2;
2691 uint32 items_to_commit
= kDefaultMaxCommitBatchSize
* num_batches
;
2693 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2694 for (uint32 i
= 0; i
< items_to_commit
; i
++) {
2695 string nameutf8
= base::StringPrintf("%d", i
);
2696 string
name(nameutf8
.begin(), nameutf8
.end());
2697 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
2698 e
.Put(IS_UNSYNCED
, true);
2699 e
.Put(IS_DIR
, true);
2700 e
.Put(SPECIFICS
, DefaultBookmarkSpecifics());
2703 ASSERT_EQ(items_to_commit
, directory()->unsynced_entity_count());
2705 // Return a CONFLICT response for the first item.
2706 mock_server_
->set_conflict_n_commits(1);
2709 // We should stop looping at the first sign of trouble.
2710 EXPECT_EQ(1U, mock_server_
->commit_messages().size());
2711 EXPECT_EQ(items_to_commit
- (kDefaultMaxCommitBatchSize
- 1),
2712 directory()->unsynced_entity_count());
2715 TEST_F(SyncerTest
, HugeConflict
) {
2716 int item_count
= 300; // We should be able to do 300 or 3000 w/o issue.
2718 syncable::Id parent_id
= ids_
.NewServerId();
2719 syncable::Id last_id
= parent_id
;
2720 vector
<syncable::Id
> tree_ids
;
2722 // Create a lot of updates for which the parent does not exist yet.
2723 // Generate a huge deep tree which should all fail to apply at first.
2725 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2726 for (int i
= 0; i
< item_count
; i
++) {
2727 syncable::Id next_id
= ids_
.NewServerId();
2728 syncable::Id local_id
= ids_
.NewLocalId();
2729 tree_ids
.push_back(next_id
);
2730 mock_server_
->AddUpdateDirectory(next_id
, last_id
, "BOB", 2, 20,
2731 foreign_cache_guid(),
2732 local_id
.GetServerId());
2738 // Check they're in the expected conflict state.
2740 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2741 for (int i
= 0; i
< item_count
; i
++) {
2742 Entry
e(&trans
, GET_BY_ID
, tree_ids
[i
]);
2743 // They should all exist but none should be applied.
2744 ASSERT_TRUE(e
.good());
2745 EXPECT_TRUE(e
.Get(IS_DEL
));
2746 EXPECT_TRUE(e
.Get(IS_UNAPPLIED_UPDATE
));
2750 // Add the missing parent directory.
2751 mock_server_
->AddUpdateDirectory(parent_id
, TestIdFactory::root(),
2752 "BOB", 2, 20, foreign_cache_guid(), "-3500");
2755 // Now they should all be OK.
2757 syncable::ReadTransaction
trans(FROM_HERE
, directory());
2758 for (int i
= 0; i
< item_count
; i
++) {
2759 Entry
e(&trans
, GET_BY_ID
, tree_ids
[i
]);
2760 ASSERT_TRUE(e
.good());
2761 EXPECT_FALSE(e
.Get(IS_DEL
));
2762 EXPECT_FALSE(e
.Get(IS_UNAPPLIED_UPDATE
));
2767 TEST_F(SyncerTest
, DontCrashOnCaseChange
) {
2768 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
2769 foreign_cache_guid(), "-1");
2772 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2773 MutableEntry
e(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
2774 ASSERT_TRUE(e
.good());
2775 e
.Put(IS_UNSYNCED
, true);
2777 mock_server_
->set_conflict_all_commits(true);
2778 mock_server_
->AddUpdateDirectory(1, 0, "BOB", 2, 20,
2779 foreign_cache_guid(), "-1");
2780 SyncShareNudge(); // USED TO CAUSE AN ASSERT
2781 saw_syncer_event_
= false;
2784 TEST_F(SyncerTest
, UnsyncedItemAndUpdate
) {
2785 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
2786 foreign_cache_guid(), "-1");
2788 mock_server_
->set_conflict_all_commits(true);
2789 mock_server_
->AddUpdateDirectory(2, 0, "bob", 2, 20,
2790 foreign_cache_guid(), "-2");
2791 SyncShareNudge(); // USED TO CAUSE AN ASSERT
2792 saw_syncer_event_
= false;
2795 TEST_F(SyncerTest
, NewEntryAndAlteredServerEntrySharePath
) {
2796 mock_server_
->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
2797 foreign_cache_guid(), "-1");
2799 int64 local_folder_handle
;
2800 syncable::Id local_folder_id
;
2802 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2803 MutableEntry
new_entry(
2804 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bar.htm");
2805 ASSERT_TRUE(new_entry
.good());
2806 local_folder_id
= new_entry
.Get(ID
);
2807 local_folder_handle
= new_entry
.Get(META_HANDLE
);
2808 new_entry
.Put(IS_UNSYNCED
, true);
2809 new_entry
.Put(SPECIFICS
, DefaultBookmarkSpecifics());
2810 MutableEntry
old(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2811 ASSERT_TRUE(old
.good());
2812 WriteTestDataToEntry(&wtrans
, &old
);
2814 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
2815 foreign_cache_guid(), "-1");
2816 mock_server_
->set_conflict_all_commits(true);
2818 saw_syncer_event_
= false;
2820 // Update #20 should have been dropped in favor of the local version.
2821 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2822 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2823 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
2824 ASSERT_TRUE(server
.good());
2825 ASSERT_TRUE(local
.good());
2826 EXPECT_TRUE(local
.Get(META_HANDLE
) != server
.Get(META_HANDLE
));
2827 EXPECT_FALSE(server
.Get(IS_UNAPPLIED_UPDATE
));
2828 EXPECT_FALSE(local
.Get(IS_UNAPPLIED_UPDATE
));
2829 EXPECT_TRUE(server
.Get(IS_UNSYNCED
));
2830 EXPECT_TRUE(local
.Get(IS_UNSYNCED
));
2831 EXPECT_EQ("Foo.htm", server
.Get(NON_UNIQUE_NAME
));
2832 EXPECT_EQ("Bar.htm", local
.Get(NON_UNIQUE_NAME
));
2834 // Allow local changes to commit.
2835 mock_server_
->set_conflict_all_commits(false);
2837 saw_syncer_event_
= false;
2839 // Now add a server change to make the two names equal. There should
2840 // be no conflict with that, since names are not unique.
2841 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
2842 foreign_cache_guid(), "-1");
2844 saw_syncer_event_
= false;
2846 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2847 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2848 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
2849 ASSERT_TRUE(server
.good());
2850 ASSERT_TRUE(local
.good());
2851 EXPECT_TRUE(local
.Get(META_HANDLE
) != server
.Get(META_HANDLE
));
2852 EXPECT_FALSE(server
.Get(IS_UNAPPLIED_UPDATE
));
2853 EXPECT_FALSE(local
.Get(IS_UNAPPLIED_UPDATE
));
2854 EXPECT_FALSE(server
.Get(IS_UNSYNCED
));
2855 EXPECT_FALSE(local
.Get(IS_UNSYNCED
));
2856 EXPECT_EQ("Bar.htm", server
.Get(NON_UNIQUE_NAME
));
2857 EXPECT_EQ("Bar.htm", local
.Get(NON_UNIQUE_NAME
));
2858 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
2859 server
.Get(SPECIFICS
).bookmark().url());
2863 // Same as NewEntryAnddServerEntrySharePath, but using the old-style protocol.
2864 TEST_F(SyncerTest
, NewEntryAndAlteredServerEntrySharePath_OldBookmarksProto
) {
2865 mock_server_
->set_use_legacy_bookmarks_protocol(true);
2866 mock_server_
->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
2867 foreign_cache_guid(), "-1");
2869 int64 local_folder_handle
;
2870 syncable::Id local_folder_id
;
2872 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2873 MutableEntry
new_entry(
2874 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "Bar.htm");
2875 ASSERT_TRUE(new_entry
.good());
2876 local_folder_id
= new_entry
.Get(ID
);
2877 local_folder_handle
= new_entry
.Get(META_HANDLE
);
2878 new_entry
.Put(IS_UNSYNCED
, true);
2879 new_entry
.Put(SPECIFICS
, DefaultBookmarkSpecifics());
2880 MutableEntry
old(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2881 ASSERT_TRUE(old
.good());
2882 WriteTestDataToEntry(&wtrans
, &old
);
2884 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
2885 foreign_cache_guid(), "-1");
2886 mock_server_
->set_conflict_all_commits(true);
2888 saw_syncer_event_
= false;
2890 // Update #20 should have been dropped in favor of the local version.
2891 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2892 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2893 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
2894 ASSERT_TRUE(server
.good());
2895 ASSERT_TRUE(local
.good());
2896 EXPECT_TRUE(local
.Get(META_HANDLE
) != server
.Get(META_HANDLE
));
2897 EXPECT_FALSE(server
.Get(IS_UNAPPLIED_UPDATE
));
2898 EXPECT_FALSE(local
.Get(IS_UNAPPLIED_UPDATE
));
2899 EXPECT_TRUE(server
.Get(IS_UNSYNCED
));
2900 EXPECT_TRUE(local
.Get(IS_UNSYNCED
));
2901 EXPECT_EQ("Foo.htm", server
.Get(NON_UNIQUE_NAME
));
2902 EXPECT_EQ("Bar.htm", local
.Get(NON_UNIQUE_NAME
));
2904 // Allow local changes to commit.
2905 mock_server_
->set_conflict_all_commits(false);
2907 saw_syncer_event_
= false;
2909 // Now add a server change to make the two names equal. There should
2910 // be no conflict with that, since names are not unique.
2911 mock_server_
->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
2912 foreign_cache_guid(), "-1");
2914 saw_syncer_event_
= false;
2916 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2917 MutableEntry
server(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2918 MutableEntry
local(&wtrans
, GET_BY_HANDLE
, local_folder_handle
);
2919 ASSERT_TRUE(server
.good());
2920 ASSERT_TRUE(local
.good());
2921 EXPECT_TRUE(local
.Get(META_HANDLE
) != server
.Get(META_HANDLE
));
2922 EXPECT_FALSE(server
.Get(IS_UNAPPLIED_UPDATE
));
2923 EXPECT_FALSE(local
.Get(IS_UNAPPLIED_UPDATE
));
2924 EXPECT_FALSE(server
.Get(IS_UNSYNCED
));
2925 EXPECT_FALSE(local
.Get(IS_UNSYNCED
));
2926 EXPECT_EQ("Bar.htm", server
.Get(NON_UNIQUE_NAME
));
2927 EXPECT_EQ("Bar.htm", local
.Get(NON_UNIQUE_NAME
));
2928 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
2929 server
.Get(SPECIFICS
).bookmark().url());
2933 // Circular links should be resolved by the server.
2934 TEST_F(SyncerTest
, SiblingDirectoriesBecomeCircular
) {
2935 // we don't currently resolve this. This test ensures we don't.
2936 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
2937 foreign_cache_guid(), "-1");
2938 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
2939 foreign_cache_guid(), "-2");
2942 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2943 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2944 ASSERT_TRUE(A
.good());
2945 A
.Put(IS_UNSYNCED
, true);
2946 ASSERT_TRUE(A
.Put(PARENT_ID
, ids_
.FromNumber(2)));
2947 ASSERT_TRUE(A
.Put(NON_UNIQUE_NAME
, "B"));
2949 mock_server_
->AddUpdateDirectory(2, 1, "A", 20, 20,
2950 foreign_cache_guid(), "-2");
2951 mock_server_
->set_conflict_all_commits(true);
2953 saw_syncer_event_
= false;
2955 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2956 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2957 ASSERT_TRUE(A
.good());
2958 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
2959 ASSERT_TRUE(B
.good());
2960 EXPECT_TRUE(A
.Get(NON_UNIQUE_NAME
) == "B");
2961 EXPECT_TRUE(B
.Get(NON_UNIQUE_NAME
) == "B");
2965 TEST_F(SyncerTest
, SwapEntryNames
) {
2966 // Simple transaction test.
2967 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
2968 foreign_cache_guid(), "-1");
2969 mock_server_
->AddUpdateDirectory(2, 0, "B", 10, 10,
2970 foreign_cache_guid(), "-2");
2971 mock_server_
->set_conflict_all_commits(true);
2974 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
2975 MutableEntry
A(&wtrans
, GET_BY_ID
, ids_
.FromNumber(1));
2976 ASSERT_TRUE(A
.good());
2977 A
.Put(IS_UNSYNCED
, true);
2978 MutableEntry
B(&wtrans
, GET_BY_ID
, ids_
.FromNumber(2));
2979 ASSERT_TRUE(B
.good());
2980 B
.Put(IS_UNSYNCED
, true);
2981 ASSERT_TRUE(A
.Put(NON_UNIQUE_NAME
, "C"));
2982 ASSERT_TRUE(B
.Put(NON_UNIQUE_NAME
, "A"));
2983 ASSERT_TRUE(A
.Put(NON_UNIQUE_NAME
, "B"));
2986 saw_syncer_event_
= false;
2989 TEST_F(SyncerTest
, DualDeletionWithNewItemNameClash
) {
2990 mock_server_
->AddUpdateDirectory(1, 0, "A", 10, 10,
2991 foreign_cache_guid(), "-1");
2992 mock_server_
->AddUpdateBookmark(2, 0, "B", 10, 10,
2993 foreign_cache_guid(), "-2");
2994 mock_server_
->set_conflict_all_commits(true);
2997 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
2998 MutableEntry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
2999 ASSERT_TRUE(B
.good());
3000 WriteTestDataToEntry(&trans
, &B
);
3001 B
.Put(IS_DEL
, true);
3003 mock_server_
->AddUpdateBookmark(2, 0, "A", 11, 11,
3004 foreign_cache_guid(), "-2");
3005 mock_server_
->SetLastUpdateDeleted();
3008 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3009 Entry
B(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3010 ASSERT_TRUE(B
.good());
3011 EXPECT_FALSE(B
.Get(IS_UNSYNCED
));
3012 EXPECT_FALSE(B
.Get(IS_UNAPPLIED_UPDATE
));
3014 saw_syncer_event_
= false;
3017 // When we undelete an entity as a result of conflict resolution, we reuse the
3018 // existing server id and preserve the old version, simply updating the server
3019 // version with the new non-deleted entity.
3020 TEST_F(SyncerTest
, ResolveWeWroteTheyDeleted
) {
3021 int64 bob_metahandle
;
3023 mock_server_
->AddUpdateBookmark(1, 0, "bob", 1, 10,
3024 foreign_cache_guid(), "-1");
3027 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3028 MutableEntry
bob(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3029 ASSERT_TRUE(bob
.good());
3030 bob_metahandle
= bob
.Get(META_HANDLE
);
3031 WriteTestDataToEntry(&trans
, &bob
);
3033 mock_server_
->AddUpdateBookmark(1, 0, "bob", 2, 10,
3034 foreign_cache_guid(), "-1");
3035 mock_server_
->SetLastUpdateDeleted();
3036 mock_server_
->set_conflict_all_commits(true);
3040 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3041 Entry
bob(&trans
, GET_BY_HANDLE
, bob_metahandle
);
3042 ASSERT_TRUE(bob
.good());
3043 EXPECT_TRUE(bob
.Get(IS_UNSYNCED
));
3044 EXPECT_TRUE(bob
.Get(ID
).ServerKnows());
3045 EXPECT_FALSE(bob
.Get(IS_UNAPPLIED_UPDATE
));
3046 EXPECT_FALSE(bob
.Get(IS_DEL
));
3047 EXPECT_EQ(2, bob
.Get(SERVER_VERSION
));
3048 EXPECT_EQ(2, bob
.Get(BASE_VERSION
));
3050 saw_syncer_event_
= false;
3053 // This test is to reproduce a check failure. Sometimes we would get a bad ID
3054 // back when creating an entry.
3055 TEST_F(SyncerTest
, DuplicateIDReturn
) {
3057 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3058 MutableEntry
folder(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bob");
3059 ASSERT_TRUE(folder
.good());
3060 folder
.Put(IS_UNSYNCED
, true);
3061 folder
.Put(IS_DIR
, true);
3062 folder
.Put(SPECIFICS
, DefaultBookmarkSpecifics());
3063 MutableEntry
folder2(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "fred");
3064 ASSERT_TRUE(folder2
.good());
3065 folder2
.Put(IS_UNSYNCED
, false);
3066 folder2
.Put(IS_DIR
, true);
3067 folder2
.Put(SPECIFICS
, DefaultBookmarkSpecifics());
3068 folder2
.Put(BASE_VERSION
, 3);
3069 folder2
.Put(ID
, syncable::Id::CreateFromServerId("mock_server:10000"));
3071 mock_server_
->set_next_new_id(10000);
3072 EXPECT_EQ(1u, directory()->unsynced_entity_count());
3073 // we get back a bad id in here (should never happen).
3075 EXPECT_EQ(1u, directory()->unsynced_entity_count());
3076 SyncShareNudge(); // another bad id in here.
3077 EXPECT_EQ(0u, directory()->unsynced_entity_count());
3078 saw_syncer_event_
= false;
3081 TEST_F(SyncerTest
, DeletedEntryWithBadParentInLoopCalculation
) {
3082 mock_server_
->AddUpdateDirectory(1, 0, "bob", 1, 10,
3083 foreign_cache_guid(), "-1");
3086 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3087 MutableEntry
bob(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3088 ASSERT_TRUE(bob
.good());
3089 // This is valid, because the parent could have gone away a long time ago.
3090 bob
.Put(PARENT_ID
, ids_
.FromNumber(54));
3091 bob
.Put(IS_DEL
, true);
3092 bob
.Put(IS_UNSYNCED
, true);
3094 mock_server_
->AddUpdateDirectory(2, 1, "fred", 1, 10,
3095 foreign_cache_guid(), "-2");
3100 TEST_F(SyncerTest
, ConflictResolverMergesLocalDeleteAndServerUpdate
) {
3101 syncable::Id local_id
;
3103 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3105 MutableEntry
local_deleted(
3106 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "name");
3107 local_id
= local_deleted
.Get(ID
);
3108 local_deleted
.Put(ID
, ids_
.FromNumber(1));
3109 local_deleted
.Put(BASE_VERSION
, 1);
3110 local_deleted
.Put(IS_DEL
, true);
3111 local_deleted
.Put(IS_DIR
, false);
3112 local_deleted
.Put(IS_UNSYNCED
, true);
3113 local_deleted
.Put(SPECIFICS
, DefaultBookmarkSpecifics());
3116 mock_server_
->AddUpdateBookmark(ids_
.FromNumber(1), root_id_
, "name", 10, 10,
3118 local_id
.GetServerId());
3120 // We don't care about actually committing, just the resolution.
3121 mock_server_
->set_conflict_all_commits(true);
3125 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3126 Entry
local_deleted(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3127 EXPECT_TRUE(local_deleted
.Get(BASE_VERSION
) == 10);
3128 EXPECT_TRUE(local_deleted
.Get(IS_UNAPPLIED_UPDATE
) == false);
3129 EXPECT_TRUE(local_deleted
.Get(IS_UNSYNCED
) == true);
3130 EXPECT_TRUE(local_deleted
.Get(IS_DEL
) == true);
3131 EXPECT_TRUE(local_deleted
.Get(IS_DIR
) == false);
3135 // See what happens if the IS_DIR bit gets flipped. This can cause us
3136 // all kinds of disasters.
3137 TEST_F(SyncerTest
, UpdateFlipsTheFolderBit
) {
3138 // Local object: a deleted directory (container), revision 1, unsynced.
3140 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3142 MutableEntry
local_deleted(
3143 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "name");
3144 local_deleted
.Put(ID
, ids_
.FromNumber(1));
3145 local_deleted
.Put(BASE_VERSION
, 1);
3146 local_deleted
.Put(IS_DEL
, true);
3147 local_deleted
.Put(IS_DIR
, true);
3148 local_deleted
.Put(IS_UNSYNCED
, true);
3149 local_deleted
.Put(SPECIFICS
, DefaultBookmarkSpecifics());
3152 // Server update: entry-type object (not a container), revision 10.
3153 mock_server_
->AddUpdateBookmark(ids_
.FromNumber(1), root_id_
, "name", 10, 10,
3155 ids_
.FromNumber(1).GetServerId());
3157 // Don't attempt to commit.
3158 mock_server_
->set_conflict_all_commits(true);
3160 // The syncer should not attempt to apply the invalid update.
3164 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3165 Entry
local_deleted(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3166 EXPECT_TRUE(local_deleted
.Get(BASE_VERSION
) == 1);
3167 EXPECT_TRUE(local_deleted
.Get(IS_UNAPPLIED_UPDATE
) == false);
3168 EXPECT_TRUE(local_deleted
.Get(IS_UNSYNCED
) == true);
3169 EXPECT_TRUE(local_deleted
.Get(IS_DEL
) == true);
3170 EXPECT_TRUE(local_deleted
.Get(IS_DIR
) == true);
3175 // Merge conflict resolution will merge a new local entry with another entry
3176 // that needs updates, resulting in CHECK.
3177 TEST_F(SyncerTest
, MergingExistingItems
) {
3178 mock_server_
->set_conflict_all_commits(true);
3179 mock_server_
->AddUpdateBookmark(1, 0, "base", 10, 10,
3180 local_cache_guid(), "-1");
3183 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3185 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "Copy of base");
3186 WriteTestDataToEntry(&trans
, &entry
);
3188 mock_server_
->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
3189 local_cache_guid(), "-1");
3190 SyncRepeatedlyToTriggerConflictResolution(session_
.get());
3193 // In this test a long changelog contains a child at the start of the changelog
3194 // and a parent at the end. While these updates are in progress the client would
3196 TEST_F(SyncerTest
, LongChangelistWithApplicationConflict
) {
3197 const int depth
= 400;
3198 syncable::Id folder_id
= ids_
.FromNumber(1);
3200 // First we an item in a folder in the root. However the folder won't come
3202 syncable::Id stuck_entry_id
= TestIdFactory::FromNumber(99999);
3203 mock_server_
->AddUpdateDirectory(stuck_entry_id
,
3204 folder_id
, "stuck", 1, 1,
3205 foreign_cache_guid(), "-99999");
3206 mock_server_
->SetChangesRemaining(depth
- 1);
3207 syncer_
->SyncShare(session_
.get(), SYNCER_BEGIN
, SYNCER_END
);
3209 // Buffer up a very long series of downloads.
3210 // We should never be stuck (conflict resolution shouldn't
3211 // kick in so long as we're making forward progress).
3212 for (int i
= 0; i
< depth
; i
++) {
3213 mock_server_
->NextUpdateBatch();
3214 mock_server_
->SetNewTimestamp(i
+ 1);
3215 mock_server_
->SetChangesRemaining(depth
- i
);
3218 syncer_
->SyncShare(session_
.get(), SYNCER_BEGIN
, SYNCER_END
);
3220 // Ensure our folder hasn't somehow applied.
3222 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3223 Entry
child(&trans
, GET_BY_ID
, stuck_entry_id
);
3224 EXPECT_TRUE(child
.good());
3225 EXPECT_TRUE(child
.Get(IS_UNAPPLIED_UPDATE
));
3226 EXPECT_TRUE(child
.Get(IS_DEL
));
3227 EXPECT_FALSE(child
.Get(IS_UNSYNCED
));
3230 // And finally the folder.
3231 mock_server_
->AddUpdateDirectory(folder_id
,
3232 TestIdFactory::root(), "folder", 1, 1,
3233 foreign_cache_guid(), "-1");
3234 mock_server_
->SetChangesRemaining(0);
3237 // Check that everything is as expected after the commit.
3239 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3240 Entry
entry(&trans
, GET_BY_ID
, folder_id
);
3241 ASSERT_TRUE(entry
.good());
3242 Entry
child(&trans
, GET_BY_ID
, stuck_entry_id
);
3243 EXPECT_EQ(entry
.Get(ID
), child
.Get(PARENT_ID
));
3244 EXPECT_EQ("stuck", child
.Get(NON_UNIQUE_NAME
));
3245 EXPECT_TRUE(child
.good());
3249 TEST_F(SyncerTest
, DontMergeTwoExistingItems
) {
3250 mock_server_
->set_conflict_all_commits(true);
3251 mock_server_
->AddUpdateBookmark(1, 0, "base", 10, 10,
3252 foreign_cache_guid(), "-1");
3253 mock_server_
->AddUpdateBookmark(2, 0, "base2", 10, 10,
3254 foreign_cache_guid(), "-2");
3257 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3258 MutableEntry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3259 ASSERT_TRUE(entry
.good());
3260 EXPECT_TRUE(entry
.Put(NON_UNIQUE_NAME
, "Copy of base"));
3261 entry
.Put(IS_UNSYNCED
, true);
3263 mock_server_
->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
3264 foreign_cache_guid(), "-1");
3265 SyncRepeatedlyToTriggerConflictResolution(session_
.get());
3267 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3268 Entry
entry1(&trans
, GET_BY_ID
, ids_
.FromNumber(1));
3269 EXPECT_FALSE(entry1
.Get(IS_UNAPPLIED_UPDATE
));
3270 EXPECT_FALSE(entry1
.Get(IS_UNSYNCED
));
3271 EXPECT_FALSE(entry1
.Get(IS_DEL
));
3272 Entry
entry2(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3273 EXPECT_FALSE(entry2
.Get(IS_UNAPPLIED_UPDATE
));
3274 EXPECT_TRUE(entry2
.Get(IS_UNSYNCED
));
3275 EXPECT_FALSE(entry2
.Get(IS_DEL
));
3276 EXPECT_EQ(entry1
.Get(NON_UNIQUE_NAME
), entry2
.Get(NON_UNIQUE_NAME
));
3280 TEST_F(SyncerTest
, TestUndeleteUpdate
) {
3281 mock_server_
->set_conflict_all_commits(true);
3282 mock_server_
->AddUpdateDirectory(1, 0, "foo", 1, 1,
3283 foreign_cache_guid(), "-1");
3284 mock_server_
->AddUpdateDirectory(2, 1, "bar", 1, 2,
3285 foreign_cache_guid(), "-2");
3287 mock_server_
->AddUpdateDirectory(2, 1, "bar", 2, 3,
3288 foreign_cache_guid(), "-2");
3289 mock_server_
->SetLastUpdateDeleted();
3294 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3295 Entry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3296 ASSERT_TRUE(entry
.good());
3297 EXPECT_TRUE(entry
.Get(IS_DEL
));
3298 metahandle
= entry
.Get(META_HANDLE
);
3300 mock_server_
->AddUpdateDirectory(1, 0, "foo", 2, 4,
3301 foreign_cache_guid(), "-1");
3302 mock_server_
->SetLastUpdateDeleted();
3304 // This used to be rejected as it's an undeletion. Now, it results in moving
3305 // the delete path aside.
3306 mock_server_
->AddUpdateDirectory(2, 1, "bar", 3, 5,
3307 foreign_cache_guid(), "-2");
3310 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3311 Entry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3312 ASSERT_TRUE(entry
.good());
3313 EXPECT_TRUE(entry
.Get(IS_DEL
));
3314 EXPECT_FALSE(entry
.Get(SERVER_IS_DEL
));
3315 EXPECT_TRUE(entry
.Get(IS_UNAPPLIED_UPDATE
));
3316 EXPECT_NE(entry
.Get(META_HANDLE
), metahandle
);
3320 TEST_F(SyncerTest
, TestMoveSanitizedNamedFolder
) {
3321 mock_server_
->AddUpdateDirectory(1, 0, "foo", 1, 1,
3322 foreign_cache_guid(), "-1");
3323 mock_server_
->AddUpdateDirectory(2, 0, ":::", 1, 2,
3324 foreign_cache_guid(), "-2");
3327 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3328 MutableEntry
entry(&trans
, GET_BY_ID
, ids_
.FromNumber(2));
3329 ASSERT_TRUE(entry
.good());
3330 EXPECT_TRUE(entry
.Put(PARENT_ID
, ids_
.FromNumber(1)));
3331 EXPECT_TRUE(entry
.Put(IS_UNSYNCED
, true));
3334 // We use the same sync ts as before so our times match up.
3335 mock_server_
->AddUpdateDirectory(2, 1, ":::", 2, 2,
3336 foreign_cache_guid(), "-2");
3340 // Don't crash when this occurs.
3341 TEST_F(SyncerTest
, UpdateWhereParentIsNotAFolder
) {
3342 mock_server_
->AddUpdateBookmark(1, 0, "B", 10, 10,
3343 foreign_cache_guid(), "-1");
3344 mock_server_
->AddUpdateDirectory(2, 1, "BookmarkParent", 10, 10,
3345 foreign_cache_guid(), "-2");
3346 // Used to cause a CHECK
3349 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
3350 Entry
good_entry(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(1));
3351 ASSERT_TRUE(good_entry
.good());
3352 EXPECT_FALSE(good_entry
.Get(IS_UNAPPLIED_UPDATE
));
3353 Entry
bad_parent(&rtrans
, syncable::GET_BY_ID
, ids_
.FromNumber(2));
3354 ASSERT_TRUE(bad_parent
.good());
3355 EXPECT_TRUE(bad_parent
.Get(IS_UNAPPLIED_UPDATE
));
3359 const char kRootId
[] = "0";
3361 TEST_F(SyncerTest
, DirectoryUpdateTest
) {
3362 Id in_root_id
= ids_
.NewServerId();
3363 Id in_in_root_id
= ids_
.NewServerId();
3365 mock_server_
->AddUpdateDirectory(in_root_id
, TestIdFactory::root(),
3366 "in_root_name", 2, 2,
3367 foreign_cache_guid(), "-1");
3368 mock_server_
->AddUpdateDirectory(in_in_root_id
, in_root_id
,
3369 "in_in_root_name", 3, 3,
3370 foreign_cache_guid(), "-2");
3373 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3374 Entry
in_root(&trans
, GET_BY_ID
, in_root_id
);
3375 ASSERT_TRUE(in_root
.good());
3376 EXPECT_EQ("in_root_name", in_root
.Get(NON_UNIQUE_NAME
));
3377 EXPECT_EQ(TestIdFactory::root(), in_root
.Get(PARENT_ID
));
3379 Entry
in_in_root(&trans
, GET_BY_ID
, in_in_root_id
);
3380 ASSERT_TRUE(in_in_root
.good());
3381 EXPECT_EQ("in_in_root_name", in_in_root
.Get(NON_UNIQUE_NAME
));
3382 EXPECT_EQ(in_root_id
, in_in_root
.Get(PARENT_ID
));
3386 TEST_F(SyncerTest
, DirectoryCommitTest
) {
3387 syncable::Id in_root_id
, in_dir_id
;
3388 int64 foo_metahandle
;
3389 int64 bar_metahandle
;
3392 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3393 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root_id_
, "foo");
3394 ASSERT_TRUE(parent
.good());
3395 parent
.Put(syncable::IS_UNSYNCED
, true);
3396 parent
.Put(syncable::IS_DIR
, true);
3397 parent
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
3398 in_root_id
= parent
.Get(syncable::ID
);
3399 foo_metahandle
= parent
.Get(META_HANDLE
);
3401 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent
.Get(ID
), "bar");
3402 ASSERT_TRUE(child
.good());
3403 child
.Put(syncable::IS_UNSYNCED
, true);
3404 child
.Put(syncable::IS_DIR
, true);
3405 child
.Put(syncable::SPECIFICS
, DefaultBookmarkSpecifics());
3406 bar_metahandle
= child
.Get(META_HANDLE
);
3407 in_dir_id
= parent
.Get(syncable::ID
);
3411 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3412 Entry
fail_by_old_id_entry(&trans
, GET_BY_ID
, in_root_id
);
3413 ASSERT_FALSE(fail_by_old_id_entry
.good());
3415 Entry
foo_entry(&trans
, GET_BY_HANDLE
, foo_metahandle
);
3416 ASSERT_TRUE(foo_entry
.good());
3417 EXPECT_EQ("foo", foo_entry
.Get(NON_UNIQUE_NAME
));
3418 EXPECT_NE(foo_entry
.Get(syncable::ID
), in_root_id
);
3420 Entry
bar_entry(&trans
, GET_BY_HANDLE
, bar_metahandle
);
3421 ASSERT_TRUE(bar_entry
.good());
3422 EXPECT_EQ("bar", bar_entry
.Get(NON_UNIQUE_NAME
));
3423 EXPECT_NE(bar_entry
.Get(syncable::ID
), in_dir_id
);
3424 EXPECT_EQ(foo_entry
.Get(syncable::ID
), bar_entry
.Get(PARENT_ID
));
3428 TEST_F(SyncerTest
, TestClientCommandDuringUpdate
) {
3429 using sync_pb::ClientCommand
;
3431 ClientCommand
* command
= new ClientCommand();
3432 command
->set_set_sync_poll_interval(8);
3433 command
->set_set_sync_long_poll_interval(800);
3434 command
->set_sessions_commit_delay_seconds(3141);
3435 mock_server_
->AddUpdateDirectory(1, 0, "in_root", 1, 1,
3436 foreign_cache_guid(), "-1");
3437 mock_server_
->SetGUClientCommand(command
);
3440 EXPECT_TRUE(TimeDelta::FromSeconds(8) ==
3441 last_short_poll_interval_received_
);
3442 EXPECT_TRUE(TimeDelta::FromSeconds(800) ==
3443 last_long_poll_interval_received_
);
3444 EXPECT_TRUE(TimeDelta::FromSeconds(3141) ==
3445 last_sessions_commit_delay_seconds_
);
3447 command
= new ClientCommand();
3448 command
->set_set_sync_poll_interval(180);
3449 command
->set_set_sync_long_poll_interval(190);
3450 command
->set_sessions_commit_delay_seconds(2718);
3451 mock_server_
->AddUpdateDirectory(1, 0, "in_root", 1, 1,
3452 foreign_cache_guid(), "-1");
3453 mock_server_
->SetGUClientCommand(command
);
3456 EXPECT_TRUE(TimeDelta::FromSeconds(180) ==
3457 last_short_poll_interval_received_
);
3458 EXPECT_TRUE(TimeDelta::FromSeconds(190) ==
3459 last_long_poll_interval_received_
);
3460 EXPECT_TRUE(TimeDelta::FromSeconds(2718) ==
3461 last_sessions_commit_delay_seconds_
);
3464 TEST_F(SyncerTest
, TestClientCommandDuringCommit
) {
3465 using sync_pb::ClientCommand
;
3467 ClientCommand
* command
= new ClientCommand();
3468 command
->set_set_sync_poll_interval(8);
3469 command
->set_set_sync_long_poll_interval(800);
3470 command
->set_sessions_commit_delay_seconds(3141);
3471 CreateUnsyncedDirectory("X", "id_X");
3472 mock_server_
->SetCommitClientCommand(command
);
3475 EXPECT_TRUE(TimeDelta::FromSeconds(8) ==
3476 last_short_poll_interval_received_
);
3477 EXPECT_TRUE(TimeDelta::FromSeconds(800) ==
3478 last_long_poll_interval_received_
);
3479 EXPECT_TRUE(TimeDelta::FromSeconds(3141) ==
3480 last_sessions_commit_delay_seconds_
);
3482 command
= new ClientCommand();
3483 command
->set_set_sync_poll_interval(180);
3484 command
->set_set_sync_long_poll_interval(190);
3485 command
->set_sessions_commit_delay_seconds(2718);
3486 CreateUnsyncedDirectory("Y", "id_Y");
3487 mock_server_
->SetCommitClientCommand(command
);
3490 EXPECT_TRUE(TimeDelta::FromSeconds(180) ==
3491 last_short_poll_interval_received_
);
3492 EXPECT_TRUE(TimeDelta::FromSeconds(190) ==
3493 last_long_poll_interval_received_
);
3494 EXPECT_TRUE(TimeDelta::FromSeconds(2718) ==
3495 last_sessions_commit_delay_seconds_
);
3498 TEST_F(SyncerTest
, EnsureWeSendUpOldParent
) {
3499 syncable::Id folder_one_id
= ids_
.FromNumber(1);
3500 syncable::Id folder_two_id
= ids_
.FromNumber(2);
3502 mock_server_
->AddUpdateDirectory(folder_one_id
, TestIdFactory::root(),
3503 "folder_one", 1, 1, foreign_cache_guid(), "-1");
3504 mock_server_
->AddUpdateDirectory(folder_two_id
, TestIdFactory::root(),
3505 "folder_two", 1, 1, foreign_cache_guid(), "-2");
3508 // A moved entry should send an "old parent."
3509 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3510 MutableEntry
entry(&trans
, GET_BY_ID
, folder_one_id
);
3511 ASSERT_TRUE(entry
.good());
3512 entry
.Put(PARENT_ID
, folder_two_id
);
3513 entry
.Put(IS_UNSYNCED
, true);
3514 // A new entry should send no "old parent."
3515 MutableEntry
create(
3516 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "new_folder");
3517 create
.Put(IS_UNSYNCED
, true);
3518 create
.Put(SPECIFICS
, DefaultBookmarkSpecifics());
3521 const sync_pb::CommitMessage
& commit
= mock_server_
->last_sent_commit();
3522 ASSERT_EQ(2, commit
.entries_size());
3523 EXPECT_TRUE(commit
.entries(0).parent_id_string() == "2");
3524 EXPECT_TRUE(commit
.entries(0).old_parent_id() == "0");
3525 EXPECT_FALSE(commit
.entries(1).has_old_parent_id());
3528 TEST_F(SyncerTest
, Test64BitVersionSupport
) {
3529 int64 really_big_int
= std::numeric_limits
<int64
>::max() - 12;
3530 const string
name("ringo's dang orang ran rings around my o-ring");
3531 int64 item_metahandle
;
3533 // Try writing max int64 to the version fields of a meta entry.
3535 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3536 MutableEntry
entry(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), name
);
3537 ASSERT_TRUE(entry
.good());
3538 entry
.Put(syncable::BASE_VERSION
, really_big_int
);
3539 entry
.Put(syncable::SERVER_VERSION
, really_big_int
);
3540 entry
.Put(syncable::ID
, ids_
.NewServerId());
3541 item_metahandle
= entry
.Get(META_HANDLE
);
3543 // Now read it back out and make sure the value is max int64.
3544 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
3545 Entry
entry(&rtrans
, syncable::GET_BY_HANDLE
, item_metahandle
);
3546 ASSERT_TRUE(entry
.good());
3547 EXPECT_TRUE(really_big_int
== entry
.Get(syncable::BASE_VERSION
));
3550 TEST_F(SyncerTest
, TestSimpleUndelete
) {
3551 Id id
= ids_
.MakeServer("undeletion item"), root
= TestIdFactory::root();
3552 mock_server_
->set_conflict_all_commits(true);
3553 // Let there be an entry from the server.
3554 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 1, 10,
3555 foreign_cache_guid(), "-1");
3557 // Check it out and delete it.
3559 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3560 MutableEntry
entry(&wtrans
, GET_BY_ID
, id
);
3561 ASSERT_TRUE(entry
.good());
3562 EXPECT_FALSE(entry
.Get(IS_UNAPPLIED_UPDATE
));
3563 EXPECT_FALSE(entry
.Get(IS_UNSYNCED
));
3564 EXPECT_FALSE(entry
.Get(IS_DEL
));
3565 // Delete it locally.
3566 entry
.Put(IS_DEL
, true);
3569 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3571 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3572 Entry
entry(&trans
, GET_BY_ID
, id
);
3573 ASSERT_TRUE(entry
.good());
3574 EXPECT_FALSE(entry
.Get(IS_UNAPPLIED_UPDATE
));
3575 EXPECT_FALSE(entry
.Get(IS_UNSYNCED
));
3576 EXPECT_TRUE(entry
.Get(IS_DEL
));
3577 EXPECT_FALSE(entry
.Get(SERVER_IS_DEL
));
3580 // Update from server confirming deletion.
3581 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 11,
3582 foreign_cache_guid(), "-1");
3583 mock_server_
->SetLastUpdateDeleted();
3585 // IS_DEL AND SERVER_IS_DEL now both true.
3587 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3588 Entry
entry(&trans
, GET_BY_ID
, id
);
3589 ASSERT_TRUE(entry
.good());
3590 EXPECT_FALSE(entry
.Get(IS_UNAPPLIED_UPDATE
));
3591 EXPECT_FALSE(entry
.Get(IS_UNSYNCED
));
3592 EXPECT_TRUE(entry
.Get(IS_DEL
));
3593 EXPECT_TRUE(entry
.Get(SERVER_IS_DEL
));
3595 // Undelete from server.
3596 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 12,
3597 foreign_cache_guid(), "-1");
3599 // IS_DEL and SERVER_IS_DEL now both false.
3601 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3602 Entry
entry(&trans
, GET_BY_ID
, id
);
3603 ASSERT_TRUE(entry
.good());
3604 EXPECT_FALSE(entry
.Get(IS_UNAPPLIED_UPDATE
));
3605 EXPECT_FALSE(entry
.Get(IS_UNSYNCED
));
3606 EXPECT_FALSE(entry
.Get(IS_DEL
));
3607 EXPECT_FALSE(entry
.Get(SERVER_IS_DEL
));
3611 TEST_F(SyncerTest
, TestUndeleteWithMissingDeleteUpdate
) {
3612 Id id
= ids_
.MakeServer("undeletion item"), root
= TestIdFactory::root();
3613 // Let there be a entry, from the server.
3614 mock_server_
->set_conflict_all_commits(true);
3615 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 1, 10,
3616 foreign_cache_guid(), "-1");
3618 // Check it out and delete it.
3620 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
3621 MutableEntry
entry(&wtrans
, GET_BY_ID
, id
);
3622 ASSERT_TRUE(entry
.good());
3623 EXPECT_FALSE(entry
.Get(IS_UNAPPLIED_UPDATE
));
3624 EXPECT_FALSE(entry
.Get(IS_UNSYNCED
));
3625 EXPECT_FALSE(entry
.Get(IS_DEL
));
3626 // Delete it locally.
3627 entry
.Put(IS_DEL
, true);
3630 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3632 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3633 Entry
entry(&trans
, GET_BY_ID
, id
);
3634 ASSERT_TRUE(entry
.good());
3635 EXPECT_FALSE(entry
.Get(IS_UNAPPLIED_UPDATE
));
3636 EXPECT_FALSE(entry
.Get(IS_UNSYNCED
));
3637 EXPECT_TRUE(entry
.Get(IS_DEL
));
3638 EXPECT_FALSE(entry
.Get(SERVER_IS_DEL
));
3641 // Say we do not get an update from server confirming deletion. Undelete
3643 mock_server_
->AddUpdateBookmark(id
, root
, "foo", 2, 12,
3644 foreign_cache_guid(), "-1");
3646 // IS_DEL and SERVER_IS_DEL now both false.
3648 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3649 Entry
entry(&trans
, GET_BY_ID
, id
);
3650 ASSERT_TRUE(entry
.good());
3651 EXPECT_FALSE(entry
.Get(IS_UNAPPLIED_UPDATE
));
3652 EXPECT_FALSE(entry
.Get(IS_UNSYNCED
));
3653 EXPECT_FALSE(entry
.Get(IS_DEL
));
3654 EXPECT_FALSE(entry
.Get(SERVER_IS_DEL
));
3658 TEST_F(SyncerTest
, TestUndeleteIgnoreCorrectlyUnappliedUpdate
) {
3659 Id id1
= ids_
.MakeServer("first"), id2
= ids_
.MakeServer("second");
3660 Id root
= TestIdFactory::root();
3661 // Duplicate! expect path clashing!
3662 mock_server_
->set_conflict_all_commits(true);
3663 mock_server_
->AddUpdateBookmark(id1
, root
, "foo", 1, 10,
3664 foreign_cache_guid(), "-1");
3665 mock_server_
->AddUpdateBookmark(id2
, root
, "foo", 1, 10,
3666 foreign_cache_guid(), "-2");
3668 mock_server_
->AddUpdateBookmark(id2
, root
, "foo2", 2, 20,
3669 foreign_cache_guid(), "-2");
3670 SyncShareNudge(); // Now just don't explode.
3673 TEST_F(SyncerTest
, ClientTagServerCreatedUpdatesWork
) {
3674 mock_server_
->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
3675 foreign_cache_guid(), "-1");
3676 mock_server_
->SetLastUpdateClientTag("permfolder");
3681 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3682 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
3683 ASSERT_TRUE(perm_folder
.good());
3684 EXPECT_FALSE(perm_folder
.Get(IS_DEL
));
3685 EXPECT_FALSE(perm_folder
.Get(IS_UNAPPLIED_UPDATE
));
3686 EXPECT_FALSE(perm_folder
.Get(IS_UNSYNCED
));
3687 EXPECT_EQ(perm_folder
.Get(UNIQUE_CLIENT_TAG
), "permfolder");
3688 EXPECT_EQ(perm_folder
.Get(NON_UNIQUE_NAME
), "permitem1");
3691 mock_server_
->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
3692 foreign_cache_guid(), "-1");
3693 mock_server_
->SetLastUpdateClientTag("permfolder");
3697 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3699 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
3700 ASSERT_TRUE(perm_folder
.good());
3701 EXPECT_FALSE(perm_folder
.Get(IS_DEL
));
3702 EXPECT_FALSE(perm_folder
.Get(IS_UNAPPLIED_UPDATE
));
3703 EXPECT_FALSE(perm_folder
.Get(IS_UNSYNCED
));
3704 EXPECT_EQ(perm_folder
.Get(UNIQUE_CLIENT_TAG
), "permfolder");
3705 EXPECT_EQ(perm_folder
.Get(NON_UNIQUE_NAME
), "permitem_renamed");
3709 TEST_F(SyncerTest
, ClientTagIllegalUpdateIgnored
) {
3710 mock_server_
->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
3711 foreign_cache_guid(), "-1");
3712 mock_server_
->SetLastUpdateClientTag("permfolder");
3717 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3718 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
3719 ASSERT_TRUE(perm_folder
.good());
3720 EXPECT_FALSE(perm_folder
.Get(IS_UNAPPLIED_UPDATE
));
3721 EXPECT_FALSE(perm_folder
.Get(IS_UNSYNCED
));
3722 EXPECT_EQ(perm_folder
.Get(UNIQUE_CLIENT_TAG
), "permfolder");
3723 EXPECT_TRUE(perm_folder
.Get(NON_UNIQUE_NAME
) == "permitem1");
3724 EXPECT_TRUE(perm_folder
.Get(ID
).ServerKnows());
3727 mock_server_
->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
3728 foreign_cache_guid(), "-1");
3729 mock_server_
->SetLastUpdateClientTag("wrongtag");
3733 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3735 // This update is rejected because it has the same ID, but a
3736 // different tag than one that is already on the client.
3737 // The client has a ServerKnows ID, which cannot be overwritten.
3738 Entry
rejected_update(&trans
, GET_BY_CLIENT_TAG
, "wrongtag");
3739 EXPECT_FALSE(rejected_update
.good());
3741 Entry
perm_folder(&trans
, GET_BY_CLIENT_TAG
, "permfolder");
3742 ASSERT_TRUE(perm_folder
.good());
3743 EXPECT_FALSE(perm_folder
.Get(IS_UNAPPLIED_UPDATE
));
3744 EXPECT_FALSE(perm_folder
.Get(IS_UNSYNCED
));
3745 EXPECT_EQ(perm_folder
.Get(NON_UNIQUE_NAME
), "permitem1");
3749 TEST_F(SyncerTest
, ClientTagUncommittedTagMatchesUpdate
) {
3750 int64 original_metahandle
= 0;
3753 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3755 &trans
, CREATE
, PREFERENCES
, ids_
.root(), "name");
3756 ASSERT_TRUE(pref
.good());
3757 pref
.Put(UNIQUE_CLIENT_TAG
, "tag");
3758 pref
.Put(IS_UNSYNCED
, true);
3759 EXPECT_FALSE(pref
.Get(IS_UNAPPLIED_UPDATE
));
3760 EXPECT_FALSE(pref
.Get(ID
).ServerKnows());
3761 original_metahandle
= pref
.Get(META_HANDLE
);
3764 syncable::Id server_id
= TestIdFactory::MakeServer("id");
3765 mock_server_
->AddUpdatePref(server_id
.GetServerId(),
3766 ids_
.root().GetServerId(),
3768 mock_server_
->set_conflict_all_commits(true);
3771 // This should cause client tag reunion, preserving the metahandle.
3773 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3775 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
3776 ASSERT_TRUE(pref
.good());
3777 EXPECT_FALSE(pref
.Get(IS_DEL
));
3778 EXPECT_FALSE(pref
.Get(IS_UNAPPLIED_UPDATE
));
3779 EXPECT_TRUE(pref
.Get(IS_UNSYNCED
));
3780 EXPECT_EQ(10, pref
.Get(BASE_VERSION
));
3781 // Entry should have been given the new ID while preserving the
3782 // metahandle; client should have won the conflict resolution.
3783 EXPECT_EQ(original_metahandle
, pref
.Get(META_HANDLE
));
3784 EXPECT_EQ("tag", pref
.Get(UNIQUE_CLIENT_TAG
));
3785 EXPECT_TRUE(pref
.Get(ID
).ServerKnows());
3788 mock_server_
->set_conflict_all_commits(false);
3791 // The resolved entry ought to commit cleanly.
3793 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3795 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
3796 ASSERT_TRUE(pref
.good());
3797 EXPECT_FALSE(pref
.Get(IS_DEL
));
3798 EXPECT_FALSE(pref
.Get(IS_UNAPPLIED_UPDATE
));
3799 EXPECT_FALSE(pref
.Get(IS_UNSYNCED
));
3800 EXPECT_TRUE(10 < pref
.Get(BASE_VERSION
));
3801 // Entry should have been given the new ID while preserving the
3802 // metahandle; client should have won the conflict resolution.
3803 EXPECT_EQ(original_metahandle
, pref
.Get(META_HANDLE
));
3804 EXPECT_EQ("tag", pref
.Get(UNIQUE_CLIENT_TAG
));
3805 EXPECT_TRUE(pref
.Get(ID
).ServerKnows());
3809 TEST_F(SyncerTest
, ClientTagConflictWithDeletedLocalEntry
) {
3811 // Create a deleted local entry with a unique client tag.
3812 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
3814 &trans
, CREATE
, PREFERENCES
, ids_
.root(), "name");
3815 ASSERT_TRUE(pref
.good());
3816 ASSERT_FALSE(pref
.Get(ID
).ServerKnows());
3817 pref
.Put(UNIQUE_CLIENT_TAG
, "tag");
3818 pref
.Put(IS_UNSYNCED
, true);
3820 // Note: IS_DEL && !ServerKnows() will clear the UNSYNCED bit.
3821 // (We never attempt to commit server-unknown deleted items, so this
3822 // helps us clean up those entries).
3823 pref
.Put(IS_DEL
, true);
3826 // Prepare an update with the same unique client tag.
3827 syncable::Id server_id
= TestIdFactory::MakeServer("id");
3828 mock_server_
->AddUpdatePref(server_id
.GetServerId(),
3829 ids_
.root().GetServerId(),
3833 // The local entry will be overwritten.
3835 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3837 Entry
pref(&trans
, GET_BY_CLIENT_TAG
, "tag");
3838 ASSERT_TRUE(pref
.good());
3839 ASSERT_TRUE(pref
.Get(ID
).ServerKnows());
3840 EXPECT_FALSE(pref
.Get(IS_DEL
));
3841 EXPECT_FALSE(pref
.Get(IS_UNAPPLIED_UPDATE
));
3842 EXPECT_FALSE(pref
.Get(IS_UNSYNCED
));
3843 EXPECT_EQ(pref
.Get(BASE_VERSION
), 10);
3844 EXPECT_EQ(pref
.Get(UNIQUE_CLIENT_TAG
), "tag");
3848 TEST_F(SyncerTest
, ClientTagUpdateClashesWithLocalEntry
) {
3849 // This test is written assuming that ID comparison
3850 // will work out in a particular way.
3851 EXPECT_TRUE(ids_
.FromNumber(1) < ids_
.FromNumber(2));
3852 EXPECT_TRUE(ids_
.FromNumber(3) < ids_
.FromNumber(4));
3854 syncable::Id id1
= TestIdFactory::MakeServer("1");
3855 mock_server_
->AddUpdatePref(id1
.GetServerId(), ids_
.root().GetServerId(),
3858 syncable::Id id4
= TestIdFactory::MakeServer("4");
3859 mock_server_
->AddUpdatePref(id4
.GetServerId(), ids_
.root().GetServerId(),
3862 mock_server_
->set_conflict_all_commits(true);
3865 int64 tag1_metahandle
= syncable::kInvalidMetaHandle
;
3866 int64 tag2_metahandle
= syncable::kInvalidMetaHandle
;
3867 // This should cause client tag overwrite.
3869 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3871 Entry
tag1(&trans
, GET_BY_CLIENT_TAG
, "tag1");
3872 ASSERT_TRUE(tag1
.good());
3873 ASSERT_TRUE(tag1
.Get(ID
).ServerKnows());
3874 ASSERT_TRUE(id1
== tag1
.Get(ID
));
3875 EXPECT_FALSE(tag1
.Get(IS_DEL
));
3876 EXPECT_FALSE(tag1
.Get(IS_UNAPPLIED_UPDATE
));
3877 EXPECT_FALSE(tag1
.Get(IS_UNSYNCED
));
3878 EXPECT_EQ(10, tag1
.Get(BASE_VERSION
));
3879 EXPECT_EQ("tag1", tag1
.Get(UNIQUE_CLIENT_TAG
));
3880 tag1_metahandle
= tag1
.Get(META_HANDLE
);
3882 Entry
tag2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
3883 ASSERT_TRUE(tag2
.good());
3884 ASSERT_TRUE(tag2
.Get(ID
).ServerKnows());
3885 ASSERT_TRUE(id4
== tag2
.Get(ID
));
3886 EXPECT_FALSE(tag2
.Get(IS_DEL
));
3887 EXPECT_FALSE(tag2
.Get(IS_UNAPPLIED_UPDATE
));
3888 EXPECT_FALSE(tag2
.Get(IS_UNSYNCED
));
3889 EXPECT_EQ(11, tag2
.Get(BASE_VERSION
));
3890 EXPECT_EQ("tag2", tag2
.Get(UNIQUE_CLIENT_TAG
));
3891 tag2_metahandle
= tag2
.Get(META_HANDLE
);
3893 syncable::Directory::ChildHandles children
;
3894 directory()->GetChildHandlesById(&trans
, trans
.root_id(), &children
);
3895 ASSERT_EQ(2U, children
.size());
3898 syncable::Id id2
= TestIdFactory::MakeServer("2");
3899 mock_server_
->AddUpdatePref(id2
.GetServerId(), ids_
.root().GetServerId(),
3901 syncable::Id id3
= TestIdFactory::MakeServer("3");
3902 mock_server_
->AddUpdatePref(id3
.GetServerId(), ids_
.root().GetServerId(),
3907 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3909 Entry
tag1(&trans
, GET_BY_CLIENT_TAG
, "tag1");
3910 ASSERT_TRUE(tag1
.good());
3911 ASSERT_TRUE(tag1
.Get(ID
).ServerKnows());
3912 ASSERT_EQ(id1
, tag1
.Get(ID
))
3913 << "ID 1 should be kept, since it was less than ID 2.";
3914 EXPECT_FALSE(tag1
.Get(IS_DEL
));
3915 EXPECT_FALSE(tag1
.Get(IS_UNAPPLIED_UPDATE
));
3916 EXPECT_FALSE(tag1
.Get(IS_UNSYNCED
));
3917 EXPECT_EQ(10, tag1
.Get(BASE_VERSION
));
3918 EXPECT_EQ("tag1", tag1
.Get(UNIQUE_CLIENT_TAG
));
3919 EXPECT_EQ(tag1_metahandle
, tag1
.Get(META_HANDLE
));
3921 Entry
tag2(&trans
, GET_BY_CLIENT_TAG
, "tag2");
3922 ASSERT_TRUE(tag2
.good());
3923 ASSERT_TRUE(tag2
.Get(ID
).ServerKnows());
3924 ASSERT_EQ(id3
, tag2
.Get(ID
))
3925 << "ID 3 should be kept, since it was less than ID 4.";
3926 EXPECT_FALSE(tag2
.Get(IS_DEL
));
3927 EXPECT_FALSE(tag2
.Get(IS_UNAPPLIED_UPDATE
));
3928 EXPECT_FALSE(tag2
.Get(IS_UNSYNCED
));
3929 EXPECT_EQ(13, tag2
.Get(BASE_VERSION
));
3930 EXPECT_EQ("tag2", tag2
.Get(UNIQUE_CLIENT_TAG
));
3931 EXPECT_EQ(tag2_metahandle
, tag2
.Get(META_HANDLE
));
3933 syncable::Directory::ChildHandles children
;
3934 directory()->GetChildHandlesById(&trans
, trans
.root_id(), &children
);
3935 ASSERT_EQ(2U, children
.size());
3939 TEST_F(SyncerTest
, ClientTagClashWithinBatchOfUpdates
) {
3940 // This test is written assuming that ID comparison
3941 // will work out in a particular way.
3942 EXPECT_TRUE(ids_
.FromNumber(1) < ids_
.FromNumber(4));
3943 EXPECT_TRUE(ids_
.FromNumber(201) < ids_
.FromNumber(205));
3945 // Least ID: winner.
3946 mock_server_
->AddUpdatePref(ids_
.FromNumber(1).GetServerId(),
3947 ids_
.root().GetServerId(), "tag a", 1, 10);
3948 mock_server_
->AddUpdatePref(ids_
.FromNumber(2).GetServerId(),
3949 ids_
.root().GetServerId(), "tag a", 11, 110);
3950 mock_server_
->AddUpdatePref(ids_
.FromNumber(3).GetServerId(),
3951 ids_
.root().GetServerId(), "tag a", 12, 120);
3952 mock_server_
->AddUpdatePref(ids_
.FromNumber(4).GetServerId(),
3953 ids_
.root().GetServerId(), "tag a", 13, 130);
3955 mock_server_
->AddUpdatePref(ids_
.FromNumber(105).GetServerId(),
3956 ids_
.root().GetServerId(), "tag b", 14, 140);
3957 mock_server_
->AddUpdatePref(ids_
.FromNumber(102).GetServerId(),
3958 ids_
.root().GetServerId(), "tag b", 15, 150);
3959 // Least ID: winner.
3960 mock_server_
->AddUpdatePref(ids_
.FromNumber(101).GetServerId(),
3961 ids_
.root().GetServerId(), "tag b", 16, 160);
3962 mock_server_
->AddUpdatePref(ids_
.FromNumber(104).GetServerId(),
3963 ids_
.root().GetServerId(), "tag b", 17, 170);
3965 mock_server_
->AddUpdatePref(ids_
.FromNumber(205).GetServerId(),
3966 ids_
.root().GetServerId(), "tag c", 18, 180);
3967 mock_server_
->AddUpdatePref(ids_
.FromNumber(202).GetServerId(),
3968 ids_
.root().GetServerId(), "tag c", 19, 190);
3969 mock_server_
->AddUpdatePref(ids_
.FromNumber(204).GetServerId(),
3970 ids_
.root().GetServerId(), "tag c", 20, 200);
3971 // Least ID: winner.
3972 mock_server_
->AddUpdatePref(ids_
.FromNumber(201).GetServerId(),
3973 ids_
.root().GetServerId(), "tag c", 21, 210);
3975 mock_server_
->set_conflict_all_commits(true);
3978 // This should cause client tag overwrite.
3980 syncable::ReadTransaction
trans(FROM_HERE
, directory());
3982 Entry
tag_a(&trans
, GET_BY_CLIENT_TAG
, "tag a");
3983 ASSERT_TRUE(tag_a
.good());
3984 EXPECT_TRUE(tag_a
.Get(ID
).ServerKnows());
3985 EXPECT_EQ(ids_
.FromNumber(1), tag_a
.Get(ID
));
3986 EXPECT_FALSE(tag_a
.Get(IS_DEL
));
3987 EXPECT_FALSE(tag_a
.Get(IS_UNAPPLIED_UPDATE
));
3988 EXPECT_FALSE(tag_a
.Get(IS_UNSYNCED
));
3989 EXPECT_EQ(1, tag_a
.Get(BASE_VERSION
));
3990 EXPECT_EQ("tag a", tag_a
.Get(UNIQUE_CLIENT_TAG
));
3992 Entry
tag_b(&trans
, GET_BY_CLIENT_TAG
, "tag b");
3993 ASSERT_TRUE(tag_b
.good());
3994 EXPECT_TRUE(tag_b
.Get(ID
).ServerKnows());
3995 EXPECT_EQ(ids_
.FromNumber(101), tag_b
.Get(ID
));
3996 EXPECT_FALSE(tag_b
.Get(IS_DEL
));
3997 EXPECT_FALSE(tag_b
.Get(IS_UNAPPLIED_UPDATE
));
3998 EXPECT_FALSE(tag_b
.Get(IS_UNSYNCED
));
3999 EXPECT_EQ(16, tag_b
.Get(BASE_VERSION
));
4000 EXPECT_EQ("tag b", tag_b
.Get(UNIQUE_CLIENT_TAG
));
4002 Entry
tag_c(&trans
, GET_BY_CLIENT_TAG
, "tag c");
4003 ASSERT_TRUE(tag_c
.good());
4004 EXPECT_TRUE(tag_c
.Get(ID
).ServerKnows());
4005 EXPECT_EQ(ids_
.FromNumber(201), tag_c
.Get(ID
));
4006 EXPECT_FALSE(tag_c
.Get(IS_DEL
));
4007 EXPECT_FALSE(tag_c
.Get(IS_UNAPPLIED_UPDATE
));
4008 EXPECT_FALSE(tag_c
.Get(IS_UNSYNCED
));
4009 EXPECT_EQ(21, tag_c
.Get(BASE_VERSION
));
4010 EXPECT_EQ("tag c", tag_c
.Get(UNIQUE_CLIENT_TAG
));
4012 syncable::Directory::ChildHandles children
;
4013 directory()->GetChildHandlesById(&trans
, trans
.root_id(), &children
);
4014 ASSERT_EQ(3U, children
.size());
4018 TEST_F(SyncerTest
, UniqueServerTagUpdates
) {
4019 // As a hurdle, introduce an item whose name is the same as the tag value
4021 int64 hurdle_handle
= CreateUnsyncedDirectory("bob", "id_bob");
4023 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4024 Entry
hurdle(&trans
, GET_BY_HANDLE
, hurdle_handle
);
4025 ASSERT_TRUE(hurdle
.good());
4026 ASSERT_TRUE(!hurdle
.Get(IS_DEL
));
4027 ASSERT_TRUE(hurdle
.Get(UNIQUE_SERVER_TAG
).empty());
4028 ASSERT_TRUE(hurdle
.Get(NON_UNIQUE_NAME
) == "bob");
4030 // Try to lookup by the tagname. These should fail.
4031 Entry
tag_alpha(&trans
, GET_BY_SERVER_TAG
, "alpha");
4032 EXPECT_FALSE(tag_alpha
.good());
4033 Entry
tag_bob(&trans
, GET_BY_SERVER_TAG
, "bob");
4034 EXPECT_FALSE(tag_bob
.good());
4037 // Now download some tagged items as updates.
4038 mock_server_
->AddUpdateDirectory(
4039 1, 0, "update1", 1, 10, std::string(), std::string());
4040 mock_server_
->SetLastUpdateServerTag("alpha");
4041 mock_server_
->AddUpdateDirectory(
4042 2, 0, "update2", 2, 20, std::string(), std::string());
4043 mock_server_
->SetLastUpdateServerTag("bob");
4047 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4049 // The new items should be applied as new entries, and we should be able
4050 // to look them up by their tag values.
4051 Entry
tag_alpha(&trans
, GET_BY_SERVER_TAG
, "alpha");
4052 ASSERT_TRUE(tag_alpha
.good());
4053 ASSERT_TRUE(!tag_alpha
.Get(IS_DEL
));
4054 ASSERT_TRUE(tag_alpha
.Get(UNIQUE_SERVER_TAG
) == "alpha");
4055 ASSERT_TRUE(tag_alpha
.Get(NON_UNIQUE_NAME
) == "update1");
4056 Entry
tag_bob(&trans
, GET_BY_SERVER_TAG
, "bob");
4057 ASSERT_TRUE(tag_bob
.good());
4058 ASSERT_TRUE(!tag_bob
.Get(IS_DEL
));
4059 ASSERT_TRUE(tag_bob
.Get(UNIQUE_SERVER_TAG
) == "bob");
4060 ASSERT_TRUE(tag_bob
.Get(NON_UNIQUE_NAME
) == "update2");
4061 // The old item should be unchanged.
4062 Entry
hurdle(&trans
, GET_BY_HANDLE
, hurdle_handle
);
4063 ASSERT_TRUE(hurdle
.good());
4064 ASSERT_TRUE(!hurdle
.Get(IS_DEL
));
4065 ASSERT_TRUE(hurdle
.Get(UNIQUE_SERVER_TAG
).empty());
4066 ASSERT_TRUE(hurdle
.Get(NON_UNIQUE_NAME
) == "bob");
4070 TEST_F(SyncerTest
, GetUpdatesSetsRequestedTypes
) {
4071 // The expectations of this test happen in the MockConnectionManager's
4072 // GetUpdates handler. EnableDatatype sets the expectation value from our
4073 // set of enabled/disabled datatypes.
4074 EnableDatatype(BOOKMARKS
);
4076 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4078 EnableDatatype(AUTOFILL
);
4080 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4082 EnableDatatype(PREFERENCES
);
4084 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4086 DisableDatatype(BOOKMARKS
);
4088 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4090 DisableDatatype(AUTOFILL
);
4092 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4094 DisableDatatype(PREFERENCES
);
4095 EnableDatatype(AUTOFILL
);
4097 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4100 // A typical scenario: server and client each have one update for the other.
4101 // This is the "happy path" alternative to UpdateFailsThenDontCommit.
4102 TEST_F(SyncerTest
, UpdateThenCommit
) {
4103 syncable::Id to_receive
= ids_
.NewServerId();
4104 syncable::Id to_commit
= ids_
.NewLocalId();
4106 mock_server_
->AddUpdateDirectory(to_receive
, ids_
.root(), "x", 1, 10,
4107 foreign_cache_guid(), "-1");
4108 int64 commit_handle
= CreateUnsyncedDirectory("y", to_commit
);
4111 // The sync cycle should have included a GetUpdate, then a commit. By the
4112 // time the commit happened, we should have known for sure that there were no
4113 // hierarchy conflicts, and reported this fact to the server.
4114 ASSERT_TRUE(mock_server_
->last_request().has_commit());
4115 VerifyNoHierarchyConflictsReported(mock_server_
->last_request());
4117 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4119 Entry
received(&trans
, GET_BY_ID
, to_receive
);
4120 ASSERT_TRUE(received
.good());
4121 EXPECT_FALSE(received
.Get(IS_UNSYNCED
));
4122 EXPECT_FALSE(received
.Get(IS_UNAPPLIED_UPDATE
));
4124 Entry
committed(&trans
, GET_BY_HANDLE
, commit_handle
);
4125 ASSERT_TRUE(committed
.good());
4126 EXPECT_FALSE(committed
.Get(IS_UNSYNCED
));
4127 EXPECT_FALSE(committed
.Get(IS_UNAPPLIED_UPDATE
));
4130 // Same as above, but this time we fail to download updates.
4131 // We should not attempt to commit anything unless we successfully downloaded
4132 // updates, otherwise we risk causing a server-side conflict.
4133 TEST_F(SyncerTest
, UpdateFailsThenDontCommit
) {
4134 syncable::Id to_receive
= ids_
.NewServerId();
4135 syncable::Id to_commit
= ids_
.NewLocalId();
4137 mock_server_
->AddUpdateDirectory(to_receive
, ids_
.root(), "x", 1, 10,
4138 foreign_cache_guid(), "-1");
4139 int64 commit_handle
= CreateUnsyncedDirectory("y", to_commit
);
4140 mock_server_
->FailNextPostBufferToPathCall();
4143 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4145 // We did not receive this update.
4146 Entry
received(&trans
, GET_BY_ID
, to_receive
);
4147 ASSERT_FALSE(received
.good());
4149 // And our local update remains unapplied.
4150 Entry
committed(&trans
, GET_BY_HANDLE
, commit_handle
);
4151 ASSERT_TRUE(committed
.good());
4152 EXPECT_TRUE(committed
.Get(IS_UNSYNCED
));
4153 EXPECT_FALSE(committed
.Get(IS_UNAPPLIED_UPDATE
));
4155 // Inform the Mock we won't be fetching all updates.
4156 mock_server_
->ClearUpdatesQueue();
4159 // Downloads two updates and applies them successfully.
4160 // This is the "happy path" alternative to ConfigureFailsDontApplyUpdates.
4161 TEST_F(SyncerTest
, ConfigureDownloadsTwoBatchesSuccess
) {
4162 syncable::Id node1
= ids_
.NewServerId();
4163 syncable::Id node2
= ids_
.NewServerId();
4165 // Construct the first GetUpdates response.
4166 mock_server_
->AddUpdateDirectory(node1
, ids_
.root(), "one", 1, 10,
4167 foreign_cache_guid(), "-2");
4168 mock_server_
->SetChangesRemaining(1);
4169 mock_server_
->NextUpdateBatch();
4171 // Construct the second GetUpdates response.
4172 mock_server_
->AddUpdateDirectory(node2
, ids_
.root(), "two", 1, 20,
4173 foreign_cache_guid(), "-2");
4175 SyncShareConfigure();
4177 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4178 // Both nodes should be downloaded and applied.
4180 Entry
n1(&trans
, GET_BY_ID
, node1
);
4181 ASSERT_TRUE(n1
.good());
4182 EXPECT_FALSE(n1
.Get(IS_UNAPPLIED_UPDATE
));
4184 Entry
n2(&trans
, GET_BY_ID
, node2
);
4185 ASSERT_TRUE(n2
.good());
4186 EXPECT_FALSE(n2
.Get(IS_UNAPPLIED_UPDATE
));
4189 // Same as the above case, but this time the second batch fails to download.
4190 TEST_F(SyncerTest
, ConfigureFailsDontApplyUpdates
) {
4191 syncable::Id node1
= ids_
.NewServerId();
4192 syncable::Id node2
= ids_
.NewServerId();
4194 // The scenario: we have two batches of updates with one update each. A
4195 // normal confgure step would download all the updates one batch at a time and
4196 // apply them. This configure will succeed in downloading the first batch
4197 // then fail when downloading the second.
4198 mock_server_
->FailNthPostBufferToPathCall(2);
4200 // Construct the first GetUpdates response.
4201 mock_server_
->AddUpdateDirectory(node1
, ids_
.root(), "one", 1, 10,
4202 foreign_cache_guid(), "-1");
4203 mock_server_
->SetChangesRemaining(1);
4204 mock_server_
->NextUpdateBatch();
4206 // Consutrct the second GetUpdates response.
4207 mock_server_
->AddUpdateDirectory(node2
, ids_
.root(), "two", 1, 20,
4208 foreign_cache_guid(), "-2");
4210 SyncShareConfigure();
4212 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4214 // The first node was downloaded, but not applied.
4215 Entry
n1(&trans
, GET_BY_ID
, node1
);
4216 ASSERT_TRUE(n1
.good());
4217 EXPECT_TRUE(n1
.Get(IS_UNAPPLIED_UPDATE
));
4219 // The second node was not downloaded.
4220 Entry
n2(&trans
, GET_BY_ID
, node2
);
4221 EXPECT_FALSE(n2
.good());
4223 // One update remains undownloaded.
4224 mock_server_
->ClearUpdatesQueue();
4227 TEST_F(SyncerTest
, GetKeySuccess
) {
4229 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4230 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4233 SyncShareConfigure();
4235 EXPECT_EQ(session_
->status_controller().last_get_key_result(), SYNCER_OK
);
4237 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4238 EXPECT_FALSE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4242 TEST_F(SyncerTest
, GetKeyEmpty
) {
4244 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4245 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4248 mock_server_
->SetKeystoreKey(std::string());
4249 SyncShareConfigure();
4251 EXPECT_NE(session_
->status_controller().last_get_key_result(), SYNCER_OK
);
4253 syncable::ReadTransaction
rtrans(FROM_HERE
, directory());
4254 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans
));
4258 // Test what happens if a client deletes, then recreates, an object very
4259 // quickly. It is possible that the deletion gets sent as a commit, and
4260 // the undelete happens during the commit request. The principle here
4261 // is that with a single committing client, conflicts should never
4262 // be encountered, and a client encountering its past actions during
4263 // getupdates should never feed back to override later actions.
4265 // In cases of ordering A-F below, the outcome should be the same.
4266 // Exercised by UndeleteDuringCommit:
4267 // A. Delete - commit - undelete - commitresponse.
4268 // B. Delete - commit - undelete - commitresponse - getupdates.
4269 // Exercised by UndeleteBeforeCommit:
4270 // C. Delete - undelete - commit - commitresponse.
4271 // D. Delete - undelete - commit - commitresponse - getupdates.
4272 // Exercised by UndeleteAfterCommit:
4273 // E. Delete - commit - commitresponse - undelete - commit
4274 // - commitresponse.
4275 // F. Delete - commit - commitresponse - undelete - commit -
4276 // - commitresponse - getupdates.
4277 class SyncerUndeletionTest
: public SyncerTest
{
4279 SyncerUndeletionTest()
4280 : client_tag_("foobar"),
4281 metahandle_(syncable::kInvalidMetaHandle
) {
4285 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4286 MutableEntry
perm_folder(
4287 &trans
, CREATE
, BOOKMARKS
, ids_
.root(), "clientname");
4288 ASSERT_TRUE(perm_folder
.good());
4289 perm_folder
.Put(UNIQUE_CLIENT_TAG
, client_tag_
);
4290 perm_folder
.Put(IS_UNSYNCED
, true);
4291 perm_folder
.Put(SYNCING
, false);
4292 perm_folder
.Put(SPECIFICS
, DefaultBookmarkSpecifics());
4293 EXPECT_FALSE(perm_folder
.Get(IS_UNAPPLIED_UPDATE
));
4294 EXPECT_FALSE(perm_folder
.Get(ID
).ServerKnows());
4295 metahandle_
= perm_folder
.Get(META_HANDLE
);
4296 local_id_
= perm_folder
.Get(ID
);
4300 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4301 MutableEntry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4302 ASSERT_TRUE(entry
.good());
4303 EXPECT_EQ(metahandle_
, entry
.Get(META_HANDLE
));
4304 entry
.Put(IS_DEL
, true);
4305 entry
.Put(IS_UNSYNCED
, true);
4306 entry
.Put(SYNCING
, false);
4310 WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
4311 MutableEntry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4312 ASSERT_TRUE(entry
.good());
4313 EXPECT_EQ(metahandle_
, entry
.Get(META_HANDLE
));
4314 EXPECT_TRUE(entry
.Get(IS_DEL
));
4315 entry
.Put(IS_DEL
, false);
4316 entry
.Put(IS_UNSYNCED
, true);
4317 entry
.Put(SYNCING
, false);
4320 int64
GetMetahandleOfTag() {
4321 syncable::ReadTransaction
trans(FROM_HERE
, directory());
4322 Entry
entry(&trans
, GET_BY_CLIENT_TAG
, client_tag_
);
4323 EXPECT_TRUE(entry
.good());
4324 if (!entry
.good()) {
4325 return syncable::kInvalidMetaHandle
;
4327 return entry
.Get(META_HANDLE
);
4330 void ExpectUnsyncedCreation() {
4331 EXPECT_EQ(metahandle_
, GetMetahandleOfTag());
4332 EXPECT_FALSE(Get(metahandle_
, IS_DEL
));
4333 EXPECT_FALSE(Get(metahandle_
, SERVER_IS_DEL
)); // Never been committed.
4334 EXPECT_GE(0, Get(metahandle_
, BASE_VERSION
));
4335 EXPECT_TRUE(Get(metahandle_
, IS_UNSYNCED
));
4336 EXPECT_FALSE(Get(metahandle_
, IS_UNAPPLIED_UPDATE
));
4339 void ExpectUnsyncedUndeletion() {
4340 EXPECT_EQ(metahandle_
, GetMetahandleOfTag());
4341 EXPECT_FALSE(Get(metahandle_
, IS_DEL
));
4342 EXPECT_TRUE(Get(metahandle_
, SERVER_IS_DEL
));
4343 EXPECT_EQ(0, Get(metahandle_
, BASE_VERSION
));
4344 EXPECT_TRUE(Get(metahandle_
, IS_UNSYNCED
));
4345 EXPECT_FALSE(Get(metahandle_
, IS_UNAPPLIED_UPDATE
));
4346 EXPECT_TRUE(Get(metahandle_
, ID
).ServerKnows());
4349 void ExpectUnsyncedEdit() {
4350 EXPECT_EQ(metahandle_
, GetMetahandleOfTag());
4351 EXPECT_FALSE(Get(metahandle_
, IS_DEL
));
4352 EXPECT_FALSE(Get(metahandle_
, SERVER_IS_DEL
));
4353 EXPECT_LT(0, Get(metahandle_
, BASE_VERSION
));
4354 EXPECT_TRUE(Get(metahandle_
, IS_UNSYNCED
));
4355 EXPECT_FALSE(Get(metahandle_
, IS_UNAPPLIED_UPDATE
));
4356 EXPECT_TRUE(Get(metahandle_
, ID
).ServerKnows());
4359 void ExpectUnsyncedDeletion() {
4360 EXPECT_EQ(metahandle_
, GetMetahandleOfTag());
4361 EXPECT_TRUE(Get(metahandle_
, IS_DEL
));
4362 EXPECT_FALSE(Get(metahandle_
, SERVER_IS_DEL
));
4363 EXPECT_TRUE(Get(metahandle_
, IS_UNSYNCED
));
4364 EXPECT_FALSE(Get(metahandle_
, IS_UNAPPLIED_UPDATE
));
4365 EXPECT_LT(0, Get(metahandle_
, BASE_VERSION
));
4366 EXPECT_LT(0, Get(metahandle_
, SERVER_VERSION
));
4369 void ExpectSyncedAndCreated() {
4370 EXPECT_EQ(metahandle_
, GetMetahandleOfTag());
4371 EXPECT_FALSE(Get(metahandle_
, IS_DEL
));
4372 EXPECT_FALSE(Get(metahandle_
, SERVER_IS_DEL
));
4373 EXPECT_LT(0, Get(metahandle_
, BASE_VERSION
));
4374 EXPECT_EQ(Get(metahandle_
, BASE_VERSION
), Get(metahandle_
, SERVER_VERSION
));
4375 EXPECT_FALSE(Get(metahandle_
, IS_UNSYNCED
));
4376 EXPECT_FALSE(Get(metahandle_
, IS_UNAPPLIED_UPDATE
));
4379 void ExpectSyncedAndDeleted() {
4380 EXPECT_EQ(metahandle_
, GetMetahandleOfTag());
4381 EXPECT_TRUE(Get(metahandle_
, IS_DEL
));
4382 EXPECT_TRUE(Get(metahandle_
, SERVER_IS_DEL
));
4383 EXPECT_FALSE(Get(metahandle_
, IS_UNSYNCED
));
4384 EXPECT_FALSE(Get(metahandle_
, IS_UNAPPLIED_UPDATE
));
4385 EXPECT_GE(0, Get(metahandle_
, BASE_VERSION
));
4386 EXPECT_GE(0, Get(metahandle_
, SERVER_VERSION
));
4390 const std::string client_tag_
;
4391 syncable::Id local_id_
;
4395 TEST_F(SyncerUndeletionTest
, UndeleteDuringCommit
) {
4397 ExpectUnsyncedCreation();
4400 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4401 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4402 ExpectSyncedAndCreated();
4404 // Delete, begin committing the delete, then undelete while committing.
4406 ExpectUnsyncedDeletion();
4407 mock_server_
->SetMidCommitCallback(
4408 base::Bind(&SyncerUndeletionTest::Undelete
, base::Unretained(this)));
4411 // We will continue to commit until all nodes are synced, so we expect
4412 // that both the delete and following undelete were committed. We haven't
4413 // downloaded any updates, though, so the SERVER fields will be the same
4414 // as they were at the start of the cycle.
4415 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4416 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4418 // Server fields lag behind.
4419 EXPECT_FALSE(Get(metahandle_
, SERVER_IS_DEL
));
4421 // We have committed the second (undelete) update.
4422 EXPECT_FALSE(Get(metahandle_
, IS_DEL
));
4423 EXPECT_FALSE(Get(metahandle_
, IS_UNSYNCED
));
4424 EXPECT_FALSE(Get(metahandle_
, IS_UNAPPLIED_UPDATE
));
4426 // Now, encounter a GetUpdates corresponding to the deletion from
4427 // the server. The undeletion should prevail again and be committed.
4428 // None of this should trigger any conflict detection -- it is perfectly
4429 // normal to recieve updates from our own commits.
4430 mock_server_
->SetMidCommitCallback(base::Closure());
4431 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
4432 update
->set_originator_cache_guid(local_cache_guid());
4433 update
->set_originator_client_item_id(local_id_
.GetServerId());
4436 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4437 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4438 ExpectSyncedAndCreated();
4441 TEST_F(SyncerUndeletionTest
, UndeleteBeforeCommit
) {
4443 ExpectUnsyncedCreation();
4446 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4447 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4448 ExpectSyncedAndCreated();
4450 // Delete and undelete, then sync to pick up the result.
4452 ExpectUnsyncedDeletion();
4454 ExpectUnsyncedEdit(); // Edit, not undelete: server thinks it exists.
4457 // The item ought to have committed successfully.
4458 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4459 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4460 ExpectSyncedAndCreated();
4461 EXPECT_EQ(2, Get(metahandle_
, BASE_VERSION
));
4463 // Now, encounter a GetUpdates corresponding to the just-committed
4465 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
4466 update
->set_originator_cache_guid(local_cache_guid());
4467 update
->set_originator_client_item_id(local_id_
.GetServerId());
4469 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4470 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4471 ExpectSyncedAndCreated();
4474 TEST_F(SyncerUndeletionTest
, UndeleteAfterCommitButBeforeGetUpdates
) {
4476 ExpectUnsyncedCreation();
4479 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4480 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4481 ExpectSyncedAndCreated();
4483 // Delete and commit.
4485 ExpectUnsyncedDeletion();
4488 // The item ought to have committed successfully.
4489 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4490 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4491 ExpectSyncedAndDeleted();
4493 // Before the GetUpdates, the item is locally undeleted.
4495 ExpectUnsyncedUndeletion();
4497 // Now, encounter a GetUpdates corresponding to the just-committed
4498 // deletion update. The undeletion should prevail.
4499 mock_server_
->AddUpdateFromLastCommit();
4501 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4502 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4503 ExpectSyncedAndCreated();
4506 TEST_F(SyncerUndeletionTest
, UndeleteAfterDeleteAndGetUpdates
) {
4508 ExpectUnsyncedCreation();
4511 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4512 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4513 ExpectSyncedAndCreated();
4515 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
4516 update
->set_originator_cache_guid(local_cache_guid());
4517 update
->set_originator_client_item_id(local_id_
.GetServerId());
4519 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4520 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4521 ExpectSyncedAndCreated();
4523 // Delete and commit.
4525 ExpectUnsyncedDeletion();
4528 // The item ought to have committed successfully.
4529 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4530 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4531 ExpectSyncedAndDeleted();
4533 // Now, encounter a GetUpdates corresponding to the just-committed
4534 // deletion update. Should be consistent.
4535 mock_server_
->AddUpdateFromLastCommit();
4537 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4538 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4539 ExpectSyncedAndDeleted();
4541 // After the GetUpdates, the item is locally undeleted.
4543 ExpectUnsyncedUndeletion();
4545 // Now, encounter a GetUpdates corresponding to the just-committed
4546 // deletion update. The undeletion should prevail.
4548 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4549 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4550 ExpectSyncedAndCreated();
4553 // Test processing of undeletion GetUpdateses.
4554 TEST_F(SyncerUndeletionTest
, UndeleteAfterOtherClientDeletes
) {
4556 ExpectUnsyncedCreation();
4559 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4560 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4561 ExpectSyncedAndCreated();
4563 // Add a delete from the server.
4564 sync_pb::SyncEntity
* update1
= mock_server_
->AddUpdateFromLastCommit();
4565 update1
->set_originator_cache_guid(local_cache_guid());
4566 update1
->set_originator_client_item_id(local_id_
.GetServerId());
4568 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4569 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4570 ExpectSyncedAndCreated();
4572 // Some other client deletes the item.
4573 mock_server_
->AddUpdateTombstone(Get(metahandle_
, ID
));
4576 // The update ought to have applied successfully.
4577 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4578 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4579 ExpectSyncedAndDeleted();
4581 // Undelete it locally.
4583 ExpectUnsyncedUndeletion();
4585 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4586 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4587 ExpectSyncedAndCreated();
4589 // Now, encounter a GetUpdates corresponding to the just-committed
4590 // deletion update. The undeletion should prevail.
4591 sync_pb::SyncEntity
* update2
= mock_server_
->AddUpdateFromLastCommit();
4592 update2
->set_originator_cache_guid(local_cache_guid());
4593 update2
->set_originator_client_item_id(local_id_
.GetServerId());
4595 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4596 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4597 ExpectSyncedAndCreated();
4600 TEST_F(SyncerUndeletionTest
, UndeleteAfterOtherClientDeletesImmediately
) {
4602 ExpectUnsyncedCreation();
4605 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4606 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4607 ExpectSyncedAndCreated();
4609 // Some other client deletes the item before we get a chance
4610 // to GetUpdates our original request.
4611 mock_server_
->AddUpdateTombstone(Get(metahandle_
, ID
));
4614 // The update ought to have applied successfully.
4615 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4616 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4617 ExpectSyncedAndDeleted();
4619 // Undelete it locally.
4621 ExpectUnsyncedUndeletion();
4623 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4624 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4625 ExpectSyncedAndCreated();
4627 // Now, encounter a GetUpdates corresponding to the just-committed
4628 // deletion update. The undeletion should prevail.
4629 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
4630 update
->set_originator_cache_guid(local_cache_guid());
4631 update
->set_originator_client_item_id(local_id_
.GetServerId());
4633 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4634 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4635 ExpectSyncedAndCreated();
4638 TEST_F(SyncerUndeletionTest
, OtherClientUndeletes
) {
4640 ExpectUnsyncedCreation();
4643 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4644 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4645 ExpectSyncedAndCreated();
4647 // Get the updates of our just-committed entry.
4648 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
4649 update
->set_originator_cache_guid(local_cache_guid());
4650 update
->set_originator_client_item_id(local_id_
.GetServerId());
4652 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4653 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4654 ExpectSyncedAndCreated();
4656 // We delete the item.
4658 ExpectUnsyncedDeletion();
4661 // The update ought to have applied successfully.
4662 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4663 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4664 ExpectSyncedAndDeleted();
4666 // Now, encounter a GetUpdates corresponding to the just-committed
4668 mock_server_
->AddUpdateFromLastCommit();
4670 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4671 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4672 ExpectSyncedAndDeleted();
4674 // Some other client undeletes the item.
4675 mock_server_
->AddUpdateBookmark(Get(metahandle_
, ID
),
4676 Get(metahandle_
, PARENT_ID
),
4677 "Thadeusz", 100, 1000,
4678 local_cache_guid(), local_id_
.GetServerId());
4679 mock_server_
->SetLastUpdateClientTag(client_tag_
);
4681 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4682 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4683 ExpectSyncedAndCreated();
4684 EXPECT_EQ("Thadeusz", Get(metahandle_
, NON_UNIQUE_NAME
));
4687 TEST_F(SyncerUndeletionTest
, OtherClientUndeletesImmediately
) {
4689 ExpectUnsyncedCreation();
4692 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4693 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4694 ExpectSyncedAndCreated();
4696 // Get the updates of our just-committed entry.
4697 sync_pb::SyncEntity
* update
= mock_server_
->AddUpdateFromLastCommit();
4698 update
->set_originator_cache_guid(local_cache_guid());
4699 update
->set_originator_client_item_id(local_id_
.GetServerId());
4701 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4702 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4703 ExpectSyncedAndCreated();
4705 // We delete the item.
4707 ExpectUnsyncedDeletion();
4710 // The update ought to have applied successfully.
4711 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4712 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4713 ExpectSyncedAndDeleted();
4715 // Some other client undeletes before we see the update from our
4717 mock_server_
->AddUpdateBookmark(Get(metahandle_
, ID
),
4718 Get(metahandle_
, PARENT_ID
),
4719 "Thadeusz", 100, 1000,
4720 local_cache_guid(), local_id_
.GetServerId());
4721 mock_server_
->SetLastUpdateClientTag(client_tag_
);
4723 EXPECT_EQ(0, session_
->status_controller().TotalNumConflictingItems());
4724 EXPECT_EQ(1, mock_server_
->GetAndClearNumGetUpdatesRequests());
4725 ExpectSyncedAndCreated();
4726 EXPECT_EQ("Thadeusz", Get(metahandle_
, NON_UNIQUE_NAME
));
4730 TEST_PARAM_BOOKMARK_ENABLE_BIT
,
4731 TEST_PARAM_AUTOFILL_ENABLE_BIT
,
4732 TEST_PARAM_BIT_COUNT
4737 public ::testing::WithParamInterface
<int> {
4739 bool ShouldFailBookmarkCommit() {
4740 return (GetParam() & (1 << TEST_PARAM_BOOKMARK_ENABLE_BIT
)) == 0;
4742 bool ShouldFailAutofillCommit() {
4743 return (GetParam() & (1 << TEST_PARAM_AUTOFILL_ENABLE_BIT
)) == 0;
4747 INSTANTIATE_TEST_CASE_P(ExtensionsActivity
,
4749 testing::Range(0, 1 << TEST_PARAM_BIT_COUNT
));
4751 TEST_P(MixedResult
, ExtensionsActivity
) {
4753 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, directory());
4755 MutableEntry
pref(&wtrans
, CREATE
, PREFERENCES
, wtrans
.root_id(), "pref");
4756 ASSERT_TRUE(pref
.good());
4757 pref
.Put(syncable::IS_UNSYNCED
, true);
4759 MutableEntry
bookmark(
4760 &wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "bookmark");
4761 ASSERT_TRUE(bookmark
.good());
4762 bookmark
.Put(syncable::IS_UNSYNCED
, true);
4764 if (ShouldFailBookmarkCommit()) {
4765 mock_server_
->SetTransientErrorId(bookmark
.Get(ID
));
4768 if (ShouldFailAutofillCommit()) {
4769 mock_server_
->SetTransientErrorId(pref
.Get(ID
));
4774 // Put some extenions activity records into the monitor.
4776 ExtensionsActivityMonitor::Records records
;
4777 records
["ABC"].extension_id
= "ABC";
4778 records
["ABC"].bookmark_write_count
= 2049U;
4779 records
["xyz"].extension_id
= "xyz";
4780 records
["xyz"].bookmark_write_count
= 4U;
4781 context_
->extensions_monitor()->PutRecords(records
);
4786 ExtensionsActivityMonitor::Records final_monitor_records
;
4787 context_
->extensions_monitor()->GetAndClearRecords(&final_monitor_records
);
4788 if (ShouldFailBookmarkCommit()) {
4789 ASSERT_EQ(2U, final_monitor_records
.size())
4790 << "Should restore records after unsuccessful bookmark commit.";
4791 EXPECT_EQ("ABC", final_monitor_records
["ABC"].extension_id
);
4792 EXPECT_EQ("xyz", final_monitor_records
["xyz"].extension_id
);
4793 EXPECT_EQ(2049U, final_monitor_records
["ABC"].bookmark_write_count
);
4794 EXPECT_EQ(4U, final_monitor_records
["xyz"].bookmark_write_count
);
4796 EXPECT_TRUE(final_monitor_records
.empty())
4797 << "Should not restore records after successful bookmark commit.";
4801 } // namespace syncer