Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(199)

Side by Side Diff: sync/engine/syncer_unittest.cc

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

Powered by Google App Engine
This is Rietveld 408576698