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/test/sessions/mock_debug_info_getter.h"
54 #include "sync/util/cryptographer.h"
55 #include "sync/util/extensions_activity.h"
56 #include "sync/util/time.h"
57 #include "testing/gtest/include/gtest/gtest.h"
59 using base::TimeDelta;
70 using syncable::BaseTransaction;
72 using syncable::CountEntriesWithName;
73 using syncable::Directory;
74 using syncable::Entry;
75 using syncable::GetFirstEntryWithName;
76 using syncable::GetOnlyEntryWithName;
78 using syncable::kEncryptedString;
79 using syncable::MutableEntry;
80 using syncable::WriteTransaction;
82 using syncable::BASE_VERSION;
83 using syncable::CREATE;
84 using syncable::GET_BY_HANDLE;
85 using syncable::GET_BY_ID;
86 using syncable::GET_BY_CLIENT_TAG;
87 using syncable::GET_BY_SERVER_TAG;
89 using syncable::IS_DEL;
90 using syncable::IS_DIR;
91 using syncable::IS_UNAPPLIED_UPDATE;
92 using syncable::IS_UNSYNCED;
93 using syncable::META_HANDLE;
94 using syncable::MTIME;
95 using syncable::NON_UNIQUE_NAME;
96 using syncable::PARENT_ID;
97 using syncable::BASE_SERVER_SPECIFICS;
98 using syncable::SERVER_IS_DEL;
99 using syncable::SERVER_PARENT_ID;
100 using syncable::SERVER_SPECIFICS;
101 using syncable::SERVER_VERSION;
102 using syncable::UNIQUE_CLIENT_TAG;
103 using syncable::UNIQUE_SERVER_TAG;
104 using syncable::SPECIFICS;
105 using syncable::SYNCING;
106 using syncable::UNITTEST;
108 using sessions::MockDebugInfoGetter;
109 using sessions::StatusController;
110 using sessions::SyncSessionContext;
111 using sessions::SyncSession;
113 class SyncerTest : public testing::Test,
114 public SyncSession::Delegate,
115 public SyncEngineEventListener {
118 : extensions_activity_(new ExtensionsActivity),
120 saw_syncer_event_(false),
121 last_client_invalidation_hint_buffer_size_(10),
122 traffic_recorder_(0, 0) {
125 // SyncSession::Delegate implementation.
126 virtual void OnThrottled(const base::TimeDelta& throttle_duration) OVERRIDE {
127 FAIL() << "Should not get silenced.";
129 virtual void OnTypesThrottled(
131 const base::TimeDelta& throttle_duration) OVERRIDE {
132 FAIL() << "Should not get silenced.";
134 virtual bool IsCurrentlyThrottled() OVERRIDE {
137 virtual void OnReceivedLongPollIntervalUpdate(
138 const base::TimeDelta& new_interval) OVERRIDE {
139 last_long_poll_interval_received_ = new_interval;
141 virtual void OnReceivedShortPollIntervalUpdate(
142 const base::TimeDelta& new_interval) OVERRIDE {
143 last_short_poll_interval_received_ = new_interval;
145 virtual void OnReceivedSessionsCommitDelay(
146 const base::TimeDelta& new_delay) OVERRIDE {
147 last_sessions_commit_delay_seconds_ = new_delay;
149 virtual void OnReceivedClientInvalidationHintBufferSize(
151 last_client_invalidation_hint_buffer_size_ = size;
153 virtual void OnReceivedGuRetryDelay(const base::TimeDelta& delay) OVERRIDE {}
154 virtual void OnReceivedMigrationRequest(ModelTypeSet types) OVERRIDE {}
155 virtual void OnSyncProtocolError(const SyncProtocolError& error) OVERRIDE {}
157 void GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) {
158 // We're just testing the sync engine here, so we shunt everything to
159 // the SyncerThread. Datatypes which aren't enabled aren't in the map.
160 for (ModelTypeSet::Iterator it = enabled_datatypes_.First();
161 it.Good(); it.Inc()) {
162 (*out)[it.Get()] = GROUP_PASSIVE;
166 virtual void OnSyncCycleEvent(const SyncCycleEvent& event) OVERRIDE {
167 DVLOG(1) << "HandleSyncEngineEvent in unittest " << event.what_happened;
168 // we only test for entry-specific events, not status changed ones.
169 switch (event.what_happened) {
170 case SyncCycleEvent::SYNC_CYCLE_BEGIN: // Fall through.
171 case SyncCycleEvent::STATUS_CHANGED:
172 case SyncCycleEvent::SYNC_CYCLE_ENDED:
175 CHECK(false) << "Handling unknown error type in unit tests!!";
177 saw_syncer_event_ = true;
180 virtual void OnActionableError(const SyncProtocolError& error) OVERRIDE {}
181 virtual void OnRetryTimeChanged(base::Time retry_time) OVERRIDE {}
182 virtual void OnThrottledTypesChanged(ModelTypeSet throttled_types) OVERRIDE {}
183 virtual void OnMigrationRequested(ModelTypeSet types) OVERRIDE {}
185 void ResetSession() {
186 session_.reset(SyncSession::Build(context_.get(), this));
189 void SyncShareNudge() {
192 // Pretend we've seen a local change, to make the nudge_tracker look normal.
193 nudge_tracker_.RecordLocalChange(ModelTypeSet(BOOKMARKS));
196 syncer_->NormalSyncShare(
197 context_->enabled_types(),
202 void SyncShareConfigure() {
204 EXPECT_TRUE(syncer_->ConfigureSyncShare(
205 context_->enabled_types(),
206 sync_pb::GetUpdatesCallerInfo::RECONFIGURATION,
210 virtual void SetUp() {
212 mock_server_.reset(new MockConnectionManager(directory(),
213 &cancelation_signal_));
214 debug_info_getter_.reset(new MockDebugInfoGetter);
215 EnableDatatype(BOOKMARKS);
216 EnableDatatype(NIGORI);
217 EnableDatatype(PREFERENCES);
218 EnableDatatype(NIGORI);
219 workers_.push_back(scoped_refptr<ModelSafeWorker>(
220 new FakeModelWorker(GROUP_PASSIVE)));
221 std::vector<SyncEngineEventListener*> listeners;
222 listeners.push_back(this);
224 ModelSafeRoutingInfo routing_info;
225 GetModelSafeRoutingInfo(&routing_info);
227 model_type_registry_.reset(new ModelTypeRegistry(workers_, directory()));
230 new SyncSessionContext(
231 mock_server_.get(), directory(),
232 extensions_activity_,
233 listeners, debug_info_getter_.get(), &traffic_recorder_,
234 model_type_registry_.get(),
235 true, // enable keystore encryption
236 false, // force enable pre-commit GU avoidance experiment
237 "fake_invalidator_client_id"));
238 context_->SetRoutingInfo(routing_info);
239 syncer_ = new Syncer(&cancelation_signal_);
241 syncable::ReadTransaction trans(FROM_HERE, directory());
242 syncable::Directory::Metahandles children;
243 directory()->GetChildHandlesById(&trans, trans.root_id(), &children);
244 ASSERT_EQ(0u, children.size());
245 saw_syncer_event_ = false;
246 root_id_ = TestIdFactory::root();
247 parent_id_ = ids_.MakeServer("parent id");
248 child_id_ = ids_.MakeServer("child id");
249 directory()->set_store_birthday(mock_server_->store_birthday());
250 mock_server_->SetKeystoreKey("encryption_key");
253 virtual void TearDown() {
254 mock_server_.reset();
257 dir_maker_.TearDown();
259 void WriteTestDataToEntry(WriteTransaction* trans, MutableEntry* entry) {
260 EXPECT_FALSE(entry->GetIsDir());
261 EXPECT_FALSE(entry->GetIsDel());
262 sync_pb::EntitySpecifics specifics;
263 specifics.mutable_bookmark()->set_url("http://demo/");
264 specifics.mutable_bookmark()->set_favicon("PNG");
265 entry->PutSpecifics(specifics);
266 entry->PutIsUnsynced(true);
268 void VerifyTestDataInEntry(BaseTransaction* trans, Entry* entry) {
269 EXPECT_FALSE(entry->GetIsDir());
270 EXPECT_FALSE(entry->GetIsDel());
271 VerifyTestBookmarkDataInEntry(entry);
273 void VerifyTestBookmarkDataInEntry(Entry* entry) {
274 const sync_pb::EntitySpecifics& specifics = entry->GetSpecifics();
275 EXPECT_TRUE(specifics.has_bookmark());
276 EXPECT_EQ("PNG", specifics.bookmark().favicon());
277 EXPECT_EQ("http://demo/", specifics.bookmark().url());
280 void VerifyHierarchyConflictsReported(
281 const sync_pb::ClientToServerMessage& message) {
282 // Our request should have included a warning about hierarchy conflicts.
283 const sync_pb::ClientStatus& client_status = message.client_status();
284 EXPECT_TRUE(client_status.has_hierarchy_conflict_detected());
285 EXPECT_TRUE(client_status.hierarchy_conflict_detected());
288 void VerifyNoHierarchyConflictsReported(
289 const sync_pb::ClientToServerMessage& message) {
290 // Our request should have reported no hierarchy conflicts detected.
291 const sync_pb::ClientStatus& client_status = message.client_status();
292 EXPECT_TRUE(client_status.has_hierarchy_conflict_detected());
293 EXPECT_FALSE(client_status.hierarchy_conflict_detected());
296 void VerifyHierarchyConflictsUnspecified(
297 const sync_pb::ClientToServerMessage& message) {
298 // Our request should have neither confirmed nor denied hierarchy conflicts.
299 const sync_pb::ClientStatus& client_status = message.client_status();
300 EXPECT_FALSE(client_status.has_hierarchy_conflict_detected());
303 sync_pb::EntitySpecifics DefaultBookmarkSpecifics() {
304 sync_pb::EntitySpecifics result;
305 AddDefaultFieldValue(BOOKMARKS, &result);
309 sync_pb::EntitySpecifics DefaultPreferencesSpecifics() {
310 sync_pb::EntitySpecifics result;
311 AddDefaultFieldValue(PREFERENCES, &result);
314 // Enumeration of alterations to entries for commit ordering tests.
316 LIST_END = 0, // Denotes the end of the list of features from below.
317 SYNCED, // Items are unsynced by default
323 struct CommitOrderingTest {
324 // expected commit index.
326 // Details about the item
328 syncable::Id parent_id;
329 EntryFeature features[10];
331 static CommitOrderingTest MakeLastCommitItem() {
332 CommitOrderingTest last_commit_item;
333 last_commit_item.commit_index = -1;
334 last_commit_item.id = TestIdFactory::root();
335 return last_commit_item;
339 void RunCommitOrderingTest(CommitOrderingTest* test) {
340 map<int, syncable::Id> expected_positions;
341 { // Transaction scope.
342 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
343 while (!test->id.IsRoot()) {
344 if (test->commit_index >= 0) {
345 map<int, syncable::Id>::value_type entry(test->commit_index,
347 bool double_position = !expected_positions.insert(entry).second;
348 ASSERT_FALSE(double_position) << "Two id's expected at one position";
350 string utf8_name = test->id.GetServerId();
351 string name(utf8_name.begin(), utf8_name.end());
352 MutableEntry entry(&trans, CREATE, BOOKMARKS, test->parent_id, name);
354 entry.PutId(test->id);
355 if (test->id.ServerKnows()) {
356 entry.PutBaseVersion(5);
357 entry.PutServerVersion(5);
358 entry.PutServerParentId(test->parent_id);
360 entry.PutIsDir(true);
361 entry.PutIsUnsynced(true);
362 entry.PutSpecifics(DefaultBookmarkSpecifics());
363 // Set the time to 30 seconds in the future to reduce the chance of
365 const base::Time& now_plus_30s =
366 base::Time::Now() + base::TimeDelta::FromSeconds(30);
367 const base::Time& now_minus_2h =
368 base::Time::Now() - base::TimeDelta::FromHours(2);
369 entry.PutMtime(now_plus_30s);
370 for (size_t i = 0 ; i < arraysize(test->features) ; ++i) {
371 switch (test->features[i]) {
375 entry.PutIsUnsynced(false);
378 entry.PutIsDel(true);
381 entry.PutMtime(now_minus_2h);
383 case MOVED_FROM_ROOT:
384 entry.PutServerParentId(trans.root_id());
387 FAIL() << "Bad value in CommitOrderingTest list";
394 ASSERT_TRUE(expected_positions.size() ==
395 mock_server_->committed_ids().size());
396 // If this test starts failing, be aware other sort orders could be valid.
397 for (size_t i = 0; i < expected_positions.size(); ++i) {
399 EXPECT_EQ(1u, expected_positions.count(i));
400 EXPECT_EQ(expected_positions[i], mock_server_->committed_ids()[i]);
404 const StatusController& status() {
405 return session_->status_controller();
408 Directory* directory() {
409 return dir_maker_.directory();
412 const std::string local_cache_guid() {
413 return directory()->cache_guid();
416 const std::string foreign_cache_guid() {
417 return "kqyg7097kro6GSUod+GSg==";
420 int64 CreateUnsyncedDirectory(const string& entry_name,
421 const string& idstring) {
422 return CreateUnsyncedDirectory(entry_name,
423 syncable::Id::CreateFromServerId(idstring));
426 int64 CreateUnsyncedDirectory(const string& entry_name,
427 const syncable::Id& id) {
428 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
430 &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), entry_name);
431 EXPECT_TRUE(entry.good());
432 entry.PutIsUnsynced(true);
433 entry.PutIsDir(true);
434 entry.PutSpecifics(DefaultBookmarkSpecifics());
435 entry.PutBaseVersion(id.ServerKnows() ? 1 : 0);
437 return entry.GetMetahandle();
440 void EnableDatatype(ModelType model_type) {
441 enabled_datatypes_.Put(model_type);
443 ModelSafeRoutingInfo routing_info;
444 GetModelSafeRoutingInfo(&routing_info);
447 context_->SetRoutingInfo(routing_info);
450 mock_server_->ExpectGetUpdatesRequestTypes(enabled_datatypes_);
453 void DisableDatatype(ModelType model_type) {
454 enabled_datatypes_.Remove(model_type);
456 ModelSafeRoutingInfo routing_info;
457 GetModelSafeRoutingInfo(&routing_info);
460 context_->SetRoutingInfo(routing_info);
463 mock_server_->ExpectGetUpdatesRequestTypes(enabled_datatypes_);
466 Cryptographer* GetCryptographer(syncable::BaseTransaction* trans) {
467 return directory()->GetCryptographer(trans);
470 // Configures SyncSessionContext and NudgeTracker so Syncer won't call
471 // GetUpdates prior to Commit. This method can be used to ensure a Commit is
472 // not preceeded by GetUpdates.
473 void ConfigureNoGetUpdatesRequired() {
474 context_->set_server_enabled_pre_commit_update_avoidance(true);
475 nudge_tracker_.OnInvalidationsEnabled();
476 nudge_tracker_.RecordSuccessfulSyncCycle();
478 ASSERT_FALSE(context_->ShouldFetchUpdatesBeforeCommit());
479 ASSERT_FALSE(nudge_tracker_.IsGetUpdatesRequired());
482 base::MessageLoop message_loop_;
484 // Some ids to aid tests. Only the root one's value is specific. The rest
485 // are named for test clarity.
486 // TODO(chron): Get rid of these inbuilt IDs. They only make it
488 syncable::Id root_id_;
489 syncable::Id parent_id_;
490 syncable::Id child_id_;
494 TestDirectorySetterUpper dir_maker_;
495 FakeEncryptor encryptor_;
496 scoped_refptr<ExtensionsActivity> extensions_activity_;
497 scoped_ptr<MockConnectionManager> mock_server_;
498 CancelationSignal cancelation_signal_;
502 scoped_ptr<SyncSession> session_;
503 scoped_ptr<ModelTypeRegistry> model_type_registry_;
504 scoped_ptr<SyncSessionContext> context_;
505 bool saw_syncer_event_;
506 base::TimeDelta last_short_poll_interval_received_;
507 base::TimeDelta last_long_poll_interval_received_;
508 base::TimeDelta last_sessions_commit_delay_seconds_;
509 int last_client_invalidation_hint_buffer_size_;
510 std::vector<scoped_refptr<ModelSafeWorker> > workers_;
512 ModelTypeSet enabled_datatypes_;
513 TrafficRecorder traffic_recorder_;
514 sessions::NudgeTracker nudge_tracker_;
515 scoped_ptr<MockDebugInfoGetter> debug_info_getter_;
517 DISALLOW_COPY_AND_ASSIGN(SyncerTest);
520 TEST_F(SyncerTest, TestCallGatherUnsyncedEntries) {
522 Syncer::UnsyncedMetaHandles handles;
524 syncable::ReadTransaction trans(FROM_HERE, directory());
525 GetUnsyncedEntries(&trans, &handles);
527 ASSERT_EQ(0u, handles.size());
529 // TODO(sync): When we can dynamically connect and disconnect the mock
530 // ServerConnectionManager test disconnected GetUnsyncedEntries here. It's a
531 // regression for a very old bug.
534 TEST_F(SyncerTest, GetCommitIdsFiltersThrottledEntries) {
535 const ModelTypeSet throttled_types(BOOKMARKS);
536 sync_pb::EntitySpecifics bookmark_data;
537 AddDefaultFieldValue(BOOKMARKS, &bookmark_data);
539 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
540 foreign_cache_guid(), "-1");
544 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
545 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
546 ASSERT_TRUE(A.good());
547 A.PutIsUnsynced(true);
548 A.PutSpecifics(bookmark_data);
549 A.PutNonUniqueName("bookmark");
552 // Now sync without enabling bookmarks.
553 mock_server_->ExpectGetUpdatesRequestTypes(
554 Difference(context_->enabled_types(), ModelTypeSet(BOOKMARKS)));
556 syncer_->NormalSyncShare(
557 Difference(context_->enabled_types(), ModelTypeSet(BOOKMARKS)),
562 // Nothing should have been committed as bookmarks is throttled.
563 syncable::ReadTransaction rtrans(FROM_HERE, directory());
564 Entry entryA(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1));
565 ASSERT_TRUE(entryA.good());
566 EXPECT_TRUE(entryA.GetIsUnsynced());
569 // Sync again with bookmarks enabled.
570 mock_server_->ExpectGetUpdatesRequestTypes(context_->enabled_types());
573 // It should have been committed.
574 syncable::ReadTransaction rtrans(FROM_HERE, directory());
575 Entry entryA(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1));
576 ASSERT_TRUE(entryA.good());
577 EXPECT_FALSE(entryA.GetIsUnsynced());
581 // We use a macro so we can preserve the error location.
582 #define VERIFY_ENTRY(id, is_unapplied, is_unsynced, prev_initialized, \
583 parent_id, version, server_version, id_fac, rtrans) \
585 Entry entryA(rtrans, syncable::GET_BY_ID, id_fac.FromNumber(id)); \
586 ASSERT_TRUE(entryA.good()); \
587 /* We don't use EXPECT_EQ here because when the left side param is false,
588 gcc 4.6 warns about converting 'false' to pointer type for argument 1. */ \
589 EXPECT_TRUE(is_unsynced == entryA.GetIsUnsynced()); \
590 EXPECT_TRUE(is_unapplied == entryA.GetIsUnappliedUpdate()); \
591 EXPECT_TRUE(prev_initialized == \
592 IsRealDataType(GetModelTypeFromSpecifics( \
593 entryA.GetBaseServerSpecifics()))); \
594 EXPECT_TRUE(parent_id == -1 || \
595 entryA.GetParentId()== id_fac.FromNumber(parent_id)); \
596 EXPECT_EQ(version, entryA.GetBaseVersion()); \
597 EXPECT_EQ(server_version, entryA.GetServerVersion()); \
600 TEST_F(SyncerTest, GetCommitIdsFiltersUnreadyEntries) {
601 KeyParams key_params = {"localhost", "dummy", "foobar"};
602 KeyParams other_params = {"localhost", "dummy", "foobar2"};
603 sync_pb::EntitySpecifics bookmark, encrypted_bookmark;
604 bookmark.mutable_bookmark()->set_url("url");
605 bookmark.mutable_bookmark()->set_title("title");
606 AddDefaultFieldValue(BOOKMARKS, &encrypted_bookmark);
607 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
608 foreign_cache_guid(), "-1");
609 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10,
610 foreign_cache_guid(), "-2");
611 mock_server_->AddUpdateDirectory(3, 0, "C", 10, 10,
612 foreign_cache_guid(), "-3");
613 mock_server_->AddUpdateDirectory(4, 0, "D", 10, 10,
614 foreign_cache_guid(), "-4");
616 // Server side change will put A in conflict.
617 mock_server_->AddUpdateDirectory(1, 0, "A", 20, 20,
618 foreign_cache_guid(), "-1");
620 // Mark bookmarks as encrypted and set the cryptographer to have pending
622 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
623 Cryptographer other_cryptographer(&encryptor_);
624 other_cryptographer.AddKey(other_params);
625 sync_pb::EntitySpecifics specifics;
626 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
627 other_cryptographer.GetKeys(nigori->mutable_encryption_keybag());
628 dir_maker_.encryption_handler()->EnableEncryptEverything();
629 // Set up with an old passphrase, but have pending keys
630 GetCryptographer(&wtrans)->AddKey(key_params);
631 GetCryptographer(&wtrans)->Encrypt(bookmark,
632 encrypted_bookmark.mutable_encrypted());
633 GetCryptographer(&wtrans)->SetPendingKeys(nigori->encryption_keybag());
635 // In conflict but properly encrypted.
636 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
637 ASSERT_TRUE(A.good());
638 A.PutIsUnsynced(true);
639 A.PutSpecifics(encrypted_bookmark);
640 A.PutNonUniqueName(kEncryptedString);
641 // Not in conflict and properly encrypted.
642 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
643 ASSERT_TRUE(B.good());
644 B.PutIsUnsynced(true);
645 B.PutSpecifics(encrypted_bookmark);
646 B.PutNonUniqueName(kEncryptedString);
647 // Unencrypted specifics.
648 MutableEntry C(&wtrans, GET_BY_ID, ids_.FromNumber(3));
649 ASSERT_TRUE(C.good());
650 C.PutIsUnsynced(true);
651 C.PutNonUniqueName(kEncryptedString);
652 // Unencrypted non_unique_name.
653 MutableEntry D(&wtrans, GET_BY_ID, ids_.FromNumber(4));
654 ASSERT_TRUE(D.good());
655 D.PutIsUnsynced(true);
656 D.PutSpecifics(encrypted_bookmark);
657 D.PutNonUniqueName("not encrypted");
661 // Nothing should have commited due to bookmarks being encrypted and
662 // the cryptographer having pending keys. A would have been resolved
663 // as a simple conflict, but still be unsynced until the next sync cycle.
664 syncable::ReadTransaction rtrans(FROM_HERE, directory());
665 VERIFY_ENTRY(1, false, true, false, 0, 20, 20, ids_, &rtrans);
666 VERIFY_ENTRY(2, false, true, false, 0, 10, 10, ids_, &rtrans);
667 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_, &rtrans);
668 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_, &rtrans);
670 // Resolve the pending keys.
671 GetCryptographer(&rtrans)->DecryptPendingKeys(other_params);
675 // All properly encrypted and non-conflicting items should commit. "A" was
676 // conflicting, but last sync cycle resolved it as simple conflict, so on
677 // this sync cycle it committed succesfullly.
678 syncable::ReadTransaction rtrans(FROM_HERE, directory());
679 // Committed successfully.
680 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_, &rtrans);
681 // Committed successfully.
682 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_, &rtrans);
683 // Was not properly encrypted.
684 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_, &rtrans);
685 // Was not properly encrypted.
686 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_, &rtrans);
689 // Fix the remaining items.
690 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
691 MutableEntry C(&wtrans, GET_BY_ID, ids_.FromNumber(3));
692 ASSERT_TRUE(C.good());
693 C.PutSpecifics(encrypted_bookmark);
694 C.PutNonUniqueName(kEncryptedString);
695 MutableEntry D(&wtrans, GET_BY_ID, ids_.FromNumber(4));
696 ASSERT_TRUE(D.good());
697 D.PutSpecifics(encrypted_bookmark);
698 D.PutNonUniqueName(kEncryptedString);
702 const StatusController& status_controller = session_->status_controller();
704 EXPECT_EQ(status_controller.model_neutral_state().commit_result, SYNCER_OK);
705 // None should be unsynced anymore.
706 syncable::ReadTransaction rtrans(FROM_HERE, directory());
707 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_, &rtrans);
708 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_, &rtrans);
709 VERIFY_ENTRY(3, false, false, false, 0, 11, 11, ids_, &rtrans);
710 VERIFY_ENTRY(4, false, false, false, 0, 11, 11, ids_, &rtrans);
714 TEST_F(SyncerTest, EncryptionAwareConflicts) {
715 KeyParams key_params = {"localhost", "dummy", "foobar"};
716 Cryptographer other_cryptographer(&encryptor_);
717 other_cryptographer.AddKey(key_params);
718 sync_pb::EntitySpecifics bookmark, encrypted_bookmark, modified_bookmark;
719 bookmark.mutable_bookmark()->set_title("title");
720 other_cryptographer.Encrypt(bookmark,
721 encrypted_bookmark.mutable_encrypted());
722 AddDefaultFieldValue(BOOKMARKS, &encrypted_bookmark);
723 modified_bookmark.mutable_bookmark()->set_title("title2");
724 other_cryptographer.Encrypt(modified_bookmark,
725 modified_bookmark.mutable_encrypted());
726 sync_pb::EntitySpecifics pref, encrypted_pref, modified_pref;
727 pref.mutable_preference()->set_name("name");
728 AddDefaultFieldValue(PREFERENCES, &encrypted_pref);
729 other_cryptographer.Encrypt(pref,
730 encrypted_pref.mutable_encrypted());
731 modified_pref.mutable_preference()->set_name("name2");
732 other_cryptographer.Encrypt(modified_pref,
733 modified_pref.mutable_encrypted());
735 // Mark bookmarks and preferences as encrypted and set the cryptographer to
736 // have pending keys.
737 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
738 sync_pb::EntitySpecifics specifics;
739 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
740 other_cryptographer.GetKeys(nigori->mutable_encryption_keybag());
741 dir_maker_.encryption_handler()->EnableEncryptEverything();
742 GetCryptographer(&wtrans)->SetPendingKeys(nigori->encryption_keybag());
743 EXPECT_TRUE(GetCryptographer(&wtrans)->has_pending_keys());
746 // We need to remember the exact position of our local items, so we can
747 // make updates that do not modify those positions.
752 mock_server_->AddUpdateSpecifics(1, 0, "A", 10, 10, true, 0, bookmark,
753 foreign_cache_guid(), "-1");
754 mock_server_->AddUpdateSpecifics(2, 1, "B", 10, 10, false, 2, bookmark,
755 foreign_cache_guid(), "-2");
756 mock_server_->AddUpdateSpecifics(3, 1, "C", 10, 10, false, 1, bookmark,
757 foreign_cache_guid(), "-3");
758 mock_server_->AddUpdateSpecifics(4, 0, "D", 10, 10, false, 0, pref);
761 // Initial state. Everything is normal.
762 syncable::ReadTransaction rtrans(FROM_HERE, directory());
763 VERIFY_ENTRY(1, false, false, false, 0, 10, 10, ids_, &rtrans);
764 VERIFY_ENTRY(2, false, false, false, 1, 10, 10, ids_, &rtrans);
765 VERIFY_ENTRY(3, false, false, false, 1, 10, 10, ids_, &rtrans);
766 VERIFY_ENTRY(4, false, false, false, 0, 10, 10, ids_, &rtrans);
768 Entry entry1(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1));
769 ASSERT_TRUE(entry1.GetUniquePosition().Equals(
770 entry1.GetServerUniquePosition()));
771 pos1 = entry1.GetUniquePosition();
772 Entry entry2(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(2));
773 pos2 = entry2.GetUniquePosition();
774 Entry entry3(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(3));
775 pos3 = entry3.GetUniquePosition();
778 // Server side encryption will not be applied due to undecryptable data.
779 // At this point, BASE_SERVER_SPECIFICS should be filled for all four items.
780 mock_server_->AddUpdateSpecifics(1, 0, kEncryptedString, 20, 20, true, 0,
782 foreign_cache_guid(), "-1");
783 mock_server_->AddUpdateSpecifics(2, 1, kEncryptedString, 20, 20, false, 2,
785 foreign_cache_guid(), "-2");
786 mock_server_->AddUpdateSpecifics(3, 1, kEncryptedString, 20, 20, false, 1,
788 foreign_cache_guid(), "-3");
789 mock_server_->AddUpdateSpecifics(4, 0, kEncryptedString, 20, 20, false, 0,
791 foreign_cache_guid(), "-4");
794 // All should be unapplied due to being undecryptable and have a valid
795 // BASE_SERVER_SPECIFICS.
796 syncable::ReadTransaction rtrans(FROM_HERE, directory());
797 VERIFY_ENTRY(1, true, false, true, 0, 10, 20, ids_, &rtrans);
798 VERIFY_ENTRY(2, true, false, true, 1, 10, 20, ids_, &rtrans);
799 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_, &rtrans);
800 VERIFY_ENTRY(4, true, false, true, 0, 10, 20, ids_, &rtrans);
803 // Server side change that don't modify anything should not affect
804 // BASE_SERVER_SPECIFICS (such as name changes and mtime changes).
805 mock_server_->AddUpdateSpecifics(1, 0, kEncryptedString, 30, 30, true, 0,
807 foreign_cache_guid(), "-1");
808 mock_server_->AddUpdateSpecifics(2, 1, kEncryptedString, 30, 30, false, 2,
810 foreign_cache_guid(), "-2");
811 // Item 3 doesn't change.
812 mock_server_->AddUpdateSpecifics(4, 0, kEncryptedString, 30, 30, false, 0,
814 foreign_cache_guid(), "-4");
817 // Items 1, 2, and 4 should have newer server versions, 3 remains the same.
818 // All should remain unapplied due to be undecryptable.
819 syncable::ReadTransaction rtrans(FROM_HERE, directory());
820 VERIFY_ENTRY(1, true, false, true, 0, 10, 30, ids_, &rtrans);
821 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_, &rtrans);
822 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_, &rtrans);
823 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_, &rtrans);
826 // Positional changes, parent changes, and specifics changes should reset
827 // BASE_SERVER_SPECIFICS.
828 // Became unencrypted.
829 mock_server_->AddUpdateSpecifics(1, 0, "A", 40, 40, true, 0, bookmark,
830 foreign_cache_guid(), "-1");
831 // Reordered to after item 2.
832 mock_server_->AddUpdateSpecifics(3, 1, kEncryptedString, 30, 30, false, 3,
834 foreign_cache_guid(), "-3");
837 // Items 2 and 4 should be the only ones with BASE_SERVER_SPECIFICS set.
838 // Items 1 is now unencrypted, so should have applied normally.
839 syncable::ReadTransaction rtrans(FROM_HERE, directory());
840 VERIFY_ENTRY(1, false, false, false, 0, 40, 40, ids_, &rtrans);
841 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_, &rtrans);
842 VERIFY_ENTRY(3, true, false, false, 1, 10, 30, ids_, &rtrans);
843 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_, &rtrans);
846 // Make local changes, which should remain unsynced for items 2, 3, 4.
848 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
849 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
850 ASSERT_TRUE(A.good());
851 A.PutSpecifics(modified_bookmark);
852 A.PutNonUniqueName(kEncryptedString);
853 A.PutIsUnsynced(true);
854 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
855 ASSERT_TRUE(B.good());
856 B.PutSpecifics(modified_bookmark);
857 B.PutNonUniqueName(kEncryptedString);
858 B.PutIsUnsynced(true);
859 MutableEntry C(&wtrans, GET_BY_ID, ids_.FromNumber(3));
860 ASSERT_TRUE(C.good());
861 C.PutSpecifics(modified_bookmark);
862 C.PutNonUniqueName(kEncryptedString);
863 C.PutIsUnsynced(true);
864 MutableEntry D(&wtrans, GET_BY_ID, ids_.FromNumber(4));
865 ASSERT_TRUE(D.good());
866 D.PutSpecifics(modified_pref);
867 D.PutNonUniqueName(kEncryptedString);
868 D.PutIsUnsynced(true);
872 // Item 1 remains unsynced due to there being pending keys.
873 // Items 2, 3, 4 should remain unsynced since they were not up to date.
874 syncable::ReadTransaction rtrans(FROM_HERE, directory());
875 VERIFY_ENTRY(1, false, true, false, 0, 40, 40, ids_, &rtrans);
876 VERIFY_ENTRY(2, true, true, true, 1, 10, 30, ids_, &rtrans);
877 VERIFY_ENTRY(3, true, true, false, 1, 10, 30, ids_, &rtrans);
878 VERIFY_ENTRY(4, true, true, true, 0, 10, 30, ids_, &rtrans);
882 syncable::ReadTransaction rtrans(FROM_HERE, directory());
883 // Resolve the pending keys.
884 GetCryptographer(&rtrans)->DecryptPendingKeys(key_params);
886 // First cycle resolves conflicts, second cycle commits changes.
888 EXPECT_EQ(2, status().model_neutral_state().num_server_overwrites);
889 EXPECT_EQ(1, status().model_neutral_state().num_local_overwrites);
890 // We successfully commited item(s).
891 EXPECT_EQ(status().model_neutral_state().commit_result, SYNCER_OK);
894 // Everything should be resolved now. The local changes should have
895 // overwritten the server changes for 2 and 4, while the server changes
896 // overwrote the local for entry 3.
897 EXPECT_EQ(0, status().model_neutral_state().num_server_overwrites);
898 EXPECT_EQ(0, status().model_neutral_state().num_local_overwrites);
899 EXPECT_EQ(status().model_neutral_state().commit_result, SYNCER_OK);
900 syncable::ReadTransaction rtrans(FROM_HERE, directory());
901 VERIFY_ENTRY(1, false, false, false, 0, 41, 41, ids_, &rtrans);
902 VERIFY_ENTRY(2, false, false, false, 1, 31, 31, ids_, &rtrans);
903 VERIFY_ENTRY(3, false, false, false, 1, 30, 30, ids_, &rtrans);
904 VERIFY_ENTRY(4, false, false, false, 0, 31, 31, ids_, &rtrans);
909 TEST_F(SyncerTest, TestGetUnsyncedAndSimpleCommit) {
911 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
912 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Pete");
913 ASSERT_TRUE(parent.good());
914 parent.PutIsUnsynced(true);
915 parent.PutIsDir(true);
916 parent.PutSpecifics(DefaultBookmarkSpecifics());
917 parent.PutBaseVersion(1);
918 parent.PutId(parent_id_);
919 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent_id_, "Pete");
920 ASSERT_TRUE(child.good());
921 child.PutId(child_id_);
922 child.PutBaseVersion(1);
923 WriteTestDataToEntry(&wtrans, &child);
927 ASSERT_EQ(2u, mock_server_->committed_ids().size());
928 // If this test starts failing, be aware other sort orders could be valid.
929 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
930 EXPECT_TRUE(child_id_ == mock_server_->committed_ids()[1]);
932 syncable::ReadTransaction rt(FROM_HERE, directory());
933 Entry entry(&rt, syncable::GET_BY_ID, child_id_);
934 ASSERT_TRUE(entry.good());
935 VerifyTestDataInEntry(&rt, &entry);
939 TEST_F(SyncerTest, TestPurgeWhileUnsynced) {
940 // Similar to above, but throw a purge operation into the mix. Bug 49278.
941 syncable::Id pref_node_id = TestIdFactory::MakeServer("Tim");
943 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
944 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Pete");
945 ASSERT_TRUE(parent.good());
946 parent.PutIsUnsynced(true);
947 parent.PutIsDir(true);
948 parent.PutSpecifics(DefaultBookmarkSpecifics());
949 parent.PutBaseVersion(1);
950 parent.PutId(parent_id_);
951 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent_id_, "Pete");
952 ASSERT_TRUE(child.good());
953 child.PutId(child_id_);
954 child.PutBaseVersion(1);
955 WriteTestDataToEntry(&wtrans, &child);
957 MutableEntry parent2(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Tim");
958 ASSERT_TRUE(parent2.good());
959 parent2.PutIsUnsynced(true);
960 parent2.PutIsDir(true);
961 parent2.PutSpecifics(DefaultPreferencesSpecifics());
962 parent2.PutBaseVersion(1);
963 parent2.PutId(pref_node_id);
966 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES),
971 ASSERT_EQ(2U, mock_server_->committed_ids().size());
972 // If this test starts failing, be aware other sort orders could be valid.
973 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
974 EXPECT_TRUE(child_id_ == mock_server_->committed_ids()[1]);
976 syncable::ReadTransaction rt(FROM_HERE, directory());
977 Entry entry(&rt, syncable::GET_BY_ID, child_id_);
978 ASSERT_TRUE(entry.good());
979 VerifyTestDataInEntry(&rt, &entry);
981 directory()->SaveChanges();
983 syncable::ReadTransaction rt(FROM_HERE, directory());
984 Entry entry(&rt, syncable::GET_BY_ID, pref_node_id);
985 ASSERT_FALSE(entry.good());
989 TEST_F(SyncerTest, TestPurgeWhileUnapplied) {
990 // Similar to above, but for unapplied items. Bug 49278.
992 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
993 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Pete");
994 ASSERT_TRUE(parent.good());
995 parent.PutIsUnappliedUpdate(true);
996 parent.PutIsDir(true);
997 parent.PutSpecifics(DefaultBookmarkSpecifics());
998 parent.PutBaseVersion(1);
999 parent.PutId(parent_id_);
1002 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(BOOKMARKS),
1007 directory()->SaveChanges();
1009 syncable::ReadTransaction rt(FROM_HERE, directory());
1010 Entry entry(&rt, syncable::GET_BY_ID, parent_id_);
1011 ASSERT_FALSE(entry.good());
1015 TEST_F(SyncerTest, TestPurgeWithJournal) {
1017 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1018 MutableEntry parent(&wtrans, syncable::CREATE, BOOKMARKS, wtrans.root_id(),
1020 ASSERT_TRUE(parent.good());
1021 parent.PutIsDir(true);
1022 parent.PutSpecifics(DefaultBookmarkSpecifics());
1023 parent.PutBaseVersion(1);
1024 parent.PutId(parent_id_);
1025 MutableEntry child(&wtrans, syncable::CREATE, BOOKMARKS, parent_id_,
1027 ASSERT_TRUE(child.good());
1028 child.PutId(child_id_);
1029 child.PutBaseVersion(1);
1030 WriteTestDataToEntry(&wtrans, &child);
1032 MutableEntry parent2(&wtrans, syncable::CREATE, PREFERENCES,
1033 wtrans.root_id(), "Tim");
1034 ASSERT_TRUE(parent2.good());
1035 parent2.PutIsDir(true);
1036 parent2.PutSpecifics(DefaultPreferencesSpecifics());
1037 parent2.PutBaseVersion(1);
1038 parent2.PutId(TestIdFactory::MakeServer("Tim"));
1041 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES, BOOKMARKS),
1042 ModelTypeSet(BOOKMARKS),
1045 // Verify bookmark nodes are saved in delete journal but not preference
1047 syncable::ReadTransaction rt(FROM_HERE, directory());
1048 syncable::DeleteJournal* delete_journal = directory()->delete_journal();
1049 EXPECT_EQ(2u, delete_journal->GetDeleteJournalSize(&rt));
1050 syncable::EntryKernelSet journal_entries;
1051 directory()->delete_journal()->GetDeleteJournals(&rt, BOOKMARKS,
1053 EXPECT_EQ(parent_id_, (*journal_entries.begin())->ref(syncable::ID));
1054 EXPECT_EQ(child_id_, (*journal_entries.rbegin())->ref(syncable::ID));
1058 TEST_F(SyncerTest, TestCommitListOrderingTwoItemsTall) {
1059 CommitOrderingTest items[] = {
1060 {1, ids_.FromNumber(-1001), ids_.FromNumber(-1000)},
1061 {0, ids_.FromNumber(-1000), ids_.FromNumber(0)},
1062 CommitOrderingTest::MakeLastCommitItem(),
1064 RunCommitOrderingTest(items);
1067 TEST_F(SyncerTest, TestCommitListOrderingThreeItemsTall) {
1068 CommitOrderingTest items[] = {
1069 {1, ids_.FromNumber(-2001), ids_.FromNumber(-2000)},
1070 {0, ids_.FromNumber(-2000), ids_.FromNumber(0)},
1071 {2, ids_.FromNumber(-2002), ids_.FromNumber(-2001)},
1072 CommitOrderingTest::MakeLastCommitItem(),
1074 RunCommitOrderingTest(items);
1077 TEST_F(SyncerTest, TestCommitListOrderingFourItemsTall) {
1078 CommitOrderingTest items[] = {
1079 {3, ids_.FromNumber(-2003), ids_.FromNumber(-2002)},
1080 {1, ids_.FromNumber(-2001), ids_.FromNumber(-2000)},
1081 {0, ids_.FromNumber(-2000), ids_.FromNumber(0)},
1082 {2, ids_.FromNumber(-2002), ids_.FromNumber(-2001)},
1083 CommitOrderingTest::MakeLastCommitItem(),
1085 RunCommitOrderingTest(items);
1088 TEST_F(SyncerTest, TestCommitListOrderingThreeItemsTallLimitedSize) {
1089 context_->set_max_commit_batch_size(2);
1090 CommitOrderingTest items[] = {
1091 {1, ids_.FromNumber(-2001), ids_.FromNumber(-2000)},
1092 {0, ids_.FromNumber(-2000), ids_.FromNumber(0)},
1093 {2, ids_.FromNumber(-2002), ids_.FromNumber(-2001)},
1094 CommitOrderingTest::MakeLastCommitItem(),
1096 RunCommitOrderingTest(items);
1099 TEST_F(SyncerTest, TestCommitListOrderingSingleDeletedItem) {
1100 CommitOrderingTest items[] = {
1101 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED}},
1102 CommitOrderingTest::MakeLastCommitItem(),
1104 RunCommitOrderingTest(items);
1107 TEST_F(SyncerTest, TestCommitListOrderingSingleUncommittedDeletedItem) {
1108 CommitOrderingTest items[] = {
1109 {-1, ids_.FromNumber(-1000), ids_.FromNumber(0), {DELETED}},
1110 CommitOrderingTest::MakeLastCommitItem(),
1112 RunCommitOrderingTest(items);
1115 TEST_F(SyncerTest, TestCommitListOrderingSingleDeletedItemWithUnroll) {
1116 CommitOrderingTest items[] = {
1117 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED}},
1118 CommitOrderingTest::MakeLastCommitItem(),
1120 RunCommitOrderingTest(items);
1124 TestCommitListOrderingSingleLongDeletedItemWithUnroll) {
1125 CommitOrderingTest items[] = {
1126 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1127 CommitOrderingTest::MakeLastCommitItem(),
1129 RunCommitOrderingTest(items);
1132 TEST_F(SyncerTest, TestCommitListOrderingTwoLongDeletedItemWithUnroll) {
1133 CommitOrderingTest items[] = {
1134 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1135 {-1, ids_.FromNumber(1001), ids_.FromNumber(1000), {DELETED, OLD_MTIME}},
1136 CommitOrderingTest::MakeLastCommitItem(),
1138 RunCommitOrderingTest(items);
1141 TEST_F(SyncerTest, TestCommitListOrdering3LongDeletedItemsWithSizeLimit) {
1142 context_->set_max_commit_batch_size(2);
1143 CommitOrderingTest items[] = {
1144 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1145 {1, ids_.FromNumber(1001), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1146 {2, ids_.FromNumber(1002), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1147 CommitOrderingTest::MakeLastCommitItem(),
1149 RunCommitOrderingTest(items);
1152 TEST_F(SyncerTest, TestCommitListOrderingTwoDeletedItemsWithUnroll) {
1153 CommitOrderingTest items[] = {
1154 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED}},
1155 {-1, ids_.FromNumber(1001), ids_.FromNumber(1000), {DELETED}},
1156 CommitOrderingTest::MakeLastCommitItem(),
1158 RunCommitOrderingTest(items);
1161 TEST_F(SyncerTest, TestCommitListOrderingComplexDeletionScenario) {
1162 CommitOrderingTest items[] = {
1163 { 0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1164 {-1, ids_.FromNumber(1001), ids_.FromNumber(0), {SYNCED}},
1165 {1, ids_.FromNumber(1002), ids_.FromNumber(1001), {DELETED, OLD_MTIME}},
1166 {-1, ids_.FromNumber(1003), ids_.FromNumber(1001), {SYNCED}},
1167 {2, ids_.FromNumber(1004), ids_.FromNumber(1003), {DELETED}},
1168 CommitOrderingTest::MakeLastCommitItem(),
1170 RunCommitOrderingTest(items);
1174 TestCommitListOrderingComplexDeletionScenarioWith2RecentDeletes) {
1175 CommitOrderingTest items[] = {
1176 { 0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1177 {-1, ids_.FromNumber(1001), ids_.FromNumber(0), {SYNCED}},
1178 {1, ids_.FromNumber(1002), ids_.FromNumber(1001), {DELETED, OLD_MTIME}},
1179 {-1, ids_.FromNumber(1003), ids_.FromNumber(1001), {SYNCED}},
1180 {2, ids_.FromNumber(1004), ids_.FromNumber(1003), {DELETED}},
1181 {3, ids_.FromNumber(1005), ids_.FromNumber(1003), {DELETED}},
1182 CommitOrderingTest::MakeLastCommitItem(),
1184 RunCommitOrderingTest(items);
1187 TEST_F(SyncerTest, TestCommitListOrderingDeleteMovedItems) {
1188 CommitOrderingTest items[] = {
1189 {1, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1190 {0, ids_.FromNumber(1001), ids_.FromNumber(1000), {DELETED, OLD_MTIME,
1192 CommitOrderingTest::MakeLastCommitItem(),
1194 RunCommitOrderingTest(items);
1197 TEST_F(SyncerTest, TestCommitListOrderingWithNesting) {
1198 const base::Time& now_minus_2h =
1199 base::Time::Now() - base::TimeDelta::FromHours(2);
1201 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1203 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Bob");
1204 ASSERT_TRUE(parent.good());
1205 parent.PutIsUnsynced(true);
1206 parent.PutIsDir(true);
1207 parent.PutSpecifics(DefaultBookmarkSpecifics());
1208 parent.PutId(ids_.FromNumber(100));
1209 parent.PutBaseVersion(1);
1211 &wtrans, CREATE, BOOKMARKS, ids_.FromNumber(100), "Bob");
1212 ASSERT_TRUE(child.good());
1213 child.PutIsUnsynced(true);
1214 child.PutIsDir(true);
1215 child.PutSpecifics(DefaultBookmarkSpecifics());
1216 child.PutId(ids_.FromNumber(101));
1217 child.PutBaseVersion(1);
1218 MutableEntry grandchild(
1219 &wtrans, CREATE, BOOKMARKS, ids_.FromNumber(101), "Bob");
1220 ASSERT_TRUE(grandchild.good());
1221 grandchild.PutId(ids_.FromNumber(102));
1222 grandchild.PutIsUnsynced(true);
1223 grandchild.PutSpecifics(DefaultBookmarkSpecifics());
1224 grandchild.PutBaseVersion(1);
1227 // Create three deleted items which deletions we expect to be sent to the
1229 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Pete");
1230 ASSERT_TRUE(parent.good());
1231 parent.PutId(ids_.FromNumber(103));
1232 parent.PutIsUnsynced(true);
1233 parent.PutIsDir(true);
1234 parent.PutSpecifics(DefaultBookmarkSpecifics());
1235 parent.PutIsDel(true);
1236 parent.PutBaseVersion(1);
1237 parent.PutMtime(now_minus_2h);
1239 &wtrans, CREATE, BOOKMARKS, ids_.FromNumber(103), "Pete");
1240 ASSERT_TRUE(child.good());
1241 child.PutId(ids_.FromNumber(104));
1242 child.PutIsUnsynced(true);
1243 child.PutIsDir(true);
1244 child.PutSpecifics(DefaultBookmarkSpecifics());
1245 child.PutIsDel(true);
1246 child.PutBaseVersion(1);
1247 child.PutMtime(now_minus_2h);
1248 MutableEntry grandchild(
1249 &wtrans, CREATE, BOOKMARKS, ids_.FromNumber(104), "Pete");
1250 ASSERT_TRUE(grandchild.good());
1251 grandchild.PutId(ids_.FromNumber(105));
1252 grandchild.PutIsUnsynced(true);
1253 grandchild.PutIsDel(true);
1254 grandchild.PutIsDir(false);
1255 grandchild.PutSpecifics(DefaultBookmarkSpecifics());
1256 grandchild.PutBaseVersion(1);
1257 grandchild.PutMtime(now_minus_2h);
1262 ASSERT_EQ(6u, mock_server_->committed_ids().size());
1263 // This test will NOT unroll deletes because SERVER_PARENT_ID is not set.
1264 // It will treat these like moves.
1265 vector<syncable::Id> commit_ids(mock_server_->committed_ids());
1266 EXPECT_TRUE(ids_.FromNumber(100) == commit_ids[0]);
1267 EXPECT_TRUE(ids_.FromNumber(101) == commit_ids[1]);
1268 EXPECT_TRUE(ids_.FromNumber(102) == commit_ids[2]);
1269 // We don't guarantee the delete orders in this test, only that they occur
1271 std::sort(commit_ids.begin() + 3, commit_ids.end());
1272 EXPECT_TRUE(ids_.FromNumber(103) == commit_ids[3]);
1273 EXPECT_TRUE(ids_.FromNumber(104) == commit_ids[4]);
1274 EXPECT_TRUE(ids_.FromNumber(105) == commit_ids[5]);
1277 TEST_F(SyncerTest, TestCommitListOrderingWithNewItems) {
1278 syncable::Id parent1_id = ids_.MakeServer("p1");
1279 syncable::Id parent2_id = ids_.MakeServer("p2");
1282 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1283 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "1");
1284 ASSERT_TRUE(parent.good());
1285 parent.PutIsUnsynced(true);
1286 parent.PutIsDir(true);
1287 parent.PutSpecifics(DefaultBookmarkSpecifics());
1288 parent.PutId(parent1_id);
1289 MutableEntry child(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "2");
1290 ASSERT_TRUE(child.good());
1291 child.PutIsUnsynced(true);
1292 child.PutIsDir(true);
1293 child.PutSpecifics(DefaultBookmarkSpecifics());
1294 child.PutId(parent2_id);
1295 parent.PutBaseVersion(1);
1296 child.PutBaseVersion(1);
1299 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1300 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, parent1_id, "A");
1301 ASSERT_TRUE(parent.good());
1302 parent.PutIsUnsynced(true);
1303 parent.PutIsDir(true);
1304 parent.PutSpecifics(DefaultBookmarkSpecifics());
1305 parent.PutId(ids_.FromNumber(102));
1306 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent1_id, "B");
1307 ASSERT_TRUE(child.good());
1308 child.PutIsUnsynced(true);
1309 child.PutIsDir(true);
1310 child.PutSpecifics(DefaultBookmarkSpecifics());
1311 child.PutId(ids_.FromNumber(-103));
1312 parent.PutBaseVersion(1);
1315 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1316 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, parent2_id, "A");
1317 ASSERT_TRUE(parent.good());
1318 parent.PutIsUnsynced(true);
1319 parent.PutIsDir(true);
1320 parent.PutSpecifics(DefaultBookmarkSpecifics());
1321 parent.PutId(ids_.FromNumber(-104));
1322 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent2_id, "B");
1323 ASSERT_TRUE(child.good());
1324 child.PutIsUnsynced(true);
1325 child.PutIsDir(true);
1326 child.PutSpecifics(DefaultBookmarkSpecifics());
1327 child.PutId(ids_.FromNumber(105));
1328 child.PutBaseVersion(1);
1332 ASSERT_EQ(6u, mock_server_->committed_ids().size());
1334 // This strange iteration and std::count() usage is to allow the order to
1335 // vary. All we really care about is that parent1_id and parent2_id are the
1336 // first two IDs, and that the children make up the next four. Other than
1337 // that, ordering doesn't matter.
1339 vector<syncable::Id>::const_iterator i =
1340 mock_server_->committed_ids().begin();
1341 vector<syncable::Id>::const_iterator parents_begin = i;
1344 vector<syncable::Id>::const_iterator parents_end = i;
1345 vector<syncable::Id>::const_iterator children_begin = i;
1346 vector<syncable::Id>::const_iterator children_end =
1347 mock_server_->committed_ids().end();
1349 EXPECT_EQ(1, count(parents_begin, parents_end, parent1_id));
1350 EXPECT_EQ(1, count(parents_begin, parents_end, parent2_id));
1352 EXPECT_EQ(1, count(children_begin, children_end, ids_.FromNumber(-103)));
1353 EXPECT_EQ(1, count(children_begin, children_end, ids_.FromNumber(102)));
1354 EXPECT_EQ(1, count(children_begin, children_end, ids_.FromNumber(105)));
1355 EXPECT_EQ(1, count(children_begin, children_end, ids_.FromNumber(-104)));
1358 TEST_F(SyncerTest, TestCommitListOrderingCounterexample) {
1359 syncable::Id child2_id = ids_.NewServerId();
1362 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1363 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "P");
1364 ASSERT_TRUE(parent.good());
1365 parent.PutIsUnsynced(true);
1366 parent.PutIsDir(true);
1367 parent.PutSpecifics(DefaultBookmarkSpecifics());
1368 parent.PutId(parent_id_);
1369 MutableEntry child1(&wtrans, CREATE, BOOKMARKS, parent_id_, "1");
1370 ASSERT_TRUE(child1.good());
1371 child1.PutIsUnsynced(true);
1372 child1.PutId(child_id_);
1373 child1.PutSpecifics(DefaultBookmarkSpecifics());
1374 MutableEntry child2(&wtrans, CREATE, BOOKMARKS, parent_id_, "2");
1375 ASSERT_TRUE(child2.good());
1376 child2.PutIsUnsynced(true);
1377 child2.PutSpecifics(DefaultBookmarkSpecifics());
1378 child2.PutId(child2_id);
1380 parent.PutBaseVersion(1);
1381 child1.PutBaseVersion(1);
1382 child2.PutBaseVersion(1);
1386 ASSERT_EQ(3u, mock_server_->committed_ids().size());
1387 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
1388 // There are two possible valid orderings.
1389 if (child2_id == mock_server_->committed_ids()[1]) {
1390 EXPECT_TRUE(child2_id == mock_server_->committed_ids()[1]);
1391 EXPECT_TRUE(child_id_ == mock_server_->committed_ids()[2]);
1393 EXPECT_TRUE(child_id_ == mock_server_->committed_ids()[1]);
1394 EXPECT_TRUE(child2_id == mock_server_->committed_ids()[2]);
1398 TEST_F(SyncerTest, TestCommitListOrderingAndNewParent) {
1399 string parent1_name = "1";
1400 string parent2_name = "A";
1401 string child_name = "B";
1404 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1405 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(),
1407 ASSERT_TRUE(parent.good());
1408 parent.PutIsUnsynced(true);
1409 parent.PutIsDir(true);
1410 parent.PutSpecifics(DefaultBookmarkSpecifics());
1411 parent.PutId(parent_id_);
1412 parent.PutBaseVersion(1);
1415 syncable::Id parent2_id = ids_.NewLocalId();
1416 syncable::Id child_id = ids_.NewServerId();
1418 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1419 MutableEntry parent2(
1420 &wtrans, CREATE, BOOKMARKS, parent_id_, parent2_name);
1421 ASSERT_TRUE(parent2.good());
1422 parent2.PutIsUnsynced(true);
1423 parent2.PutIsDir(true);
1424 parent2.PutSpecifics(DefaultBookmarkSpecifics());
1425 parent2.PutId(parent2_id);
1428 &wtrans, CREATE, BOOKMARKS, parent2_id, child_name);
1429 ASSERT_TRUE(child.good());
1430 child.PutIsUnsynced(true);
1431 child.PutIsDir(true);
1432 child.PutSpecifics(DefaultBookmarkSpecifics());
1433 child.PutId(child_id);
1434 child.PutBaseVersion(1);
1438 ASSERT_EQ(3u, mock_server_->committed_ids().size());
1439 // If this test starts failing, be aware other sort orders could be valid.
1440 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
1441 EXPECT_TRUE(parent2_id == mock_server_->committed_ids()[1]);
1442 EXPECT_TRUE(child_id == mock_server_->committed_ids()[2]);
1444 syncable::ReadTransaction rtrans(FROM_HERE, directory());
1445 // Check that things committed correctly.
1446 Entry entry_1(&rtrans, syncable::GET_BY_ID, parent_id_);
1447 EXPECT_EQ(entry_1.GetNonUniqueName(), parent1_name);
1448 // Check that parent2 is a subfolder of parent1.
1449 EXPECT_EQ(1, CountEntriesWithName(&rtrans,
1453 // Parent2 was a local ID and thus should have changed on commit!
1454 Entry pre_commit_entry_parent2(&rtrans, syncable::GET_BY_ID, parent2_id);
1455 ASSERT_FALSE(pre_commit_entry_parent2.good());
1457 // Look up the new ID.
1458 Id parent2_committed_id =
1459 GetOnlyEntryWithName(&rtrans, parent_id_, parent2_name);
1460 EXPECT_TRUE(parent2_committed_id.ServerKnows());
1462 Entry child(&rtrans, syncable::GET_BY_ID, child_id);
1463 EXPECT_EQ(parent2_committed_id, child.GetParentId());
1467 TEST_F(SyncerTest, TestCommitListOrderingAndNewParentAndChild) {
1468 string parent_name = "1";
1469 string parent2_name = "A";
1470 string child_name = "B";
1473 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1474 MutableEntry parent(&wtrans,
1478 ASSERT_TRUE(parent.good());
1479 parent.PutIsUnsynced(true);
1480 parent.PutIsDir(true);
1481 parent.PutSpecifics(DefaultBookmarkSpecifics());
1482 parent.PutId(parent_id_);
1483 parent.PutBaseVersion(1);
1486 int64 meta_handle_b;
1487 const Id parent2_local_id = ids_.NewLocalId();
1488 const Id child_local_id = ids_.NewLocalId();
1490 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1491 MutableEntry parent2(&wtrans, CREATE, BOOKMARKS, parent_id_, parent2_name);
1492 ASSERT_TRUE(parent2.good());
1493 parent2.PutIsUnsynced(true);
1494 parent2.PutIsDir(true);
1495 parent2.PutSpecifics(DefaultBookmarkSpecifics());
1497 parent2.PutId(parent2_local_id);
1499 &wtrans, CREATE, BOOKMARKS, parent2_local_id, child_name);
1500 ASSERT_TRUE(child.good());
1501 child.PutIsUnsynced(true);
1502 child.PutIsDir(true);
1503 child.PutSpecifics(DefaultBookmarkSpecifics());
1504 child.PutId(child_local_id);
1505 meta_handle_b = child.GetMetahandle();
1509 ASSERT_EQ(3u, mock_server_->committed_ids().size());
1510 // If this test starts failing, be aware other sort orders could be valid.
1511 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
1512 EXPECT_TRUE(parent2_local_id == mock_server_->committed_ids()[1]);
1513 EXPECT_TRUE(child_local_id == mock_server_->committed_ids()[2]);
1515 syncable::ReadTransaction rtrans(FROM_HERE, directory());
1517 Entry parent(&rtrans, syncable::GET_BY_ID,
1518 GetOnlyEntryWithName(&rtrans, rtrans.root_id(), parent_name));
1519 ASSERT_TRUE(parent.good());
1520 EXPECT_TRUE(parent.GetId().ServerKnows());
1522 Entry parent2(&rtrans, syncable::GET_BY_ID,
1523 GetOnlyEntryWithName(&rtrans, parent.GetId(), parent2_name));
1524 ASSERT_TRUE(parent2.good());
1525 EXPECT_TRUE(parent2.GetId().ServerKnows());
1527 // Id changed on commit, so this should fail.
1528 Entry local_parent2_id_entry(&rtrans,
1529 syncable::GET_BY_ID,
1531 ASSERT_FALSE(local_parent2_id_entry.good());
1533 Entry entry_b(&rtrans, syncable::GET_BY_HANDLE, meta_handle_b);
1534 EXPECT_TRUE(entry_b.GetId().ServerKnows());
1535 EXPECT_TRUE(parent2.GetId()== entry_b.GetParentId());
1539 TEST_F(SyncerTest, UpdateWithZeroLengthName) {
1540 // One illegal update
1541 mock_server_->AddUpdateDirectory(
1542 1, 0, std::string(), 1, 10, foreign_cache_guid(), "-1");
1543 // And one legal one that we're going to delete.
1544 mock_server_->AddUpdateDirectory(2, 0, "FOO", 1, 10,
1545 foreign_cache_guid(), "-2");
1547 // Delete the legal one. The new update has a null name.
1548 mock_server_->AddUpdateDirectory(
1549 2, 0, std::string(), 2, 20, foreign_cache_guid(), "-2");
1550 mock_server_->SetLastUpdateDeleted();
1554 TEST_F(SyncerTest, TestBasicUpdate) {
1555 string id = "some_id";
1556 string parent_id = "0";
1557 string name = "in_root";
1559 int64 timestamp = 10;
1560 mock_server_->AddUpdateDirectory(id, parent_id, name, version, timestamp,
1561 foreign_cache_guid(), "-1");
1565 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1566 Entry entry(&trans, GET_BY_ID,
1567 syncable::Id::CreateFromServerId("some_id"));
1568 ASSERT_TRUE(entry.good());
1569 EXPECT_TRUE(entry.GetIsDir());
1570 EXPECT_TRUE(entry.GetServerVersion()== version);
1571 EXPECT_TRUE(entry.GetBaseVersion()== version);
1572 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
1573 EXPECT_FALSE(entry.GetIsUnsynced());
1574 EXPECT_FALSE(entry.GetServerIsDel());
1575 EXPECT_FALSE(entry.GetIsDel());
1579 TEST_F(SyncerTest, IllegalAndLegalUpdates) {
1580 Id root = TestIdFactory::root();
1581 // Should apply just fine.
1582 mock_server_->AddUpdateDirectory(1, 0, "in_root", 10, 10,
1583 foreign_cache_guid(), "-1");
1585 // Same name. But this SHOULD work.
1586 mock_server_->AddUpdateDirectory(2, 0, "in_root", 10, 10,
1587 foreign_cache_guid(), "-2");
1589 // Unknown parent: should never be applied. "-80" is a legal server ID,
1590 // because any string sent by the server is a legal server ID in the sync
1591 // protocol, but it's not the ID of any item known to the client. This
1592 // update should succeed validation, but be stuck in the unapplied state
1593 // until an item with the server ID "-80" arrives.
1594 mock_server_->AddUpdateDirectory(3, -80, "bad_parent", 10, 10,
1595 foreign_cache_guid(), "-3");
1599 // Id 3 should be in conflict now.
1600 EXPECT_EQ(1, status().TotalNumConflictingItems());
1601 EXPECT_EQ(1, status().num_hierarchy_conflicts());
1603 // The only request in that loop should have been a GetUpdate.
1604 // At that point, we didn't know whether or not we had conflicts.
1605 ASSERT_TRUE(mock_server_->last_request().has_get_updates());
1606 VerifyHierarchyConflictsUnspecified(mock_server_->last_request());
1608 // These entries will be used in the second set of updates.
1609 mock_server_->AddUpdateDirectory(4, 0, "newer_version", 20, 10,
1610 foreign_cache_guid(), "-4");
1611 mock_server_->AddUpdateDirectory(5, 0, "circular1", 10, 10,
1612 foreign_cache_guid(), "-5");
1613 mock_server_->AddUpdateDirectory(6, 5, "circular2", 10, 10,
1614 foreign_cache_guid(), "-6");
1615 mock_server_->AddUpdateDirectory(9, 3, "bad_parent_child", 10, 10,
1616 foreign_cache_guid(), "-9");
1617 mock_server_->AddUpdateDirectory(100, 9, "bad_parent_child2", 10, 10,
1618 foreign_cache_guid(), "-100");
1619 mock_server_->AddUpdateDirectory(10, 0, "dir_to_bookmark", 10, 10,
1620 foreign_cache_guid(), "-10");
1623 // The three items with an unresolved parent should be unapplied (3, 9, 100).
1624 // The name clash should also still be in conflict.
1625 EXPECT_EQ(3, status().TotalNumConflictingItems());
1626 EXPECT_EQ(3, status().num_hierarchy_conflicts());
1628 // This time around, we knew that there were conflicts.
1629 ASSERT_TRUE(mock_server_->last_request().has_get_updates());
1630 VerifyHierarchyConflictsReported(mock_server_->last_request());
1633 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1634 // Even though it has the same name, it should work.
1635 Entry name_clash(&trans, GET_BY_ID, ids_.FromNumber(2));
1636 ASSERT_TRUE(name_clash.good());
1637 EXPECT_FALSE(name_clash.GetIsUnappliedUpdate())
1638 << "Duplicate name SHOULD be OK.";
1640 Entry bad_parent(&trans, GET_BY_ID, ids_.FromNumber(3));
1641 ASSERT_TRUE(bad_parent.good());
1642 EXPECT_TRUE(bad_parent.GetIsUnappliedUpdate())
1643 << "child of unknown parent should be in conflict";
1645 Entry bad_parent_child(&trans, GET_BY_ID, ids_.FromNumber(9));
1646 ASSERT_TRUE(bad_parent_child.good());
1647 EXPECT_TRUE(bad_parent_child.GetIsUnappliedUpdate())
1648 << "grandchild of unknown parent should be in conflict";
1650 Entry bad_parent_child2(&trans, GET_BY_ID, ids_.FromNumber(100));
1651 ASSERT_TRUE(bad_parent_child2.good());
1652 EXPECT_TRUE(bad_parent_child2.GetIsUnappliedUpdate())
1653 << "great-grandchild of unknown parent should be in conflict";
1656 // Updating 1 should not affect item 2 of the same name.
1657 mock_server_->AddUpdateDirectory(1, 0, "new_name", 20, 20,
1658 foreign_cache_guid(), "-1");
1660 // Moving 5 under 6 will create a cycle: a conflict.
1661 mock_server_->AddUpdateDirectory(5, 6, "circular3", 20, 20,
1662 foreign_cache_guid(), "-5");
1664 // Flip the is_dir bit: should fail verify & be dropped.
1665 mock_server_->AddUpdateBookmark(10, 0, "dir_to_bookmark", 20, 20,
1666 foreign_cache_guid(), "-10");
1669 // Version number older than last known: should fail verify & be dropped.
1670 mock_server_->AddUpdateDirectory(4, 0, "old_version", 10, 10,
1671 foreign_cache_guid(), "-4");
1674 syncable::ReadTransaction trans(FROM_HERE, directory());
1676 Entry still_a_dir(&trans, GET_BY_ID, ids_.FromNumber(10));
1677 ASSERT_TRUE(still_a_dir.good());
1678 EXPECT_FALSE(still_a_dir.GetIsUnappliedUpdate());
1679 EXPECT_EQ(10u, still_a_dir.GetBaseVersion());
1680 EXPECT_EQ(10u, still_a_dir.GetServerVersion());
1681 EXPECT_TRUE(still_a_dir.GetIsDir());
1683 Entry rename(&trans, GET_BY_ID, ids_.FromNumber(1));
1684 ASSERT_TRUE(rename.good());
1685 EXPECT_EQ(root, rename.GetParentId());
1686 EXPECT_EQ("new_name", rename.GetNonUniqueName());
1687 EXPECT_FALSE(rename.GetIsUnappliedUpdate());
1688 EXPECT_TRUE(ids_.FromNumber(1) == rename.GetId());
1689 EXPECT_EQ(20u, rename.GetBaseVersion());
1691 Entry name_clash(&trans, GET_BY_ID, ids_.FromNumber(2));
1692 ASSERT_TRUE(name_clash.good());
1693 EXPECT_EQ(root, name_clash.GetParentId());
1694 EXPECT_TRUE(ids_.FromNumber(2) == name_clash.GetId());
1695 EXPECT_EQ(10u, name_clash.GetBaseVersion());
1696 EXPECT_EQ("in_root", name_clash.GetNonUniqueName());
1698 Entry ignored_old_version(&trans, GET_BY_ID, ids_.FromNumber(4));
1699 ASSERT_TRUE(ignored_old_version.good());
1701 ignored_old_version.GetNonUniqueName()== "newer_version");
1702 EXPECT_FALSE(ignored_old_version.GetIsUnappliedUpdate());
1703 EXPECT_EQ(20u, ignored_old_version.GetBaseVersion());
1705 Entry circular_parent_issue(&trans, GET_BY_ID, ids_.FromNumber(5));
1706 ASSERT_TRUE(circular_parent_issue.good());
1707 EXPECT_TRUE(circular_parent_issue.GetIsUnappliedUpdate())
1708 << "circular move should be in conflict";
1709 EXPECT_TRUE(circular_parent_issue.GetParentId()== root_id_);
1710 EXPECT_TRUE(circular_parent_issue.GetServerParentId()==
1711 ids_.FromNumber(6));
1712 EXPECT_EQ(10u, circular_parent_issue.GetBaseVersion());
1714 Entry circular_parent_target(&trans, GET_BY_ID, ids_.FromNumber(6));
1715 ASSERT_TRUE(circular_parent_target.good());
1716 EXPECT_FALSE(circular_parent_target.GetIsUnappliedUpdate());
1717 EXPECT_TRUE(circular_parent_issue.GetId()==
1718 circular_parent_target.GetParentId());
1719 EXPECT_EQ(10u, circular_parent_target.GetBaseVersion());
1722 EXPECT_FALSE(saw_syncer_event_);
1723 EXPECT_EQ(4, status().TotalNumConflictingItems());
1724 EXPECT_EQ(4, status().num_hierarchy_conflicts());
1727 // A commit with a lost response produces an update that has to be reunited with
1729 TEST_F(SyncerTest, CommitReuniteUpdateAdjustsChildren) {
1730 // Create a folder in the root.
1731 int64 metahandle_folder;
1733 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1735 &trans, CREATE, BOOKMARKS, trans.root_id(), "new_folder");
1736 ASSERT_TRUE(entry.good());
1737 entry.PutIsDir(true);
1738 entry.PutSpecifics(DefaultBookmarkSpecifics());
1739 entry.PutIsUnsynced(true);
1740 metahandle_folder = entry.GetMetahandle();
1743 // Verify it and pull the ID out of the folder.
1744 syncable::Id folder_id;
1745 int64 metahandle_entry;
1747 syncable::ReadTransaction trans(FROM_HERE, directory());
1748 Entry entry(&trans, GET_BY_HANDLE, metahandle_folder);
1749 ASSERT_TRUE(entry.good());
1750 folder_id = entry.GetId();
1751 ASSERT_TRUE(!folder_id.ServerKnows());
1754 // Create an entry in the newly created folder.
1756 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1757 MutableEntry entry(&trans, CREATE, BOOKMARKS, folder_id, "new_entry");
1758 ASSERT_TRUE(entry.good());
1759 metahandle_entry = entry.GetMetahandle();
1760 WriteTestDataToEntry(&trans, &entry);
1763 // Verify it and pull the ID out of the entry.
1764 syncable::Id entry_id;
1766 syncable::ReadTransaction trans(FROM_HERE, directory());
1767 Entry entry(&trans, syncable::GET_BY_HANDLE, metahandle_entry);
1768 ASSERT_TRUE(entry.good());
1769 EXPECT_EQ(folder_id, entry.GetParentId());
1770 EXPECT_EQ("new_entry", entry.GetNonUniqueName());
1771 entry_id = entry.GetId();
1772 EXPECT_TRUE(!entry_id.ServerKnows());
1773 VerifyTestDataInEntry(&trans, &entry);
1776 // Now, to emulate a commit response failure, we just don't commit it.
1777 int64 new_version = 150; // any larger value.
1778 int64 timestamp = 20; // arbitrary value.
1779 syncable::Id new_folder_id =
1780 syncable::Id::CreateFromServerId("folder_server_id");
1782 // The following update should cause the folder to both apply the update, as
1783 // well as reassociate the id.
1784 mock_server_->AddUpdateDirectory(new_folder_id, root_id_,
1785 "new_folder", new_version, timestamp,
1786 local_cache_guid(), folder_id.GetServerId());
1788 // We don't want it accidentally committed, just the update applied.
1789 mock_server_->set_conflict_all_commits(true);
1791 // Alright! Apply that update!
1794 // The folder's ID should have been updated.
1795 syncable::ReadTransaction trans(FROM_HERE, directory());
1796 Entry folder(&trans, GET_BY_HANDLE, metahandle_folder);
1797 ASSERT_TRUE(folder.good());
1798 EXPECT_EQ("new_folder", folder.GetNonUniqueName());
1799 EXPECT_TRUE(new_version == folder.GetBaseVersion());
1800 EXPECT_TRUE(new_folder_id == folder.GetId());
1801 EXPECT_TRUE(folder.GetId().ServerKnows());
1802 EXPECT_EQ(trans.root_id(), folder.GetParentId());
1804 // Since it was updated, the old folder should not exist.
1805 Entry old_dead_folder(&trans, GET_BY_ID, folder_id);
1806 EXPECT_FALSE(old_dead_folder.good());
1808 // The child's parent should have changed.
1809 Entry entry(&trans, syncable::GET_BY_HANDLE, metahandle_entry);
1810 ASSERT_TRUE(entry.good());
1811 EXPECT_EQ("new_entry", entry.GetNonUniqueName());
1812 EXPECT_EQ(new_folder_id, entry.GetParentId());
1813 EXPECT_TRUE(!entry.GetId().ServerKnows());
1814 VerifyTestDataInEntry(&trans, &entry);
1818 // A commit with a lost response produces an update that has to be reunited with
1820 TEST_F(SyncerTest, CommitReuniteUpdate) {
1821 // Create an entry in the root.
1822 int64 entry_metahandle;
1824 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1825 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "new_entry");
1826 ASSERT_TRUE(entry.good());
1827 entry_metahandle = entry.GetMetahandle();
1828 WriteTestDataToEntry(&trans, &entry);
1831 // Verify it and pull the ID out.
1832 syncable::Id entry_id;
1834 syncable::ReadTransaction trans(FROM_HERE, directory());
1836 Entry entry(&trans, GET_BY_HANDLE, entry_metahandle);
1837 ASSERT_TRUE(entry.good());
1838 entry_id = entry.GetId();
1839 EXPECT_TRUE(!entry_id.ServerKnows());
1840 VerifyTestDataInEntry(&trans, &entry);
1843 // Now, to emulate a commit response failure, we just don't commit it.
1844 int64 new_version = 150; // any larger value.
1845 int64 timestamp = 20; // arbitrary value.
1846 syncable::Id new_entry_id = syncable::Id::CreateFromServerId("server_id");
1848 // Generate an update from the server with a relevant ID reassignment.
1849 mock_server_->AddUpdateBookmark(new_entry_id, root_id_,
1850 "new_entry", new_version, timestamp,
1851 local_cache_guid(), entry_id.GetServerId());
1853 // We don't want it accidentally committed, just the update applied.
1854 mock_server_->set_conflict_all_commits(true);
1856 // Alright! Apply that update!
1859 syncable::ReadTransaction trans(FROM_HERE, directory());
1860 Entry entry(&trans, GET_BY_HANDLE, entry_metahandle);
1861 ASSERT_TRUE(entry.good());
1862 EXPECT_TRUE(new_version == entry.GetBaseVersion());
1863 EXPECT_TRUE(new_entry_id == entry.GetId());
1864 EXPECT_EQ("new_entry", entry.GetNonUniqueName());
1868 // A commit with a lost response must work even if the local entry was deleted
1869 // before the update is applied. We should not duplicate the local entry in
1870 // this case, but just create another one alongside. We may wish to examine
1871 // this behavior in the future as it can create hanging uploads that never
1872 // finish, that must be cleaned up on the server side after some time.
1873 TEST_F(SyncerTest, CommitReuniteUpdateDoesNotChokeOnDeletedLocalEntry) {
1874 // Create a entry in the root.
1875 int64 entry_metahandle;
1877 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1878 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "new_entry");
1879 ASSERT_TRUE(entry.good());
1880 entry_metahandle = entry.GetMetahandle();
1881 WriteTestDataToEntry(&trans, &entry);
1883 // Verify it and pull the ID out.
1884 syncable::Id entry_id;
1886 syncable::ReadTransaction trans(FROM_HERE, directory());
1887 Entry entry(&trans, GET_BY_HANDLE, entry_metahandle);
1888 ASSERT_TRUE(entry.good());
1889 entry_id = entry.GetId();
1890 EXPECT_TRUE(!entry_id.ServerKnows());
1891 VerifyTestDataInEntry(&trans, &entry);
1894 // Now, to emulate a commit response failure, we just don't commit it.
1895 int64 new_version = 150; // any larger value.
1896 int64 timestamp = 20; // arbitrary value.
1897 syncable::Id new_entry_id = syncable::Id::CreateFromServerId("server_id");
1899 // Generate an update from the server with a relevant ID reassignment.
1900 mock_server_->AddUpdateBookmark(new_entry_id, root_id_,
1901 "new_entry", new_version, timestamp,
1902 local_cache_guid(), entry_id.GetServerId());
1904 // We don't want it accidentally committed, just the update applied.
1905 mock_server_->set_conflict_all_commits(true);
1907 // Purposefully delete the entry now before the update application finishes.
1909 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1910 Id new_entry_id = GetOnlyEntryWithName(
1911 &trans, trans.root_id(), "new_entry");
1912 MutableEntry entry(&trans, GET_BY_ID, new_entry_id);
1913 ASSERT_TRUE(entry.good());
1914 entry.PutIsDel(true);
1917 // Just don't CHECK fail in sync, have the update split.
1920 syncable::ReadTransaction trans(FROM_HERE, directory());
1921 Id new_entry_id = GetOnlyEntryWithName(
1922 &trans, trans.root_id(), "new_entry");
1923 Entry entry(&trans, GET_BY_ID, new_entry_id);
1924 ASSERT_TRUE(entry.good());
1925 EXPECT_FALSE(entry.GetIsDel());
1927 Entry old_entry(&trans, GET_BY_ID, entry_id);
1928 ASSERT_TRUE(old_entry.good());
1929 EXPECT_TRUE(old_entry.GetIsDel());
1933 // TODO(chron): Add more unsanitized name tests.
1934 TEST_F(SyncerTest, ConflictMatchingEntryHandlesUnsanitizedNames) {
1935 mock_server_->AddUpdateDirectory(1, 0, "A/A", 10, 10,
1936 foreign_cache_guid(), "-1");
1937 mock_server_->AddUpdateDirectory(2, 0, "B/B", 10, 10,
1938 foreign_cache_guid(), "-2");
1939 mock_server_->set_conflict_all_commits(true);
1942 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1944 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
1945 ASSERT_TRUE(A.good());
1946 A.PutIsUnsynced(true);
1947 A.PutIsUnappliedUpdate(true);
1948 A.PutServerVersion(20);
1950 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
1951 ASSERT_TRUE(B.good());
1952 B.PutIsUnappliedUpdate(true);
1953 B.PutServerVersion(20);
1956 saw_syncer_event_ = false;
1957 mock_server_->set_conflict_all_commits(false);
1960 syncable::ReadTransaction trans(FROM_HERE, directory());
1962 Entry A(&trans, GET_BY_ID, ids_.FromNumber(1));
1963 ASSERT_TRUE(A.good());
1964 EXPECT_TRUE(A.GetIsUnsynced()== false);
1965 EXPECT_TRUE(A.GetIsUnappliedUpdate()== false);
1966 EXPECT_TRUE(A.GetServerVersion()== 20);
1968 Entry B(&trans, GET_BY_ID, ids_.FromNumber(2));
1969 ASSERT_TRUE(B.good());
1970 EXPECT_TRUE(B.GetIsUnsynced()== false);
1971 EXPECT_TRUE(B.GetIsUnappliedUpdate()== false);
1972 EXPECT_TRUE(B.GetServerVersion()== 20);
1976 TEST_F(SyncerTest, ConflictMatchingEntryHandlesNormalNames) {
1977 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
1978 foreign_cache_guid(), "-1");
1979 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10,
1980 foreign_cache_guid(), "-2");
1981 mock_server_->set_conflict_all_commits(true);
1984 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1986 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
1987 ASSERT_TRUE(A.good());
1988 A.PutIsUnsynced(true);
1989 A.PutIsUnappliedUpdate(true);
1990 A.PutServerVersion(20);
1992 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
1993 ASSERT_TRUE(B.good());
1994 B.PutIsUnappliedUpdate(true);
1995 B.PutServerVersion(20);
1998 saw_syncer_event_ = false;
1999 mock_server_->set_conflict_all_commits(false);
2002 syncable::ReadTransaction trans(FROM_HERE, directory());
2004 Entry A(&trans, GET_BY_ID, ids_.FromNumber(1));
2005 ASSERT_TRUE(A.good());
2006 EXPECT_TRUE(A.GetIsUnsynced()== false);
2007 EXPECT_TRUE(A.GetIsUnappliedUpdate()== false);
2008 EXPECT_TRUE(A.GetServerVersion()== 20);
2010 Entry B(&trans, GET_BY_ID, ids_.FromNumber(2));
2011 ASSERT_TRUE(B.good());
2012 EXPECT_TRUE(B.GetIsUnsynced()== false);
2013 EXPECT_TRUE(B.GetIsUnappliedUpdate()== false);
2014 EXPECT_TRUE(B.GetServerVersion()== 20);
2018 TEST_F(SyncerTest, ReverseFolderOrderingTest) {
2019 mock_server_->AddUpdateDirectory(4, 3, "ggchild", 10, 10,
2020 foreign_cache_guid(), "-4");
2021 mock_server_->AddUpdateDirectory(3, 2, "gchild", 10, 10,
2022 foreign_cache_guid(), "-3");
2023 mock_server_->AddUpdateDirectory(5, 4, "gggchild", 10, 10,
2024 foreign_cache_guid(), "-5");
2025 mock_server_->AddUpdateDirectory(2, 1, "child", 10, 10,
2026 foreign_cache_guid(), "-2");
2027 mock_server_->AddUpdateDirectory(1, 0, "parent", 10, 10,
2028 foreign_cache_guid(), "-1");
2030 syncable::ReadTransaction trans(FROM_HERE, directory());
2032 Id child_id = GetOnlyEntryWithName(
2033 &trans, ids_.FromNumber(4), "gggchild");
2034 Entry child(&trans, GET_BY_ID, child_id);
2035 ASSERT_TRUE(child.good());
2038 class EntryCreatedInNewFolderTest : public SyncerTest {
2040 void CreateFolderInBob() {
2041 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2042 MutableEntry bob(&trans,
2043 syncable::GET_BY_ID,
2044 GetOnlyEntryWithName(&trans,
2045 TestIdFactory::root(),
2049 MutableEntry entry2(
2050 &trans, CREATE, BOOKMARKS, bob.GetId(), "bob");
2051 CHECK(entry2.good());
2052 entry2.PutIsDir(true);
2053 entry2.PutIsUnsynced(true);
2054 entry2.PutSpecifics(DefaultBookmarkSpecifics());
2058 TEST_F(EntryCreatedInNewFolderTest, EntryCreatedInNewFolderMidSync) {
2060 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2061 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "bob");
2062 ASSERT_TRUE(entry.good());
2063 entry.PutIsDir(true);
2064 entry.PutIsUnsynced(true);
2065 entry.PutSpecifics(DefaultBookmarkSpecifics());
2068 mock_server_->SetMidCommitCallback(
2069 base::Bind(&EntryCreatedInNewFolderTest::CreateFolderInBob,
2070 base::Unretained(this)));
2072 // We loop until no unsynced handles remain, so we will commit both ids.
2073 EXPECT_EQ(2u, mock_server_->committed_ids().size());
2075 syncable::ReadTransaction trans(FROM_HERE, directory());
2076 Entry parent_entry(&trans, syncable::GET_BY_ID,
2077 GetOnlyEntryWithName(&trans, TestIdFactory::root(), "bob"));
2078 ASSERT_TRUE(parent_entry.good());
2081 GetOnlyEntryWithName(&trans, parent_entry.GetId(), "bob");
2082 Entry child(&trans, syncable::GET_BY_ID, child_id);
2083 ASSERT_TRUE(child.good());
2084 EXPECT_EQ(parent_entry.GetId(), child.GetParentId());
2088 TEST_F(SyncerTest, NegativeIDInUpdate) {
2089 mock_server_->AddUpdateBookmark(-10, 0, "bad", 40, 40,
2090 foreign_cache_guid(), "-100");
2092 // The negative id would make us CHECK!
2095 TEST_F(SyncerTest, UnappliedUpdateOnCreatedItemItemDoesNotCrash) {
2096 int64 metahandle_fred;
2097 syncable::Id orig_id;
2100 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2101 MutableEntry fred_match(&trans, CREATE, BOOKMARKS, trans.root_id(),
2103 ASSERT_TRUE(fred_match.good());
2104 metahandle_fred = fred_match.GetMetahandle();
2105 orig_id = fred_match.GetId();
2106 WriteTestDataToEntry(&trans, &fred_match);
2110 EXPECT_EQ(1u, mock_server_->committed_ids().size());
2111 mock_server_->set_conflict_all_commits(true);
2112 syncable::Id fred_match_id;
2114 // Now receive a change from outside.
2115 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2116 MutableEntry fred_match(&trans, GET_BY_HANDLE, metahandle_fred);
2117 ASSERT_TRUE(fred_match.good());
2118 EXPECT_TRUE(fred_match.GetId().ServerKnows());
2119 fred_match_id = fred_match.GetId();
2120 mock_server_->AddUpdateBookmark(fred_match_id, trans.root_id(),
2121 "fred_match", 40, 40, local_cache_guid(), orig_id.GetServerId());
2124 for (int i = 0 ; i < 30 ; ++i) {
2130 * In the event that we have a double changed entry, that is changed on both
2131 * the client and the server, the conflict resolver should just drop one of
2132 * them and accept the other.
2135 TEST_F(SyncerTest, DoublyChangedWithResolver) {
2136 syncable::Id local_id;
2138 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2139 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, root_id_, "Folder");
2140 ASSERT_TRUE(parent.good());
2141 parent.PutIsDir(true);
2142 parent.PutId(parent_id_);
2143 parent.PutBaseVersion(5);
2144 parent.PutSpecifics(DefaultBookmarkSpecifics());
2145 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent_id_, "Pete.htm");
2146 ASSERT_TRUE(child.good());
2147 local_id = child.GetId();
2148 child.PutId(child_id_);
2149 child.PutBaseVersion(10);
2150 WriteTestDataToEntry(&wtrans, &child);
2152 mock_server_->AddUpdateBookmark(child_id_, parent_id_, "Pete2.htm", 11, 10,
2153 local_cache_guid(), local_id.GetServerId());
2154 mock_server_->set_conflict_all_commits(true);
2156 syncable::Directory::Metahandles children;
2158 syncable::ReadTransaction trans(FROM_HERE, directory());
2159 directory()->GetChildHandlesById(&trans, parent_id_, &children);
2160 // We expect the conflict resolver to preserve the local entry.
2161 Entry child(&trans, syncable::GET_BY_ID, child_id_);
2162 ASSERT_TRUE(child.good());
2163 EXPECT_TRUE(child.GetIsUnsynced());
2164 EXPECT_FALSE(child.GetIsUnappliedUpdate());
2165 EXPECT_TRUE(child.GetSpecifics().has_bookmark());
2166 EXPECT_EQ("Pete.htm", child.GetNonUniqueName());
2167 VerifyTestBookmarkDataInEntry(&child);
2170 // Only one entry, since we just overwrite one.
2171 EXPECT_EQ(1u, children.size());
2172 saw_syncer_event_ = false;
2175 // We got this repro case when someone was editing bookmarks while sync was
2176 // occuring. The entry had changed out underneath the user.
2177 TEST_F(SyncerTest, CommitsUpdateDoesntAlterEntry) {
2178 const base::Time& test_time = ProtoTimeToTime(123456);
2179 syncable::Id local_id;
2180 int64 entry_metahandle;
2182 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2183 MutableEntry entry(&wtrans, CREATE, BOOKMARKS, root_id_, "Pete");
2184 ASSERT_TRUE(entry.good());
2185 EXPECT_FALSE(entry.GetId().ServerKnows());
2186 local_id = entry.GetId();
2187 entry.PutIsDir(true);
2188 entry.PutSpecifics(DefaultBookmarkSpecifics());
2189 entry.PutIsUnsynced(true);
2190 entry.PutMtime(test_time);
2191 entry_metahandle = entry.GetMetahandle();
2197 syncable::ReadTransaction trans(FROM_HERE, directory());
2198 Entry entry(&trans, syncable::GET_BY_HANDLE, entry_metahandle);
2199 ASSERT_TRUE(entry.good());
2201 EXPECT_TRUE(id.ServerKnows());
2202 version = entry.GetBaseVersion();
2204 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
2205 update->set_originator_cache_guid(local_cache_guid());
2206 update->set_originator_client_item_id(local_id.GetServerId());
2207 EXPECT_EQ("Pete", update->name());
2208 EXPECT_EQ(id.GetServerId(), update->id_string());
2209 EXPECT_EQ(root_id_.GetServerId(), update->parent_id_string());
2210 EXPECT_EQ(version, update->version());
2213 syncable::ReadTransaction trans(FROM_HERE, directory());
2214 Entry entry(&trans, syncable::GET_BY_ID, id);
2215 ASSERT_TRUE(entry.good());
2216 EXPECT_TRUE(entry.GetMtime()== test_time);
2220 TEST_F(SyncerTest, ParentAndChildBothMatch) {
2221 const FullModelTypeSet all_types = FullModelTypeSet::All();
2222 syncable::Id parent_id = ids_.NewServerId();
2223 syncable::Id child_id = ids_.NewServerId();
2224 syncable::Id parent_local_id;
2225 syncable::Id child_local_id;
2229 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2230 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, root_id_, "Folder");
2231 ASSERT_TRUE(parent.good());
2232 parent_local_id = parent.GetId();
2233 parent.PutIsDir(true);
2234 parent.PutIsUnsynced(true);
2235 parent.PutId(parent_id);
2236 parent.PutBaseVersion(1);
2237 parent.PutSpecifics(DefaultBookmarkSpecifics());
2239 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent.GetId(), "test.htm");
2240 ASSERT_TRUE(child.good());
2241 child_local_id = child.GetId();
2242 child.PutId(child_id);
2243 child.PutBaseVersion(1);
2244 child.PutSpecifics(DefaultBookmarkSpecifics());
2245 WriteTestDataToEntry(&wtrans, &child);
2247 mock_server_->AddUpdateDirectory(parent_id, root_id_, "Folder", 10, 10,
2249 parent_local_id.GetServerId());
2250 mock_server_->AddUpdateBookmark(child_id, parent_id, "test.htm", 10, 10,
2252 child_local_id.GetServerId());
2253 mock_server_->set_conflict_all_commits(true);
2258 syncable::ReadTransaction trans(FROM_HERE, directory());
2259 Directory::Metahandles children;
2260 directory()->GetChildHandlesById(&trans, root_id_, &children);
2261 EXPECT_EQ(1u, children.size());
2262 directory()->GetChildHandlesById(&trans, parent_id, &children);
2263 EXPECT_EQ(1u, children.size());
2264 std::vector<int64> unapplied;
2265 directory()->GetUnappliedUpdateMetaHandles(&trans, all_types, &unapplied);
2266 EXPECT_EQ(0u, unapplied.size());
2267 syncable::Directory::Metahandles unsynced;
2268 directory()->GetUnsyncedMetaHandles(&trans, &unsynced);
2269 EXPECT_EQ(0u, unsynced.size());
2270 saw_syncer_event_ = false;
2274 TEST_F(SyncerTest, CommittingNewDeleted) {
2276 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2277 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "bob");
2278 entry.PutIsUnsynced(true);
2279 entry.PutIsDel(true);
2282 EXPECT_EQ(0u, mock_server_->committed_ids().size());
2285 // Original problem synopsis:
2286 // Check failed: entry->GetBaseVersion()<= entry->GetServerVersion()
2287 // Client creates entry, client finishes committing entry. Between
2288 // commit and getting update back, we delete the entry.
2289 // We get the update for the entry, but the local one was modified
2290 // so we store the entry but don't apply it. IS_UNAPPLIED_UPDATE is set.
2291 // We commit deletion and get a new version number.
2292 // We apply unapplied updates again before we get the update about the deletion.
2293 // This means we have an unapplied update where server_version < base_version.
2294 TEST_F(SyncerTest, UnappliedUpdateDuringCommit) {
2295 // This test is a little fake.
2297 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2298 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "bob");
2299 entry.PutId(ids_.FromNumber(20));
2300 entry.PutBaseVersion(1);
2301 entry.PutServerVersion(1);
2302 entry.PutServerParentId(ids_.FromNumber(9999)); // Bad parent.
2303 entry.PutIsUnsynced(true);
2304 entry.PutIsUnappliedUpdate(true);
2305 entry.PutSpecifics(DefaultBookmarkSpecifics());
2306 entry.PutServerSpecifics(DefaultBookmarkSpecifics());
2307 entry.PutIsDel(false);
2310 EXPECT_EQ(1, session_->status_controller().TotalNumConflictingItems());
2311 saw_syncer_event_ = false;
2314 // Original problem synopsis:
2316 // Unexpected error during sync if we:
2317 // make a new folder bob
2319 // make a new folder fred
2320 // move bob into fred
2323 // if no syncing occured midway, bob will have an illegal parent
2324 TEST_F(SyncerTest, DeletingEntryInFolder) {
2325 // This test is a little fake.
2326 int64 existing_metahandle;
2328 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2329 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "existing");
2330 ASSERT_TRUE(entry.good());
2331 entry.PutIsDir(true);
2332 entry.PutSpecifics(DefaultBookmarkSpecifics());
2333 entry.PutIsUnsynced(true);
2334 existing_metahandle = entry.GetMetahandle();
2338 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2339 MutableEntry newfolder(&trans, CREATE, BOOKMARKS, trans.root_id(), "new");
2340 ASSERT_TRUE(newfolder.good());
2341 newfolder.PutIsDir(true);
2342 newfolder.PutSpecifics(DefaultBookmarkSpecifics());
2343 newfolder.PutIsUnsynced(true);
2345 MutableEntry existing(&trans, GET_BY_HANDLE, existing_metahandle);
2346 ASSERT_TRUE(existing.good());
2347 existing.PutParentId(newfolder.GetId());
2348 existing.PutIsUnsynced(true);
2349 EXPECT_TRUE(existing.GetId().ServerKnows());
2351 newfolder.PutIsDel(true);
2352 existing.PutIsDel(true);
2355 EXPECT_EQ(0, status().num_server_conflicts());
2358 TEST_F(SyncerTest, DeletingEntryWithLocalEdits) {
2359 int64 newfolder_metahandle;
2361 mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10,
2362 foreign_cache_guid(), "-1");
2365 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2366 MutableEntry newfolder(
2367 &trans, CREATE, BOOKMARKS, ids_.FromNumber(1), "local");
2368 ASSERT_TRUE(newfolder.good());
2369 newfolder.PutIsUnsynced(true);
2370 newfolder.PutIsDir(true);
2371 newfolder.PutSpecifics(DefaultBookmarkSpecifics());
2372 newfolder_metahandle = newfolder.GetMetahandle();
2374 mock_server_->AddUpdateDirectory(1, 0, "bob", 2, 20,
2375 foreign_cache_guid(), "-1");
2376 mock_server_->SetLastUpdateDeleted();
2377 SyncShareConfigure();
2379 syncable::ReadTransaction trans(FROM_HERE, directory());
2380 Entry entry(&trans, syncable::GET_BY_HANDLE, newfolder_metahandle);
2381 ASSERT_TRUE(entry.good());
2385 TEST_F(SyncerTest, FolderSwapUpdate) {
2386 mock_server_->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2387 foreign_cache_guid(), "-7801");
2388 mock_server_->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2389 foreign_cache_guid(), "-1024");
2391 mock_server_->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2392 foreign_cache_guid(), "-1024");
2393 mock_server_->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2394 foreign_cache_guid(), "-7801");
2397 syncable::ReadTransaction trans(FROM_HERE, directory());
2398 Entry id1(&trans, GET_BY_ID, ids_.FromNumber(7801));
2399 ASSERT_TRUE(id1.good());
2400 EXPECT_TRUE("fred" == id1.GetNonUniqueName());
2401 EXPECT_TRUE(root_id_ == id1.GetParentId());
2402 Entry id2(&trans, GET_BY_ID, ids_.FromNumber(1024));
2403 ASSERT_TRUE(id2.good());
2404 EXPECT_TRUE("bob" == id2.GetNonUniqueName());
2405 EXPECT_TRUE(root_id_ == id2.GetParentId());
2407 saw_syncer_event_ = false;
2410 TEST_F(SyncerTest, NameCollidingFolderSwapWorksFine) {
2411 mock_server_->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2412 foreign_cache_guid(), "-7801");
2413 mock_server_->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2414 foreign_cache_guid(), "-1024");
2415 mock_server_->AddUpdateDirectory(4096, 0, "alice", 1, 10,
2416 foreign_cache_guid(), "-4096");
2419 syncable::ReadTransaction trans(FROM_HERE, directory());
2420 Entry id1(&trans, GET_BY_ID, ids_.FromNumber(7801));
2421 ASSERT_TRUE(id1.good());
2422 EXPECT_TRUE("bob" == id1.GetNonUniqueName());
2423 EXPECT_TRUE(root_id_ == id1.GetParentId());
2424 Entry id2(&trans, GET_BY_ID, ids_.FromNumber(1024));
2425 ASSERT_TRUE(id2.good());
2426 EXPECT_TRUE("fred" == id2.GetNonUniqueName());
2427 EXPECT_TRUE(root_id_ == id2.GetParentId());
2428 Entry id3(&trans, GET_BY_ID, ids_.FromNumber(4096));
2429 ASSERT_TRUE(id3.good());
2430 EXPECT_TRUE("alice" == id3.GetNonUniqueName());
2431 EXPECT_TRUE(root_id_ == id3.GetParentId());
2433 mock_server_->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2434 foreign_cache_guid(), "-1024");
2435 mock_server_->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2436 foreign_cache_guid(), "-7801");
2437 mock_server_->AddUpdateDirectory(4096, 0, "bob", 2, 20,
2438 foreign_cache_guid(), "-4096");
2441 syncable::ReadTransaction trans(FROM_HERE, directory());
2442 Entry id1(&trans, GET_BY_ID, ids_.FromNumber(7801));
2443 ASSERT_TRUE(id1.good());
2444 EXPECT_TRUE("fred" == id1.GetNonUniqueName());
2445 EXPECT_TRUE(root_id_ == id1.GetParentId());
2446 Entry id2(&trans, GET_BY_ID, ids_.FromNumber(1024));
2447 ASSERT_TRUE(id2.good());
2448 EXPECT_TRUE("bob" == id2.GetNonUniqueName());
2449 EXPECT_TRUE(root_id_ == id2.GetParentId());
2450 Entry id3(&trans, GET_BY_ID, ids_.FromNumber(4096));
2451 ASSERT_TRUE(id3.good());
2452 EXPECT_TRUE("bob" == id3.GetNonUniqueName());
2453 EXPECT_TRUE(root_id_ == id3.GetParentId());
2455 saw_syncer_event_ = false;
2458 // Committing more than kDefaultMaxCommitBatchSize items requires that
2459 // we post more than one commit command to the server. This test makes
2460 // sure that scenario works as expected.
2461 TEST_F(SyncerTest, CommitManyItemsInOneGo_Success) {
2462 uint32 num_batches = 3;
2463 uint32 items_to_commit = kDefaultMaxCommitBatchSize * num_batches;
2465 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2466 for (uint32 i = 0; i < items_to_commit; i++) {
2467 string nameutf8 = base::StringPrintf("%d", i);
2468 string name(nameutf8.begin(), nameutf8.end());
2469 MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
2470 e.PutIsUnsynced(true);
2472 e.PutSpecifics(DefaultBookmarkSpecifics());
2475 ASSERT_EQ(items_to_commit, directory()->unsynced_entity_count());
2478 EXPECT_EQ(num_batches, mock_server_->commit_messages().size());
2479 EXPECT_EQ(0, directory()->unsynced_entity_count());
2482 // Test that a single failure to contact the server will cause us to exit the
2483 // commit loop immediately.
2484 TEST_F(SyncerTest, CommitManyItemsInOneGo_PostBufferFail) {
2485 uint32 num_batches = 3;
2486 uint32 items_to_commit = kDefaultMaxCommitBatchSize * num_batches;
2488 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2489 for (uint32 i = 0; i < items_to_commit; i++) {
2490 string nameutf8 = base::StringPrintf("%d", i);
2491 string name(nameutf8.begin(), nameutf8.end());
2492 MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
2493 e.PutIsUnsynced(true);
2495 e.PutSpecifics(DefaultBookmarkSpecifics());
2498 ASSERT_EQ(items_to_commit, directory()->unsynced_entity_count());
2500 // The second commit should fail. It will be preceded by one successful
2501 // GetUpdate and one succesful commit.
2502 mock_server_->FailNthPostBufferToPathCall(3);
2505 EXPECT_EQ(1U, mock_server_->commit_messages().size());
2506 EXPECT_EQ(SYNC_SERVER_ERROR,
2507 session_->status_controller().model_neutral_state().commit_result);
2508 EXPECT_EQ(items_to_commit - kDefaultMaxCommitBatchSize,
2509 directory()->unsynced_entity_count());
2512 // Test that a single conflict response from the server will cause us to exit
2513 // the commit loop immediately.
2514 TEST_F(SyncerTest, CommitManyItemsInOneGo_CommitConflict) {
2515 uint32 num_batches = 2;
2516 uint32 items_to_commit = kDefaultMaxCommitBatchSize * num_batches;
2518 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2519 for (uint32 i = 0; i < items_to_commit; i++) {
2520 string nameutf8 = base::StringPrintf("%d", i);
2521 string name(nameutf8.begin(), nameutf8.end());
2522 MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
2523 e.PutIsUnsynced(true);
2525 e.PutSpecifics(DefaultBookmarkSpecifics());
2528 ASSERT_EQ(items_to_commit, directory()->unsynced_entity_count());
2530 // Return a CONFLICT response for the first item.
2531 mock_server_->set_conflict_n_commits(1);
2534 // We should stop looping at the first sign of trouble.
2535 EXPECT_EQ(1U, mock_server_->commit_messages().size());
2536 EXPECT_EQ(items_to_commit - (kDefaultMaxCommitBatchSize - 1),
2537 directory()->unsynced_entity_count());
2540 // Tests that sending debug info events works.
2541 TEST_F(SyncerTest, SendDebugInfoEventsOnGetUpdates_HappyCase) {
2542 debug_info_getter_->AddDebugEvent();
2543 debug_info_getter_->AddDebugEvent();
2547 // Verify we received one GetUpdates request with two debug info events.
2548 EXPECT_EQ(1U, mock_server_->requests().size());
2549 ASSERT_TRUE(mock_server_->last_request().has_get_updates());
2550 EXPECT_EQ(2, mock_server_->last_request().debug_info().events_size());
2554 // See that we received another GetUpdates request, but that it contains no
2555 // debug info events.
2556 EXPECT_EQ(2U, mock_server_->requests().size());
2557 ASSERT_TRUE(mock_server_->last_request().has_get_updates());
2558 EXPECT_EQ(0, mock_server_->last_request().debug_info().events_size());
2560 debug_info_getter_->AddDebugEvent();
2564 // See that we received another GetUpdates request and it contains one debug
2566 EXPECT_EQ(3U, mock_server_->requests().size());
2567 ASSERT_TRUE(mock_server_->last_request().has_get_updates());
2568 EXPECT_EQ(1, mock_server_->last_request().debug_info().events_size());
2571 // Tests that debug info events are dropped on server error.
2572 TEST_F(SyncerTest, SendDebugInfoEventsOnGetUpdates_PostFailsDontDrop) {
2573 debug_info_getter_->AddDebugEvent();
2574 debug_info_getter_->AddDebugEvent();
2576 mock_server_->FailNextPostBufferToPathCall();
2579 // Verify we attempted to send one GetUpdates request with two debug info
2581 EXPECT_EQ(1U, mock_server_->requests().size());
2582 ASSERT_TRUE(mock_server_->last_request().has_get_updates());
2583 EXPECT_EQ(2, mock_server_->last_request().debug_info().events_size());
2587 // See that the client resent the two debug info events.
2588 EXPECT_EQ(2U, mock_server_->requests().size());
2589 ASSERT_TRUE(mock_server_->last_request().has_get_updates());
2590 EXPECT_EQ(2, mock_server_->last_request().debug_info().events_size());
2592 // The previous send was successful so this next one shouldn't generate any
2593 // debug info events.
2595 EXPECT_EQ(3U, mock_server_->requests().size());
2596 ASSERT_TRUE(mock_server_->last_request().has_get_updates());
2597 EXPECT_EQ(0, mock_server_->last_request().debug_info().events_size());
2600 // Tests that sending debug info events on Commit works.
2601 TEST_F(SyncerTest, SendDebugInfoEventsOnCommit_HappyCase) {
2602 // Make sure GetUpdate isn't call as it would "steal" debug info events before
2603 // Commit has a chance to send them.
2604 ConfigureNoGetUpdatesRequired();
2606 // Generate a debug info event and trigger a commit.
2607 debug_info_getter_->AddDebugEvent();
2608 CreateUnsyncedDirectory("X", "id_X");
2611 // Verify that the last request received is a Commit and that it contains a
2612 // debug info event.
2613 EXPECT_EQ(1U, mock_server_->requests().size());
2614 ASSERT_TRUE(mock_server_->last_request().has_commit());
2615 EXPECT_EQ(1, mock_server_->last_request().debug_info().events_size());
2617 // Generate another commit, but no debug info event.
2618 CreateUnsyncedDirectory("Y", "id_Y");
2621 // See that it was received and contains no debug info events.
2622 EXPECT_EQ(2U, mock_server_->requests().size());
2623 ASSERT_TRUE(mock_server_->last_request().has_commit());
2624 EXPECT_EQ(0, mock_server_->last_request().debug_info().events_size());
2627 // Tests that debug info events are not dropped on server error.
2628 TEST_F(SyncerTest, SendDebugInfoEventsOnCommit_PostFailsDontDrop) {
2629 // Make sure GetUpdate isn't call as it would "steal" debug info events before
2630 // Commit has a chance to send them.
2631 ConfigureNoGetUpdatesRequired();
2633 mock_server_->FailNextPostBufferToPathCall();
2635 // Generate a debug info event and trigger a commit.
2636 debug_info_getter_->AddDebugEvent();
2637 CreateUnsyncedDirectory("X", "id_X");
2640 // Verify that the last request sent is a Commit and that it contains a debug
2642 EXPECT_EQ(1U, mock_server_->requests().size());
2643 ASSERT_TRUE(mock_server_->last_request().has_commit());
2644 EXPECT_EQ(1, mock_server_->last_request().debug_info().events_size());
2649 // Verify that we've received another Commit and that it contains a debug info
2650 // event (just like the previous one).
2651 EXPECT_EQ(2U, mock_server_->requests().size());
2652 ASSERT_TRUE(mock_server_->last_request().has_commit());
2653 EXPECT_EQ(1, mock_server_->last_request().debug_info().events_size());
2655 // Generate another commit and try again.
2656 CreateUnsyncedDirectory("Y", "id_Y");
2659 // See that it was received and contains no debug info events.
2660 EXPECT_EQ(3U, mock_server_->requests().size());
2661 ASSERT_TRUE(mock_server_->last_request().has_commit());
2662 EXPECT_EQ(0, mock_server_->last_request().debug_info().events_size());
2665 TEST_F(SyncerTest, HugeConflict) {
2666 int item_count = 300; // We should be able to do 300 or 3000 w/o issue.
2668 syncable::Id parent_id = ids_.NewServerId();
2669 syncable::Id last_id = parent_id;
2670 vector<syncable::Id> tree_ids;
2672 // Create a lot of updates for which the parent does not exist yet.
2673 // Generate a huge deep tree which should all fail to apply at first.
2675 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2676 for (int i = 0; i < item_count ; i++) {
2677 syncable::Id next_id = ids_.NewServerId();
2678 syncable::Id local_id = ids_.NewLocalId();
2679 tree_ids.push_back(next_id);
2680 mock_server_->AddUpdateDirectory(next_id, last_id, "BOB", 2, 20,
2681 foreign_cache_guid(),
2682 local_id.GetServerId());
2688 // Check they're in the expected conflict state.
2690 syncable::ReadTransaction trans(FROM_HERE, directory());
2691 for (int i = 0; i < item_count; i++) {
2692 Entry e(&trans, GET_BY_ID, tree_ids[i]);
2693 // They should all exist but none should be applied.
2694 ASSERT_TRUE(e.good());
2695 EXPECT_TRUE(e.GetIsDel());
2696 EXPECT_TRUE(e.GetIsUnappliedUpdate());
2700 // Add the missing parent directory.
2701 mock_server_->AddUpdateDirectory(parent_id, TestIdFactory::root(),
2702 "BOB", 2, 20, foreign_cache_guid(), "-3500");
2705 // Now they should all be OK.
2707 syncable::ReadTransaction trans(FROM_HERE, directory());
2708 for (int i = 0; i < item_count; i++) {
2709 Entry e(&trans, GET_BY_ID, tree_ids[i]);
2710 ASSERT_TRUE(e.good());
2711 EXPECT_FALSE(e.GetIsDel());
2712 EXPECT_FALSE(e.GetIsUnappliedUpdate());
2717 TEST_F(SyncerTest, DontCrashOnCaseChange) {
2718 mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10,
2719 foreign_cache_guid(), "-1");
2722 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2723 MutableEntry e(&trans, GET_BY_ID, ids_.FromNumber(1));
2724 ASSERT_TRUE(e.good());
2725 e.PutIsUnsynced(true);
2727 mock_server_->set_conflict_all_commits(true);
2728 mock_server_->AddUpdateDirectory(1, 0, "BOB", 2, 20,
2729 foreign_cache_guid(), "-1");
2730 SyncShareNudge(); // USED TO CAUSE AN ASSERT
2731 saw_syncer_event_ = false;
2734 TEST_F(SyncerTest, UnsyncedItemAndUpdate) {
2735 mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10,
2736 foreign_cache_guid(), "-1");
2738 mock_server_->set_conflict_all_commits(true);
2739 mock_server_->AddUpdateDirectory(2, 0, "bob", 2, 20,
2740 foreign_cache_guid(), "-2");
2741 SyncShareNudge(); // USED TO CAUSE AN ASSERT
2742 saw_syncer_event_ = false;
2745 TEST_F(SyncerTest, NewEntryAndAlteredServerEntrySharePath) {
2746 mock_server_->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
2747 foreign_cache_guid(), "-1");
2749 int64 local_folder_handle;
2750 syncable::Id local_folder_id;
2752 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2753 MutableEntry new_entry(
2754 &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Bar.htm");
2755 ASSERT_TRUE(new_entry.good());
2756 local_folder_id = new_entry.GetId();
2757 local_folder_handle = new_entry.GetMetahandle();
2758 new_entry.PutIsUnsynced(true);
2759 new_entry.PutSpecifics(DefaultBookmarkSpecifics());
2760 MutableEntry old(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2761 ASSERT_TRUE(old.good());
2762 WriteTestDataToEntry(&wtrans, &old);
2764 mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
2765 foreign_cache_guid(), "-1");
2766 mock_server_->set_conflict_all_commits(true);
2768 saw_syncer_event_ = false;
2770 // Update #20 should have been dropped in favor of the local version.
2771 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2772 MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2773 MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle);
2774 ASSERT_TRUE(server.good());
2775 ASSERT_TRUE(local.good());
2776 EXPECT_TRUE(local.GetMetahandle()!= server.GetMetahandle());
2777 EXPECT_FALSE(server.GetIsUnappliedUpdate());
2778 EXPECT_FALSE(local.GetIsUnappliedUpdate());
2779 EXPECT_TRUE(server.GetIsUnsynced());
2780 EXPECT_TRUE(local.GetIsUnsynced());
2781 EXPECT_EQ("Foo.htm", server.GetNonUniqueName());
2782 EXPECT_EQ("Bar.htm", local.GetNonUniqueName());
2784 // Allow local changes to commit.
2785 mock_server_->set_conflict_all_commits(false);
2787 saw_syncer_event_ = false;
2789 // Now add a server change to make the two names equal. There should
2790 // be no conflict with that, since names are not unique.
2791 mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
2792 foreign_cache_guid(), "-1");
2794 saw_syncer_event_ = false;
2796 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2797 MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2798 MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle);
2799 ASSERT_TRUE(server.good());
2800 ASSERT_TRUE(local.good());
2801 EXPECT_TRUE(local.GetMetahandle()!= server.GetMetahandle());
2802 EXPECT_FALSE(server.GetIsUnappliedUpdate());
2803 EXPECT_FALSE(local.GetIsUnappliedUpdate());
2804 EXPECT_FALSE(server.GetIsUnsynced());
2805 EXPECT_FALSE(local.GetIsUnsynced());
2806 EXPECT_EQ("Bar.htm", server.GetNonUniqueName());
2807 EXPECT_EQ("Bar.htm", local.GetNonUniqueName());
2808 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
2809 server.GetSpecifics().bookmark().url());
2813 // Same as NewEntryAnddServerEntrySharePath, but using the old-style protocol.
2814 TEST_F(SyncerTest, NewEntryAndAlteredServerEntrySharePath_OldBookmarksProto) {
2815 mock_server_->set_use_legacy_bookmarks_protocol(true);
2816 mock_server_->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
2817 foreign_cache_guid(), "-1");
2819 int64 local_folder_handle;
2820 syncable::Id local_folder_id;
2822 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2823 MutableEntry new_entry(
2824 &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Bar.htm");
2825 ASSERT_TRUE(new_entry.good());
2826 local_folder_id = new_entry.GetId();
2827 local_folder_handle = new_entry.GetMetahandle();
2828 new_entry.PutIsUnsynced(true);
2829 new_entry.PutSpecifics(DefaultBookmarkSpecifics());
2830 MutableEntry old(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2831 ASSERT_TRUE(old.good());
2832 WriteTestDataToEntry(&wtrans, &old);
2834 mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
2835 foreign_cache_guid(), "-1");
2836 mock_server_->set_conflict_all_commits(true);
2838 saw_syncer_event_ = false;
2840 // Update #20 should have been dropped in favor of the local version.
2841 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2842 MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2843 MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle);
2844 ASSERT_TRUE(server.good());
2845 ASSERT_TRUE(local.good());
2846 EXPECT_TRUE(local.GetMetahandle()!= server.GetMetahandle());
2847 EXPECT_FALSE(server.GetIsUnappliedUpdate());
2848 EXPECT_FALSE(local.GetIsUnappliedUpdate());
2849 EXPECT_TRUE(server.GetIsUnsynced());
2850 EXPECT_TRUE(local.GetIsUnsynced());
2851 EXPECT_EQ("Foo.htm", server.GetNonUniqueName());
2852 EXPECT_EQ("Bar.htm", local.GetNonUniqueName());
2854 // Allow local changes to commit.
2855 mock_server_->set_conflict_all_commits(false);
2857 saw_syncer_event_ = false;
2859 // Now add a server change to make the two names equal. There should
2860 // be no conflict with that, since names are not unique.
2861 mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
2862 foreign_cache_guid(), "-1");
2864 saw_syncer_event_ = false;
2866 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2867 MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2868 MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle);
2869 ASSERT_TRUE(server.good());
2870 ASSERT_TRUE(local.good());
2871 EXPECT_TRUE(local.GetMetahandle()!= server.GetMetahandle());
2872 EXPECT_FALSE(server.GetIsUnappliedUpdate());
2873 EXPECT_FALSE(local.GetIsUnappliedUpdate());
2874 EXPECT_FALSE(server.GetIsUnsynced());
2875 EXPECT_FALSE(local.GetIsUnsynced());
2876 EXPECT_EQ("Bar.htm", server.GetNonUniqueName());
2877 EXPECT_EQ("Bar.htm", local.GetNonUniqueName());
2878 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
2879 server.GetSpecifics().bookmark().url());
2883 // Circular links should be resolved by the server.
2884 TEST_F(SyncerTest, SiblingDirectoriesBecomeCircular) {
2885 // we don't currently resolve this. This test ensures we don't.
2886 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
2887 foreign_cache_guid(), "-1");
2888 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10,
2889 foreign_cache_guid(), "-2");
2892 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2893 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2894 ASSERT_TRUE(A.good());
2895 A.PutIsUnsynced(true);
2896 A.PutParentId(ids_.FromNumber(2));
2897 A.PutNonUniqueName("B");
2899 mock_server_->AddUpdateDirectory(2, 1, "A", 20, 20,
2900 foreign_cache_guid(), "-2");
2901 mock_server_->set_conflict_all_commits(true);
2903 saw_syncer_event_ = false;
2905 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2906 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2907 ASSERT_TRUE(A.good());
2908 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
2909 ASSERT_TRUE(B.good());
2910 EXPECT_TRUE(A.GetNonUniqueName()== "B");
2911 EXPECT_TRUE(B.GetNonUniqueName()== "B");
2915 TEST_F(SyncerTest, SwapEntryNames) {
2916 // Simple transaction test.
2917 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
2918 foreign_cache_guid(), "-1");
2919 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10,
2920 foreign_cache_guid(), "-2");
2921 mock_server_->set_conflict_all_commits(true);
2924 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2925 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2926 ASSERT_TRUE(A.good());
2927 A.PutIsUnsynced(true);
2928 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
2929 ASSERT_TRUE(B.good());
2930 B.PutIsUnsynced(true);
2931 A.PutNonUniqueName("C");
2932 B.PutNonUniqueName("A");
2933 A.PutNonUniqueName("B");
2936 saw_syncer_event_ = false;
2939 TEST_F(SyncerTest, DualDeletionWithNewItemNameClash) {
2940 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
2941 foreign_cache_guid(), "-1");
2942 mock_server_->AddUpdateBookmark(2, 0, "B", 10, 10,
2943 foreign_cache_guid(), "-2");
2944 mock_server_->set_conflict_all_commits(true);
2947 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2948 MutableEntry B(&trans, GET_BY_ID, ids_.FromNumber(2));
2949 ASSERT_TRUE(B.good());
2950 WriteTestDataToEntry(&trans, &B);
2953 mock_server_->AddUpdateBookmark(2, 0, "A", 11, 11,
2954 foreign_cache_guid(), "-2");
2955 mock_server_->SetLastUpdateDeleted();
2958 syncable::ReadTransaction trans(FROM_HERE, directory());
2959 Entry B(&trans, GET_BY_ID, ids_.FromNumber(2));
2960 ASSERT_TRUE(B.good());
2961 EXPECT_FALSE(B.GetIsUnsynced());
2962 EXPECT_FALSE(B.GetIsUnappliedUpdate());
2964 saw_syncer_event_ = false;
2967 // When we undelete an entity as a result of conflict resolution, we reuse the
2968 // existing server id and preserve the old version, simply updating the server
2969 // version with the new non-deleted entity.
2970 TEST_F(SyncerTest, ResolveWeWroteTheyDeleted) {
2971 int64 bob_metahandle;
2973 mock_server_->AddUpdateBookmark(1, 0, "bob", 1, 10,
2974 foreign_cache_guid(), "-1");
2977 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2978 MutableEntry bob(&trans, GET_BY_ID, ids_.FromNumber(1));
2979 ASSERT_TRUE(bob.good());
2980 bob_metahandle = bob.GetMetahandle();
2981 WriteTestDataToEntry(&trans, &bob);
2983 mock_server_->AddUpdateBookmark(1, 0, "bob", 2, 10,
2984 foreign_cache_guid(), "-1");
2985 mock_server_->SetLastUpdateDeleted();
2986 mock_server_->set_conflict_all_commits(true);
2990 syncable::ReadTransaction trans(FROM_HERE, directory());
2991 Entry bob(&trans, GET_BY_HANDLE, bob_metahandle);
2992 ASSERT_TRUE(bob.good());
2993 EXPECT_TRUE(bob.GetIsUnsynced());
2994 EXPECT_TRUE(bob.GetId().ServerKnows());
2995 EXPECT_FALSE(bob.GetIsUnappliedUpdate());
2996 EXPECT_FALSE(bob.GetIsDel());
2997 EXPECT_EQ(2, bob.GetServerVersion());
2998 EXPECT_EQ(2, bob.GetBaseVersion());
3000 saw_syncer_event_ = false;
3003 // This test is to reproduce a check failure. Sometimes we would get a bad ID
3004 // back when creating an entry.
3005 TEST_F(SyncerTest, DuplicateIDReturn) {
3007 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3008 MutableEntry folder(&trans, CREATE, BOOKMARKS, trans.root_id(), "bob");
3009 ASSERT_TRUE(folder.good());
3010 folder.PutIsUnsynced(true);
3011 folder.PutIsDir(true);
3012 folder.PutSpecifics(DefaultBookmarkSpecifics());
3013 MutableEntry folder2(&trans, CREATE, BOOKMARKS, trans.root_id(), "fred");
3014 ASSERT_TRUE(folder2.good());
3015 folder2.PutIsUnsynced(false);
3016 folder2.PutIsDir(true);
3017 folder2.PutSpecifics(DefaultBookmarkSpecifics());
3018 folder2.PutBaseVersion(3);
3019 folder2.PutId(syncable::Id::CreateFromServerId("mock_server:10000"));
3021 mock_server_->set_next_new_id(10000);
3022 EXPECT_EQ(1u, directory()->unsynced_entity_count());
3023 // we get back a bad id in here (should never happen).
3025 EXPECT_EQ(1u, directory()->unsynced_entity_count());
3026 SyncShareNudge(); // another bad id in here.
3027 EXPECT_EQ(0u, directory()->unsynced_entity_count());
3028 saw_syncer_event_ = false;
3031 TEST_F(SyncerTest, DeletedEntryWithBadParentInLoopCalculation) {
3032 mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10,
3033 foreign_cache_guid(), "-1");
3036 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3037 MutableEntry bob(&trans, GET_BY_ID, ids_.FromNumber(1));
3038 ASSERT_TRUE(bob.good());
3039 // This is valid, because the parent could have gone away a long time ago.
3040 bob.PutParentId(ids_.FromNumber(54));
3042 bob.PutIsUnsynced(true);
3044 mock_server_->AddUpdateDirectory(2, 1, "fred", 1, 10,
3045 foreign_cache_guid(), "-2");
3050 TEST_F(SyncerTest, ConflictResolverMergesLocalDeleteAndServerUpdate) {
3051 syncable::Id local_id;
3053 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3055 MutableEntry local_deleted(
3056 &trans, CREATE, BOOKMARKS, trans.root_id(), "name");
3057 local_id = local_deleted.GetId();
3058 local_deleted.PutId(ids_.FromNumber(1));
3059 local_deleted.PutBaseVersion(1);
3060 local_deleted.PutIsDel(true);
3061 local_deleted.PutIsDir(false);
3062 local_deleted.PutIsUnsynced(true);
3063 local_deleted.PutSpecifics(DefaultBookmarkSpecifics());
3066 mock_server_->AddUpdateBookmark(ids_.FromNumber(1), root_id_, "name", 10, 10,
3068 local_id.GetServerId());
3070 // We don't care about actually committing, just the resolution.
3071 mock_server_->set_conflict_all_commits(true);
3075 syncable::ReadTransaction trans(FROM_HERE, directory());
3076 Entry local_deleted(&trans, GET_BY_ID, ids_.FromNumber(1));
3077 EXPECT_TRUE(local_deleted.GetBaseVersion()== 10);
3078 EXPECT_TRUE(local_deleted.GetIsUnappliedUpdate()== false);
3079 EXPECT_TRUE(local_deleted.GetIsUnsynced()== true);
3080 EXPECT_TRUE(local_deleted.GetIsDel()== true);
3081 EXPECT_TRUE(local_deleted.GetIsDir()== false);
3085 // See what happens if the IS_DIR bit gets flipped. This can cause us
3086 // all kinds of disasters.
3087 TEST_F(SyncerTest, UpdateFlipsTheFolderBit) {
3088 // Local object: a deleted directory (container), revision 1, unsynced.
3090 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3092 MutableEntry local_deleted(
3093 &trans, CREATE, BOOKMARKS, trans.root_id(), "name");
3094 local_deleted.PutId(ids_.FromNumber(1));
3095 local_deleted.PutBaseVersion(1);
3096 local_deleted.PutIsDel(true);
3097 local_deleted.PutIsDir(true);
3098 local_deleted.PutIsUnsynced(true);
3099 local_deleted.PutSpecifics(DefaultBookmarkSpecifics());
3102 // Server update: entry-type object (not a container), revision 10.
3103 mock_server_->AddUpdateBookmark(ids_.FromNumber(1), root_id_, "name", 10, 10,
3105 ids_.FromNumber(1).GetServerId());
3107 // Don't attempt to commit.
3108 mock_server_->set_conflict_all_commits(true);
3110 // The syncer should not attempt to apply the invalid update.
3114 syncable::ReadTransaction trans(FROM_HERE, directory());
3115 Entry local_deleted(&trans, GET_BY_ID, ids_.FromNumber(1));
3116 EXPECT_TRUE(local_deleted.GetBaseVersion()== 1);
3117 EXPECT_TRUE(local_deleted.GetIsUnappliedUpdate()== false);
3118 EXPECT_TRUE(local_deleted.GetIsUnsynced()== true);
3119 EXPECT_TRUE(local_deleted.GetIsDel()== true);
3120 EXPECT_TRUE(local_deleted.GetIsDir()== true);
3125 // Merge conflict resolution will merge a new local entry with another entry
3126 // that needs updates, resulting in CHECK.
3127 TEST_F(SyncerTest, MergingExistingItems) {
3128 mock_server_->set_conflict_all_commits(true);
3129 mock_server_->AddUpdateBookmark(1, 0, "base", 10, 10,
3130 local_cache_guid(), "-1");
3133 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3135 &trans, CREATE, BOOKMARKS, trans.root_id(), "Copy of base");
3136 WriteTestDataToEntry(&trans, &entry);
3138 mock_server_->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
3139 local_cache_guid(), "-1");
3143 // In this test a long changelog contains a child at the start of the changelog
3144 // and a parent at the end. While these updates are in progress the client would
3146 TEST_F(SyncerTest, LongChangelistWithApplicationConflict) {
3147 const int depth = 400;
3148 syncable::Id folder_id = ids_.FromNumber(1);
3150 // First we an item in a folder in the root. However the folder won't come
3152 syncable::Id stuck_entry_id = TestIdFactory::FromNumber(99999);
3153 mock_server_->AddUpdateDirectory(stuck_entry_id,
3154 folder_id, "stuck", 1, 1,
3155 foreign_cache_guid(), "-99999");
3156 mock_server_->SetChangesRemaining(depth - 1);
3159 // Buffer up a very long series of downloads.
3160 // We should never be stuck (conflict resolution shouldn't
3161 // kick in so long as we're making forward progress).
3162 for (int i = 0; i < depth; i++) {
3163 mock_server_->NextUpdateBatch();
3164 mock_server_->SetNewTimestamp(i + 1);
3165 mock_server_->SetChangesRemaining(depth - i);
3170 // Ensure our folder hasn't somehow applied.
3172 syncable::ReadTransaction trans(FROM_HERE, directory());
3173 Entry child(&trans, GET_BY_ID, stuck_entry_id);
3174 EXPECT_TRUE(child.good());
3175 EXPECT_TRUE(child.GetIsUnappliedUpdate());
3176 EXPECT_TRUE(child.GetIsDel());
3177 EXPECT_FALSE(child.GetIsUnsynced());
3180 // And finally the folder.
3181 mock_server_->AddUpdateDirectory(folder_id,
3182 TestIdFactory::root(), "folder", 1, 1,
3183 foreign_cache_guid(), "-1");
3184 mock_server_->SetChangesRemaining(0);
3187 // Check that everything is as expected after the commit.
3189 syncable::ReadTransaction trans(FROM_HERE, directory());
3190 Entry entry(&trans, GET_BY_ID, folder_id);
3191 ASSERT_TRUE(entry.good());
3192 Entry child(&trans, GET_BY_ID, stuck_entry_id);
3193 EXPECT_EQ(entry.GetId(), child.GetParentId());
3194 EXPECT_EQ("stuck", child.GetNonUniqueName());
3195 EXPECT_TRUE(child.good());
3199 TEST_F(SyncerTest, DontMergeTwoExistingItems) {
3200 mock_server_->set_conflict_all_commits(true);
3201 mock_server_->AddUpdateBookmark(1, 0, "base", 10, 10,
3202 foreign_cache_guid(), "-1");
3203 mock_server_->AddUpdateBookmark(2, 0, "base2", 10, 10,
3204 foreign_cache_guid(), "-2");
3207 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3208 MutableEntry entry(&trans, GET_BY_ID, ids_.FromNumber(2));
3209 ASSERT_TRUE(entry.good());
3210 entry.PutNonUniqueName("Copy of base");
3211 entry.PutIsUnsynced(true);
3213 mock_server_->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
3214 foreign_cache_guid(), "-1");
3217 syncable::ReadTransaction trans(FROM_HERE, directory());
3218 Entry entry1(&trans, GET_BY_ID, ids_.FromNumber(1));
3219 EXPECT_FALSE(entry1.GetIsUnappliedUpdate());
3220 EXPECT_FALSE(entry1.GetIsUnsynced());
3221 EXPECT_FALSE(entry1.GetIsDel());
3222 Entry entry2(&trans, GET_BY_ID, ids_.FromNumber(2));
3223 EXPECT_FALSE(entry2.GetIsUnappliedUpdate());
3224 EXPECT_TRUE(entry2.GetIsUnsynced());
3225 EXPECT_FALSE(entry2.GetIsDel());
3226 EXPECT_EQ(entry1.GetNonUniqueName(), entry2.GetNonUniqueName());
3230 TEST_F(SyncerTest, TestUndeleteUpdate) {
3231 mock_server_->set_conflict_all_commits(true);
3232 mock_server_->AddUpdateDirectory(1, 0, "foo", 1, 1,
3233 foreign_cache_guid(), "-1");
3234 mock_server_->AddUpdateDirectory(2, 1, "bar", 1, 2,
3235 foreign_cache_guid(), "-2");
3237 mock_server_->AddUpdateDirectory(2, 1, "bar", 2, 3,
3238 foreign_cache_guid(), "-2");
3239 mock_server_->SetLastUpdateDeleted();
3244 syncable::ReadTransaction trans(FROM_HERE, directory());
3245 Entry entry(&trans, GET_BY_ID, ids_.FromNumber(2));
3246 ASSERT_TRUE(entry.good());
3247 EXPECT_TRUE(entry.GetIsDel());
3248 metahandle = entry.GetMetahandle();
3250 mock_server_->AddUpdateDirectory(1, 0, "foo", 2, 4,
3251 foreign_cache_guid(), "-1");
3252 mock_server_->SetLastUpdateDeleted();
3254 // This used to be rejected as it's an undeletion. Now, it results in moving
3255 // the delete path aside.
3256 mock_server_->AddUpdateDirectory(2, 1, "bar", 3, 5,
3257 foreign_cache_guid(), "-2");
3260 syncable::ReadTransaction trans(FROM_HERE, directory());
3261 Entry entry(&trans, GET_BY_ID, ids_.FromNumber(2));
3262 ASSERT_TRUE(entry.good());
3263 EXPECT_TRUE(entry.GetIsDel());
3264 EXPECT_FALSE(entry.GetServerIsDel());
3265 EXPECT_TRUE(entry.GetIsUnappliedUpdate());
3266 EXPECT_NE(entry.GetMetahandle(), metahandle);
3270 TEST_F(SyncerTest, TestMoveSanitizedNamedFolder) {
3271 mock_server_->AddUpdateDirectory(1, 0, "foo", 1, 1,
3272 foreign_cache_guid(), "-1");
3273 mock_server_->AddUpdateDirectory(2, 0, ":::", 1, 2,
3274 foreign_cache_guid(), "-2");
3277 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3278 MutableEntry entry(&trans, GET_BY_ID, ids_.FromNumber(2));
3279 ASSERT_TRUE(entry.good());
3280 entry.PutParentId(ids_.FromNumber(1));
3281 EXPECT_TRUE(entry.PutIsUnsynced(true));
3284 // We use the same sync ts as before so our times match up.
3285 mock_server_->AddUpdateDirectory(2, 1, ":::", 2, 2,
3286 foreign_cache_guid(), "-2");
3290 // Don't crash when this occurs.
3291 TEST_F(SyncerTest, UpdateWhereParentIsNotAFolder) {
3292 mock_server_->AddUpdateBookmark(1, 0, "B", 10, 10,
3293 foreign_cache_guid(), "-1");
3294 mock_server_->AddUpdateDirectory(2, 1, "BookmarkParent", 10, 10,
3295 foreign_cache_guid(), "-2");
3296 // Used to cause a CHECK
3299 syncable::ReadTransaction rtrans(FROM_HERE, directory());
3300 Entry good_entry(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1));
3301 ASSERT_TRUE(good_entry.good());
3302 EXPECT_FALSE(good_entry.GetIsUnappliedUpdate());
3303 Entry bad_parent(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(2));
3304 ASSERT_TRUE(bad_parent.good());
3305 EXPECT_TRUE(bad_parent.GetIsUnappliedUpdate());
3309 TEST_F(SyncerTest, DirectoryUpdateTest) {
3310 Id in_root_id = ids_.NewServerId();
3311 Id in_in_root_id = ids_.NewServerId();
3313 mock_server_->AddUpdateDirectory(in_root_id, TestIdFactory::root(),
3314 "in_root_name", 2, 2,
3315 foreign_cache_guid(), "-1");
3316 mock_server_->AddUpdateDirectory(in_in_root_id, in_root_id,
3317 "in_in_root_name", 3, 3,
3318 foreign_cache_guid(), "-2");
3321 syncable::ReadTransaction trans(FROM_HERE, directory());
3322 Entry in_root(&trans, GET_BY_ID, in_root_id);
3323 ASSERT_TRUE(in_root.good());
3324 EXPECT_EQ("in_root_name", in_root.GetNonUniqueName());
3325 EXPECT_EQ(TestIdFactory::root(), in_root.GetParentId());
3327 Entry in_in_root(&trans, GET_BY_ID, in_in_root_id);
3328 ASSERT_TRUE(in_in_root.good());
3329 EXPECT_EQ("in_in_root_name", in_in_root.GetNonUniqueName());
3330 EXPECT_EQ(in_root_id, in_in_root.GetParentId());
3334 TEST_F(SyncerTest, DirectoryCommitTest) {
3335 syncable::Id in_root_id, in_dir_id;
3336 int64 foo_metahandle;
3337 int64 bar_metahandle;
3340 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3341 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, root_id_, "foo");
3342 ASSERT_TRUE(parent.good());
3343 parent.PutIsUnsynced(true);
3344 parent.PutIsDir(true);
3345 parent.PutSpecifics(DefaultBookmarkSpecifics());
3346 in_root_id = parent.GetId();
3347 foo_metahandle = parent.GetMetahandle();
3349 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent.GetId(), "bar");
3350 ASSERT_TRUE(child.good());
3351 child.PutIsUnsynced(true);
3352 child.PutIsDir(true);
3353 child.PutSpecifics(DefaultBookmarkSpecifics());
3354 bar_metahandle = child.GetMetahandle();
3355 in_dir_id = parent.GetId();
3359 syncable::ReadTransaction trans(FROM_HERE, directory());
3360 Entry fail_by_old_id_entry(&trans, GET_BY_ID, in_root_id);
3361 ASSERT_FALSE(fail_by_old_id_entry.good());
3363 Entry foo_entry(&trans, GET_BY_HANDLE, foo_metahandle);
3364 ASSERT_TRUE(foo_entry.good());
3365 EXPECT_EQ("foo", foo_entry.GetNonUniqueName());
3366 EXPECT_NE(foo_entry.GetId(), in_root_id);
3368 Entry bar_entry(&trans, GET_BY_HANDLE, bar_metahandle);
3369 ASSERT_TRUE(bar_entry.good());
3370 EXPECT_EQ("bar", bar_entry.GetNonUniqueName());
3371 EXPECT_NE(bar_entry.GetId(), in_dir_id);
3372 EXPECT_EQ(foo_entry.GetId(), bar_entry.GetParentId());
3376 TEST_F(SyncerTest, TestClientCommandDuringUpdate) {
3377 using sync_pb::ClientCommand;
3379 ClientCommand* command = new ClientCommand();
3380 command->set_set_sync_poll_interval(8);
3381 command->set_set_sync_long_poll_interval(800);
3382 command->set_sessions_commit_delay_seconds(3141);
3383 command->set_client_invalidation_hint_buffer_size(11);
3384 mock_server_->AddUpdateDirectory(1, 0, "in_root", 1, 1,
3385 foreign_cache_guid(), "-1");
3386 mock_server_->SetGUClientCommand(command);
3389 EXPECT_TRUE(TimeDelta::FromSeconds(8) ==
3390 last_short_poll_interval_received_);
3391 EXPECT_TRUE(TimeDelta::FromSeconds(800) ==
3392 last_long_poll_interval_received_);
3393 EXPECT_TRUE(TimeDelta::FromSeconds(3141) ==
3394 last_sessions_commit_delay_seconds_);
3395 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_);
3397 command = new ClientCommand();
3398 command->set_set_sync_poll_interval(180);
3399 command->set_set_sync_long_poll_interval(190);
3400 command->set_sessions_commit_delay_seconds(2718);
3401 command->set_client_invalidation_hint_buffer_size(9);
3402 mock_server_->AddUpdateDirectory(1, 0, "in_root", 1, 1,
3403 foreign_cache_guid(), "-1");
3404 mock_server_->SetGUClientCommand(command);
3407 EXPECT_TRUE(TimeDelta::FromSeconds(180) ==
3408 last_short_poll_interval_received_);
3409 EXPECT_TRUE(TimeDelta::FromSeconds(190) ==
3410 last_long_poll_interval_received_);
3411 EXPECT_TRUE(TimeDelta::FromSeconds(2718) ==
3412 last_sessions_commit_delay_seconds_);
3413 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_);
3416 TEST_F(SyncerTest, TestClientCommandDuringCommit) {
3417 using sync_pb::ClientCommand;
3419 ClientCommand* command = new ClientCommand();
3420 command->set_set_sync_poll_interval(8);
3421 command->set_set_sync_long_poll_interval(800);
3422 command->set_sessions_commit_delay_seconds(3141);
3423 command->set_client_invalidation_hint_buffer_size(11);
3424 CreateUnsyncedDirectory("X", "id_X");
3425 mock_server_->SetCommitClientCommand(command);
3428 EXPECT_TRUE(TimeDelta::FromSeconds(8) ==
3429 last_short_poll_interval_received_);
3430 EXPECT_TRUE(TimeDelta::FromSeconds(800) ==
3431 last_long_poll_interval_received_);
3432 EXPECT_TRUE(TimeDelta::FromSeconds(3141) ==
3433 last_sessions_commit_delay_seconds_);
3434 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_);
3436 command = new ClientCommand();
3437 command->set_set_sync_poll_interval(180);
3438 command->set_set_sync_long_poll_interval(190);
3439 command->set_sessions_commit_delay_seconds(2718);
3440 command->set_client_invalidation_hint_buffer_size(9);
3441 CreateUnsyncedDirectory("Y", "id_Y");
3442 mock_server_->SetCommitClientCommand(command);
3445 EXPECT_TRUE(TimeDelta::FromSeconds(180) ==
3446 last_short_poll_interval_received_);
3447 EXPECT_TRUE(TimeDelta::FromSeconds(190) ==
3448 last_long_poll_interval_received_);
3449 EXPECT_TRUE(TimeDelta::FromSeconds(2718) ==
3450 last_sessions_commit_delay_seconds_);
3451 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_);
3454 TEST_F(SyncerTest, EnsureWeSendUpOldParent) {
3455 syncable::Id folder_one_id = ids_.FromNumber(1);
3456 syncable::Id folder_two_id = ids_.FromNumber(2);
3458 mock_server_->AddUpdateDirectory(folder_one_id, TestIdFactory::root(),
3459 "folder_one", 1, 1, foreign_cache_guid(), "-1");
3460 mock_server_->AddUpdateDirectory(folder_two_id, TestIdFactory::root(),
3461 "folder_two", 1, 1, foreign_cache_guid(), "-2");
3464 // A moved entry should send an "old parent."
3465 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3466 MutableEntry entry(&trans, GET_BY_ID, folder_one_id);
3467 ASSERT_TRUE(entry.good());
3468 entry.PutParentId(folder_two_id);
3469 entry.PutIsUnsynced(true);
3470 // A new entry should send no "old parent."
3471 MutableEntry create(
3472 &trans, CREATE, BOOKMARKS, trans.root_id(), "new_folder");
3473 create.PutIsUnsynced(true);
3474 create.PutSpecifics(DefaultBookmarkSpecifics());
3477 const sync_pb::CommitMessage& commit = mock_server_->last_sent_commit();
3478 ASSERT_EQ(2, commit.entries_size());
3479 EXPECT_TRUE(commit.entries(0).parent_id_string() == "2");
3480 EXPECT_TRUE(commit.entries(0).old_parent_id() == "0");
3481 EXPECT_FALSE(commit.entries(1).has_old_parent_id());
3484 TEST_F(SyncerTest, Test64BitVersionSupport) {
3485 int64 really_big_int = std::numeric_limits<int64>::max() - 12;
3486 const string name("ringo's dang orang ran rings around my o-ring");
3487 int64 item_metahandle;
3489 // Try writing max int64 to the version fields of a meta entry.
3491 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3492 MutableEntry entry(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), name);
3493 ASSERT_TRUE(entry.good());
3494 entry.PutBaseVersion(really_big_int);
3495 entry.PutServerVersion(really_big_int);
3496 entry.PutId(ids_.NewServerId());
3497 item_metahandle = entry.GetMetahandle();
3499 // Now read it back out and make sure the value is max int64.
3500 syncable::ReadTransaction rtrans(FROM_HERE, directory());
3501 Entry entry(&rtrans, syncable::GET_BY_HANDLE, item_metahandle);
3502 ASSERT_TRUE(entry.good());
3503 EXPECT_TRUE(really_big_int == entry.GetBaseVersion());
3506 TEST_F(SyncerTest, TestSimpleUndelete) {
3507 Id id = ids_.MakeServer("undeletion item"), root = TestIdFactory::root();
3508 mock_server_->set_conflict_all_commits(true);
3509 // Let there be an entry from the server.
3510 mock_server_->AddUpdateBookmark(id, root, "foo", 1, 10,
3511 foreign_cache_guid(), "-1");
3513 // Check it out and delete it.
3515 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3516 MutableEntry entry(&wtrans, GET_BY_ID, id);
3517 ASSERT_TRUE(entry.good());
3518 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3519 EXPECT_FALSE(entry.GetIsUnsynced());
3520 EXPECT_FALSE(entry.GetIsDel());
3521 // Delete it locally.
3522 entry.PutIsDel(true);
3525 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3527 syncable::ReadTransaction trans(FROM_HERE, directory());
3528 Entry entry(&trans, GET_BY_ID, id);
3529 ASSERT_TRUE(entry.good());
3530 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3531 EXPECT_FALSE(entry.GetIsUnsynced());
3532 EXPECT_TRUE(entry.GetIsDel());
3533 EXPECT_FALSE(entry.GetServerIsDel());
3536 // Update from server confirming deletion.
3537 mock_server_->AddUpdateBookmark(id, root, "foo", 2, 11,
3538 foreign_cache_guid(), "-1");
3539 mock_server_->SetLastUpdateDeleted();
3541 // IS_DEL AND SERVER_IS_DEL now both true.
3543 syncable::ReadTransaction trans(FROM_HERE, directory());
3544 Entry entry(&trans, GET_BY_ID, id);
3545 ASSERT_TRUE(entry.good());
3546 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3547 EXPECT_FALSE(entry.GetIsUnsynced());
3548 EXPECT_TRUE(entry.GetIsDel());
3549 EXPECT_TRUE(entry.GetServerIsDel());
3551 // Undelete from server.
3552 mock_server_->AddUpdateBookmark(id, root, "foo", 2, 12,
3553 foreign_cache_guid(), "-1");
3555 // IS_DEL and SERVER_IS_DEL now both false.
3557 syncable::ReadTransaction trans(FROM_HERE, directory());
3558 Entry entry(&trans, GET_BY_ID, id);
3559 ASSERT_TRUE(entry.good());
3560 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3561 EXPECT_FALSE(entry.GetIsUnsynced());
3562 EXPECT_FALSE(entry.GetIsDel());
3563 EXPECT_FALSE(entry.GetServerIsDel());
3567 TEST_F(SyncerTest, TestUndeleteWithMissingDeleteUpdate) {
3568 Id id = ids_.MakeServer("undeletion item"), root = TestIdFactory::root();
3569 // Let there be a entry, from the server.
3570 mock_server_->set_conflict_all_commits(true);
3571 mock_server_->AddUpdateBookmark(id, root, "foo", 1, 10,
3572 foreign_cache_guid(), "-1");
3574 // Check it out and delete it.
3576 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3577 MutableEntry entry(&wtrans, GET_BY_ID, id);
3578 ASSERT_TRUE(entry.good());
3579 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3580 EXPECT_FALSE(entry.GetIsUnsynced());
3581 EXPECT_FALSE(entry.GetIsDel());
3582 // Delete it locally.
3583 entry.PutIsDel(true);
3586 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3588 syncable::ReadTransaction trans(FROM_HERE, directory());
3589 Entry entry(&trans, GET_BY_ID, id);
3590 ASSERT_TRUE(entry.good());
3591 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3592 EXPECT_FALSE(entry.GetIsUnsynced());
3593 EXPECT_TRUE(entry.GetIsDel());
3594 EXPECT_FALSE(entry.GetServerIsDel());
3597 // Say we do not get an update from server confirming deletion. Undelete
3599 mock_server_->AddUpdateBookmark(id, root, "foo", 2, 12,
3600 foreign_cache_guid(), "-1");
3602 // IS_DEL and SERVER_IS_DEL now both false.
3604 syncable::ReadTransaction trans(FROM_HERE, directory());
3605 Entry entry(&trans, GET_BY_ID, id);
3606 ASSERT_TRUE(entry.good());
3607 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3608 EXPECT_FALSE(entry.GetIsUnsynced());
3609 EXPECT_FALSE(entry.GetIsDel());
3610 EXPECT_FALSE(entry.GetServerIsDel());
3614 TEST_F(SyncerTest, TestUndeleteIgnoreCorrectlyUnappliedUpdate) {
3615 Id id1 = ids_.MakeServer("first"), id2 = ids_.MakeServer("second");
3616 Id root = TestIdFactory::root();
3617 // Duplicate! expect path clashing!
3618 mock_server_->set_conflict_all_commits(true);
3619 mock_server_->AddUpdateBookmark(id1, root, "foo", 1, 10,
3620 foreign_cache_guid(), "-1");
3621 mock_server_->AddUpdateBookmark(id2, root, "foo", 1, 10,
3622 foreign_cache_guid(), "-2");
3624 mock_server_->AddUpdateBookmark(id2, root, "foo2", 2, 20,
3625 foreign_cache_guid(), "-2");
3626 SyncShareNudge(); // Now just don't explode.
3629 TEST_F(SyncerTest, ClientTagServerCreatedUpdatesWork) {
3630 mock_server_->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
3631 foreign_cache_guid(), "-1");
3632 mock_server_->SetLastUpdateClientTag("permfolder");
3637 syncable::ReadTransaction trans(FROM_HERE, directory());
3638 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
3639 ASSERT_TRUE(perm_folder.good());
3640 EXPECT_FALSE(perm_folder.GetIsDel());
3641 EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate());
3642 EXPECT_FALSE(perm_folder.GetIsUnsynced());
3643 EXPECT_EQ(perm_folder.GetUniqueClientTag(), "permfolder");
3644 EXPECT_EQ(perm_folder.GetNonUniqueName(), "permitem1");
3647 mock_server_->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
3648 foreign_cache_guid(), "-1");
3649 mock_server_->SetLastUpdateClientTag("permfolder");
3653 syncable::ReadTransaction trans(FROM_HERE, directory());
3655 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
3656 ASSERT_TRUE(perm_folder.good());
3657 EXPECT_FALSE(perm_folder.GetIsDel());
3658 EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate());
3659 EXPECT_FALSE(perm_folder.GetIsUnsynced());
3660 EXPECT_EQ(perm_folder.GetUniqueClientTag(), "permfolder");
3661 EXPECT_EQ(perm_folder.GetNonUniqueName(), "permitem_renamed");
3665 TEST_F(SyncerTest, ClientTagIllegalUpdateIgnored) {
3666 mock_server_->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
3667 foreign_cache_guid(), "-1");
3668 mock_server_->SetLastUpdateClientTag("permfolder");
3673 syncable::ReadTransaction trans(FROM_HERE, directory());
3674 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
3675 ASSERT_TRUE(perm_folder.good());
3676 EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate());
3677 EXPECT_FALSE(perm_folder.GetIsUnsynced());
3678 EXPECT_EQ(perm_folder.GetUniqueClientTag(), "permfolder");
3679 EXPECT_TRUE(perm_folder.GetNonUniqueName()== "permitem1");
3680 EXPECT_TRUE(perm_folder.GetId().ServerKnows());
3683 mock_server_->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
3684 foreign_cache_guid(), "-1");
3685 mock_server_->SetLastUpdateClientTag("wrongtag");
3689 syncable::ReadTransaction trans(FROM_HERE, directory());
3691 // This update is rejected because it has the same ID, but a
3692 // different tag than one that is already on the client.
3693 // The client has a ServerKnows ID, which cannot be overwritten.
3694 Entry rejected_update(&trans, GET_BY_CLIENT_TAG, "wrongtag");
3695 EXPECT_FALSE(rejected_update.good());
3697 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
3698 ASSERT_TRUE(perm_folder.good());
3699 EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate());
3700 EXPECT_FALSE(perm_folder.GetIsUnsynced());
3701 EXPECT_EQ(perm_folder.GetNonUniqueName(), "permitem1");
3705 TEST_F(SyncerTest, ClientTagUncommittedTagMatchesUpdate) {
3706 int64 original_metahandle = 0;
3709 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3711 &trans, CREATE, PREFERENCES, ids_.root(), "name");
3712 ASSERT_TRUE(pref.good());
3713 pref.PutUniqueClientTag("tag");
3714 pref.PutIsUnsynced(true);
3715 EXPECT_FALSE(pref.GetIsUnappliedUpdate());
3716 EXPECT_FALSE(pref.GetId().ServerKnows());
3717 original_metahandle = pref.GetMetahandle();
3720 syncable::Id server_id = TestIdFactory::MakeServer("id");
3721 mock_server_->AddUpdatePref(server_id.GetServerId(),
3722 ids_.root().GetServerId(),
3724 mock_server_->set_conflict_all_commits(true);
3727 // This should cause client tag reunion, preserving the metahandle.
3729 syncable::ReadTransaction trans(FROM_HERE, directory());
3731 Entry pref(&trans, GET_BY_CLIENT_TAG, "tag");
3732 ASSERT_TRUE(pref.good());
3733 EXPECT_FALSE(pref.GetIsDel());
3734 EXPECT_FALSE(pref.GetIsUnappliedUpdate());
3735 EXPECT_TRUE(pref.GetIsUnsynced());
3736 EXPECT_EQ(10, pref.GetBaseVersion());
3737 // Entry should have been given the new ID while preserving the
3738 // metahandle; client should have won the conflict resolution.
3739 EXPECT_EQ(original_metahandle, pref.GetMetahandle());
3740 EXPECT_EQ("tag", pref.GetUniqueClientTag());
3741 EXPECT_TRUE(pref.GetId().ServerKnows());
3744 mock_server_->set_conflict_all_commits(false);
3747 // The resolved entry ought to commit cleanly.
3749 syncable::ReadTransaction trans(FROM_HERE, directory());
3751 Entry pref(&trans, GET_BY_CLIENT_TAG, "tag");
3752 ASSERT_TRUE(pref.good());
3753 EXPECT_FALSE(pref.GetIsDel());
3754 EXPECT_FALSE(pref.GetIsUnappliedUpdate());
3755 EXPECT_FALSE(pref.GetIsUnsynced());
3756 EXPECT_TRUE(10 < pref.GetBaseVersion());
3757 // Entry should have been given the new ID while preserving the
3758 // metahandle; client should have won the conflict resolution.
3759 EXPECT_EQ(original_metahandle, pref.GetMetahandle());
3760 EXPECT_EQ("tag", pref.GetUniqueClientTag());
3761 EXPECT_TRUE(pref.GetId().ServerKnows());
3765 TEST_F(SyncerTest, ClientTagConflictWithDeletedLocalEntry) {
3767 // Create a deleted local entry with a unique client tag.
3768 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3770 &trans, CREATE, PREFERENCES, ids_.root(), "name");
3771 ASSERT_TRUE(pref.good());
3772 ASSERT_FALSE(pref.GetId().ServerKnows());
3773 pref.PutUniqueClientTag("tag");
3774 pref.PutIsUnsynced(true);
3776 // Note: IS_DEL && !ServerKnows() will clear the UNSYNCED bit.
3777 // (We never attempt to commit server-unknown deleted items, so this
3778 // helps us clean up those entries).
3779 pref.PutIsDel(true);
3782 // Prepare an update with the same unique client tag.
3783 syncable::Id server_id = TestIdFactory::MakeServer("id");
3784 mock_server_->AddUpdatePref(server_id.GetServerId(),
3785 ids_.root().GetServerId(),
3789 // The local entry will be overwritten.
3791 syncable::ReadTransaction trans(FROM_HERE, directory());
3793 Entry pref(&trans, GET_BY_CLIENT_TAG, "tag");
3794 ASSERT_TRUE(pref.good());
3795 ASSERT_TRUE(pref.GetId().ServerKnows());
3796 EXPECT_FALSE(pref.GetIsDel());
3797 EXPECT_FALSE(pref.GetIsUnappliedUpdate());
3798 EXPECT_FALSE(pref.GetIsUnsynced());
3799 EXPECT_EQ(pref.GetBaseVersion(), 10);
3800 EXPECT_EQ(pref.GetUniqueClientTag(), "tag");
3804 TEST_F(SyncerTest, ClientTagUpdateClashesWithLocalEntry) {
3805 // This test is written assuming that ID comparison
3806 // will work out in a particular way.
3807 EXPECT_TRUE(ids_.FromNumber(1) < ids_.FromNumber(2));
3808 EXPECT_TRUE(ids_.FromNumber(3) < ids_.FromNumber(4));
3810 syncable::Id id1 = TestIdFactory::MakeServer("1");
3811 mock_server_->AddUpdatePref(id1.GetServerId(), ids_.root().GetServerId(),
3814 syncable::Id id4 = TestIdFactory::MakeServer("4");
3815 mock_server_->AddUpdatePref(id4.GetServerId(), ids_.root().GetServerId(),
3818 mock_server_->set_conflict_all_commits(true);
3821 int64 tag1_metahandle = syncable::kInvalidMetaHandle;
3822 int64 tag2_metahandle = syncable::kInvalidMetaHandle;
3823 // This should cause client tag overwrite.
3825 syncable::ReadTransaction trans(FROM_HERE, directory());
3827 Entry tag1(&trans, GET_BY_CLIENT_TAG, "tag1");
3828 ASSERT_TRUE(tag1.good());
3829 ASSERT_TRUE(tag1.GetId().ServerKnows());
3830 ASSERT_TRUE(id1 == tag1.GetId());
3831 EXPECT_FALSE(tag1.GetIsDel());
3832 EXPECT_FALSE(tag1.GetIsUnappliedUpdate());
3833 EXPECT_FALSE(tag1.GetIsUnsynced());
3834 EXPECT_EQ(10, tag1.GetBaseVersion());
3835 EXPECT_EQ("tag1", tag1.GetUniqueClientTag());
3836 tag1_metahandle = tag1.GetMetahandle();
3838 Entry tag2(&trans, GET_BY_CLIENT_TAG, "tag2");
3839 ASSERT_TRUE(tag2.good());
3840 ASSERT_TRUE(tag2.GetId().ServerKnows());
3841 ASSERT_TRUE(id4 == tag2.GetId());
3842 EXPECT_FALSE(tag2.GetIsDel());
3843 EXPECT_FALSE(tag2.GetIsUnappliedUpdate());
3844 EXPECT_FALSE(tag2.GetIsUnsynced());
3845 EXPECT_EQ(11, tag2.GetBaseVersion());
3846 EXPECT_EQ("tag2", tag2.GetUniqueClientTag());
3847 tag2_metahandle = tag2.GetMetahandle();
3849 syncable::Directory::Metahandles children;
3850 directory()->GetChildHandlesById(&trans, trans.root_id(), &children);
3851 ASSERT_EQ(2U, children.size());
3854 syncable::Id id2 = TestIdFactory::MakeServer("2");
3855 mock_server_->AddUpdatePref(id2.GetServerId(), ids_.root().GetServerId(),
3857 syncable::Id id3 = TestIdFactory::MakeServer("3");
3858 mock_server_->AddUpdatePref(id3.GetServerId(), ids_.root().GetServerId(),
3863 syncable::ReadTransaction trans(FROM_HERE, directory());
3865 Entry tag1(&trans, GET_BY_CLIENT_TAG, "tag1");
3866 ASSERT_TRUE(tag1.good());
3867 ASSERT_TRUE(tag1.GetId().ServerKnows());
3868 ASSERT_EQ(id1, tag1.GetId())
3869 << "ID 1 should be kept, since it was less than ID 2.";
3870 EXPECT_FALSE(tag1.GetIsDel());
3871 EXPECT_FALSE(tag1.GetIsUnappliedUpdate());
3872 EXPECT_FALSE(tag1.GetIsUnsynced());
3873 EXPECT_EQ(10, tag1.GetBaseVersion());
3874 EXPECT_EQ("tag1", tag1.GetUniqueClientTag());
3875 EXPECT_EQ(tag1_metahandle, tag1.GetMetahandle());
3877 Entry tag2(&trans, GET_BY_CLIENT_TAG, "tag2");
3878 ASSERT_TRUE(tag2.good());
3879 ASSERT_TRUE(tag2.GetId().ServerKnows());
3880 ASSERT_EQ(id3, tag2.GetId())
3881 << "ID 3 should be kept, since it was less than ID 4.";
3882 EXPECT_FALSE(tag2.GetIsDel());
3883 EXPECT_FALSE(tag2.GetIsUnappliedUpdate());
3884 EXPECT_FALSE(tag2.GetIsUnsynced());
3885 EXPECT_EQ(13, tag2.GetBaseVersion());
3886 EXPECT_EQ("tag2", tag2.GetUniqueClientTag());
3887 EXPECT_EQ(tag2_metahandle, tag2.GetMetahandle());
3889 syncable::Directory::Metahandles children;
3890 directory()->GetChildHandlesById(&trans, trans.root_id(), &children);
3891 ASSERT_EQ(2U, children.size());
3895 TEST_F(SyncerTest, ClientTagClashWithinBatchOfUpdates) {
3896 // This test is written assuming that ID comparison
3897 // will work out in a particular way.
3898 EXPECT_TRUE(ids_.FromNumber(1) < ids_.FromNumber(4));
3899 EXPECT_TRUE(ids_.FromNumber(201) < ids_.FromNumber(205));
3901 // Least ID: winner.
3902 mock_server_->AddUpdatePref(ids_.FromNumber(1).GetServerId(),
3903 ids_.root().GetServerId(), "tag a", 1, 10);
3904 mock_server_->AddUpdatePref(ids_.FromNumber(2).GetServerId(),
3905 ids_.root().GetServerId(), "tag a", 11, 110);
3906 mock_server_->AddUpdatePref(ids_.FromNumber(3).GetServerId(),
3907 ids_.root().GetServerId(), "tag a", 12, 120);
3908 mock_server_->AddUpdatePref(ids_.FromNumber(4).GetServerId(),
3909 ids_.root().GetServerId(), "tag a", 13, 130);
3911 mock_server_->AddUpdatePref(ids_.FromNumber(105).GetServerId(),
3912 ids_.root().GetServerId(), "tag b", 14, 140);
3913 mock_server_->AddUpdatePref(ids_.FromNumber(102).GetServerId(),
3914 ids_.root().GetServerId(), "tag b", 15, 150);
3915 // Least ID: winner.
3916 mock_server_->AddUpdatePref(ids_.FromNumber(101).GetServerId(),
3917 ids_.root().GetServerId(), "tag b", 16, 160);
3918 mock_server_->AddUpdatePref(ids_.FromNumber(104).GetServerId(),
3919 ids_.root().GetServerId(), "tag b", 17, 170);
3921 mock_server_->AddUpdatePref(ids_.FromNumber(205).GetServerId(),
3922 ids_.root().GetServerId(), "tag c", 18, 180);
3923 mock_server_->AddUpdatePref(ids_.FromNumber(202).GetServerId(),
3924 ids_.root().GetServerId(), "tag c", 19, 190);
3925 mock_server_->AddUpdatePref(ids_.FromNumber(204).GetServerId(),
3926 ids_.root().GetServerId(), "tag c", 20, 200);
3927 // Least ID: winner.
3928 mock_server_->AddUpdatePref(ids_.FromNumber(201).GetServerId(),
3929 ids_.root().GetServerId(), "tag c", 21, 210);
3931 mock_server_->set_conflict_all_commits(true);
3934 // This should cause client tag overwrite.
3936 syncable::ReadTransaction trans(FROM_HERE, directory());
3938 Entry tag_a(&trans, GET_BY_CLIENT_TAG, "tag a");
3939 ASSERT_TRUE(tag_a.good());
3940 EXPECT_TRUE(tag_a.GetId().ServerKnows());
3941 EXPECT_EQ(ids_.FromNumber(1), tag_a.GetId());
3942 EXPECT_FALSE(tag_a.GetIsDel());
3943 EXPECT_FALSE(tag_a.GetIsUnappliedUpdate());
3944 EXPECT_FALSE(tag_a.GetIsUnsynced());
3945 EXPECT_EQ(1, tag_a.GetBaseVersion());
3946 EXPECT_EQ("tag a", tag_a.GetUniqueClientTag());
3948 Entry tag_b(&trans, GET_BY_CLIENT_TAG, "tag b");
3949 ASSERT_TRUE(tag_b.good());
3950 EXPECT_TRUE(tag_b.GetId().ServerKnows());
3951 EXPECT_EQ(ids_.FromNumber(101), tag_b.GetId());
3952 EXPECT_FALSE(tag_b.GetIsDel());
3953 EXPECT_FALSE(tag_b.GetIsUnappliedUpdate());
3954 EXPECT_FALSE(tag_b.GetIsUnsynced());
3955 EXPECT_EQ(16, tag_b.GetBaseVersion());
3956 EXPECT_EQ("tag b", tag_b.GetUniqueClientTag());
3958 Entry tag_c(&trans, GET_BY_CLIENT_TAG, "tag c");
3959 ASSERT_TRUE(tag_c.good());
3960 EXPECT_TRUE(tag_c.GetId().ServerKnows());
3961 EXPECT_EQ(ids_.FromNumber(201), tag_c.GetId());
3962 EXPECT_FALSE(tag_c.GetIsDel());
3963 EXPECT_FALSE(tag_c.GetIsUnappliedUpdate());
3964 EXPECT_FALSE(tag_c.GetIsUnsynced());
3965 EXPECT_EQ(21, tag_c.GetBaseVersion());
3966 EXPECT_EQ("tag c", tag_c.GetUniqueClientTag());
3968 syncable::Directory::Metahandles children;
3969 directory()->GetChildHandlesById(&trans, trans.root_id(), &children);
3970 ASSERT_EQ(3U, children.size());
3974 TEST_F(SyncerTest, UniqueServerTagUpdates) {
3975 // As a hurdle, introduce an item whose name is the same as the tag value
3977 int64 hurdle_handle = CreateUnsyncedDirectory("bob", "id_bob");
3979 syncable::ReadTransaction trans(FROM_HERE, directory());
3980 Entry hurdle(&trans, GET_BY_HANDLE, hurdle_handle);
3981 ASSERT_TRUE(hurdle.good());
3982 ASSERT_TRUE(!hurdle.GetIsDel());
3983 ASSERT_TRUE(hurdle.GetUniqueServerTag().empty());
3984 ASSERT_TRUE(hurdle.GetNonUniqueName()== "bob");
3986 // Try to lookup by the tagname. These should fail.
3987 Entry tag_alpha(&trans, GET_BY_SERVER_TAG, "alpha");
3988 EXPECT_FALSE(tag_alpha.good());
3989 Entry tag_bob(&trans, GET_BY_SERVER_TAG, "bob");
3990 EXPECT_FALSE(tag_bob.good());
3993 // Now download some tagged items as updates.
3994 mock_server_->AddUpdateDirectory(
3995 1, 0, "update1", 1, 10, std::string(), std::string());
3996 mock_server_->SetLastUpdateServerTag("alpha");
3997 mock_server_->AddUpdateDirectory(
3998 2, 0, "update2", 2, 20, std::string(), std::string());
3999 mock_server_->SetLastUpdateServerTag("bob");
4003 syncable::ReadTransaction trans(FROM_HERE, directory());
4005 // The new items should be applied as new entries, and we should be able
4006 // to look them up by their tag values.
4007 Entry tag_alpha(&trans, GET_BY_SERVER_TAG, "alpha");
4008 ASSERT_TRUE(tag_alpha.good());
4009 ASSERT_TRUE(!tag_alpha.GetIsDel());
4010 ASSERT_TRUE(tag_alpha.GetUniqueServerTag()== "alpha");
4011 ASSERT_TRUE(tag_alpha.GetNonUniqueName()== "update1");
4012 Entry tag_bob(&trans, GET_BY_SERVER_TAG, "bob");
4013 ASSERT_TRUE(tag_bob.good());
4014 ASSERT_TRUE(!tag_bob.GetIsDel());
4015 ASSERT_TRUE(tag_bob.GetUniqueServerTag()== "bob");
4016 ASSERT_TRUE(tag_bob.GetNonUniqueName()== "update2");
4017 // The old item should be unchanged.
4018 Entry hurdle(&trans, GET_BY_HANDLE, hurdle_handle);
4019 ASSERT_TRUE(hurdle.good());
4020 ASSERT_TRUE(!hurdle.GetIsDel());
4021 ASSERT_TRUE(hurdle.GetUniqueServerTag().empty());
4022 ASSERT_TRUE(hurdle.GetNonUniqueName()== "bob");
4026 TEST_F(SyncerTest, GetUpdatesSetsRequestedTypes) {
4027 // The expectations of this test happen in the MockConnectionManager's
4028 // GetUpdates handler. EnableDatatype sets the expectation value from our
4029 // set of enabled/disabled datatypes.
4030 EnableDatatype(BOOKMARKS);
4032 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4034 EnableDatatype(AUTOFILL);
4036 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4038 EnableDatatype(PREFERENCES);
4040 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4042 DisableDatatype(BOOKMARKS);
4044 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4046 DisableDatatype(AUTOFILL);
4048 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4050 DisableDatatype(PREFERENCES);
4051 EnableDatatype(AUTOFILL);
4053 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4056 // A typical scenario: server and client each have one update for the other.
4057 // This is the "happy path" alternative to UpdateFailsThenDontCommit.
4058 TEST_F(SyncerTest, UpdateThenCommit) {
4059 syncable::Id to_receive = ids_.NewServerId();
4060 syncable::Id to_commit = ids_.NewLocalId();
4062 mock_server_->AddUpdateDirectory(to_receive, ids_.root(), "x", 1, 10,
4063 foreign_cache_guid(), "-1");
4064 int64 commit_handle = CreateUnsyncedDirectory("y", to_commit);
4067 // The sync cycle should have included a GetUpdate, then a commit. By the
4068 // time the commit happened, we should have known for sure that there were no
4069 // hierarchy conflicts, and reported this fact to the server.
4070 ASSERT_TRUE(mock_server_->last_request().has_commit());
4071 VerifyNoHierarchyConflictsReported(mock_server_->last_request());
4073 syncable::ReadTransaction trans(FROM_HERE, directory());
4075 Entry received(&trans, GET_BY_ID, to_receive);
4076 ASSERT_TRUE(received.good());
4077 EXPECT_FALSE(received.GetIsUnsynced());
4078 EXPECT_FALSE(received.GetIsUnappliedUpdate());
4080 Entry committed(&trans, GET_BY_HANDLE, commit_handle);
4081 ASSERT_TRUE(committed.good());
4082 EXPECT_FALSE(committed.GetIsUnsynced());
4083 EXPECT_FALSE(committed.GetIsUnappliedUpdate());
4086 // Same as above, but this time we fail to download updates.
4087 // We should not attempt to commit anything unless we successfully downloaded
4088 // updates, otherwise we risk causing a server-side conflict.
4089 TEST_F(SyncerTest, UpdateFailsThenDontCommit) {
4090 syncable::Id to_receive = ids_.NewServerId();
4091 syncable::Id to_commit = ids_.NewLocalId();
4093 mock_server_->AddUpdateDirectory(to_receive, ids_.root(), "x", 1, 10,
4094 foreign_cache_guid(), "-1");
4095 int64 commit_handle = CreateUnsyncedDirectory("y", to_commit);
4096 mock_server_->FailNextPostBufferToPathCall();
4099 syncable::ReadTransaction trans(FROM_HERE, directory());
4101 // We did not receive this update.
4102 Entry received(&trans, GET_BY_ID, to_receive);
4103 ASSERT_FALSE(received.good());
4105 // And our local update remains unapplied.
4106 Entry committed(&trans, GET_BY_HANDLE, commit_handle);
4107 ASSERT_TRUE(committed.good());
4108 EXPECT_TRUE(committed.GetIsUnsynced());
4109 EXPECT_FALSE(committed.GetIsUnappliedUpdate());
4111 // Inform the Mock we won't be fetching all updates.
4112 mock_server_->ClearUpdatesQueue();
4115 // Downloads two updates and applies them successfully.
4116 // This is the "happy path" alternative to ConfigureFailsDontApplyUpdates.
4117 TEST_F(SyncerTest, ConfigureDownloadsTwoBatchesSuccess) {
4118 syncable::Id node1 = ids_.NewServerId();
4119 syncable::Id node2 = ids_.NewServerId();
4121 // Construct the first GetUpdates response.
4122 mock_server_->AddUpdateDirectory(node1, ids_.root(), "one", 1, 10,
4123 foreign_cache_guid(), "-2");
4124 mock_server_->SetChangesRemaining(1);
4125 mock_server_->NextUpdateBatch();
4127 // Construct the second GetUpdates response.
4128 mock_server_->AddUpdateDirectory(node2, ids_.root(), "two", 1, 20,
4129 foreign_cache_guid(), "-2");
4131 SyncShareConfigure();
4133 syncable::ReadTransaction trans(FROM_HERE, directory());
4134 // Both nodes should be downloaded and applied.
4136 Entry n1(&trans, GET_BY_ID, node1);
4137 ASSERT_TRUE(n1.good());
4138 EXPECT_FALSE(n1.GetIsUnappliedUpdate());
4140 Entry n2(&trans, GET_BY_ID, node2);
4141 ASSERT_TRUE(n2.good());
4142 EXPECT_FALSE(n2.GetIsUnappliedUpdate());
4145 // Same as the above case, but this time the second batch fails to download.
4146 TEST_F(SyncerTest, ConfigureFailsDontApplyUpdates) {
4147 syncable::Id node1 = ids_.NewServerId();
4148 syncable::Id node2 = ids_.NewServerId();
4150 // The scenario: we have two batches of updates with one update each. A
4151 // normal confgure step would download all the updates one batch at a time and
4152 // apply them. This configure will succeed in downloading the first batch
4153 // then fail when downloading the second.
4154 mock_server_->FailNthPostBufferToPathCall(2);
4156 // Construct the first GetUpdates response.
4157 mock_server_->AddUpdateDirectory(node1, ids_.root(), "one", 1, 10,
4158 foreign_cache_guid(), "-1");
4159 mock_server_->SetChangesRemaining(1);
4160 mock_server_->NextUpdateBatch();
4162 // Consutrct the second GetUpdates response.
4163 mock_server_->AddUpdateDirectory(node2, ids_.root(), "two", 1, 20,
4164 foreign_cache_guid(), "-2");
4166 SyncShareConfigure();
4168 syncable::ReadTransaction trans(FROM_HERE, directory());
4170 // The first node was downloaded, but not applied.
4171 Entry n1(&trans, GET_BY_ID, node1);
4172 ASSERT_TRUE(n1.good());
4173 EXPECT_TRUE(n1.GetIsUnappliedUpdate());
4175 // The second node was not downloaded.
4176 Entry n2(&trans, GET_BY_ID, node2);
4177 EXPECT_FALSE(n2.good());
4179 // One update remains undownloaded.
4180 mock_server_->ClearUpdatesQueue();
4183 TEST_F(SyncerTest, GetKeySuccess) {
4185 syncable::ReadTransaction rtrans(FROM_HERE, directory());
4186 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans));
4189 SyncShareConfigure();
4191 EXPECT_EQ(session_->status_controller().last_get_key_result(), SYNCER_OK);
4193 syncable::ReadTransaction rtrans(FROM_HERE, directory());
4194 EXPECT_FALSE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans));
4198 TEST_F(SyncerTest, GetKeyEmpty) {
4200 syncable::ReadTransaction rtrans(FROM_HERE, directory());
4201 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans));
4204 mock_server_->SetKeystoreKey(std::string());
4205 SyncShareConfigure();
4207 EXPECT_NE(session_->status_controller().last_get_key_result(), SYNCER_OK);
4209 syncable::ReadTransaction rtrans(FROM_HERE, directory());
4210 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans));
4214 // Test what happens if a client deletes, then recreates, an object very
4215 // quickly. It is possible that the deletion gets sent as a commit, and
4216 // the undelete happens during the commit request. The principle here
4217 // is that with a single committing client, conflicts should never
4218 // be encountered, and a client encountering its past actions during
4219 // getupdates should never feed back to override later actions.
4221 // In cases of ordering A-F below, the outcome should be the same.
4222 // Exercised by UndeleteDuringCommit:
4223 // A. Delete - commit - undelete - commitresponse.
4224 // B. Delete - commit - undelete - commitresponse - getupdates.
4225 // Exercised by UndeleteBeforeCommit:
4226 // C. Delete - undelete - commit - commitresponse.
4227 // D. Delete - undelete - commit - commitresponse - getupdates.
4228 // Exercised by UndeleteAfterCommit:
4229 // E. Delete - commit - commitresponse - undelete - commit
4230 // - commitresponse.
4231 // F. Delete - commit - commitresponse - undelete - commit -
4232 // - commitresponse - getupdates.
4233 class SyncerUndeletionTest : public SyncerTest {
4235 SyncerUndeletionTest()
4236 : client_tag_("foobar"),
4237 metahandle_(syncable::kInvalidMetaHandle) {
4241 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
4242 MutableEntry perm_folder(
4243 &trans, CREATE, BOOKMARKS, ids_.root(), "clientname");
4244 ASSERT_TRUE(perm_folder.good());
4245 perm_folder.PutUniqueClientTag(client_tag_);
4246 perm_folder.PutIsUnsynced(true);
4247 perm_folder.PutSyncing(false);
4248 perm_folder.PutSpecifics(DefaultBookmarkSpecifics());
4249 EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate());
4250 EXPECT_FALSE(perm_folder.GetId().ServerKnows());
4251 metahandle_ = perm_folder.GetMetahandle();
4252 local_id_ = perm_folder.GetId();
4256 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
4257 MutableEntry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4258 ASSERT_TRUE(entry.good());
4259 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4260 entry.PutIsDel(true);
4261 entry.PutIsUnsynced(true);
4262 entry.PutSyncing(false);
4266 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
4267 MutableEntry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4268 ASSERT_TRUE(entry.good());
4269 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4270 EXPECT_TRUE(entry.GetIsDel());
4271 entry.PutIsDel(false);
4272 entry.PutIsUnsynced(true);
4273 entry.PutSyncing(false);
4276 int64 GetMetahandleOfTag() {
4277 syncable::ReadTransaction trans(FROM_HERE, directory());
4278 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4279 EXPECT_TRUE(entry.good());
4280 if (!entry.good()) {
4281 return syncable::kInvalidMetaHandle;
4283 return entry.GetMetahandle();
4286 void ExpectUnsyncedCreation() {
4287 syncable::ReadTransaction trans(FROM_HERE, directory());
4288 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4290 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4291 EXPECT_FALSE(entry.GetIsDel());
4292 EXPECT_FALSE(entry.GetServerIsDel()); // Never been committed.
4293 EXPECT_GE(0, entry.GetBaseVersion());
4294 EXPECT_TRUE(entry.GetIsUnsynced());
4295 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4298 void ExpectUnsyncedUndeletion() {
4299 syncable::ReadTransaction trans(FROM_HERE, directory());
4300 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4302 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4303 EXPECT_FALSE(entry.GetIsDel());
4304 EXPECT_TRUE(entry.GetServerIsDel());
4305 EXPECT_EQ(0, entry.GetBaseVersion());
4306 EXPECT_TRUE(entry.GetIsUnsynced());
4307 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4308 EXPECT_TRUE(entry.GetId().ServerKnows());
4311 void ExpectUnsyncedEdit() {
4312 syncable::ReadTransaction trans(FROM_HERE, directory());
4313 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4315 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4316 EXPECT_FALSE(entry.GetIsDel());
4317 EXPECT_FALSE(entry.GetServerIsDel());
4318 EXPECT_LT(0, entry.GetBaseVersion());
4319 EXPECT_TRUE(entry.GetIsUnsynced());
4320 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4321 EXPECT_TRUE(entry.GetId().ServerKnows());
4324 void ExpectUnsyncedDeletion() {
4325 syncable::ReadTransaction trans(FROM_HERE, directory());
4326 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4328 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4329 EXPECT_TRUE(entry.GetIsDel());
4330 EXPECT_FALSE(entry.GetServerIsDel());
4331 EXPECT_TRUE(entry.GetIsUnsynced());
4332 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4333 EXPECT_LT(0, entry.GetBaseVersion());
4334 EXPECT_LT(0, entry.GetServerVersion());
4337 void ExpectSyncedAndCreated() {
4338 syncable::ReadTransaction trans(FROM_HERE, directory());
4339 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4341 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4342 EXPECT_FALSE(entry.GetIsDel());
4343 EXPECT_FALSE(entry.GetServerIsDel());
4344 EXPECT_LT(0, entry.GetBaseVersion());
4345 EXPECT_EQ(entry.GetBaseVersion(), entry.GetServerVersion());
4346 EXPECT_FALSE(entry.GetIsUnsynced());
4347 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4350 void ExpectSyncedAndDeleted() {
4351 syncable::ReadTransaction trans(FROM_HERE, directory());
4352 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4354 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4355 EXPECT_TRUE(entry.GetIsDel());
4356 EXPECT_TRUE(entry.GetServerIsDel());
4357 EXPECT_FALSE(entry.GetIsUnsynced());
4358 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4359 EXPECT_GE(0, entry.GetBaseVersion());
4360 EXPECT_GE(0, entry.GetServerVersion());
4364 const std::string client_tag_;
4365 syncable::Id local_id_;
4369 TEST_F(SyncerUndeletionTest, UndeleteDuringCommit) {
4371 ExpectUnsyncedCreation();
4374 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4375 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4376 ExpectSyncedAndCreated();
4378 // Delete, begin committing the delete, then undelete while committing.
4380 ExpectUnsyncedDeletion();
4381 mock_server_->SetMidCommitCallback(
4382 base::Bind(&SyncerUndeletionTest::Undelete, base::Unretained(this)));
4385 // We will continue to commit until all nodes are synced, so we expect
4386 // that both the delete and following undelete were committed. We haven't
4387 // downloaded any updates, though, so the SERVER fields will be the same
4388 // as they were at the start of the cycle.
4389 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4390 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4393 syncable::ReadTransaction trans(FROM_HERE, directory());
4394 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4396 // Server fields lag behind.
4397 EXPECT_FALSE(entry.GetServerIsDel());
4399 // We have committed the second (undelete) update.
4400 EXPECT_FALSE(entry.GetIsDel());
4401 EXPECT_FALSE(entry.GetIsUnsynced());
4402 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4405 // Now, encounter a GetUpdates corresponding to the deletion from
4406 // the server. The undeletion should prevail again and be committed.
4407 // None of this should trigger any conflict detection -- it is perfectly
4408 // normal to recieve updates from our own commits.
4409 mock_server_->SetMidCommitCallback(base::Closure());
4410 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
4411 update->set_originator_cache_guid(local_cache_guid());
4412 update->set_originator_client_item_id(local_id_.GetServerId());
4415 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4416 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4417 ExpectSyncedAndCreated();
4420 TEST_F(SyncerUndeletionTest, UndeleteBeforeCommit) {
4422 ExpectUnsyncedCreation();
4425 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4426 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4427 ExpectSyncedAndCreated();
4429 // Delete and undelete, then sync to pick up the result.
4431 ExpectUnsyncedDeletion();
4433 ExpectUnsyncedEdit(); // Edit, not undelete: server thinks it exists.
4436 // The item ought to have committed successfully.
4437 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4438 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4439 ExpectSyncedAndCreated();
4441 syncable::ReadTransaction trans(FROM_HERE, directory());
4442 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4443 EXPECT_EQ(2, entry.GetBaseVersion());
4446 // Now, encounter a GetUpdates corresponding to the just-committed
4448 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
4449 update->set_originator_cache_guid(local_cache_guid());
4450 update->set_originator_client_item_id(local_id_.GetServerId());
4452 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4453 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4454 ExpectSyncedAndCreated();
4457 TEST_F(SyncerUndeletionTest, UndeleteAfterCommitButBeforeGetUpdates) {
4459 ExpectUnsyncedCreation();
4462 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4463 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4464 ExpectSyncedAndCreated();
4466 // Delete and commit.
4468 ExpectUnsyncedDeletion();
4471 // The item ought to have committed successfully.
4472 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4473 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4474 ExpectSyncedAndDeleted();
4476 // Before the GetUpdates, the item is locally undeleted.
4478 ExpectUnsyncedUndeletion();
4480 // Now, encounter a GetUpdates corresponding to the just-committed
4481 // deletion update. The undeletion should prevail.
4482 mock_server_->AddUpdateFromLastCommit();
4484 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4485 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4486 ExpectSyncedAndCreated();
4489 TEST_F(SyncerUndeletionTest, UndeleteAfterDeleteAndGetUpdates) {
4491 ExpectUnsyncedCreation();
4494 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4495 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4496 ExpectSyncedAndCreated();
4498 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
4499 update->set_originator_cache_guid(local_cache_guid());
4500 update->set_originator_client_item_id(local_id_.GetServerId());
4502 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4503 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4504 ExpectSyncedAndCreated();
4506 // Delete and commit.
4508 ExpectUnsyncedDeletion();
4511 // The item ought to have committed successfully.
4512 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4513 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4514 ExpectSyncedAndDeleted();
4516 // Now, encounter a GetUpdates corresponding to the just-committed
4517 // deletion update. Should be consistent.
4518 mock_server_->AddUpdateFromLastCommit();
4520 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4521 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4522 ExpectSyncedAndDeleted();
4524 // After the GetUpdates, the item is locally undeleted.
4526 ExpectUnsyncedUndeletion();
4528 // Now, encounter a GetUpdates corresponding to the just-committed
4529 // deletion update. The undeletion should prevail.
4531 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4532 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4533 ExpectSyncedAndCreated();
4536 // Test processing of undeletion GetUpdateses.
4537 TEST_F(SyncerUndeletionTest, UndeleteAfterOtherClientDeletes) {
4539 ExpectUnsyncedCreation();
4542 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4543 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4544 ExpectSyncedAndCreated();
4546 // Add a delete from the server.
4547 sync_pb::SyncEntity* update1 = mock_server_->AddUpdateFromLastCommit();
4548 update1->set_originator_cache_guid(local_cache_guid());
4549 update1->set_originator_client_item_id(local_id_.GetServerId());
4551 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4552 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4553 ExpectSyncedAndCreated();
4555 // Some other client deletes the item.
4557 syncable::ReadTransaction trans(FROM_HERE, directory());
4558 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4559 mock_server_->AddUpdateTombstone(entry.GetId());
4563 // The update ought to have applied successfully.
4564 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4565 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4566 ExpectSyncedAndDeleted();
4568 // Undelete it locally.
4570 ExpectUnsyncedUndeletion();
4572 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4573 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4574 ExpectSyncedAndCreated();
4576 // Now, encounter a GetUpdates corresponding to the just-committed
4577 // deletion update. The undeletion should prevail.
4578 sync_pb::SyncEntity* update2 = mock_server_->AddUpdateFromLastCommit();
4579 update2->set_originator_cache_guid(local_cache_guid());
4580 update2->set_originator_client_item_id(local_id_.GetServerId());
4582 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4583 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4584 ExpectSyncedAndCreated();
4587 TEST_F(SyncerUndeletionTest, UndeleteAfterOtherClientDeletesImmediately) {
4589 ExpectUnsyncedCreation();
4592 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4593 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4594 ExpectSyncedAndCreated();
4596 // Some other client deletes the item before we get a chance
4597 // to GetUpdates our original request.
4599 syncable::ReadTransaction trans(FROM_HERE, directory());
4600 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4601 mock_server_->AddUpdateTombstone(entry.GetId());
4605 // The update ought to have applied successfully.
4606 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4607 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4608 ExpectSyncedAndDeleted();
4610 // Undelete it locally.
4612 ExpectUnsyncedUndeletion();
4614 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4615 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4616 ExpectSyncedAndCreated();
4618 // Now, encounter a GetUpdates corresponding to the just-committed
4619 // deletion update. The undeletion should prevail.
4620 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
4621 update->set_originator_cache_guid(local_cache_guid());
4622 update->set_originator_client_item_id(local_id_.GetServerId());
4624 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4625 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4626 ExpectSyncedAndCreated();
4629 TEST_F(SyncerUndeletionTest, OtherClientUndeletes) {
4631 ExpectUnsyncedCreation();
4634 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4635 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4636 ExpectSyncedAndCreated();
4638 // Get the updates of our just-committed entry.
4639 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
4640 update->set_originator_cache_guid(local_cache_guid());
4641 update->set_originator_client_item_id(local_id_.GetServerId());
4643 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4644 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4645 ExpectSyncedAndCreated();
4647 // We delete the item.
4649 ExpectUnsyncedDeletion();
4652 // The update ought to have applied successfully.
4653 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4654 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4655 ExpectSyncedAndDeleted();
4657 // Now, encounter a GetUpdates corresponding to the just-committed
4659 mock_server_->AddUpdateFromLastCommit();
4661 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4662 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4663 ExpectSyncedAndDeleted();
4665 // Some other client undeletes the item.
4667 syncable::ReadTransaction trans(FROM_HERE, directory());
4668 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4669 mock_server_->AddUpdateBookmark(
4671 entry.GetParentId(),
4672 "Thadeusz", 100, 1000,
4673 local_cache_guid(), local_id_.GetServerId());
4675 mock_server_->SetLastUpdateClientTag(client_tag_);
4677 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4678 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4679 ExpectSyncedAndCreated();
4681 syncable::ReadTransaction trans(FROM_HERE, directory());
4682 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4683 EXPECT_EQ("Thadeusz", entry.GetNonUniqueName());
4687 TEST_F(SyncerUndeletionTest, OtherClientUndeletesImmediately) {
4689 ExpectUnsyncedCreation();
4692 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4693 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4694 ExpectSyncedAndCreated();
4696 // Get the updates of our just-committed entry.
4697 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
4698 update->set_originator_cache_guid(local_cache_guid());
4700 syncable::ReadTransaction trans(FROM_HERE, directory());
4701 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4702 update->set_originator_client_item_id(local_id_.GetServerId());
4705 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4706 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4707 ExpectSyncedAndCreated();
4709 // We delete the item.
4711 ExpectUnsyncedDeletion();
4714 // The update ought to have applied successfully.
4715 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4716 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4717 ExpectSyncedAndDeleted();
4719 // Some other client undeletes before we see the update from our
4722 syncable::ReadTransaction trans(FROM_HERE, directory());
4723 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4724 mock_server_->AddUpdateBookmark(
4726 entry.GetParentId(),
4727 "Thadeusz", 100, 1000,
4728 local_cache_guid(), local_id_.GetServerId());
4730 mock_server_->SetLastUpdateClientTag(client_tag_);
4732 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4733 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4734 ExpectSyncedAndCreated();
4736 syncable::ReadTransaction trans(FROM_HERE, directory());
4737 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4738 EXPECT_EQ("Thadeusz", entry.GetNonUniqueName());
4743 TEST_PARAM_BOOKMARK_ENABLE_BIT,
4744 TEST_PARAM_AUTOFILL_ENABLE_BIT,
4745 TEST_PARAM_BIT_COUNT
4750 public ::testing::WithParamInterface<int> {
4752 bool ShouldFailBookmarkCommit() {
4753 return (GetParam() & (1 << TEST_PARAM_BOOKMARK_ENABLE_BIT)) == 0;
4755 bool ShouldFailAutofillCommit() {
4756 return (GetParam() & (1 << TEST_PARAM_AUTOFILL_ENABLE_BIT)) == 0;
4760 INSTANTIATE_TEST_CASE_P(ExtensionsActivity,
4762 testing::Range(0, 1 << TEST_PARAM_BIT_COUNT));
4764 TEST_P(MixedResult, ExtensionsActivity) {
4766 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
4768 MutableEntry pref(&wtrans, CREATE, PREFERENCES, wtrans.root_id(), "pref");
4769 ASSERT_TRUE(pref.good());
4770 pref.PutIsUnsynced(true);
4772 MutableEntry bookmark(
4773 &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "bookmark");
4774 ASSERT_TRUE(bookmark.good());
4775 bookmark.PutIsUnsynced(true);
4777 if (ShouldFailBookmarkCommit()) {
4778 mock_server_->SetTransientErrorId(bookmark.GetId());
4781 if (ShouldFailAutofillCommit()) {
4782 mock_server_->SetTransientErrorId(pref.GetId());
4787 // Put some extenions activity records into the monitor.
4789 ExtensionsActivity::Records records;
4790 records["ABC"].extension_id = "ABC";
4791 records["ABC"].bookmark_write_count = 2049U;
4792 records["xyz"].extension_id = "xyz";
4793 records["xyz"].bookmark_write_count = 4U;
4794 context_->extensions_activity()->PutRecords(records);
4799 ExtensionsActivity::Records final_monitor_records;
4800 context_->extensions_activity()->GetAndClearRecords(&final_monitor_records);
4801 if (ShouldFailBookmarkCommit()) {
4802 ASSERT_EQ(2U, final_monitor_records.size())
4803 << "Should restore records after unsuccessful bookmark commit.";
4804 EXPECT_EQ("ABC", final_monitor_records["ABC"].extension_id);
4805 EXPECT_EQ("xyz", final_monitor_records["xyz"].extension_id);
4806 EXPECT_EQ(2049U, final_monitor_records["ABC"].bookmark_write_count);
4807 EXPECT_EQ(4U, final_monitor_records["xyz"].bookmark_write_count);
4809 EXPECT_TRUE(final_monitor_records.empty())
4810 << "Should not restore records after successful bookmark commit.";
4814 } // namespace syncer