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