Get rid of a couple references to IDR_WARNING
[chromium-blink-merge.git] / sync / engine / syncer_unittest.cc
blobdcd326e8c887888ddab0eb867fafb20be8a07bcb
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/time/time.h"
24 #include "build/build_config.h"
25 #include "sync/engine/backoff_delay_provider.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::CountEntriesWithName;
75 using syncable::Directory;
76 using syncable::Entry;
77 using syncable::GetFirstEntryWithName;
78 using syncable::GetOnlyEntryWithName;
79 using syncable::Id;
80 using syncable::kEncryptedString;
81 using syncable::MutableEntry;
82 using syncable::WriteTransaction;
84 using syncable::CREATE;
85 using syncable::GET_BY_HANDLE;
86 using syncable::GET_BY_ID;
87 using syncable::GET_BY_CLIENT_TAG;
88 using syncable::GET_BY_SERVER_TAG;
89 using syncable::GET_TYPE_ROOT;
90 using syncable::UNITTEST;
92 using sessions::MockDebugInfoGetter;
93 using sessions::StatusController;
94 using sessions::SyncSessionContext;
95 using sessions::SyncSession;
97 namespace {
99 // A helper to hold on to the counters emitted by the sync engine.
100 class TypeDebugInfoCache : public TypeDebugInfoObserver {
101 public:
102 TypeDebugInfoCache();
103 ~TypeDebugInfoCache() override;
105 CommitCounters GetLatestCommitCounters(ModelType type) const;
106 UpdateCounters GetLatestUpdateCounters(ModelType type) const;
107 StatusCounters GetLatestStatusCounters(ModelType type) const;
109 // TypeDebugInfoObserver implementation.
110 void OnCommitCountersUpdated(syncer::ModelType type,
111 const CommitCounters& counters) override;
112 void OnUpdateCountersUpdated(syncer::ModelType type,
113 const UpdateCounters& counters) override;
114 void OnStatusCountersUpdated(syncer::ModelType type,
115 const StatusCounters& counters) override;
117 private:
118 std::map<ModelType, CommitCounters> commit_counters_map_;
119 std::map<ModelType, UpdateCounters> update_counters_map_;
120 std::map<ModelType, StatusCounters> status_counters_map_;
123 TypeDebugInfoCache::TypeDebugInfoCache() {}
125 TypeDebugInfoCache::~TypeDebugInfoCache() {}
127 CommitCounters TypeDebugInfoCache::GetLatestCommitCounters(
128 ModelType type) const {
129 std::map<ModelType, CommitCounters>::const_iterator it =
130 commit_counters_map_.find(type);
131 if (it == commit_counters_map_.end()) {
132 return CommitCounters();
133 } else {
134 return it->second;
138 UpdateCounters TypeDebugInfoCache::GetLatestUpdateCounters(
139 ModelType type) const {
140 std::map<ModelType, UpdateCounters>::const_iterator it =
141 update_counters_map_.find(type);
142 if (it == update_counters_map_.end()) {
143 return UpdateCounters();
144 } else {
145 return it->second;
149 StatusCounters TypeDebugInfoCache::GetLatestStatusCounters(
150 ModelType type) const {
151 std::map<ModelType, StatusCounters>::const_iterator it =
152 status_counters_map_.find(type);
153 if (it == status_counters_map_.end()) {
154 return StatusCounters();
155 } else {
156 return it->second;
160 void TypeDebugInfoCache::OnCommitCountersUpdated(
161 syncer::ModelType type,
162 const CommitCounters& counters) {
163 commit_counters_map_[type] = counters;
166 void TypeDebugInfoCache::OnUpdateCountersUpdated(
167 syncer::ModelType type,
168 const UpdateCounters& counters) {
169 update_counters_map_[type] = counters;
172 void TypeDebugInfoCache::OnStatusCountersUpdated(
173 syncer::ModelType type,
174 const StatusCounters& counters) {
175 status_counters_map_[type] = counters;
178 } // namespace
180 class SyncerTest : public testing::Test,
181 public SyncSession::Delegate,
182 public SyncEngineEventListener {
183 protected:
184 SyncerTest()
185 : extensions_activity_(new ExtensionsActivity),
186 syncer_(NULL),
187 saw_syncer_event_(false),
188 last_client_invalidation_hint_buffer_size_(10) {
191 // SyncSession::Delegate implementation.
192 void OnThrottled(const base::TimeDelta& throttle_duration) override {
193 FAIL() << "Should not get silenced.";
195 void OnTypesThrottled(ModelTypeSet types,
196 const base::TimeDelta& throttle_duration) override {
197 scheduler_->OnTypesThrottled(types, throttle_duration);
199 bool IsCurrentlyThrottled() override { return false; }
200 void OnReceivedLongPollIntervalUpdate(
201 const base::TimeDelta& new_interval) override {
202 last_long_poll_interval_received_ = new_interval;
204 void OnReceivedShortPollIntervalUpdate(
205 const base::TimeDelta& new_interval) override {
206 last_short_poll_interval_received_ = new_interval;
208 void OnReceivedCustomNudgeDelays(
209 const std::map<ModelType, base::TimeDelta>& delay_map) override {
210 std::map<ModelType, base::TimeDelta>::const_iterator iter =
211 delay_map.find(SESSIONS);
212 if (iter != delay_map.end() && iter->second > base::TimeDelta())
213 last_sessions_commit_delay_ = iter->second;
214 iter = delay_map.find(BOOKMARKS);
215 if (iter != delay_map.end() && iter->second > base::TimeDelta())
216 last_bookmarks_commit_delay_ = iter->second;
218 void OnReceivedClientInvalidationHintBufferSize(int size) override {
219 last_client_invalidation_hint_buffer_size_ = size;
221 void OnReceivedGuRetryDelay(const base::TimeDelta& delay) override {}
222 void OnReceivedMigrationRequest(ModelTypeSet types) override {}
223 void OnProtocolEvent(const ProtocolEvent& event) override {}
224 void OnSyncProtocolError(const SyncProtocolError& error) override {}
226 void GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) {
227 // We're just testing the sync engine here, so we shunt everything to
228 // the SyncerThread. Datatypes which aren't enabled aren't in the map.
229 for (ModelTypeSet::Iterator it = enabled_datatypes_.First();
230 it.Good(); it.Inc()) {
231 (*out)[it.Get()] = GROUP_PASSIVE;
235 void OnSyncCycleEvent(const SyncCycleEvent& event) override {
236 DVLOG(1) << "HandleSyncEngineEvent in unittest " << event.what_happened;
237 // we only test for entry-specific events, not status changed ones.
238 switch (event.what_happened) {
239 case SyncCycleEvent::SYNC_CYCLE_BEGIN: // Fall through.
240 case SyncCycleEvent::STATUS_CHANGED:
241 case SyncCycleEvent::SYNC_CYCLE_ENDED:
242 return;
243 default:
244 CHECK(false) << "Handling unknown error type in unit tests!!";
246 saw_syncer_event_ = true;
249 void OnActionableError(const SyncProtocolError& error) override {}
250 void OnRetryTimeChanged(base::Time retry_time) override {}
251 void OnThrottledTypesChanged(ModelTypeSet throttled_types) override {}
252 void OnMigrationRequested(ModelTypeSet types) override {}
254 void ResetSession() {
255 session_.reset(SyncSession::Build(context_.get(), this));
258 bool SyncShareNudge() {
259 ResetSession();
261 // Pretend we've seen a local change, to make the nudge_tracker look normal.
262 nudge_tracker_.RecordLocalChange(ModelTypeSet(BOOKMARKS));
264 return syncer_->NormalSyncShare(context_->GetEnabledTypes(),
265 &nudge_tracker_, session_.get());
268 bool SyncShareConfigure() {
269 ResetSession();
270 return syncer_->ConfigureSyncShare(
271 context_->GetEnabledTypes(),
272 sync_pb::GetUpdatesCallerInfo::RECONFIGURATION,
273 session_.get());
276 void SetUp() override {
277 dir_maker_.SetUp();
278 mock_server_.reset(new MockConnectionManager(directory(),
279 &cancelation_signal_));
280 debug_info_getter_.reset(new MockDebugInfoGetter);
281 EnableDatatype(BOOKMARKS);
282 EnableDatatype(NIGORI);
283 EnableDatatype(PREFERENCES);
284 EnableDatatype(NIGORI);
285 workers_.push_back(scoped_refptr<ModelSafeWorker>(
286 new FakeModelWorker(GROUP_PASSIVE)));
287 std::vector<SyncEngineEventListener*> listeners;
288 listeners.push_back(this);
290 ModelSafeRoutingInfo routing_info;
291 GetModelSafeRoutingInfo(&routing_info);
293 model_type_registry_.reset(
294 new ModelTypeRegistry(workers_, directory(), &mock_nudge_handler_));
295 model_type_registry_->RegisterDirectoryTypeDebugInfoObserver(
296 &debug_info_cache_);
298 context_.reset(new SyncSessionContext(
299 mock_server_.get(),
300 directory(),
301 extensions_activity_.get(),
302 listeners,
303 debug_info_getter_.get(),
304 model_type_registry_.get(),
305 true, // enable keystore encryption
306 false, // force enable pre-commit GU avoidance experiment
307 "fake_invalidator_client_id"));
308 context_->SetRoutingInfo(routing_info);
309 syncer_ = new Syncer(&cancelation_signal_);
310 scheduler_.reset(new SyncSchedulerImpl(
311 "TestSyncScheduler",
312 BackoffDelayProvider::FromDefaults(),
313 context_.get(),
314 // scheduler_ owned syncer_ now and will manage the memory of syncer_
315 syncer_));
317 syncable::ReadTransaction trans(FROM_HERE, directory());
318 syncable::Directory::Metahandles children;
319 directory()->GetChildHandlesById(&trans, trans.root_id(), &children);
320 ASSERT_EQ(0u, children.size());
321 saw_syncer_event_ = false;
322 root_id_ = TestIdFactory::root();
323 parent_id_ = ids_.MakeServer("parent id");
324 child_id_ = ids_.MakeServer("child id");
325 directory()->set_store_birthday(mock_server_->store_birthday());
326 mock_server_->SetKeystoreKey("encryption_key");
329 void TearDown() override {
330 model_type_registry_->UnregisterDirectoryTypeDebugInfoObserver(
331 &debug_info_cache_);
332 mock_server_.reset();
333 scheduler_.reset();
334 dir_maker_.TearDown();
337 void WriteTestDataToEntry(WriteTransaction* trans, MutableEntry* entry) {
338 EXPECT_FALSE(entry->GetIsDir());
339 EXPECT_FALSE(entry->GetIsDel());
340 sync_pb::EntitySpecifics specifics;
341 specifics.mutable_bookmark()->set_url("http://demo/");
342 specifics.mutable_bookmark()->set_favicon("PNG");
343 entry->PutSpecifics(specifics);
344 entry->PutIsUnsynced(true);
346 void VerifyTestDataInEntry(BaseTransaction* trans, Entry* entry) {
347 EXPECT_FALSE(entry->GetIsDir());
348 EXPECT_FALSE(entry->GetIsDel());
349 VerifyTestBookmarkDataInEntry(entry);
351 void VerifyTestBookmarkDataInEntry(Entry* entry) {
352 const sync_pb::EntitySpecifics& specifics = entry->GetSpecifics();
353 EXPECT_TRUE(specifics.has_bookmark());
354 EXPECT_EQ("PNG", specifics.bookmark().favicon());
355 EXPECT_EQ("http://demo/", specifics.bookmark().url());
358 void VerifyHierarchyConflictsReported(
359 const sync_pb::ClientToServerMessage& message) {
360 // Our request should have included a warning about hierarchy conflicts.
361 const sync_pb::ClientStatus& client_status = message.client_status();
362 EXPECT_TRUE(client_status.has_hierarchy_conflict_detected());
363 EXPECT_TRUE(client_status.hierarchy_conflict_detected());
366 void VerifyNoHierarchyConflictsReported(
367 const sync_pb::ClientToServerMessage& message) {
368 // Our request should have reported no hierarchy conflicts detected.
369 const sync_pb::ClientStatus& client_status = message.client_status();
370 EXPECT_TRUE(client_status.has_hierarchy_conflict_detected());
371 EXPECT_FALSE(client_status.hierarchy_conflict_detected());
374 void VerifyHierarchyConflictsUnspecified(
375 const sync_pb::ClientToServerMessage& message) {
376 // Our request should have neither confirmed nor denied hierarchy conflicts.
377 const sync_pb::ClientStatus& client_status = message.client_status();
378 EXPECT_FALSE(client_status.has_hierarchy_conflict_detected());
381 sync_pb::EntitySpecifics DefaultBookmarkSpecifics() {
382 sync_pb::EntitySpecifics result;
383 AddDefaultFieldValue(BOOKMARKS, &result);
384 return result;
387 sync_pb::EntitySpecifics DefaultPreferencesSpecifics() {
388 sync_pb::EntitySpecifics result;
389 AddDefaultFieldValue(PREFERENCES, &result);
390 return result;
392 // Enumeration of alterations to entries for commit ordering tests.
393 enum EntryFeature {
394 LIST_END = 0, // Denotes the end of the list of features from below.
395 SYNCED, // Items are unsynced by default
396 DELETED,
397 OLD_MTIME,
398 MOVED_FROM_ROOT,
401 struct CommitOrderingTest {
402 // expected commit index.
403 int commit_index;
404 // Details about the item
405 syncable::Id id;
406 syncable::Id parent_id;
407 EntryFeature features[10];
409 static CommitOrderingTest MakeLastCommitItem() {
410 CommitOrderingTest last_commit_item;
411 last_commit_item.commit_index = -1;
412 last_commit_item.id = TestIdFactory::root();
413 return last_commit_item;
417 void RunCommitOrderingTest(CommitOrderingTest* test) {
418 map<int, syncable::Id> expected_positions;
419 { // Transaction scope.
420 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
421 while (!test->id.IsRoot()) {
422 if (test->commit_index >= 0) {
423 map<int, syncable::Id>::value_type entry(test->commit_index,
424 test->id);
425 bool double_position = !expected_positions.insert(entry).second;
426 ASSERT_FALSE(double_position) << "Two id's expected at one position";
428 string utf8_name = test->id.GetServerId();
429 string name(utf8_name.begin(), utf8_name.end());
430 MutableEntry entry(&trans, CREATE, BOOKMARKS, test->parent_id, name);
432 entry.PutId(test->id);
433 if (test->id.ServerKnows()) {
434 entry.PutBaseVersion(5);
435 entry.PutServerVersion(5);
436 entry.PutServerParentId(test->parent_id);
438 entry.PutIsDir(true);
439 entry.PutIsUnsynced(true);
440 entry.PutSpecifics(DefaultBookmarkSpecifics());
441 // Set the time to 30 seconds in the future to reduce the chance of
442 // flaky tests.
443 const base::Time& now_plus_30s =
444 base::Time::Now() + base::TimeDelta::FromSeconds(30);
445 const base::Time& now_minus_2h =
446 base::Time::Now() - base::TimeDelta::FromHours(2);
447 entry.PutMtime(now_plus_30s);
448 for (size_t i = 0 ; i < arraysize(test->features) ; ++i) {
449 switch (test->features[i]) {
450 case LIST_END:
451 break;
452 case SYNCED:
453 entry.PutIsUnsynced(false);
454 break;
455 case DELETED:
456 entry.PutIsDel(true);
457 break;
458 case OLD_MTIME:
459 entry.PutMtime(now_minus_2h);
460 break;
461 case MOVED_FROM_ROOT:
462 entry.PutServerParentId(trans.root_id());
463 break;
464 default:
465 FAIL() << "Bad value in CommitOrderingTest list";
468 test++;
471 EXPECT_TRUE(SyncShareNudge());
472 ASSERT_TRUE(expected_positions.size() ==
473 mock_server_->committed_ids().size());
474 // If this test starts failing, be aware other sort orders could be valid.
475 for (size_t i = 0; i < expected_positions.size(); ++i) {
476 SCOPED_TRACE(i);
477 EXPECT_EQ(1u, expected_positions.count(i));
478 EXPECT_EQ(expected_positions[i], mock_server_->committed_ids()[i]);
482 CommitCounters GetCommitCounters(ModelType type) {
483 return debug_info_cache_.GetLatestCommitCounters(type);
486 UpdateCounters GetUpdateCounters(ModelType type) {
487 return debug_info_cache_.GetLatestUpdateCounters(type);
490 StatusCounters GetStatusCounters(ModelType type) {
491 return debug_info_cache_.GetLatestStatusCounters(type);
494 Directory* directory() {
495 return dir_maker_.directory();
498 const std::string local_cache_guid() {
499 return directory()->cache_guid();
502 const std::string foreign_cache_guid() {
503 return "kqyg7097kro6GSUod+GSg==";
506 int64 CreateUnsyncedDirectory(const string& entry_name,
507 const string& idstring) {
508 return CreateUnsyncedDirectory(entry_name,
509 syncable::Id::CreateFromServerId(idstring));
512 int64 CreateUnsyncedDirectory(const string& entry_name,
513 const syncable::Id& id) {
514 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
515 MutableEntry entry(
516 &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), entry_name);
517 EXPECT_TRUE(entry.good());
518 entry.PutIsUnsynced(true);
519 entry.PutIsDir(true);
520 entry.PutSpecifics(DefaultBookmarkSpecifics());
521 entry.PutBaseVersion(id.ServerKnows() ? 1 : 0);
522 entry.PutId(id);
523 return entry.GetMetahandle();
526 void EnableDatatype(ModelType model_type) {
527 enabled_datatypes_.Put(model_type);
529 ModelSafeRoutingInfo routing_info;
530 GetModelSafeRoutingInfo(&routing_info);
532 if (context_) {
533 context_->SetRoutingInfo(routing_info);
536 mock_server_->ExpectGetUpdatesRequestTypes(enabled_datatypes_);
539 void DisableDatatype(ModelType model_type) {
540 enabled_datatypes_.Remove(model_type);
542 ModelSafeRoutingInfo routing_info;
543 GetModelSafeRoutingInfo(&routing_info);
545 if (context_) {
546 context_->SetRoutingInfo(routing_info);
549 mock_server_->ExpectGetUpdatesRequestTypes(enabled_datatypes_);
552 Cryptographer* GetCryptographer(syncable::BaseTransaction* trans) {
553 return directory()->GetCryptographer(trans);
556 // Configures SyncSessionContext and NudgeTracker so Syncer won't call
557 // GetUpdates prior to Commit. This method can be used to ensure a Commit is
558 // not preceeded by GetUpdates.
559 void ConfigureNoGetUpdatesRequired() {
560 context_->set_server_enabled_pre_commit_update_avoidance(true);
561 nudge_tracker_.OnInvalidationsEnabled();
562 nudge_tracker_.RecordSuccessfulSyncCycle();
564 ASSERT_FALSE(context_->ShouldFetchUpdatesBeforeCommit());
565 ASSERT_FALSE(nudge_tracker_.IsGetUpdatesRequired());
568 base::MessageLoop message_loop_;
570 // Some ids to aid tests. Only the root one's value is specific. The rest
571 // are named for test clarity.
572 // TODO(chron): Get rid of these inbuilt IDs. They only make it
573 // more confusing.
574 syncable::Id root_id_;
575 syncable::Id parent_id_;
576 syncable::Id child_id_;
578 TestIdFactory ids_;
580 TestDirectorySetterUpper dir_maker_;
581 FakeEncryptor encryptor_;
582 scoped_refptr<ExtensionsActivity> extensions_activity_;
583 scoped_ptr<MockConnectionManager> mock_server_;
584 CancelationSignal cancelation_signal_;
586 Syncer* syncer_;
588 scoped_ptr<SyncSession> session_;
589 TypeDebugInfoCache debug_info_cache_;
590 MockNudgeHandler mock_nudge_handler_;
591 scoped_ptr<ModelTypeRegistry> model_type_registry_;
592 scoped_ptr<SyncSchedulerImpl> scheduler_;
593 scoped_ptr<SyncSessionContext> context_;
594 bool saw_syncer_event_;
595 base::TimeDelta last_short_poll_interval_received_;
596 base::TimeDelta last_long_poll_interval_received_;
597 base::TimeDelta last_sessions_commit_delay_;
598 base::TimeDelta last_bookmarks_commit_delay_;
599 int last_client_invalidation_hint_buffer_size_;
600 std::vector<scoped_refptr<ModelSafeWorker> > workers_;
602 ModelTypeSet enabled_datatypes_;
603 sessions::NudgeTracker nudge_tracker_;
604 scoped_ptr<MockDebugInfoGetter> debug_info_getter_;
606 DISALLOW_COPY_AND_ASSIGN(SyncerTest);
609 TEST_F(SyncerTest, TestCallGatherUnsyncedEntries) {
611 Syncer::UnsyncedMetaHandles handles;
613 syncable::ReadTransaction trans(FROM_HERE, directory());
614 GetUnsyncedEntries(&trans, &handles);
616 ASSERT_EQ(0u, handles.size());
618 // TODO(sync): When we can dynamically connect and disconnect the mock
619 // ServerConnectionManager test disconnected GetUnsyncedEntries here. It's a
620 // regression for a very old bug.
623 TEST_F(SyncerTest, GetCommitIdsFiltersThrottledEntries) {
624 const ModelTypeSet throttled_types(BOOKMARKS);
625 sync_pb::EntitySpecifics bookmark_data;
626 AddDefaultFieldValue(BOOKMARKS, &bookmark_data);
628 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
629 foreign_cache_guid(), "-1");
630 EXPECT_TRUE(SyncShareNudge());
633 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
634 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
635 ASSERT_TRUE(A.good());
636 A.PutIsUnsynced(true);
637 A.PutSpecifics(bookmark_data);
638 A.PutNonUniqueName("bookmark");
641 // Now sync without enabling bookmarks.
642 mock_server_->ExpectGetUpdatesRequestTypes(
643 Difference(context_->GetEnabledTypes(), ModelTypeSet(BOOKMARKS)));
644 ResetSession();
645 syncer_->NormalSyncShare(
646 Difference(context_->GetEnabledTypes(), ModelTypeSet(BOOKMARKS)),
647 &nudge_tracker_, session_.get());
650 // Nothing should have been committed as bookmarks is throttled.
651 syncable::ReadTransaction rtrans(FROM_HERE, directory());
652 Entry entryA(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1));
653 ASSERT_TRUE(entryA.good());
654 EXPECT_TRUE(entryA.GetIsUnsynced());
657 // Sync again with bookmarks enabled.
658 mock_server_->ExpectGetUpdatesRequestTypes(context_->GetEnabledTypes());
659 EXPECT_TRUE(SyncShareNudge());
661 // It should have been committed.
662 syncable::ReadTransaction rtrans(FROM_HERE, directory());
663 Entry entryA(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1));
664 ASSERT_TRUE(entryA.good());
665 EXPECT_FALSE(entryA.GetIsUnsynced());
669 // We use a macro so we can preserve the error location.
670 #define VERIFY_ENTRY(id, is_unapplied, is_unsynced, prev_initialized, \
671 parent_id, version, server_version, id_fac, rtrans) \
672 do { \
673 Entry entryA(rtrans, syncable::GET_BY_ID, id_fac.FromNumber(id)); \
674 ASSERT_TRUE(entryA.good()); \
675 /* We don't use EXPECT_EQ here because when the left side param is false,
676 gcc 4.6 warns about converting 'false' to pointer type for argument 1. */ \
677 EXPECT_TRUE(is_unsynced == entryA.GetIsUnsynced()); \
678 EXPECT_TRUE(is_unapplied == entryA.GetIsUnappliedUpdate()); \
679 EXPECT_TRUE(prev_initialized == \
680 IsRealDataType(GetModelTypeFromSpecifics( \
681 entryA.GetBaseServerSpecifics()))); \
682 EXPECT_TRUE(parent_id == -1 || \
683 entryA.GetParentId()== id_fac.FromNumber(parent_id)); \
684 EXPECT_EQ(version, entryA.GetBaseVersion()); \
685 EXPECT_EQ(server_version, entryA.GetServerVersion()); \
686 } while (0)
688 TEST_F(SyncerTest, GetCommitIdsFiltersUnreadyEntries) {
689 KeyParams key_params = {"localhost", "dummy", "foobar"};
690 KeyParams other_params = {"localhost", "dummy", "foobar2"};
691 sync_pb::EntitySpecifics bookmark, encrypted_bookmark;
692 bookmark.mutable_bookmark()->set_url("url");
693 bookmark.mutable_bookmark()->set_title("title");
694 AddDefaultFieldValue(BOOKMARKS, &encrypted_bookmark);
695 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
696 foreign_cache_guid(), "-1");
697 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10,
698 foreign_cache_guid(), "-2");
699 mock_server_->AddUpdateDirectory(3, 0, "C", 10, 10,
700 foreign_cache_guid(), "-3");
701 mock_server_->AddUpdateDirectory(4, 0, "D", 10, 10,
702 foreign_cache_guid(), "-4");
703 EXPECT_TRUE(SyncShareNudge());
704 // Server side change will put A in conflict.
705 mock_server_->AddUpdateDirectory(1, 0, "A", 20, 20,
706 foreign_cache_guid(), "-1");
708 // Mark bookmarks as encrypted and set the cryptographer to have pending
709 // keys.
710 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
711 Cryptographer other_cryptographer(&encryptor_);
712 other_cryptographer.AddKey(other_params);
713 sync_pb::EntitySpecifics specifics;
714 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
715 other_cryptographer.GetKeys(nigori->mutable_encryption_keybag());
716 dir_maker_.encryption_handler()->EnableEncryptEverything();
717 // Set up with an old passphrase, but have pending keys
718 GetCryptographer(&wtrans)->AddKey(key_params);
719 GetCryptographer(&wtrans)->Encrypt(bookmark,
720 encrypted_bookmark.mutable_encrypted());
721 GetCryptographer(&wtrans)->SetPendingKeys(nigori->encryption_keybag());
723 // In conflict but properly encrypted.
724 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
725 ASSERT_TRUE(A.good());
726 A.PutIsUnsynced(true);
727 A.PutSpecifics(encrypted_bookmark);
728 A.PutNonUniqueName(kEncryptedString);
729 // Not in conflict and properly encrypted.
730 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
731 ASSERT_TRUE(B.good());
732 B.PutIsUnsynced(true);
733 B.PutSpecifics(encrypted_bookmark);
734 B.PutNonUniqueName(kEncryptedString);
735 // Unencrypted specifics.
736 MutableEntry C(&wtrans, GET_BY_ID, ids_.FromNumber(3));
737 ASSERT_TRUE(C.good());
738 C.PutIsUnsynced(true);
739 C.PutNonUniqueName(kEncryptedString);
740 // Unencrypted non_unique_name.
741 MutableEntry D(&wtrans, GET_BY_ID, ids_.FromNumber(4));
742 ASSERT_TRUE(D.good());
743 D.PutIsUnsynced(true);
744 D.PutSpecifics(encrypted_bookmark);
745 D.PutNonUniqueName("not encrypted");
747 EXPECT_TRUE(SyncShareNudge());
749 // Nothing should have commited due to bookmarks being encrypted and
750 // the cryptographer having pending keys. A would have been resolved
751 // as a simple conflict, but still be unsynced until the next sync cycle.
752 syncable::ReadTransaction rtrans(FROM_HERE, directory());
753 VERIFY_ENTRY(1, false, true, false, 0, 20, 20, ids_, &rtrans);
754 VERIFY_ENTRY(2, false, true, false, 0, 10, 10, ids_, &rtrans);
755 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_, &rtrans);
756 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_, &rtrans);
758 // Resolve the pending keys.
759 GetCryptographer(&rtrans)->DecryptPendingKeys(other_params);
761 EXPECT_TRUE(SyncShareNudge());
763 // All properly encrypted and non-conflicting items should commit. "A" was
764 // conflicting, but last sync cycle resolved it as simple conflict, so on
765 // this sync cycle it committed succesfullly.
766 syncable::ReadTransaction rtrans(FROM_HERE, directory());
767 // Committed successfully.
768 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_, &rtrans);
769 // Committed successfully.
770 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_, &rtrans);
771 // Was not properly encrypted.
772 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_, &rtrans);
773 // Was not properly encrypted.
774 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_, &rtrans);
777 // Fix the remaining items.
778 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
779 MutableEntry C(&wtrans, GET_BY_ID, ids_.FromNumber(3));
780 ASSERT_TRUE(C.good());
781 C.PutSpecifics(encrypted_bookmark);
782 C.PutNonUniqueName(kEncryptedString);
783 MutableEntry D(&wtrans, GET_BY_ID, ids_.FromNumber(4));
784 ASSERT_TRUE(D.good());
785 D.PutSpecifics(encrypted_bookmark);
786 D.PutNonUniqueName(kEncryptedString);
788 EXPECT_TRUE(SyncShareNudge());
790 const StatusController& status_controller = session_->status_controller();
791 // Expect success.
792 EXPECT_EQ(status_controller.model_neutral_state().commit_result, SYNCER_OK);
793 // None should be unsynced anymore.
794 syncable::ReadTransaction rtrans(FROM_HERE, directory());
795 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_, &rtrans);
796 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_, &rtrans);
797 VERIFY_ENTRY(3, false, false, false, 0, 11, 11, ids_, &rtrans);
798 VERIFY_ENTRY(4, false, false, false, 0, 11, 11, ids_, &rtrans);
802 TEST_F(SyncerTest, GetUpdatesPartialThrottled) {
803 sync_pb::EntitySpecifics bookmark, pref;
804 bookmark.mutable_bookmark()->set_title("title");
805 pref.mutable_preference()->set_name("name");
806 AddDefaultFieldValue(BOOKMARKS, &bookmark);
807 AddDefaultFieldValue(PREFERENCES, &pref);
809 // Normal sync, all the data types should get synced.
810 mock_server_->AddUpdateSpecifics(1, 0, "A", 10, 10, true, 0, bookmark,
811 foreign_cache_guid(), "-1");
812 mock_server_->AddUpdateSpecifics(2, 1, "B", 10, 10, false, 2, bookmark,
813 foreign_cache_guid(), "-2");
814 mock_server_->AddUpdateSpecifics(3, 1, "C", 10, 10, false, 1, bookmark,
815 foreign_cache_guid(), "-3");
816 mock_server_->AddUpdateSpecifics(4, 0, "D", 10, 10, false, 0, pref);
818 EXPECT_TRUE(SyncShareNudge());
820 // Initial state. Everything is normal.
821 syncable::ReadTransaction rtrans(FROM_HERE, directory());
822 VERIFY_ENTRY(1, false, false, false, 0, 10, 10, ids_, &rtrans);
823 VERIFY_ENTRY(2, false, false, false, 1, 10, 10, ids_, &rtrans);
824 VERIFY_ENTRY(3, false, false, false, 1, 10, 10, ids_, &rtrans);
825 VERIFY_ENTRY(4, false, false, false, 0, 10, 10, ids_, &rtrans);
828 // Set BOOKMARKS throttled but PREFERENCES not,
829 // then BOOKMARKS should not get synced but PREFERENCES should.
830 ModelTypeSet throttled_types(BOOKMARKS);
831 mock_server_->set_partial_throttling(true);
832 mock_server_->SetThrottledTypes(throttled_types);
834 mock_server_->AddUpdateSpecifics(1, 0, "E", 20, 20, true, 0, bookmark,
835 foreign_cache_guid(), "-1");
836 mock_server_->AddUpdateSpecifics(2, 1, "F", 20, 20, false, 2, bookmark,
837 foreign_cache_guid(), "-2");
838 mock_server_->AddUpdateSpecifics(3, 1, "G", 20, 20, false, 1, bookmark,
839 foreign_cache_guid(), "-3");
840 mock_server_->AddUpdateSpecifics(4, 0, "H", 20, 20, false, 0, pref);
842 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
843 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
844 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
845 MutableEntry C(&wtrans, GET_BY_ID, ids_.FromNumber(3));
846 MutableEntry D(&wtrans, GET_BY_ID, ids_.FromNumber(4));
847 A.PutIsUnsynced(true);
848 B.PutIsUnsynced(true);
849 C.PutIsUnsynced(true);
850 D.PutIsUnsynced(true);
852 EXPECT_TRUE(SyncShareNudge());
854 // BOOKMARKS throttled.
855 syncable::ReadTransaction rtrans(FROM_HERE, directory());
856 VERIFY_ENTRY(1, false, true, false, 0, 10, 10, ids_, &rtrans);
857 VERIFY_ENTRY(2, false, true, false, 1, 10, 10, ids_, &rtrans);
858 VERIFY_ENTRY(3, false, true, false, 1, 10, 10, ids_, &rtrans);
859 VERIFY_ENTRY(4, false, false, false, 0, 21, 21, ids_, &rtrans);
862 // Unthrottled BOOKMARKS, then BOOKMARKS should get synced now.
863 mock_server_->set_partial_throttling(false);
865 mock_server_->AddUpdateSpecifics(1, 0, "E", 30, 30, true, 0, bookmark,
866 foreign_cache_guid(), "-1");
867 mock_server_->AddUpdateSpecifics(2, 1, "F", 30, 30, false, 2, bookmark,
868 foreign_cache_guid(), "-2");
869 mock_server_->AddUpdateSpecifics(3, 1, "G", 30, 30, false, 1, bookmark,
870 foreign_cache_guid(), "-3");
871 mock_server_->AddUpdateSpecifics(4, 0, "H", 30, 30, false, 0, pref);
872 EXPECT_TRUE(SyncShareNudge());
874 // BOOKMARKS unthrottled.
875 syncable::ReadTransaction rtrans(FROM_HERE, directory());
876 VERIFY_ENTRY(1, false, false, false, 0, 31, 31, ids_, &rtrans);
877 VERIFY_ENTRY(2, false, false, false, 1, 31, 31, ids_, &rtrans);
878 VERIFY_ENTRY(3, false, false, false, 1, 31, 31, ids_, &rtrans);
879 VERIFY_ENTRY(4, false, false, false, 0, 30, 30, ids_, &rtrans);
883 // This test uses internal knowledge of the directory to test correctness of
884 // GetCommitIds. In almost every other test, the hierarchy is created from
885 // parent to child order, and so parents always have metahandles that are
886 // smaller than those of their children. This makes it very difficult to test
887 // some GetCommitIds edge cases, since it uses metahandle ordering as
888 // a starting point.
889 TEST_F(SyncerTest, GetCommitIds_VerifyDeletionCommitOrder) {
891 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
893 // Create four bookmarks folders at the root node.
894 for (int i = 1; i < 5; ++i) {
895 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "");
896 entry.PutId(ids_.FromNumber(i));
897 entry.PutIsDir(true);
898 entry.PutBaseVersion(5);
899 entry.PutServerVersion(5);
900 entry.PutServerParentId(trans.root_id());
901 entry.PutServerIsDir(true);
902 entry.PutIsUnsynced(true);
903 entry.PutSpecifics(DefaultBookmarkSpecifics());
906 // Now iterate in reverse order make a hierarchy of them.
907 // While we're at it, also mark them as deleted.
908 syncable::Id parent_id = trans.root_id();
909 for (int i = 4; i > 0; --i) {
910 MutableEntry entry(&trans, GET_BY_ID, ids_.FromNumber(i));
911 entry.PutParentId(parent_id);
912 entry.PutServerParentId(parent_id);
913 entry.PutIsDel(true);
914 parent_id = ids_.FromNumber(i);
919 // Run GetCommitIds, the function being tested.
920 syncable::Directory::Metahandles result_handles;
921 syncable::ReadTransaction trans(FROM_HERE, directory());
922 GetCommitIdsForType(&trans, BOOKMARKS, 100, &result_handles);
924 // Now verify the output. We expect four results in child to parent order.
925 ASSERT_EQ(4U, result_handles.size());
927 Entry entry0(&trans, GET_BY_HANDLE, result_handles[0]);
928 EXPECT_EQ(ids_.FromNumber(1), entry0.GetId());
930 Entry entry1(&trans, GET_BY_HANDLE, result_handles[1]);
931 EXPECT_EQ(ids_.FromNumber(2), entry1.GetId());
933 Entry entry2(&trans, GET_BY_HANDLE, result_handles[2]);
934 EXPECT_EQ(ids_.FromNumber(3), entry2.GetId());
936 Entry entry3(&trans, GET_BY_HANDLE, result_handles[3]);
937 EXPECT_EQ(ids_.FromNumber(4), entry3.GetId());
941 // Verify that if there are more deleted items than the maximum number of
942 // entries, child to parent order is still preserved.
943 TEST_F(SyncerTest, GetCommitIds_VerifyDeletionCommitOrderMaxEntries) {
945 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
947 // Create a bookmark tree with one root, two second level, and three third
948 // level bookmarks, all folders.
949 for (int i = 1; i <= 6; ++i) {
950 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "");
951 entry.PutId(ids_.FromNumber(i));
952 entry.PutIsDir(true);
953 entry.PutBaseVersion(5);
954 entry.PutServerVersion(5);
955 entry.PutParentId(ids_.FromNumber(i/2));
956 entry.PutServerParentId(ids_.FromNumber(i/2));
957 entry.PutServerIsDir(true);
958 entry.PutIsUnsynced(true);
959 entry.PutSpecifics(DefaultBookmarkSpecifics());
960 entry.PutIsDel(true);
965 // Run GetCommitIds with a limit of 2 entries to commit.
966 syncable::Directory::Metahandles result_handles;
967 syncable::ReadTransaction trans(FROM_HERE, directory());
968 GetCommitIdsForType(&trans, BOOKMARKS, 2, &result_handles);
970 // Now verify the output. We expect two results in child to parent order
971 // (descending id order).
972 ASSERT_EQ(2U, result_handles.size());
974 Entry entry0(&trans, GET_BY_HANDLE, result_handles[0]);
975 EXPECT_EQ(ids_.FromNumber(6), entry0.GetId());
977 Entry entry1(&trans, GET_BY_HANDLE, result_handles[1]);
978 EXPECT_EQ(ids_.FromNumber(5), entry1.GetId());
982 TEST_F(SyncerTest, EncryptionAwareConflicts) {
983 KeyParams key_params = {"localhost", "dummy", "foobar"};
984 Cryptographer other_cryptographer(&encryptor_);
985 other_cryptographer.AddKey(key_params);
986 sync_pb::EntitySpecifics bookmark, encrypted_bookmark, modified_bookmark;
987 bookmark.mutable_bookmark()->set_title("title");
988 other_cryptographer.Encrypt(bookmark,
989 encrypted_bookmark.mutable_encrypted());
990 AddDefaultFieldValue(BOOKMARKS, &encrypted_bookmark);
991 modified_bookmark.mutable_bookmark()->set_title("title2");
992 other_cryptographer.Encrypt(modified_bookmark,
993 modified_bookmark.mutable_encrypted());
994 sync_pb::EntitySpecifics pref, encrypted_pref, modified_pref;
995 pref.mutable_preference()->set_name("name");
996 AddDefaultFieldValue(PREFERENCES, &encrypted_pref);
997 other_cryptographer.Encrypt(pref,
998 encrypted_pref.mutable_encrypted());
999 modified_pref.mutable_preference()->set_name("name2");
1000 other_cryptographer.Encrypt(modified_pref,
1001 modified_pref.mutable_encrypted());
1003 // Mark bookmarks and preferences as encrypted and set the cryptographer to
1004 // have pending keys.
1005 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1006 sync_pb::EntitySpecifics specifics;
1007 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
1008 other_cryptographer.GetKeys(nigori->mutable_encryption_keybag());
1009 dir_maker_.encryption_handler()->EnableEncryptEverything();
1010 GetCryptographer(&wtrans)->SetPendingKeys(nigori->encryption_keybag());
1011 EXPECT_TRUE(GetCryptographer(&wtrans)->has_pending_keys());
1014 // We need to remember the exact position of our local items, so we can
1015 // make updates that do not modify those positions.
1016 UniquePosition pos1;
1017 UniquePosition pos2;
1018 UniquePosition pos3;
1020 mock_server_->AddUpdateSpecifics(1, 0, "A", 10, 10, true, 0, bookmark,
1021 foreign_cache_guid(), "-1");
1022 mock_server_->AddUpdateSpecifics(2, 1, "B", 10, 10, false, 2, bookmark,
1023 foreign_cache_guid(), "-2");
1024 mock_server_->AddUpdateSpecifics(3, 1, "C", 10, 10, false, 1, bookmark,
1025 foreign_cache_guid(), "-3");
1026 mock_server_->AddUpdateSpecifics(4, 0, "D", 10, 10, false, 0, pref);
1027 EXPECT_TRUE(SyncShareNudge());
1029 // Initial state. Everything is normal.
1030 syncable::ReadTransaction rtrans(FROM_HERE, directory());
1031 VERIFY_ENTRY(1, false, false, false, 0, 10, 10, ids_, &rtrans);
1032 VERIFY_ENTRY(2, false, false, false, 1, 10, 10, ids_, &rtrans);
1033 VERIFY_ENTRY(3, false, false, false, 1, 10, 10, ids_, &rtrans);
1034 VERIFY_ENTRY(4, false, false, false, 0, 10, 10, ids_, &rtrans);
1036 Entry entry1(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1));
1037 ASSERT_TRUE(entry1.GetUniquePosition().Equals(
1038 entry1.GetServerUniquePosition()));
1039 pos1 = entry1.GetUniquePosition();
1040 Entry entry2(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(2));
1041 pos2 = entry2.GetUniquePosition();
1042 Entry entry3(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(3));
1043 pos3 = entry3.GetUniquePosition();
1046 // Server side encryption will not be applied due to undecryptable data.
1047 // At this point, BASE_SERVER_SPECIFICS should be filled for all four items.
1048 mock_server_->AddUpdateSpecifics(1, 0, kEncryptedString, 20, 20, true, 0,
1049 encrypted_bookmark,
1050 foreign_cache_guid(), "-1");
1051 mock_server_->AddUpdateSpecifics(2, 1, kEncryptedString, 20, 20, false, 2,
1052 encrypted_bookmark,
1053 foreign_cache_guid(), "-2");
1054 mock_server_->AddUpdateSpecifics(3, 1, kEncryptedString, 20, 20, false, 1,
1055 encrypted_bookmark,
1056 foreign_cache_guid(), "-3");
1057 mock_server_->AddUpdateSpecifics(4, 0, kEncryptedString, 20, 20, false, 0,
1058 encrypted_pref,
1059 foreign_cache_guid(), "-4");
1060 EXPECT_TRUE(SyncShareNudge());
1062 // All should be unapplied due to being undecryptable and have a valid
1063 // BASE_SERVER_SPECIFICS.
1064 syncable::ReadTransaction rtrans(FROM_HERE, directory());
1065 VERIFY_ENTRY(1, true, false, true, 0, 10, 20, ids_, &rtrans);
1066 VERIFY_ENTRY(2, true, false, true, 1, 10, 20, ids_, &rtrans);
1067 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_, &rtrans);
1068 VERIFY_ENTRY(4, true, false, true, 0, 10, 20, ids_, &rtrans);
1071 // Server side change that don't modify anything should not affect
1072 // BASE_SERVER_SPECIFICS (such as name changes and mtime changes).
1073 mock_server_->AddUpdateSpecifics(1, 0, kEncryptedString, 30, 30, true, 0,
1074 encrypted_bookmark,
1075 foreign_cache_guid(), "-1");
1076 mock_server_->AddUpdateSpecifics(2, 1, kEncryptedString, 30, 30, false, 2,
1077 encrypted_bookmark,
1078 foreign_cache_guid(), "-2");
1079 // Item 3 doesn't change.
1080 mock_server_->AddUpdateSpecifics(4, 0, kEncryptedString, 30, 30, false, 0,
1081 encrypted_pref,
1082 foreign_cache_guid(), "-4");
1083 EXPECT_TRUE(SyncShareNudge());
1085 // Items 1, 2, and 4 should have newer server versions, 3 remains the same.
1086 // All should remain unapplied due to be undecryptable.
1087 syncable::ReadTransaction rtrans(FROM_HERE, directory());
1088 VERIFY_ENTRY(1, true, false, true, 0, 10, 30, ids_, &rtrans);
1089 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_, &rtrans);
1090 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_, &rtrans);
1091 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_, &rtrans);
1094 // Positional changes, parent changes, and specifics changes should reset
1095 // BASE_SERVER_SPECIFICS.
1096 // Became unencrypted.
1097 mock_server_->AddUpdateSpecifics(1, 0, "A", 40, 40, true, 0, bookmark,
1098 foreign_cache_guid(), "-1");
1099 // Reordered to after item 2.
1100 mock_server_->AddUpdateSpecifics(3, 1, kEncryptedString, 30, 30, false, 3,
1101 encrypted_bookmark,
1102 foreign_cache_guid(), "-3");
1103 EXPECT_TRUE(SyncShareNudge());
1105 // Items 2 and 4 should be the only ones with BASE_SERVER_SPECIFICS set.
1106 // Items 1 is now unencrypted, so should have applied normally.
1107 syncable::ReadTransaction rtrans(FROM_HERE, directory());
1108 VERIFY_ENTRY(1, false, false, false, 0, 40, 40, ids_, &rtrans);
1109 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_, &rtrans);
1110 VERIFY_ENTRY(3, true, false, false, 1, 10, 30, ids_, &rtrans);
1111 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_, &rtrans);
1114 // Make local changes, which should remain unsynced for items 2, 3, 4.
1116 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1117 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
1118 ASSERT_TRUE(A.good());
1119 A.PutSpecifics(modified_bookmark);
1120 A.PutNonUniqueName(kEncryptedString);
1121 A.PutIsUnsynced(true);
1122 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
1123 ASSERT_TRUE(B.good());
1124 B.PutSpecifics(modified_bookmark);
1125 B.PutNonUniqueName(kEncryptedString);
1126 B.PutIsUnsynced(true);
1127 MutableEntry C(&wtrans, GET_BY_ID, ids_.FromNumber(3));
1128 ASSERT_TRUE(C.good());
1129 C.PutSpecifics(modified_bookmark);
1130 C.PutNonUniqueName(kEncryptedString);
1131 C.PutIsUnsynced(true);
1132 MutableEntry D(&wtrans, GET_BY_ID, ids_.FromNumber(4));
1133 ASSERT_TRUE(D.good());
1134 D.PutSpecifics(modified_pref);
1135 D.PutNonUniqueName(kEncryptedString);
1136 D.PutIsUnsynced(true);
1138 EXPECT_TRUE(SyncShareNudge());
1140 // Item 1 remains unsynced due to there being pending keys.
1141 // Items 2, 3, 4 should remain unsynced since they were not up to date.
1142 syncable::ReadTransaction rtrans(FROM_HERE, directory());
1143 VERIFY_ENTRY(1, false, true, false, 0, 40, 40, ids_, &rtrans);
1144 VERIFY_ENTRY(2, true, true, true, 1, 10, 30, ids_, &rtrans);
1145 VERIFY_ENTRY(3, true, true, false, 1, 10, 30, ids_, &rtrans);
1146 VERIFY_ENTRY(4, true, true, true, 0, 10, 30, ids_, &rtrans);
1150 syncable::ReadTransaction rtrans(FROM_HERE, directory());
1151 // Resolve the pending keys.
1152 GetCryptographer(&rtrans)->DecryptPendingKeys(key_params);
1154 // First cycle resolves conflicts, second cycle commits changes.
1155 EXPECT_TRUE(SyncShareNudge());
1156 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS).num_server_overwrites);
1157 EXPECT_EQ(1, GetUpdateCounters(PREFERENCES).num_server_overwrites);
1158 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS).num_local_overwrites);
1160 // We successfully commited item(s).
1161 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS).num_commits_attempted);
1162 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS).num_commits_success);
1163 EXPECT_EQ(1, GetCommitCounters(PREFERENCES).num_commits_attempted);
1164 EXPECT_EQ(1, GetCommitCounters(PREFERENCES).num_commits_success);
1166 EXPECT_TRUE(SyncShareNudge());
1168 // Everything should be resolved now. The local changes should have
1169 // overwritten the server changes for 2 and 4, while the server changes
1170 // overwrote the local for entry 3.
1172 // Expect there will be no new overwrites.
1173 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS).num_server_overwrites);
1174 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS).num_local_overwrites);
1176 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS).num_commits_success);
1177 EXPECT_EQ(1, GetCommitCounters(PREFERENCES).num_commits_success);
1179 syncable::ReadTransaction rtrans(FROM_HERE, directory());
1180 VERIFY_ENTRY(1, false, false, false, 0, 41, 41, ids_, &rtrans);
1181 VERIFY_ENTRY(2, false, false, false, 1, 31, 31, ids_, &rtrans);
1182 VERIFY_ENTRY(3, false, false, false, 1, 30, 30, ids_, &rtrans);
1183 VERIFY_ENTRY(4, false, false, false, 0, 31, 31, ids_, &rtrans);
1186 #undef VERIFY_ENTRY
1188 TEST_F(SyncerTest, TestGetUnsyncedAndSimpleCommit) {
1190 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1191 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Pete");
1192 ASSERT_TRUE(parent.good());
1193 parent.PutIsUnsynced(true);
1194 parent.PutIsDir(true);
1195 parent.PutSpecifics(DefaultBookmarkSpecifics());
1196 parent.PutBaseVersion(1);
1197 parent.PutId(parent_id_);
1198 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent_id_, "Pete");
1199 ASSERT_TRUE(child.good());
1200 child.PutId(child_id_);
1201 child.PutBaseVersion(1);
1202 WriteTestDataToEntry(&wtrans, &child);
1205 EXPECT_TRUE(SyncShareNudge());
1206 ASSERT_EQ(2u, mock_server_->committed_ids().size());
1207 // If this test starts failing, be aware other sort orders could be valid.
1208 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
1209 EXPECT_TRUE(child_id_ == mock_server_->committed_ids()[1]);
1211 syncable::ReadTransaction rt(FROM_HERE, directory());
1212 Entry entry(&rt, syncable::GET_BY_ID, child_id_);
1213 ASSERT_TRUE(entry.good());
1214 VerifyTestDataInEntry(&rt, &entry);
1218 TEST_F(SyncerTest, TestPurgeWhileUnsynced) {
1219 // Similar to above, but throw a purge operation into the mix. Bug 49278.
1220 syncable::Id pref_node_id = TestIdFactory::MakeServer("Tim");
1222 directory()->SetDownloadProgress(BOOKMARKS,
1223 syncable::BuildProgress(BOOKMARKS));
1224 directory()->SetDownloadProgress(PREFERENCES,
1225 syncable::BuildProgress(PREFERENCES));
1226 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1227 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Pete");
1228 ASSERT_TRUE(parent.good());
1229 parent.PutIsUnsynced(true);
1230 parent.PutIsDir(true);
1231 parent.PutSpecifics(DefaultBookmarkSpecifics());
1232 parent.PutBaseVersion(1);
1233 parent.PutId(parent_id_);
1234 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent_id_, "Pete");
1235 ASSERT_TRUE(child.good());
1236 child.PutId(child_id_);
1237 child.PutBaseVersion(1);
1238 WriteTestDataToEntry(&wtrans, &child);
1240 MutableEntry parent2(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Tim");
1241 ASSERT_TRUE(parent2.good());
1242 parent2.PutIsUnsynced(true);
1243 parent2.PutIsDir(true);
1244 parent2.PutSpecifics(DefaultPreferencesSpecifics());
1245 parent2.PutBaseVersion(1);
1246 parent2.PutId(pref_node_id);
1249 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES),
1250 ModelTypeSet(),
1251 ModelTypeSet());
1253 EXPECT_TRUE(SyncShareNudge());
1254 ASSERT_EQ(2U, mock_server_->committed_ids().size());
1255 // If this test starts failing, be aware other sort orders could be valid.
1256 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
1257 EXPECT_TRUE(child_id_ == mock_server_->committed_ids()[1]);
1259 syncable::ReadTransaction rt(FROM_HERE, directory());
1260 Entry entry(&rt, syncable::GET_BY_ID, child_id_);
1261 ASSERT_TRUE(entry.good());
1262 VerifyTestDataInEntry(&rt, &entry);
1264 directory()->SaveChanges();
1266 syncable::ReadTransaction rt(FROM_HERE, directory());
1267 Entry entry(&rt, syncable::GET_BY_ID, pref_node_id);
1268 ASSERT_FALSE(entry.good());
1272 TEST_F(SyncerTest, TestPurgeWhileUnapplied) {
1273 // Similar to above, but for unapplied items. Bug 49278.
1275 directory()->SetDownloadProgress(BOOKMARKS,
1276 syncable::BuildProgress(BOOKMARKS));
1277 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1278 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Pete");
1279 ASSERT_TRUE(parent.good());
1280 parent.PutIsUnappliedUpdate(true);
1281 parent.PutIsDir(true);
1282 parent.PutSpecifics(DefaultBookmarkSpecifics());
1283 parent.PutBaseVersion(1);
1284 parent.PutId(parent_id_);
1287 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(BOOKMARKS),
1288 ModelTypeSet(),
1289 ModelTypeSet());
1291 EXPECT_TRUE(SyncShareNudge());
1292 directory()->SaveChanges();
1294 syncable::ReadTransaction rt(FROM_HERE, directory());
1295 Entry entry(&rt, syncable::GET_BY_ID, parent_id_);
1296 ASSERT_FALSE(entry.good());
1300 TEST_F(SyncerTest, TestPurgeWithJournal) {
1302 directory()->SetDownloadProgress(BOOKMARKS,
1303 syncable::BuildProgress(BOOKMARKS));
1304 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1305 MutableEntry parent(&wtrans, syncable::CREATE, BOOKMARKS, wtrans.root_id(),
1306 "Pete");
1307 ASSERT_TRUE(parent.good());
1308 parent.PutIsDir(true);
1309 parent.PutSpecifics(DefaultBookmarkSpecifics());
1310 parent.PutBaseVersion(1);
1311 parent.PutId(parent_id_);
1312 MutableEntry child(&wtrans, syncable::CREATE, BOOKMARKS, parent_id_,
1313 "Pete");
1314 ASSERT_TRUE(child.good());
1315 child.PutId(child_id_);
1316 child.PutBaseVersion(1);
1317 WriteTestDataToEntry(&wtrans, &child);
1319 MutableEntry parent2(&wtrans, syncable::CREATE, PREFERENCES,
1320 wtrans.root_id(), "Tim");
1321 ASSERT_TRUE(parent2.good());
1322 parent2.PutIsDir(true);
1323 parent2.PutSpecifics(DefaultPreferencesSpecifics());
1324 parent2.PutBaseVersion(1);
1325 parent2.PutId(TestIdFactory::MakeServer("Tim"));
1328 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES, BOOKMARKS),
1329 ModelTypeSet(BOOKMARKS),
1330 ModelTypeSet());
1332 // Verify bookmark nodes are saved in delete journal but not preference
1333 // node.
1334 syncable::ReadTransaction rt(FROM_HERE, directory());
1335 syncable::DeleteJournal* delete_journal = directory()->delete_journal();
1336 EXPECT_EQ(2u, delete_journal->GetDeleteJournalSize(&rt));
1337 syncable::EntryKernelSet journal_entries;
1338 directory()->delete_journal()->GetDeleteJournals(&rt, BOOKMARKS,
1339 &journal_entries);
1340 EXPECT_EQ(parent_id_, (*journal_entries.begin())->ref(syncable::ID));
1341 EXPECT_EQ(child_id_, (*journal_entries.rbegin())->ref(syncable::ID));
1345 TEST_F(SyncerTest, ResetVersions) {
1346 // Download some pref items.
1347 mock_server_->AddUpdatePref("id1", "", "tag1", 20, 20);
1348 mock_server_->AddUpdatePref("id2", "", "tag2", 30, 30);
1349 mock_server_->AddUpdatePref("id3", "", "tag3", 40, 40);
1350 EXPECT_TRUE(SyncShareNudge());
1353 // Modify one of the preferences locally, mark another one as unapplied,
1354 // and create another unsynced preference.
1355 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1356 MutableEntry entry(&wtrans, GET_BY_CLIENT_TAG, "tag1");
1357 entry.PutIsUnsynced(true);
1359 MutableEntry entry2(&wtrans, GET_BY_CLIENT_TAG, "tag2");
1360 entry2.PutIsUnappliedUpdate(true);
1362 MutableEntry entry4(&wtrans, CREATE, PREFERENCES, "name");
1363 entry4.PutUniqueClientTag("tag4");
1364 entry4.PutIsUnsynced(true);
1368 // Reset the versions.
1369 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1370 ASSERT_TRUE(directory()->ResetVersionsForType(&wtrans, PREFERENCES));
1374 // Verify the synced items are all with version 1 now, with
1375 // unsynced/unapplied state preserved.
1376 syncable::ReadTransaction trans(FROM_HERE, directory());
1377 Entry entry(&trans, GET_BY_CLIENT_TAG, "tag1");
1378 EXPECT_EQ(1, entry.GetBaseVersion());
1379 EXPECT_EQ(1, entry.GetServerVersion());
1380 EXPECT_TRUE(entry.GetIsUnsynced());
1381 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
1382 Entry entry2(&trans, GET_BY_CLIENT_TAG, "tag2");
1383 EXPECT_EQ(1, entry2.GetBaseVersion());
1384 EXPECT_EQ(1, entry2.GetServerVersion());
1385 EXPECT_FALSE(entry2.GetIsUnsynced());
1386 EXPECT_TRUE(entry2.GetIsUnappliedUpdate());
1387 Entry entry3(&trans, GET_BY_CLIENT_TAG, "tag3");
1388 EXPECT_EQ(1, entry3.GetBaseVersion());
1389 EXPECT_EQ(1, entry3.GetServerVersion());
1390 EXPECT_FALSE(entry3.GetIsUnsynced());
1391 EXPECT_FALSE(entry3.GetIsUnappliedUpdate());
1393 // Entry 4 (the locally created one) should remain the same.
1394 Entry entry4(&trans, GET_BY_CLIENT_TAG, "tag4");
1395 EXPECT_EQ(-1, entry4.GetBaseVersion());
1396 EXPECT_EQ(0, entry4.GetServerVersion());
1397 EXPECT_TRUE(entry4.GetIsUnsynced());
1398 EXPECT_FALSE(entry4.GetIsUnappliedUpdate());
1402 TEST_F(SyncerTest, TestCommitListOrderingTwoItemsTall) {
1403 CommitOrderingTest items[] = {
1404 {1, ids_.FromNumber(-1001), ids_.FromNumber(-1000)},
1405 {0, ids_.FromNumber(-1000), ids_.FromNumber(0)},
1406 CommitOrderingTest::MakeLastCommitItem(),
1408 RunCommitOrderingTest(items);
1411 TEST_F(SyncerTest, TestCommitListOrderingThreeItemsTall) {
1412 CommitOrderingTest items[] = {
1413 {1, ids_.FromNumber(-2001), ids_.FromNumber(-2000)},
1414 {0, ids_.FromNumber(-2000), ids_.FromNumber(0)},
1415 {2, ids_.FromNumber(-2002), ids_.FromNumber(-2001)},
1416 CommitOrderingTest::MakeLastCommitItem(),
1418 RunCommitOrderingTest(items);
1421 TEST_F(SyncerTest, TestCommitListOrderingFourItemsTall) {
1422 CommitOrderingTest items[] = {
1423 {3, ids_.FromNumber(-2003), ids_.FromNumber(-2002)},
1424 {1, ids_.FromNumber(-2001), ids_.FromNumber(-2000)},
1425 {0, ids_.FromNumber(-2000), ids_.FromNumber(0)},
1426 {2, ids_.FromNumber(-2002), ids_.FromNumber(-2001)},
1427 CommitOrderingTest::MakeLastCommitItem(),
1429 RunCommitOrderingTest(items);
1432 TEST_F(SyncerTest, TestCommitListOrderingThreeItemsTallLimitedSize) {
1433 context_->set_max_commit_batch_size(2);
1434 CommitOrderingTest items[] = {
1435 {1, ids_.FromNumber(-2001), ids_.FromNumber(-2000)},
1436 {0, ids_.FromNumber(-2000), ids_.FromNumber(0)},
1437 {2, ids_.FromNumber(-2002), ids_.FromNumber(-2001)},
1438 CommitOrderingTest::MakeLastCommitItem(),
1440 RunCommitOrderingTest(items);
1443 TEST_F(SyncerTest, TestCommitListOrderingSingleDeletedItem) {
1444 CommitOrderingTest items[] = {
1445 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED}},
1446 CommitOrderingTest::MakeLastCommitItem(),
1448 RunCommitOrderingTest(items);
1451 TEST_F(SyncerTest, TestCommitListOrderingSingleUncommittedDeletedItem) {
1452 CommitOrderingTest items[] = {
1453 {-1, ids_.FromNumber(-1000), ids_.FromNumber(0), {DELETED}},
1454 CommitOrderingTest::MakeLastCommitItem(),
1456 RunCommitOrderingTest(items);
1459 TEST_F(SyncerTest, TestCommitListOrderingSingleDeletedItemWithUnroll) {
1460 CommitOrderingTest items[] = {
1461 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED}},
1462 CommitOrderingTest::MakeLastCommitItem(),
1464 RunCommitOrderingTest(items);
1467 TEST_F(SyncerTest,
1468 TestCommitListOrderingSingleLongDeletedItemWithUnroll) {
1469 CommitOrderingTest items[] = {
1470 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1471 CommitOrderingTest::MakeLastCommitItem(),
1473 RunCommitOrderingTest(items);
1476 TEST_F(SyncerTest, TestCommitListOrderingTwoLongDeletedItemWithUnroll) {
1477 CommitOrderingTest items[] = {
1478 {1, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1479 {0, ids_.FromNumber(1001), ids_.FromNumber(1000), {DELETED, OLD_MTIME}},
1480 CommitOrderingTest::MakeLastCommitItem(),
1482 RunCommitOrderingTest(items);
1485 TEST_F(SyncerTest, TestCommitListOrdering3LongDeletedItemsWithSizeLimit) {
1486 context_->set_max_commit_batch_size(2);
1487 CommitOrderingTest items[] = {
1488 {2, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1489 {1, ids_.FromNumber(1001), ids_.FromNumber(1000), {DELETED, OLD_MTIME}},
1490 {0, ids_.FromNumber(1002), ids_.FromNumber(1001), {DELETED, OLD_MTIME}},
1491 CommitOrderingTest::MakeLastCommitItem(),
1493 RunCommitOrderingTest(items);
1496 TEST_F(SyncerTest, TestCommitListOrderingTwoDeletedItemsWithUnroll) {
1497 CommitOrderingTest items[] = {
1498 {1, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED}},
1499 {0, ids_.FromNumber(1001), ids_.FromNumber(1000), {DELETED}},
1500 CommitOrderingTest::MakeLastCommitItem(),
1502 RunCommitOrderingTest(items);
1505 TEST_F(SyncerTest, TestCommitListOrderingComplexDeletionScenario) {
1506 CommitOrderingTest items[] = {
1507 {2, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1508 {-1, ids_.FromNumber(1001), ids_.FromNumber(0), {SYNCED}},
1509 {1, ids_.FromNumber(1002), ids_.FromNumber(1001), {DELETED, OLD_MTIME}},
1510 {-1, ids_.FromNumber(1003), ids_.FromNumber(1001), {SYNCED}},
1511 {0, ids_.FromNumber(1004), ids_.FromNumber(1003), {DELETED}},
1512 CommitOrderingTest::MakeLastCommitItem(),
1514 RunCommitOrderingTest(items);
1517 TEST_F(SyncerTest,
1518 TestCommitListOrderingComplexDeletionScenarioWith2RecentDeletes) {
1519 CommitOrderingTest items[] = {
1520 {3, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1521 {-1, ids_.FromNumber(1001), ids_.FromNumber(0), {SYNCED}},
1522 {2, ids_.FromNumber(1002), ids_.FromNumber(1001), {DELETED, OLD_MTIME}},
1523 {-1, ids_.FromNumber(1003), ids_.FromNumber(1001), {SYNCED}},
1524 {1, ids_.FromNumber(1004), ids_.FromNumber(1003), {DELETED}},
1525 {0, ids_.FromNumber(1005), ids_.FromNumber(1003), {DELETED}},
1526 CommitOrderingTest::MakeLastCommitItem(),
1528 RunCommitOrderingTest(items);
1531 TEST_F(SyncerTest, TestCommitListOrderingDeleteMovedItems) {
1532 CommitOrderingTest items[] = {
1533 {1, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1534 {0, ids_.FromNumber(1001), ids_.FromNumber(1000), {DELETED, OLD_MTIME,
1535 MOVED_FROM_ROOT}},
1536 CommitOrderingTest::MakeLastCommitItem(),
1538 RunCommitOrderingTest(items);
1541 TEST_F(SyncerTest, TestCommitListOrderingWithNesting) {
1542 const base::Time& now_minus_2h =
1543 base::Time::Now() - base::TimeDelta::FromHours(2);
1545 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1547 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Bob");
1548 ASSERT_TRUE(parent.good());
1549 parent.PutIsUnsynced(true);
1550 parent.PutIsDir(true);
1551 parent.PutSpecifics(DefaultBookmarkSpecifics());
1552 parent.PutId(ids_.FromNumber(100));
1553 parent.PutBaseVersion(1);
1554 MutableEntry child(
1555 &wtrans, CREATE, BOOKMARKS, ids_.FromNumber(100), "Bob");
1556 ASSERT_TRUE(child.good());
1557 child.PutIsUnsynced(true);
1558 child.PutIsDir(true);
1559 child.PutSpecifics(DefaultBookmarkSpecifics());
1560 child.PutId(ids_.FromNumber(101));
1561 child.PutBaseVersion(1);
1562 MutableEntry grandchild(
1563 &wtrans, CREATE, BOOKMARKS, ids_.FromNumber(101), "Bob");
1564 ASSERT_TRUE(grandchild.good());
1565 grandchild.PutId(ids_.FromNumber(102));
1566 grandchild.PutIsUnsynced(true);
1567 grandchild.PutSpecifics(DefaultBookmarkSpecifics());
1568 grandchild.PutBaseVersion(1);
1571 // Create three deleted items which deletions we expect to be sent to the
1572 // server.
1573 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Pete");
1574 ASSERT_TRUE(parent.good());
1575 parent.PutId(ids_.FromNumber(103));
1576 parent.PutIsUnsynced(true);
1577 parent.PutIsDir(true);
1578 parent.PutSpecifics(DefaultBookmarkSpecifics());
1579 parent.PutIsDel(true);
1580 parent.PutBaseVersion(1);
1581 parent.PutMtime(now_minus_2h);
1582 MutableEntry child(
1583 &wtrans, CREATE, BOOKMARKS, ids_.FromNumber(103), "Pete");
1584 ASSERT_TRUE(child.good());
1585 child.PutId(ids_.FromNumber(104));
1586 child.PutIsUnsynced(true);
1587 child.PutIsDir(true);
1588 child.PutSpecifics(DefaultBookmarkSpecifics());
1589 child.PutIsDel(true);
1590 child.PutBaseVersion(1);
1591 child.PutMtime(now_minus_2h);
1592 MutableEntry grandchild(
1593 &wtrans, CREATE, BOOKMARKS, ids_.FromNumber(104), "Pete");
1594 ASSERT_TRUE(grandchild.good());
1595 grandchild.PutId(ids_.FromNumber(105));
1596 grandchild.PutIsUnsynced(true);
1597 grandchild.PutIsDel(true);
1598 grandchild.PutIsDir(false);
1599 grandchild.PutSpecifics(DefaultBookmarkSpecifics());
1600 grandchild.PutBaseVersion(1);
1601 grandchild.PutMtime(now_minus_2h);
1605 EXPECT_TRUE(SyncShareNudge());
1606 ASSERT_EQ(6u, mock_server_->committed_ids().size());
1607 // This test will NOT unroll deletes because SERVER_PARENT_ID is not set.
1608 // It will treat these like moves.
1609 vector<syncable::Id> commit_ids(mock_server_->committed_ids());
1610 EXPECT_TRUE(ids_.FromNumber(100) == commit_ids[0]);
1611 EXPECT_TRUE(ids_.FromNumber(101) == commit_ids[1]);
1612 EXPECT_TRUE(ids_.FromNumber(102) == commit_ids[2]);
1613 // We don't guarantee the delete orders in this test, only that they occur
1614 // at the end.
1615 std::sort(commit_ids.begin() + 3, commit_ids.end());
1616 EXPECT_TRUE(ids_.FromNumber(103) == commit_ids[3]);
1617 EXPECT_TRUE(ids_.FromNumber(104) == commit_ids[4]);
1618 EXPECT_TRUE(ids_.FromNumber(105) == commit_ids[5]);
1621 TEST_F(SyncerTest, TestCommitListOrderingWithNewItems) {
1622 syncable::Id parent1_id = ids_.MakeServer("p1");
1623 syncable::Id parent2_id = ids_.MakeServer("p2");
1626 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1627 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "1");
1628 ASSERT_TRUE(parent.good());
1629 parent.PutIsUnsynced(true);
1630 parent.PutIsDir(true);
1631 parent.PutSpecifics(DefaultBookmarkSpecifics());
1632 parent.PutId(parent1_id);
1633 MutableEntry child(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "2");
1634 ASSERT_TRUE(child.good());
1635 child.PutIsUnsynced(true);
1636 child.PutIsDir(true);
1637 child.PutSpecifics(DefaultBookmarkSpecifics());
1638 child.PutId(parent2_id);
1639 parent.PutBaseVersion(1);
1640 child.PutBaseVersion(1);
1643 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1644 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, parent1_id, "A");
1645 ASSERT_TRUE(parent.good());
1646 parent.PutIsUnsynced(true);
1647 parent.PutIsDir(true);
1648 parent.PutSpecifics(DefaultBookmarkSpecifics());
1649 parent.PutId(ids_.FromNumber(102));
1650 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent1_id, "B");
1651 ASSERT_TRUE(child.good());
1652 child.PutIsUnsynced(true);
1653 child.PutIsDir(true);
1654 child.PutSpecifics(DefaultBookmarkSpecifics());
1655 child.PutId(ids_.FromNumber(-103));
1656 parent.PutBaseVersion(1);
1659 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1660 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, parent2_id, "A");
1661 ASSERT_TRUE(parent.good());
1662 parent.PutIsUnsynced(true);
1663 parent.PutIsDir(true);
1664 parent.PutSpecifics(DefaultBookmarkSpecifics());
1665 parent.PutId(ids_.FromNumber(-104));
1666 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent2_id, "B");
1667 ASSERT_TRUE(child.good());
1668 child.PutIsUnsynced(true);
1669 child.PutIsDir(true);
1670 child.PutSpecifics(DefaultBookmarkSpecifics());
1671 child.PutId(ids_.FromNumber(105));
1672 child.PutBaseVersion(1);
1675 EXPECT_TRUE(SyncShareNudge());
1676 ASSERT_EQ(6u, mock_server_->committed_ids().size());
1678 // This strange iteration and std::count() usage is to allow the order to
1679 // vary. All we really care about is that parent1_id and parent2_id are the
1680 // first two IDs, and that the children make up the next four. Other than
1681 // that, ordering doesn't matter.
1683 vector<syncable::Id>::const_iterator i =
1684 mock_server_->committed_ids().begin();
1685 vector<syncable::Id>::const_iterator parents_begin = i;
1686 i++;
1687 i++;
1688 vector<syncable::Id>::const_iterator parents_end = i;
1689 vector<syncable::Id>::const_iterator children_begin = i;
1690 vector<syncable::Id>::const_iterator children_end =
1691 mock_server_->committed_ids().end();
1693 EXPECT_EQ(1, count(parents_begin, parents_end, parent1_id));
1694 EXPECT_EQ(1, count(parents_begin, parents_end, parent2_id));
1696 EXPECT_EQ(1, count(children_begin, children_end, ids_.FromNumber(-103)));
1697 EXPECT_EQ(1, count(children_begin, children_end, ids_.FromNumber(102)));
1698 EXPECT_EQ(1, count(children_begin, children_end, ids_.FromNumber(105)));
1699 EXPECT_EQ(1, count(children_begin, children_end, ids_.FromNumber(-104)));
1702 TEST_F(SyncerTest, TestCommitListOrderingCounterexample) {
1703 syncable::Id child2_id = ids_.NewServerId();
1706 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1707 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "P");
1708 ASSERT_TRUE(parent.good());
1709 parent.PutIsUnsynced(true);
1710 parent.PutIsDir(true);
1711 parent.PutSpecifics(DefaultBookmarkSpecifics());
1712 parent.PutId(parent_id_);
1713 MutableEntry child1(&wtrans, CREATE, BOOKMARKS, parent_id_, "1");
1714 ASSERT_TRUE(child1.good());
1715 child1.PutIsUnsynced(true);
1716 child1.PutId(child_id_);
1717 child1.PutSpecifics(DefaultBookmarkSpecifics());
1718 MutableEntry child2(&wtrans, CREATE, BOOKMARKS, parent_id_, "2");
1719 ASSERT_TRUE(child2.good());
1720 child2.PutIsUnsynced(true);
1721 child2.PutSpecifics(DefaultBookmarkSpecifics());
1722 child2.PutId(child2_id);
1724 parent.PutBaseVersion(1);
1725 child1.PutBaseVersion(1);
1726 child2.PutBaseVersion(1);
1729 EXPECT_TRUE(SyncShareNudge());
1730 ASSERT_EQ(3u, mock_server_->committed_ids().size());
1731 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
1732 // There are two possible valid orderings.
1733 if (child2_id == mock_server_->committed_ids()[1]) {
1734 EXPECT_TRUE(child2_id == mock_server_->committed_ids()[1]);
1735 EXPECT_TRUE(child_id_ == mock_server_->committed_ids()[2]);
1736 } else {
1737 EXPECT_TRUE(child_id_ == mock_server_->committed_ids()[1]);
1738 EXPECT_TRUE(child2_id == mock_server_->committed_ids()[2]);
1742 TEST_F(SyncerTest, TestCommitListOrderingAndNewParent) {
1743 string parent1_name = "1";
1744 string parent2_name = "A";
1745 string child_name = "B";
1748 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1749 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(),
1750 parent1_name);
1751 ASSERT_TRUE(parent.good());
1752 parent.PutIsUnsynced(true);
1753 parent.PutIsDir(true);
1754 parent.PutSpecifics(DefaultBookmarkSpecifics());
1755 parent.PutId(parent_id_);
1756 parent.PutBaseVersion(1);
1759 syncable::Id parent2_id = ids_.NewLocalId();
1760 syncable::Id child_id = ids_.NewServerId();
1762 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1763 MutableEntry parent2(
1764 &wtrans, CREATE, BOOKMARKS, parent_id_, parent2_name);
1765 ASSERT_TRUE(parent2.good());
1766 parent2.PutIsUnsynced(true);
1767 parent2.PutIsDir(true);
1768 parent2.PutSpecifics(DefaultBookmarkSpecifics());
1769 parent2.PutId(parent2_id);
1771 MutableEntry child(
1772 &wtrans, CREATE, BOOKMARKS, parent2_id, child_name);
1773 ASSERT_TRUE(child.good());
1774 child.PutIsUnsynced(true);
1775 child.PutIsDir(true);
1776 child.PutSpecifics(DefaultBookmarkSpecifics());
1777 child.PutId(child_id);
1778 child.PutBaseVersion(1);
1781 EXPECT_TRUE(SyncShareNudge());
1782 ASSERT_EQ(3u, mock_server_->committed_ids().size());
1783 // If this test starts failing, be aware other sort orders could be valid.
1784 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
1785 EXPECT_TRUE(parent2_id == mock_server_->committed_ids()[1]);
1786 EXPECT_TRUE(child_id == mock_server_->committed_ids()[2]);
1788 syncable::ReadTransaction rtrans(FROM_HERE, directory());
1789 // Check that things committed correctly.
1790 Entry entry_1(&rtrans, syncable::GET_BY_ID, parent_id_);
1791 EXPECT_EQ(entry_1.GetNonUniqueName(), parent1_name);
1792 // Check that parent2 is a subfolder of parent1.
1793 EXPECT_EQ(1, CountEntriesWithName(&rtrans,
1794 parent_id_,
1795 parent2_name));
1797 // Parent2 was a local ID and thus should have changed on commit!
1798 Entry pre_commit_entry_parent2(&rtrans, syncable::GET_BY_ID, parent2_id);
1799 ASSERT_FALSE(pre_commit_entry_parent2.good());
1801 // Look up the new ID.
1802 Id parent2_committed_id =
1803 GetOnlyEntryWithName(&rtrans, parent_id_, parent2_name);
1804 EXPECT_TRUE(parent2_committed_id.ServerKnows());
1806 Entry child(&rtrans, syncable::GET_BY_ID, child_id);
1807 EXPECT_EQ(parent2_committed_id, child.GetParentId());
1811 TEST_F(SyncerTest, TestCommitListOrderingAndNewParentAndChild) {
1812 string parent_name = "1";
1813 string parent2_name = "A";
1814 string child_name = "B";
1817 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1818 MutableEntry parent(&wtrans,
1819 CREATE, BOOKMARKS,
1820 wtrans.root_id(),
1821 parent_name);
1822 ASSERT_TRUE(parent.good());
1823 parent.PutIsUnsynced(true);
1824 parent.PutIsDir(true);
1825 parent.PutSpecifics(DefaultBookmarkSpecifics());
1826 parent.PutId(parent_id_);
1827 parent.PutBaseVersion(1);
1830 int64 meta_handle_b;
1831 const Id parent2_local_id = ids_.NewLocalId();
1832 const Id child_local_id = ids_.NewLocalId();
1834 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1835 MutableEntry parent2(&wtrans, CREATE, BOOKMARKS, parent_id_, parent2_name);
1836 ASSERT_TRUE(parent2.good());
1837 parent2.PutIsUnsynced(true);
1838 parent2.PutIsDir(true);
1839 parent2.PutSpecifics(DefaultBookmarkSpecifics());
1841 parent2.PutId(parent2_local_id);
1842 MutableEntry child(
1843 &wtrans, CREATE, BOOKMARKS, parent2_local_id, child_name);
1844 ASSERT_TRUE(child.good());
1845 child.PutIsUnsynced(true);
1846 child.PutIsDir(true);
1847 child.PutSpecifics(DefaultBookmarkSpecifics());
1848 child.PutId(child_local_id);
1849 meta_handle_b = child.GetMetahandle();
1852 EXPECT_TRUE(SyncShareNudge());
1853 ASSERT_EQ(3u, mock_server_->committed_ids().size());
1854 // If this test starts failing, be aware other sort orders could be valid.
1855 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
1856 EXPECT_TRUE(parent2_local_id == mock_server_->committed_ids()[1]);
1857 EXPECT_TRUE(child_local_id == mock_server_->committed_ids()[2]);
1859 syncable::ReadTransaction rtrans(FROM_HERE, directory());
1861 Entry parent(&rtrans, syncable::GET_BY_ID,
1862 GetOnlyEntryWithName(&rtrans, rtrans.root_id(), parent_name));
1863 ASSERT_TRUE(parent.good());
1864 EXPECT_TRUE(parent.GetId().ServerKnows());
1866 Entry parent2(&rtrans, syncable::GET_BY_ID,
1867 GetOnlyEntryWithName(&rtrans, parent.GetId(), parent2_name));
1868 ASSERT_TRUE(parent2.good());
1869 EXPECT_TRUE(parent2.GetId().ServerKnows());
1871 // Id changed on commit, so this should fail.
1872 Entry local_parent2_id_entry(&rtrans,
1873 syncable::GET_BY_ID,
1874 parent2_local_id);
1875 ASSERT_FALSE(local_parent2_id_entry.good());
1877 Entry entry_b(&rtrans, syncable::GET_BY_HANDLE, meta_handle_b);
1878 EXPECT_TRUE(entry_b.GetId().ServerKnows());
1879 EXPECT_TRUE(parent2.GetId()== entry_b.GetParentId());
1883 TEST_F(SyncerTest, UpdateWithZeroLengthName) {
1884 // One illegal update
1885 mock_server_->AddUpdateDirectory(
1886 1, 0, std::string(), 1, 10, foreign_cache_guid(), "-1");
1887 // And one legal one that we're going to delete.
1888 mock_server_->AddUpdateDirectory(2, 0, "FOO", 1, 10,
1889 foreign_cache_guid(), "-2");
1890 EXPECT_TRUE(SyncShareNudge());
1891 // Delete the legal one. The new update has a null name.
1892 mock_server_->AddUpdateDirectory(
1893 2, 0, std::string(), 2, 20, foreign_cache_guid(), "-2");
1894 mock_server_->SetLastUpdateDeleted();
1895 EXPECT_TRUE(SyncShareNudge());
1898 TEST_F(SyncerTest, TestBasicUpdate) {
1899 string id = "some_id";
1900 string parent_id = "0";
1901 string name = "in_root";
1902 int64 version = 10;
1903 int64 timestamp = 10;
1904 mock_server_->AddUpdateDirectory(id, parent_id, name, version, timestamp,
1905 foreign_cache_guid(), "-1");
1907 EXPECT_TRUE(SyncShareNudge());
1909 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1910 Entry entry(&trans, GET_BY_ID,
1911 syncable::Id::CreateFromServerId("some_id"));
1912 ASSERT_TRUE(entry.good());
1913 EXPECT_TRUE(entry.GetIsDir());
1914 EXPECT_TRUE(entry.GetServerVersion()== version);
1915 EXPECT_TRUE(entry.GetBaseVersion()== version);
1916 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
1917 EXPECT_FALSE(entry.GetIsUnsynced());
1918 EXPECT_FALSE(entry.GetServerIsDel());
1919 EXPECT_FALSE(entry.GetIsDel());
1923 TEST_F(SyncerTest, IllegalAndLegalUpdates) {
1924 Id root = TestIdFactory::root();
1925 // Should apply just fine.
1926 mock_server_->AddUpdateDirectory(1, 0, "in_root", 10, 10,
1927 foreign_cache_guid(), "-1");
1929 // Same name. But this SHOULD work.
1930 mock_server_->AddUpdateDirectory(2, 0, "in_root", 10, 10,
1931 foreign_cache_guid(), "-2");
1933 // Unknown parent: should never be applied. "-80" is a legal server ID,
1934 // because any string sent by the server is a legal server ID in the sync
1935 // protocol, but it's not the ID of any item known to the client. This
1936 // update should succeed validation, but be stuck in the unapplied state
1937 // until an item with the server ID "-80" arrives.
1938 mock_server_->AddUpdateDirectory(3, -80, "bad_parent", 10, 10,
1939 foreign_cache_guid(), "-3");
1941 EXPECT_TRUE(SyncShareNudge());
1943 // Id 3 should be in conflict now.
1944 EXPECT_EQ(
1946 GetUpdateCounters(BOOKMARKS).num_hierarchy_conflict_application_failures);
1948 // The only request in that loop should have been a GetUpdate.
1949 // At that point, we didn't know whether or not we had conflicts.
1950 ASSERT_TRUE(mock_server_->last_request().has_get_updates());
1951 VerifyHierarchyConflictsUnspecified(mock_server_->last_request());
1953 // These entries will be used in the second set of updates.
1954 mock_server_->AddUpdateDirectory(4, 0, "newer_version", 20, 10,
1955 foreign_cache_guid(), "-4");
1956 mock_server_->AddUpdateDirectory(5, 0, "circular1", 10, 10,
1957 foreign_cache_guid(), "-5");
1958 mock_server_->AddUpdateDirectory(6, 5, "circular2", 10, 10,
1959 foreign_cache_guid(), "-6");
1960 mock_server_->AddUpdateDirectory(9, 3, "bad_parent_child", 10, 10,
1961 foreign_cache_guid(), "-9");
1962 mock_server_->AddUpdateDirectory(100, 9, "bad_parent_child2", 10, 10,
1963 foreign_cache_guid(), "-100");
1964 mock_server_->AddUpdateDirectory(10, 0, "dir_to_bookmark", 10, 10,
1965 foreign_cache_guid(), "-10");
1967 EXPECT_TRUE(SyncShareNudge());
1968 // The three items with an unresolved parent should be unapplied (3, 9, 100).
1969 // The name clash should also still be in conflict.
1970 EXPECT_EQ(
1972 GetUpdateCounters(BOOKMARKS).num_hierarchy_conflict_application_failures);
1974 // This time around, we knew that there were conflicts.
1975 ASSERT_TRUE(mock_server_->last_request().has_get_updates());
1976 VerifyHierarchyConflictsReported(mock_server_->last_request());
1979 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1980 // Even though it has the same name, it should work.
1981 Entry name_clash(&trans, GET_BY_ID, ids_.FromNumber(2));
1982 ASSERT_TRUE(name_clash.good());
1983 EXPECT_FALSE(name_clash.GetIsUnappliedUpdate())
1984 << "Duplicate name SHOULD be OK.";
1986 Entry bad_parent(&trans, GET_BY_ID, ids_.FromNumber(3));
1987 ASSERT_TRUE(bad_parent.good());
1988 EXPECT_TRUE(bad_parent.GetIsUnappliedUpdate())
1989 << "child of unknown parent should be in conflict";
1991 Entry bad_parent_child(&trans, GET_BY_ID, ids_.FromNumber(9));
1992 ASSERT_TRUE(bad_parent_child.good());
1993 EXPECT_TRUE(bad_parent_child.GetIsUnappliedUpdate())
1994 << "grandchild of unknown parent should be in conflict";
1996 Entry bad_parent_child2(&trans, GET_BY_ID, ids_.FromNumber(100));
1997 ASSERT_TRUE(bad_parent_child2.good());
1998 EXPECT_TRUE(bad_parent_child2.GetIsUnappliedUpdate())
1999 << "great-grandchild of unknown parent should be in conflict";
2002 // Updating 1 should not affect item 2 of the same name.
2003 mock_server_->AddUpdateDirectory(1, 0, "new_name", 20, 20,
2004 foreign_cache_guid(), "-1");
2006 // Moving 5 under 6 will create a cycle: a conflict.
2007 mock_server_->AddUpdateDirectory(5, 6, "circular3", 20, 20,
2008 foreign_cache_guid(), "-5");
2010 // Flip the is_dir bit: should fail verify & be dropped.
2011 mock_server_->AddUpdateBookmark(10, 0, "dir_to_bookmark", 20, 20,
2012 foreign_cache_guid(), "-10");
2013 EXPECT_TRUE(SyncShareNudge());
2015 // Version number older than last known: should fail verify & be dropped.
2016 mock_server_->AddUpdateDirectory(4, 0, "old_version", 10, 10,
2017 foreign_cache_guid(), "-4");
2018 EXPECT_TRUE(SyncShareNudge());
2020 syncable::ReadTransaction trans(FROM_HERE, directory());
2022 Entry still_a_dir(&trans, GET_BY_ID, ids_.FromNumber(10));
2023 ASSERT_TRUE(still_a_dir.good());
2024 EXPECT_FALSE(still_a_dir.GetIsUnappliedUpdate());
2025 EXPECT_EQ(10u, still_a_dir.GetBaseVersion());
2026 EXPECT_EQ(10u, still_a_dir.GetServerVersion());
2027 EXPECT_TRUE(still_a_dir.GetIsDir());
2029 Entry rename(&trans, GET_BY_ID, ids_.FromNumber(1));
2030 ASSERT_TRUE(rename.good());
2031 EXPECT_EQ(root, rename.GetParentId());
2032 EXPECT_EQ("new_name", rename.GetNonUniqueName());
2033 EXPECT_FALSE(rename.GetIsUnappliedUpdate());
2034 EXPECT_TRUE(ids_.FromNumber(1) == rename.GetId());
2035 EXPECT_EQ(20u, rename.GetBaseVersion());
2037 Entry name_clash(&trans, GET_BY_ID, ids_.FromNumber(2));
2038 ASSERT_TRUE(name_clash.good());
2039 EXPECT_EQ(root, name_clash.GetParentId());
2040 EXPECT_TRUE(ids_.FromNumber(2) == name_clash.GetId());
2041 EXPECT_EQ(10u, name_clash.GetBaseVersion());
2042 EXPECT_EQ("in_root", name_clash.GetNonUniqueName());
2044 Entry ignored_old_version(&trans, GET_BY_ID, ids_.FromNumber(4));
2045 ASSERT_TRUE(ignored_old_version.good());
2046 EXPECT_TRUE(
2047 ignored_old_version.GetNonUniqueName()== "newer_version");
2048 EXPECT_FALSE(ignored_old_version.GetIsUnappliedUpdate());
2049 EXPECT_EQ(20u, ignored_old_version.GetBaseVersion());
2051 Entry circular_parent_issue(&trans, GET_BY_ID, ids_.FromNumber(5));
2052 ASSERT_TRUE(circular_parent_issue.good());
2053 EXPECT_TRUE(circular_parent_issue.GetIsUnappliedUpdate())
2054 << "circular move should be in conflict";
2055 EXPECT_TRUE(circular_parent_issue.GetParentId()== root_id_);
2056 EXPECT_TRUE(circular_parent_issue.GetServerParentId()==
2057 ids_.FromNumber(6));
2058 EXPECT_EQ(10u, circular_parent_issue.GetBaseVersion());
2060 Entry circular_parent_target(&trans, GET_BY_ID, ids_.FromNumber(6));
2061 ASSERT_TRUE(circular_parent_target.good());
2062 EXPECT_FALSE(circular_parent_target.GetIsUnappliedUpdate());
2063 EXPECT_TRUE(circular_parent_issue.GetId()==
2064 circular_parent_target.GetParentId());
2065 EXPECT_EQ(10u, circular_parent_target.GetBaseVersion());
2068 EXPECT_FALSE(saw_syncer_event_);
2069 EXPECT_EQ(
2071 GetUpdateCounters(BOOKMARKS).num_hierarchy_conflict_application_failures);
2074 // A commit with a lost response produces an update that has to be reunited with
2075 // its parent.
2076 TEST_F(SyncerTest, CommitReuniteUpdateAdjustsChildren) {
2077 // Create a folder in the root.
2078 int64 metahandle_folder;
2080 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2081 MutableEntry entry(
2082 &trans, CREATE, BOOKMARKS, trans.root_id(), "new_folder");
2083 ASSERT_TRUE(entry.good());
2084 entry.PutIsDir(true);
2085 entry.PutSpecifics(DefaultBookmarkSpecifics());
2086 entry.PutIsUnsynced(true);
2087 metahandle_folder = entry.GetMetahandle();
2090 // Verify it and pull the ID out of the folder.
2091 syncable::Id folder_id;
2092 int64 metahandle_entry;
2094 syncable::ReadTransaction trans(FROM_HERE, directory());
2095 Entry entry(&trans, GET_BY_HANDLE, metahandle_folder);
2096 ASSERT_TRUE(entry.good());
2097 folder_id = entry.GetId();
2098 ASSERT_TRUE(!folder_id.ServerKnows());
2101 // Create an entry in the newly created folder.
2103 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2104 MutableEntry entry(&trans, CREATE, BOOKMARKS, folder_id, "new_entry");
2105 ASSERT_TRUE(entry.good());
2106 metahandle_entry = entry.GetMetahandle();
2107 WriteTestDataToEntry(&trans, &entry);
2110 // Verify it and pull the ID out of the entry.
2111 syncable::Id entry_id;
2113 syncable::ReadTransaction trans(FROM_HERE, directory());
2114 Entry entry(&trans, syncable::GET_BY_HANDLE, metahandle_entry);
2115 ASSERT_TRUE(entry.good());
2116 EXPECT_EQ(folder_id, entry.GetParentId());
2117 EXPECT_EQ("new_entry", entry.GetNonUniqueName());
2118 entry_id = entry.GetId();
2119 EXPECT_TRUE(!entry_id.ServerKnows());
2120 VerifyTestDataInEntry(&trans, &entry);
2123 // Now, to emulate a commit response failure, we just don't commit it.
2124 int64 new_version = 150; // any larger value.
2125 int64 timestamp = 20; // arbitrary value.
2126 syncable::Id new_folder_id =
2127 syncable::Id::CreateFromServerId("folder_server_id");
2129 // The following update should cause the folder to both apply the update, as
2130 // well as reassociate the id.
2131 mock_server_->AddUpdateDirectory(new_folder_id, root_id_,
2132 "new_folder", new_version, timestamp,
2133 local_cache_guid(), folder_id.GetServerId());
2135 // We don't want it accidentally committed, just the update applied.
2136 mock_server_->set_conflict_all_commits(true);
2138 // Alright! Apply that update!
2139 EXPECT_FALSE(SyncShareNudge());
2141 // The folder's ID should have been updated.
2142 syncable::ReadTransaction trans(FROM_HERE, directory());
2143 Entry folder(&trans, GET_BY_HANDLE, metahandle_folder);
2144 ASSERT_TRUE(folder.good());
2145 EXPECT_EQ("new_folder", folder.GetNonUniqueName());
2146 EXPECT_TRUE(new_version == folder.GetBaseVersion());
2147 EXPECT_TRUE(new_folder_id == folder.GetId());
2148 EXPECT_TRUE(folder.GetId().ServerKnows());
2149 EXPECT_EQ(trans.root_id(), folder.GetParentId());
2151 // Since it was updated, the old folder should not exist.
2152 Entry old_dead_folder(&trans, GET_BY_ID, folder_id);
2153 EXPECT_FALSE(old_dead_folder.good());
2155 // The child's parent should have changed.
2156 Entry entry(&trans, syncable::GET_BY_HANDLE, metahandle_entry);
2157 ASSERT_TRUE(entry.good());
2158 EXPECT_EQ("new_entry", entry.GetNonUniqueName());
2159 EXPECT_EQ(new_folder_id, entry.GetParentId());
2160 EXPECT_TRUE(!entry.GetId().ServerKnows());
2161 VerifyTestDataInEntry(&trans, &entry);
2165 // A commit with a lost response produces an update that has to be reunited with
2166 // its parent.
2167 TEST_F(SyncerTest, CommitReuniteUpdate) {
2168 // Create an entry in the root.
2169 int64 entry_metahandle;
2171 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2172 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "new_entry");
2173 ASSERT_TRUE(entry.good());
2174 entry_metahandle = entry.GetMetahandle();
2175 WriteTestDataToEntry(&trans, &entry);
2178 // Verify it and pull the ID out.
2179 syncable::Id entry_id;
2181 syncable::ReadTransaction trans(FROM_HERE, directory());
2183 Entry entry(&trans, GET_BY_HANDLE, entry_metahandle);
2184 ASSERT_TRUE(entry.good());
2185 entry_id = entry.GetId();
2186 EXPECT_TRUE(!entry_id.ServerKnows());
2187 VerifyTestDataInEntry(&trans, &entry);
2190 // Now, to emulate a commit response failure, we just don't commit it.
2191 int64 new_version = 150; // any larger value.
2192 int64 timestamp = 20; // arbitrary value.
2193 syncable::Id new_entry_id = syncable::Id::CreateFromServerId("server_id");
2195 // Generate an update from the server with a relevant ID reassignment.
2196 mock_server_->AddUpdateBookmark(new_entry_id, root_id_,
2197 "new_entry", new_version, timestamp,
2198 local_cache_guid(), entry_id.GetServerId());
2200 // We don't want it accidentally committed, just the update applied.
2201 mock_server_->set_conflict_all_commits(true);
2203 // Alright! Apply that update!
2204 EXPECT_TRUE(SyncShareNudge());
2206 syncable::ReadTransaction trans(FROM_HERE, directory());
2207 Entry entry(&trans, GET_BY_HANDLE, entry_metahandle);
2208 ASSERT_TRUE(entry.good());
2209 EXPECT_TRUE(new_version == entry.GetBaseVersion());
2210 EXPECT_TRUE(new_entry_id == entry.GetId());
2211 EXPECT_EQ("new_entry", entry.GetNonUniqueName());
2215 // A commit with a lost response must work even if the local entry was deleted
2216 // before the update is applied. We should not duplicate the local entry in
2217 // this case, but just create another one alongside. We may wish to examine
2218 // this behavior in the future as it can create hanging uploads that never
2219 // finish, that must be cleaned up on the server side after some time.
2220 TEST_F(SyncerTest, CommitReuniteUpdateDoesNotChokeOnDeletedLocalEntry) {
2221 // Create a entry in the root.
2222 int64 entry_metahandle;
2224 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2225 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "new_entry");
2226 ASSERT_TRUE(entry.good());
2227 entry_metahandle = entry.GetMetahandle();
2228 WriteTestDataToEntry(&trans, &entry);
2230 // Verify it and pull the ID out.
2231 syncable::Id entry_id;
2233 syncable::ReadTransaction trans(FROM_HERE, directory());
2234 Entry entry(&trans, GET_BY_HANDLE, entry_metahandle);
2235 ASSERT_TRUE(entry.good());
2236 entry_id = entry.GetId();
2237 EXPECT_TRUE(!entry_id.ServerKnows());
2238 VerifyTestDataInEntry(&trans, &entry);
2241 // Now, to emulate a commit response failure, we just don't commit it.
2242 int64 new_version = 150; // any larger value.
2243 int64 timestamp = 20; // arbitrary value.
2244 syncable::Id new_entry_id = syncable::Id::CreateFromServerId("server_id");
2246 // Generate an update from the server with a relevant ID reassignment.
2247 mock_server_->AddUpdateBookmark(new_entry_id, root_id_,
2248 "new_entry", new_version, timestamp,
2249 local_cache_guid(), entry_id.GetServerId());
2251 // We don't want it accidentally committed, just the update applied.
2252 mock_server_->set_conflict_all_commits(true);
2254 // Purposefully delete the entry now before the update application finishes.
2256 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2257 Id new_entry_id = GetOnlyEntryWithName(
2258 &trans, trans.root_id(), "new_entry");
2259 MutableEntry entry(&trans, GET_BY_ID, new_entry_id);
2260 ASSERT_TRUE(entry.good());
2261 entry.PutIsDel(true);
2264 // Just don't CHECK fail in sync, have the update split.
2265 EXPECT_TRUE(SyncShareNudge());
2267 syncable::ReadTransaction trans(FROM_HERE, directory());
2268 Id new_entry_id = GetOnlyEntryWithName(
2269 &trans, trans.root_id(), "new_entry");
2270 Entry entry(&trans, GET_BY_ID, new_entry_id);
2271 ASSERT_TRUE(entry.good());
2272 EXPECT_FALSE(entry.GetIsDel());
2274 Entry old_entry(&trans, GET_BY_ID, entry_id);
2275 ASSERT_TRUE(old_entry.good());
2276 EXPECT_TRUE(old_entry.GetIsDel());
2280 // TODO(chron): Add more unsanitized name tests.
2281 TEST_F(SyncerTest, ConflictMatchingEntryHandlesUnsanitizedNames) {
2282 mock_server_->AddUpdateDirectory(1, 0, "A/A", 10, 10,
2283 foreign_cache_guid(), "-1");
2284 mock_server_->AddUpdateDirectory(2, 0, "B/B", 10, 10,
2285 foreign_cache_guid(), "-2");
2286 mock_server_->set_conflict_all_commits(true);
2287 EXPECT_TRUE(SyncShareNudge());
2289 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2291 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2292 ASSERT_TRUE(A.good());
2293 A.PutIsUnsynced(true);
2294 A.PutIsUnappliedUpdate(true);
2295 A.PutServerVersion(20);
2297 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
2298 ASSERT_TRUE(B.good());
2299 B.PutIsUnappliedUpdate(true);
2300 B.PutServerVersion(20);
2302 EXPECT_TRUE(SyncShareNudge());
2303 saw_syncer_event_ = false;
2304 mock_server_->set_conflict_all_commits(false);
2307 syncable::ReadTransaction trans(FROM_HERE, directory());
2309 Entry A(&trans, GET_BY_ID, ids_.FromNumber(1));
2310 ASSERT_TRUE(A.good());
2311 EXPECT_TRUE(A.GetIsUnsynced()== false);
2312 EXPECT_TRUE(A.GetIsUnappliedUpdate()== false);
2313 EXPECT_TRUE(A.GetServerVersion()== 20);
2315 Entry B(&trans, GET_BY_ID, ids_.FromNumber(2));
2316 ASSERT_TRUE(B.good());
2317 EXPECT_TRUE(B.GetIsUnsynced()== false);
2318 EXPECT_TRUE(B.GetIsUnappliedUpdate()== false);
2319 EXPECT_TRUE(B.GetServerVersion()== 20);
2323 TEST_F(SyncerTest, ConflictMatchingEntryHandlesNormalNames) {
2324 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
2325 foreign_cache_guid(), "-1");
2326 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10,
2327 foreign_cache_guid(), "-2");
2328 mock_server_->set_conflict_all_commits(true);
2329 EXPECT_TRUE(SyncShareNudge());
2331 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2333 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2334 ASSERT_TRUE(A.good());
2335 A.PutIsUnsynced(true);
2336 A.PutIsUnappliedUpdate(true);
2337 A.PutServerVersion(20);
2339 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
2340 ASSERT_TRUE(B.good());
2341 B.PutIsUnappliedUpdate(true);
2342 B.PutServerVersion(20);
2344 EXPECT_TRUE(SyncShareNudge());
2345 saw_syncer_event_ = false;
2346 mock_server_->set_conflict_all_commits(false);
2349 syncable::ReadTransaction trans(FROM_HERE, directory());
2351 Entry A(&trans, GET_BY_ID, ids_.FromNumber(1));
2352 ASSERT_TRUE(A.good());
2353 EXPECT_TRUE(A.GetIsUnsynced()== false);
2354 EXPECT_TRUE(A.GetIsUnappliedUpdate()== false);
2355 EXPECT_TRUE(A.GetServerVersion()== 20);
2357 Entry B(&trans, GET_BY_ID, ids_.FromNumber(2));
2358 ASSERT_TRUE(B.good());
2359 EXPECT_TRUE(B.GetIsUnsynced()== false);
2360 EXPECT_TRUE(B.GetIsUnappliedUpdate()== false);
2361 EXPECT_TRUE(B.GetServerVersion()== 20);
2365 TEST_F(SyncerTest, ReverseFolderOrderingTest) {
2366 mock_server_->AddUpdateDirectory(4, 3, "ggchild", 10, 10,
2367 foreign_cache_guid(), "-4");
2368 mock_server_->AddUpdateDirectory(3, 2, "gchild", 10, 10,
2369 foreign_cache_guid(), "-3");
2370 mock_server_->AddUpdateDirectory(5, 4, "gggchild", 10, 10,
2371 foreign_cache_guid(), "-5");
2372 mock_server_->AddUpdateDirectory(2, 1, "child", 10, 10,
2373 foreign_cache_guid(), "-2");
2374 mock_server_->AddUpdateDirectory(1, 0, "parent", 10, 10,
2375 foreign_cache_guid(), "-1");
2376 EXPECT_TRUE(SyncShareNudge());
2377 syncable::ReadTransaction trans(FROM_HERE, directory());
2379 Id child_id = GetOnlyEntryWithName(
2380 &trans, ids_.FromNumber(4), "gggchild");
2381 Entry child(&trans, GET_BY_ID, child_id);
2382 ASSERT_TRUE(child.good());
2385 class EntryCreatedInNewFolderTest : public SyncerTest {
2386 public:
2387 void CreateFolderInBob() {
2388 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2389 MutableEntry bob(&trans,
2390 syncable::GET_BY_ID,
2391 GetOnlyEntryWithName(&trans,
2392 TestIdFactory::root(),
2393 "bob"));
2394 CHECK(bob.good());
2396 MutableEntry entry2(
2397 &trans, CREATE, BOOKMARKS, bob.GetId(), "bob");
2398 CHECK(entry2.good());
2399 entry2.PutIsDir(true);
2400 entry2.PutIsUnsynced(true);
2401 entry2.PutSpecifics(DefaultBookmarkSpecifics());
2405 TEST_F(EntryCreatedInNewFolderTest, EntryCreatedInNewFolderMidSync) {
2407 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2408 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "bob");
2409 ASSERT_TRUE(entry.good());
2410 entry.PutIsDir(true);
2411 entry.PutIsUnsynced(true);
2412 entry.PutSpecifics(DefaultBookmarkSpecifics());
2415 mock_server_->SetMidCommitCallback(
2416 base::Bind(&EntryCreatedInNewFolderTest::CreateFolderInBob,
2417 base::Unretained(this)));
2418 EXPECT_TRUE(SyncShareNudge());
2419 // We loop until no unsynced handles remain, so we will commit both ids.
2420 EXPECT_EQ(2u, mock_server_->committed_ids().size());
2422 syncable::ReadTransaction trans(FROM_HERE, directory());
2423 Entry parent_entry(&trans, syncable::GET_BY_ID,
2424 GetOnlyEntryWithName(&trans, TestIdFactory::root(), "bob"));
2425 ASSERT_TRUE(parent_entry.good());
2427 Id child_id =
2428 GetOnlyEntryWithName(&trans, parent_entry.GetId(), "bob");
2429 Entry child(&trans, syncable::GET_BY_ID, child_id);
2430 ASSERT_TRUE(child.good());
2431 EXPECT_EQ(parent_entry.GetId(), child.GetParentId());
2435 TEST_F(SyncerTest, NegativeIDInUpdate) {
2436 mock_server_->AddUpdateBookmark(-10, 0, "bad", 40, 40,
2437 foreign_cache_guid(), "-100");
2438 EXPECT_TRUE(SyncShareNudge());
2439 // The negative id would make us CHECK!
2442 TEST_F(SyncerTest, UnappliedUpdateOnCreatedItemItemDoesNotCrash) {
2443 int64 metahandle_fred;
2444 syncable::Id orig_id;
2446 // Create an item.
2447 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2448 MutableEntry fred_match(&trans, CREATE, BOOKMARKS, trans.root_id(),
2449 "fred_match");
2450 ASSERT_TRUE(fred_match.good());
2451 metahandle_fred = fred_match.GetMetahandle();
2452 orig_id = fred_match.GetId();
2453 WriteTestDataToEntry(&trans, &fred_match);
2455 // Commit it.
2456 EXPECT_TRUE(SyncShareNudge());
2457 EXPECT_EQ(1u, mock_server_->committed_ids().size());
2458 mock_server_->set_conflict_all_commits(true);
2459 syncable::Id fred_match_id;
2461 // Now receive a change from outside.
2462 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2463 MutableEntry fred_match(&trans, GET_BY_HANDLE, metahandle_fred);
2464 ASSERT_TRUE(fred_match.good());
2465 EXPECT_TRUE(fred_match.GetId().ServerKnows());
2466 fred_match_id = fred_match.GetId();
2467 mock_server_->AddUpdateBookmark(fred_match_id, trans.root_id(),
2468 "fred_match", 40, 40, local_cache_guid(), orig_id.GetServerId());
2470 // Run the syncer.
2471 for (int i = 0 ; i < 30 ; ++i) {
2472 EXPECT_TRUE(SyncShareNudge());
2477 * In the event that we have a double changed entry, that is changed on both
2478 * the client and the server, the conflict resolver should just drop one of
2479 * them and accept the other.
2482 TEST_F(SyncerTest, DoublyChangedWithResolver) {
2483 syncable::Id local_id;
2485 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2486 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, root_id_, "Folder");
2487 ASSERT_TRUE(parent.good());
2488 parent.PutIsDir(true);
2489 parent.PutId(parent_id_);
2490 parent.PutBaseVersion(5);
2491 parent.PutSpecifics(DefaultBookmarkSpecifics());
2492 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent_id_, "Pete.htm");
2493 ASSERT_TRUE(child.good());
2494 local_id = child.GetId();
2495 child.PutId(child_id_);
2496 child.PutBaseVersion(10);
2497 WriteTestDataToEntry(&wtrans, &child);
2499 mock_server_->AddUpdateBookmark(child_id_, parent_id_, "Pete2.htm", 11, 10,
2500 local_cache_guid(), local_id.GetServerId());
2501 mock_server_->set_conflict_all_commits(true);
2502 EXPECT_FALSE(SyncShareNudge());
2503 syncable::Directory::Metahandles children;
2505 syncable::ReadTransaction trans(FROM_HERE, directory());
2506 directory()->GetChildHandlesById(&trans, parent_id_, &children);
2507 // We expect the conflict resolver to preserve the local entry.
2508 Entry child(&trans, syncable::GET_BY_ID, child_id_);
2509 ASSERT_TRUE(child.good());
2510 EXPECT_TRUE(child.GetIsUnsynced());
2511 EXPECT_FALSE(child.GetIsUnappliedUpdate());
2512 EXPECT_TRUE(child.GetSpecifics().has_bookmark());
2513 EXPECT_EQ("Pete.htm", child.GetNonUniqueName());
2514 VerifyTestBookmarkDataInEntry(&child);
2517 // Only one entry, since we just overwrite one.
2518 EXPECT_EQ(1u, children.size());
2519 saw_syncer_event_ = false;
2522 // We got this repro case when someone was editing bookmarks while sync was
2523 // occuring. The entry had changed out underneath the user.
2524 TEST_F(SyncerTest, CommitsUpdateDoesntAlterEntry) {
2525 const base::Time& test_time = ProtoTimeToTime(123456);
2526 syncable::Id local_id;
2527 int64 entry_metahandle;
2529 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2530 MutableEntry entry(&wtrans, CREATE, BOOKMARKS, root_id_, "Pete");
2531 ASSERT_TRUE(entry.good());
2532 EXPECT_FALSE(entry.GetId().ServerKnows());
2533 local_id = entry.GetId();
2534 entry.PutIsDir(true);
2535 entry.PutSpecifics(DefaultBookmarkSpecifics());
2536 entry.PutIsUnsynced(true);
2537 entry.PutMtime(test_time);
2538 entry_metahandle = entry.GetMetahandle();
2540 EXPECT_TRUE(SyncShareNudge());
2541 syncable::Id id;
2542 int64 version;
2544 syncable::ReadTransaction trans(FROM_HERE, directory());
2545 Entry entry(&trans, syncable::GET_BY_HANDLE, entry_metahandle);
2546 ASSERT_TRUE(entry.good());
2547 id = entry.GetId();
2548 EXPECT_TRUE(id.ServerKnows());
2549 version = entry.GetBaseVersion();
2551 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
2552 update->set_originator_cache_guid(local_cache_guid());
2553 update->set_originator_client_item_id(local_id.GetServerId());
2554 EXPECT_EQ("Pete", update->name());
2555 EXPECT_EQ(id.GetServerId(), update->id_string());
2556 EXPECT_EQ(root_id_.GetServerId(), update->parent_id_string());
2557 EXPECT_EQ(version, update->version());
2558 EXPECT_TRUE(SyncShareNudge());
2560 syncable::ReadTransaction trans(FROM_HERE, directory());
2561 Entry entry(&trans, syncable::GET_BY_ID, id);
2562 ASSERT_TRUE(entry.good());
2563 EXPECT_TRUE(entry.GetMtime()== test_time);
2567 TEST_F(SyncerTest, ParentAndChildBothMatch) {
2568 // Disable PREFERENCES which is enabled at the setup step to avoid
2569 // auto-creating
2570 // PREFERENCES root folder and failing the test below that verifies the number
2571 // of children at the root.
2572 DisableDatatype(PREFERENCES);
2574 const FullModelTypeSet all_types = FullModelTypeSet::All();
2575 syncable::Id parent_id = ids_.NewServerId();
2576 syncable::Id child_id = ids_.NewServerId();
2577 syncable::Id parent_local_id;
2578 syncable::Id child_local_id;
2581 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2582 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, root_id_, "Folder");
2583 ASSERT_TRUE(parent.good());
2584 parent_local_id = parent.GetId();
2585 parent.PutIsDir(true);
2586 parent.PutIsUnsynced(true);
2587 parent.PutId(parent_id);
2588 parent.PutBaseVersion(1);
2589 parent.PutSpecifics(DefaultBookmarkSpecifics());
2591 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent.GetId(), "test.htm");
2592 ASSERT_TRUE(child.good());
2593 child_local_id = child.GetId();
2594 child.PutId(child_id);
2595 child.PutBaseVersion(1);
2596 child.PutSpecifics(DefaultBookmarkSpecifics());
2597 WriteTestDataToEntry(&wtrans, &child);
2599 mock_server_->AddUpdateDirectory(parent_id, root_id_, "Folder", 10, 10,
2600 local_cache_guid(),
2601 parent_local_id.GetServerId());
2602 mock_server_->AddUpdateBookmark(child_id, parent_id, "test.htm", 10, 10,
2603 local_cache_guid(),
2604 child_local_id.GetServerId());
2605 mock_server_->set_conflict_all_commits(true);
2606 EXPECT_TRUE(SyncShareNudge());
2607 EXPECT_TRUE(SyncShareNudge());
2608 EXPECT_TRUE(SyncShareNudge());
2610 syncable::ReadTransaction trans(FROM_HERE, directory());
2611 Directory::Metahandles children;
2612 directory()->GetChildHandlesById(&trans, root_id_, &children);
2613 EXPECT_EQ(1u, children.size());
2614 directory()->GetChildHandlesById(&trans, parent_id, &children);
2615 EXPECT_EQ(1u, children.size());
2616 std::vector<int64> unapplied;
2617 directory()->GetUnappliedUpdateMetaHandles(&trans, all_types, &unapplied);
2618 EXPECT_EQ(0u, unapplied.size());
2619 syncable::Directory::Metahandles unsynced;
2620 directory()->GetUnsyncedMetaHandles(&trans, &unsynced);
2621 EXPECT_EQ(0u, unsynced.size());
2622 saw_syncer_event_ = false;
2626 TEST_F(SyncerTest, CommittingNewDeleted) {
2628 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2629 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "bob");
2630 entry.PutIsUnsynced(true);
2631 entry.PutIsDel(true);
2633 EXPECT_TRUE(SyncShareNudge());
2634 EXPECT_EQ(0u, mock_server_->committed_ids().size());
2637 // Original problem synopsis:
2638 // Check failed: entry->GetBaseVersion()<= entry->GetServerVersion()
2639 // Client creates entry, client finishes committing entry. Between
2640 // commit and getting update back, we delete the entry.
2641 // We get the update for the entry, but the local one was modified
2642 // so we store the entry but don't apply it. IS_UNAPPLIED_UPDATE is set.
2643 // We commit deletion and get a new version number.
2644 // We apply unapplied updates again before we get the update about the deletion.
2645 // This means we have an unapplied update where server_version < base_version.
2646 TEST_F(SyncerTest, UnappliedUpdateDuringCommit) {
2647 // This test is a little fake.
2649 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2650 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "bob");
2651 entry.PutId(ids_.FromNumber(20));
2652 entry.PutBaseVersion(1);
2653 entry.PutServerVersion(1);
2654 entry.PutServerParentId(ids_.FromNumber(9999)); // Bad parent.
2655 entry.PutIsUnsynced(true);
2656 entry.PutIsUnappliedUpdate(true);
2657 entry.PutSpecifics(DefaultBookmarkSpecifics());
2658 entry.PutServerSpecifics(DefaultBookmarkSpecifics());
2659 entry.PutIsDel(false);
2661 EXPECT_TRUE(SyncShareNudge());
2662 EXPECT_EQ(1, session_->status_controller().TotalNumConflictingItems());
2663 saw_syncer_event_ = false;
2666 // Original problem synopsis:
2667 // Illegal parent
2668 // Unexpected error during sync if we:
2669 // make a new folder bob
2670 // wait for sync
2671 // make a new folder fred
2672 // move bob into fred
2673 // remove bob
2674 // remove fred
2675 // if no syncing occured midway, bob will have an illegal parent
2676 TEST_F(SyncerTest, DeletingEntryInFolder) {
2677 // This test is a little fake.
2678 int64 existing_metahandle;
2680 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2681 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "existing");
2682 ASSERT_TRUE(entry.good());
2683 entry.PutIsDir(true);
2684 entry.PutSpecifics(DefaultBookmarkSpecifics());
2685 entry.PutIsUnsynced(true);
2686 existing_metahandle = entry.GetMetahandle();
2688 EXPECT_TRUE(SyncShareNudge());
2690 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2691 MutableEntry newfolder(&trans, CREATE, BOOKMARKS, trans.root_id(), "new");
2692 ASSERT_TRUE(newfolder.good());
2693 newfolder.PutIsDir(true);
2694 newfolder.PutSpecifics(DefaultBookmarkSpecifics());
2695 newfolder.PutIsUnsynced(true);
2697 MutableEntry existing(&trans, GET_BY_HANDLE, existing_metahandle);
2698 ASSERT_TRUE(existing.good());
2699 existing.PutParentId(newfolder.GetId());
2700 existing.PutIsUnsynced(true);
2701 EXPECT_TRUE(existing.GetId().ServerKnows());
2703 newfolder.PutIsDel(true);
2704 existing.PutIsDel(true);
2706 EXPECT_TRUE(SyncShareNudge());
2707 EXPECT_EQ(0, GetCommitCounters(BOOKMARKS).num_commits_conflict);
2710 TEST_F(SyncerTest, DeletingEntryWithLocalEdits) {
2711 int64 newfolder_metahandle;
2713 mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10,
2714 foreign_cache_guid(), "-1");
2715 EXPECT_TRUE(SyncShareNudge());
2717 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2718 MutableEntry newfolder(
2719 &trans, CREATE, BOOKMARKS, ids_.FromNumber(1), "local");
2720 ASSERT_TRUE(newfolder.good());
2721 newfolder.PutIsUnsynced(true);
2722 newfolder.PutIsDir(true);
2723 newfolder.PutSpecifics(DefaultBookmarkSpecifics());
2724 newfolder_metahandle = newfolder.GetMetahandle();
2726 mock_server_->AddUpdateDirectory(1, 0, "bob", 2, 20,
2727 foreign_cache_guid(), "-1");
2728 mock_server_->SetLastUpdateDeleted();
2729 SyncShareConfigure();
2731 syncable::ReadTransaction trans(FROM_HERE, directory());
2732 Entry entry(&trans, syncable::GET_BY_HANDLE, newfolder_metahandle);
2733 ASSERT_TRUE(entry.good());
2737 TEST_F(SyncerTest, FolderSwapUpdate) {
2738 mock_server_->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2739 foreign_cache_guid(), "-7801");
2740 mock_server_->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2741 foreign_cache_guid(), "-1024");
2742 EXPECT_TRUE(SyncShareNudge());
2743 mock_server_->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2744 foreign_cache_guid(), "-1024");
2745 mock_server_->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2746 foreign_cache_guid(), "-7801");
2747 EXPECT_TRUE(SyncShareNudge());
2749 syncable::ReadTransaction trans(FROM_HERE, directory());
2750 Entry id1(&trans, GET_BY_ID, ids_.FromNumber(7801));
2751 ASSERT_TRUE(id1.good());
2752 EXPECT_TRUE("fred" == id1.GetNonUniqueName());
2753 EXPECT_TRUE(root_id_ == id1.GetParentId());
2754 Entry id2(&trans, GET_BY_ID, ids_.FromNumber(1024));
2755 ASSERT_TRUE(id2.good());
2756 EXPECT_TRUE("bob" == id2.GetNonUniqueName());
2757 EXPECT_TRUE(root_id_ == id2.GetParentId());
2759 saw_syncer_event_ = false;
2762 TEST_F(SyncerTest, NameCollidingFolderSwapWorksFine) {
2763 mock_server_->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2764 foreign_cache_guid(), "-7801");
2765 mock_server_->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2766 foreign_cache_guid(), "-1024");
2767 mock_server_->AddUpdateDirectory(4096, 0, "alice", 1, 10,
2768 foreign_cache_guid(), "-4096");
2769 EXPECT_TRUE(SyncShareNudge());
2771 syncable::ReadTransaction trans(FROM_HERE, directory());
2772 Entry id1(&trans, GET_BY_ID, ids_.FromNumber(7801));
2773 ASSERT_TRUE(id1.good());
2774 EXPECT_TRUE("bob" == id1.GetNonUniqueName());
2775 EXPECT_TRUE(root_id_ == id1.GetParentId());
2776 Entry id2(&trans, GET_BY_ID, ids_.FromNumber(1024));
2777 ASSERT_TRUE(id2.good());
2778 EXPECT_TRUE("fred" == id2.GetNonUniqueName());
2779 EXPECT_TRUE(root_id_ == id2.GetParentId());
2780 Entry id3(&trans, GET_BY_ID, ids_.FromNumber(4096));
2781 ASSERT_TRUE(id3.good());
2782 EXPECT_TRUE("alice" == id3.GetNonUniqueName());
2783 EXPECT_TRUE(root_id_ == id3.GetParentId());
2785 mock_server_->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2786 foreign_cache_guid(), "-1024");
2787 mock_server_->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2788 foreign_cache_guid(), "-7801");
2789 mock_server_->AddUpdateDirectory(4096, 0, "bob", 2, 20,
2790 foreign_cache_guid(), "-4096");
2791 EXPECT_TRUE(SyncShareNudge());
2793 syncable::ReadTransaction trans(FROM_HERE, directory());
2794 Entry id1(&trans, GET_BY_ID, ids_.FromNumber(7801));
2795 ASSERT_TRUE(id1.good());
2796 EXPECT_TRUE("fred" == id1.GetNonUniqueName());
2797 EXPECT_TRUE(root_id_ == id1.GetParentId());
2798 Entry id2(&trans, GET_BY_ID, ids_.FromNumber(1024));
2799 ASSERT_TRUE(id2.good());
2800 EXPECT_TRUE("bob" == id2.GetNonUniqueName());
2801 EXPECT_TRUE(root_id_ == id2.GetParentId());
2802 Entry id3(&trans, GET_BY_ID, ids_.FromNumber(4096));
2803 ASSERT_TRUE(id3.good());
2804 EXPECT_TRUE("bob" == id3.GetNonUniqueName());
2805 EXPECT_TRUE(root_id_ == id3.GetParentId());
2807 saw_syncer_event_ = false;
2810 // Committing more than kDefaultMaxCommitBatchSize items requires that
2811 // we post more than one commit command to the server. This test makes
2812 // sure that scenario works as expected.
2813 TEST_F(SyncerTest, CommitManyItemsInOneGo_Success) {
2814 uint32 num_batches = 3;
2815 uint32 items_to_commit = kDefaultMaxCommitBatchSize * num_batches;
2817 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2818 for (uint32 i = 0; i < items_to_commit; i++) {
2819 string nameutf8 = base::UintToString(i);
2820 string name(nameutf8.begin(), nameutf8.end());
2821 MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
2822 e.PutIsUnsynced(true);
2823 e.PutIsDir(true);
2824 e.PutSpecifics(DefaultBookmarkSpecifics());
2827 ASSERT_EQ(items_to_commit, directory()->unsynced_entity_count());
2829 EXPECT_TRUE(SyncShareNudge());
2830 EXPECT_EQ(num_batches, mock_server_->commit_messages().size());
2831 EXPECT_EQ(0, directory()->unsynced_entity_count());
2834 // Test that a single failure to contact the server will cause us to exit the
2835 // commit loop immediately.
2836 TEST_F(SyncerTest, CommitManyItemsInOneGo_PostBufferFail) {
2837 uint32 num_batches = 3;
2838 uint32 items_to_commit = kDefaultMaxCommitBatchSize * num_batches;
2840 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2841 for (uint32 i = 0; i < items_to_commit; i++) {
2842 string nameutf8 = base::UintToString(i);
2843 string name(nameutf8.begin(), nameutf8.end());
2844 MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
2845 e.PutIsUnsynced(true);
2846 e.PutIsDir(true);
2847 e.PutSpecifics(DefaultBookmarkSpecifics());
2850 ASSERT_EQ(items_to_commit, directory()->unsynced_entity_count());
2852 // The second commit should fail. It will be preceded by one successful
2853 // GetUpdate and one succesful commit.
2854 mock_server_->FailNthPostBufferToPathCall(3);
2855 EXPECT_FALSE(SyncShareNudge());
2857 EXPECT_EQ(1U, mock_server_->commit_messages().size());
2858 EXPECT_EQ(SYNC_SERVER_ERROR,
2859 session_->status_controller().model_neutral_state().commit_result);
2860 EXPECT_EQ(items_to_commit - kDefaultMaxCommitBatchSize,
2861 directory()->unsynced_entity_count());
2864 // Test that a single conflict response from the server will cause us to exit
2865 // the commit loop immediately.
2866 TEST_F(SyncerTest, CommitManyItemsInOneGo_CommitConflict) {
2867 uint32 num_batches = 2;
2868 uint32 items_to_commit = kDefaultMaxCommitBatchSize * num_batches;
2870 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2871 for (uint32 i = 0; i < items_to_commit; i++) {
2872 string nameutf8 = base::UintToString(i);
2873 string name(nameutf8.begin(), nameutf8.end());
2874 MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
2875 e.PutIsUnsynced(true);
2876 e.PutIsDir(true);
2877 e.PutSpecifics(DefaultBookmarkSpecifics());
2880 ASSERT_EQ(items_to_commit, directory()->unsynced_entity_count());
2882 // Return a CONFLICT response for the first item.
2883 mock_server_->set_conflict_n_commits(1);
2884 EXPECT_FALSE(SyncShareNudge());
2886 // We should stop looping at the first sign of trouble.
2887 EXPECT_EQ(1U, mock_server_->commit_messages().size());
2888 EXPECT_EQ(items_to_commit - (kDefaultMaxCommitBatchSize - 1),
2889 directory()->unsynced_entity_count());
2892 // Tests that sending debug info events works.
2893 TEST_F(SyncerTest, SendDebugInfoEventsOnGetUpdates_HappyCase) {
2894 debug_info_getter_->AddDebugEvent();
2895 debug_info_getter_->AddDebugEvent();
2897 EXPECT_TRUE(SyncShareNudge());
2899 // Verify we received one GetUpdates request with two debug info events.
2900 EXPECT_EQ(1U, mock_server_->requests().size());
2901 ASSERT_TRUE(mock_server_->last_request().has_get_updates());
2902 EXPECT_EQ(2, mock_server_->last_request().debug_info().events_size());
2904 EXPECT_TRUE(SyncShareNudge());
2906 // See that we received another GetUpdates request, but that it contains no
2907 // debug info events.
2908 EXPECT_EQ(2U, mock_server_->requests().size());
2909 ASSERT_TRUE(mock_server_->last_request().has_get_updates());
2910 EXPECT_EQ(0, mock_server_->last_request().debug_info().events_size());
2912 debug_info_getter_->AddDebugEvent();
2914 EXPECT_TRUE(SyncShareNudge());
2916 // See that we received another GetUpdates request and it contains one debug
2917 // info event.
2918 EXPECT_EQ(3U, mock_server_->requests().size());
2919 ASSERT_TRUE(mock_server_->last_request().has_get_updates());
2920 EXPECT_EQ(1, mock_server_->last_request().debug_info().events_size());
2923 // Tests that debug info events are dropped on server error.
2924 TEST_F(SyncerTest, SendDebugInfoEventsOnGetUpdates_PostFailsDontDrop) {
2925 debug_info_getter_->AddDebugEvent();
2926 debug_info_getter_->AddDebugEvent();
2928 mock_server_->FailNextPostBufferToPathCall();
2929 EXPECT_FALSE(SyncShareNudge());
2931 // Verify we attempted to send one GetUpdates request with two debug info
2932 // events.
2933 EXPECT_EQ(1U, mock_server_->requests().size());
2934 ASSERT_TRUE(mock_server_->last_request().has_get_updates());
2935 EXPECT_EQ(2, mock_server_->last_request().debug_info().events_size());
2937 EXPECT_TRUE(SyncShareNudge());
2939 // See that the client resent the two debug info events.
2940 EXPECT_EQ(2U, mock_server_->requests().size());
2941 ASSERT_TRUE(mock_server_->last_request().has_get_updates());
2942 EXPECT_EQ(2, mock_server_->last_request().debug_info().events_size());
2944 // The previous send was successful so this next one shouldn't generate any
2945 // debug info events.
2946 EXPECT_TRUE(SyncShareNudge());
2947 EXPECT_EQ(3U, mock_server_->requests().size());
2948 ASSERT_TRUE(mock_server_->last_request().has_get_updates());
2949 EXPECT_EQ(0, mock_server_->last_request().debug_info().events_size());
2952 // Tests that commit failure with conflict will trigger GetUpdates for next
2953 // cycle of sync
2954 TEST_F(SyncerTest, CommitFailureWithConflict) {
2955 ConfigureNoGetUpdatesRequired();
2956 CreateUnsyncedDirectory("X", "id_X");
2957 EXPECT_FALSE(nudge_tracker_.IsGetUpdatesRequired());
2959 EXPECT_TRUE(SyncShareNudge());
2960 EXPECT_FALSE(nudge_tracker_.IsGetUpdatesRequired());
2962 CreateUnsyncedDirectory("Y", "id_Y");
2963 mock_server_->set_conflict_n_commits(1);
2964 EXPECT_FALSE(SyncShareNudge());
2965 EXPECT_TRUE(nudge_tracker_.IsGetUpdatesRequired());
2967 nudge_tracker_.RecordSuccessfulSyncCycle();
2968 EXPECT_FALSE(nudge_tracker_.IsGetUpdatesRequired());
2971 // Tests that sending debug info events on Commit works.
2972 TEST_F(SyncerTest, SendDebugInfoEventsOnCommit_HappyCase) {
2973 // Make sure GetUpdate isn't call as it would "steal" debug info events before
2974 // Commit has a chance to send them.
2975 ConfigureNoGetUpdatesRequired();
2977 // Generate a debug info event and trigger a commit.
2978 debug_info_getter_->AddDebugEvent();
2979 CreateUnsyncedDirectory("X", "id_X");
2980 EXPECT_TRUE(SyncShareNudge());
2982 // Verify that the last request received is a Commit and that it contains a
2983 // debug info event.
2984 EXPECT_EQ(1U, mock_server_->requests().size());
2985 ASSERT_TRUE(mock_server_->last_request().has_commit());
2986 EXPECT_EQ(1, mock_server_->last_request().debug_info().events_size());
2988 // Generate another commit, but no debug info event.
2989 CreateUnsyncedDirectory("Y", "id_Y");
2990 EXPECT_TRUE(SyncShareNudge());
2992 // See that it was received and contains no debug info events.
2993 EXPECT_EQ(2U, mock_server_->requests().size());
2994 ASSERT_TRUE(mock_server_->last_request().has_commit());
2995 EXPECT_EQ(0, mock_server_->last_request().debug_info().events_size());
2998 // Tests that debug info events are not dropped on server error.
2999 TEST_F(SyncerTest, SendDebugInfoEventsOnCommit_PostFailsDontDrop) {
3000 // Make sure GetUpdate isn't call as it would "steal" debug info events before
3001 // Commit has a chance to send them.
3002 ConfigureNoGetUpdatesRequired();
3004 mock_server_->FailNextPostBufferToPathCall();
3006 // Generate a debug info event and trigger a commit.
3007 debug_info_getter_->AddDebugEvent();
3008 CreateUnsyncedDirectory("X", "id_X");
3009 EXPECT_FALSE(SyncShareNudge());
3011 // Verify that the last request sent is a Commit and that it contains a debug
3012 // info event.
3013 EXPECT_EQ(1U, mock_server_->requests().size());
3014 ASSERT_TRUE(mock_server_->last_request().has_commit());
3015 EXPECT_EQ(1, mock_server_->last_request().debug_info().events_size());
3017 // Try again.
3018 EXPECT_TRUE(SyncShareNudge());
3020 // Verify that we've received another Commit and that it contains a debug info
3021 // event (just like the previous one).
3022 EXPECT_EQ(2U, mock_server_->requests().size());
3023 ASSERT_TRUE(mock_server_->last_request().has_commit());
3024 EXPECT_EQ(1, mock_server_->last_request().debug_info().events_size());
3026 // Generate another commit and try again.
3027 CreateUnsyncedDirectory("Y", "id_Y");
3028 EXPECT_TRUE(SyncShareNudge());
3030 // See that it was received and contains no debug info events.
3031 EXPECT_EQ(3U, mock_server_->requests().size());
3032 ASSERT_TRUE(mock_server_->last_request().has_commit());
3033 EXPECT_EQ(0, mock_server_->last_request().debug_info().events_size());
3036 TEST_F(SyncerTest, HugeConflict) {
3037 int item_count = 300; // We should be able to do 300 or 3000 w/o issue.
3039 syncable::Id parent_id = ids_.NewServerId();
3040 syncable::Id last_id = parent_id;
3041 vector<syncable::Id> tree_ids;
3043 // Create a lot of updates for which the parent does not exist yet.
3044 // Generate a huge deep tree which should all fail to apply at first.
3046 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3047 for (int i = 0; i < item_count ; i++) {
3048 syncable::Id next_id = ids_.NewServerId();
3049 syncable::Id local_id = ids_.NewLocalId();
3050 tree_ids.push_back(next_id);
3051 mock_server_->AddUpdateDirectory(next_id, last_id, "BOB", 2, 20,
3052 foreign_cache_guid(),
3053 local_id.GetServerId());
3054 last_id = next_id;
3057 EXPECT_TRUE(SyncShareNudge());
3059 // Check they're in the expected conflict state.
3061 syncable::ReadTransaction trans(FROM_HERE, directory());
3062 for (int i = 0; i < item_count; i++) {
3063 Entry e(&trans, GET_BY_ID, tree_ids[i]);
3064 // They should all exist but none should be applied.
3065 ASSERT_TRUE(e.good());
3066 EXPECT_TRUE(e.GetIsDel());
3067 EXPECT_TRUE(e.GetIsUnappliedUpdate());
3071 // Add the missing parent directory.
3072 mock_server_->AddUpdateDirectory(parent_id, TestIdFactory::root(),
3073 "BOB", 2, 20, foreign_cache_guid(), "-3500");
3074 EXPECT_TRUE(SyncShareNudge());
3076 // Now they should all be OK.
3078 syncable::ReadTransaction trans(FROM_HERE, directory());
3079 for (int i = 0; i < item_count; i++) {
3080 Entry e(&trans, GET_BY_ID, tree_ids[i]);
3081 ASSERT_TRUE(e.good());
3082 EXPECT_FALSE(e.GetIsDel());
3083 EXPECT_FALSE(e.GetIsUnappliedUpdate());
3088 TEST_F(SyncerTest, DontCrashOnCaseChange) {
3089 mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10,
3090 foreign_cache_guid(), "-1");
3091 EXPECT_TRUE(SyncShareNudge());
3093 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3094 MutableEntry e(&trans, GET_BY_ID, ids_.FromNumber(1));
3095 ASSERT_TRUE(e.good());
3096 e.PutIsUnsynced(true);
3098 mock_server_->set_conflict_all_commits(true);
3099 mock_server_->AddUpdateDirectory(1, 0, "BOB", 2, 20,
3100 foreign_cache_guid(), "-1");
3101 EXPECT_FALSE(SyncShareNudge()); // USED TO CAUSE AN ASSERT
3102 saw_syncer_event_ = false;
3105 TEST_F(SyncerTest, UnsyncedItemAndUpdate) {
3106 mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10,
3107 foreign_cache_guid(), "-1");
3108 EXPECT_TRUE(SyncShareNudge());
3109 mock_server_->set_conflict_all_commits(true);
3110 mock_server_->AddUpdateDirectory(2, 0, "bob", 2, 20,
3111 foreign_cache_guid(), "-2");
3112 EXPECT_TRUE(SyncShareNudge()); // USED TO CAUSE AN ASSERT
3113 saw_syncer_event_ = false;
3116 TEST_F(SyncerTest, NewEntryAndAlteredServerEntrySharePath) {
3117 mock_server_->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
3118 foreign_cache_guid(), "-1");
3119 EXPECT_TRUE(SyncShareNudge());
3120 int64 local_folder_handle;
3121 syncable::Id local_folder_id;
3123 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3124 MutableEntry new_entry(
3125 &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Bar.htm");
3126 ASSERT_TRUE(new_entry.good());
3127 local_folder_id = new_entry.GetId();
3128 local_folder_handle = new_entry.GetMetahandle();
3129 new_entry.PutIsUnsynced(true);
3130 new_entry.PutSpecifics(DefaultBookmarkSpecifics());
3131 MutableEntry old(&wtrans, GET_BY_ID, ids_.FromNumber(1));
3132 ASSERT_TRUE(old.good());
3133 WriteTestDataToEntry(&wtrans, &old);
3135 mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
3136 foreign_cache_guid(), "-1");
3137 mock_server_->set_conflict_all_commits(true);
3138 EXPECT_FALSE(SyncShareNudge());
3139 saw_syncer_event_ = false;
3141 // Update #20 should have been dropped in favor of the local version.
3142 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3143 MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1));
3144 MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle);
3145 ASSERT_TRUE(server.good());
3146 ASSERT_TRUE(local.good());
3147 EXPECT_TRUE(local.GetMetahandle()!= server.GetMetahandle());
3148 EXPECT_FALSE(server.GetIsUnappliedUpdate());
3149 EXPECT_FALSE(local.GetIsUnappliedUpdate());
3150 EXPECT_TRUE(server.GetIsUnsynced());
3151 EXPECT_TRUE(local.GetIsUnsynced());
3152 EXPECT_EQ("Foo.htm", server.GetNonUniqueName());
3153 EXPECT_EQ("Bar.htm", local.GetNonUniqueName());
3155 // Allow local changes to commit.
3156 mock_server_->set_conflict_all_commits(false);
3157 EXPECT_TRUE(SyncShareNudge());
3158 saw_syncer_event_ = false;
3160 // Now add a server change to make the two names equal. There should
3161 // be no conflict with that, since names are not unique.
3162 mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
3163 foreign_cache_guid(), "-1");
3164 EXPECT_TRUE(SyncShareNudge());
3165 saw_syncer_event_ = false;
3167 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3168 MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1));
3169 MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle);
3170 ASSERT_TRUE(server.good());
3171 ASSERT_TRUE(local.good());
3172 EXPECT_TRUE(local.GetMetahandle()!= server.GetMetahandle());
3173 EXPECT_FALSE(server.GetIsUnappliedUpdate());
3174 EXPECT_FALSE(local.GetIsUnappliedUpdate());
3175 EXPECT_FALSE(server.GetIsUnsynced());
3176 EXPECT_FALSE(local.GetIsUnsynced());
3177 EXPECT_EQ("Bar.htm", server.GetNonUniqueName());
3178 EXPECT_EQ("Bar.htm", local.GetNonUniqueName());
3179 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
3180 server.GetSpecifics().bookmark().url());
3184 // Same as NewEntryAnddServerEntrySharePath, but using the old-style protocol.
3185 TEST_F(SyncerTest, NewEntryAndAlteredServerEntrySharePath_OldBookmarksProto) {
3186 mock_server_->set_use_legacy_bookmarks_protocol(true);
3187 mock_server_->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
3188 foreign_cache_guid(), "-1");
3189 EXPECT_TRUE(SyncShareNudge());
3190 int64 local_folder_handle;
3191 syncable::Id local_folder_id;
3193 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3194 MutableEntry new_entry(
3195 &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Bar.htm");
3196 ASSERT_TRUE(new_entry.good());
3197 local_folder_id = new_entry.GetId();
3198 local_folder_handle = new_entry.GetMetahandle();
3199 new_entry.PutIsUnsynced(true);
3200 new_entry.PutSpecifics(DefaultBookmarkSpecifics());
3201 MutableEntry old(&wtrans, GET_BY_ID, ids_.FromNumber(1));
3202 ASSERT_TRUE(old.good());
3203 WriteTestDataToEntry(&wtrans, &old);
3205 mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
3206 foreign_cache_guid(), "-1");
3207 mock_server_->set_conflict_all_commits(true);
3208 EXPECT_FALSE(SyncShareNudge());
3209 saw_syncer_event_ = false;
3211 // Update #20 should have been dropped in favor of the local version.
3212 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3213 MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1));
3214 MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle);
3215 ASSERT_TRUE(server.good());
3216 ASSERT_TRUE(local.good());
3217 EXPECT_TRUE(local.GetMetahandle()!= server.GetMetahandle());
3218 EXPECT_FALSE(server.GetIsUnappliedUpdate());
3219 EXPECT_FALSE(local.GetIsUnappliedUpdate());
3220 EXPECT_TRUE(server.GetIsUnsynced());
3221 EXPECT_TRUE(local.GetIsUnsynced());
3222 EXPECT_EQ("Foo.htm", server.GetNonUniqueName());
3223 EXPECT_EQ("Bar.htm", local.GetNonUniqueName());
3225 // Allow local changes to commit.
3226 mock_server_->set_conflict_all_commits(false);
3227 EXPECT_TRUE(SyncShareNudge());
3228 saw_syncer_event_ = false;
3230 // Now add a server change to make the two names equal. There should
3231 // be no conflict with that, since names are not unique.
3232 mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
3233 foreign_cache_guid(), "-1");
3234 EXPECT_TRUE(SyncShareNudge());
3235 saw_syncer_event_ = false;
3237 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3238 MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1));
3239 MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle);
3240 ASSERT_TRUE(server.good());
3241 ASSERT_TRUE(local.good());
3242 EXPECT_TRUE(local.GetMetahandle()!= server.GetMetahandle());
3243 EXPECT_FALSE(server.GetIsUnappliedUpdate());
3244 EXPECT_FALSE(local.GetIsUnappliedUpdate());
3245 EXPECT_FALSE(server.GetIsUnsynced());
3246 EXPECT_FALSE(local.GetIsUnsynced());
3247 EXPECT_EQ("Bar.htm", server.GetNonUniqueName());
3248 EXPECT_EQ("Bar.htm", local.GetNonUniqueName());
3249 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
3250 server.GetSpecifics().bookmark().url());
3254 // Circular links should be resolved by the server.
3255 TEST_F(SyncerTest, SiblingDirectoriesBecomeCircular) {
3256 // we don't currently resolve this. This test ensures we don't.
3257 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
3258 foreign_cache_guid(), "-1");
3259 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10,
3260 foreign_cache_guid(), "-2");
3261 EXPECT_TRUE(SyncShareNudge());
3263 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3264 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
3265 ASSERT_TRUE(A.good());
3266 A.PutIsUnsynced(true);
3267 A.PutParentId(ids_.FromNumber(2));
3268 A.PutNonUniqueName("B");
3270 mock_server_->AddUpdateDirectory(2, 1, "A", 20, 20,
3271 foreign_cache_guid(), "-2");
3272 mock_server_->set_conflict_all_commits(true);
3273 EXPECT_FALSE(SyncShareNudge());
3274 saw_syncer_event_ = false;
3276 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3277 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
3278 ASSERT_TRUE(A.good());
3279 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
3280 ASSERT_TRUE(B.good());
3281 EXPECT_TRUE(A.GetNonUniqueName()== "B");
3282 EXPECT_TRUE(B.GetNonUniqueName()== "B");
3286 TEST_F(SyncerTest, SwapEntryNames) {
3287 // Simple transaction test.
3288 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
3289 foreign_cache_guid(), "-1");
3290 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10,
3291 foreign_cache_guid(), "-2");
3292 mock_server_->set_conflict_all_commits(true);
3293 EXPECT_TRUE(SyncShareNudge());
3295 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3296 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
3297 ASSERT_TRUE(A.good());
3298 A.PutIsUnsynced(true);
3299 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
3300 ASSERT_TRUE(B.good());
3301 B.PutIsUnsynced(true);
3302 A.PutNonUniqueName("C");
3303 B.PutNonUniqueName("A");
3304 A.PutNonUniqueName("B");
3306 EXPECT_FALSE(SyncShareNudge());
3307 saw_syncer_event_ = false;
3310 TEST_F(SyncerTest, DualDeletionWithNewItemNameClash) {
3311 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
3312 foreign_cache_guid(), "-1");
3313 mock_server_->AddUpdateBookmark(2, 0, "B", 10, 10,
3314 foreign_cache_guid(), "-2");
3315 mock_server_->set_conflict_all_commits(true);
3316 EXPECT_TRUE(SyncShareNudge());
3318 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3319 MutableEntry B(&trans, GET_BY_ID, ids_.FromNumber(2));
3320 ASSERT_TRUE(B.good());
3321 WriteTestDataToEntry(&trans, &B);
3322 B.PutIsDel(true);
3324 mock_server_->AddUpdateBookmark(2, 0, "A", 11, 11,
3325 foreign_cache_guid(), "-2");
3326 mock_server_->SetLastUpdateDeleted();
3327 EXPECT_TRUE(SyncShareNudge());
3329 syncable::ReadTransaction trans(FROM_HERE, directory());
3330 Entry B(&trans, GET_BY_ID, ids_.FromNumber(2));
3331 ASSERT_TRUE(B.good());
3332 EXPECT_FALSE(B.GetIsUnsynced());
3333 EXPECT_FALSE(B.GetIsUnappliedUpdate());
3335 saw_syncer_event_ = false;
3338 // When we undelete an entity as a result of conflict resolution, we reuse the
3339 // existing server id and preserve the old version, simply updating the server
3340 // version with the new non-deleted entity.
3341 TEST_F(SyncerTest, ResolveWeWroteTheyDeleted) {
3342 int64 bob_metahandle;
3344 mock_server_->AddUpdateBookmark(1, 0, "bob", 1, 10,
3345 foreign_cache_guid(), "-1");
3346 EXPECT_TRUE(SyncShareNudge());
3348 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3349 MutableEntry bob(&trans, GET_BY_ID, ids_.FromNumber(1));
3350 ASSERT_TRUE(bob.good());
3351 bob_metahandle = bob.GetMetahandle();
3352 WriteTestDataToEntry(&trans, &bob);
3354 mock_server_->AddUpdateBookmark(1, 0, "bob", 2, 10,
3355 foreign_cache_guid(), "-1");
3356 mock_server_->SetLastUpdateDeleted();
3357 mock_server_->set_conflict_all_commits(true);
3358 EXPECT_FALSE(SyncShareNudge());
3359 EXPECT_FALSE(SyncShareNudge());
3361 syncable::ReadTransaction trans(FROM_HERE, directory());
3362 Entry bob(&trans, GET_BY_HANDLE, bob_metahandle);
3363 ASSERT_TRUE(bob.good());
3364 EXPECT_TRUE(bob.GetIsUnsynced());
3365 EXPECT_TRUE(bob.GetId().ServerKnows());
3366 EXPECT_FALSE(bob.GetIsUnappliedUpdate());
3367 EXPECT_FALSE(bob.GetIsDel());
3368 EXPECT_EQ(2, bob.GetServerVersion());
3369 EXPECT_EQ(2, bob.GetBaseVersion());
3371 saw_syncer_event_ = false;
3374 // This test is to reproduce a check failure. Sometimes we would get a bad ID
3375 // back when creating an entry.
3376 TEST_F(SyncerTest, DuplicateIDReturn) {
3378 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3379 MutableEntry folder(&trans, CREATE, BOOKMARKS, trans.root_id(), "bob");
3380 ASSERT_TRUE(folder.good());
3381 folder.PutIsUnsynced(true);
3382 folder.PutIsDir(true);
3383 folder.PutSpecifics(DefaultBookmarkSpecifics());
3384 MutableEntry folder2(&trans, CREATE, BOOKMARKS, trans.root_id(), "fred");
3385 ASSERT_TRUE(folder2.good());
3386 folder2.PutIsUnsynced(false);
3387 folder2.PutIsDir(true);
3388 folder2.PutSpecifics(DefaultBookmarkSpecifics());
3389 folder2.PutBaseVersion(3);
3390 folder2.PutId(syncable::Id::CreateFromServerId("mock_server:10000"));
3392 mock_server_->set_next_new_id(10000);
3393 EXPECT_EQ(1u, directory()->unsynced_entity_count());
3394 // we get back a bad id in here (should never happen).
3395 EXPECT_FALSE(SyncShareNudge());
3396 EXPECT_EQ(1u, directory()->unsynced_entity_count());
3397 EXPECT_TRUE(SyncShareNudge()); // another bad id in here.
3398 EXPECT_EQ(0u, directory()->unsynced_entity_count());
3399 saw_syncer_event_ = false;
3402 TEST_F(SyncerTest, DeletedEntryWithBadParentInLoopCalculation) {
3403 mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10,
3404 foreign_cache_guid(), "-1");
3405 EXPECT_TRUE(SyncShareNudge());
3407 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3408 MutableEntry bob(&trans, GET_BY_ID, ids_.FromNumber(1));
3409 ASSERT_TRUE(bob.good());
3410 // This is valid, because the parent could have gone away a long time ago.
3411 bob.PutParentId(ids_.FromNumber(54));
3412 bob.PutIsDel(true);
3413 bob.PutIsUnsynced(true);
3415 mock_server_->AddUpdateDirectory(2, 1, "fred", 1, 10,
3416 foreign_cache_guid(), "-2");
3417 EXPECT_TRUE(SyncShareNudge());
3418 EXPECT_TRUE(SyncShareNudge());
3421 TEST_F(SyncerTest, ConflictResolverMergesLocalDeleteAndServerUpdate) {
3422 syncable::Id local_id;
3424 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3426 MutableEntry local_deleted(
3427 &trans, CREATE, BOOKMARKS, trans.root_id(), "name");
3428 local_id = local_deleted.GetId();
3429 local_deleted.PutId(ids_.FromNumber(1));
3430 local_deleted.PutBaseVersion(1);
3431 local_deleted.PutIsDel(true);
3432 local_deleted.PutIsDir(false);
3433 local_deleted.PutIsUnsynced(true);
3434 local_deleted.PutSpecifics(DefaultBookmarkSpecifics());
3437 mock_server_->AddUpdateBookmark(ids_.FromNumber(1), root_id_, "name", 10, 10,
3438 local_cache_guid(),
3439 local_id.GetServerId());
3441 // We don't care about actually committing, just the resolution.
3442 mock_server_->set_conflict_all_commits(true);
3443 EXPECT_FALSE(SyncShareNudge());
3446 syncable::ReadTransaction trans(FROM_HERE, directory());
3447 Entry local_deleted(&trans, GET_BY_ID, ids_.FromNumber(1));
3448 EXPECT_TRUE(local_deleted.GetBaseVersion()== 10);
3449 EXPECT_TRUE(local_deleted.GetIsUnappliedUpdate()== false);
3450 EXPECT_TRUE(local_deleted.GetIsUnsynced()== true);
3451 EXPECT_TRUE(local_deleted.GetIsDel()== true);
3452 EXPECT_TRUE(local_deleted.GetIsDir()== false);
3456 // See what happens if the IS_DIR bit gets flipped. This can cause us
3457 // all kinds of disasters.
3458 TEST_F(SyncerTest, UpdateFlipsTheFolderBit) {
3459 // Local object: a deleted directory (container), revision 1, unsynced.
3461 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3463 MutableEntry local_deleted(
3464 &trans, CREATE, BOOKMARKS, trans.root_id(), "name");
3465 local_deleted.PutId(ids_.FromNumber(1));
3466 local_deleted.PutBaseVersion(1);
3467 local_deleted.PutIsDel(true);
3468 local_deleted.PutIsDir(true);
3469 local_deleted.PutIsUnsynced(true);
3470 local_deleted.PutSpecifics(DefaultBookmarkSpecifics());
3473 // Server update: entry-type object (not a container), revision 10.
3474 mock_server_->AddUpdateBookmark(ids_.FromNumber(1), root_id_, "name", 10, 10,
3475 local_cache_guid(),
3476 ids_.FromNumber(1).GetServerId());
3478 // Don't attempt to commit.
3479 mock_server_->set_conflict_all_commits(true);
3481 // The syncer should not attempt to apply the invalid update.
3482 EXPECT_FALSE(SyncShareNudge());
3485 syncable::ReadTransaction trans(FROM_HERE, directory());
3486 Entry local_deleted(&trans, GET_BY_ID, ids_.FromNumber(1));
3487 EXPECT_TRUE(local_deleted.GetBaseVersion()== 1);
3488 EXPECT_TRUE(local_deleted.GetIsUnappliedUpdate()== false);
3489 EXPECT_TRUE(local_deleted.GetIsUnsynced()== true);
3490 EXPECT_TRUE(local_deleted.GetIsDel()== true);
3491 EXPECT_TRUE(local_deleted.GetIsDir()== true);
3495 // Bug Synopsis:
3496 // Merge conflict resolution will merge a new local entry with another entry
3497 // that needs updates, resulting in CHECK.
3498 TEST_F(SyncerTest, MergingExistingItems) {
3499 mock_server_->set_conflict_all_commits(true);
3500 mock_server_->AddUpdateBookmark(1, 0, "base", 10, 10,
3501 local_cache_guid(), "-1");
3502 EXPECT_TRUE(SyncShareNudge());
3504 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3505 MutableEntry entry(
3506 &trans, CREATE, BOOKMARKS, trans.root_id(), "Copy of base");
3507 WriteTestDataToEntry(&trans, &entry);
3509 mock_server_->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
3510 local_cache_guid(), "-1");
3511 EXPECT_FALSE(SyncShareNudge());
3514 // In this test a long changelog contains a child at the start of the changelog
3515 // and a parent at the end. While these updates are in progress the client would
3516 // appear stuck.
3517 TEST_F(SyncerTest, LongChangelistWithApplicationConflict) {
3518 const int depth = 400;
3519 syncable::Id folder_id = ids_.FromNumber(1);
3521 // First we an item in a folder in the root. However the folder won't come
3522 // till much later.
3523 syncable::Id stuck_entry_id = TestIdFactory::FromNumber(99999);
3524 mock_server_->AddUpdateDirectory(stuck_entry_id,
3525 folder_id, "stuck", 1, 1,
3526 foreign_cache_guid(), "-99999");
3527 mock_server_->SetChangesRemaining(depth - 1);
3528 EXPECT_TRUE(SyncShareNudge());
3530 // Buffer up a very long series of downloads.
3531 // We should never be stuck (conflict resolution shouldn't
3532 // kick in so long as we're making forward progress).
3533 for (int i = 0; i < depth; i++) {
3534 mock_server_->NextUpdateBatch();
3535 mock_server_->SetNewTimestamp(i + 1);
3536 mock_server_->SetChangesRemaining(depth - i);
3539 EXPECT_TRUE(SyncShareNudge());
3541 // Ensure our folder hasn't somehow applied.
3543 syncable::ReadTransaction trans(FROM_HERE, directory());
3544 Entry child(&trans, GET_BY_ID, stuck_entry_id);
3545 EXPECT_TRUE(child.good());
3546 EXPECT_TRUE(child.GetIsUnappliedUpdate());
3547 EXPECT_TRUE(child.GetIsDel());
3548 EXPECT_FALSE(child.GetIsUnsynced());
3551 // And finally the folder.
3552 mock_server_->AddUpdateDirectory(folder_id,
3553 TestIdFactory::root(), "folder", 1, 1,
3554 foreign_cache_guid(), "-1");
3555 mock_server_->SetChangesRemaining(0);
3556 EXPECT_TRUE(SyncShareNudge());
3557 EXPECT_TRUE(SyncShareNudge());
3558 // Check that everything is as expected after the commit.
3560 syncable::ReadTransaction trans(FROM_HERE, directory());
3561 Entry entry(&trans, GET_BY_ID, folder_id);
3562 ASSERT_TRUE(entry.good());
3563 Entry child(&trans, GET_BY_ID, stuck_entry_id);
3564 EXPECT_EQ(entry.GetId(), child.GetParentId());
3565 EXPECT_EQ("stuck", child.GetNonUniqueName());
3566 EXPECT_TRUE(child.good());
3570 TEST_F(SyncerTest, DontMergeTwoExistingItems) {
3571 mock_server_->set_conflict_all_commits(true);
3572 mock_server_->AddUpdateBookmark(1, 0, "base", 10, 10,
3573 foreign_cache_guid(), "-1");
3574 mock_server_->AddUpdateBookmark(2, 0, "base2", 10, 10,
3575 foreign_cache_guid(), "-2");
3576 EXPECT_TRUE(SyncShareNudge());
3578 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3579 MutableEntry entry(&trans, GET_BY_ID, ids_.FromNumber(2));
3580 ASSERT_TRUE(entry.good());
3581 entry.PutNonUniqueName("Copy of base");
3582 entry.PutIsUnsynced(true);
3584 mock_server_->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
3585 foreign_cache_guid(), "-1");
3586 EXPECT_FALSE(SyncShareNudge());
3588 syncable::ReadTransaction trans(FROM_HERE, directory());
3589 Entry entry1(&trans, GET_BY_ID, ids_.FromNumber(1));
3590 EXPECT_FALSE(entry1.GetIsUnappliedUpdate());
3591 EXPECT_FALSE(entry1.GetIsUnsynced());
3592 EXPECT_FALSE(entry1.GetIsDel());
3593 Entry entry2(&trans, GET_BY_ID, ids_.FromNumber(2));
3594 EXPECT_FALSE(entry2.GetIsUnappliedUpdate());
3595 EXPECT_TRUE(entry2.GetIsUnsynced());
3596 EXPECT_FALSE(entry2.GetIsDel());
3597 EXPECT_EQ(entry1.GetNonUniqueName(), entry2.GetNonUniqueName());
3601 TEST_F(SyncerTest, TestUndeleteUpdate) {
3602 mock_server_->set_conflict_all_commits(true);
3603 mock_server_->AddUpdateDirectory(1, 0, "foo", 1, 1,
3604 foreign_cache_guid(), "-1");
3605 mock_server_->AddUpdateDirectory(2, 1, "bar", 1, 2,
3606 foreign_cache_guid(), "-2");
3607 EXPECT_TRUE(SyncShareNudge());
3608 mock_server_->AddUpdateDirectory(2, 1, "bar", 2, 3,
3609 foreign_cache_guid(), "-2");
3610 mock_server_->SetLastUpdateDeleted();
3611 EXPECT_TRUE(SyncShareNudge());
3613 int64 metahandle;
3615 syncable::ReadTransaction trans(FROM_HERE, directory());
3616 Entry entry(&trans, GET_BY_ID, ids_.FromNumber(2));
3617 ASSERT_TRUE(entry.good());
3618 EXPECT_TRUE(entry.GetIsDel());
3619 metahandle = entry.GetMetahandle();
3621 mock_server_->AddUpdateDirectory(1, 0, "foo", 2, 4,
3622 foreign_cache_guid(), "-1");
3623 mock_server_->SetLastUpdateDeleted();
3624 EXPECT_TRUE(SyncShareNudge());
3625 // This used to be rejected as it's an undeletion. Now, it results in moving
3626 // the delete path aside.
3627 mock_server_->AddUpdateDirectory(2, 1, "bar", 3, 5,
3628 foreign_cache_guid(), "-2");
3629 EXPECT_TRUE(SyncShareNudge());
3631 syncable::ReadTransaction trans(FROM_HERE, directory());
3632 Entry entry(&trans, GET_BY_ID, ids_.FromNumber(2));
3633 ASSERT_TRUE(entry.good());
3634 EXPECT_TRUE(entry.GetIsDel());
3635 EXPECT_FALSE(entry.GetServerIsDel());
3636 EXPECT_TRUE(entry.GetIsUnappliedUpdate());
3637 EXPECT_NE(entry.GetMetahandle(), metahandle);
3641 TEST_F(SyncerTest, TestMoveSanitizedNamedFolder) {
3642 mock_server_->AddUpdateDirectory(1, 0, "foo", 1, 1,
3643 foreign_cache_guid(), "-1");
3644 mock_server_->AddUpdateDirectory(2, 0, ":::", 1, 2,
3645 foreign_cache_guid(), "-2");
3646 EXPECT_TRUE(SyncShareNudge());
3648 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3649 MutableEntry entry(&trans, GET_BY_ID, ids_.FromNumber(2));
3650 ASSERT_TRUE(entry.good());
3651 entry.PutParentId(ids_.FromNumber(1));
3652 EXPECT_TRUE(entry.PutIsUnsynced(true));
3654 EXPECT_TRUE(SyncShareNudge());
3655 // We use the same sync ts as before so our times match up.
3656 mock_server_->AddUpdateDirectory(2, 1, ":::", 2, 2,
3657 foreign_cache_guid(), "-2");
3658 EXPECT_TRUE(SyncShareNudge());
3661 // Don't crash when this occurs.
3662 TEST_F(SyncerTest, UpdateWhereParentIsNotAFolder) {
3663 mock_server_->AddUpdateBookmark(1, 0, "B", 10, 10,
3664 foreign_cache_guid(), "-1");
3665 mock_server_->AddUpdateDirectory(2, 1, "BookmarkParent", 10, 10,
3666 foreign_cache_guid(), "-2");
3667 // Used to cause a CHECK
3668 EXPECT_TRUE(SyncShareNudge());
3670 syncable::ReadTransaction rtrans(FROM_HERE, directory());
3671 Entry good_entry(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1));
3672 ASSERT_TRUE(good_entry.good());
3673 EXPECT_FALSE(good_entry.GetIsUnappliedUpdate());
3674 Entry bad_parent(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(2));
3675 ASSERT_TRUE(bad_parent.good());
3676 EXPECT_TRUE(bad_parent.GetIsUnappliedUpdate());
3680 TEST_F(SyncerTest, DirectoryUpdateTest) {
3681 Id in_root_id = ids_.NewServerId();
3682 Id in_in_root_id = ids_.NewServerId();
3684 mock_server_->AddUpdateDirectory(in_root_id, TestIdFactory::root(),
3685 "in_root_name", 2, 2,
3686 foreign_cache_guid(), "-1");
3687 mock_server_->AddUpdateDirectory(in_in_root_id, in_root_id,
3688 "in_in_root_name", 3, 3,
3689 foreign_cache_guid(), "-2");
3690 EXPECT_TRUE(SyncShareNudge());
3692 syncable::ReadTransaction trans(FROM_HERE, directory());
3693 Entry in_root(&trans, GET_BY_ID, in_root_id);
3694 ASSERT_TRUE(in_root.good());
3695 EXPECT_EQ("in_root_name", in_root.GetNonUniqueName());
3696 EXPECT_EQ(TestIdFactory::root(), in_root.GetParentId());
3698 Entry in_in_root(&trans, GET_BY_ID, in_in_root_id);
3699 ASSERT_TRUE(in_in_root.good());
3700 EXPECT_EQ("in_in_root_name", in_in_root.GetNonUniqueName());
3701 EXPECT_EQ(in_root_id, in_in_root.GetParentId());
3705 TEST_F(SyncerTest, DirectoryCommitTest) {
3706 syncable::Id in_root_id, in_dir_id;
3707 int64 foo_metahandle;
3708 int64 bar_metahandle;
3711 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3712 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, root_id_, "foo");
3713 ASSERT_TRUE(parent.good());
3714 parent.PutIsUnsynced(true);
3715 parent.PutIsDir(true);
3716 parent.PutSpecifics(DefaultBookmarkSpecifics());
3717 in_root_id = parent.GetId();
3718 foo_metahandle = parent.GetMetahandle();
3720 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent.GetId(), "bar");
3721 ASSERT_TRUE(child.good());
3722 child.PutIsUnsynced(true);
3723 child.PutIsDir(true);
3724 child.PutSpecifics(DefaultBookmarkSpecifics());
3725 bar_metahandle = child.GetMetahandle();
3726 in_dir_id = parent.GetId();
3728 EXPECT_TRUE(SyncShareNudge());
3730 syncable::ReadTransaction trans(FROM_HERE, directory());
3731 Entry fail_by_old_id_entry(&trans, GET_BY_ID, in_root_id);
3732 ASSERT_FALSE(fail_by_old_id_entry.good());
3734 Entry foo_entry(&trans, GET_BY_HANDLE, foo_metahandle);
3735 ASSERT_TRUE(foo_entry.good());
3736 EXPECT_EQ("foo", foo_entry.GetNonUniqueName());
3737 EXPECT_NE(foo_entry.GetId(), in_root_id);
3739 Entry bar_entry(&trans, GET_BY_HANDLE, bar_metahandle);
3740 ASSERT_TRUE(bar_entry.good());
3741 EXPECT_EQ("bar", bar_entry.GetNonUniqueName());
3742 EXPECT_NE(bar_entry.GetId(), in_dir_id);
3743 EXPECT_EQ(foo_entry.GetId(), bar_entry.GetParentId());
3747 TEST_F(SyncerTest, TestClientCommandDuringUpdate) {
3748 using sync_pb::ClientCommand;
3750 ClientCommand* command = new ClientCommand();
3751 command->set_set_sync_poll_interval(8);
3752 command->set_set_sync_long_poll_interval(800);
3753 command->set_sessions_commit_delay_seconds(3141);
3754 sync_pb::CustomNudgeDelay* bookmark_delay =
3755 command->add_custom_nudge_delays();
3756 bookmark_delay->set_datatype_id(
3757 GetSpecificsFieldNumberFromModelType(BOOKMARKS));
3758 bookmark_delay->set_delay_ms(950);
3759 command->set_client_invalidation_hint_buffer_size(11);
3760 mock_server_->AddUpdateDirectory(1, 0, "in_root", 1, 1,
3761 foreign_cache_guid(), "-1");
3762 mock_server_->SetGUClientCommand(command);
3763 EXPECT_TRUE(SyncShareNudge());
3765 EXPECT_EQ(TimeDelta::FromSeconds(8), last_short_poll_interval_received_);
3766 EXPECT_EQ(TimeDelta::FromSeconds(800), last_long_poll_interval_received_);
3767 EXPECT_EQ(TimeDelta::FromSeconds(3141), last_sessions_commit_delay_);
3768 EXPECT_EQ(TimeDelta::FromMilliseconds(950), last_bookmarks_commit_delay_);
3769 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_);
3771 command = new ClientCommand();
3772 command->set_set_sync_poll_interval(180);
3773 command->set_set_sync_long_poll_interval(190);
3774 command->set_sessions_commit_delay_seconds(2718);
3775 bookmark_delay = command->add_custom_nudge_delays();
3776 bookmark_delay->set_datatype_id(
3777 GetSpecificsFieldNumberFromModelType(BOOKMARKS));
3778 bookmark_delay->set_delay_ms(1050);
3779 command->set_client_invalidation_hint_buffer_size(9);
3780 mock_server_->AddUpdateDirectory(
3781 1, 0, "in_root", 1, 1, foreign_cache_guid(), "-1");
3782 mock_server_->SetGUClientCommand(command);
3783 EXPECT_TRUE(SyncShareNudge());
3785 EXPECT_EQ(TimeDelta::FromSeconds(180), last_short_poll_interval_received_);
3786 EXPECT_EQ(TimeDelta::FromSeconds(190), last_long_poll_interval_received_);
3787 EXPECT_EQ(TimeDelta::FromSeconds(2718), last_sessions_commit_delay_);
3788 EXPECT_EQ(TimeDelta::FromMilliseconds(1050), last_bookmarks_commit_delay_);
3789 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_);
3792 TEST_F(SyncerTest, TestClientCommandDuringCommit) {
3793 using sync_pb::ClientCommand;
3795 ClientCommand* command = new ClientCommand();
3796 command->set_set_sync_poll_interval(8);
3797 command->set_set_sync_long_poll_interval(800);
3798 command->set_sessions_commit_delay_seconds(3141);
3799 sync_pb::CustomNudgeDelay* bookmark_delay =
3800 command->add_custom_nudge_delays();
3801 bookmark_delay->set_datatype_id(
3802 GetSpecificsFieldNumberFromModelType(BOOKMARKS));
3803 bookmark_delay->set_delay_ms(950);
3804 command->set_client_invalidation_hint_buffer_size(11);
3805 CreateUnsyncedDirectory("X", "id_X");
3806 mock_server_->SetCommitClientCommand(command);
3807 EXPECT_TRUE(SyncShareNudge());
3809 EXPECT_EQ(TimeDelta::FromSeconds(8), last_short_poll_interval_received_);
3810 EXPECT_EQ(TimeDelta::FromSeconds(800), last_long_poll_interval_received_);
3811 EXPECT_EQ(TimeDelta::FromSeconds(3141), last_sessions_commit_delay_);
3812 EXPECT_EQ(TimeDelta::FromMilliseconds(950), last_bookmarks_commit_delay_);
3813 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_);
3815 command = new ClientCommand();
3816 command->set_set_sync_poll_interval(180);
3817 command->set_set_sync_long_poll_interval(190);
3818 command->set_sessions_commit_delay_seconds(2718);
3819 bookmark_delay = command->add_custom_nudge_delays();
3820 bookmark_delay->set_datatype_id(
3821 GetSpecificsFieldNumberFromModelType(BOOKMARKS));
3822 bookmark_delay->set_delay_ms(1050);
3823 command->set_client_invalidation_hint_buffer_size(9);
3824 CreateUnsyncedDirectory("Y", "id_Y");
3825 mock_server_->SetCommitClientCommand(command);
3826 EXPECT_TRUE(SyncShareNudge());
3828 EXPECT_EQ(TimeDelta::FromSeconds(180), last_short_poll_interval_received_);
3829 EXPECT_EQ(TimeDelta::FromSeconds(190), last_long_poll_interval_received_);
3830 EXPECT_EQ(TimeDelta::FromSeconds(2718), last_sessions_commit_delay_);
3831 EXPECT_EQ(TimeDelta::FromMilliseconds(1050), last_bookmarks_commit_delay_);
3832 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_);
3835 TEST_F(SyncerTest, EnsureWeSendUpOldParent) {
3836 syncable::Id folder_one_id = ids_.FromNumber(1);
3837 syncable::Id folder_two_id = ids_.FromNumber(2);
3839 mock_server_->AddUpdateDirectory(folder_one_id, TestIdFactory::root(),
3840 "folder_one", 1, 1, foreign_cache_guid(), "-1");
3841 mock_server_->AddUpdateDirectory(folder_two_id, TestIdFactory::root(),
3842 "folder_two", 1, 1, foreign_cache_guid(), "-2");
3843 EXPECT_TRUE(SyncShareNudge());
3845 // A moved entry should send an "old parent."
3846 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3847 MutableEntry entry(&trans, GET_BY_ID, folder_one_id);
3848 ASSERT_TRUE(entry.good());
3849 entry.PutParentId(folder_two_id);
3850 entry.PutIsUnsynced(true);
3851 // A new entry should send no "old parent."
3852 MutableEntry create(
3853 &trans, CREATE, BOOKMARKS, trans.root_id(), "new_folder");
3854 create.PutIsUnsynced(true);
3855 create.PutSpecifics(DefaultBookmarkSpecifics());
3857 EXPECT_TRUE(SyncShareNudge());
3858 const sync_pb::CommitMessage& commit = mock_server_->last_sent_commit();
3859 ASSERT_EQ(2, commit.entries_size());
3860 EXPECT_TRUE(commit.entries(0).parent_id_string() == "2");
3861 EXPECT_TRUE(commit.entries(0).old_parent_id() == "0");
3862 EXPECT_FALSE(commit.entries(1).has_old_parent_id());
3865 TEST_F(SyncerTest, Test64BitVersionSupport) {
3866 int64 really_big_int = std::numeric_limits<int64>::max() - 12;
3867 const string name("ringo's dang orang ran rings around my o-ring");
3868 int64 item_metahandle;
3870 // Try writing max int64 to the version fields of a meta entry.
3872 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3873 MutableEntry entry(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), name);
3874 ASSERT_TRUE(entry.good());
3875 entry.PutBaseVersion(really_big_int);
3876 entry.PutServerVersion(really_big_int);
3877 entry.PutId(ids_.NewServerId());
3878 item_metahandle = entry.GetMetahandle();
3880 // Now read it back out and make sure the value is max int64.
3881 syncable::ReadTransaction rtrans(FROM_HERE, directory());
3882 Entry entry(&rtrans, syncable::GET_BY_HANDLE, item_metahandle);
3883 ASSERT_TRUE(entry.good());
3884 EXPECT_TRUE(really_big_int == entry.GetBaseVersion());
3887 TEST_F(SyncerTest, TestSimpleUndelete) {
3888 Id id = ids_.MakeServer("undeletion item"), root = TestIdFactory::root();
3889 mock_server_->set_conflict_all_commits(true);
3890 // Let there be an entry from the server.
3891 mock_server_->AddUpdateBookmark(id, root, "foo", 1, 10,
3892 foreign_cache_guid(), "-1");
3893 EXPECT_TRUE(SyncShareNudge());
3894 // Check it out and delete it.
3896 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3897 MutableEntry entry(&wtrans, GET_BY_ID, id);
3898 ASSERT_TRUE(entry.good());
3899 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3900 EXPECT_FALSE(entry.GetIsUnsynced());
3901 EXPECT_FALSE(entry.GetIsDel());
3902 // Delete it locally.
3903 entry.PutIsDel(true);
3905 EXPECT_TRUE(SyncShareNudge());
3906 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3908 syncable::ReadTransaction trans(FROM_HERE, directory());
3909 Entry entry(&trans, GET_BY_ID, id);
3910 ASSERT_TRUE(entry.good());
3911 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3912 EXPECT_FALSE(entry.GetIsUnsynced());
3913 EXPECT_TRUE(entry.GetIsDel());
3914 EXPECT_FALSE(entry.GetServerIsDel());
3916 EXPECT_TRUE(SyncShareNudge());
3917 // Update from server confirming deletion.
3918 mock_server_->AddUpdateBookmark(id, root, "foo", 2, 11,
3919 foreign_cache_guid(), "-1");
3920 mock_server_->SetLastUpdateDeleted();
3921 EXPECT_TRUE(SyncShareNudge());
3922 // IS_DEL AND SERVER_IS_DEL now both true.
3924 syncable::ReadTransaction trans(FROM_HERE, directory());
3925 Entry entry(&trans, GET_BY_ID, id);
3926 ASSERT_TRUE(entry.good());
3927 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3928 EXPECT_FALSE(entry.GetIsUnsynced());
3929 EXPECT_TRUE(entry.GetIsDel());
3930 EXPECT_TRUE(entry.GetServerIsDel());
3932 // Undelete from server.
3933 mock_server_->AddUpdateBookmark(id, root, "foo", 2, 12,
3934 foreign_cache_guid(), "-1");
3935 EXPECT_TRUE(SyncShareNudge());
3936 // IS_DEL and SERVER_IS_DEL now both false.
3938 syncable::ReadTransaction trans(FROM_HERE, directory());
3939 Entry entry(&trans, GET_BY_ID, id);
3940 ASSERT_TRUE(entry.good());
3941 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3942 EXPECT_FALSE(entry.GetIsUnsynced());
3943 EXPECT_FALSE(entry.GetIsDel());
3944 EXPECT_FALSE(entry.GetServerIsDel());
3948 TEST_F(SyncerTest, TestUndeleteWithMissingDeleteUpdate) {
3949 Id id = ids_.MakeServer("undeletion item"), root = TestIdFactory::root();
3950 // Let there be a entry, from the server.
3951 mock_server_->set_conflict_all_commits(true);
3952 mock_server_->AddUpdateBookmark(id, root, "foo", 1, 10,
3953 foreign_cache_guid(), "-1");
3954 EXPECT_TRUE(SyncShareNudge());
3955 // Check it out and delete it.
3957 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3958 MutableEntry entry(&wtrans, GET_BY_ID, id);
3959 ASSERT_TRUE(entry.good());
3960 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3961 EXPECT_FALSE(entry.GetIsUnsynced());
3962 EXPECT_FALSE(entry.GetIsDel());
3963 // Delete it locally.
3964 entry.PutIsDel(true);
3966 EXPECT_TRUE(SyncShareNudge());
3967 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3969 syncable::ReadTransaction trans(FROM_HERE, directory());
3970 Entry entry(&trans, GET_BY_ID, id);
3971 ASSERT_TRUE(entry.good());
3972 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3973 EXPECT_FALSE(entry.GetIsUnsynced());
3974 EXPECT_TRUE(entry.GetIsDel());
3975 EXPECT_FALSE(entry.GetServerIsDel());
3977 EXPECT_TRUE(SyncShareNudge());
3978 // Say we do not get an update from server confirming deletion. Undelete
3979 // from server
3980 mock_server_->AddUpdateBookmark(id, root, "foo", 2, 12,
3981 foreign_cache_guid(), "-1");
3982 EXPECT_TRUE(SyncShareNudge());
3983 // IS_DEL and SERVER_IS_DEL now both false.
3985 syncable::ReadTransaction trans(FROM_HERE, directory());
3986 Entry entry(&trans, GET_BY_ID, id);
3987 ASSERT_TRUE(entry.good());
3988 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3989 EXPECT_FALSE(entry.GetIsUnsynced());
3990 EXPECT_FALSE(entry.GetIsDel());
3991 EXPECT_FALSE(entry.GetServerIsDel());
3995 TEST_F(SyncerTest, TestUndeleteIgnoreCorrectlyUnappliedUpdate) {
3996 Id id1 = ids_.MakeServer("first"), id2 = ids_.MakeServer("second");
3997 Id root = TestIdFactory::root();
3998 // Duplicate! expect path clashing!
3999 mock_server_->set_conflict_all_commits(true);
4000 mock_server_->AddUpdateBookmark(id1, root, "foo", 1, 10,
4001 foreign_cache_guid(), "-1");
4002 mock_server_->AddUpdateBookmark(id2, root, "foo", 1, 10,
4003 foreign_cache_guid(), "-2");
4004 EXPECT_TRUE(SyncShareNudge());
4005 mock_server_->AddUpdateBookmark(id2, root, "foo2", 2, 20,
4006 foreign_cache_guid(), "-2");
4007 EXPECT_TRUE(SyncShareNudge()); // Now just don't explode.
4010 TEST_F(SyncerTest, ClientTagServerCreatedUpdatesWork) {
4011 mock_server_->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
4012 foreign_cache_guid(), "-1");
4013 mock_server_->SetLastUpdateClientTag("permfolder");
4015 EXPECT_TRUE(SyncShareNudge());
4018 syncable::ReadTransaction trans(FROM_HERE, directory());
4019 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
4020 ASSERT_TRUE(perm_folder.good());
4021 EXPECT_FALSE(perm_folder.GetIsDel());
4022 EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate());
4023 EXPECT_FALSE(perm_folder.GetIsUnsynced());
4024 EXPECT_EQ(perm_folder.GetUniqueClientTag(), "permfolder");
4025 EXPECT_EQ(perm_folder.GetNonUniqueName(), "permitem1");
4028 mock_server_->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
4029 foreign_cache_guid(), "-1");
4030 mock_server_->SetLastUpdateClientTag("permfolder");
4031 EXPECT_TRUE(SyncShareNudge());
4034 syncable::ReadTransaction trans(FROM_HERE, directory());
4036 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
4037 ASSERT_TRUE(perm_folder.good());
4038 EXPECT_FALSE(perm_folder.GetIsDel());
4039 EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate());
4040 EXPECT_FALSE(perm_folder.GetIsUnsynced());
4041 EXPECT_EQ(perm_folder.GetUniqueClientTag(), "permfolder");
4042 EXPECT_EQ(perm_folder.GetNonUniqueName(), "permitem_renamed");
4046 TEST_F(SyncerTest, ClientTagIllegalUpdateIgnored) {
4047 mock_server_->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
4048 foreign_cache_guid(), "-1");
4049 mock_server_->SetLastUpdateClientTag("permfolder");
4051 EXPECT_TRUE(SyncShareNudge());
4054 syncable::ReadTransaction trans(FROM_HERE, directory());
4055 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
4056 ASSERT_TRUE(perm_folder.good());
4057 EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate());
4058 EXPECT_FALSE(perm_folder.GetIsUnsynced());
4059 EXPECT_EQ(perm_folder.GetUniqueClientTag(), "permfolder");
4060 EXPECT_TRUE(perm_folder.GetNonUniqueName()== "permitem1");
4061 EXPECT_TRUE(perm_folder.GetId().ServerKnows());
4064 mock_server_->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
4065 foreign_cache_guid(), "-1");
4066 mock_server_->SetLastUpdateClientTag("wrongtag");
4067 EXPECT_TRUE(SyncShareNudge());
4070 syncable::ReadTransaction trans(FROM_HERE, directory());
4072 // This update is rejected because it has the same ID, but a
4073 // different tag than one that is already on the client.
4074 // The client has a ServerKnows ID, which cannot be overwritten.
4075 Entry rejected_update(&trans, GET_BY_CLIENT_TAG, "wrongtag");
4076 EXPECT_FALSE(rejected_update.good());
4078 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
4079 ASSERT_TRUE(perm_folder.good());
4080 EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate());
4081 EXPECT_FALSE(perm_folder.GetIsUnsynced());
4082 EXPECT_EQ(perm_folder.GetNonUniqueName(), "permitem1");
4086 TEST_F(SyncerTest, ClientTagUncommittedTagMatchesUpdate) {
4087 int64 original_metahandle = 0;
4090 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
4091 MutableEntry pref(
4092 &trans, CREATE, PREFERENCES, ids_.root(), "name");
4093 ASSERT_TRUE(pref.good());
4094 pref.PutUniqueClientTag("tag");
4095 pref.PutIsUnsynced(true);
4096 EXPECT_FALSE(pref.GetIsUnappliedUpdate());
4097 EXPECT_FALSE(pref.GetId().ServerKnows());
4098 original_metahandle = pref.GetMetahandle();
4101 syncable::Id server_id = TestIdFactory::MakeServer("id");
4102 mock_server_->AddUpdatePref(server_id.GetServerId(),
4103 ids_.root().GetServerId(),
4104 "tag", 10, 100);
4105 mock_server_->set_conflict_all_commits(true);
4107 EXPECT_FALSE(SyncShareNudge());
4108 // This should cause client tag reunion, preserving the metahandle.
4110 syncable::ReadTransaction trans(FROM_HERE, directory());
4112 Entry pref(&trans, GET_BY_CLIENT_TAG, "tag");
4113 ASSERT_TRUE(pref.good());
4114 EXPECT_FALSE(pref.GetIsDel());
4115 EXPECT_FALSE(pref.GetIsUnappliedUpdate());
4116 EXPECT_TRUE(pref.GetIsUnsynced());
4117 EXPECT_EQ(10, pref.GetBaseVersion());
4118 // Entry should have been given the new ID while preserving the
4119 // metahandle; client should have won the conflict resolution.
4120 EXPECT_EQ(original_metahandle, pref.GetMetahandle());
4121 EXPECT_EQ("tag", pref.GetUniqueClientTag());
4122 EXPECT_TRUE(pref.GetId().ServerKnows());
4125 mock_server_->set_conflict_all_commits(false);
4126 EXPECT_TRUE(SyncShareNudge());
4128 // The resolved entry ought to commit cleanly.
4130 syncable::ReadTransaction trans(FROM_HERE, directory());
4132 Entry pref(&trans, GET_BY_CLIENT_TAG, "tag");
4133 ASSERT_TRUE(pref.good());
4134 EXPECT_FALSE(pref.GetIsDel());
4135 EXPECT_FALSE(pref.GetIsUnappliedUpdate());
4136 EXPECT_FALSE(pref.GetIsUnsynced());
4137 EXPECT_TRUE(10 < pref.GetBaseVersion());
4138 // Entry should have been given the new ID while preserving the
4139 // metahandle; client should have won the conflict resolution.
4140 EXPECT_EQ(original_metahandle, pref.GetMetahandle());
4141 EXPECT_EQ("tag", pref.GetUniqueClientTag());
4142 EXPECT_TRUE(pref.GetId().ServerKnows());
4146 TEST_F(SyncerTest, ClientTagConflictWithDeletedLocalEntry) {
4148 // Create a deleted local entry with a unique client tag.
4149 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
4150 MutableEntry pref(
4151 &trans, CREATE, PREFERENCES, ids_.root(), "name");
4152 ASSERT_TRUE(pref.good());
4153 ASSERT_FALSE(pref.GetId().ServerKnows());
4154 pref.PutUniqueClientTag("tag");
4155 pref.PutIsUnsynced(true);
4157 // Note: IS_DEL && !ServerKnows() will clear the UNSYNCED bit.
4158 // (We never attempt to commit server-unknown deleted items, so this
4159 // helps us clean up those entries).
4160 pref.PutIsDel(true);
4163 // Prepare an update with the same unique client tag.
4164 syncable::Id server_id = TestIdFactory::MakeServer("id");
4165 mock_server_->AddUpdatePref(server_id.GetServerId(),
4166 ids_.root().GetServerId(),
4167 "tag", 10, 100);
4169 EXPECT_TRUE(SyncShareNudge());
4170 // The local entry will be overwritten.
4172 syncable::ReadTransaction trans(FROM_HERE, directory());
4174 Entry pref(&trans, GET_BY_CLIENT_TAG, "tag");
4175 ASSERT_TRUE(pref.good());
4176 ASSERT_TRUE(pref.GetId().ServerKnows());
4177 EXPECT_FALSE(pref.GetIsDel());
4178 EXPECT_FALSE(pref.GetIsUnappliedUpdate());
4179 EXPECT_FALSE(pref.GetIsUnsynced());
4180 EXPECT_EQ(pref.GetBaseVersion(), 10);
4181 EXPECT_EQ(pref.GetUniqueClientTag(), "tag");
4185 TEST_F(SyncerTest, ClientTagUpdateClashesWithLocalEntry) {
4186 // This test is written assuming that ID comparison
4187 // will work out in a particular way.
4188 EXPECT_TRUE(ids_.FromNumber(1) < ids_.FromNumber(2));
4189 EXPECT_TRUE(ids_.FromNumber(3) < ids_.FromNumber(4));
4191 syncable::Id id1 = TestIdFactory::MakeServer("1");
4192 mock_server_->AddUpdatePref(id1.GetServerId(), "", "tag1", 10, 100);
4194 syncable::Id id4 = TestIdFactory::MakeServer("4");
4195 mock_server_->AddUpdatePref(id4.GetServerId(), "", "tag2", 11, 110);
4197 mock_server_->set_conflict_all_commits(true);
4199 EXPECT_TRUE(SyncShareNudge());
4200 int64 tag1_metahandle = syncable::kInvalidMetaHandle;
4201 int64 tag2_metahandle = syncable::kInvalidMetaHandle;
4202 // This should cause client tag overwrite.
4204 syncable::ReadTransaction trans(FROM_HERE, directory());
4206 Entry tag1(&trans, GET_BY_CLIENT_TAG, "tag1");
4207 ASSERT_TRUE(tag1.good());
4208 ASSERT_TRUE(tag1.GetId().ServerKnows());
4209 ASSERT_TRUE(id1 == tag1.GetId());
4210 EXPECT_FALSE(tag1.GetIsDel());
4211 EXPECT_FALSE(tag1.GetIsUnappliedUpdate());
4212 EXPECT_FALSE(tag1.GetIsUnsynced());
4213 EXPECT_EQ(10, tag1.GetBaseVersion());
4214 EXPECT_EQ("tag1", tag1.GetUniqueClientTag());
4215 tag1_metahandle = tag1.GetMetahandle();
4217 Entry tag2(&trans, GET_BY_CLIENT_TAG, "tag2");
4218 ASSERT_TRUE(tag2.good());
4219 ASSERT_TRUE(tag2.GetId().ServerKnows());
4220 ASSERT_TRUE(id4 == tag2.GetId());
4221 EXPECT_FALSE(tag2.GetIsDel());
4222 EXPECT_FALSE(tag2.GetIsUnappliedUpdate());
4223 EXPECT_FALSE(tag2.GetIsUnsynced());
4224 EXPECT_EQ(11, tag2.GetBaseVersion());
4225 EXPECT_EQ("tag2", tag2.GetUniqueClientTag());
4226 tag2_metahandle = tag2.GetMetahandle();
4228 // Preferences type root should have been created by the updates above.
4229 Entry pref_root(&trans, GET_TYPE_ROOT, PREFERENCES);
4230 ASSERT_TRUE(pref_root.good());
4232 syncable::Directory::Metahandles children;
4233 directory()->GetChildHandlesById(&trans, pref_root.GetId(), &children);
4234 ASSERT_EQ(2U, children.size());
4237 syncable::Id id2 = TestIdFactory::MakeServer("2");
4238 mock_server_->AddUpdatePref(id2.GetServerId(), "", "tag1", 12, 120);
4239 syncable::Id id3 = TestIdFactory::MakeServer("3");
4240 mock_server_->AddUpdatePref(id3.GetServerId(), "", "tag2", 13, 130);
4241 EXPECT_TRUE(SyncShareNudge());
4244 syncable::ReadTransaction trans(FROM_HERE, directory());
4246 Entry tag1(&trans, GET_BY_CLIENT_TAG, "tag1");
4247 ASSERT_TRUE(tag1.good());
4248 ASSERT_TRUE(tag1.GetId().ServerKnows());
4249 ASSERT_EQ(id1, tag1.GetId())
4250 << "ID 1 should be kept, since it was less than ID 2.";
4251 EXPECT_FALSE(tag1.GetIsDel());
4252 EXPECT_FALSE(tag1.GetIsUnappliedUpdate());
4253 EXPECT_FALSE(tag1.GetIsUnsynced());
4254 EXPECT_EQ(10, tag1.GetBaseVersion());
4255 EXPECT_EQ("tag1", tag1.GetUniqueClientTag());
4256 EXPECT_EQ(tag1_metahandle, tag1.GetMetahandle());
4258 Entry tag2(&trans, GET_BY_CLIENT_TAG, "tag2");
4259 ASSERT_TRUE(tag2.good());
4260 ASSERT_TRUE(tag2.GetId().ServerKnows());
4261 ASSERT_EQ(id3, tag2.GetId())
4262 << "ID 3 should be kept, since it was less than ID 4.";
4263 EXPECT_FALSE(tag2.GetIsDel());
4264 EXPECT_FALSE(tag2.GetIsUnappliedUpdate());
4265 EXPECT_FALSE(tag2.GetIsUnsynced());
4266 EXPECT_EQ(13, tag2.GetBaseVersion());
4267 EXPECT_EQ("tag2", tag2.GetUniqueClientTag());
4268 EXPECT_EQ(tag2_metahandle, tag2.GetMetahandle());
4270 // Preferences type root should have been created by the updates above.
4271 Entry pref_root(&trans, GET_TYPE_ROOT, PREFERENCES);
4272 ASSERT_TRUE(pref_root.good());
4274 syncable::Directory::Metahandles children;
4275 directory()->GetChildHandlesById(&trans, pref_root.GetId(), &children);
4276 ASSERT_EQ(2U, children.size());
4280 TEST_F(SyncerTest, ClientTagClashWithinBatchOfUpdates) {
4281 // This test is written assuming that ID comparison
4282 // will work out in a particular way.
4283 EXPECT_TRUE(ids_.FromNumber(1) < ids_.FromNumber(4));
4284 EXPECT_TRUE(ids_.FromNumber(201) < ids_.FromNumber(205));
4286 // Least ID: winner.
4287 mock_server_->AddUpdatePref(ids_.FromNumber(1).GetServerId(), "", "tag a", 1,
4288 10);
4289 mock_server_->AddUpdatePref(ids_.FromNumber(2).GetServerId(), "", "tag a", 11,
4290 110);
4291 mock_server_->AddUpdatePref(ids_.FromNumber(3).GetServerId(), "", "tag a", 12,
4292 120);
4293 mock_server_->AddUpdatePref(ids_.FromNumber(4).GetServerId(), "", "tag a", 13,
4294 130);
4295 mock_server_->AddUpdatePref(ids_.FromNumber(105).GetServerId(), "", "tag b",
4296 14, 140);
4297 mock_server_->AddUpdatePref(ids_.FromNumber(102).GetServerId(), "", "tag b",
4298 15, 150);
4299 // Least ID: winner.
4300 mock_server_->AddUpdatePref(ids_.FromNumber(101).GetServerId(), "", "tag b",
4301 16, 160);
4302 mock_server_->AddUpdatePref(ids_.FromNumber(104).GetServerId(), "", "tag b",
4303 17, 170);
4305 mock_server_->AddUpdatePref(ids_.FromNumber(205).GetServerId(), "", "tag c",
4306 18, 180);
4307 mock_server_->AddUpdatePref(ids_.FromNumber(202).GetServerId(), "", "tag c",
4308 19, 190);
4309 mock_server_->AddUpdatePref(ids_.FromNumber(204).GetServerId(), "", "tag c",
4310 20, 200);
4311 // Least ID: winner.
4312 mock_server_->AddUpdatePref(ids_.FromNumber(201).GetServerId(), "", "tag c",
4313 21, 210);
4315 mock_server_->set_conflict_all_commits(true);
4317 EXPECT_TRUE(SyncShareNudge());
4318 // This should cause client tag overwrite.
4320 syncable::ReadTransaction trans(FROM_HERE, directory());
4322 Entry tag_a(&trans, GET_BY_CLIENT_TAG, "tag a");
4323 ASSERT_TRUE(tag_a.good());
4324 EXPECT_TRUE(tag_a.GetId().ServerKnows());
4325 EXPECT_EQ(ids_.FromNumber(1), tag_a.GetId());
4326 EXPECT_FALSE(tag_a.GetIsDel());
4327 EXPECT_FALSE(tag_a.GetIsUnappliedUpdate());
4328 EXPECT_FALSE(tag_a.GetIsUnsynced());
4329 EXPECT_EQ(1, tag_a.GetBaseVersion());
4330 EXPECT_EQ("tag a", tag_a.GetUniqueClientTag());
4332 Entry tag_b(&trans, GET_BY_CLIENT_TAG, "tag b");
4333 ASSERT_TRUE(tag_b.good());
4334 EXPECT_TRUE(tag_b.GetId().ServerKnows());
4335 EXPECT_EQ(ids_.FromNumber(101), tag_b.GetId());
4336 EXPECT_FALSE(tag_b.GetIsDel());
4337 EXPECT_FALSE(tag_b.GetIsUnappliedUpdate());
4338 EXPECT_FALSE(tag_b.GetIsUnsynced());
4339 EXPECT_EQ(16, tag_b.GetBaseVersion());
4340 EXPECT_EQ("tag b", tag_b.GetUniqueClientTag());
4342 Entry tag_c(&trans, GET_BY_CLIENT_TAG, "tag c");
4343 ASSERT_TRUE(tag_c.good());
4344 EXPECT_TRUE(tag_c.GetId().ServerKnows());
4345 EXPECT_EQ(ids_.FromNumber(201), tag_c.GetId());
4346 EXPECT_FALSE(tag_c.GetIsDel());
4347 EXPECT_FALSE(tag_c.GetIsUnappliedUpdate());
4348 EXPECT_FALSE(tag_c.GetIsUnsynced());
4349 EXPECT_EQ(21, tag_c.GetBaseVersion());
4350 EXPECT_EQ("tag c", tag_c.GetUniqueClientTag());
4352 // Preferences type root should have been created by the updates above.
4353 Entry pref_root(&trans, GET_TYPE_ROOT, PREFERENCES);
4354 ASSERT_TRUE(pref_root.good());
4356 // Verify that we have exactly 3 tagged nodes under the type root.
4357 syncable::Directory::Metahandles children;
4358 directory()->GetChildHandlesById(&trans, pref_root.GetId(), &children);
4359 ASSERT_EQ(3U, children.size());
4363 // This verifies transition to implicit permanent folders.
4364 TEST_F(SyncerTest, EntryWithParentIdUpdatedWithEntryWithoutParentId) {
4365 // Make sure SPECIFICS root exists so that we can get its parent ID.
4366 mock_server_->AddUpdateSpecifics(1, 0, "Folder", 10, 10, true, 1,
4367 DefaultPreferencesSpecifics());
4368 mock_server_->SetLastUpdateServerTag(ModelTypeToRootTag(PREFERENCES));
4369 EXPECT_TRUE(SyncShareNudge());
4371 Id pref_root_id;
4373 // Preferences type root should have been created by the update above.
4374 // We need it in order to get its ID.
4375 syncable::ReadTransaction trans(FROM_HERE, directory());
4376 Entry pref_root(&trans, GET_TYPE_ROOT, PREFERENCES);
4377 ASSERT_TRUE(pref_root.good());
4378 pref_root_id = pref_root.GetId();
4381 // Add a preference item with explicit parent ID.
4383 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
4384 MutableEntry entry(&trans, CREATE, PREFERENCES, pref_root_id, "tag");
4385 ASSERT_TRUE(entry.good());
4386 entry.PutIsDir(false);
4387 entry.PutBaseVersion(1);
4388 entry.PutUniqueClientTag("tag");
4389 entry.PutId(ids_.FromNumber(2));
4392 // Verify the entry above.
4394 syncable::ReadTransaction trans(FROM_HERE, directory());
4395 Entry pref_entry(&trans, GET_BY_CLIENT_TAG, "tag");
4396 ASSERT_TRUE(pref_entry.good());
4397 ASSERT_EQ(pref_root_id, pref_entry.GetParentId());
4400 // Make another update where the same item get updated, this time
4401 // with implicit parent ID.
4402 mock_server_->AddUpdatePref(ids_.FromNumber(2).GetServerId(), "", "tag", 2,
4403 20);
4405 EXPECT_TRUE(SyncShareNudge());
4408 syncable::ReadTransaction trans(FROM_HERE, directory());
4409 Entry pref_entry(&trans, GET_BY_CLIENT_TAG, "tag");
4410 ASSERT_TRUE(pref_entry.good());
4411 ASSERT_TRUE(pref_entry.GetParentId().IsNull());
4413 // Verify that there is still one node under the type root.
4414 syncable::Directory::Metahandles children;
4415 directory()->GetChildHandlesById(&trans, pref_root_id, &children);
4416 ASSERT_EQ(1U, children.size());
4420 TEST_F(SyncerTest, UniqueServerTagUpdates) {
4421 // As a hurdle, introduce an item whose name is the same as the tag value
4422 // we'll use later.
4423 int64 hurdle_handle = CreateUnsyncedDirectory("bob", "id_bob");
4425 syncable::ReadTransaction trans(FROM_HERE, directory());
4426 Entry hurdle(&trans, GET_BY_HANDLE, hurdle_handle);
4427 ASSERT_TRUE(hurdle.good());
4428 ASSERT_TRUE(!hurdle.GetIsDel());
4429 ASSERT_TRUE(hurdle.GetUniqueServerTag().empty());
4430 ASSERT_TRUE(hurdle.GetNonUniqueName()== "bob");
4432 // Try to lookup by the tagname. These should fail.
4433 Entry tag_alpha(&trans, GET_BY_SERVER_TAG, "alpha");
4434 EXPECT_FALSE(tag_alpha.good());
4435 Entry tag_bob(&trans, GET_BY_SERVER_TAG, "bob");
4436 EXPECT_FALSE(tag_bob.good());
4439 // Now download some tagged items as updates.
4440 mock_server_->AddUpdateDirectory(
4441 1, 0, "update1", 1, 10, std::string(), std::string());
4442 mock_server_->SetLastUpdateServerTag("alpha");
4443 mock_server_->AddUpdateDirectory(
4444 2, 0, "update2", 2, 20, std::string(), std::string());
4445 mock_server_->SetLastUpdateServerTag("bob");
4446 EXPECT_TRUE(SyncShareNudge());
4449 syncable::ReadTransaction trans(FROM_HERE, directory());
4451 // The new items should be applied as new entries, and we should be able
4452 // to look them up by their tag values.
4453 Entry tag_alpha(&trans, GET_BY_SERVER_TAG, "alpha");
4454 ASSERT_TRUE(tag_alpha.good());
4455 ASSERT_TRUE(!tag_alpha.GetIsDel());
4456 ASSERT_TRUE(tag_alpha.GetUniqueServerTag()== "alpha");
4457 ASSERT_TRUE(tag_alpha.GetNonUniqueName()== "update1");
4458 Entry tag_bob(&trans, GET_BY_SERVER_TAG, "bob");
4459 ASSERT_TRUE(tag_bob.good());
4460 ASSERT_TRUE(!tag_bob.GetIsDel());
4461 ASSERT_TRUE(tag_bob.GetUniqueServerTag()== "bob");
4462 ASSERT_TRUE(tag_bob.GetNonUniqueName()== "update2");
4463 // The old item should be unchanged.
4464 Entry hurdle(&trans, GET_BY_HANDLE, hurdle_handle);
4465 ASSERT_TRUE(hurdle.good());
4466 ASSERT_TRUE(!hurdle.GetIsDel());
4467 ASSERT_TRUE(hurdle.GetUniqueServerTag().empty());
4468 ASSERT_TRUE(hurdle.GetNonUniqueName()== "bob");
4472 TEST_F(SyncerTest, GetUpdatesSetsRequestedTypes) {
4473 // The expectations of this test happen in the MockConnectionManager's
4474 // GetUpdates handler. EnableDatatype sets the expectation value from our
4475 // set of enabled/disabled datatypes.
4476 EnableDatatype(BOOKMARKS);
4477 EXPECT_TRUE(SyncShareNudge());
4478 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4480 EnableDatatype(AUTOFILL);
4481 EXPECT_TRUE(SyncShareNudge());
4482 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4484 EnableDatatype(PREFERENCES);
4485 EXPECT_TRUE(SyncShareNudge());
4486 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4488 DisableDatatype(BOOKMARKS);
4489 EXPECT_TRUE(SyncShareNudge());
4490 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4492 DisableDatatype(AUTOFILL);
4493 EXPECT_TRUE(SyncShareNudge());
4494 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4496 DisableDatatype(PREFERENCES);
4497 EnableDatatype(AUTOFILL);
4498 EXPECT_TRUE(SyncShareNudge());
4499 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4502 // A typical scenario: server and client each have one update for the other.
4503 // This is the "happy path" alternative to UpdateFailsThenDontCommit.
4504 TEST_F(SyncerTest, UpdateThenCommit) {
4505 syncable::Id to_receive = ids_.NewServerId();
4506 syncable::Id to_commit = ids_.NewLocalId();
4508 mock_server_->AddUpdateDirectory(to_receive, ids_.root(), "x", 1, 10,
4509 foreign_cache_guid(), "-1");
4510 int64 commit_handle = CreateUnsyncedDirectory("y", to_commit);
4511 EXPECT_TRUE(SyncShareNudge());
4513 // The sync cycle should have included a GetUpdate, then a commit. By the
4514 // time the commit happened, we should have known for sure that there were no
4515 // hierarchy conflicts, and reported this fact to the server.
4516 ASSERT_TRUE(mock_server_->last_request().has_commit());
4517 VerifyNoHierarchyConflictsReported(mock_server_->last_request());
4519 syncable::ReadTransaction trans(FROM_HERE, directory());
4521 Entry received(&trans, GET_BY_ID, to_receive);
4522 ASSERT_TRUE(received.good());
4523 EXPECT_FALSE(received.GetIsUnsynced());
4524 EXPECT_FALSE(received.GetIsUnappliedUpdate());
4526 Entry committed(&trans, GET_BY_HANDLE, commit_handle);
4527 ASSERT_TRUE(committed.good());
4528 EXPECT_FALSE(committed.GetIsUnsynced());
4529 EXPECT_FALSE(committed.GetIsUnappliedUpdate());
4532 // Same as above, but this time we fail to download updates.
4533 // We should not attempt to commit anything unless we successfully downloaded
4534 // updates, otherwise we risk causing a server-side conflict.
4535 TEST_F(SyncerTest, UpdateFailsThenDontCommit) {
4536 syncable::Id to_receive = ids_.NewServerId();
4537 syncable::Id to_commit = ids_.NewLocalId();
4539 mock_server_->AddUpdateDirectory(to_receive, ids_.root(), "x", 1, 10,
4540 foreign_cache_guid(), "-1");
4541 int64 commit_handle = CreateUnsyncedDirectory("y", to_commit);
4542 mock_server_->FailNextPostBufferToPathCall();
4543 EXPECT_FALSE(SyncShareNudge());
4545 syncable::ReadTransaction trans(FROM_HERE, directory());
4547 // We did not receive this update.
4548 Entry received(&trans, GET_BY_ID, to_receive);
4549 ASSERT_FALSE(received.good());
4551 // And our local update remains unapplied.
4552 Entry committed(&trans, GET_BY_HANDLE, commit_handle);
4553 ASSERT_TRUE(committed.good());
4554 EXPECT_TRUE(committed.GetIsUnsynced());
4555 EXPECT_FALSE(committed.GetIsUnappliedUpdate());
4557 // Inform the Mock we won't be fetching all updates.
4558 mock_server_->ClearUpdatesQueue();
4561 // Downloads two updates and applies them successfully.
4562 // This is the "happy path" alternative to ConfigureFailsDontApplyUpdates.
4563 TEST_F(SyncerTest, ConfigureDownloadsTwoBatchesSuccess) {
4564 syncable::Id node1 = ids_.NewServerId();
4565 syncable::Id node2 = ids_.NewServerId();
4567 // Construct the first GetUpdates response.
4568 mock_server_->AddUpdateDirectory(node1, ids_.root(), "one", 1, 10,
4569 foreign_cache_guid(), "-2");
4570 mock_server_->SetChangesRemaining(1);
4571 mock_server_->NextUpdateBatch();
4573 // Construct the second GetUpdates response.
4574 mock_server_->AddUpdateDirectory(node2, ids_.root(), "two", 1, 20,
4575 foreign_cache_guid(), "-2");
4577 SyncShareConfigure();
4579 syncable::ReadTransaction trans(FROM_HERE, directory());
4580 // Both nodes should be downloaded and applied.
4582 Entry n1(&trans, GET_BY_ID, node1);
4583 ASSERT_TRUE(n1.good());
4584 EXPECT_FALSE(n1.GetIsUnappliedUpdate());
4586 Entry n2(&trans, GET_BY_ID, node2);
4587 ASSERT_TRUE(n2.good());
4588 EXPECT_FALSE(n2.GetIsUnappliedUpdate());
4591 // Same as the above case, but this time the second batch fails to download.
4592 TEST_F(SyncerTest, ConfigureFailsDontApplyUpdates) {
4593 syncable::Id node1 = ids_.NewServerId();
4594 syncable::Id node2 = ids_.NewServerId();
4596 // The scenario: we have two batches of updates with one update each. A
4597 // normal confgure step would download all the updates one batch at a time and
4598 // apply them. This configure will succeed in downloading the first batch
4599 // then fail when downloading the second.
4600 mock_server_->FailNthPostBufferToPathCall(2);
4602 // Construct the first GetUpdates response.
4603 mock_server_->AddUpdateDirectory(node1, ids_.root(), "one", 1, 10,
4604 foreign_cache_guid(), "-1");
4605 mock_server_->SetChangesRemaining(1);
4606 mock_server_->NextUpdateBatch();
4608 // Consutrct the second GetUpdates response.
4609 mock_server_->AddUpdateDirectory(node2, ids_.root(), "two", 1, 20,
4610 foreign_cache_guid(), "-2");
4612 SyncShareConfigure();
4614 syncable::ReadTransaction trans(FROM_HERE, directory());
4616 // The first node was downloaded, but not applied.
4617 Entry n1(&trans, GET_BY_ID, node1);
4618 ASSERT_TRUE(n1.good());
4619 EXPECT_TRUE(n1.GetIsUnappliedUpdate());
4621 // The second node was not downloaded.
4622 Entry n2(&trans, GET_BY_ID, node2);
4623 EXPECT_FALSE(n2.good());
4625 // One update remains undownloaded.
4626 mock_server_->ClearUpdatesQueue();
4629 TEST_F(SyncerTest, GetKeySuccess) {
4631 syncable::ReadTransaction rtrans(FROM_HERE, directory());
4632 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans));
4635 SyncShareConfigure();
4637 EXPECT_EQ(session_->status_controller().last_get_key_result(), SYNCER_OK);
4639 syncable::ReadTransaction rtrans(FROM_HERE, directory());
4640 EXPECT_FALSE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans));
4644 TEST_F(SyncerTest, GetKeyEmpty) {
4646 syncable::ReadTransaction rtrans(FROM_HERE, directory());
4647 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans));
4650 mock_server_->SetKeystoreKey(std::string());
4651 SyncShareConfigure();
4653 EXPECT_NE(session_->status_controller().last_get_key_result(), SYNCER_OK);
4655 syncable::ReadTransaction rtrans(FROM_HERE, directory());
4656 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans));
4660 // Tests specifically related to bookmark (and therefore no client tags) sync
4661 // logic. Entities without client tags have custom logic in parts of the code,
4662 // and hence are not covered by e.g. the Undeletion tests below.
4663 class SyncerBookmarksTest : public SyncerTest {
4664 public:
4665 SyncerBookmarksTest() : metahandle_(syncable::kInvalidMetaHandle) {
4668 void Create() {
4669 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
4670 MutableEntry bookmark(
4671 &trans, CREATE, BOOKMARKS, ids_.root(), "clientname");
4672 ASSERT_TRUE(bookmark.good());
4673 bookmark.PutSpecifics(DefaultBookmarkSpecifics());
4674 EXPECT_FALSE(bookmark.GetIsUnappliedUpdate());
4675 EXPECT_FALSE(bookmark.GetId().ServerKnows());
4676 metahandle_ = bookmark.GetMetahandle();
4677 local_id_ = bookmark.GetId();
4678 bookmark.PutIsUnsynced(true);
4681 void Update() {
4682 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
4683 MutableEntry bookmark(&trans, GET_BY_ID, local_id_);
4684 ASSERT_TRUE(bookmark.good());
4685 bookmark.PutSpecifics(DefaultBookmarkSpecifics());
4686 EXPECT_FALSE(bookmark.GetIsUnappliedUpdate());
4687 bookmark.PutIsUnsynced(true);
4688 if (bookmark.GetSyncing())
4689 bookmark.PutDirtySync(true);
4692 void Delete() {
4693 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
4694 MutableEntry entry(&trans, GET_BY_HANDLE, metahandle_);
4695 ASSERT_TRUE(entry.good());
4696 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4697 // The order of setting IS_UNSYNCED vs IS_DEL matters. See
4698 // WriteNode::Tombstone().
4699 entry.PutIsUnsynced(true);
4700 if (entry.GetSyncing())
4701 entry.PutDirtySync(true);
4702 entry.PutIsDel(true);
4705 void UpdateAndDelete() {
4706 Update();
4707 Delete();
4710 void Undelete() {
4711 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
4712 MutableEntry entry(&trans, GET_BY_HANDLE, metahandle_);
4713 ASSERT_TRUE(entry.good());
4714 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4715 EXPECT_TRUE(entry.GetIsDel());
4716 entry.PutIsDel(false);
4717 entry.PutIsUnsynced(true);
4718 if (entry.GetSyncing())
4719 entry.PutDirtySync(true);
4722 int64 GetMetahandleOfTag() {
4723 syncable::ReadTransaction trans(FROM_HERE, directory());
4724 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4725 EXPECT_TRUE(entry.good());
4726 if (!entry.good()) {
4727 return syncable::kInvalidMetaHandle;
4729 return entry.GetMetahandle();
4732 Id GetServerId() {
4733 syncable::ReadTransaction trans(FROM_HERE, directory());
4734 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4735 EXPECT_TRUE(entry.good());
4736 if (!entry.good()) {
4737 return Id();
4739 return entry.GetId();
4742 void ExpectUnsyncedCreation() {
4743 syncable::ReadTransaction trans(FROM_HERE, directory());
4744 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4746 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4747 EXPECT_FALSE(entry.GetIsDel());
4748 EXPECT_FALSE(entry.GetServerIsDel()); // Never been committed.
4749 EXPECT_LT(entry.GetBaseVersion(), 0);
4750 EXPECT_TRUE(entry.GetIsUnsynced());
4751 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4754 void ExpectUnsyncedUndeletion() {
4755 syncable::ReadTransaction trans(FROM_HERE, directory());
4756 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4758 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4759 EXPECT_FALSE(entry.GetIsDel());
4760 EXPECT_TRUE(entry.GetServerIsDel());
4761 EXPECT_GE(entry.GetBaseVersion(), 0);
4762 EXPECT_TRUE(entry.GetIsUnsynced());
4763 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4764 EXPECT_TRUE(entry.GetId().ServerKnows());
4767 void ExpectUnsyncedEdit() {
4768 syncable::ReadTransaction trans(FROM_HERE, directory());
4769 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4771 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4772 EXPECT_FALSE(entry.GetIsDel());
4773 EXPECT_FALSE(entry.GetServerIsDel());
4774 EXPECT_GE(entry.GetBaseVersion(), 0);
4775 EXPECT_TRUE(entry.GetIsUnsynced());
4776 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4777 EXPECT_TRUE(entry.GetId().ServerKnows());
4780 void ExpectUnsyncedDeletion() {
4781 syncable::ReadTransaction trans(FROM_HERE, directory());
4782 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4784 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4785 EXPECT_TRUE(entry.GetIsDel());
4786 EXPECT_FALSE(entry.GetServerIsDel());
4787 EXPECT_TRUE(entry.GetIsUnsynced());
4788 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4789 EXPECT_GE(entry.GetBaseVersion(), 0);
4790 EXPECT_GE(entry.GetServerVersion(), 0);
4793 void ExpectSyncedAndCreated() {
4794 syncable::ReadTransaction trans(FROM_HERE, directory());
4795 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4797 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4798 EXPECT_FALSE(entry.GetIsDel());
4799 EXPECT_FALSE(entry.GetServerIsDel());
4800 EXPECT_GE(entry.GetBaseVersion(), 0);
4801 EXPECT_EQ(entry.GetBaseVersion(), entry.GetServerVersion());
4802 EXPECT_FALSE(entry.GetIsUnsynced());
4803 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4806 void ExpectSyncedAndDeleted() {
4807 syncable::ReadTransaction trans(FROM_HERE, directory());
4808 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4810 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4811 EXPECT_TRUE(entry.GetIsDel());
4812 EXPECT_TRUE(entry.GetServerIsDel());
4813 EXPECT_FALSE(entry.GetIsUnsynced());
4814 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4815 EXPECT_GE(entry.GetBaseVersion(), 0);
4816 EXPECT_GE(entry.GetServerVersion(), 0);
4819 protected:
4820 syncable::Id local_id_;
4821 int64 metahandle_;
4824 TEST_F(SyncerBookmarksTest, CreateSyncThenDeleteSync) {
4825 Create();
4826 ExpectUnsyncedCreation();
4827 EXPECT_TRUE(SyncShareNudge());
4828 ExpectSyncedAndCreated();
4829 Delete();
4830 ExpectUnsyncedDeletion();
4831 EXPECT_TRUE(SyncShareNudge());
4832 ExpectSyncedAndDeleted();
4835 TEST_F(SyncerBookmarksTest, CreateThenDeleteBeforeSync) {
4836 Create();
4837 ExpectUnsyncedCreation();
4838 Delete();
4840 // Deleting before the initial commit should result in not needing to send
4841 // the delete to the server. It will still be in an unsynced state, but with
4842 // IS_UNSYNCED set to false.
4844 syncable::ReadTransaction trans(FROM_HERE, directory());
4845 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4847 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4848 EXPECT_TRUE(entry.GetIsDel());
4849 EXPECT_FALSE(entry.GetServerIsDel());
4850 EXPECT_FALSE(entry.GetIsUnsynced());
4851 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4852 EXPECT_EQ(entry.GetBaseVersion(), -1);
4853 EXPECT_EQ(entry.GetServerVersion(), 0);
4857 TEST_F(SyncerBookmarksTest, LocalDeleteRemoteChangeConflict) {
4858 Create();
4859 ExpectUnsyncedCreation();
4860 EXPECT_TRUE(SyncShareNudge());
4861 ExpectSyncedAndCreated();
4862 Delete();
4863 ExpectUnsyncedDeletion();
4865 // Trigger a getupdates that modifies the bookmark. The update should be
4866 // clobbered by the local delete.
4867 mock_server_->AddUpdateBookmark(GetServerId(), Id::GetRoot(), "dummy", 10, 10,
4868 local_cache_guid(), local_id_.GetServerId());
4870 EXPECT_TRUE(SyncShareNudge());
4871 ExpectSyncedAndDeleted();
4874 TEST_F(SyncerBookmarksTest, CreateThenDeleteDuringCommit) {
4875 Create();
4876 ExpectUnsyncedCreation();
4878 // In the middle of the initial creation commit, perform a deletion.
4879 // This should trigger performing two consecutive commit cycles, resulting
4880 // in the bookmark being both deleted and synced.
4881 mock_server_->SetMidCommitCallback(
4882 base::Bind(&SyncerBookmarksTest::Delete, base::Unretained(this)));
4884 EXPECT_TRUE(SyncShareNudge());
4885 ExpectSyncedAndDeleted();
4888 TEST_F(SyncerBookmarksTest, CreateThenUpdateAndDeleteDuringCommit) {
4889 Create();
4890 ExpectUnsyncedCreation();
4892 // In the middle of the initial creation commit, perform an updated followed
4893 // by a deletion. This should trigger performing two consecutive commit
4894 // cycles, resulting in the bookmark being both deleted and synced.
4895 mock_server_->SetMidCommitCallback(base::Bind(
4896 &SyncerBookmarksTest::UpdateAndDelete, base::Unretained(this)));
4898 EXPECT_TRUE(SyncShareNudge());
4899 ExpectSyncedAndDeleted();
4902 // Test what happens if a client deletes, then recreates, an object very
4903 // quickly. It is possible that the deletion gets sent as a commit, and
4904 // the undelete happens during the commit request. The principle here
4905 // is that with a single committing client, conflicts should never
4906 // be encountered, and a client encountering its past actions during
4907 // getupdates should never feed back to override later actions.
4909 // In cases of ordering A-F below, the outcome should be the same.
4910 // Exercised by UndeleteDuringCommit:
4911 // A. Delete - commit - undelete - commitresponse.
4912 // B. Delete - commit - undelete - commitresponse - getupdates.
4913 // Exercised by UndeleteBeforeCommit:
4914 // C. Delete - undelete - commit - commitresponse.
4915 // D. Delete - undelete - commit - commitresponse - getupdates.
4916 // Exercised by UndeleteAfterCommit:
4917 // E. Delete - commit - commitresponse - undelete - commit
4918 // - commitresponse.
4919 // F. Delete - commit - commitresponse - undelete - commit -
4920 // - commitresponse - getupdates.
4921 class SyncerUndeletionTest : public SyncerTest {
4922 public:
4923 SyncerUndeletionTest()
4924 : client_tag_("foobar"),
4925 metahandle_(syncable::kInvalidMetaHandle) {
4928 void Create() {
4929 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
4930 MutableEntry perm_folder(
4931 &trans, CREATE, PREFERENCES, ids_.root(), "clientname");
4932 ASSERT_TRUE(perm_folder.good());
4933 perm_folder.PutUniqueClientTag(client_tag_);
4934 perm_folder.PutIsUnsynced(true);
4935 if (perm_folder.GetSyncing())
4936 perm_folder.PutDirtySync(true);
4937 perm_folder.PutSpecifics(DefaultPreferencesSpecifics());
4938 EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate());
4939 EXPECT_FALSE(perm_folder.GetId().ServerKnows());
4940 metahandle_ = perm_folder.GetMetahandle();
4941 local_id_ = perm_folder.GetId();
4944 void Delete() {
4945 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
4946 MutableEntry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4947 ASSERT_TRUE(entry.good());
4948 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4949 // The order of setting IS_UNSYNCED vs IS_DEL matters. See
4950 // WriteNode::Tombstone().
4951 entry.PutIsUnsynced(true);
4952 if (entry.GetSyncing())
4953 entry.PutDirtySync(true);
4954 entry.PutIsDel(true);
4957 void Undelete() {
4958 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
4959 MutableEntry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4960 ASSERT_TRUE(entry.good());
4961 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4962 EXPECT_TRUE(entry.GetIsDel());
4963 entry.PutIsDel(false);
4964 entry.PutIsUnsynced(true);
4965 if (entry.GetSyncing())
4966 entry.PutDirtySync(true);
4969 int64 GetMetahandleOfTag() {
4970 syncable::ReadTransaction trans(FROM_HERE, directory());
4971 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4972 EXPECT_TRUE(entry.good());
4973 if (!entry.good()) {
4974 return syncable::kInvalidMetaHandle;
4976 return entry.GetMetahandle();
4979 void ExpectUnsyncedCreation() {
4980 syncable::ReadTransaction trans(FROM_HERE, directory());
4981 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4983 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4984 EXPECT_FALSE(entry.GetIsDel());
4985 EXPECT_FALSE(entry.GetServerIsDel()); // Never been committed.
4986 EXPECT_LT(entry.GetBaseVersion(), 0);
4987 EXPECT_TRUE(entry.GetIsUnsynced());
4988 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4991 void ExpectUnsyncedUndeletion() {
4992 syncable::ReadTransaction trans(FROM_HERE, directory());
4993 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4995 EXPECT_EQ(metahandle_, entry.GetMetahandle());
4996 EXPECT_FALSE(entry.GetIsDel());
4997 EXPECT_TRUE(entry.GetServerIsDel());
4998 EXPECT_GE(entry.GetBaseVersion(), 0);
4999 EXPECT_TRUE(entry.GetIsUnsynced());
5000 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
5001 EXPECT_TRUE(entry.GetId().ServerKnows());
5004 void ExpectUnsyncedEdit() {
5005 syncable::ReadTransaction trans(FROM_HERE, directory());
5006 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
5008 EXPECT_EQ(metahandle_, entry.GetMetahandle());
5009 EXPECT_FALSE(entry.GetIsDel());
5010 EXPECT_FALSE(entry.GetServerIsDel());
5011 EXPECT_GE(entry.GetBaseVersion(), 0);
5012 EXPECT_TRUE(entry.GetIsUnsynced());
5013 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
5014 EXPECT_TRUE(entry.GetId().ServerKnows());
5017 void ExpectUnsyncedDeletion() {
5018 syncable::ReadTransaction trans(FROM_HERE, directory());
5019 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
5021 EXPECT_EQ(metahandle_, entry.GetMetahandle());
5022 EXPECT_TRUE(entry.GetIsDel());
5023 EXPECT_FALSE(entry.GetServerIsDel());
5024 EXPECT_TRUE(entry.GetIsUnsynced());
5025 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
5026 EXPECT_GE(entry.GetBaseVersion(), 0);
5027 EXPECT_GE(entry.GetServerVersion(), 0);
5030 void ExpectSyncedAndCreated() {
5031 syncable::ReadTransaction trans(FROM_HERE, directory());
5032 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
5034 EXPECT_EQ(metahandle_, entry.GetMetahandle());
5035 EXPECT_FALSE(entry.GetIsDel());
5036 EXPECT_FALSE(entry.GetServerIsDel());
5037 EXPECT_GE(entry.GetBaseVersion(), 0);
5038 EXPECT_EQ(entry.GetBaseVersion(), entry.GetServerVersion());
5039 EXPECT_FALSE(entry.GetIsUnsynced());
5040 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
5043 void ExpectSyncedAndDeleted() {
5044 syncable::ReadTransaction trans(FROM_HERE, directory());
5045 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
5047 EXPECT_EQ(metahandle_, entry.GetMetahandle());
5048 EXPECT_TRUE(entry.GetIsDel());
5049 EXPECT_TRUE(entry.GetServerIsDel());
5050 EXPECT_FALSE(entry.GetIsUnsynced());
5051 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
5052 EXPECT_GE(entry.GetBaseVersion(), 0);
5053 EXPECT_GE(entry.GetServerVersion(), 0);
5056 protected:
5057 const std::string client_tag_;
5058 syncable::Id local_id_;
5059 int64 metahandle_;
5062 TEST_F(SyncerUndeletionTest, UndeleteDuringCommit) {
5063 Create();
5064 ExpectUnsyncedCreation();
5065 EXPECT_TRUE(SyncShareNudge());
5067 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5068 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5069 ExpectSyncedAndCreated();
5071 // Delete, begin committing the delete, then undelete while committing.
5072 Delete();
5073 ExpectUnsyncedDeletion();
5074 mock_server_->SetMidCommitCallback(
5075 base::Bind(&SyncerUndeletionTest::Undelete, base::Unretained(this)));
5076 EXPECT_TRUE(SyncShareNudge());
5078 // We will continue to commit until all nodes are synced, so we expect
5079 // that both the delete and following undelete were committed. We haven't
5080 // downloaded any updates, though, so the SERVER fields will be the same
5081 // as they were at the start of the cycle.
5082 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5083 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5086 syncable::ReadTransaction trans(FROM_HERE, directory());
5087 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
5089 // Server fields lag behind.
5090 EXPECT_FALSE(entry.GetServerIsDel());
5092 // We have committed the second (undelete) update.
5093 EXPECT_FALSE(entry.GetIsDel());
5094 EXPECT_FALSE(entry.GetIsUnsynced());
5095 EXPECT_FALSE(entry.GetIsUnappliedUpdate());
5098 // Now, encounter a GetUpdates corresponding to the deletion from
5099 // the server. The undeletion should prevail again and be committed.
5100 // None of this should trigger any conflict detection -- it is perfectly
5101 // normal to recieve updates from our own commits.
5102 mock_server_->SetMidCommitCallback(base::Closure());
5103 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
5104 update->set_originator_cache_guid(local_cache_guid());
5105 update->set_originator_client_item_id(local_id_.GetServerId());
5107 EXPECT_TRUE(SyncShareNudge());
5108 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5109 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5110 ExpectSyncedAndCreated();
5113 TEST_F(SyncerUndeletionTest, UndeleteBeforeCommit) {
5114 Create();
5115 ExpectUnsyncedCreation();
5116 EXPECT_TRUE(SyncShareNudge());
5118 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5119 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5120 ExpectSyncedAndCreated();
5122 // Delete and undelete, then sync to pick up the result.
5123 Delete();
5124 ExpectUnsyncedDeletion();
5125 Undelete();
5126 ExpectUnsyncedEdit(); // Edit, not undelete: server thinks it exists.
5127 EXPECT_TRUE(SyncShareNudge());
5129 // The item ought to have committed successfully.
5130 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5131 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5132 ExpectSyncedAndCreated();
5134 syncable::ReadTransaction trans(FROM_HERE, directory());
5135 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
5136 EXPECT_EQ(2, entry.GetBaseVersion());
5139 // Now, encounter a GetUpdates corresponding to the just-committed
5140 // update.
5141 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
5142 update->set_originator_cache_guid(local_cache_guid());
5143 update->set_originator_client_item_id(local_id_.GetServerId());
5144 EXPECT_TRUE(SyncShareNudge());
5145 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5146 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5147 ExpectSyncedAndCreated();
5150 TEST_F(SyncerUndeletionTest, UndeleteAfterCommitButBeforeGetUpdates) {
5151 Create();
5152 ExpectUnsyncedCreation();
5153 EXPECT_TRUE(SyncShareNudge());
5155 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5156 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5157 ExpectSyncedAndCreated();
5159 // Delete and commit.
5160 Delete();
5161 ExpectUnsyncedDeletion();
5162 EXPECT_TRUE(SyncShareNudge());
5164 // The item ought to have committed successfully.
5165 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5166 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5167 ExpectSyncedAndDeleted();
5169 // Before the GetUpdates, the item is locally undeleted.
5170 Undelete();
5171 ExpectUnsyncedUndeletion();
5173 // Now, encounter a GetUpdates corresponding to the just-committed
5174 // deletion update. The undeletion should prevail.
5175 mock_server_->AddUpdateFromLastCommit();
5176 EXPECT_TRUE(SyncShareNudge());
5177 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5178 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5179 ExpectSyncedAndCreated();
5182 TEST_F(SyncerUndeletionTest, UndeleteAfterDeleteAndGetUpdates) {
5183 Create();
5184 ExpectUnsyncedCreation();
5185 EXPECT_TRUE(SyncShareNudge());
5187 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5188 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5189 ExpectSyncedAndCreated();
5191 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
5192 update->set_originator_cache_guid(local_cache_guid());
5193 update->set_originator_client_item_id(local_id_.GetServerId());
5194 EXPECT_TRUE(SyncShareNudge());
5195 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5196 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5197 ExpectSyncedAndCreated();
5199 // Delete and commit.
5200 Delete();
5201 ExpectUnsyncedDeletion();
5202 EXPECT_TRUE(SyncShareNudge());
5204 // The item ought to have committed successfully.
5205 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5206 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5207 ExpectSyncedAndDeleted();
5209 // Now, encounter a GetUpdates corresponding to the just-committed
5210 // deletion update. Should be consistent.
5211 mock_server_->AddUpdateFromLastCommit();
5212 EXPECT_TRUE(SyncShareNudge());
5213 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5214 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5215 ExpectSyncedAndDeleted();
5217 // After the GetUpdates, the item is locally undeleted.
5218 Undelete();
5219 ExpectUnsyncedUndeletion();
5221 // Now, encounter a GetUpdates corresponding to the just-committed
5222 // deletion update. The undeletion should prevail.
5223 EXPECT_TRUE(SyncShareNudge());
5224 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5225 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5226 ExpectSyncedAndCreated();
5229 // Test processing of undeletion GetUpdateses.
5230 TEST_F(SyncerUndeletionTest, UndeleteAfterOtherClientDeletes) {
5231 Create();
5232 ExpectUnsyncedCreation();
5233 EXPECT_TRUE(SyncShareNudge());
5235 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5236 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5237 ExpectSyncedAndCreated();
5239 // Add a delete from the server.
5240 sync_pb::SyncEntity* update1 = mock_server_->AddUpdateFromLastCommit();
5241 update1->set_originator_cache_guid(local_cache_guid());
5242 update1->set_originator_client_item_id(local_id_.GetServerId());
5243 EXPECT_TRUE(SyncShareNudge());
5244 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5245 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5246 ExpectSyncedAndCreated();
5248 // Some other client deletes the item.
5250 syncable::ReadTransaction trans(FROM_HERE, directory());
5251 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
5252 mock_server_->AddUpdateTombstone(entry.GetId(), PREFERENCES);
5254 EXPECT_TRUE(SyncShareNudge());
5256 // The update ought to have applied successfully.
5257 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5258 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5259 ExpectSyncedAndDeleted();
5261 // Undelete it locally.
5262 Undelete();
5263 ExpectUnsyncedUndeletion();
5264 EXPECT_TRUE(SyncShareNudge());
5265 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5266 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5267 ExpectSyncedAndCreated();
5269 // Now, encounter a GetUpdates corresponding to the just-committed
5270 // deletion update. The undeletion should prevail.
5271 sync_pb::SyncEntity* update2 = mock_server_->AddUpdateFromLastCommit();
5272 update2->set_originator_cache_guid(local_cache_guid());
5273 update2->set_originator_client_item_id(local_id_.GetServerId());
5274 EXPECT_TRUE(SyncShareNudge());
5275 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5276 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5277 ExpectSyncedAndCreated();
5280 TEST_F(SyncerUndeletionTest, UndeleteAfterOtherClientDeletesImmediately) {
5281 Create();
5282 ExpectUnsyncedCreation();
5283 EXPECT_TRUE(SyncShareNudge());
5285 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5286 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5287 ExpectSyncedAndCreated();
5289 // Some other client deletes the item before we get a chance
5290 // to GetUpdates our original request.
5292 syncable::ReadTransaction trans(FROM_HERE, directory());
5293 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
5294 mock_server_->AddUpdateTombstone(entry.GetId(), PREFERENCES);
5296 EXPECT_TRUE(SyncShareNudge());
5298 // The update ought to have applied successfully.
5299 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5300 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5301 ExpectSyncedAndDeleted();
5303 // Undelete it locally.
5304 Undelete();
5305 ExpectUnsyncedUndeletion();
5306 EXPECT_TRUE(SyncShareNudge());
5307 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5308 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5309 ExpectSyncedAndCreated();
5311 // Now, encounter a GetUpdates corresponding to the just-committed
5312 // deletion update. The undeletion should prevail.
5313 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
5314 update->set_originator_cache_guid(local_cache_guid());
5315 update->set_originator_client_item_id(local_id_.GetServerId());
5316 EXPECT_TRUE(SyncShareNudge());
5317 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5318 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5319 ExpectSyncedAndCreated();
5322 TEST_F(SyncerUndeletionTest, OtherClientUndeletes) {
5323 Create();
5324 ExpectUnsyncedCreation();
5325 EXPECT_TRUE(SyncShareNudge());
5327 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5328 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5329 ExpectSyncedAndCreated();
5331 // Get the updates of our just-committed entry.
5332 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
5333 update->set_originator_cache_guid(local_cache_guid());
5334 update->set_originator_client_item_id(local_id_.GetServerId());
5335 EXPECT_TRUE(SyncShareNudge());
5336 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5337 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5338 ExpectSyncedAndCreated();
5340 // We delete the item.
5341 Delete();
5342 ExpectUnsyncedDeletion();
5343 EXPECT_TRUE(SyncShareNudge());
5345 // The update ought to have applied successfully.
5346 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5347 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5348 ExpectSyncedAndDeleted();
5350 // Now, encounter a GetUpdates corresponding to the just-committed
5351 // deletion update.
5352 mock_server_->AddUpdateFromLastCommit();
5353 EXPECT_TRUE(SyncShareNudge());
5354 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5355 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5356 ExpectSyncedAndDeleted();
5358 // Some other client undeletes the item.
5360 syncable::ReadTransaction trans(FROM_HERE, directory());
5361 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
5362 mock_server_->AddUpdatePref(
5363 entry.GetId().GetServerId(),
5364 entry.GetParentId().GetServerId(),
5365 client_tag_, 100, 1000);
5367 mock_server_->SetLastUpdateClientTag(client_tag_);
5368 EXPECT_TRUE(SyncShareNudge());
5369 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5370 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5371 ExpectSyncedAndCreated();
5374 TEST_F(SyncerUndeletionTest, OtherClientUndeletesImmediately) {
5375 Create();
5376 ExpectUnsyncedCreation();
5377 EXPECT_TRUE(SyncShareNudge());
5379 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5380 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5381 ExpectSyncedAndCreated();
5383 // Get the updates of our just-committed entry.
5384 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
5385 update->set_originator_cache_guid(local_cache_guid());
5387 syncable::ReadTransaction trans(FROM_HERE, directory());
5388 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
5389 update->set_originator_client_item_id(local_id_.GetServerId());
5391 EXPECT_TRUE(SyncShareNudge());
5392 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5393 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5394 ExpectSyncedAndCreated();
5396 // We delete the item.
5397 Delete();
5398 ExpectUnsyncedDeletion();
5399 EXPECT_TRUE(SyncShareNudge());
5401 // The update ought to have applied successfully.
5402 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5403 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5404 ExpectSyncedAndDeleted();
5406 // Some other client undeletes before we see the update from our
5407 // commit.
5409 syncable::ReadTransaction trans(FROM_HERE, directory());
5410 Entry entry(&trans, GET_BY_HANDLE, metahandle_);
5411 mock_server_->AddUpdatePref(
5412 entry.GetId().GetServerId(),
5413 entry.GetParentId().GetServerId(),
5414 client_tag_, 100, 1000);
5416 mock_server_->SetLastUpdateClientTag(client_tag_);
5417 EXPECT_TRUE(SyncShareNudge());
5418 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
5419 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
5420 ExpectSyncedAndCreated();
5423 enum {
5424 TEST_PARAM_BOOKMARK_ENABLE_BIT,
5425 TEST_PARAM_AUTOFILL_ENABLE_BIT,
5426 TEST_PARAM_BIT_COUNT
5429 class MixedResult :
5430 public SyncerTest,
5431 public ::testing::WithParamInterface<int> {
5432 protected:
5433 bool ShouldFailBookmarkCommit() {
5434 return (GetParam() & (1 << TEST_PARAM_BOOKMARK_ENABLE_BIT)) == 0;
5436 bool ShouldFailAutofillCommit() {
5437 return (GetParam() & (1 << TEST_PARAM_AUTOFILL_ENABLE_BIT)) == 0;
5441 INSTANTIATE_TEST_CASE_P(ExtensionsActivity,
5442 MixedResult,
5443 testing::Range(0, 1 << TEST_PARAM_BIT_COUNT));
5445 TEST_P(MixedResult, ExtensionsActivity) {
5447 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
5449 MutableEntry pref(&wtrans, CREATE, PREFERENCES, wtrans.root_id(), "pref");
5450 ASSERT_TRUE(pref.good());
5451 pref.PutIsUnsynced(true);
5453 MutableEntry bookmark(
5454 &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "bookmark");
5455 ASSERT_TRUE(bookmark.good());
5456 bookmark.PutIsUnsynced(true);
5458 if (ShouldFailBookmarkCommit()) {
5459 mock_server_->SetTransientErrorId(bookmark.GetId());
5462 if (ShouldFailAutofillCommit()) {
5463 mock_server_->SetTransientErrorId(pref.GetId());
5468 // Put some extenions activity records into the monitor.
5470 ExtensionsActivity::Records records;
5471 records["ABC"].extension_id = "ABC";
5472 records["ABC"].bookmark_write_count = 2049U;
5473 records["xyz"].extension_id = "xyz";
5474 records["xyz"].bookmark_write_count = 4U;
5475 context_->extensions_activity()->PutRecords(records);
5478 EXPECT_EQ(!ShouldFailBookmarkCommit() && !ShouldFailAutofillCommit(),
5479 SyncShareNudge());
5481 ExtensionsActivity::Records final_monitor_records;
5482 context_->extensions_activity()->GetAndClearRecords(&final_monitor_records);
5483 if (ShouldFailBookmarkCommit()) {
5484 ASSERT_EQ(2U, final_monitor_records.size())
5485 << "Should restore records after unsuccessful bookmark commit.";
5486 EXPECT_EQ("ABC", final_monitor_records["ABC"].extension_id);
5487 EXPECT_EQ("xyz", final_monitor_records["xyz"].extension_id);
5488 EXPECT_EQ(2049U, final_monitor_records["ABC"].bookmark_write_count);
5489 EXPECT_EQ(4U, final_monitor_records["xyz"].bookmark_write_count);
5490 } else {
5491 EXPECT_TRUE(final_monitor_records.empty())
5492 << "Should not restore records after successful bookmark commit.";
5496 } // namespace syncer