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