1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // Syncer unit tests. Unfortunately a lot of these tests
6 // are outdated and need to be reworked and updated.
15 #include "base/bind.h"
16 #include "base/bind_helpers.h"
17 #include "base/callback.h"
18 #include "base/compiler_specific.h"
19 #include "base/location.h"
20 #include "base/memory/scoped_ptr.h"
21 #include "base/message_loop/message_loop.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/stringprintf.h"
24 #include "base/time/time.h"
25 #include "build/build_config.h"
26 #include "sync/engine/get_commit_ids.h"
27 #include "sync/engine/net/server_connection_manager.h"
28 #include "sync/engine/sync_scheduler_impl.h"
29 #include "sync/engine/syncer.h"
30 #include "sync/engine/syncer_proto_util.h"
31 #include "sync/engine/traffic_recorder.h"
32 #include "sync/internal_api/public/base/cancelation_signal.h"
33 #include "sync/internal_api/public/base/model_type.h"
34 #include "sync/internal_api/public/engine/model_safe_worker.h"
35 #include "sync/protocol/bookmark_specifics.pb.h"
36 #include "sync/protocol/nigori_specifics.pb.h"
37 #include "sync/protocol/preference_specifics.pb.h"
38 #include "sync/protocol/sync.pb.h"
39 #include "sync/sessions/sync_session_context.h"
40 #include "sync/syncable/mutable_entry.h"
41 #include "sync/syncable/nigori_util.h"
42 #include "sync/syncable/syncable_delete_journal.h"
43 #include "sync/syncable/syncable_read_transaction.h"
44 #include "sync/syncable/syncable_util.h"
45 #include "sync/syncable/syncable_write_transaction.h"
46 #include "sync/test/engine/fake_model_worker.h"
47 #include "sync/test/engine/mock_connection_manager.h"
48 #include "sync/test/engine/test_directory_setter_upper.h"
49 #include "sync/test/engine/test_id_factory.h"
50 #include "sync/test/engine/test_syncable_utils.h"
51 #include "sync/test/fake_encryptor.h"
52 #include "sync/test/fake_sync_encryption_handler.h"
53 #include "sync/util/cryptographer.h"
54 #include "sync/util/extensions_activity.h"
55 #include "sync/util/time.h"
56 #include "testing/gtest/include/gtest/gtest.h"
58 using base::TimeDelta;
69 using syncable::BaseTransaction;
71 using syncable::CountEntriesWithName;
72 using syncable::Directory;
73 using syncable::Entry;
74 using syncable::GetFirstEntryWithName;
75 using syncable::GetOnlyEntryWithName;
77 using syncable::kEncryptedString;
78 using syncable::MutableEntry;
79 using syncable::WriteTransaction;
81 using syncable::BASE_VERSION;
82 using syncable::CREATE;
83 using syncable::GET_BY_HANDLE;
84 using syncable::GET_BY_ID;
85 using syncable::GET_BY_CLIENT_TAG;
86 using syncable::GET_BY_SERVER_TAG;
88 using syncable::IS_DEL;
89 using syncable::IS_DIR;
90 using syncable::IS_UNAPPLIED_UPDATE;
91 using syncable::IS_UNSYNCED;
92 using syncable::META_HANDLE;
93 using syncable::MTIME;
94 using syncable::NON_UNIQUE_NAME;
95 using syncable::PARENT_ID;
96 using syncable::BASE_SERVER_SPECIFICS;
97 using syncable::SERVER_IS_DEL;
98 using syncable::SERVER_PARENT_ID;
99 using syncable::SERVER_SPECIFICS;
100 using syncable::SERVER_VERSION;
101 using syncable::UNIQUE_CLIENT_TAG;
102 using syncable::UNIQUE_SERVER_TAG;
103 using syncable::SPECIFICS;
104 using syncable::SYNCING;
105 using syncable::UNITTEST;
107 using sessions::StatusController;
108 using sessions::SyncSessionContext;
109 using sessions::SyncSession;
111 class SyncerTest : public testing::Test,
112 public SyncSession::Delegate,
113 public SyncEngineEventListener {
116 : extensions_activity_(new ExtensionsActivity),
118 saw_syncer_event_(false),
119 last_client_invalidation_hint_buffer_size_(10),
120 traffic_recorder_(0, 0) {
123 // SyncSession::Delegate implementation.
124 virtual void OnThrottled(const base::TimeDelta& throttle_duration) OVERRIDE {
125 FAIL() << "Should not get silenced.";
127 virtual void OnTypesThrottled(
129 const base::TimeDelta& throttle_duration) OVERRIDE {
130 FAIL() << "Should not get silenced.";
132 virtual bool IsCurrentlyThrottled() OVERRIDE {
135 virtual void OnReceivedLongPollIntervalUpdate(
136 const base::TimeDelta& new_interval) OVERRIDE {
137 last_long_poll_interval_received_ = new_interval;
139 virtual void OnReceivedShortPollIntervalUpdate(
140 const base::TimeDelta& new_interval) OVERRIDE {
141 last_short_poll_interval_received_ = new_interval;
143 virtual void OnReceivedSessionsCommitDelay(
144 const base::TimeDelta& new_delay) OVERRIDE {
145 last_sessions_commit_delay_seconds_ = new_delay;
147 virtual void OnReceivedClientInvalidationHintBufferSize(
149 last_client_invalidation_hint_buffer_size_ = size;
151 virtual void OnShouldStopSyncingPermanently() OVERRIDE {
153 virtual void OnSyncProtocolError(
154 const sessions::SyncSessionSnapshot& snapshot) OVERRIDE {
157 void GetWorkers(std::vector<ModelSafeWorker*>* out) {
158 out->push_back(worker_.get());
161 void GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) {
162 // We're just testing the sync engine here, so we shunt everything to
163 // the SyncerThread. Datatypes which aren't enabled aren't in the map.
164 for (ModelTypeSet::Iterator it = enabled_datatypes_.First();
165 it.Good(); it.Inc()) {
166 (*out)[it.Get()] = GROUP_PASSIVE;
170 virtual void OnSyncEngineEvent(const SyncEngineEvent& event) OVERRIDE {
171 DVLOG(1) << "HandleSyncEngineEvent in unittest " << event.what_happened;
172 // we only test for entry-specific events, not status changed ones.
173 switch (event.what_happened) {
174 case SyncEngineEvent::SYNC_CYCLE_BEGIN: // Fall through.
175 case SyncEngineEvent::STATUS_CHANGED:
176 case SyncEngineEvent::SYNC_CYCLE_ENDED:
179 CHECK(false) << "Handling unknown error type in unit tests!!";
181 saw_syncer_event_ = true;
184 void SyncShareNudge() {
185 session_.reset(SyncSession::Build(context_.get(), this));
187 // Pretend we've seen a local change, to make the nudge_tracker look normal.
188 nudge_tracker_.RecordLocalChange(ModelTypeSet(BOOKMARKS));
191 syncer_->NormalSyncShare(
192 GetRoutingInfoTypes(context_->routing_info()),
197 void SyncShareConfigure() {
198 session_.reset(SyncSession::Build(context_.get(), this));
199 EXPECT_TRUE(syncer_->ConfigureSyncShare(
200 GetRoutingInfoTypes(context_->routing_info()),
201 sync_pb::GetUpdatesCallerInfo::RECONFIGURATION,
205 virtual void SetUp() {
207 mock_server_.reset(new MockConnectionManager(directory(),
208 &cancelation_signal_));
209 EnableDatatype(BOOKMARKS);
210 EnableDatatype(NIGORI);
211 EnableDatatype(PREFERENCES);
212 EnableDatatype(NIGORI);
213 worker_ = new FakeModelWorker(GROUP_PASSIVE);
214 std::vector<SyncEngineEventListener*> listeners;
215 listeners.push_back(this);
217 ModelSafeRoutingInfo routing_info;
218 std::vector<ModelSafeWorker*> workers;
220 GetModelSafeRoutingInfo(&routing_info);
221 GetWorkers(&workers);
224 new SyncSessionContext(
225 mock_server_.get(), directory(), workers,
226 extensions_activity_,
227 listeners, NULL, &traffic_recorder_,
228 true, // enable keystore encryption
229 false, // force enable pre-commit GU avoidance experiment
230 "fake_invalidator_client_id"));
231 context_->set_routing_info(routing_info);
232 syncer_ = new Syncer(&cancelation_signal_);
234 syncable::ReadTransaction trans(FROM_HERE, directory());
235 syncable::Directory::Metahandles children;
236 directory()->GetChildHandlesById(&trans, trans.root_id(), &children);
237 ASSERT_EQ(0u, children.size());
238 saw_syncer_event_ = false;
239 root_id_ = TestIdFactory::root();
240 parent_id_ = ids_.MakeServer("parent id");
241 child_id_ = ids_.MakeServer("child id");
242 directory()->set_store_birthday(mock_server_->store_birthday());
243 mock_server_->SetKeystoreKey("encryption_key");
246 virtual void TearDown() {
247 mock_server_.reset();
250 dir_maker_.TearDown();
252 void WriteTestDataToEntry(WriteTransaction* trans, MutableEntry* entry) {
253 EXPECT_FALSE(entry->GetIsDir());
254 EXPECT_FALSE(entry->GetIsDel());
255 sync_pb::EntitySpecifics specifics;
256 specifics.mutable_bookmark()->set_url("http://demo/");
257 specifics.mutable_bookmark()->set_favicon("PNG");
258 entry->PutSpecifics(specifics);
259 entry->PutIsUnsynced(true);
261 void VerifyTestDataInEntry(BaseTransaction* trans, Entry* entry) {
262 EXPECT_FALSE(entry->GetIsDir());
263 EXPECT_FALSE(entry->GetIsDel());
264 VerifyTestBookmarkDataInEntry(entry);
266 void VerifyTestBookmarkDataInEntry(Entry* entry) {
267 const sync_pb::EntitySpecifics& specifics = entry->GetSpecifics();
268 EXPECT_TRUE(specifics.has_bookmark());
269 EXPECT_EQ("PNG", specifics.bookmark().favicon());
270 EXPECT_EQ("http://demo/", specifics.bookmark().url());
273 void VerifyHierarchyConflictsReported(
274 const sync_pb::ClientToServerMessage& message) {
275 // Our request should have included a warning about hierarchy conflicts.
276 const sync_pb::ClientStatus& client_status = message.client_status();
277 EXPECT_TRUE(client_status.has_hierarchy_conflict_detected());
278 EXPECT_TRUE(client_status.hierarchy_conflict_detected());
281 void VerifyNoHierarchyConflictsReported(
282 const sync_pb::ClientToServerMessage& message) {
283 // Our request should have reported no hierarchy conflicts detected.
284 const sync_pb::ClientStatus& client_status = message.client_status();
285 EXPECT_TRUE(client_status.has_hierarchy_conflict_detected());
286 EXPECT_FALSE(client_status.hierarchy_conflict_detected());
289 void VerifyHierarchyConflictsUnspecified(
290 const sync_pb::ClientToServerMessage& message) {
291 // Our request should have neither confirmed nor denied hierarchy conflicts.
292 const sync_pb::ClientStatus& client_status = message.client_status();
293 EXPECT_FALSE(client_status.has_hierarchy_conflict_detected());
296 sync_pb::EntitySpecifics DefaultBookmarkSpecifics() {
297 sync_pb::EntitySpecifics result;
298 AddDefaultFieldValue(BOOKMARKS, &result);
302 sync_pb::EntitySpecifics DefaultPreferencesSpecifics() {
303 sync_pb::EntitySpecifics result;
304 AddDefaultFieldValue(PREFERENCES, &result);
307 // Enumeration of alterations to entries for commit ordering tests.
309 LIST_END = 0, // Denotes the end of the list of features from below.
310 SYNCED, // Items are unsynced by default
316 struct CommitOrderingTest {
317 // expected commit index.
319 // Details about the item
321 syncable::Id parent_id;
322 EntryFeature features[10];
324 static CommitOrderingTest MakeLastCommitItem() {
325 CommitOrderingTest last_commit_item;
326 last_commit_item.commit_index = -1;
327 last_commit_item.id = TestIdFactory::root();
328 return last_commit_item;
332 void RunCommitOrderingTest(CommitOrderingTest* test) {
333 map<int, syncable::Id> expected_positions;
334 { // Transaction scope.
335 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
336 while (!test->id.IsRoot()) {
337 if (test->commit_index >= 0) {
338 map<int, syncable::Id>::value_type entry(test->commit_index,
340 bool double_position = !expected_positions.insert(entry).second;
341 ASSERT_FALSE(double_position) << "Two id's expected at one position";
343 string utf8_name = test->id.GetServerId();
344 string name(utf8_name.begin(), utf8_name.end());
345 MutableEntry entry(&trans, CREATE, BOOKMARKS, test->parent_id, name);
347 entry.PutId(test->id);
348 if (test->id.ServerKnows()) {
349 entry.PutBaseVersion(5);
350 entry.PutServerVersion(5);
351 entry.PutServerParentId(test->parent_id);
353 entry.PutIsDir(true);
354 entry.PutIsUnsynced(true);
355 entry.PutSpecifics(DefaultBookmarkSpecifics());
356 // Set the time to 30 seconds in the future to reduce the chance of
358 const base::Time& now_plus_30s =
359 base::Time::Now() + base::TimeDelta::FromSeconds(30);
360 const base::Time& now_minus_2h =
361 base::Time::Now() - base::TimeDelta::FromHours(2);
362 entry.PutMtime(now_plus_30s);
363 for (size_t i = 0 ; i < arraysize(test->features) ; ++i) {
364 switch (test->features[i]) {
368 entry.PutIsUnsynced(false);
371 entry.PutIsDel(true);
374 entry.PutMtime(now_minus_2h);
376 case MOVED_FROM_ROOT:
377 entry.PutServerParentId(trans.root_id());
380 FAIL() << "Bad value in CommitOrderingTest list";
387 ASSERT_TRUE(expected_positions.size() ==
388 mock_server_->committed_ids().size());
389 // If this test starts failing, be aware other sort orders could be valid.
390 for (size_t i = 0; i < expected_positions.size(); ++i) {
392 EXPECT_EQ(1u, expected_positions.count(i));
393 EXPECT_EQ(expected_positions[i], mock_server_->committed_ids()[i]);
397 const StatusController& status() {
398 return session_->status_controller();
401 Directory* directory() {
402 return dir_maker_.directory();
405 const std::string local_cache_guid() {
406 return directory()->cache_guid();
409 const std::string foreign_cache_guid() {
410 return "kqyg7097kro6GSUod+GSg==";
413 int64 CreateUnsyncedDirectory(const string& entry_name,
414 const string& idstring) {
415 return CreateUnsyncedDirectory(entry_name,
416 syncable::Id::CreateFromServerId(idstring));
419 int64 CreateUnsyncedDirectory(const string& entry_name,
420 const syncable::Id& id) {
421 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
423 &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), entry_name);
424 EXPECT_TRUE(entry.good());
425 entry.PutIsUnsynced(true);
426 entry.PutIsDir(true);
427 entry.PutSpecifics(DefaultBookmarkSpecifics());
428 entry.PutBaseVersion(id.ServerKnows() ? 1 : 0);
430 return entry.GetMetahandle();
433 void EnableDatatype(ModelType model_type) {
434 enabled_datatypes_.Put(model_type);
436 ModelSafeRoutingInfo routing_info;
437 GetModelSafeRoutingInfo(&routing_info);
440 context_->set_routing_info(routing_info);
443 mock_server_->ExpectGetUpdatesRequestTypes(enabled_datatypes_);
446 void DisableDatatype(ModelType model_type) {
447 enabled_datatypes_.Remove(model_type);
449 ModelSafeRoutingInfo routing_info;
450 GetModelSafeRoutingInfo(&routing_info);
453 context_->set_routing_info(routing_info);
456 mock_server_->ExpectGetUpdatesRequestTypes(enabled_datatypes_);
459 Cryptographer* GetCryptographer(syncable::BaseTransaction* trans) {
460 return directory()->GetCryptographer(trans);
463 base::MessageLoop message_loop_;
465 // Some ids to aid tests. Only the root one's value is specific. The rest
466 // are named for test clarity.
467 // TODO(chron): Get rid of these inbuilt IDs. They only make it
469 syncable::Id root_id_;
470 syncable::Id parent_id_;
471 syncable::Id child_id_;
475 TestDirectorySetterUpper dir_maker_;
476 FakeEncryptor encryptor_;
477 scoped_refptr<ExtensionsActivity> extensions_activity_;
478 scoped_ptr<MockConnectionManager> mock_server_;
479 CancelationSignal cancelation_signal_;
483 scoped_ptr<SyncSession> session_;
484 scoped_ptr<SyncSessionContext> context_;
485 bool saw_syncer_event_;
486 base::TimeDelta last_short_poll_interval_received_;
487 base::TimeDelta last_long_poll_interval_received_;
488 base::TimeDelta last_sessions_commit_delay_seconds_;
489 int last_client_invalidation_hint_buffer_size_;
490 scoped_refptr<ModelSafeWorker> worker_;
492 ModelTypeSet enabled_datatypes_;
493 TrafficRecorder traffic_recorder_;
494 sessions::NudgeTracker nudge_tracker_;
496 DISALLOW_COPY_AND_ASSIGN(SyncerTest);
499 TEST_F(SyncerTest, TestCallGatherUnsyncedEntries) {
501 Syncer::UnsyncedMetaHandles handles;
503 syncable::ReadTransaction trans(FROM_HERE, directory());
504 GetUnsyncedEntries(&trans, &handles);
506 ASSERT_EQ(0u, handles.size());
508 // TODO(sync): When we can dynamically connect and disconnect the mock
509 // ServerConnectionManager test disconnected GetUnsyncedEntries here. It's a
510 // regression for a very old bug.
513 TEST_F(SyncerTest, GetCommitIdsFiltersThrottledEntries) {
514 const ModelTypeSet throttled_types(BOOKMARKS);
515 sync_pb::EntitySpecifics bookmark_data;
516 AddDefaultFieldValue(BOOKMARKS, &bookmark_data);
518 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
519 foreign_cache_guid(), "-1");
523 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
524 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
525 ASSERT_TRUE(A.good());
526 A.PutIsUnsynced(true);
527 A.PutSpecifics(bookmark_data);
528 A.PutNonUniqueName("bookmark");
531 // Now sync without enabling bookmarks.
532 syncer_->NormalSyncShare(
533 Difference(GetRoutingInfoTypes(context_->routing_info()),
534 ModelTypeSet(BOOKMARKS)),
539 // Nothing should have been committed as bookmarks is throttled.
540 syncable::ReadTransaction rtrans(FROM_HERE, directory());
541 Entry entryA(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1));
542 ASSERT_TRUE(entryA.good());
543 EXPECT_TRUE(entryA.GetIsUnsynced());
546 // Sync again with bookmarks enabled.
547 syncer_->NormalSyncShare(
548 GetRoutingInfoTypes(context_->routing_info()),
553 // It should have been committed.
554 syncable::ReadTransaction rtrans(FROM_HERE, directory());
555 Entry entryA(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1));
556 ASSERT_TRUE(entryA.good());
557 EXPECT_FALSE(entryA.GetIsUnsynced());
561 // We use a macro so we can preserve the error location.
562 #define VERIFY_ENTRY(id, is_unapplied, is_unsynced, prev_initialized, \
563 parent_id, version, server_version, id_fac, rtrans) \
565 Entry entryA(rtrans, syncable::GET_BY_ID, id_fac.FromNumber(id)); \
566 ASSERT_TRUE(entryA.good()); \
567 /* We don't use EXPECT_EQ here because when the left side param is false,
568 gcc 4.6 warns about converting 'false' to pointer type for argument 1. */ \
569 EXPECT_TRUE(is_unsynced == entryA.GetIsUnsynced()); \
570 EXPECT_TRUE(is_unapplied == entryA.GetIsUnappliedUpdate()); \
571 EXPECT_TRUE(prev_initialized == \
572 IsRealDataType(GetModelTypeFromSpecifics( \
573 entryA.GetBaseServerSpecifics()))); \
574 EXPECT_TRUE(parent_id == -1 || \
575 entryA.GetParentId()== id_fac.FromNumber(parent_id)); \
576 EXPECT_EQ(version, entryA.GetBaseVersion()); \
577 EXPECT_EQ(server_version, entryA.GetServerVersion()); \
580 TEST_F(SyncerTest, GetCommitIdsFiltersUnreadyEntries) {
581 KeyParams key_params = {"localhost", "dummy", "foobar"};
582 KeyParams other_params = {"localhost", "dummy", "foobar2"};
583 sync_pb::EntitySpecifics bookmark, encrypted_bookmark;
584 bookmark.mutable_bookmark()->set_url("url");
585 bookmark.mutable_bookmark()->set_title("title");
586 AddDefaultFieldValue(BOOKMARKS, &encrypted_bookmark);
587 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
588 foreign_cache_guid(), "-1");
589 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10,
590 foreign_cache_guid(), "-2");
591 mock_server_->AddUpdateDirectory(3, 0, "C", 10, 10,
592 foreign_cache_guid(), "-3");
593 mock_server_->AddUpdateDirectory(4, 0, "D", 10, 10,
594 foreign_cache_guid(), "-4");
596 // Server side change will put A in conflict.
597 mock_server_->AddUpdateDirectory(1, 0, "A", 20, 20,
598 foreign_cache_guid(), "-1");
600 // Mark bookmarks as encrypted and set the cryptographer to have pending
602 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
603 Cryptographer other_cryptographer(&encryptor_);
604 other_cryptographer.AddKey(other_params);
605 sync_pb::EntitySpecifics specifics;
606 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
607 other_cryptographer.GetKeys(nigori->mutable_encryption_keybag());
608 dir_maker_.encryption_handler()->EnableEncryptEverything();
609 // Set up with an old passphrase, but have pending keys
610 GetCryptographer(&wtrans)->AddKey(key_params);
611 GetCryptographer(&wtrans)->Encrypt(bookmark,
612 encrypted_bookmark.mutable_encrypted());
613 GetCryptographer(&wtrans)->SetPendingKeys(nigori->encryption_keybag());
615 // In conflict but properly encrypted.
616 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
617 ASSERT_TRUE(A.good());
618 A.PutIsUnsynced(true);
619 A.PutSpecifics(encrypted_bookmark);
620 A.PutNonUniqueName(kEncryptedString);
621 // Not in conflict and properly encrypted.
622 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
623 ASSERT_TRUE(B.good());
624 B.PutIsUnsynced(true);
625 B.PutSpecifics(encrypted_bookmark);
626 B.PutNonUniqueName(kEncryptedString);
627 // Unencrypted specifics.
628 MutableEntry C(&wtrans, GET_BY_ID, ids_.FromNumber(3));
629 ASSERT_TRUE(C.good());
630 C.PutIsUnsynced(true);
631 C.PutNonUniqueName(kEncryptedString);
632 // Unencrypted non_unique_name.
633 MutableEntry D(&wtrans, GET_BY_ID, ids_.FromNumber(4));
634 ASSERT_TRUE(D.good());
635 D.PutIsUnsynced(true);
636 D.PutSpecifics(encrypted_bookmark);
637 D.PutNonUniqueName("not encrypted");
641 // Nothing should have commited due to bookmarks being encrypted and
642 // the cryptographer having pending keys. A would have been resolved
643 // as a simple conflict, but still be unsynced until the next sync cycle.
644 syncable::ReadTransaction rtrans(FROM_HERE, directory());
645 VERIFY_ENTRY(1, false, true, false, 0, 20, 20, ids_, &rtrans);
646 VERIFY_ENTRY(2, false, true, false, 0, 10, 10, ids_, &rtrans);
647 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_, &rtrans);
648 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_, &rtrans);
650 // Resolve the pending keys.
651 GetCryptographer(&rtrans)->DecryptPendingKeys(other_params);
655 // All properly encrypted and non-conflicting items should commit. "A" was
656 // conflicting, but last sync cycle resolved it as simple conflict, so on
657 // this sync cycle it committed succesfullly.
658 syncable::ReadTransaction rtrans(FROM_HERE, directory());
659 // Committed successfully.
660 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_, &rtrans);
661 // Committed successfully.
662 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_, &rtrans);
663 // Was not properly encrypted.
664 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_, &rtrans);
665 // Was not properly encrypted.
666 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_, &rtrans);
669 // Fix the remaining items.
670 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
671 MutableEntry C(&wtrans, GET_BY_ID, ids_.FromNumber(3));
672 ASSERT_TRUE(C.good());
673 C.PutSpecifics(encrypted_bookmark);
674 C.PutNonUniqueName(kEncryptedString);
675 MutableEntry D(&wtrans, GET_BY_ID, ids_.FromNumber(4));
676 ASSERT_TRUE(D.good());
677 D.PutSpecifics(encrypted_bookmark);
678 D.PutNonUniqueName(kEncryptedString);
682 const StatusController& status_controller = session_->status_controller();
684 EXPECT_EQ(status_controller.model_neutral_state().commit_result, SYNCER_OK);
685 // None should be unsynced anymore.
686 syncable::ReadTransaction rtrans(FROM_HERE, directory());
687 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_, &rtrans);
688 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_, &rtrans);
689 VERIFY_ENTRY(3, false, false, false, 0, 11, 11, ids_, &rtrans);
690 VERIFY_ENTRY(4, false, false, false, 0, 11, 11, ids_, &rtrans);
694 TEST_F(SyncerTest, EncryptionAwareConflicts) {
695 KeyParams key_params = {"localhost", "dummy", "foobar"};
696 Cryptographer other_cryptographer(&encryptor_);
697 other_cryptographer.AddKey(key_params);
698 sync_pb::EntitySpecifics bookmark, encrypted_bookmark, modified_bookmark;
699 bookmark.mutable_bookmark()->set_title("title");
700 other_cryptographer.Encrypt(bookmark,
701 encrypted_bookmark.mutable_encrypted());
702 AddDefaultFieldValue(BOOKMARKS, &encrypted_bookmark);
703 modified_bookmark.mutable_bookmark()->set_title("title2");
704 other_cryptographer.Encrypt(modified_bookmark,
705 modified_bookmark.mutable_encrypted());
706 sync_pb::EntitySpecifics pref, encrypted_pref, modified_pref;
707 pref.mutable_preference()->set_name("name");
708 AddDefaultFieldValue(PREFERENCES, &encrypted_pref);
709 other_cryptographer.Encrypt(pref,
710 encrypted_pref.mutable_encrypted());
711 modified_pref.mutable_preference()->set_name("name2");
712 other_cryptographer.Encrypt(modified_pref,
713 modified_pref.mutable_encrypted());
715 // Mark bookmarks and preferences as encrypted and set the cryptographer to
716 // have pending keys.
717 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
718 sync_pb::EntitySpecifics specifics;
719 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
720 other_cryptographer.GetKeys(nigori->mutable_encryption_keybag());
721 dir_maker_.encryption_handler()->EnableEncryptEverything();
722 GetCryptographer(&wtrans)->SetPendingKeys(nigori->encryption_keybag());
723 EXPECT_TRUE(GetCryptographer(&wtrans)->has_pending_keys());
726 // We need to remember the exact position of our local items, so we can
727 // make updates that do not modify those positions.
732 mock_server_->AddUpdateSpecifics(1, 0, "A", 10, 10, true, 0, bookmark,
733 foreign_cache_guid(), "-1");
734 mock_server_->AddUpdateSpecifics(2, 1, "B", 10, 10, false, 2, bookmark,
735 foreign_cache_guid(), "-2");
736 mock_server_->AddUpdateSpecifics(3, 1, "C", 10, 10, false, 1, bookmark,
737 foreign_cache_guid(), "-3");
738 mock_server_->AddUpdateSpecifics(4, 0, "D", 10, 10, false, 0, pref);
741 // Initial state. Everything is normal.
742 syncable::ReadTransaction rtrans(FROM_HERE, directory());
743 VERIFY_ENTRY(1, false, false, false, 0, 10, 10, ids_, &rtrans);
744 VERIFY_ENTRY(2, false, false, false, 1, 10, 10, ids_, &rtrans);
745 VERIFY_ENTRY(3, false, false, false, 1, 10, 10, ids_, &rtrans);
746 VERIFY_ENTRY(4, false, false, false, 0, 10, 10, ids_, &rtrans);
748 Entry entry1(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1));
749 ASSERT_TRUE(entry1.GetUniquePosition().Equals(
750 entry1.GetServerUniquePosition()));
751 pos1 = entry1.GetUniquePosition();
752 Entry entry2(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(2));
753 pos2 = entry2.GetUniquePosition();
754 Entry entry3(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(3));
755 pos3 = entry3.GetUniquePosition();
758 // Server side encryption will not be applied due to undecryptable data.
759 // At this point, BASE_SERVER_SPECIFICS should be filled for all four items.
760 mock_server_->AddUpdateSpecifics(1, 0, kEncryptedString, 20, 20, true, 0,
762 foreign_cache_guid(), "-1");
763 mock_server_->AddUpdateSpecifics(2, 1, kEncryptedString, 20, 20, false, 2,
765 foreign_cache_guid(), "-2");
766 mock_server_->AddUpdateSpecifics(3, 1, kEncryptedString, 20, 20, false, 1,
768 foreign_cache_guid(), "-3");
769 mock_server_->AddUpdateSpecifics(4, 0, kEncryptedString, 20, 20, false, 0,
771 foreign_cache_guid(), "-4");
774 // All should be unapplied due to being undecryptable and have a valid
775 // BASE_SERVER_SPECIFICS.
776 syncable::ReadTransaction rtrans(FROM_HERE, directory());
777 VERIFY_ENTRY(1, true, false, true, 0, 10, 20, ids_, &rtrans);
778 VERIFY_ENTRY(2, true, false, true, 1, 10, 20, ids_, &rtrans);
779 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_, &rtrans);
780 VERIFY_ENTRY(4, true, false, true, 0, 10, 20, ids_, &rtrans);
783 // Server side change that don't modify anything should not affect
784 // BASE_SERVER_SPECIFICS (such as name changes and mtime changes).
785 mock_server_->AddUpdateSpecifics(1, 0, kEncryptedString, 30, 30, true, 0,
787 foreign_cache_guid(), "-1");
788 mock_server_->AddUpdateSpecifics(2, 1, kEncryptedString, 30, 30, false, 2,
790 foreign_cache_guid(), "-2");
791 // Item 3 doesn't change.
792 mock_server_->AddUpdateSpecifics(4, 0, kEncryptedString, 30, 30, false, 0,
794 foreign_cache_guid(), "-4");
797 // Items 1, 2, and 4 should have newer server versions, 3 remains the same.
798 // All should remain unapplied due to be undecryptable.
799 syncable::ReadTransaction rtrans(FROM_HERE, directory());
800 VERIFY_ENTRY(1, true, false, true, 0, 10, 30, ids_, &rtrans);
801 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_, &rtrans);
802 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_, &rtrans);
803 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_, &rtrans);
806 // Positional changes, parent changes, and specifics changes should reset
807 // BASE_SERVER_SPECIFICS.
808 // Became unencrypted.
809 mock_server_->AddUpdateSpecifics(1, 0, "A", 40, 40, true, 0, bookmark,
810 foreign_cache_guid(), "-1");
811 // Reordered to after item 2.
812 mock_server_->AddUpdateSpecifics(3, 1, kEncryptedString, 30, 30, false, 3,
814 foreign_cache_guid(), "-3");
817 // Items 2 and 4 should be the only ones with BASE_SERVER_SPECIFICS set.
818 // Items 1 is now unencrypted, so should have applied normally.
819 syncable::ReadTransaction rtrans(FROM_HERE, directory());
820 VERIFY_ENTRY(1, false, false, false, 0, 40, 40, ids_, &rtrans);
821 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_, &rtrans);
822 VERIFY_ENTRY(3, true, false, false, 1, 10, 30, ids_, &rtrans);
823 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_, &rtrans);
826 // Make local changes, which should remain unsynced for items 2, 3, 4.
828 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
829 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
830 ASSERT_TRUE(A.good());
831 A.PutSpecifics(modified_bookmark);
832 A.PutNonUniqueName(kEncryptedString);
833 A.PutIsUnsynced(true);
834 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
835 ASSERT_TRUE(B.good());
836 B.PutSpecifics(modified_bookmark);
837 B.PutNonUniqueName(kEncryptedString);
838 B.PutIsUnsynced(true);
839 MutableEntry C(&wtrans, GET_BY_ID, ids_.FromNumber(3));
840 ASSERT_TRUE(C.good());
841 C.PutSpecifics(modified_bookmark);
842 C.PutNonUniqueName(kEncryptedString);
843 C.PutIsUnsynced(true);
844 MutableEntry D(&wtrans, GET_BY_ID, ids_.FromNumber(4));
845 ASSERT_TRUE(D.good());
846 D.PutSpecifics(modified_pref);
847 D.PutNonUniqueName(kEncryptedString);
848 D.PutIsUnsynced(true);
852 // Item 1 remains unsynced due to there being pending keys.
853 // Items 2, 3, 4 should remain unsynced since they were not up to date.
854 syncable::ReadTransaction rtrans(FROM_HERE, directory());
855 VERIFY_ENTRY(1, false, true, false, 0, 40, 40, ids_, &rtrans);
856 VERIFY_ENTRY(2, true, true, true, 1, 10, 30, ids_, &rtrans);
857 VERIFY_ENTRY(3, true, true, false, 1, 10, 30, ids_, &rtrans);
858 VERIFY_ENTRY(4, true, true, true, 0, 10, 30, ids_, &rtrans);
862 syncable::ReadTransaction rtrans(FROM_HERE, directory());
863 // Resolve the pending keys.
864 GetCryptographer(&rtrans)->DecryptPendingKeys(key_params);
866 // First cycle resolves conflicts, second cycle commits changes.
868 EXPECT_EQ(2, status().model_neutral_state().num_server_overwrites);
869 EXPECT_EQ(1, status().model_neutral_state().num_local_overwrites);
870 // We successfully commited item(s).
871 EXPECT_EQ(status().model_neutral_state().commit_result, SYNCER_OK);
874 // Everything should be resolved now. The local changes should have
875 // overwritten the server changes for 2 and 4, while the server changes
876 // overwrote the local for entry 3.
877 EXPECT_EQ(0, status().model_neutral_state().num_server_overwrites);
878 EXPECT_EQ(0, status().model_neutral_state().num_local_overwrites);
879 EXPECT_EQ(status().model_neutral_state().commit_result, SYNCER_OK);
880 syncable::ReadTransaction rtrans(FROM_HERE, directory());
881 VERIFY_ENTRY(1, false, false, false, 0, 41, 41, ids_, &rtrans);
882 VERIFY_ENTRY(2, false, false, false, 1, 31, 31, ids_, &rtrans);
883 VERIFY_ENTRY(3, false, false, false, 1, 30, 30, ids_, &rtrans);
884 VERIFY_ENTRY(4, false, false, false, 0, 31, 31, ids_, &rtrans);
889 TEST_F(SyncerTest, TestGetUnsyncedAndSimpleCommit) {
891 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
892 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Pete");
893 ASSERT_TRUE(parent.good());
894 parent.PutIsUnsynced(true);
895 parent.PutIsDir(true);
896 parent.PutSpecifics(DefaultBookmarkSpecifics());
897 parent.PutBaseVersion(1);
898 parent.PutId(parent_id_);
899 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent_id_, "Pete");
900 ASSERT_TRUE(child.good());
901 child.PutId(child_id_);
902 child.PutBaseVersion(1);
903 WriteTestDataToEntry(&wtrans, &child);
907 ASSERT_EQ(2u, mock_server_->committed_ids().size());
908 // If this test starts failing, be aware other sort orders could be valid.
909 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
910 EXPECT_TRUE(child_id_ == mock_server_->committed_ids()[1]);
912 syncable::ReadTransaction rt(FROM_HERE, directory());
913 Entry entry(&rt, syncable::GET_BY_ID, child_id_);
914 ASSERT_TRUE(entry.good());
915 VerifyTestDataInEntry(&rt, &entry);
919 TEST_F(SyncerTest, TestPurgeWhileUnsynced) {
920 // Similar to above, but throw a purge operation into the mix. Bug 49278.
921 syncable::Id pref_node_id = TestIdFactory::MakeServer("Tim");
923 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
924 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Pete");
925 ASSERT_TRUE(parent.good());
926 parent.PutIsUnsynced(true);
927 parent.PutIsDir(true);
928 parent.PutSpecifics(DefaultBookmarkSpecifics());
929 parent.PutBaseVersion(1);
930 parent.PutId(parent_id_);
931 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent_id_, "Pete");
932 ASSERT_TRUE(child.good());
933 child.PutId(child_id_);
934 child.PutBaseVersion(1);
935 WriteTestDataToEntry(&wtrans, &child);
937 MutableEntry parent2(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Tim");
938 ASSERT_TRUE(parent2.good());
939 parent2.PutIsUnsynced(true);
940 parent2.PutIsDir(true);
941 parent2.PutSpecifics(DefaultPreferencesSpecifics());
942 parent2.PutBaseVersion(1);
943 parent2.PutId(pref_node_id);
946 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES),
951 ASSERT_EQ(2U, mock_server_->committed_ids().size());
952 // If this test starts failing, be aware other sort orders could be valid.
953 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
954 EXPECT_TRUE(child_id_ == mock_server_->committed_ids()[1]);
956 syncable::ReadTransaction rt(FROM_HERE, directory());
957 Entry entry(&rt, syncable::GET_BY_ID, child_id_);
958 ASSERT_TRUE(entry.good());
959 VerifyTestDataInEntry(&rt, &entry);
961 directory()->SaveChanges();
963 syncable::ReadTransaction rt(FROM_HERE, directory());
964 Entry entry(&rt, syncable::GET_BY_ID, pref_node_id);
965 ASSERT_FALSE(entry.good());
969 TEST_F(SyncerTest, TestPurgeWhileUnapplied) {
970 // Similar to above, but for unapplied items. Bug 49278.
972 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
973 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Pete");
974 ASSERT_TRUE(parent.good());
975 parent.PutIsUnappliedUpdate(true);
976 parent.PutIsDir(true);
977 parent.PutSpecifics(DefaultBookmarkSpecifics());
978 parent.PutBaseVersion(1);
979 parent.PutId(parent_id_);
982 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(BOOKMARKS),
987 directory()->SaveChanges();
989 syncable::ReadTransaction rt(FROM_HERE, directory());
990 Entry entry(&rt, syncable::GET_BY_ID, parent_id_);
991 ASSERT_FALSE(entry.good());
995 TEST_F(SyncerTest, TestPurgeWithJournal) {
997 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
998 MutableEntry parent(&wtrans, syncable::CREATE, BOOKMARKS, wtrans.root_id(),
1000 ASSERT_TRUE(parent.good());
1001 parent.PutIsDir(true);
1002 parent.PutSpecifics(DefaultBookmarkSpecifics());
1003 parent.PutBaseVersion(1);
1004 parent.PutId(parent_id_);
1005 MutableEntry child(&wtrans, syncable::CREATE, BOOKMARKS, parent_id_,
1007 ASSERT_TRUE(child.good());
1008 child.PutId(child_id_);
1009 child.PutBaseVersion(1);
1010 WriteTestDataToEntry(&wtrans, &child);
1012 MutableEntry parent2(&wtrans, syncable::CREATE, PREFERENCES,
1013 wtrans.root_id(), "Tim");
1014 ASSERT_TRUE(parent2.good());
1015 parent2.PutIsDir(true);
1016 parent2.PutSpecifics(DefaultPreferencesSpecifics());
1017 parent2.PutBaseVersion(1);
1018 parent2.PutId(TestIdFactory::MakeServer("Tim"));
1021 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES, BOOKMARKS),
1022 ModelTypeSet(BOOKMARKS),
1025 // Verify bookmark nodes are saved in delete journal but not preference
1027 syncable::ReadTransaction rt(FROM_HERE, directory());
1028 syncable::DeleteJournal* delete_journal = directory()->delete_journal();
1029 EXPECT_EQ(2u, delete_journal->GetDeleteJournalSize(&rt));
1030 syncable::EntryKernelSet journal_entries;
1031 directory()->delete_journal()->GetDeleteJournals(&rt, BOOKMARKS,
1033 EXPECT_EQ(parent_id_, (*journal_entries.begin())->ref(syncable::ID));
1034 EXPECT_EQ(child_id_, (*journal_entries.rbegin())->ref(syncable::ID));
1038 TEST_F(SyncerTest, TestCommitListOrderingTwoItemsTall) {
1039 CommitOrderingTest items[] = {
1040 {1, ids_.FromNumber(-1001), ids_.FromNumber(-1000)},
1041 {0, ids_.FromNumber(-1000), ids_.FromNumber(0)},
1042 CommitOrderingTest::MakeLastCommitItem(),
1044 RunCommitOrderingTest(items);
1047 TEST_F(SyncerTest, TestCommitListOrderingThreeItemsTall) {
1048 CommitOrderingTest items[] = {
1049 {1, ids_.FromNumber(-2001), ids_.FromNumber(-2000)},
1050 {0, ids_.FromNumber(-2000), ids_.FromNumber(0)},
1051 {2, ids_.FromNumber(-2002), ids_.FromNumber(-2001)},
1052 CommitOrderingTest::MakeLastCommitItem(),
1054 RunCommitOrderingTest(items);
1057 TEST_F(SyncerTest, TestCommitListOrderingFourItemsTall) {
1058 CommitOrderingTest items[] = {
1059 {3, ids_.FromNumber(-2003), ids_.FromNumber(-2002)},
1060 {1, ids_.FromNumber(-2001), ids_.FromNumber(-2000)},
1061 {0, ids_.FromNumber(-2000), ids_.FromNumber(0)},
1062 {2, ids_.FromNumber(-2002), ids_.FromNumber(-2001)},
1063 CommitOrderingTest::MakeLastCommitItem(),
1065 RunCommitOrderingTest(items);
1068 TEST_F(SyncerTest, TestCommitListOrderingThreeItemsTallLimitedSize) {
1069 context_->set_max_commit_batch_size(2);
1070 CommitOrderingTest items[] = {
1071 {1, ids_.FromNumber(-2001), ids_.FromNumber(-2000)},
1072 {0, ids_.FromNumber(-2000), ids_.FromNumber(0)},
1073 {2, ids_.FromNumber(-2002), ids_.FromNumber(-2001)},
1074 CommitOrderingTest::MakeLastCommitItem(),
1076 RunCommitOrderingTest(items);
1079 TEST_F(SyncerTest, TestCommitListOrderingSingleDeletedItem) {
1080 CommitOrderingTest items[] = {
1081 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED}},
1082 CommitOrderingTest::MakeLastCommitItem(),
1084 RunCommitOrderingTest(items);
1087 TEST_F(SyncerTest, TestCommitListOrderingSingleUncommittedDeletedItem) {
1088 CommitOrderingTest items[] = {
1089 {-1, ids_.FromNumber(-1000), ids_.FromNumber(0), {DELETED}},
1090 CommitOrderingTest::MakeLastCommitItem(),
1092 RunCommitOrderingTest(items);
1095 TEST_F(SyncerTest, TestCommitListOrderingSingleDeletedItemWithUnroll) {
1096 CommitOrderingTest items[] = {
1097 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED}},
1098 CommitOrderingTest::MakeLastCommitItem(),
1100 RunCommitOrderingTest(items);
1104 TestCommitListOrderingSingleLongDeletedItemWithUnroll) {
1105 CommitOrderingTest items[] = {
1106 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1107 CommitOrderingTest::MakeLastCommitItem(),
1109 RunCommitOrderingTest(items);
1112 TEST_F(SyncerTest, TestCommitListOrderingTwoLongDeletedItemWithUnroll) {
1113 CommitOrderingTest items[] = {
1114 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1115 {-1, ids_.FromNumber(1001), ids_.FromNumber(1000), {DELETED, OLD_MTIME}},
1116 CommitOrderingTest::MakeLastCommitItem(),
1118 RunCommitOrderingTest(items);
1121 TEST_F(SyncerTest, TestCommitListOrdering3LongDeletedItemsWithSizeLimit) {
1122 context_->set_max_commit_batch_size(2);
1123 CommitOrderingTest items[] = {
1124 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1125 {1, ids_.FromNumber(1001), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1126 {2, ids_.FromNumber(1002), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1127 CommitOrderingTest::MakeLastCommitItem(),
1129 RunCommitOrderingTest(items);
1132 TEST_F(SyncerTest, TestCommitListOrderingTwoDeletedItemsWithUnroll) {
1133 CommitOrderingTest items[] = {
1134 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED}},
1135 {-1, ids_.FromNumber(1001), ids_.FromNumber(1000), {DELETED}},
1136 CommitOrderingTest::MakeLastCommitItem(),
1138 RunCommitOrderingTest(items);
1141 TEST_F(SyncerTest, TestCommitListOrderingComplexDeletionScenario) {
1142 CommitOrderingTest items[] = {
1143 { 0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1144 {-1, ids_.FromNumber(1001), ids_.FromNumber(0), {SYNCED}},
1145 {1, ids_.FromNumber(1002), ids_.FromNumber(1001), {DELETED, OLD_MTIME}},
1146 {-1, ids_.FromNumber(1003), ids_.FromNumber(1001), {SYNCED}},
1147 {2, ids_.FromNumber(1004), ids_.FromNumber(1003), {DELETED}},
1148 CommitOrderingTest::MakeLastCommitItem(),
1150 RunCommitOrderingTest(items);
1154 TestCommitListOrderingComplexDeletionScenarioWith2RecentDeletes) {
1155 CommitOrderingTest items[] = {
1156 { 0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1157 {-1, ids_.FromNumber(1001), ids_.FromNumber(0), {SYNCED}},
1158 {1, ids_.FromNumber(1002), ids_.FromNumber(1001), {DELETED, OLD_MTIME}},
1159 {-1, ids_.FromNumber(1003), ids_.FromNumber(1001), {SYNCED}},
1160 {2, ids_.FromNumber(1004), ids_.FromNumber(1003), {DELETED}},
1161 {3, ids_.FromNumber(1005), ids_.FromNumber(1003), {DELETED}},
1162 CommitOrderingTest::MakeLastCommitItem(),
1164 RunCommitOrderingTest(items);
1167 TEST_F(SyncerTest, TestCommitListOrderingDeleteMovedItems) {
1168 CommitOrderingTest items[] = {
1169 {1, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1170 {0, ids_.FromNumber(1001), ids_.FromNumber(1000), {DELETED, OLD_MTIME,
1172 CommitOrderingTest::MakeLastCommitItem(),
1174 RunCommitOrderingTest(items);
1177 TEST_F(SyncerTest, TestCommitListOrderingWithNesting) {
1178 const base::Time& now_minus_2h =
1179 base::Time::Now() - base::TimeDelta::FromHours(2);
1181 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1183 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Bob");
1184 ASSERT_TRUE(parent.good());
1185 parent.PutIsUnsynced(true);
1186 parent.PutIsDir(true);
1187 parent.PutSpecifics(DefaultBookmarkSpecifics());
1188 parent.PutId(ids_.FromNumber(100));
1189 parent.PutBaseVersion(1);
1191 &wtrans, CREATE, BOOKMARKS, ids_.FromNumber(100), "Bob");
1192 ASSERT_TRUE(child.good());
1193 child.PutIsUnsynced(true);
1194 child.PutIsDir(true);
1195 child.PutSpecifics(DefaultBookmarkSpecifics());
1196 child.PutId(ids_.FromNumber(101));
1197 child.PutBaseVersion(1);
1198 MutableEntry grandchild(
1199 &wtrans, CREATE, BOOKMARKS, ids_.FromNumber(101), "Bob");
1200 ASSERT_TRUE(grandchild.good());
1201 grandchild.PutId(ids_.FromNumber(102));
1202 grandchild.PutIsUnsynced(true);
1203 grandchild.PutSpecifics(DefaultBookmarkSpecifics());
1204 grandchild.PutBaseVersion(1);
1207 // Create three deleted items which deletions we expect to be sent to the
1209 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Pete");
1210 ASSERT_TRUE(parent.good());
1211 parent.PutId(ids_.FromNumber(103));
1212 parent.PutIsUnsynced(true);
1213 parent.PutIsDir(true);
1214 parent.PutSpecifics(DefaultBookmarkSpecifics());
1215 parent.PutIsDel(true);
1216 parent.PutBaseVersion(1);
1217 parent.PutMtime(now_minus_2h);
1219 &wtrans, CREATE, BOOKMARKS, ids_.FromNumber(103), "Pete");
1220 ASSERT_TRUE(child.good());
1221 child.PutId(ids_.FromNumber(104));
1222 child.PutIsUnsynced(true);
1223 child.PutIsDir(true);
1224 child.PutSpecifics(DefaultBookmarkSpecifics());
1225 child.PutIsDel(true);
1226 child.PutBaseVersion(1);
1227 child.PutMtime(now_minus_2h);
1228 MutableEntry grandchild(
1229 &wtrans, CREATE, BOOKMARKS, ids_.FromNumber(104), "Pete");
1230 ASSERT_TRUE(grandchild.good());
1231 grandchild.PutId(ids_.FromNumber(105));
1232 grandchild.PutIsUnsynced(true);
1233 grandchild.PutIsDel(true);
1234 grandchild.PutIsDir(false);
1235 grandchild.PutSpecifics(DefaultBookmarkSpecifics());
1236 grandchild.PutBaseVersion(1);
1237 grandchild.PutMtime(now_minus_2h);
1242 ASSERT_EQ(6u, mock_server_->committed_ids().size());
1243 // This test will NOT unroll deletes because SERVER_PARENT_ID is not set.
1244 // It will treat these like moves.
1245 vector<syncable::Id> commit_ids(mock_server_->committed_ids());
1246 EXPECT_TRUE(ids_.FromNumber(100) == commit_ids[0]);
1247 EXPECT_TRUE(ids_.FromNumber(101) == commit_ids[1]);
1248 EXPECT_TRUE(ids_.FromNumber(102) == commit_ids[2]);
1249 // We don't guarantee the delete orders in this test, only that they occur
1251 std::sort(commit_ids.begin() + 3, commit_ids.end());
1252 EXPECT_TRUE(ids_.FromNumber(103) == commit_ids[3]);
1253 EXPECT_TRUE(ids_.FromNumber(104) == commit_ids[4]);
1254 EXPECT_TRUE(ids_.FromNumber(105) == commit_ids[5]);
1257 TEST_F(SyncerTest, TestCommitListOrderingWithNewItems) {
1258 syncable::Id parent1_id = ids_.MakeServer("p1");
1259 syncable::Id parent2_id = ids_.MakeServer("p2");
1262 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1263 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "1");
1264 ASSERT_TRUE(parent.good());
1265 parent.PutIsUnsynced(true);
1266 parent.PutIsDir(true);
1267 parent.PutSpecifics(DefaultBookmarkSpecifics());
1268 parent.PutId(parent1_id);
1269 MutableEntry child(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "2");
1270 ASSERT_TRUE(child.good());
1271 child.PutIsUnsynced(true);
1272 child.PutIsDir(true);
1273 child.PutSpecifics(DefaultBookmarkSpecifics());
1274 child.PutId(parent2_id);
1275 parent.PutBaseVersion(1);
1276 child.PutBaseVersion(1);
1279 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1280 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, parent1_id, "A");
1281 ASSERT_TRUE(parent.good());
1282 parent.PutIsUnsynced(true);
1283 parent.PutIsDir(true);
1284 parent.PutSpecifics(DefaultBookmarkSpecifics());
1285 parent.PutId(ids_.FromNumber(102));
1286 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent1_id, "B");
1287 ASSERT_TRUE(child.good());
1288 child.PutIsUnsynced(true);
1289 child.PutIsDir(true);
1290 child.PutSpecifics(DefaultBookmarkSpecifics());
1291 child.PutId(ids_.FromNumber(-103));
1292 parent.PutBaseVersion(1);
1295 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1296 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, parent2_id, "A");
1297 ASSERT_TRUE(parent.good());
1298 parent.PutIsUnsynced(true);
1299 parent.PutIsDir(true);
1300 parent.PutSpecifics(DefaultBookmarkSpecifics());
1301 parent.PutId(ids_.FromNumber(-104));
1302 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent2_id, "B");
1303 ASSERT_TRUE(child.good());
1304 child.PutIsUnsynced(true);
1305 child.PutIsDir(true);
1306 child.PutSpecifics(DefaultBookmarkSpecifics());
1307 child.PutId(ids_.FromNumber(105));
1308 child.PutBaseVersion(1);
1312 ASSERT_EQ(6u, mock_server_->committed_ids().size());
1314 // This strange iteration and std::count() usage is to allow the order to
1315 // vary. All we really care about is that parent1_id and parent2_id are the
1316 // first two IDs, and that the children make up the next four. Other than
1317 // that, ordering doesn't matter.
1319 vector<syncable::Id>::const_iterator i =
1320 mock_server_->committed_ids().begin();
1321 vector<syncable::Id>::const_iterator parents_begin = i;
1324 vector<syncable::Id>::const_iterator parents_end = i;
1325 vector<syncable::Id>::const_iterator children_begin = i;
1326 vector<syncable::Id>::const_iterator children_end =
1327 mock_server_->committed_ids().end();
1329 EXPECT_EQ(1, count(parents_begin, parents_end, parent1_id));
1330 EXPECT_EQ(1, count(parents_begin, parents_end, parent2_id));
1332 EXPECT_EQ(1, count(children_begin, children_end, ids_.FromNumber(-103)));
1333 EXPECT_EQ(1, count(children_begin, children_end, ids_.FromNumber(102)));
1334 EXPECT_EQ(1, count(children_begin, children_end, ids_.FromNumber(105)));
1335 EXPECT_EQ(1, count(children_begin, children_end, ids_.FromNumber(-104)));
1338 TEST_F(SyncerTest, TestCommitListOrderingCounterexample) {
1339 syncable::Id child2_id = ids_.NewServerId();
1342 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1343 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "P");
1344 ASSERT_TRUE(parent.good());
1345 parent.PutIsUnsynced(true);
1346 parent.PutIsDir(true);
1347 parent.PutSpecifics(DefaultBookmarkSpecifics());
1348 parent.PutId(parent_id_);
1349 MutableEntry child1(&wtrans, CREATE, BOOKMARKS, parent_id_, "1");
1350 ASSERT_TRUE(child1.good());
1351 child1.PutIsUnsynced(true);
1352 child1.PutId(child_id_);
1353 child1.PutSpecifics(DefaultBookmarkSpecifics());
1354 MutableEntry child2(&wtrans, CREATE, BOOKMARKS, parent_id_, "2");
1355 ASSERT_TRUE(child2.good());
1356 child2.PutIsUnsynced(true);
1357 child2.PutSpecifics(DefaultBookmarkSpecifics());
1358 child2.PutId(child2_id);
1360 parent.PutBaseVersion(1);
1361 child1.PutBaseVersion(1);
1362 child2.PutBaseVersion(1);
1366 ASSERT_EQ(3u, mock_server_->committed_ids().size());
1367 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
1368 // There are two possible valid orderings.
1369 if (child2_id == mock_server_->committed_ids()[1]) {
1370 EXPECT_TRUE(child2_id == mock_server_->committed_ids()[1]);
1371 EXPECT_TRUE(child_id_ == mock_server_->committed_ids()[2]);
1373 EXPECT_TRUE(child_id_ == mock_server_->committed_ids()[1]);
1374 EXPECT_TRUE(child2_id == mock_server_->committed_ids()[2]);
1378 TEST_F(SyncerTest, TestCommitListOrderingAndNewParent) {
1379 string parent1_name = "1";
1380 string parent2_name = "A";
1381 string child_name = "B";
1384 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1385 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(),
1387 ASSERT_TRUE(parent.good());
1388 parent.PutIsUnsynced(true);
1389 parent.PutIsDir(true);
1390 parent.PutSpecifics(DefaultBookmarkSpecifics());
1391 parent.PutId(parent_id_);
1392 parent.PutBaseVersion(1);
1395 syncable::Id parent2_id = ids_.NewLocalId();
1396 syncable::Id child_id = ids_.NewServerId();
1398 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1399 MutableEntry parent2(
1400 &wtrans, CREATE, BOOKMARKS, parent_id_, parent2_name);
1401 ASSERT_TRUE(parent2.good());
1402 parent2.PutIsUnsynced(true);
1403 parent2.PutIsDir(true);
1404 parent2.PutSpecifics(DefaultBookmarkSpecifics());
1405 parent2.PutId(parent2_id);
1408 &wtrans, CREATE, BOOKMARKS, parent2_id, child_name);
1409 ASSERT_TRUE(child.good());
1410 child.PutIsUnsynced(true);
1411 child.PutIsDir(true);
1412 child.PutSpecifics(DefaultBookmarkSpecifics());
1413 child.PutId(child_id);
1414 child.PutBaseVersion(1);
1418 ASSERT_EQ(3u, mock_server_->committed_ids().size());
1419 // If this test starts failing, be aware other sort orders could be valid.
1420 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
1421 EXPECT_TRUE(parent2_id == mock_server_->committed_ids()[1]);
1422 EXPECT_TRUE(child_id == mock_server_->committed_ids()[2]);
1424 syncable::ReadTransaction rtrans(FROM_HERE, directory());
1425 // Check that things committed correctly.
1426 Entry entry_1(&rtrans, syncable::GET_BY_ID, parent_id_);
1427 EXPECT_EQ(entry_1.GetNonUniqueName(), parent1_name);
1428 // Check that parent2 is a subfolder of parent1.
1429 EXPECT_EQ(1, CountEntriesWithName(&rtrans,
1433 // Parent2 was a local ID and thus should have changed on commit!
1434 Entry pre_commit_entry_parent2(&rtrans, syncable::GET_BY_ID, parent2_id);
1435 ASSERT_FALSE(pre_commit_entry_parent2.good());
1437 // Look up the new ID.
1438 Id parent2_committed_id =
1439 GetOnlyEntryWithName(&rtrans, parent_id_, parent2_name);
1440 EXPECT_TRUE(parent2_committed_id.ServerKnows());
1442 Entry child(&rtrans, syncable::GET_BY_ID, child_id);
1443 EXPECT_EQ(parent2_committed_id, child.GetParentId());
1447 TEST_F(SyncerTest, TestCommitListOrderingAndNewParentAndChild) {
1448 string parent_name = "1";
1449 string parent2_name = "A";
1450 string child_name = "B";
1453 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1454 MutableEntry parent(&wtrans,
1458 ASSERT_TRUE(parent.good());
1459 parent.PutIsUnsynced(true);
1460 parent.PutIsDir(true);
1461 parent.PutSpecifics(DefaultBookmarkSpecifics());
1462 parent.PutId(parent_id_);
1463 parent.PutBaseVersion(1);
1466 int64 meta_handle_b;
1467 const Id parent2_local_id = ids_.NewLocalId();
1468 const Id child_local_id = ids_.NewLocalId();
1470 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1471 MutableEntry parent2(&wtrans, CREATE, BOOKMARKS, parent_id_, parent2_name);
1472 ASSERT_TRUE(parent2.good());
1473 parent2.PutIsUnsynced(true);
1474 parent2.PutIsDir(true);
1475 parent2.PutSpecifics(DefaultBookmarkSpecifics());
1477 parent2.PutId(parent2_local_id);
1479 &wtrans, CREATE, BOOKMARKS, parent2_local_id, child_name);
1480 ASSERT_TRUE(child.good());
1481 child.PutIsUnsynced(true);
1482 child.PutIsDir(true);
1483 child.PutSpecifics(DefaultBookmarkSpecifics());
1484 child.PutId(child_local_id);
1485 meta_handle_b = child.GetMetahandle();
1489 ASSERT_EQ(3u, mock_server_->committed_ids().size());
1490 // If this test starts failing, be aware other sort orders could be valid.
1491 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
1492 EXPECT_TRUE(parent2_local_id == mock_server_->committed_ids()[1]);
1493 EXPECT_TRUE(child_local_id == mock_server_->committed_ids()[2]);
1495 syncable::ReadTransaction rtrans(FROM_HERE, directory());
1497 Entry parent(&rtrans, syncable::GET_BY_ID,
1498 GetOnlyEntryWithName(&rtrans, rtrans.root_id(), parent_name));
1499 ASSERT_TRUE(parent.good());
1500 EXPECT_TRUE(parent.GetId().ServerKnows());
1502 Entry parent2(&rtrans, syncable::GET_BY_ID,
1503 GetOnlyEntryWithName(&rtrans, parent.GetId(), parent2_name));
1504 ASSERT_TRUE(parent2.good());
1505 EXPECT_TRUE(parent2.GetId().ServerKnows());
1507 // Id changed on commit, so this should fail.
1508 Entry local_parent2_id_entry(&rtrans,
1509 syncable::GET_BY_ID,
1511 ASSERT_FALSE(local_parent2_id_entry.good());
1513 Entry entry_b(&rtrans, syncable::GET_BY_HANDLE, meta_handle_b);
1514 EXPECT_TRUE(entry_b.GetId().ServerKnows());
1515 EXPECT_TRUE(parent2.GetId()== entry_b.GetParentId());
1519 TEST_F(SyncerTest, UpdateWithZeroLengthName) {
1520 // One illegal update
1521 mock_server_->AddUpdateDirectory(
1522 1, 0, std::string(), 1, 10, foreign_cache_guid(), "-1");
1523 // And one legal one that we're going to delete.
1524 mock_server_->AddUpdateDirectory(2, 0, "FOO", 1, 10,
1525 foreign_cache_guid(), "-2");
1527 // Delete the legal one. The new update has a null name.
1528 mock_server_->AddUpdateDirectory(
1529 2, 0, std::string(), 2, 20, foreign_cache_guid(), "-2");
1530 mock_server_->SetLastUpdateDeleted();
1534 TEST_F(SyncerTest, TestBasicUpdate) {
1535 string id = "some_id";
1536 string parent_id = "0";
1537 string name = "in_root";
1539 int64 timestamp = 10;
1540 mock_server_->AddUpdateDirectory(id, parent_id, name, version, timestamp,
1541 foreign_cache_guid(), "-1");
1545 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1546 Entry entry(&trans, GET_BY_ID,
1547 syncable::Id::CreateFromServerId("some_id"));
1548 ASSERT_TRUE(entry.good());
1549 EXPECT_TRUE(entry.GetIsDir());
1550 EXPECT_TRUE(entry.GetServerVersion()== version);
1551 EXPECT_TRUE(entry.GetBaseVersion()== version);
1552 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
1553 EXPECT_FALSE(entry.GetIsUnsynced());
1554 EXPECT_FALSE(entry.GetServerIsDel());
1555 EXPECT_FALSE(entry.GetIsDel());
1559 TEST_F(SyncerTest, IllegalAndLegalUpdates) {
1560 Id root = TestIdFactory::root();
1561 // Should apply just fine.
1562 mock_server_->AddUpdateDirectory(1, 0, "in_root", 10, 10,
1563 foreign_cache_guid(), "-1");
1565 // Same name. But this SHOULD work.
1566 mock_server_->AddUpdateDirectory(2, 0, "in_root", 10, 10,
1567 foreign_cache_guid(), "-2");
1569 // Unknown parent: should never be applied. "-80" is a legal server ID,
1570 // because any string sent by the server is a legal server ID in the sync
1571 // protocol, but it's not the ID of any item known to the client. This
1572 // update should succeed validation, but be stuck in the unapplied state
1573 // until an item with the server ID "-80" arrives.
1574 mock_server_->AddUpdateDirectory(3, -80, "bad_parent", 10, 10,
1575 foreign_cache_guid(), "-3");
1579 // Id 3 should be in conflict now.
1580 EXPECT_EQ(1, status().TotalNumConflictingItems());
1581 EXPECT_EQ(1, status().num_hierarchy_conflicts());
1583 // The only request in that loop should have been a GetUpdate.
1584 // At that point, we didn't know whether or not we had conflicts.
1585 ASSERT_TRUE(mock_server_->last_request().has_get_updates());
1586 VerifyHierarchyConflictsUnspecified(mock_server_->last_request());
1588 // These entries will be used in the second set of updates.
1589 mock_server_->AddUpdateDirectory(4, 0, "newer_version", 20, 10,
1590 foreign_cache_guid(), "-4");
1591 mock_server_->AddUpdateDirectory(5, 0, "circular1", 10, 10,
1592 foreign_cache_guid(), "-5");
1593 mock_server_->AddUpdateDirectory(6, 5, "circular2", 10, 10,
1594 foreign_cache_guid(), "-6");
1595 mock_server_->AddUpdateDirectory(9, 3, "bad_parent_child", 10, 10,
1596 foreign_cache_guid(), "-9");
1597 mock_server_->AddUpdateDirectory(100, 9, "bad_parent_child2", 10, 10,
1598 foreign_cache_guid(), "-100");
1599 mock_server_->AddUpdateDirectory(10, 0, "dir_to_bookmark", 10, 10,
1600 foreign_cache_guid(), "-10");
1603 // The three items with an unresolved parent should be unapplied (3, 9, 100).
1604 // The name clash should also still be in conflict.
1605 EXPECT_EQ(3, status().TotalNumConflictingItems());
1606 EXPECT_EQ(3, status().num_hierarchy_conflicts());
1608 // This time around, we knew that there were conflicts.
1609 ASSERT_TRUE(mock_server_->last_request().has_get_updates());
1610 VerifyHierarchyConflictsReported(mock_server_->last_request());
1613 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1614 // Even though it has the same name, it should work.
1615 Entry name_clash(&trans, GET_BY_ID, ids_.FromNumber(2));
1616 ASSERT_TRUE(name_clash.good());
1617 EXPECT_FALSE(name_clash.GetIsUnappliedUpdate())
1618 << "Duplicate name SHOULD be OK.";
1620 Entry bad_parent(&trans, GET_BY_ID, ids_.FromNumber(3));
1621 ASSERT_TRUE(bad_parent.good());
1622 EXPECT_TRUE(bad_parent.GetIsUnappliedUpdate())
1623 << "child of unknown parent should be in conflict";
1625 Entry bad_parent_child(&trans, GET_BY_ID, ids_.FromNumber(9));
1626 ASSERT_TRUE(bad_parent_child.good());
1627 EXPECT_TRUE(bad_parent_child.GetIsUnappliedUpdate())
1628 << "grandchild of unknown parent should be in conflict";
1630 Entry bad_parent_child2(&trans, GET_BY_ID, ids_.FromNumber(100));
1631 ASSERT_TRUE(bad_parent_child2.good());
1632 EXPECT_TRUE(bad_parent_child2.GetIsUnappliedUpdate())
1633 << "great-grandchild of unknown parent should be in conflict";
1636 // Updating 1 should not affect item 2 of the same name.
1637 mock_server_->AddUpdateDirectory(1, 0, "new_name", 20, 20,
1638 foreign_cache_guid(), "-1");
1640 // Moving 5 under 6 will create a cycle: a conflict.
1641 mock_server_->AddUpdateDirectory(5, 6, "circular3", 20, 20,
1642 foreign_cache_guid(), "-5");
1644 // Flip the is_dir bit: should fail verify & be dropped.
1645 mock_server_->AddUpdateBookmark(10, 0, "dir_to_bookmark", 20, 20,
1646 foreign_cache_guid(), "-10");
1649 // Version number older than last known: should fail verify & be dropped.
1650 mock_server_->AddUpdateDirectory(4, 0, "old_version", 10, 10,
1651 foreign_cache_guid(), "-4");
1654 syncable::ReadTransaction trans(FROM_HERE, directory());
1656 Entry still_a_dir(&trans, GET_BY_ID, ids_.FromNumber(10));
1657 ASSERT_TRUE(still_a_dir.good());
1658 EXPECT_FALSE(still_a_dir.GetIsUnappliedUpdate());
1659 EXPECT_EQ(10u, still_a_dir.GetBaseVersion());
1660 EXPECT_EQ(10u, still_a_dir.GetServerVersion());
1661 EXPECT_TRUE(still_a_dir.GetIsDir());
1663 Entry rename(&trans, GET_BY_ID, ids_.FromNumber(1));
1664 ASSERT_TRUE(rename.good());
1665 EXPECT_EQ(root, rename.GetParentId());
1666 EXPECT_EQ("new_name", rename.GetNonUniqueName());
1667 EXPECT_FALSE(rename.GetIsUnappliedUpdate());
1668 EXPECT_TRUE(ids_.FromNumber(1) == rename.GetId());
1669 EXPECT_EQ(20u, rename.GetBaseVersion());
1671 Entry name_clash(&trans, GET_BY_ID, ids_.FromNumber(2));
1672 ASSERT_TRUE(name_clash.good());
1673 EXPECT_EQ(root, name_clash.GetParentId());
1674 EXPECT_TRUE(ids_.FromNumber(2) == name_clash.GetId());
1675 EXPECT_EQ(10u, name_clash.GetBaseVersion());
1676 EXPECT_EQ("in_root", name_clash.GetNonUniqueName());
1678 Entry ignored_old_version(&trans, GET_BY_ID, ids_.FromNumber(4));
1679 ASSERT_TRUE(ignored_old_version.good());
1681 ignored_old_version.GetNonUniqueName()== "newer_version");
1682 EXPECT_FALSE(ignored_old_version.GetIsUnappliedUpdate());
1683 EXPECT_EQ(20u, ignored_old_version.GetBaseVersion());
1685 Entry circular_parent_issue(&trans, GET_BY_ID, ids_.FromNumber(5));
1686 ASSERT_TRUE(circular_parent_issue.good());
1687 EXPECT_TRUE(circular_parent_issue.GetIsUnappliedUpdate())
1688 << "circular move should be in conflict";
1689 EXPECT_TRUE(circular_parent_issue.GetParentId()== root_id_);
1690 EXPECT_TRUE(circular_parent_issue.GetServerParentId()==
1691 ids_.FromNumber(6));
1692 EXPECT_EQ(10u, circular_parent_issue.GetBaseVersion());
1694 Entry circular_parent_target(&trans, GET_BY_ID, ids_.FromNumber(6));
1695 ASSERT_TRUE(circular_parent_target.good());
1696 EXPECT_FALSE(circular_parent_target.GetIsUnappliedUpdate());
1697 EXPECT_TRUE(circular_parent_issue.GetId()==
1698 circular_parent_target.GetParentId());
1699 EXPECT_EQ(10u, circular_parent_target.GetBaseVersion());
1702 EXPECT_FALSE(saw_syncer_event_);
1703 EXPECT_EQ(4, status().TotalNumConflictingItems());
1704 EXPECT_EQ(4, status().num_hierarchy_conflicts());
1707 // A commit with a lost response produces an update that has to be reunited with
1709 TEST_F(SyncerTest, CommitReuniteUpdateAdjustsChildren) {
1710 // Create a folder in the root.
1711 int64 metahandle_folder;
1713 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1715 &trans, CREATE, BOOKMARKS, trans.root_id(), "new_folder");
1716 ASSERT_TRUE(entry.good());
1717 entry.PutIsDir(true);
1718 entry.PutSpecifics(DefaultBookmarkSpecifics());
1719 entry.PutIsUnsynced(true);
1720 metahandle_folder = entry.GetMetahandle();
1723 // Verify it and pull the ID out of the folder.
1724 syncable::Id folder_id;
1725 int64 metahandle_entry;
1727 syncable::ReadTransaction trans(FROM_HERE, directory());
1728 Entry entry(&trans, GET_BY_HANDLE, metahandle_folder);
1729 ASSERT_TRUE(entry.good());
1730 folder_id = entry.GetId();
1731 ASSERT_TRUE(!folder_id.ServerKnows());
1734 // Create an entry in the newly created folder.
1736 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1737 MutableEntry entry(&trans, CREATE, BOOKMARKS, folder_id, "new_entry");
1738 ASSERT_TRUE(entry.good());
1739 metahandle_entry = entry.GetMetahandle();
1740 WriteTestDataToEntry(&trans, &entry);
1743 // Verify it and pull the ID out of the entry.
1744 syncable::Id entry_id;
1746 syncable::ReadTransaction trans(FROM_HERE, directory());
1747 Entry entry(&trans, syncable::GET_BY_HANDLE, metahandle_entry);
1748 ASSERT_TRUE(entry.good());
1749 EXPECT_EQ(folder_id, entry.GetParentId());
1750 EXPECT_EQ("new_entry", entry.GetNonUniqueName());
1751 entry_id = entry.GetId();
1752 EXPECT_TRUE(!entry_id.ServerKnows());
1753 VerifyTestDataInEntry(&trans, &entry);
1756 // Now, to emulate a commit response failure, we just don't commit it.
1757 int64 new_version = 150; // any larger value.
1758 int64 timestamp = 20; // arbitrary value.
1759 syncable::Id new_folder_id =
1760 syncable::Id::CreateFromServerId("folder_server_id");
1762 // The following update should cause the folder to both apply the update, as
1763 // well as reassociate the id.
1764 mock_server_->AddUpdateDirectory(new_folder_id, root_id_,
1765 "new_folder", new_version, timestamp,
1766 local_cache_guid(), folder_id.GetServerId());
1768 // We don't want it accidentally committed, just the update applied.
1769 mock_server_->set_conflict_all_commits(true);
1771 // Alright! Apply that update!
1774 // The folder's ID should have been updated.
1775 syncable::ReadTransaction trans(FROM_HERE, directory());
1776 Entry folder(&trans, GET_BY_HANDLE, metahandle_folder);
1777 ASSERT_TRUE(folder.good());
1778 EXPECT_EQ("new_folder", folder.GetNonUniqueName());
1779 EXPECT_TRUE(new_version == folder.GetBaseVersion());
1780 EXPECT_TRUE(new_folder_id == folder.GetId());
1781 EXPECT_TRUE(folder.GetId().ServerKnows());
1782 EXPECT_EQ(trans.root_id(), folder.GetParentId());
1784 // Since it was updated, the old folder should not exist.
1785 Entry old_dead_folder(&trans, GET_BY_ID, folder_id);
1786 EXPECT_FALSE(old_dead_folder.good());
1788 // The child's parent should have changed.
1789 Entry entry(&trans, syncable::GET_BY_HANDLE, metahandle_entry);
1790 ASSERT_TRUE(entry.good());
1791 EXPECT_EQ("new_entry", entry.GetNonUniqueName());
1792 EXPECT_EQ(new_folder_id, entry.GetParentId());
1793 EXPECT_TRUE(!entry.GetId().ServerKnows());
1794 VerifyTestDataInEntry(&trans, &entry);
1798 // A commit with a lost response produces an update that has to be reunited with
1800 TEST_F(SyncerTest, CommitReuniteUpdate) {
1801 // Create an entry in the root.
1802 int64 entry_metahandle;
1804 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1805 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "new_entry");
1806 ASSERT_TRUE(entry.good());
1807 entry_metahandle = entry.GetMetahandle();
1808 WriteTestDataToEntry(&trans, &entry);
1811 // Verify it and pull the ID out.
1812 syncable::Id entry_id;
1814 syncable::ReadTransaction trans(FROM_HERE, directory());
1816 Entry entry(&trans, GET_BY_HANDLE, entry_metahandle);
1817 ASSERT_TRUE(entry.good());
1818 entry_id = entry.GetId();
1819 EXPECT_TRUE(!entry_id.ServerKnows());
1820 VerifyTestDataInEntry(&trans, &entry);
1823 // Now, to emulate a commit response failure, we just don't commit it.
1824 int64 new_version = 150; // any larger value.
1825 int64 timestamp = 20; // arbitrary value.
1826 syncable::Id new_entry_id = syncable::Id::CreateFromServerId("server_id");
1828 // Generate an update from the server with a relevant ID reassignment.
1829 mock_server_->AddUpdateBookmark(new_entry_id, root_id_,
1830 "new_entry", new_version, timestamp,
1831 local_cache_guid(), entry_id.GetServerId());
1833 // We don't want it accidentally committed, just the update applied.
1834 mock_server_->set_conflict_all_commits(true);
1836 // Alright! Apply that update!
1839 syncable::ReadTransaction trans(FROM_HERE, directory());
1840 Entry entry(&trans, GET_BY_HANDLE, entry_metahandle);
1841 ASSERT_TRUE(entry.good());
1842 EXPECT_TRUE(new_version == entry.GetBaseVersion());
1843 EXPECT_TRUE(new_entry_id == entry.GetId());
1844 EXPECT_EQ("new_entry", entry.GetNonUniqueName());
1848 // A commit with a lost response must work even if the local entry was deleted
1849 // before the update is applied. We should not duplicate the local entry in
1850 // this case, but just create another one alongside. We may wish to examine
1851 // this behavior in the future as it can create hanging uploads that never
1852 // finish, that must be cleaned up on the server side after some time.
1853 TEST_F(SyncerTest, CommitReuniteUpdateDoesNotChokeOnDeletedLocalEntry) {
1854 // Create a entry in the root.
1855 int64 entry_metahandle;
1857 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1858 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "new_entry");
1859 ASSERT_TRUE(entry.good());
1860 entry_metahandle = entry.GetMetahandle();
1861 WriteTestDataToEntry(&trans, &entry);
1863 // Verify it and pull the ID out.
1864 syncable::Id entry_id;
1866 syncable::ReadTransaction trans(FROM_HERE, directory());
1867 Entry entry(&trans, GET_BY_HANDLE, entry_metahandle);
1868 ASSERT_TRUE(entry.good());
1869 entry_id = entry.GetId();
1870 EXPECT_TRUE(!entry_id.ServerKnows());
1871 VerifyTestDataInEntry(&trans, &entry);
1874 // Now, to emulate a commit response failure, we just don't commit it.
1875 int64 new_version = 150; // any larger value.
1876 int64 timestamp = 20; // arbitrary value.
1877 syncable::Id new_entry_id = syncable::Id::CreateFromServerId("server_id");
1879 // Generate an update from the server with a relevant ID reassignment.
1880 mock_server_->AddUpdateBookmark(new_entry_id, root_id_,
1881 "new_entry", new_version, timestamp,
1882 local_cache_guid(), entry_id.GetServerId());
1884 // We don't want it accidentally committed, just the update applied.
1885 mock_server_->set_conflict_all_commits(true);
1887 // Purposefully delete the entry now before the update application finishes.
1889 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1890 Id new_entry_id = GetOnlyEntryWithName(
1891 &trans, trans.root_id(), "new_entry");
1892 MutableEntry entry(&trans, GET_BY_ID, new_entry_id);
1893 ASSERT_TRUE(entry.good());
1894 entry.PutIsDel(true);
1897 // Just don't CHECK fail in sync, have the update split.
1900 syncable::ReadTransaction trans(FROM_HERE, directory());
1901 Id new_entry_id = GetOnlyEntryWithName(
1902 &trans, trans.root_id(), "new_entry");
1903 Entry entry(&trans, GET_BY_ID, new_entry_id);
1904 ASSERT_TRUE(entry.good());
1905 EXPECT_FALSE(entry.GetIsDel());
1907 Entry old_entry(&trans, GET_BY_ID, entry_id);
1908 ASSERT_TRUE(old_entry.good());
1909 EXPECT_TRUE(old_entry.GetIsDel());
1913 // TODO(chron): Add more unsanitized name tests.
1914 TEST_F(SyncerTest, ConflictMatchingEntryHandlesUnsanitizedNames) {
1915 mock_server_->AddUpdateDirectory(1, 0, "A/A", 10, 10,
1916 foreign_cache_guid(), "-1");
1917 mock_server_->AddUpdateDirectory(2, 0, "B/B", 10, 10,
1918 foreign_cache_guid(), "-2");
1919 mock_server_->set_conflict_all_commits(true);
1922 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1924 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
1925 ASSERT_TRUE(A.good());
1926 A.PutIsUnsynced(true);
1927 A.PutIsUnappliedUpdate(true);
1928 A.PutServerVersion(20);
1930 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
1931 ASSERT_TRUE(B.good());
1932 B.PutIsUnappliedUpdate(true);
1933 B.PutServerVersion(20);
1936 saw_syncer_event_ = false;
1937 mock_server_->set_conflict_all_commits(false);
1940 syncable::ReadTransaction trans(FROM_HERE, directory());
1942 Entry A(&trans, GET_BY_ID, ids_.FromNumber(1));
1943 ASSERT_TRUE(A.good());
1944 EXPECT_TRUE(A.GetIsUnsynced()== false);
1945 EXPECT_TRUE(A.GetIsUnappliedUpdate()== false);
1946 EXPECT_TRUE(A.GetServerVersion()== 20);
1948 Entry B(&trans, GET_BY_ID, ids_.FromNumber(2));
1949 ASSERT_TRUE(B.good());
1950 EXPECT_TRUE(B.GetIsUnsynced()== false);
1951 EXPECT_TRUE(B.GetIsUnappliedUpdate()== false);
1952 EXPECT_TRUE(B.GetServerVersion()== 20);
1956 TEST_F(SyncerTest, ConflictMatchingEntryHandlesNormalNames) {
1957 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
1958 foreign_cache_guid(), "-1");
1959 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10,
1960 foreign_cache_guid(), "-2");
1961 mock_server_->set_conflict_all_commits(true);
1964 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1966 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
1967 ASSERT_TRUE(A.good());
1968 A.PutIsUnsynced(true);
1969 A.PutIsUnappliedUpdate(true);
1970 A.PutServerVersion(20);
1972 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
1973 ASSERT_TRUE(B.good());
1974 B.PutIsUnappliedUpdate(true);
1975 B.PutServerVersion(20);
1978 saw_syncer_event_ = false;
1979 mock_server_->set_conflict_all_commits(false);
1982 syncable::ReadTransaction trans(FROM_HERE, directory());
1984 Entry A(&trans, GET_BY_ID, ids_.FromNumber(1));
1985 ASSERT_TRUE(A.good());
1986 EXPECT_TRUE(A.GetIsUnsynced()== false);
1987 EXPECT_TRUE(A.GetIsUnappliedUpdate()== false);
1988 EXPECT_TRUE(A.GetServerVersion()== 20);
1990 Entry B(&trans, GET_BY_ID, ids_.FromNumber(2));
1991 ASSERT_TRUE(B.good());
1992 EXPECT_TRUE(B.GetIsUnsynced()== false);
1993 EXPECT_TRUE(B.GetIsUnappliedUpdate()== false);
1994 EXPECT_TRUE(B.GetServerVersion()== 20);
1998 TEST_F(SyncerTest, ReverseFolderOrderingTest) {
1999 mock_server_->AddUpdateDirectory(4, 3, "ggchild", 10, 10,
2000 foreign_cache_guid(), "-4");
2001 mock_server_->AddUpdateDirectory(3, 2, "gchild", 10, 10,
2002 foreign_cache_guid(), "-3");
2003 mock_server_->AddUpdateDirectory(5, 4, "gggchild", 10, 10,
2004 foreign_cache_guid(), "-5");
2005 mock_server_->AddUpdateDirectory(2, 1, "child", 10, 10,
2006 foreign_cache_guid(), "-2");
2007 mock_server_->AddUpdateDirectory(1, 0, "parent", 10, 10,
2008 foreign_cache_guid(), "-1");
2010 syncable::ReadTransaction trans(FROM_HERE, directory());
2012 Id child_id = GetOnlyEntryWithName(
2013 &trans, ids_.FromNumber(4), "gggchild");
2014 Entry child(&trans, GET_BY_ID, child_id);
2015 ASSERT_TRUE(child.good());
2018 class EntryCreatedInNewFolderTest : public SyncerTest {
2020 void CreateFolderInBob() {
2021 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2022 MutableEntry bob(&trans,
2023 syncable::GET_BY_ID,
2024 GetOnlyEntryWithName(&trans,
2025 TestIdFactory::root(),
2029 MutableEntry entry2(
2030 &trans, CREATE, BOOKMARKS, bob.GetId(), "bob");
2031 CHECK(entry2.good());
2032 entry2.PutIsDir(true);
2033 entry2.PutIsUnsynced(true);
2034 entry2.PutSpecifics(DefaultBookmarkSpecifics());
2038 TEST_F(EntryCreatedInNewFolderTest, EntryCreatedInNewFolderMidSync) {
2040 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2041 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "bob");
2042 ASSERT_TRUE(entry.good());
2043 entry.PutIsDir(true);
2044 entry.PutIsUnsynced(true);
2045 entry.PutSpecifics(DefaultBookmarkSpecifics());
2048 mock_server_->SetMidCommitCallback(
2049 base::Bind(&EntryCreatedInNewFolderTest::CreateFolderInBob,
2050 base::Unretained(this)));
2052 // We loop until no unsynced handles remain, so we will commit both ids.
2053 EXPECT_EQ(2u, mock_server_->committed_ids().size());
2055 syncable::ReadTransaction trans(FROM_HERE, directory());
2056 Entry parent_entry(&trans, syncable::GET_BY_ID,
2057 GetOnlyEntryWithName(&trans, TestIdFactory::root(), "bob"));
2058 ASSERT_TRUE(parent_entry.good());
2061 GetOnlyEntryWithName(&trans, parent_entry.GetId(), "bob");
2062 Entry child(&trans, syncable::GET_BY_ID, child_id);
2063 ASSERT_TRUE(child.good());
2064 EXPECT_EQ(parent_entry.GetId(), child.GetParentId());
2068 TEST_F(SyncerTest, NegativeIDInUpdate) {
2069 mock_server_->AddUpdateBookmark(-10, 0, "bad", 40, 40,
2070 foreign_cache_guid(), "-100");
2072 // The negative id would make us CHECK!
2075 TEST_F(SyncerTest, UnappliedUpdateOnCreatedItemItemDoesNotCrash) {
2076 int64 metahandle_fred;
2077 syncable::Id orig_id;
2080 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2081 MutableEntry fred_match(&trans, CREATE, BOOKMARKS, trans.root_id(),
2083 ASSERT_TRUE(fred_match.good());
2084 metahandle_fred = fred_match.GetMetahandle();
2085 orig_id = fred_match.GetId();
2086 WriteTestDataToEntry(&trans, &fred_match);
2090 EXPECT_EQ(1u, mock_server_->committed_ids().size());
2091 mock_server_->set_conflict_all_commits(true);
2092 syncable::Id fred_match_id;
2094 // Now receive a change from outside.
2095 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2096 MutableEntry fred_match(&trans, GET_BY_HANDLE, metahandle_fred);
2097 ASSERT_TRUE(fred_match.good());
2098 EXPECT_TRUE(fred_match.GetId().ServerKnows());
2099 fred_match_id = fred_match.GetId();
2100 mock_server_->AddUpdateBookmark(fred_match_id, trans.root_id(),
2101 "fred_match", 40, 40, local_cache_guid(), orig_id.GetServerId());
2104 for (int i = 0 ; i < 30 ; ++i) {
2110 * In the event that we have a double changed entry, that is changed on both
2111 * the client and the server, the conflict resolver should just drop one of
2112 * them and accept the other.
2115 TEST_F(SyncerTest, DoublyChangedWithResolver) {
2116 syncable::Id local_id;
2118 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2119 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, root_id_, "Folder");
2120 ASSERT_TRUE(parent.good());
2121 parent.PutIsDir(true);
2122 parent.PutId(parent_id_);
2123 parent.PutBaseVersion(5);
2124 parent.PutSpecifics(DefaultBookmarkSpecifics());
2125 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent_id_, "Pete.htm");
2126 ASSERT_TRUE(child.good());
2127 local_id = child.GetId();
2128 child.PutId(child_id_);
2129 child.PutBaseVersion(10);
2130 WriteTestDataToEntry(&wtrans, &child);
2132 mock_server_->AddUpdateBookmark(child_id_, parent_id_, "Pete2.htm", 11, 10,
2133 local_cache_guid(), local_id.GetServerId());
2134 mock_server_->set_conflict_all_commits(true);
2136 syncable::Directory::Metahandles children;
2138 syncable::ReadTransaction trans(FROM_HERE, directory());
2139 directory()->GetChildHandlesById(&trans, parent_id_, &children);
2140 // We expect the conflict resolver to preserve the local entry.
2141 Entry child(&trans, syncable::GET_BY_ID, child_id_);
2142 ASSERT_TRUE(child.good());
2143 EXPECT_TRUE(child.GetIsUnsynced());
2144 EXPECT_FALSE(child.GetIsUnappliedUpdate());
2145 EXPECT_TRUE(child.GetSpecifics().has_bookmark());
2146 EXPECT_EQ("Pete.htm", child.GetNonUniqueName());
2147 VerifyTestBookmarkDataInEntry(&child);
2150 // Only one entry, since we just overwrite one.
2151 EXPECT_EQ(1u, children.size());
2152 saw_syncer_event_ = false;
2155 // We got this repro case when someone was editing bookmarks while sync was
2156 // occuring. The entry had changed out underneath the user.
2157 TEST_F(SyncerTest, CommitsUpdateDoesntAlterEntry) {
2158 const base::Time& test_time = ProtoTimeToTime(123456);
2159 syncable::Id local_id;
2160 int64 entry_metahandle;
2162 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2163 MutableEntry entry(&wtrans, CREATE, BOOKMARKS, root_id_, "Pete");
2164 ASSERT_TRUE(entry.good());
2165 EXPECT_FALSE(entry.GetId().ServerKnows());
2166 local_id = entry.GetId();
2167 entry.PutIsDir(true);
2168 entry.PutSpecifics(DefaultBookmarkSpecifics());
2169 entry.PutIsUnsynced(true);
2170 entry.PutMtime(test_time);
2171 entry_metahandle = entry.GetMetahandle();
2177 syncable::ReadTransaction trans(FROM_HERE, directory());
2178 Entry entry(&trans, syncable::GET_BY_HANDLE, entry_metahandle);
2179 ASSERT_TRUE(entry.good());
2181 EXPECT_TRUE(id.ServerKnows());
2182 version = entry.GetBaseVersion();
2184 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
2185 update->set_originator_cache_guid(local_cache_guid());
2186 update->set_originator_client_item_id(local_id.GetServerId());
2187 EXPECT_EQ("Pete", update->name());
2188 EXPECT_EQ(id.GetServerId(), update->id_string());
2189 EXPECT_EQ(root_id_.GetServerId(), update->parent_id_string());
2190 EXPECT_EQ(version, update->version());
2193 syncable::ReadTransaction trans(FROM_HERE, directory());
2194 Entry entry(&trans, syncable::GET_BY_ID, id);
2195 ASSERT_TRUE(entry.good());
2196 EXPECT_TRUE(entry.GetMtime()== test_time);
2200 TEST_F(SyncerTest, ParentAndChildBothMatch) {
2201 const FullModelTypeSet all_types = FullModelTypeSet::All();
2202 syncable::Id parent_id = ids_.NewServerId();
2203 syncable::Id child_id = ids_.NewServerId();
2204 syncable::Id parent_local_id;
2205 syncable::Id child_local_id;
2209 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2210 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, root_id_, "Folder");
2211 ASSERT_TRUE(parent.good());
2212 parent_local_id = parent.GetId();
2213 parent.PutIsDir(true);
2214 parent.PutIsUnsynced(true);
2215 parent.PutId(parent_id);
2216 parent.PutBaseVersion(1);
2217 parent.PutSpecifics(DefaultBookmarkSpecifics());
2219 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent.GetId(), "test.htm");
2220 ASSERT_TRUE(child.good());
2221 child_local_id = child.GetId();
2222 child.PutId(child_id);
2223 child.PutBaseVersion(1);
2224 child.PutSpecifics(DefaultBookmarkSpecifics());
2225 WriteTestDataToEntry(&wtrans, &child);
2227 mock_server_->AddUpdateDirectory(parent_id, root_id_, "Folder", 10, 10,
2229 parent_local_id.GetServerId());
2230 mock_server_->AddUpdateBookmark(child_id, parent_id, "test.htm", 10, 10,
2232 child_local_id.GetServerId());
2233 mock_server_->set_conflict_all_commits(true);
2238 syncable::ReadTransaction trans(FROM_HERE, directory());
2239 Directory::Metahandles children;
2240 directory()->GetChildHandlesById(&trans, root_id_, &children);
2241 EXPECT_EQ(1u, children.size());
2242 directory()->GetChildHandlesById(&trans, parent_id, &children);
2243 EXPECT_EQ(1u, children.size());
2244 std::vector<int64> unapplied;
2245 directory()->GetUnappliedUpdateMetaHandles(&trans, all_types, &unapplied);
2246 EXPECT_EQ(0u, unapplied.size());
2247 syncable::Directory::Metahandles unsynced;
2248 directory()->GetUnsyncedMetaHandles(&trans, &unsynced);
2249 EXPECT_EQ(0u, unsynced.size());
2250 saw_syncer_event_ = false;
2254 TEST_F(SyncerTest, CommittingNewDeleted) {
2256 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2257 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "bob");
2258 entry.PutIsUnsynced(true);
2259 entry.PutIsDel(true);
2262 EXPECT_EQ(0u, mock_server_->committed_ids().size());
2265 // Original problem synopsis:
2266 // Check failed: entry->GetBaseVersion()<= entry->GetServerVersion()
2267 // Client creates entry, client finishes committing entry. Between
2268 // commit and getting update back, we delete the entry.
2269 // We get the update for the entry, but the local one was modified
2270 // so we store the entry but don't apply it. IS_UNAPPLIED_UPDATE is set.
2271 // We commit deletion and get a new version number.
2272 // We apply unapplied updates again before we get the update about the deletion.
2273 // This means we have an unapplied update where server_version < base_version.
2274 TEST_F(SyncerTest, UnappliedUpdateDuringCommit) {
2275 // This test is a little fake.
2277 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2278 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "bob");
2279 entry.PutId(ids_.FromNumber(20));
2280 entry.PutBaseVersion(1);
2281 entry.PutServerVersion(1);
2282 entry.PutServerParentId(ids_.FromNumber(9999)); // Bad parent.
2283 entry.PutIsUnsynced(true);
2284 entry.PutIsUnappliedUpdate(true);
2285 entry.PutSpecifics(DefaultBookmarkSpecifics());
2286 entry.PutServerSpecifics(DefaultBookmarkSpecifics());
2287 entry.PutIsDel(false);
2290 EXPECT_EQ(1, session_->status_controller().TotalNumConflictingItems());
2291 saw_syncer_event_ = false;
2294 // Original problem synopsis:
2296 // Unexpected error during sync if we:
2297 // make a new folder bob
2299 // make a new folder fred
2300 // move bob into fred
2303 // if no syncing occured midway, bob will have an illegal parent
2304 TEST_F(SyncerTest, DeletingEntryInFolder) {
2305 // This test is a little fake.
2306 int64 existing_metahandle;
2308 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2309 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "existing");
2310 ASSERT_TRUE(entry.good());
2311 entry.PutIsDir(true);
2312 entry.PutSpecifics(DefaultBookmarkSpecifics());
2313 entry.PutIsUnsynced(true);
2314 existing_metahandle = entry.GetMetahandle();
2318 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2319 MutableEntry newfolder(&trans, CREATE, BOOKMARKS, trans.root_id(), "new");
2320 ASSERT_TRUE(newfolder.good());
2321 newfolder.PutIsDir(true);
2322 newfolder.PutSpecifics(DefaultBookmarkSpecifics());
2323 newfolder.PutIsUnsynced(true);
2325 MutableEntry existing(&trans, GET_BY_HANDLE, existing_metahandle);
2326 ASSERT_TRUE(existing.good());
2327 existing.PutParentId(newfolder.GetId());
2328 existing.PutIsUnsynced(true);
2329 EXPECT_TRUE(existing.GetId().ServerKnows());
2331 newfolder.PutIsDel(true);
2332 existing.PutIsDel(true);
2335 EXPECT_EQ(0, status().num_server_conflicts());
2338 TEST_F(SyncerTest, DeletingEntryWithLocalEdits) {
2339 int64 newfolder_metahandle;
2341 mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10,
2342 foreign_cache_guid(), "-1");
2345 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2346 MutableEntry newfolder(
2347 &trans, CREATE, BOOKMARKS, ids_.FromNumber(1), "local");
2348 ASSERT_TRUE(newfolder.good());
2349 newfolder.PutIsUnsynced(true);
2350 newfolder.PutIsDir(true);
2351 newfolder.PutSpecifics(DefaultBookmarkSpecifics());
2352 newfolder_metahandle = newfolder.GetMetahandle();
2354 mock_server_->AddUpdateDirectory(1, 0, "bob", 2, 20,
2355 foreign_cache_guid(), "-1");
2356 mock_server_->SetLastUpdateDeleted();
2357 SyncShareConfigure();
2359 syncable::ReadTransaction trans(FROM_HERE, directory());
2360 Entry entry(&trans, syncable::GET_BY_HANDLE, newfolder_metahandle);
2361 ASSERT_TRUE(entry.good());
2365 TEST_F(SyncerTest, FolderSwapUpdate) {
2366 mock_server_->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2367 foreign_cache_guid(), "-7801");
2368 mock_server_->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2369 foreign_cache_guid(), "-1024");
2371 mock_server_->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2372 foreign_cache_guid(), "-1024");
2373 mock_server_->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2374 foreign_cache_guid(), "-7801");
2377 syncable::ReadTransaction trans(FROM_HERE, directory());
2378 Entry id1(&trans, GET_BY_ID, ids_.FromNumber(7801));
2379 ASSERT_TRUE(id1.good());
2380 EXPECT_TRUE("fred" == id1.GetNonUniqueName());
2381 EXPECT_TRUE(root_id_ == id1.GetParentId());
2382 Entry id2(&trans, GET_BY_ID, ids_.FromNumber(1024));
2383 ASSERT_TRUE(id2.good());
2384 EXPECT_TRUE("bob" == id2.GetNonUniqueName());
2385 EXPECT_TRUE(root_id_ == id2.GetParentId());
2387 saw_syncer_event_ = false;
2390 TEST_F(SyncerTest, NameCollidingFolderSwapWorksFine) {
2391 mock_server_->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2392 foreign_cache_guid(), "-7801");
2393 mock_server_->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2394 foreign_cache_guid(), "-1024");
2395 mock_server_->AddUpdateDirectory(4096, 0, "alice", 1, 10,
2396 foreign_cache_guid(), "-4096");
2399 syncable::ReadTransaction trans(FROM_HERE, directory());
2400 Entry id1(&trans, GET_BY_ID, ids_.FromNumber(7801));
2401 ASSERT_TRUE(id1.good());
2402 EXPECT_TRUE("bob" == id1.GetNonUniqueName());
2403 EXPECT_TRUE(root_id_ == id1.GetParentId());
2404 Entry id2(&trans, GET_BY_ID, ids_.FromNumber(1024));
2405 ASSERT_TRUE(id2.good());
2406 EXPECT_TRUE("fred" == id2.GetNonUniqueName());
2407 EXPECT_TRUE(root_id_ == id2.GetParentId());
2408 Entry id3(&trans, GET_BY_ID, ids_.FromNumber(4096));
2409 ASSERT_TRUE(id3.good());
2410 EXPECT_TRUE("alice" == id3.GetNonUniqueName());
2411 EXPECT_TRUE(root_id_ == id3.GetParentId());
2413 mock_server_->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2414 foreign_cache_guid(), "-1024");
2415 mock_server_->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2416 foreign_cache_guid(), "-7801");
2417 mock_server_->AddUpdateDirectory(4096, 0, "bob", 2, 20,
2418 foreign_cache_guid(), "-4096");
2421 syncable::ReadTransaction trans(FROM_HERE, directory());
2422 Entry id1(&trans, GET_BY_ID, ids_.FromNumber(7801));
2423 ASSERT_TRUE(id1.good());
2424 EXPECT_TRUE("fred" == id1.GetNonUniqueName());
2425 EXPECT_TRUE(root_id_ == id1.GetParentId());
2426 Entry id2(&trans, GET_BY_ID, ids_.FromNumber(1024));
2427 ASSERT_TRUE(id2.good());
2428 EXPECT_TRUE("bob" == id2.GetNonUniqueName());
2429 EXPECT_TRUE(root_id_ == id2.GetParentId());
2430 Entry id3(&trans, GET_BY_ID, ids_.FromNumber(4096));
2431 ASSERT_TRUE(id3.good());
2432 EXPECT_TRUE("bob" == id3.GetNonUniqueName());
2433 EXPECT_TRUE(root_id_ == id3.GetParentId());
2435 saw_syncer_event_ = false;
2438 // Committing more than kDefaultMaxCommitBatchSize items requires that
2439 // we post more than one commit command to the server. This test makes
2440 // sure that scenario works as expected.
2441 TEST_F(SyncerTest, CommitManyItemsInOneGo_Success) {
2442 uint32 num_batches = 3;
2443 uint32 items_to_commit = kDefaultMaxCommitBatchSize * num_batches;
2445 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2446 for (uint32 i = 0; i < items_to_commit; i++) {
2447 string nameutf8 = base::StringPrintf("%d", i);
2448 string name(nameutf8.begin(), nameutf8.end());
2449 MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
2450 e.PutIsUnsynced(true);
2452 e.PutSpecifics(DefaultBookmarkSpecifics());
2455 ASSERT_EQ(items_to_commit, directory()->unsynced_entity_count());
2458 EXPECT_EQ(num_batches, mock_server_->commit_messages().size());
2459 EXPECT_EQ(0, directory()->unsynced_entity_count());
2462 // Test that a single failure to contact the server will cause us to exit the
2463 // commit loop immediately.
2464 TEST_F(SyncerTest, CommitManyItemsInOneGo_PostBufferFail) {
2465 uint32 num_batches = 3;
2466 uint32 items_to_commit = kDefaultMaxCommitBatchSize * num_batches;
2468 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2469 for (uint32 i = 0; i < items_to_commit; i++) {
2470 string nameutf8 = base::StringPrintf("%d", i);
2471 string name(nameutf8.begin(), nameutf8.end());
2472 MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
2473 e.PutIsUnsynced(true);
2475 e.PutSpecifics(DefaultBookmarkSpecifics());
2478 ASSERT_EQ(items_to_commit, directory()->unsynced_entity_count());
2480 // The second commit should fail. It will be preceded by one successful
2481 // GetUpdate and one succesful commit.
2482 mock_server_->FailNthPostBufferToPathCall(3);
2485 EXPECT_EQ(1U, mock_server_->commit_messages().size());
2486 EXPECT_EQ(SYNC_SERVER_ERROR,
2487 session_->status_controller().model_neutral_state().commit_result);
2488 EXPECT_EQ(items_to_commit - kDefaultMaxCommitBatchSize,
2489 directory()->unsynced_entity_count());
2492 // Test that a single conflict response from the server will cause us to exit
2493 // the commit loop immediately.
2494 TEST_F(SyncerTest, CommitManyItemsInOneGo_CommitConflict) {
2495 uint32 num_batches = 2;
2496 uint32 items_to_commit = kDefaultMaxCommitBatchSize * num_batches;
2498 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2499 for (uint32 i = 0; i < items_to_commit; i++) {
2500 string nameutf8 = base::StringPrintf("%d", i);
2501 string name(nameutf8.begin(), nameutf8.end());
2502 MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
2503 e.PutIsUnsynced(true);
2505 e.PutSpecifics(DefaultBookmarkSpecifics());
2508 ASSERT_EQ(items_to_commit, directory()->unsynced_entity_count());
2510 // Return a CONFLICT response for the first item.
2511 mock_server_->set_conflict_n_commits(1);
2514 // We should stop looping at the first sign of trouble.
2515 EXPECT_EQ(1U, mock_server_->commit_messages().size());
2516 EXPECT_EQ(items_to_commit - (kDefaultMaxCommitBatchSize - 1),
2517 directory()->unsynced_entity_count());
2520 TEST_F(SyncerTest, HugeConflict) {
2521 int item_count = 300; // We should be able to do 300 or 3000 w/o issue.
2523 syncable::Id parent_id = ids_.NewServerId();
2524 syncable::Id last_id = parent_id;
2525 vector<syncable::Id> tree_ids;
2527 // Create a lot of updates for which the parent does not exist yet.
2528 // Generate a huge deep tree which should all fail to apply at first.
2530 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2531 for (int i = 0; i < item_count ; i++) {
2532 syncable::Id next_id = ids_.NewServerId();
2533 syncable::Id local_id = ids_.NewLocalId();
2534 tree_ids.push_back(next_id);
2535 mock_server_->AddUpdateDirectory(next_id, last_id, "BOB", 2, 20,
2536 foreign_cache_guid(),
2537 local_id.GetServerId());
2543 // Check they're in the expected conflict state.
2545 syncable::ReadTransaction trans(FROM_HERE, directory());
2546 for (int i = 0; i < item_count; i++) {
2547 Entry e(&trans, GET_BY_ID, tree_ids[i]);
2548 // They should all exist but none should be applied.
2549 ASSERT_TRUE(e.good());
2550 EXPECT_TRUE(e.GetIsDel());
2551 EXPECT_TRUE(e.GetIsUnappliedUpdate());
2555 // Add the missing parent directory.
2556 mock_server_->AddUpdateDirectory(parent_id, TestIdFactory::root(),
2557 "BOB", 2, 20, foreign_cache_guid(), "-3500");
2560 // Now they should all be OK.
2562 syncable::ReadTransaction trans(FROM_HERE, directory());
2563 for (int i = 0; i < item_count; i++) {
2564 Entry e(&trans, GET_BY_ID, tree_ids[i]);
2565 ASSERT_TRUE(e.good());
2566 EXPECT_FALSE(e.GetIsDel());
2567 EXPECT_FALSE(e.GetIsUnappliedUpdate());
2572 TEST_F(SyncerTest, DontCrashOnCaseChange) {
2573 mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10,
2574 foreign_cache_guid(), "-1");
2577 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2578 MutableEntry e(&trans, GET_BY_ID, ids_.FromNumber(1));
2579 ASSERT_TRUE(e.good());
2580 e.PutIsUnsynced(true);
2582 mock_server_->set_conflict_all_commits(true);
2583 mock_server_->AddUpdateDirectory(1, 0, "BOB", 2, 20,
2584 foreign_cache_guid(), "-1");
2585 SyncShareNudge(); // USED TO CAUSE AN ASSERT
2586 saw_syncer_event_ = false;
2589 TEST_F(SyncerTest, UnsyncedItemAndUpdate) {
2590 mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10,
2591 foreign_cache_guid(), "-1");
2593 mock_server_->set_conflict_all_commits(true);
2594 mock_server_->AddUpdateDirectory(2, 0, "bob", 2, 20,
2595 foreign_cache_guid(), "-2");
2596 SyncShareNudge(); // USED TO CAUSE AN ASSERT
2597 saw_syncer_event_ = false;
2600 TEST_F(SyncerTest, NewEntryAndAlteredServerEntrySharePath) {
2601 mock_server_->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
2602 foreign_cache_guid(), "-1");
2604 int64 local_folder_handle;
2605 syncable::Id local_folder_id;
2607 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2608 MutableEntry new_entry(
2609 &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Bar.htm");
2610 ASSERT_TRUE(new_entry.good());
2611 local_folder_id = new_entry.GetId();
2612 local_folder_handle = new_entry.GetMetahandle();
2613 new_entry.PutIsUnsynced(true);
2614 new_entry.PutSpecifics(DefaultBookmarkSpecifics());
2615 MutableEntry old(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2616 ASSERT_TRUE(old.good());
2617 WriteTestDataToEntry(&wtrans, &old);
2619 mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
2620 foreign_cache_guid(), "-1");
2621 mock_server_->set_conflict_all_commits(true);
2623 saw_syncer_event_ = false;
2625 // Update #20 should have been dropped in favor of the local version.
2626 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2627 MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2628 MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle);
2629 ASSERT_TRUE(server.good());
2630 ASSERT_TRUE(local.good());
2631 EXPECT_TRUE(local.GetMetahandle()!= server.GetMetahandle());
2632 EXPECT_FALSE(server.GetIsUnappliedUpdate());
2633 EXPECT_FALSE(local.GetIsUnappliedUpdate());
2634 EXPECT_TRUE(server.GetIsUnsynced());
2635 EXPECT_TRUE(local.GetIsUnsynced());
2636 EXPECT_EQ("Foo.htm", server.GetNonUniqueName());
2637 EXPECT_EQ("Bar.htm", local.GetNonUniqueName());
2639 // Allow local changes to commit.
2640 mock_server_->set_conflict_all_commits(false);
2642 saw_syncer_event_ = false;
2644 // Now add a server change to make the two names equal. There should
2645 // be no conflict with that, since names are not unique.
2646 mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
2647 foreign_cache_guid(), "-1");
2649 saw_syncer_event_ = false;
2651 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2652 MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2653 MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle);
2654 ASSERT_TRUE(server.good());
2655 ASSERT_TRUE(local.good());
2656 EXPECT_TRUE(local.GetMetahandle()!= server.GetMetahandle());
2657 EXPECT_FALSE(server.GetIsUnappliedUpdate());
2658 EXPECT_FALSE(local.GetIsUnappliedUpdate());
2659 EXPECT_FALSE(server.GetIsUnsynced());
2660 EXPECT_FALSE(local.GetIsUnsynced());
2661 EXPECT_EQ("Bar.htm", server.GetNonUniqueName());
2662 EXPECT_EQ("Bar.htm", local.GetNonUniqueName());
2663 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
2664 server.GetSpecifics().bookmark().url());
2668 // Same as NewEntryAnddServerEntrySharePath, but using the old-style protocol.
2669 TEST_F(SyncerTest, NewEntryAndAlteredServerEntrySharePath_OldBookmarksProto) {
2670 mock_server_->set_use_legacy_bookmarks_protocol(true);
2671 mock_server_->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
2672 foreign_cache_guid(), "-1");
2674 int64 local_folder_handle;
2675 syncable::Id local_folder_id;
2677 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2678 MutableEntry new_entry(
2679 &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Bar.htm");
2680 ASSERT_TRUE(new_entry.good());
2681 local_folder_id = new_entry.GetId();
2682 local_folder_handle = new_entry.GetMetahandle();
2683 new_entry.PutIsUnsynced(true);
2684 new_entry.PutSpecifics(DefaultBookmarkSpecifics());
2685 MutableEntry old(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2686 ASSERT_TRUE(old.good());
2687 WriteTestDataToEntry(&wtrans, &old);
2689 mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
2690 foreign_cache_guid(), "-1");
2691 mock_server_->set_conflict_all_commits(true);
2693 saw_syncer_event_ = false;
2695 // Update #20 should have been dropped in favor of the local version.
2696 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2697 MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2698 MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle);
2699 ASSERT_TRUE(server.good());
2700 ASSERT_TRUE(local.good());
2701 EXPECT_TRUE(local.GetMetahandle()!= server.GetMetahandle());
2702 EXPECT_FALSE(server.GetIsUnappliedUpdate());
2703 EXPECT_FALSE(local.GetIsUnappliedUpdate());
2704 EXPECT_TRUE(server.GetIsUnsynced());
2705 EXPECT_TRUE(local.GetIsUnsynced());
2706 EXPECT_EQ("Foo.htm", server.GetNonUniqueName());
2707 EXPECT_EQ("Bar.htm", local.GetNonUniqueName());
2709 // Allow local changes to commit.
2710 mock_server_->set_conflict_all_commits(false);
2712 saw_syncer_event_ = false;
2714 // Now add a server change to make the two names equal. There should
2715 // be no conflict with that, since names are not unique.
2716 mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
2717 foreign_cache_guid(), "-1");
2719 saw_syncer_event_ = false;
2721 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2722 MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2723 MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle);
2724 ASSERT_TRUE(server.good());
2725 ASSERT_TRUE(local.good());
2726 EXPECT_TRUE(local.GetMetahandle()!= server.GetMetahandle());
2727 EXPECT_FALSE(server.GetIsUnappliedUpdate());
2728 EXPECT_FALSE(local.GetIsUnappliedUpdate());
2729 EXPECT_FALSE(server.GetIsUnsynced());
2730 EXPECT_FALSE(local.GetIsUnsynced());
2731 EXPECT_EQ("Bar.htm", server.GetNonUniqueName());
2732 EXPECT_EQ("Bar.htm", local.GetNonUniqueName());
2733 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
2734 server.GetSpecifics().bookmark().url());
2738 // Circular links should be resolved by the server.
2739 TEST_F(SyncerTest, SiblingDirectoriesBecomeCircular) {
2740 // we don't currently resolve this. This test ensures we don't.
2741 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
2742 foreign_cache_guid(), "-1");
2743 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10,
2744 foreign_cache_guid(), "-2");
2747 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2748 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2749 ASSERT_TRUE(A.good());
2750 A.PutIsUnsynced(true);
2751 A.PutParentId(ids_.FromNumber(2));
2752 A.PutNonUniqueName("B");
2754 mock_server_->AddUpdateDirectory(2, 1, "A", 20, 20,
2755 foreign_cache_guid(), "-2");
2756 mock_server_->set_conflict_all_commits(true);
2758 saw_syncer_event_ = false;
2760 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2761 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2762 ASSERT_TRUE(A.good());
2763 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
2764 ASSERT_TRUE(B.good());
2765 EXPECT_TRUE(A.GetNonUniqueName()== "B");
2766 EXPECT_TRUE(B.GetNonUniqueName()== "B");
2770 TEST_F(SyncerTest, SwapEntryNames) {
2771 // Simple transaction test.
2772 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
2773 foreign_cache_guid(), "-1");
2774 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10,
2775 foreign_cache_guid(), "-2");
2776 mock_server_->set_conflict_all_commits(true);
2779 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2780 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2781 ASSERT_TRUE(A.good());
2782 A.PutIsUnsynced(true);
2783 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
2784 ASSERT_TRUE(B.good());
2785 B.PutIsUnsynced(true);
2786 A.PutNonUniqueName("C");
2787 B.PutNonUniqueName("A");
2788 A.PutNonUniqueName("B");
2791 saw_syncer_event_ = false;
2794 TEST_F(SyncerTest, DualDeletionWithNewItemNameClash) {
2795 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
2796 foreign_cache_guid(), "-1");
2797 mock_server_->AddUpdateBookmark(2, 0, "B", 10, 10,
2798 foreign_cache_guid(), "-2");
2799 mock_server_->set_conflict_all_commits(true);
2802 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2803 MutableEntry B(&trans, GET_BY_ID, ids_.FromNumber(2));
2804 ASSERT_TRUE(B.good());
2805 WriteTestDataToEntry(&trans, &B);
2808 mock_server_->AddUpdateBookmark(2, 0, "A", 11, 11,
2809 foreign_cache_guid(), "-2");
2810 mock_server_->SetLastUpdateDeleted();
2813 syncable::ReadTransaction trans(FROM_HERE, directory());
2814 Entry B(&trans, GET_BY_ID, ids_.FromNumber(2));
2815 ASSERT_TRUE(B.good());
2816 EXPECT_FALSE(B.GetIsUnsynced());
2817 EXPECT_FALSE(B.GetIsUnappliedUpdate());
2819 saw_syncer_event_ = false;
2822 // When we undelete an entity as a result of conflict resolution, we reuse the
2823 // existing server id and preserve the old version, simply updating the server
2824 // version with the new non-deleted entity.
2825 TEST_F(SyncerTest, ResolveWeWroteTheyDeleted) {
2826 int64 bob_metahandle;
2828 mock_server_->AddUpdateBookmark(1, 0, "bob", 1, 10,
2829 foreign_cache_guid(), "-1");
2832 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2833 MutableEntry bob(&trans, GET_BY_ID, ids_.FromNumber(1));
2834 ASSERT_TRUE(bob.good());
2835 bob_metahandle = bob.GetMetahandle();
2836 WriteTestDataToEntry(&trans, &bob);
2838 mock_server_->AddUpdateBookmark(1, 0, "bob", 2, 10,
2839 foreign_cache_guid(), "-1");
2840 mock_server_->SetLastUpdateDeleted();
2841 mock_server_->set_conflict_all_commits(true);
2845 syncable::ReadTransaction trans(FROM_HERE, directory());
2846 Entry bob(&trans, GET_BY_HANDLE, bob_metahandle);
2847 ASSERT_TRUE(bob.good());
2848 EXPECT_TRUE(bob.GetIsUnsynced());
2849 EXPECT_TRUE(bob.GetId().ServerKnows());
2850 EXPECT_FALSE(bob.GetIsUnappliedUpdate());
2851 EXPECT_FALSE(bob.GetIsDel());
2852 EXPECT_EQ(2, bob.GetServerVersion());
2853 EXPECT_EQ(2, bob.GetBaseVersion());
2855 saw_syncer_event_ = false;
2858 // This test is to reproduce a check failure. Sometimes we would get a bad ID
2859 // back when creating an entry.
2860 TEST_F(SyncerTest, DuplicateIDReturn) {
2862 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2863 MutableEntry folder(&trans, CREATE, BOOKMARKS, trans.root_id(), "bob");
2864 ASSERT_TRUE(folder.good());
2865 folder.PutIsUnsynced(true);
2866 folder.PutIsDir(true);
2867 folder.PutSpecifics(DefaultBookmarkSpecifics());
2868 MutableEntry folder2(&trans, CREATE, BOOKMARKS, trans.root_id(), "fred");
2869 ASSERT_TRUE(folder2.good());
2870 folder2.PutIsUnsynced(false);
2871 folder2.PutIsDir(true);
2872 folder2.PutSpecifics(DefaultBookmarkSpecifics());
2873 folder2.PutBaseVersion(3);
2874 folder2.PutId(syncable::Id::CreateFromServerId("mock_server:10000"));
2876 mock_server_->set_next_new_id(10000);
2877 EXPECT_EQ(1u, directory()->unsynced_entity_count());
2878 // we get back a bad id in here (should never happen).
2880 EXPECT_EQ(1u, directory()->unsynced_entity_count());
2881 SyncShareNudge(); // another bad id in here.
2882 EXPECT_EQ(0u, directory()->unsynced_entity_count());
2883 saw_syncer_event_ = false;
2886 TEST_F(SyncerTest, DeletedEntryWithBadParentInLoopCalculation) {
2887 mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10,
2888 foreign_cache_guid(), "-1");
2891 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2892 MutableEntry bob(&trans, GET_BY_ID, ids_.FromNumber(1));
2893 ASSERT_TRUE(bob.good());
2894 // This is valid, because the parent could have gone away a long time ago.
2895 bob.PutParentId(ids_.FromNumber(54));
2897 bob.PutIsUnsynced(true);
2899 mock_server_->AddUpdateDirectory(2, 1, "fred", 1, 10,
2900 foreign_cache_guid(), "-2");
2905 TEST_F(SyncerTest, ConflictResolverMergesLocalDeleteAndServerUpdate) {
2906 syncable::Id local_id;
2908 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2910 MutableEntry local_deleted(
2911 &trans, CREATE, BOOKMARKS, trans.root_id(), "name");
2912 local_id = local_deleted.GetId();
2913 local_deleted.PutId(ids_.FromNumber(1));
2914 local_deleted.PutBaseVersion(1);
2915 local_deleted.PutIsDel(true);
2916 local_deleted.PutIsDir(false);
2917 local_deleted.PutIsUnsynced(true);
2918 local_deleted.PutSpecifics(DefaultBookmarkSpecifics());
2921 mock_server_->AddUpdateBookmark(ids_.FromNumber(1), root_id_, "name", 10, 10,
2923 local_id.GetServerId());
2925 // We don't care about actually committing, just the resolution.
2926 mock_server_->set_conflict_all_commits(true);
2930 syncable::ReadTransaction trans(FROM_HERE, directory());
2931 Entry local_deleted(&trans, GET_BY_ID, ids_.FromNumber(1));
2932 EXPECT_TRUE(local_deleted.GetBaseVersion()== 10);
2933 EXPECT_TRUE(local_deleted.GetIsUnappliedUpdate()== false);
2934 EXPECT_TRUE(local_deleted.GetIsUnsynced()== true);
2935 EXPECT_TRUE(local_deleted.GetIsDel()== true);
2936 EXPECT_TRUE(local_deleted.GetIsDir()== false);
2940 // See what happens if the IS_DIR bit gets flipped. This can cause us
2941 // all kinds of disasters.
2942 TEST_F(SyncerTest, UpdateFlipsTheFolderBit) {
2943 // Local object: a deleted directory (container), revision 1, unsynced.
2945 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2947 MutableEntry local_deleted(
2948 &trans, CREATE, BOOKMARKS, trans.root_id(), "name");
2949 local_deleted.PutId(ids_.FromNumber(1));
2950 local_deleted.PutBaseVersion(1);
2951 local_deleted.PutIsDel(true);
2952 local_deleted.PutIsDir(true);
2953 local_deleted.PutIsUnsynced(true);
2954 local_deleted.PutSpecifics(DefaultBookmarkSpecifics());
2957 // Server update: entry-type object (not a container), revision 10.
2958 mock_server_->AddUpdateBookmark(ids_.FromNumber(1), root_id_, "name", 10, 10,
2960 ids_.FromNumber(1).GetServerId());
2962 // Don't attempt to commit.
2963 mock_server_->set_conflict_all_commits(true);
2965 // The syncer should not attempt to apply the invalid update.
2969 syncable::ReadTransaction trans(FROM_HERE, directory());
2970 Entry local_deleted(&trans, GET_BY_ID, ids_.FromNumber(1));
2971 EXPECT_TRUE(local_deleted.GetBaseVersion()== 1);
2972 EXPECT_TRUE(local_deleted.GetIsUnappliedUpdate()== false);
2973 EXPECT_TRUE(local_deleted.GetIsUnsynced()== true);
2974 EXPECT_TRUE(local_deleted.GetIsDel()== true);
2975 EXPECT_TRUE(local_deleted.GetIsDir()== true);
2980 // Merge conflict resolution will merge a new local entry with another entry
2981 // that needs updates, resulting in CHECK.
2982 TEST_F(SyncerTest, MergingExistingItems) {
2983 mock_server_->set_conflict_all_commits(true);
2984 mock_server_->AddUpdateBookmark(1, 0, "base", 10, 10,
2985 local_cache_guid(), "-1");
2988 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2990 &trans, CREATE, BOOKMARKS, trans.root_id(), "Copy of base");
2991 WriteTestDataToEntry(&trans, &entry);
2993 mock_server_->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
2994 local_cache_guid(), "-1");
2998 // In this test a long changelog contains a child at the start of the changelog
2999 // and a parent at the end. While these updates are in progress the client would
3001 TEST_F(SyncerTest, LongChangelistWithApplicationConflict) {
3002 const int depth = 400;
3003 syncable::Id folder_id = ids_.FromNumber(1);
3005 // First we an item in a folder in the root. However the folder won't come
3007 syncable::Id stuck_entry_id = TestIdFactory::FromNumber(99999);
3008 mock_server_->AddUpdateDirectory(stuck_entry_id,
3009 folder_id, "stuck", 1, 1,
3010 foreign_cache_guid(), "-99999");
3011 mock_server_->SetChangesRemaining(depth - 1);
3014 // Buffer up a very long series of downloads.
3015 // We should never be stuck (conflict resolution shouldn't
3016 // kick in so long as we're making forward progress).
3017 for (int i = 0; i < depth; i++) {
3018 mock_server_->NextUpdateBatch();
3019 mock_server_->SetNewTimestamp(i + 1);
3020 mock_server_->SetChangesRemaining(depth - i);
3025 // Ensure our folder hasn't somehow applied.
3027 syncable::ReadTransaction trans(FROM_HERE, directory());
3028 Entry child(&trans, GET_BY_ID, stuck_entry_id);
3029 EXPECT_TRUE(child.good());
3030 EXPECT_TRUE(child.GetIsUnappliedUpdate());
3031 EXPECT_TRUE(child.GetIsDel());
3032 EXPECT_FALSE(child.GetIsUnsynced());
3035 // And finally the folder.
3036 mock_server_->AddUpdateDirectory(folder_id,
3037 TestIdFactory::root(), "folder", 1, 1,
3038 foreign_cache_guid(), "-1");
3039 mock_server_->SetChangesRemaining(0);
3042 // Check that everything is as expected after the commit.
3044 syncable::ReadTransaction trans(FROM_HERE, directory());
3045 Entry entry(&trans, GET_BY_ID, folder_id);
3046 ASSERT_TRUE(entry.good());
3047 Entry child(&trans, GET_BY_ID, stuck_entry_id);
3048 EXPECT_EQ(entry.GetId(), child.GetParentId());
3049 EXPECT_EQ("stuck", child.GetNonUniqueName());
3050 EXPECT_TRUE(child.good());
3054 TEST_F(SyncerTest, DontMergeTwoExistingItems) {
3055 mock_server_->set_conflict_all_commits(true);
3056 mock_server_->AddUpdateBookmark(1, 0, "base", 10, 10,
3057 foreign_cache_guid(), "-1");
3058 mock_server_->AddUpdateBookmark(2, 0, "base2", 10, 10,
3059 foreign_cache_guid(), "-2");
3062 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3063 MutableEntry entry(&trans, GET_BY_ID, ids_.FromNumber(2));
3064 ASSERT_TRUE(entry.good());
3065 entry.PutNonUniqueName("Copy of base");
3066 entry.PutIsUnsynced(true);
3068 mock_server_->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
3069 foreign_cache_guid(), "-1");
3072 syncable::ReadTransaction trans(FROM_HERE, directory());
3073 Entry entry1(&trans, GET_BY_ID, ids_.FromNumber(1));
3074 EXPECT_FALSE(entry1.GetIsUnappliedUpdate());
3075 EXPECT_FALSE(entry1.GetIsUnsynced());
3076 EXPECT_FALSE(entry1.GetIsDel());
3077 Entry entry2(&trans, GET_BY_ID, ids_.FromNumber(2));
3078 EXPECT_FALSE(entry2.GetIsUnappliedUpdate());
3079 EXPECT_TRUE(entry2.GetIsUnsynced());
3080 EXPECT_FALSE(entry2.GetIsDel());
3081 EXPECT_EQ(entry1.GetNonUniqueName(), entry2.GetNonUniqueName());
3085 TEST_F(SyncerTest, TestUndeleteUpdate) {
3086 mock_server_->set_conflict_all_commits(true);
3087 mock_server_->AddUpdateDirectory(1, 0, "foo", 1, 1,
3088 foreign_cache_guid(), "-1");
3089 mock_server_->AddUpdateDirectory(2, 1, "bar", 1, 2,
3090 foreign_cache_guid(), "-2");
3092 mock_server_->AddUpdateDirectory(2, 1, "bar", 2, 3,
3093 foreign_cache_guid(), "-2");
3094 mock_server_->SetLastUpdateDeleted();
3099 syncable::ReadTransaction trans(FROM_HERE, directory());
3100 Entry entry(&trans, GET_BY_ID, ids_.FromNumber(2));
3101 ASSERT_TRUE(entry.good());
3102 EXPECT_TRUE(entry.GetIsDel());
3103 metahandle = entry.GetMetahandle();
3105 mock_server_->AddUpdateDirectory(1, 0, "foo", 2, 4,
3106 foreign_cache_guid(), "-1");
3107 mock_server_->SetLastUpdateDeleted();
3109 // This used to be rejected as it's an undeletion. Now, it results in moving
3110 // the delete path aside.
3111 mock_server_->AddUpdateDirectory(2, 1, "bar", 3, 5,
3112 foreign_cache_guid(), "-2");
3115 syncable::ReadTransaction trans(FROM_HERE, directory());
3116 Entry entry(&trans, GET_BY_ID, ids_.FromNumber(2));
3117 ASSERT_TRUE(entry.good());
3118 EXPECT_TRUE(entry.GetIsDel());
3119 EXPECT_FALSE(entry.GetServerIsDel());
3120 EXPECT_TRUE(entry.GetIsUnappliedUpdate());
3121 EXPECT_NE(entry.GetMetahandle(), metahandle);
3125 TEST_F(SyncerTest, TestMoveSanitizedNamedFolder) {
3126 mock_server_->AddUpdateDirectory(1, 0, "foo", 1, 1,
3127 foreign_cache_guid(), "-1");
3128 mock_server_->AddUpdateDirectory(2, 0, ":::", 1, 2,
3129 foreign_cache_guid(), "-2");
3132 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3133 MutableEntry entry(&trans, GET_BY_ID, ids_.FromNumber(2));
3134 ASSERT_TRUE(entry.good());
3135 entry.PutParentId(ids_.FromNumber(1));
3136 EXPECT_TRUE(entry.PutIsUnsynced(true));
3139 // We use the same sync ts as before so our times match up.
3140 mock_server_->AddUpdateDirectory(2, 1, ":::", 2, 2,
3141 foreign_cache_guid(), "-2");
3145 // Don't crash when this occurs.
3146 TEST_F(SyncerTest, UpdateWhereParentIsNotAFolder) {
3147 mock_server_->AddUpdateBookmark(1, 0, "B", 10, 10,
3148 foreign_cache_guid(), "-1");
3149 mock_server_->AddUpdateDirectory(2, 1, "BookmarkParent", 10, 10,
3150 foreign_cache_guid(), "-2");
3151 // Used to cause a CHECK
3154 syncable::ReadTransaction rtrans(FROM_HERE, directory());
3155 Entry good_entry(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1));
3156 ASSERT_TRUE(good_entry.good());
3157 EXPECT_FALSE(good_entry.GetIsUnappliedUpdate());
3158 Entry bad_parent(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(2));
3159 ASSERT_TRUE(bad_parent.good());
3160 EXPECT_TRUE(bad_parent.GetIsUnappliedUpdate());
3164 TEST_F(SyncerTest, DirectoryUpdateTest) {
3165 Id in_root_id = ids_.NewServerId();
3166 Id in_in_root_id = ids_.NewServerId();
3168 mock_server_->AddUpdateDirectory(in_root_id, TestIdFactory::root(),
3169 "in_root_name", 2, 2,
3170 foreign_cache_guid(), "-1");
3171 mock_server_->AddUpdateDirectory(in_in_root_id, in_root_id,
3172 "in_in_root_name", 3, 3,
3173 foreign_cache_guid(), "-2");
3176 syncable::ReadTransaction trans(FROM_HERE, directory());
3177 Entry in_root(&trans, GET_BY_ID, in_root_id);
3178 ASSERT_TRUE(in_root.good());
3179 EXPECT_EQ("in_root_name", in_root.GetNonUniqueName());
3180 EXPECT_EQ(TestIdFactory::root(), in_root.GetParentId());
3182 Entry in_in_root(&trans, GET_BY_ID, in_in_root_id);
3183 ASSERT_TRUE(in_in_root.good());
3184 EXPECT_EQ("in_in_root_name", in_in_root.GetNonUniqueName());
3185 EXPECT_EQ(in_root_id, in_in_root.GetParentId());
3189 TEST_F(SyncerTest, DirectoryCommitTest) {
3190 syncable::Id in_root_id, in_dir_id;
3191 int64 foo_metahandle;
3192 int64 bar_metahandle;
3195 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3196 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, root_id_, "foo");
3197 ASSERT_TRUE(parent.good());
3198 parent.PutIsUnsynced(true);
3199 parent.PutIsDir(true);
3200 parent.PutSpecifics(DefaultBookmarkSpecifics());
3201 in_root_id = parent.GetId();
3202 foo_metahandle = parent.GetMetahandle();
3204 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent.GetId(), "bar");
3205 ASSERT_TRUE(child.good());
3206 child.PutIsUnsynced(true);
3207 child.PutIsDir(true);
3208 child.PutSpecifics(DefaultBookmarkSpecifics());
3209 bar_metahandle = child.GetMetahandle();
3210 in_dir_id = parent.GetId();
3214 syncable::ReadTransaction trans(FROM_HERE, directory());
3215 Entry fail_by_old_id_entry(&trans, GET_BY_ID, in_root_id);
3216 ASSERT_FALSE(fail_by_old_id_entry.good());
3218 Entry foo_entry(&trans, GET_BY_HANDLE, foo_metahandle);
3219 ASSERT_TRUE(foo_entry.good());
3220 EXPECT_EQ("foo", foo_entry.GetNonUniqueName());
3221 EXPECT_NE(foo_entry.GetId(), in_root_id);
3223 Entry bar_entry(&trans, GET_BY_HANDLE, bar_metahandle);
3224 ASSERT_TRUE(bar_entry.good());
3225 EXPECT_EQ("bar", bar_entry.GetNonUniqueName());
3226 EXPECT_NE(bar_entry.GetId(), in_dir_id);
3227 EXPECT_EQ(foo_entry.GetId(), bar_entry.GetParentId());
3231 TEST_F(SyncerTest, TestClientCommandDuringUpdate) {
3232 using sync_pb::ClientCommand;
3234 ClientCommand* command = new ClientCommand();
3235 command->set_set_sync_poll_interval(8);
3236 command->set_set_sync_long_poll_interval(800);
3237 command->set_sessions_commit_delay_seconds(3141);
3238 command->set_client_invalidation_hint_buffer_size(11);
3239 mock_server_->AddUpdateDirectory(1, 0, "in_root", 1, 1,
3240 foreign_cache_guid(), "-1");
3241 mock_server_->SetGUClientCommand(command);
3244 EXPECT_TRUE(TimeDelta::FromSeconds(8) ==
3245 last_short_poll_interval_received_);
3246 EXPECT_TRUE(TimeDelta::FromSeconds(800) ==
3247 last_long_poll_interval_received_);
3248 EXPECT_TRUE(TimeDelta::FromSeconds(3141) ==
3249 last_sessions_commit_delay_seconds_);
3250 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_);
3252 command = new ClientCommand();
3253 command->set_set_sync_poll_interval(180);
3254 command->set_set_sync_long_poll_interval(190);
3255 command->set_sessions_commit_delay_seconds(2718);
3256 command->set_client_invalidation_hint_buffer_size(9);
3257 mock_server_->AddUpdateDirectory(1, 0, "in_root", 1, 1,
3258 foreign_cache_guid(), "-1");
3259 mock_server_->SetGUClientCommand(command);
3262 EXPECT_TRUE(TimeDelta::FromSeconds(180) ==
3263 last_short_poll_interval_received_);
3264 EXPECT_TRUE(TimeDelta::FromSeconds(190) ==
3265 last_long_poll_interval_received_);
3266 EXPECT_TRUE(TimeDelta::FromSeconds(2718) ==
3267 last_sessions_commit_delay_seconds_);
3268 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_);
3271 TEST_F(SyncerTest, TestClientCommandDuringCommit) {
3272 using sync_pb::ClientCommand;
3274 ClientCommand* command = new ClientCommand();
3275 command->set_set_sync_poll_interval(8);
3276 command->set_set_sync_long_poll_interval(800);
3277 command->set_sessions_commit_delay_seconds(3141);
3278 command->set_client_invalidation_hint_buffer_size(11);
3279 CreateUnsyncedDirectory("X", "id_X");
3280 mock_server_->SetCommitClientCommand(command);
3283 EXPECT_TRUE(TimeDelta::FromSeconds(8) ==
3284 last_short_poll_interval_received_);
3285 EXPECT_TRUE(TimeDelta::FromSeconds(800) ==
3286 last_long_poll_interval_received_);
3287 EXPECT_TRUE(TimeDelta::FromSeconds(3141) ==
3288 last_sessions_commit_delay_seconds_);
3289 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_);
3291 command = new ClientCommand();
3292 command->set_set_sync_poll_interval(180);
3293 command->set_set_sync_long_poll_interval(190);
3294 command->set_sessions_commit_delay_seconds(2718);
3295 command->set_client_invalidation_hint_buffer_size(9);
3296 CreateUnsyncedDirectory("Y", "id_Y");
3297 mock_server_->SetCommitClientCommand(command);
3300 EXPECT_TRUE(TimeDelta::FromSeconds(180) ==
3301 last_short_poll_interval_received_);
3302 EXPECT_TRUE(TimeDelta::FromSeconds(190) ==
3303 last_long_poll_interval_received_);
3304 EXPECT_TRUE(TimeDelta::FromSeconds(2718) ==
3305 last_sessions_commit_delay_seconds_);
3306 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_);
3309 TEST_F(SyncerTest, EnsureWeSendUpOldParent) {
3310 syncable::Id folder_one_id = ids_.FromNumber(1);
3311 syncable::Id folder_two_id = ids_.FromNumber(2);
3313 mock_server_->AddUpdateDirectory(folder_one_id, TestIdFactory::root(),
3314 "folder_one", 1, 1, foreign_cache_guid(), "-1");
3315 mock_server_->AddUpdateDirectory(folder_two_id, TestIdFactory::root(),
3316 "folder_two", 1, 1, foreign_cache_guid(), "-2");
3319 // A moved entry should send an "old parent."
3320 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3321 MutableEntry entry(&trans, GET_BY_ID, folder_one_id);
3322 ASSERT_TRUE(entry.good());
3323 entry.PutParentId(folder_two_id);
3324 entry.PutIsUnsynced(true);
3325 // A new entry should send no "old parent."
3326 MutableEntry create(
3327 &trans, CREATE, BOOKMARKS, trans.root_id(), "new_folder");
3328 create.PutIsUnsynced(true);
3329 create.PutSpecifics(DefaultBookmarkSpecifics());
3332 const sync_pb::CommitMessage& commit = mock_server_->last_sent_commit();
3333 ASSERT_EQ(2, commit.entries_size());
3334 EXPECT_TRUE(commit.entries(0).parent_id_string() == "2");
3335 EXPECT_TRUE(commit.entries(0).old_parent_id() == "0");
3336 EXPECT_FALSE(commit.entries(1).has_old_parent_id());
3339 TEST_F(SyncerTest, Test64BitVersionSupport) {
3340 int64 really_big_int = std::numeric_limits<int64>::max() - 12;
3341 const string name("ringo's dang orang ran rings around my o-ring");
3342 int64 item_metahandle;
3344 // Try writing max int64 to the version fields of a meta entry.
3346 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3347 MutableEntry entry(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), name);
3348 ASSERT_TRUE(entry.good());
3349 entry.PutBaseVersion(really_big_int);
3350 entry.PutServerVersion(really_big_int);
3351 entry.PutId(ids_.NewServerId());
3352 item_metahandle = entry.GetMetahandle();
3354 // Now read it back out and make sure the value is max int64.
3355 syncable::ReadTransaction rtrans(FROM_HERE, directory());
3356 Entry entry(&rtrans, syncable::GET_BY_HANDLE, item_metahandle);
3357 ASSERT_TRUE(entry.good());
3358 EXPECT_TRUE(really_big_int == entry.GetBaseVersion());
3361 TEST_F(SyncerTest, TestSimpleUndelete) {
3362 Id id = ids_.MakeServer("undeletion item"), root = TestIdFactory::root();
3363 mock_server_->set_conflict_all_commits(true);
3364 // Let there be an entry from the server.
3365 mock_server_->AddUpdateBookmark(id, root, "foo", 1, 10,
3366 foreign_cache_guid(), "-1");
3368 // Check it out and delete it.
3370 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3371 MutableEntry entry(&wtrans, GET_BY_ID, id);
3372 ASSERT_TRUE(entry.good());
3373 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3374 EXPECT_FALSE(entry.GetIsUnsynced());
3375 EXPECT_FALSE(entry.GetIsDel());
3376 // Delete it locally.
3377 entry.PutIsDel(true);
3380 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3382 syncable::ReadTransaction trans(FROM_HERE, directory());
3383 Entry entry(&trans, GET_BY_ID, id);
3384 ASSERT_TRUE(entry.good());
3385 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3386 EXPECT_FALSE(entry.GetIsUnsynced());
3387 EXPECT_TRUE(entry.GetIsDel());
3388 EXPECT_FALSE(entry.GetServerIsDel());
3391 // Update from server confirming deletion.
3392 mock_server_->AddUpdateBookmark(id, root, "foo", 2, 11,
3393 foreign_cache_guid(), "-1");
3394 mock_server_->SetLastUpdateDeleted();
3396 // IS_DEL AND SERVER_IS_DEL now both true.
3398 syncable::ReadTransaction trans(FROM_HERE, directory());
3399 Entry entry(&trans, GET_BY_ID, id);
3400 ASSERT_TRUE(entry.good());
3401 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3402 EXPECT_FALSE(entry.GetIsUnsynced());
3403 EXPECT_TRUE(entry.GetIsDel());
3404 EXPECT_TRUE(entry.GetServerIsDel());
3406 // Undelete from server.
3407 mock_server_->AddUpdateBookmark(id, root, "foo", 2, 12,
3408 foreign_cache_guid(), "-1");
3410 // IS_DEL and SERVER_IS_DEL now both false.
3412 syncable::ReadTransaction trans(FROM_HERE, directory());
3413 Entry entry(&trans, GET_BY_ID, id);
3414 ASSERT_TRUE(entry.good());
3415 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3416 EXPECT_FALSE(entry.GetIsUnsynced());
3417 EXPECT_FALSE(entry.GetIsDel());
3418 EXPECT_FALSE(entry.GetServerIsDel());
3422 TEST_F(SyncerTest, TestUndeleteWithMissingDeleteUpdate) {
3423 Id id = ids_.MakeServer("undeletion item"), root = TestIdFactory::root();
3424 // Let there be a entry, from the server.
3425 mock_server_->set_conflict_all_commits(true);
3426 mock_server_->AddUpdateBookmark(id, root, "foo", 1, 10,
3427 foreign_cache_guid(), "-1");
3429 // Check it out and delete it.
3431 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3432 MutableEntry entry(&wtrans, GET_BY_ID, id);
3433 ASSERT_TRUE(entry.good());
3434 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3435 EXPECT_FALSE(entry.GetIsUnsynced());
3436 EXPECT_FALSE(entry.GetIsDel());
3437 // Delete it locally.
3438 entry.PutIsDel(true);
3441 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3443 syncable::ReadTransaction trans(FROM_HERE, directory());
3444 Entry entry(&trans, GET_BY_ID, id);
3445 ASSERT_TRUE(entry.good());
3446 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3447 EXPECT_FALSE(entry.GetIsUnsynced());
3448 EXPECT_TRUE(entry.GetIsDel());
3449 EXPECT_FALSE(entry.GetServerIsDel());
3452 // Say we do not get an update from server confirming deletion. Undelete
3454 mock_server_->AddUpdateBookmark(id, root, "foo", 2, 12,
3455 foreign_cache_guid(), "-1");
3457 // IS_DEL and SERVER_IS_DEL now both false.
3459 syncable::ReadTransaction trans(FROM_HERE, directory());
3460 Entry entry(&trans, GET_BY_ID, id);
3461 ASSERT_TRUE(entry.good());
3462 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3463 EXPECT_FALSE(entry.GetIsUnsynced());
3464 EXPECT_FALSE(entry.GetIsDel());
3465 EXPECT_FALSE(entry.GetServerIsDel());
3469 TEST_F(SyncerTest, TestUndeleteIgnoreCorrectlyUnappliedUpdate) {
3470 Id id1 = ids_.MakeServer("first"), id2 = ids_.MakeServer("second");
3471 Id root = TestIdFactory::root();
3472 // Duplicate! expect path clashing!
3473 mock_server_->set_conflict_all_commits(true);
3474 mock_server_->AddUpdateBookmark(id1, root, "foo", 1, 10,
3475 foreign_cache_guid(), "-1");
3476 mock_server_->AddUpdateBookmark(id2, root, "foo", 1, 10,
3477 foreign_cache_guid(), "-2");
3479 mock_server_->AddUpdateBookmark(id2, root, "foo2", 2, 20,
3480 foreign_cache_guid(), "-2");
3481 SyncShareNudge(); // Now just don't explode.
3484 TEST_F(SyncerTest, ClientTagServerCreatedUpdatesWork) {
3485 mock_server_->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
3486 foreign_cache_guid(), "-1");
3487 mock_server_->SetLastUpdateClientTag("permfolder");
3492 syncable::ReadTransaction trans(FROM_HERE, directory());
3493 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
3494 ASSERT_TRUE(perm_folder.good());
3495 EXPECT_FALSE(perm_folder.GetIsDel());
3496 EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate());
3497 EXPECT_FALSE(perm_folder.GetIsUnsynced());
3498 EXPECT_EQ(perm_folder.GetUniqueClientTag(), "permfolder");
3499 EXPECT_EQ(perm_folder.GetNonUniqueName(), "permitem1");
3502 mock_server_->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
3503 foreign_cache_guid(), "-1");
3504 mock_server_->SetLastUpdateClientTag("permfolder");
3508 syncable::ReadTransaction trans(FROM_HERE, directory());
3510 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
3511 ASSERT_TRUE(perm_folder.good());
3512 EXPECT_FALSE(perm_folder.GetIsDel());
3513 EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate());
3514 EXPECT_FALSE(perm_folder.GetIsUnsynced());
3515 EXPECT_EQ(perm_folder.GetUniqueClientTag(), "permfolder");
3516 EXPECT_EQ(perm_folder.GetNonUniqueName(), "permitem_renamed");
3520 TEST_F(SyncerTest, ClientTagIllegalUpdateIgnored) {
3521 mock_server_->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
3522 foreign_cache_guid(), "-1");
3523 mock_server_->SetLastUpdateClientTag("permfolder");
3528 syncable::ReadTransaction trans(FROM_HERE, directory());
3529 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
3530 ASSERT_TRUE(perm_folder.good());
3531 EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate());
3532 EXPECT_FALSE(perm_folder.GetIsUnsynced());
3533 EXPECT_EQ(perm_folder.GetUniqueClientTag(), "permfolder");
3534 EXPECT_TRUE(perm_folder.GetNonUniqueName()== "permitem1");
3535 EXPECT_TRUE(perm_folder.GetId().ServerKnows());
3538 mock_server_->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
3539 foreign_cache_guid(), "-1");
3540 mock_server_->SetLastUpdateClientTag("wrongtag");
3544 syncable::ReadTransaction trans(FROM_HERE, directory());
3546 // This update is rejected because it has the same ID, but a
3547 // different tag than one that is already on the client.
3548 // The client has a ServerKnows ID, which cannot be overwritten.
3549 Entry rejected_update(&trans, GET_BY_CLIENT_TAG, "wrongtag");
3550 EXPECT_FALSE(rejected_update.good());
3552 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
3553 ASSERT_TRUE(perm_folder.good());
3554 EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate());
3555 EXPECT_FALSE(perm_folder.GetIsUnsynced());
3556 EXPECT_EQ(perm_folder.GetNonUniqueName(), "permitem1");
3560 TEST_F(SyncerTest, ClientTagUncommittedTagMatchesUpdate) {
3561 int64 original_metahandle = 0;
3564 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3566 &trans, CREATE, PREFERENCES, ids_.root(), "name");
3567 ASSERT_TRUE(pref.good());
3568 pref.PutUniqueClientTag("tag");
3569 pref.PutIsUnsynced(true);
3570 EXPECT_FALSE(pref.GetIsUnappliedUpdate());
3571 EXPECT_FALSE(pref.GetId().ServerKnows());
3572 original_metahandle = pref.GetMetahandle();
3575 syncable::Id server_id = TestIdFactory::MakeServer("id");
3576 mock_server_->AddUpdatePref(server_id.GetServerId(),
3577 ids_.root().GetServerId(),
3579 mock_server_->set_conflict_all_commits(true);
3582 // This should cause client tag reunion, preserving the metahandle.
3584 syncable::ReadTransaction trans(FROM_HERE, directory());
3586 Entry pref(&trans, GET_BY_CLIENT_TAG, "tag");
3587 ASSERT_TRUE(pref.good());
3588 EXPECT_FALSE(pref.GetIsDel());
3589 EXPECT_FALSE(pref.GetIsUnappliedUpdate());
3590 EXPECT_TRUE(pref.GetIsUnsynced());
3591 EXPECT_EQ(10, pref.GetBaseVersion());
3592 // Entry should have been given the new ID while preserving the
3593 // metahandle; client should have won the conflict resolution.
3594 EXPECT_EQ(original_metahandle, pref.GetMetahandle());
3595 EXPECT_EQ("tag", pref.GetUniqueClientTag());
3596 EXPECT_TRUE(pref.GetId().ServerKnows());
3599 mock_server_->set_conflict_all_commits(false);
3602 // The resolved entry ought to commit cleanly.
3604 syncable::ReadTransaction trans(FROM_HERE, directory());
3606 Entry pref(&trans, GET_BY_CLIENT_TAG, "tag");
3607 ASSERT_TRUE(pref.good());
3608 EXPECT_FALSE(pref.GetIsDel());
3609 EXPECT_FALSE(pref.GetIsUnappliedUpdate());
3610 EXPECT_FALSE(pref.GetIsUnsynced());
3611 EXPECT_TRUE(10 < pref.GetBaseVersion());
3612 // Entry should have been given the new ID while preserving the
3613 // metahandle; client should have won the conflict resolution.
3614 EXPECT_EQ(original_metahandle, pref.GetMetahandle());
3615 EXPECT_EQ("tag", pref.GetUniqueClientTag());
3616 EXPECT_TRUE(pref.GetId().ServerKnows());
3620 TEST_F(SyncerTest, ClientTagConflictWithDeletedLocalEntry) {
3622 // Create a deleted local entry with a unique client tag.
3623 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3625 &trans, CREATE, PREFERENCES, ids_.root(), "name");
3626 ASSERT_TRUE(pref.good());
3627 ASSERT_FALSE(pref.GetId().ServerKnows());
3628 pref.PutUniqueClientTag("tag");
3629 pref.PutIsUnsynced(true);
3631 // Note: IS_DEL && !ServerKnows() will clear the UNSYNCED bit.
3632 // (We never attempt to commit server-unknown deleted items, so this
3633 // helps us clean up those entries).
3634 pref.PutIsDel(true);
3637 // Prepare an update with the same unique client tag.
3638 syncable::Id server_id = TestIdFactory::MakeServer("id");
3639 mock_server_->AddUpdatePref(server_id.GetServerId(),
3640 ids_.root().GetServerId(),
3644 // The local entry will be overwritten.
3646 syncable::ReadTransaction trans(FROM_HERE, directory());
3648 Entry pref(&trans, GET_BY_CLIENT_TAG, "tag");
3649 ASSERT_TRUE(pref.good());
3650 ASSERT_TRUE(pref.GetId().ServerKnows());
3651 EXPECT_FALSE(pref.GetIsDel());
3652 EXPECT_FALSE(pref.GetIsUnappliedUpdate());
3653 EXPECT_FALSE(pref.GetIsUnsynced());
3654 EXPECT_EQ(pref.GetBaseVersion(), 10);
3655 EXPECT_EQ(pref.GetUniqueClientTag(), "tag");
3659 TEST_F(SyncerTest, ClientTagUpdateClashesWithLocalEntry) {
3660 // This test is written assuming that ID comparison
3661 // will work out in a particular way.
3662 EXPECT_TRUE(ids_.FromNumber(1) < ids_.FromNumber(2));
3663 EXPECT_TRUE(ids_.FromNumber(3) < ids_.FromNumber(4));
3665 syncable::Id id1 = TestIdFactory::MakeServer("1");
3666 mock_server_->AddUpdatePref(id1.GetServerId(), ids_.root().GetServerId(),
3669 syncable::Id id4 = TestIdFactory::MakeServer("4");
3670 mock_server_->AddUpdatePref(id4.GetServerId(), ids_.root().GetServerId(),
3673 mock_server_->set_conflict_all_commits(true);
3676 int64 tag1_metahandle = syncable::kInvalidMetaHandle;
3677 int64 tag2_metahandle = syncable::kInvalidMetaHandle;
3678 // This should cause client tag overwrite.
3680 syncable::ReadTransaction trans(FROM_HERE, directory());
3682 Entry tag1(&trans, GET_BY_CLIENT_TAG, "tag1");
3683 ASSERT_TRUE(tag1.good());
3684 ASSERT_TRUE(tag1.GetId().ServerKnows());
3685 ASSERT_TRUE(id1 == tag1.GetId());
3686 EXPECT_FALSE(tag1.GetIsDel());
3687 EXPECT_FALSE(tag1.GetIsUnappliedUpdate());
3688 EXPECT_FALSE(tag1.GetIsUnsynced());
3689 EXPECT_EQ(10, tag1.GetBaseVersion());
3690 EXPECT_EQ("tag1", tag1.GetUniqueClientTag());
3691 tag1_metahandle = tag1.GetMetahandle();
3693 Entry tag2(&trans, GET_BY_CLIENT_TAG, "tag2");
3694 ASSERT_TRUE(tag2.good());
3695 ASSERT_TRUE(tag2.GetId().ServerKnows());
3696 ASSERT_TRUE(id4 == tag2.GetId());
3697 EXPECT_FALSE(tag2.GetIsDel());
3698 EXPECT_FALSE(tag2.GetIsUnappliedUpdate());
3699 EXPECT_FALSE(tag2.GetIsUnsynced());
3700 EXPECT_EQ(11, tag2.GetBaseVersion());
3701 EXPECT_EQ("tag2", tag2.GetUniqueClientTag());
3702 tag2_metahandle = tag2.GetMetahandle();
3704 syncable::Directory::Metahandles children;
3705 directory()->GetChildHandlesById(&trans, trans.root_id(), &children);
3706 ASSERT_EQ(2U, children.size());
3709 syncable::Id id2 = TestIdFactory::MakeServer("2");
3710 mock_server_->AddUpdatePref(id2.GetServerId(), ids_.root().GetServerId(),
3712 syncable::Id id3 = TestIdFactory::MakeServer("3");
3713 mock_server_->AddUpdatePref(id3.GetServerId(), ids_.root().GetServerId(),
3718 syncable::ReadTransaction trans(FROM_HERE, directory());
3720 Entry tag1(&trans, GET_BY_CLIENT_TAG, "tag1");
3721 ASSERT_TRUE(tag1.good());
3722 ASSERT_TRUE(tag1.GetId().ServerKnows());
3723 ASSERT_EQ(id1, tag1.GetId())
3724 << "ID 1 should be kept, since it was less than ID 2.";
3725 EXPECT_FALSE(tag1.GetIsDel());
3726 EXPECT_FALSE(tag1.GetIsUnappliedUpdate());
3727 EXPECT_FALSE(tag1.GetIsUnsynced());
3728 EXPECT_EQ(10, tag1.GetBaseVersion());
3729 EXPECT_EQ("tag1", tag1.GetUniqueClientTag());
3730 EXPECT_EQ(tag1_metahandle, tag1.GetMetahandle());
3732 Entry tag2(&trans, GET_BY_CLIENT_TAG, "tag2");
3733 ASSERT_TRUE(tag2.good());
3734 ASSERT_TRUE(tag2.GetId().ServerKnows());
3735 ASSERT_EQ(id3, tag2.GetId())
3736 << "ID 3 should be kept, since it was less than ID 4.";
3737 EXPECT_FALSE(tag2.GetIsDel());
3738 EXPECT_FALSE(tag2.GetIsUnappliedUpdate());
3739 EXPECT_FALSE(tag2.GetIsUnsynced());
3740 EXPECT_EQ(13, tag2.GetBaseVersion());
3741 EXPECT_EQ("tag2", tag2.GetUniqueClientTag());
3742 EXPECT_EQ(tag2_metahandle, tag2.GetMetahandle());
3744 syncable::Directory::Metahandles children;
3745 directory()->GetChildHandlesById(&trans, trans.root_id(), &children);
3746 ASSERT_EQ(2U, children.size());
3750 TEST_F(SyncerTest, ClientTagClashWithinBatchOfUpdates) {
3751 // This test is written assuming that ID comparison
3752 // will work out in a particular way.
3753 EXPECT_TRUE(ids_.FromNumber(1) < ids_.FromNumber(4));
3754 EXPECT_TRUE(ids_.FromNumber(201) < ids_.FromNumber(205));
3756 // Least ID: winner.
3757 mock_server_->AddUpdatePref(ids_.FromNumber(1).GetServerId(),
3758 ids_.root().GetServerId(), "tag a", 1, 10);
3759 mock_server_->AddUpdatePref(ids_.FromNumber(2).GetServerId(),
3760 ids_.root().GetServerId(), "tag a", 11, 110);
3761 mock_server_->AddUpdatePref(ids_.FromNumber(3).GetServerId(),
3762 ids_.root().GetServerId(), "tag a", 12, 120);
3763 mock_server_->AddUpdatePref(ids_.FromNumber(4).GetServerId(),
3764 ids_.root().GetServerId(), "tag a", 13, 130);
3766 mock_server_->AddUpdatePref(ids_.FromNumber(105).GetServerId(),
3767 ids_.root().GetServerId(), "tag b", 14, 140);
3768 mock_server_->AddUpdatePref(ids_.FromNumber(102).GetServerId(),
3769 ids_.root().GetServerId(), "tag b", 15, 150);
3770 // Least ID: winner.
3771 mock_server_->AddUpdatePref(ids_.FromNumber(101).GetServerId(),
3772 ids_.root().GetServerId(), "tag b", 16, 160);
3773 mock_server_->AddUpdatePref(ids_.FromNumber(104).GetServerId(),
3774 ids_.root().GetServerId(), "tag b", 17, 170);
3776 mock_server_->AddUpdatePref(ids_.FromNumber(205).GetServerId(),
3777 ids_.root().GetServerId(), "tag c", 18, 180);
3778 mock_server_->AddUpdatePref(ids_.FromNumber(202).GetServerId(),
3779 ids_.root().GetServerId(), "tag c", 19, 190);
3780 mock_server_->AddUpdatePref(ids_.FromNumber(204).GetServerId(),
3781 ids_.root().GetServerId(), "tag c", 20, 200);
3782 // Least ID: winner.
3783 mock_server_->AddUpdatePref(ids_.FromNumber(201).GetServerId(),
3784 ids_.root().GetServerId(), "tag c", 21, 210);
3786 mock_server_->set_conflict_all_commits(true);
3789 // This should cause client tag overwrite.
3791 syncable::ReadTransaction trans(FROM_HERE, directory());
3793 Entry tag_a(&trans, GET_BY_CLIENT_TAG, "tag a");
3794 ASSERT_TRUE(tag_a.good());
3795 EXPECT_TRUE(tag_a.GetId().ServerKnows());
3796 EXPECT_EQ(ids_.FromNumber(1), tag_a.GetId());
3797 EXPECT_FALSE(tag_a.GetIsDel());
3798 EXPECT_FALSE(tag_a.GetIsUnappliedUpdate());
3799 EXPECT_FALSE(tag_a.GetIsUnsynced());
3800 EXPECT_EQ(1, tag_a.GetBaseVersion());
3801 EXPECT_EQ("tag a", tag_a.GetUniqueClientTag());
3803 Entry tag_b(&trans, GET_BY_CLIENT_TAG, "tag b");
3804 ASSERT_TRUE(tag_b.good());
3805 EXPECT_TRUE(tag_b.GetId().ServerKnows());
3806 EXPECT_EQ(ids_.FromNumber(101), tag_b.GetId());
3807 EXPECT_FALSE(tag_b.GetIsDel());
3808 EXPECT_FALSE(tag_b.GetIsUnappliedUpdate());
3809 EXPECT_FALSE(tag_b.GetIsUnsynced());
3810 EXPECT_EQ(16, tag_b.GetBaseVersion());
3811 EXPECT_EQ("tag b", tag_b.GetUniqueClientTag());
3813 Entry tag_c(&trans, GET_BY_CLIENT_TAG, "tag c");
3814 ASSERT_TRUE(tag_c.good());
3815 EXPECT_TRUE(tag_c.GetId().ServerKnows());
3816 EXPECT_EQ(ids_.FromNumber(201), tag_c.GetId());
3817 EXPECT_FALSE(tag_c.GetIsDel());
3818 EXPECT_FALSE(tag_c.GetIsUnappliedUpdate());
3819 EXPECT_FALSE(tag_c.GetIsUnsynced());
3820 EXPECT_EQ(21, tag_c.GetBaseVersion());
3821 EXPECT_EQ("tag c", tag_c.GetUniqueClientTag());
3823 syncable::Directory::Metahandles children;
3824 directory()->GetChildHandlesById(&trans, trans.root_id(), &children);
3825 ASSERT_EQ(3U, children.size());
3829 TEST_F(SyncerTest, UniqueServerTagUpdates) {
3830 // As a hurdle, introduce an item whose name is the same as the tag value
3832 int64 hurdle_handle = CreateUnsyncedDirectory("bob", "id_bob");
3834 syncable::ReadTransaction trans(FROM_HERE, directory());
3835 Entry hurdle(&trans, GET_BY_HANDLE, hurdle_handle);
3836 ASSERT_TRUE(hurdle.good());
3837 ASSERT_TRUE(!hurdle.GetIsDel());
3838 ASSERT_TRUE(hurdle.GetUniqueServerTag().empty());
3839 ASSERT_TRUE(hurdle.GetNonUniqueName()== "bob");
3841 // Try to lookup by the tagname. These should fail.
3842 Entry tag_alpha(&trans, GET_BY_SERVER_TAG, "alpha");
3843 EXPECT_FALSE(tag_alpha.good());
3844 Entry tag_bob(&trans, GET_BY_SERVER_TAG, "bob");
3845 EXPECT_FALSE(tag_bob.good());
3848 // Now download some tagged items as updates.
3849 mock_server_->AddUpdateDirectory(
3850 1, 0, "update1", 1, 10, std::string(), std::string());
3851 mock_server_->SetLastUpdateServerTag("alpha");
3852 mock_server_->AddUpdateDirectory(
3853 2, 0, "update2", 2, 20, std::string(), std::string());
3854 mock_server_->SetLastUpdateServerTag("bob");
3858 syncable::ReadTransaction trans(FROM_HERE, directory());
3860 // The new items should be applied as new entries, and we should be able
3861 // to look them up by their tag values.
3862 Entry tag_alpha(&trans, GET_BY_SERVER_TAG, "alpha");
3863 ASSERT_TRUE(tag_alpha.good());
3864 ASSERT_TRUE(!tag_alpha.GetIsDel());
3865 ASSERT_TRUE(tag_alpha.GetUniqueServerTag()== "alpha");
3866 ASSERT_TRUE(tag_alpha.GetNonUniqueName()== "update1");
3867 Entry tag_bob(&trans, GET_BY_SERVER_TAG, "bob");
3868 ASSERT_TRUE(tag_bob.good());
3869 ASSERT_TRUE(!tag_bob.GetIsDel());
3870 ASSERT_TRUE(tag_bob.GetUniqueServerTag()== "bob");
3871 ASSERT_TRUE(tag_bob.GetNonUniqueName()== "update2");
3872 // The old item should be unchanged.
3873 Entry hurdle(&trans, GET_BY_HANDLE, hurdle_handle);
3874 ASSERT_TRUE(hurdle.good());
3875 ASSERT_TRUE(!hurdle.GetIsDel());
3876 ASSERT_TRUE(hurdle.GetUniqueServerTag().empty());
3877 ASSERT_TRUE(hurdle.GetNonUniqueName()== "bob");
3881 TEST_F(SyncerTest, GetUpdatesSetsRequestedTypes) {
3882 // The expectations of this test happen in the MockConnectionManager's
3883 // GetUpdates handler. EnableDatatype sets the expectation value from our
3884 // set of enabled/disabled datatypes.
3885 EnableDatatype(BOOKMARKS);
3887 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
3889 EnableDatatype(AUTOFILL);
3891 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
3893 EnableDatatype(PREFERENCES);
3895 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
3897 DisableDatatype(BOOKMARKS);
3899 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
3901 DisableDatatype(AUTOFILL);
3903 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
3905 DisableDatatype(PREFERENCES);
3906 EnableDatatype(AUTOFILL);
3908 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
3911 // A typical scenario: server and client each have one update for the other.
3912 // This is the "happy path" alternative to UpdateFailsThenDontCommit.
3913 TEST_F(SyncerTest, UpdateThenCommit) {
3914 syncable::Id to_receive = ids_.NewServerId();
3915 syncable::Id to_commit = ids_.NewLocalId();
3917 mock_server_->AddUpdateDirectory(to_receive, ids_.root(), "x", 1, 10,
3918 foreign_cache_guid(), "-1");
3919 int64 commit_handle = CreateUnsyncedDirectory("y", to_commit);
3922 // The sync cycle should have included a GetUpdate, then a commit. By the
3923 // time the commit happened, we should have known for sure that there were no
3924 // hierarchy conflicts, and reported this fact to the server.
3925 ASSERT_TRUE(mock_server_->last_request().has_commit());
3926 VerifyNoHierarchyConflictsReported(mock_server_->last_request());
3928 syncable::ReadTransaction trans(FROM_HERE, directory());
3930 Entry received(&trans, GET_BY_ID, to_receive);
3931 ASSERT_TRUE(received.good());
3932 EXPECT_FALSE(received.GetIsUnsynced());
3933 EXPECT_FALSE(received.GetIsUnappliedUpdate());
3935 Entry committed(&trans, GET_BY_HANDLE, commit_handle);
3936 ASSERT_TRUE(committed.good());
3937 EXPECT_FALSE(committed.GetIsUnsynced());
3938 EXPECT_FALSE(committed.GetIsUnappliedUpdate());
3941 // Same as above, but this time we fail to download updates.
3942 // We should not attempt to commit anything unless we successfully downloaded
3943 // updates, otherwise we risk causing a server-side conflict.
3944 TEST_F(SyncerTest, UpdateFailsThenDontCommit) {
3945 syncable::Id to_receive = ids_.NewServerId();
3946 syncable::Id to_commit = ids_.NewLocalId();
3948 mock_server_->AddUpdateDirectory(to_receive, ids_.root(), "x", 1, 10,
3949 foreign_cache_guid(), "-1");
3950 int64 commit_handle = CreateUnsyncedDirectory("y", to_commit);
3951 mock_server_->FailNextPostBufferToPathCall();
3954 syncable::ReadTransaction trans(FROM_HERE, directory());
3956 // We did not receive this update.
3957 Entry received(&trans, GET_BY_ID, to_receive);
3958 ASSERT_FALSE(received.good());
3960 // And our local update remains unapplied.
3961 Entry committed(&trans, GET_BY_HANDLE, commit_handle);
3962 ASSERT_TRUE(committed.good());
3963 EXPECT_TRUE(committed.GetIsUnsynced());
3964 EXPECT_FALSE(committed.GetIsUnappliedUpdate());
3966 // Inform the Mock we won't be fetching all updates.
3967 mock_server_->ClearUpdatesQueue();
3970 // Downloads two updates and applies them successfully.
3971 // This is the "happy path" alternative to ConfigureFailsDontApplyUpdates.
3972 TEST_F(SyncerTest, ConfigureDownloadsTwoBatchesSuccess) {
3973 syncable::Id node1 = ids_.NewServerId();
3974 syncable::Id node2 = ids_.NewServerId();
3976 // Construct the first GetUpdates response.
3977 mock_server_->AddUpdateDirectory(node1, ids_.root(), "one", 1, 10,
3978 foreign_cache_guid(), "-2");
3979 mock_server_->SetChangesRemaining(1);
3980 mock_server_->NextUpdateBatch();
3982 // Construct the second GetUpdates response.
3983 mock_server_->AddUpdateDirectory(node2, ids_.root(), "two", 1, 20,
3984 foreign_cache_guid(), "-2");
3986 SyncShareConfigure();
3988 syncable::ReadTransaction trans(FROM_HERE, directory());
3989 // Both nodes should be downloaded and applied.
3991 Entry n1(&trans, GET_BY_ID, node1);
3992 ASSERT_TRUE(n1.good());
3993 EXPECT_FALSE(n1.GetIsUnappliedUpdate());
3995 Entry n2(&trans, GET_BY_ID, node2);
3996 ASSERT_TRUE(n2.good());
3997 EXPECT_FALSE(n2.GetIsUnappliedUpdate());
4000 // Same as the above case, but this time the second batch fails to download.
4001 TEST_F(SyncerTest, ConfigureFailsDontApplyUpdates) {
4002 syncable::Id node1 = ids_.NewServerId();
4003 syncable::Id node2 = ids_.NewServerId();
4005 // The scenario: we have two batches of updates with one update each. A
4006 // normal confgure step would download all the updates one batch at a time and
4007 // apply them. This configure will succeed in downloading the first batch
4008 // then fail when downloading the second.
4009 mock_server_->FailNthPostBufferToPathCall(2);
4011 // Construct the first GetUpdates response.
4012 mock_server_->AddUpdateDirectory(node1, ids_.root(), "one", 1, 10,
4013 foreign_cache_guid(), "-1");
4014 mock_server_->SetChangesRemaining(1);
4015 mock_server_->NextUpdateBatch();
4017 // Consutrct the second GetUpdates response.
4018 mock_server_->AddUpdateDirectory(node2, ids_.root(), "two", 1, 20,
4019 foreign_cache_guid(), "-2");
4021 SyncShareConfigure();
4023 syncable::ReadTransaction trans(FROM_HERE, directory());
4025 // The first node was downloaded, but not applied.
4026 Entry n1(&trans, GET_BY_ID, node1);
4027 ASSERT_TRUE(n1.good());
4028 EXPECT_TRUE(n1.GetIsUnappliedUpdate());
4030 // The second node was not downloaded.
4031 Entry n2(&trans, GET_BY_ID, node2);
4032 EXPECT_FALSE(n2.good());
4034 // One update remains undownloaded.
4035 mock_server_->ClearUpdatesQueue();
4038 TEST_F(SyncerTest, GetKeySuccess) {
4040 syncable::ReadTransaction rtrans(FROM_HERE, directory());
4041 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans));
4044 SyncShareConfigure();
4046 EXPECT_EQ(session_->status_controller().last_get_key_result(), SYNCER_OK);
4048 syncable::ReadTransaction rtrans(FROM_HERE, directory());
4049 EXPECT_FALSE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans));
4053 TEST_F(SyncerTest, GetKeyEmpty) {
4055 syncable::ReadTransaction rtrans(FROM_HERE, directory());
4056 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans));
4059 mock_server_->SetKeystoreKey(std::string());
4060 SyncShareConfigure();
4062 EXPECT_NE(session_->status_controller().last_get_key_result(), SYNCER_OK);
4064 syncable::ReadTransaction rtrans(FROM_HERE, directory());
4065 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans));
4069 // Test what happens if a client deletes, then recreates, an object very
4070 // quickly. It is possible that the deletion gets sent as a commit, and
4071 // the undelete happens during the commit request. The principle here
4072 // is that with a single committing client, conflicts should never
4073 // be encountered, and a client encountering its past actions during
4074 // getupdates should never feed back to override later actions.
4076 // In cases of ordering A-F below, the outcome should be the same.
4077 // Exercised by UndeleteDuringCommit:
4078 // A. Delete - commit - undelete - commitresponse.
4079 // B. Delete - commit - undelete - commitresponse - getupdates.
4080 // Exercised by UndeleteBeforeCommit:
4081 // C. Delete - undelete - commit - commitresponse.
4082 // D. Delete - undelete - commit - commitresponse - getupdates.
4083 // Exercised by UndeleteAfterCommit:
4084 // E. Delete - commit - commitresponse - undelete - commit
4085 // - commitresponse.
4086 // F. Delete - commit - commitresponse - undelete - commit -
4087 // - commitresponse - getupdates.
4088 class SyncerUndeletionTest : public SyncerTest {
4090 SyncerUndeletionTest()
4091 : client_tag_("foobar"),
4092 metahandle_(syncable::kInvalidMetaHandle) {
4096 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
4097 MutableEntry perm_folder(
4098 &trans, CREATE, BOOKMARKS, ids_.root(), "clientname");
4099 ASSERT_TRUE(perm_folder.good());
4100 perm_folder.PutUniqueClientTag(client_tag_);
4101 perm_folder.PutIsUnsynced(true);
4102 perm_folder.PutSyncing(false);
4103 perm_folder.PutSpecifics(DefaultBookmarkSpecifics());
4104 EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate());
4105 EXPECT_FALSE(perm_folder.GetId().ServerKnows());
4106 metahandle_ = perm_folder.GetMetahandle();
4107 local_id_ = perm_folder.GetId();
4111 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
4112 MutableEntry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4113 ASSERT_TRUE(entry.good());
4114 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4115 entry.PutIsDel(true);
4116 entry.PutIsUnsynced(true);
4117 entry.PutSyncing(false);
4121 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
4122 MutableEntry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4123 ASSERT_TRUE(entry.good());
4124 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4125 EXPECT_TRUE(entry.GetIsDel());
4126 entry.PutIsDel(false);
4127 entry.PutIsUnsynced(true);
4128 entry.PutSyncing(false);
4131 int64 GetMetahandleOfTag() {
4132 syncable::ReadTransaction trans(FROM_HERE, directory());
4133 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4134 EXPECT_TRUE(entry.good());
4135 if (!entry.good()) {
4136 return syncable::kInvalidMetaHandle;
4138 return entry.GetMetahandle();
4141 void ExpectUnsyncedCreation() {
4142 syncable::ReadTransaction trans(FROM_HERE, directory());
4143 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4145 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4146 EXPECT_FALSE(entry.GetIsDel());
4147 EXPECT_FALSE(entry.GetServerIsDel()); // Never been committed.
4148 EXPECT_GE(0, entry.GetBaseVersion());
4149 EXPECT_TRUE(entry.GetIsUnsynced());
4150 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4153 void ExpectUnsyncedUndeletion() {
4154 syncable::ReadTransaction trans(FROM_HERE, directory());
4155 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4157 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4158 EXPECT_FALSE(entry.GetIsDel());
4159 EXPECT_TRUE(entry.GetServerIsDel());
4160 EXPECT_EQ(0, entry.GetBaseVersion());
4161 EXPECT_TRUE(entry.GetIsUnsynced());
4162 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4163 EXPECT_TRUE(entry.GetId().ServerKnows());
4166 void ExpectUnsyncedEdit() {
4167 syncable::ReadTransaction trans(FROM_HERE, directory());
4168 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4170 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4171 EXPECT_FALSE(entry.GetIsDel());
4172 EXPECT_FALSE(entry.GetServerIsDel());
4173 EXPECT_LT(0, entry.GetBaseVersion());
4174 EXPECT_TRUE(entry.GetIsUnsynced());
4175 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4176 EXPECT_TRUE(entry.GetId().ServerKnows());
4179 void ExpectUnsyncedDeletion() {
4180 syncable::ReadTransaction trans(FROM_HERE, directory());
4181 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4183 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4184 EXPECT_TRUE(entry.GetIsDel());
4185 EXPECT_FALSE(entry.GetServerIsDel());
4186 EXPECT_TRUE(entry.GetIsUnsynced());
4187 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4188 EXPECT_LT(0, entry.GetBaseVersion());
4189 EXPECT_LT(0, entry.GetServerVersion());
4192 void ExpectSyncedAndCreated() {
4193 syncable::ReadTransaction trans(FROM_HERE, directory());
4194 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4196 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4197 EXPECT_FALSE(entry.GetIsDel());
4198 EXPECT_FALSE(entry.GetServerIsDel());
4199 EXPECT_LT(0, entry.GetBaseVersion());
4200 EXPECT_EQ(entry.GetBaseVersion(), entry.GetServerVersion());
4201 EXPECT_FALSE(entry.GetIsUnsynced());
4202 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4205 void ExpectSyncedAndDeleted() {
4206 syncable::ReadTransaction trans(FROM_HERE, directory());
4207 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4209 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4210 EXPECT_TRUE(entry.GetIsDel());
4211 EXPECT_TRUE(entry.GetServerIsDel());
4212 EXPECT_FALSE(entry.GetIsUnsynced());
4213 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4214 EXPECT_GE(0, entry.GetBaseVersion());
4215 EXPECT_GE(0, entry.GetServerVersion());
4219 const std::string client_tag_;
4220 syncable::Id local_id_;
4224 TEST_F(SyncerUndeletionTest, UndeleteDuringCommit) {
4226 ExpectUnsyncedCreation();
4229 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4230 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4231 ExpectSyncedAndCreated();
4233 // Delete, begin committing the delete, then undelete while committing.
4235 ExpectUnsyncedDeletion();
4236 mock_server_->SetMidCommitCallback(
4237 base::Bind(&SyncerUndeletionTest::Undelete, base::Unretained(this)));
4240 // We will continue to commit until all nodes are synced, so we expect
4241 // that both the delete and following undelete were committed. We haven't
4242 // downloaded any updates, though, so the SERVER fields will be the same
4243 // as they were at the start of the cycle.
4244 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4245 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4248 syncable::ReadTransaction trans(FROM_HERE, directory());
4249 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4251 // Server fields lag behind.
4252 EXPECT_FALSE(entry.GetServerIsDel());
4254 // We have committed the second (undelete) update.
4255 EXPECT_FALSE(entry.GetIsDel());
4256 EXPECT_FALSE(entry.GetIsUnsynced());
4257 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4260 // Now, encounter a GetUpdates corresponding to the deletion from
4261 // the server. The undeletion should prevail again and be committed.
4262 // None of this should trigger any conflict detection -- it is perfectly
4263 // normal to recieve updates from our own commits.
4264 mock_server_->SetMidCommitCallback(base::Closure());
4265 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
4266 update->set_originator_cache_guid(local_cache_guid());
4267 update->set_originator_client_item_id(local_id_.GetServerId());
4270 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4271 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4272 ExpectSyncedAndCreated();
4275 TEST_F(SyncerUndeletionTest, UndeleteBeforeCommit) {
4277 ExpectUnsyncedCreation();
4280 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4281 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4282 ExpectSyncedAndCreated();
4284 // Delete and undelete, then sync to pick up the result.
4286 ExpectUnsyncedDeletion();
4288 ExpectUnsyncedEdit(); // Edit, not undelete: server thinks it exists.
4291 // The item ought to have committed successfully.
4292 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4293 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4294 ExpectSyncedAndCreated();
4296 syncable::ReadTransaction trans(FROM_HERE, directory());
4297 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4298 EXPECT_EQ(2, entry.GetBaseVersion());
4301 // Now, encounter a GetUpdates corresponding to the just-committed
4303 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
4304 update->set_originator_cache_guid(local_cache_guid());
4305 update->set_originator_client_item_id(local_id_.GetServerId());
4307 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4308 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4309 ExpectSyncedAndCreated();
4312 TEST_F(SyncerUndeletionTest, UndeleteAfterCommitButBeforeGetUpdates) {
4314 ExpectUnsyncedCreation();
4317 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4318 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4319 ExpectSyncedAndCreated();
4321 // Delete and commit.
4323 ExpectUnsyncedDeletion();
4326 // The item ought to have committed successfully.
4327 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4328 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4329 ExpectSyncedAndDeleted();
4331 // Before the GetUpdates, the item is locally undeleted.
4333 ExpectUnsyncedUndeletion();
4335 // Now, encounter a GetUpdates corresponding to the just-committed
4336 // deletion update. The undeletion should prevail.
4337 mock_server_->AddUpdateFromLastCommit();
4339 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4340 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4341 ExpectSyncedAndCreated();
4344 TEST_F(SyncerUndeletionTest, UndeleteAfterDeleteAndGetUpdates) {
4346 ExpectUnsyncedCreation();
4349 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4350 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4351 ExpectSyncedAndCreated();
4353 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
4354 update->set_originator_cache_guid(local_cache_guid());
4355 update->set_originator_client_item_id(local_id_.GetServerId());
4357 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4358 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4359 ExpectSyncedAndCreated();
4361 // Delete and commit.
4363 ExpectUnsyncedDeletion();
4366 // The item ought to have committed successfully.
4367 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4368 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4369 ExpectSyncedAndDeleted();
4371 // Now, encounter a GetUpdates corresponding to the just-committed
4372 // deletion update. Should be consistent.
4373 mock_server_->AddUpdateFromLastCommit();
4375 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4376 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4377 ExpectSyncedAndDeleted();
4379 // After the GetUpdates, the item is locally undeleted.
4381 ExpectUnsyncedUndeletion();
4383 // Now, encounter a GetUpdates corresponding to the just-committed
4384 // deletion update. The undeletion should prevail.
4386 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4387 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4388 ExpectSyncedAndCreated();
4391 // Test processing of undeletion GetUpdateses.
4392 TEST_F(SyncerUndeletionTest, UndeleteAfterOtherClientDeletes) {
4394 ExpectUnsyncedCreation();
4397 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4398 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4399 ExpectSyncedAndCreated();
4401 // Add a delete from the server.
4402 sync_pb::SyncEntity* update1 = mock_server_->AddUpdateFromLastCommit();
4403 update1->set_originator_cache_guid(local_cache_guid());
4404 update1->set_originator_client_item_id(local_id_.GetServerId());
4406 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4407 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4408 ExpectSyncedAndCreated();
4410 // Some other client deletes the item.
4412 syncable::ReadTransaction trans(FROM_HERE, directory());
4413 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4414 mock_server_->AddUpdateTombstone(entry.GetId());
4418 // The update ought to have applied successfully.
4419 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4420 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4421 ExpectSyncedAndDeleted();
4423 // Undelete it locally.
4425 ExpectUnsyncedUndeletion();
4427 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4428 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4429 ExpectSyncedAndCreated();
4431 // Now, encounter a GetUpdates corresponding to the just-committed
4432 // deletion update. The undeletion should prevail.
4433 sync_pb::SyncEntity* update2 = mock_server_->AddUpdateFromLastCommit();
4434 update2->set_originator_cache_guid(local_cache_guid());
4435 update2->set_originator_client_item_id(local_id_.GetServerId());
4437 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4438 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4439 ExpectSyncedAndCreated();
4442 TEST_F(SyncerUndeletionTest, UndeleteAfterOtherClientDeletesImmediately) {
4444 ExpectUnsyncedCreation();
4447 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4448 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4449 ExpectSyncedAndCreated();
4451 // Some other client deletes the item before we get a chance
4452 // to GetUpdates our original request.
4454 syncable::ReadTransaction trans(FROM_HERE, directory());
4455 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4456 mock_server_->AddUpdateTombstone(entry.GetId());
4460 // The update ought to have applied successfully.
4461 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4462 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4463 ExpectSyncedAndDeleted();
4465 // Undelete it locally.
4467 ExpectUnsyncedUndeletion();
4469 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4470 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4471 ExpectSyncedAndCreated();
4473 // Now, encounter a GetUpdates corresponding to the just-committed
4474 // deletion update. The undeletion should prevail.
4475 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
4476 update->set_originator_cache_guid(local_cache_guid());
4477 update->set_originator_client_item_id(local_id_.GetServerId());
4479 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4480 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4481 ExpectSyncedAndCreated();
4484 TEST_F(SyncerUndeletionTest, OtherClientUndeletes) {
4486 ExpectUnsyncedCreation();
4489 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4490 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4491 ExpectSyncedAndCreated();
4493 // Get the updates of our just-committed entry.
4494 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
4495 update->set_originator_cache_guid(local_cache_guid());
4496 update->set_originator_client_item_id(local_id_.GetServerId());
4498 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4499 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4500 ExpectSyncedAndCreated();
4502 // We delete the item.
4504 ExpectUnsyncedDeletion();
4507 // The update ought to have applied successfully.
4508 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4509 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4510 ExpectSyncedAndDeleted();
4512 // Now, encounter a GetUpdates corresponding to the just-committed
4514 mock_server_->AddUpdateFromLastCommit();
4516 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4517 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4518 ExpectSyncedAndDeleted();
4520 // Some other client undeletes the item.
4522 syncable::ReadTransaction trans(FROM_HERE, directory());
4523 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4524 mock_server_->AddUpdateBookmark(
4526 entry.GetParentId(),
4527 "Thadeusz", 100, 1000,
4528 local_cache_guid(), local_id_.GetServerId());
4530 mock_server_->SetLastUpdateClientTag(client_tag_);
4532 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4533 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4534 ExpectSyncedAndCreated();
4536 syncable::ReadTransaction trans(FROM_HERE, directory());
4537 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4538 EXPECT_EQ("Thadeusz", entry.GetNonUniqueName());
4542 TEST_F(SyncerUndeletionTest, OtherClientUndeletesImmediately) {
4544 ExpectUnsyncedCreation();
4547 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4548 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4549 ExpectSyncedAndCreated();
4551 // Get the updates of our just-committed entry.
4552 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
4553 update->set_originator_cache_guid(local_cache_guid());
4555 syncable::ReadTransaction trans(FROM_HERE, directory());
4556 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4557 update->set_originator_client_item_id(local_id_.GetServerId());
4560 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4561 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4562 ExpectSyncedAndCreated();
4564 // We delete the item.
4566 ExpectUnsyncedDeletion();
4569 // The update ought to have applied successfully.
4570 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4571 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4572 ExpectSyncedAndDeleted();
4574 // Some other client undeletes before we see the update from our
4577 syncable::ReadTransaction trans(FROM_HERE, directory());
4578 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4579 mock_server_->AddUpdateBookmark(
4581 entry.GetParentId(),
4582 "Thadeusz", 100, 1000,
4583 local_cache_guid(), local_id_.GetServerId());
4585 mock_server_->SetLastUpdateClientTag(client_tag_);
4587 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4588 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4589 ExpectSyncedAndCreated();
4591 syncable::ReadTransaction trans(FROM_HERE, directory());
4592 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4593 EXPECT_EQ("Thadeusz", entry.GetNonUniqueName());
4598 TEST_PARAM_BOOKMARK_ENABLE_BIT,
4599 TEST_PARAM_AUTOFILL_ENABLE_BIT,
4600 TEST_PARAM_BIT_COUNT
4605 public ::testing::WithParamInterface<int> {
4607 bool ShouldFailBookmarkCommit() {
4608 return (GetParam() & (1 << TEST_PARAM_BOOKMARK_ENABLE_BIT)) == 0;
4610 bool ShouldFailAutofillCommit() {
4611 return (GetParam() & (1 << TEST_PARAM_AUTOFILL_ENABLE_BIT)) == 0;
4615 INSTANTIATE_TEST_CASE_P(ExtensionsActivity,
4617 testing::Range(0, 1 << TEST_PARAM_BIT_COUNT));
4619 TEST_P(MixedResult, ExtensionsActivity) {
4621 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
4623 MutableEntry pref(&wtrans, CREATE, PREFERENCES, wtrans.root_id(), "pref");
4624 ASSERT_TRUE(pref.good());
4625 pref.PutIsUnsynced(true);
4627 MutableEntry bookmark(
4628 &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "bookmark");
4629 ASSERT_TRUE(bookmark.good());
4630 bookmark.PutIsUnsynced(true);
4632 if (ShouldFailBookmarkCommit()) {
4633 mock_server_->SetTransientErrorId(bookmark.GetId());
4636 if (ShouldFailAutofillCommit()) {
4637 mock_server_->SetTransientErrorId(pref.GetId());
4642 // Put some extenions activity records into the monitor.
4644 ExtensionsActivity::Records records;
4645 records["ABC"].extension_id = "ABC";
4646 records["ABC"].bookmark_write_count = 2049U;
4647 records["xyz"].extension_id = "xyz";
4648 records["xyz"].bookmark_write_count = 4U;
4649 context_->extensions_activity()->PutRecords(records);
4654 ExtensionsActivity::Records final_monitor_records;
4655 context_->extensions_activity()->GetAndClearRecords(&final_monitor_records);
4656 if (ShouldFailBookmarkCommit()) {
4657 ASSERT_EQ(2U, final_monitor_records.size())
4658 << "Should restore records after unsuccessful bookmark commit.";
4659 EXPECT_EQ("ABC", final_monitor_records["ABC"].extension_id);
4660 EXPECT_EQ("xyz", final_monitor_records["xyz"].extension_id);
4661 EXPECT_EQ(2049U, final_monitor_records["ABC"].bookmark_write_count);
4662 EXPECT_EQ(4U, final_monitor_records["xyz"].bookmark_write_count);
4664 EXPECT_TRUE(final_monitor_records.empty())
4665 << "Should not restore records after successful bookmark commit.";
4669 } // namespace syncer