Make sure webview uses embedder display DPI.
[chromium-blink-merge.git] / sync / engine / syncer_unittest.cc
blob196f77ac195d10691ea22c9552686ac9acf7c3ef
1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 //
5 // Syncer unit tests. Unfortunately a lot of these tests
6 // are outdated and need to be reworked and updated.
8 #include <algorithm>
9 #include <limits>
10 #include <list>
11 #include <map>
12 #include <set>
13 #include <string>
15 #include "base/bind.h"
16 #include "base/bind_helpers.h"
17 #include "base/callback.h"
18 #include "base/compiler_specific.h"
19 #include "base/location.h"
20 #include "base/memory/scoped_ptr.h"
21 #include "base/message_loop.h"
22 #include "base/string_number_conversions.h"
23 #include "base/stringprintf.h"
24 #include "base/time.h"
25 #include "build/build_config.h"
26 #include "sync/engine/get_commit_ids_command.h"
27 #include "sync/engine/net/server_connection_manager.h"
28 #include "sync/engine/process_updates_command.h"
29 #include "sync/engine/sync_scheduler_impl.h"
30 #include "sync/engine/syncer.h"
31 #include "sync/engine/syncer_proto_util.h"
32 #include "sync/engine/throttled_data_type_tracker.h"
33 #include "sync/engine/traffic_recorder.h"
34 #include "sync/internal_api/public/base/model_type.h"
35 #include "sync/internal_api/public/engine/model_safe_worker.h"
36 #include "sync/protocol/bookmark_specifics.pb.h"
37 #include "sync/protocol/nigori_specifics.pb.h"
38 #include "sync/protocol/preference_specifics.pb.h"
39 #include "sync/protocol/sync.pb.h"
40 #include "sync/sessions/sync_session_context.h"
41 #include "sync/syncable/mutable_entry.h"
42 #include "sync/syncable/nigori_util.h"
43 #include "sync/syncable/syncable_delete_journal.h"
44 #include "sync/syncable/syncable_read_transaction.h"
45 #include "sync/syncable/syncable_util.h"
46 #include "sync/syncable/syncable_write_transaction.h"
47 #include "sync/test/engine/fake_model_worker.h"
48 #include "sync/test/engine/mock_connection_manager.h"
49 #include "sync/test/engine/test_directory_setter_upper.h"
50 #include "sync/test/engine/test_id_factory.h"
51 #include "sync/test/engine/test_syncable_utils.h"
52 #include "sync/test/fake_encryptor.h"
53 #include "sync/test/fake_extensions_activity_monitor.h"
54 #include "sync/test/fake_sync_encryption_handler.h"
55 #include "sync/util/cryptographer.h"
56 #include "sync/util/time.h"
57 #include "testing/gtest/include/gtest/gtest.h"
59 using base::TimeDelta;
61 using std::count;
62 using std::map;
63 using std::multimap;
64 using std::set;
65 using std::string;
66 using std::vector;
68 namespace syncer {
70 using syncable::BaseTransaction;
71 using syncable::Blob;
72 using syncable::CountEntriesWithName;
73 using syncable::Directory;
74 using syncable::Entry;
75 using syncable::GetFirstEntryWithName;
76 using syncable::GetOnlyEntryWithName;
77 using syncable::Id;
78 using syncable::kEncryptedString;
79 using syncable::MutableEntry;
80 using syncable::WriteTransaction;
82 using syncable::BASE_VERSION;
83 using syncable::CREATE;
84 using syncable::GET_BY_HANDLE;
85 using syncable::GET_BY_ID;
86 using syncable::GET_BY_CLIENT_TAG;
87 using syncable::GET_BY_SERVER_TAG;
88 using syncable::ID;
89 using syncable::IS_DEL;
90 using syncable::IS_DIR;
91 using syncable::IS_UNAPPLIED_UPDATE;
92 using syncable::IS_UNSYNCED;
93 using syncable::META_HANDLE;
94 using syncable::MTIME;
95 using syncable::NON_UNIQUE_NAME;
96 using syncable::PARENT_ID;
97 using syncable::BASE_SERVER_SPECIFICS;
98 using syncable::SERVER_IS_DEL;
99 using syncable::SERVER_PARENT_ID;
100 using syncable::SERVER_SPECIFICS;
101 using syncable::SERVER_VERSION;
102 using syncable::UNIQUE_CLIENT_TAG;
103 using syncable::UNIQUE_SERVER_TAG;
104 using syncable::SPECIFICS;
105 using syncable::SYNCING;
106 using syncable::UNITTEST;
108 using sessions::StatusController;
109 using sessions::SyncSessionContext;
110 using sessions::SyncSession;
112 class SyncerTest : public testing::Test,
113 public SyncSession::Delegate,
114 public SyncEngineEventListener {
115 protected:
116 SyncerTest()
117 : syncer_(NULL),
118 saw_syncer_event_(false),
119 traffic_recorder_(0, 0) {
122 // SyncSession::Delegate implementation.
123 virtual void OnSilencedUntil(const base::TimeTicks& silenced_until) OVERRIDE {
124 FAIL() << "Should not get silenced.";
126 virtual bool IsSyncingCurrentlySilenced() OVERRIDE {
127 return false;
129 virtual void OnReceivedLongPollIntervalUpdate(
130 const base::TimeDelta& new_interval) OVERRIDE {
131 last_long_poll_interval_received_ = new_interval;
133 virtual void OnReceivedShortPollIntervalUpdate(
134 const base::TimeDelta& new_interval) OVERRIDE {
135 last_short_poll_interval_received_ = new_interval;
137 virtual void OnReceivedSessionsCommitDelay(
138 const base::TimeDelta& new_delay) OVERRIDE {
139 last_sessions_commit_delay_seconds_ = new_delay;
141 virtual void OnShouldStopSyncingPermanently() OVERRIDE {
143 virtual void OnSyncProtocolError(
144 const sessions::SyncSessionSnapshot& snapshot) OVERRIDE {
147 void GetWorkers(std::vector<ModelSafeWorker*>* out) {
148 out->push_back(worker_.get());
151 void GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) {
152 // We're just testing the sync engine here, so we shunt everything to
153 // the SyncerThread. Datatypes which aren't enabled aren't in the map.
154 for (ModelTypeSet::Iterator it = enabled_datatypes_.First();
155 it.Good(); it.Inc()) {
156 (*out)[it.Get()] = GROUP_PASSIVE;
160 virtual void OnSyncEngineEvent(const SyncEngineEvent& event) OVERRIDE {
161 DVLOG(1) << "HandleSyncEngineEvent in unittest " << event.what_happened;
162 // we only test for entry-specific events, not status changed ones.
163 switch (event.what_happened) {
164 case SyncEngineEvent::SYNC_CYCLE_BEGIN: // Fall through.
165 case SyncEngineEvent::STATUS_CHANGED:
166 case SyncEngineEvent::SYNC_CYCLE_ENDED:
167 return;
168 default:
169 CHECK(false) << "Handling unknown error type in unit tests!!";
171 saw_syncer_event_ = true;
174 SyncSession* MakeSession() {
175 ModelSafeRoutingInfo info;
176 GetModelSafeRoutingInfo(&info);
177 ModelTypeInvalidationMap invalidation_map =
178 ModelSafeRoutingInfoToInvalidationMap(info, std::string());
179 sessions::SyncSourceInfo source_info(sync_pb::GetUpdatesCallerInfo::UNKNOWN,
180 invalidation_map);
181 return new SyncSession(context_.get(), this, source_info);
184 void SyncShareNudge() {
185 session_.reset(MakeSession());
186 EXPECT_TRUE(syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END));
189 void SyncShareConfigure() {
190 session_.reset(MakeSession());
191 EXPECT_TRUE(
192 syncer_->SyncShare(session_.get(), DOWNLOAD_UPDATES, APPLY_UPDATES));
195 virtual void SetUp() {
196 dir_maker_.SetUp();
197 mock_server_.reset(new MockConnectionManager(directory()));
198 EnableDatatype(BOOKMARKS);
199 EnableDatatype(NIGORI);
200 EnableDatatype(PREFERENCES);
201 EnableDatatype(NIGORI);
202 worker_ = new FakeModelWorker(GROUP_PASSIVE);
203 std::vector<SyncEngineEventListener*> listeners;
204 listeners.push_back(this);
206 ModelSafeRoutingInfo routing_info;
207 std::vector<ModelSafeWorker*> workers;
209 GetModelSafeRoutingInfo(&routing_info);
210 GetWorkers(&workers);
212 throttled_data_type_tracker_.reset(new ThrottledDataTypeTracker(NULL));
214 context_.reset(
215 new SyncSessionContext(
216 mock_server_.get(), directory(), workers,
217 &extensions_activity_monitor_, throttled_data_type_tracker_.get(),
218 listeners, NULL, &traffic_recorder_,
219 true, // enable keystore encryption
220 "fake_invalidator_client_id"));
221 context_->set_routing_info(routing_info);
222 syncer_ = new Syncer();
223 session_.reset(MakeSession());
225 syncable::ReadTransaction trans(FROM_HERE, directory());
226 syncable::Directory::ChildHandles children;
227 directory()->GetChildHandlesById(&trans, trans.root_id(), &children);
228 ASSERT_EQ(0u, children.size());
229 saw_syncer_event_ = false;
230 root_id_ = TestIdFactory::root();
231 parent_id_ = ids_.MakeServer("parent id");
232 child_id_ = ids_.MakeServer("child id");
233 directory()->set_store_birthday(mock_server_->store_birthday());
234 mock_server_->SetKeystoreKey("encryption_key");
237 virtual void TearDown() {
238 mock_server_.reset();
239 delete syncer_;
240 syncer_ = NULL;
241 dir_maker_.TearDown();
243 void WriteTestDataToEntry(WriteTransaction* trans, MutableEntry* entry) {
244 EXPECT_FALSE(entry->Get(IS_DIR));
245 EXPECT_FALSE(entry->Get(IS_DEL));
246 sync_pb::EntitySpecifics specifics;
247 specifics.mutable_bookmark()->set_url("http://demo/");
248 specifics.mutable_bookmark()->set_favicon("PNG");
249 entry->Put(syncable::SPECIFICS, specifics);
250 entry->Put(syncable::IS_UNSYNCED, true);
252 void VerifyTestDataInEntry(BaseTransaction* trans, Entry* entry) {
253 EXPECT_FALSE(entry->Get(IS_DIR));
254 EXPECT_FALSE(entry->Get(IS_DEL));
255 VerifyTestBookmarkDataInEntry(entry);
257 void VerifyTestBookmarkDataInEntry(Entry* entry) {
258 const sync_pb::EntitySpecifics& specifics = entry->Get(syncable::SPECIFICS);
259 EXPECT_TRUE(specifics.has_bookmark());
260 EXPECT_EQ("PNG", specifics.bookmark().favicon());
261 EXPECT_EQ("http://demo/", specifics.bookmark().url());
264 void VerifyHierarchyConflictsReported(
265 const sync_pb::ClientToServerMessage& message) {
266 // Our request should have included a warning about hierarchy conflicts.
267 const sync_pb::ClientStatus& client_status = message.client_status();
268 EXPECT_TRUE(client_status.has_hierarchy_conflict_detected());
269 EXPECT_TRUE(client_status.hierarchy_conflict_detected());
272 void VerifyNoHierarchyConflictsReported(
273 const sync_pb::ClientToServerMessage& message) {
274 // Our request should have reported no hierarchy conflicts detected.
275 const sync_pb::ClientStatus& client_status = message.client_status();
276 EXPECT_TRUE(client_status.has_hierarchy_conflict_detected());
277 EXPECT_FALSE(client_status.hierarchy_conflict_detected());
280 void VerifyHierarchyConflictsUnspecified(
281 const sync_pb::ClientToServerMessage& message) {
282 // Our request should have neither confirmed nor denied hierarchy conflicts.
283 const sync_pb::ClientStatus& client_status = message.client_status();
284 EXPECT_FALSE(client_status.has_hierarchy_conflict_detected());
287 void SyncRepeatedlyToTriggerConflictResolution(SyncSession* session) {
288 // We should trigger after less than 6 syncs, but extra does no harm.
289 for (int i = 0 ; i < 6 ; ++i)
290 syncer_->SyncShare(session, SYNCER_BEGIN, SYNCER_END);
292 void SyncRepeatedlyToTriggerStuckSignal(SyncSession* session) {
293 // We should trigger after less than 10 syncs, but we want to avoid brittle
294 // tests.
295 for (int i = 0 ; i < 12 ; ++i)
296 syncer_->SyncShare(session, SYNCER_BEGIN, SYNCER_END);
298 sync_pb::EntitySpecifics DefaultBookmarkSpecifics() {
299 sync_pb::EntitySpecifics result;
300 AddDefaultFieldValue(BOOKMARKS, &result);
301 return result;
304 sync_pb::EntitySpecifics DefaultPreferencesSpecifics() {
305 sync_pb::EntitySpecifics result;
306 AddDefaultFieldValue(PREFERENCES, &result);
307 return result;
309 // Enumeration of alterations to entries for commit ordering tests.
310 enum EntryFeature {
311 LIST_END = 0, // Denotes the end of the list of features from below.
312 SYNCED, // Items are unsynced by default
313 DELETED,
314 OLD_MTIME,
315 MOVED_FROM_ROOT,
318 struct CommitOrderingTest {
319 // expected commit index.
320 int commit_index;
321 // Details about the item
322 syncable::Id id;
323 syncable::Id parent_id;
324 EntryFeature features[10];
326 static CommitOrderingTest MakeLastCommitItem() {
327 CommitOrderingTest last_commit_item;
328 last_commit_item.commit_index = -1;
329 last_commit_item.id = TestIdFactory::root();
330 return last_commit_item;
334 void RunCommitOrderingTest(CommitOrderingTest* test) {
335 map<int, syncable::Id> expected_positions;
336 { // Transaction scope.
337 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
338 while (!test->id.IsRoot()) {
339 if (test->commit_index >= 0) {
340 map<int, syncable::Id>::value_type entry(test->commit_index,
341 test->id);
342 bool double_position = !expected_positions.insert(entry).second;
343 ASSERT_FALSE(double_position) << "Two id's expected at one position";
345 string utf8_name = test->id.GetServerId();
346 string name(utf8_name.begin(), utf8_name.end());
347 MutableEntry entry(&trans, CREATE, BOOKMARKS, test->parent_id, name);
349 entry.Put(syncable::ID, test->id);
350 if (test->id.ServerKnows()) {
351 entry.Put(BASE_VERSION, 5);
352 entry.Put(SERVER_VERSION, 5);
353 entry.Put(SERVER_PARENT_ID, test->parent_id);
355 entry.Put(syncable::IS_DIR, true);
356 entry.Put(syncable::IS_UNSYNCED, true);
357 entry.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
358 // Set the time to 30 seconds in the future to reduce the chance of
359 // flaky tests.
360 const base::Time& now_plus_30s =
361 base::Time::Now() + base::TimeDelta::FromSeconds(30);
362 const base::Time& now_minus_2h =
363 base::Time::Now() - base::TimeDelta::FromHours(2);
364 entry.Put(syncable::MTIME, now_plus_30s);
365 for (size_t i = 0 ; i < arraysize(test->features) ; ++i) {
366 switch (test->features[i]) {
367 case LIST_END:
368 break;
369 case SYNCED:
370 entry.Put(syncable::IS_UNSYNCED, false);
371 break;
372 case DELETED:
373 entry.Put(syncable::IS_DEL, true);
374 break;
375 case OLD_MTIME:
376 entry.Put(MTIME, now_minus_2h);
377 break;
378 case MOVED_FROM_ROOT:
379 entry.Put(SERVER_PARENT_ID, trans.root_id());
380 break;
381 default:
382 FAIL() << "Bad value in CommitOrderingTest list";
385 test++;
388 SyncShareNudge();
389 ASSERT_TRUE(expected_positions.size() ==
390 mock_server_->committed_ids().size());
391 // If this test starts failing, be aware other sort orders could be valid.
392 for (size_t i = 0; i < expected_positions.size(); ++i) {
393 EXPECT_EQ(1u, expected_positions.count(i));
394 EXPECT_TRUE(expected_positions[i] == mock_server_->committed_ids()[i]);
398 void DoTruncationTest(const vector<int64>& unsynced_handle_view,
399 const vector<syncable::Id>& expected_id_order) {
400 for (size_t limit = expected_id_order.size() + 2; limit > 0; --limit) {
401 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
403 ModelSafeRoutingInfo routes;
404 GetModelSafeRoutingInfo(&routes);
405 sessions::OrderedCommitSet output_set(routes);
406 GetCommitIdsCommand command(&wtrans, limit, &output_set);
407 std::set<int64> ready_unsynced_set;
408 command.FilterUnreadyEntries(&wtrans, ModelTypeSet(),
409 ModelTypeSet(), false,
410 unsynced_handle_view, &ready_unsynced_set);
411 command.BuildCommitIds(&wtrans, routes, ready_unsynced_set);
412 size_t truncated_size = std::min(limit, expected_id_order.size());
413 ASSERT_EQ(truncated_size, output_set.Size());
414 for (size_t i = 0; i < truncated_size; ++i) {
415 ASSERT_EQ(expected_id_order[i], output_set.GetCommitIdAt(i))
416 << "At index " << i << " with batch size limited to " << limit;
418 sessions::OrderedCommitSet::Projection proj;
419 proj = output_set.GetCommitIdProjection(GROUP_PASSIVE);
420 ASSERT_EQ(truncated_size, proj.size());
421 for (size_t i = 0; i < truncated_size; ++i) {
422 SCOPED_TRACE(::testing::Message("Projection mismatch with i = ") << i);
423 syncable::Id projected = output_set.GetCommitIdAt(proj[i]);
424 ASSERT_EQ(expected_id_order[proj[i]], projected);
425 // Since this projection is the identity, the following holds.
426 ASSERT_EQ(expected_id_order[i], projected);
431 const StatusController& status() {
432 return session_->status_controller();
435 Directory* directory() {
436 return dir_maker_.directory();
439 const std::string local_cache_guid() {
440 return directory()->cache_guid();
443 const std::string foreign_cache_guid() {
444 return "kqyg7097kro6GSUod+GSg==";
447 int64 CreateUnsyncedDirectory(const string& entry_name,
448 const string& idstring) {
449 return CreateUnsyncedDirectory(entry_name,
450 syncable::Id::CreateFromServerId(idstring));
453 int64 CreateUnsyncedDirectory(const string& entry_name,
454 const syncable::Id& id) {
455 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
456 MutableEntry entry(
457 &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), entry_name);
458 EXPECT_TRUE(entry.good());
459 entry.Put(syncable::IS_UNSYNCED, true);
460 entry.Put(syncable::IS_DIR, true);
461 entry.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
462 entry.Put(syncable::BASE_VERSION, id.ServerKnows() ? 1 : 0);
463 entry.Put(syncable::ID, id);
464 return entry.Get(META_HANDLE);
467 void EnableDatatype(ModelType model_type) {
468 enabled_datatypes_.Put(model_type);
470 ModelSafeRoutingInfo routing_info;
471 GetModelSafeRoutingInfo(&routing_info);
473 if (context_) {
474 context_->set_routing_info(routing_info);
477 mock_server_->ExpectGetUpdatesRequestTypes(enabled_datatypes_);
480 void DisableDatatype(ModelType model_type) {
481 enabled_datatypes_.Remove(model_type);
483 ModelSafeRoutingInfo routing_info;
484 GetModelSafeRoutingInfo(&routing_info);
486 if (context_) {
487 context_->set_routing_info(routing_info);
490 mock_server_->ExpectGetUpdatesRequestTypes(enabled_datatypes_);
493 template<typename FieldType, typename ValueType>
494 ValueType GetField(int64 metahandle, FieldType field,
495 ValueType default_value) {
496 syncable::ReadTransaction trans(FROM_HERE, directory());
497 Entry entry(&trans, GET_BY_HANDLE, metahandle);
498 EXPECT_TRUE(entry.good());
499 if (!entry.good()) {
500 return default_value;
502 EXPECT_EQ(metahandle, entry.Get(META_HANDLE));
503 return entry.Get(field);
506 // Helper getters that work without a transaction, to reduce boilerplate.
507 Id Get(int64 metahandle, syncable::IdField field) {
508 return GetField(metahandle, field, syncable::GetNullId());
511 string Get(int64 metahandle, syncable::StringField field) {
512 return GetField(metahandle, field, string());
515 int64 Get(int64 metahandle, syncable::Int64Field field) {
516 return GetField(metahandle, field, syncable::kInvalidMetaHandle);
519 int64 Get(int64 metahandle, syncable::BaseVersion field) {
520 const int64 kDefaultValue = -100;
521 return GetField(metahandle, field, kDefaultValue);
524 bool Get(int64 metahandle, syncable::IndexedBitField field) {
525 return GetField(metahandle, field, false);
528 bool Get(int64 metahandle, syncable::IsDelField field) {
529 return GetField(metahandle, field, false);
532 bool Get(int64 metahandle, syncable::BitField field) {
533 return GetField(metahandle, field, false);
536 Cryptographer* GetCryptographer(syncable::BaseTransaction* trans) {
537 return directory()->GetCryptographer(trans);
540 MessageLoop message_loop_;
542 // Some ids to aid tests. Only the root one's value is specific. The rest
543 // are named for test clarity.
544 // TODO(chron): Get rid of these inbuilt IDs. They only make it
545 // more confusing.
546 syncable::Id root_id_;
547 syncable::Id parent_id_;
548 syncable::Id child_id_;
550 TestIdFactory ids_;
552 TestDirectorySetterUpper dir_maker_;
553 FakeEncryptor encryptor_;
554 FakeExtensionsActivityMonitor extensions_activity_monitor_;
555 scoped_ptr<ThrottledDataTypeTracker> throttled_data_type_tracker_;
556 scoped_ptr<MockConnectionManager> mock_server_;
558 Syncer* syncer_;
560 scoped_ptr<SyncSession> session_;
561 scoped_ptr<SyncSessionContext> context_;
562 bool saw_syncer_event_;
563 base::TimeDelta last_short_poll_interval_received_;
564 base::TimeDelta last_long_poll_interval_received_;
565 base::TimeDelta last_sessions_commit_delay_seconds_;
566 scoped_refptr<ModelSafeWorker> worker_;
568 ModelTypeSet enabled_datatypes_;
569 TrafficRecorder traffic_recorder_;
571 DISALLOW_COPY_AND_ASSIGN(SyncerTest);
574 TEST_F(SyncerTest, TestCallGatherUnsyncedEntries) {
576 Syncer::UnsyncedMetaHandles handles;
578 syncable::ReadTransaction trans(FROM_HERE, directory());
579 GetUnsyncedEntries(&trans, &handles);
581 ASSERT_EQ(0u, handles.size());
583 // TODO(sync): When we can dynamically connect and disconnect the mock
584 // ServerConnectionManager test disconnected GetUnsyncedEntries here. It's a
585 // regression for a very old bug.
588 TEST_F(SyncerTest, GetCommitIdsCommandTruncates) {
589 syncable::Id root = ids_.root();
590 // Create two server entries.
591 mock_server_->AddUpdateDirectory(ids_.MakeServer("x"), root, "X", 10, 10,
592 foreign_cache_guid(), "-1");
593 mock_server_->AddUpdateDirectory(ids_.MakeServer("w"), root, "W", 10, 10,
594 foreign_cache_guid(), "-2");
595 SyncShareNudge();
597 // Create some new client entries.
598 CreateUnsyncedDirectory("C", ids_.MakeLocal("c"));
599 CreateUnsyncedDirectory("B", ids_.MakeLocal("b"));
600 CreateUnsyncedDirectory("D", ids_.MakeLocal("d"));
601 CreateUnsyncedDirectory("E", ids_.MakeLocal("e"));
602 CreateUnsyncedDirectory("J", ids_.MakeLocal("j"));
605 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
606 MutableEntry entry_x(&wtrans, GET_BY_ID, ids_.MakeServer("x"));
607 MutableEntry entry_b(&wtrans, GET_BY_ID, ids_.MakeLocal("b"));
608 MutableEntry entry_c(&wtrans, GET_BY_ID, ids_.MakeLocal("c"));
609 MutableEntry entry_d(&wtrans, GET_BY_ID, ids_.MakeLocal("d"));
610 MutableEntry entry_e(&wtrans, GET_BY_ID, ids_.MakeLocal("e"));
611 MutableEntry entry_w(&wtrans, GET_BY_ID, ids_.MakeServer("w"));
612 MutableEntry entry_j(&wtrans, GET_BY_ID, ids_.MakeLocal("j"));
613 entry_x.Put(IS_UNSYNCED, true);
614 entry_b.Put(PARENT_ID, entry_x.Get(ID));
615 entry_d.Put(PARENT_ID, entry_b.Get(ID));
616 entry_c.Put(PARENT_ID, entry_x.Get(ID));
617 entry_c.PutPredecessor(entry_b.Get(ID));
618 entry_e.Put(PARENT_ID, entry_c.Get(ID));
619 entry_w.PutPredecessor(entry_x.Get(ID));
620 entry_w.Put(IS_UNSYNCED, true);
621 entry_w.Put(SERVER_VERSION, 20);
622 entry_w.Put(IS_UNAPPLIED_UPDATE, true); // Fake a conflict.
623 entry_j.PutPredecessor(entry_w.Get(ID));
626 // The arrangement is now: x (b (d) c (e)) w j
627 // Entry "w" is in conflict, making its sucessors unready to commit.
628 vector<int64> unsynced_handle_view;
629 vector<syncable::Id> expected_order;
631 syncable::ReadTransaction rtrans(FROM_HERE, directory());
632 GetUnsyncedEntries(&rtrans, &unsynced_handle_view);
634 // The expected order is "x", "b", "c", "d", "e", truncated appropriately.
635 expected_order.push_back(ids_.MakeServer("x"));
636 expected_order.push_back(ids_.MakeLocal("b"));
637 expected_order.push_back(ids_.MakeLocal("c"));
638 expected_order.push_back(ids_.MakeLocal("d"));
639 expected_order.push_back(ids_.MakeLocal("e"));
640 DoTruncationTest(unsynced_handle_view, expected_order);
643 TEST_F(SyncerTest, GetCommitIdsFiltersThrottledEntries) {
644 const ModelTypeSet throttled_types(BOOKMARKS);
645 sync_pb::EntitySpecifics bookmark_data;
646 AddDefaultFieldValue(BOOKMARKS, &bookmark_data);
648 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
649 foreign_cache_guid(), "-1");
650 SyncShareNudge();
653 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
654 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
655 ASSERT_TRUE(A.good());
656 A.Put(IS_UNSYNCED, true);
657 A.Put(SPECIFICS, bookmark_data);
658 A.Put(NON_UNIQUE_NAME, "bookmark");
661 // Now set the throttled types.
662 context_->throttled_data_type_tracker()->SetUnthrottleTime(
663 throttled_types,
664 base::TimeTicks::Now() + base::TimeDelta::FromSeconds(1200));
665 SyncShareNudge();
668 // Nothing should have been committed as bookmarks is throttled.
669 syncable::ReadTransaction rtrans(FROM_HERE, directory());
670 Entry entryA(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1));
671 ASSERT_TRUE(entryA.good());
672 EXPECT_TRUE(entryA.Get(IS_UNSYNCED));
675 // Now unthrottle.
676 context_->throttled_data_type_tracker()->SetUnthrottleTime(
677 throttled_types,
678 base::TimeTicks::Now() - base::TimeDelta::FromSeconds(1200));
679 SyncShareNudge();
681 // It should have been committed.
682 syncable::ReadTransaction rtrans(FROM_HERE, directory());
683 Entry entryA(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1));
684 ASSERT_TRUE(entryA.good());
685 EXPECT_FALSE(entryA.Get(IS_UNSYNCED));
689 // We use a macro so we can preserve the error location.
690 #define VERIFY_ENTRY(id, is_unapplied, is_unsynced, prev_initialized, \
691 parent_id, version, server_version, id_fac, rtrans) \
692 do { \
693 Entry entryA(rtrans, syncable::GET_BY_ID, id_fac.FromNumber(id)); \
694 ASSERT_TRUE(entryA.good()); \
695 /* We don't use EXPECT_EQ here because when the left side param is false,
696 gcc 4.6 warns about converting 'false' to pointer type for argument 1. */ \
697 EXPECT_TRUE(is_unsynced == entryA.Get(IS_UNSYNCED)); \
698 EXPECT_TRUE(is_unapplied == entryA.Get(IS_UNAPPLIED_UPDATE)); \
699 EXPECT_TRUE(prev_initialized == \
700 IsRealDataType(GetModelTypeFromSpecifics( \
701 entryA.Get(BASE_SERVER_SPECIFICS)))); \
702 EXPECT_TRUE(parent_id == -1 || \
703 entryA.Get(PARENT_ID) == id_fac.FromNumber(parent_id)); \
704 EXPECT_EQ(version, entryA.Get(BASE_VERSION)); \
705 EXPECT_EQ(server_version, entryA.Get(SERVER_VERSION)); \
706 } while (0)
708 TEST_F(SyncerTest, GetCommitIdsFiltersUnreadyEntries) {
709 KeyParams key_params = {"localhost", "dummy", "foobar"};
710 KeyParams other_params = {"localhost", "dummy", "foobar2"};
711 sync_pb::EntitySpecifics bookmark, encrypted_bookmark;
712 bookmark.mutable_bookmark()->set_url("url");
713 bookmark.mutable_bookmark()->set_title("title");
714 AddDefaultFieldValue(BOOKMARKS, &encrypted_bookmark);
715 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
716 foreign_cache_guid(), "-1");
717 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10,
718 foreign_cache_guid(), "-2");
719 mock_server_->AddUpdateDirectory(3, 0, "C", 10, 10,
720 foreign_cache_guid(), "-3");
721 mock_server_->AddUpdateDirectory(4, 0, "D", 10, 10,
722 foreign_cache_guid(), "-4");
723 SyncShareNudge();
724 // Server side change will put A in conflict.
725 mock_server_->AddUpdateDirectory(1, 0, "A", 20, 20,
726 foreign_cache_guid(), "-1");
728 // Mark bookmarks as encrypted and set the cryptographer to have pending
729 // keys.
730 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
731 Cryptographer other_cryptographer(&encryptor_);
732 other_cryptographer.AddKey(other_params);
733 sync_pb::EntitySpecifics specifics;
734 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
735 other_cryptographer.GetKeys(nigori->mutable_encryption_keybag());
736 dir_maker_.encryption_handler()->EnableEncryptEverything();
737 // Set up with an old passphrase, but have pending keys
738 GetCryptographer(&wtrans)->AddKey(key_params);
739 GetCryptographer(&wtrans)->Encrypt(bookmark,
740 encrypted_bookmark.mutable_encrypted());
741 GetCryptographer(&wtrans)->SetPendingKeys(nigori->encryption_keybag());
743 // In conflict but properly encrypted.
744 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
745 ASSERT_TRUE(A.good());
746 A.Put(IS_UNSYNCED, true);
747 A.Put(SPECIFICS, encrypted_bookmark);
748 A.Put(NON_UNIQUE_NAME, kEncryptedString);
749 // Not in conflict and properly encrypted.
750 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
751 ASSERT_TRUE(B.good());
752 B.Put(IS_UNSYNCED, true);
753 B.Put(SPECIFICS, encrypted_bookmark);
754 B.Put(NON_UNIQUE_NAME, kEncryptedString);
755 // Unencrypted specifics.
756 MutableEntry C(&wtrans, GET_BY_ID, ids_.FromNumber(3));
757 ASSERT_TRUE(C.good());
758 C.Put(IS_UNSYNCED, true);
759 C.Put(NON_UNIQUE_NAME, kEncryptedString);
760 // Unencrypted non_unique_name.
761 MutableEntry D(&wtrans, GET_BY_ID, ids_.FromNumber(4));
762 ASSERT_TRUE(D.good());
763 D.Put(IS_UNSYNCED, true);
764 D.Put(SPECIFICS, encrypted_bookmark);
765 D.Put(NON_UNIQUE_NAME, "not encrypted");
767 SyncShareNudge();
769 // Nothing should have commited due to bookmarks being encrypted and
770 // the cryptographer having pending keys. A would have been resolved
771 // as a simple conflict, but still be unsynced until the next sync cycle.
772 syncable::ReadTransaction rtrans(FROM_HERE, directory());
773 VERIFY_ENTRY(1, false, true, false, 0, 20, 20, ids_, &rtrans);
774 VERIFY_ENTRY(2, false, true, false, 0, 10, 10, ids_, &rtrans);
775 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_, &rtrans);
776 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_, &rtrans);
778 // Resolve the pending keys.
779 GetCryptographer(&rtrans)->DecryptPendingKeys(other_params);
781 SyncShareNudge();
783 // All properly encrypted and non-conflicting items should commit. "A" was
784 // conflicting, but last sync cycle resolved it as simple conflict, so on
785 // this sync cycle it committed succesfullly.
786 syncable::ReadTransaction rtrans(FROM_HERE, directory());
787 // Committed successfully.
788 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_, &rtrans);
789 // Committed successfully.
790 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_, &rtrans);
791 // Was not properly encrypted.
792 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_, &rtrans);
793 // Was not properly encrypted.
794 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_, &rtrans);
797 // Fix the remaining items.
798 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
799 MutableEntry C(&wtrans, GET_BY_ID, ids_.FromNumber(3));
800 ASSERT_TRUE(C.good());
801 C.Put(SPECIFICS, encrypted_bookmark);
802 C.Put(NON_UNIQUE_NAME, kEncryptedString);
803 MutableEntry D(&wtrans, GET_BY_ID, ids_.FromNumber(4));
804 ASSERT_TRUE(D.good());
805 D.Put(SPECIFICS, encrypted_bookmark);
806 D.Put(NON_UNIQUE_NAME, kEncryptedString);
808 SyncShareNudge();
810 const StatusController& status_controller = session_->status_controller();
811 // Expect success.
812 EXPECT_EQ(status_controller.model_neutral_state().commit_result, SYNCER_OK);
813 // None should be unsynced anymore.
814 syncable::ReadTransaction rtrans(FROM_HERE, directory());
815 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_, &rtrans);
816 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_, &rtrans);
817 VERIFY_ENTRY(3, false, false, false, 0, 11, 11, ids_, &rtrans);
818 VERIFY_ENTRY(4, false, false, false, 0, 11, 11, ids_, &rtrans);
822 TEST_F(SyncerTest, EncryptionAwareConflicts) {
823 KeyParams key_params = {"localhost", "dummy", "foobar"};
824 Cryptographer other_cryptographer(&encryptor_);
825 other_cryptographer.AddKey(key_params);
826 sync_pb::EntitySpecifics bookmark, encrypted_bookmark, modified_bookmark;
827 bookmark.mutable_bookmark()->set_title("title");
828 other_cryptographer.Encrypt(bookmark,
829 encrypted_bookmark.mutable_encrypted());
830 AddDefaultFieldValue(BOOKMARKS, &encrypted_bookmark);
831 modified_bookmark.mutable_bookmark()->set_title("title2");
832 other_cryptographer.Encrypt(modified_bookmark,
833 modified_bookmark.mutable_encrypted());
834 sync_pb::EntitySpecifics pref, encrypted_pref, modified_pref;
835 pref.mutable_preference()->set_name("name");
836 AddDefaultFieldValue(PREFERENCES, &encrypted_pref);
837 other_cryptographer.Encrypt(pref,
838 encrypted_pref.mutable_encrypted());
839 modified_pref.mutable_preference()->set_name("name2");
840 other_cryptographer.Encrypt(modified_pref,
841 modified_pref.mutable_encrypted());
843 // Mark bookmarks and preferences as encrypted and set the cryptographer to
844 // have pending keys.
845 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
846 sync_pb::EntitySpecifics specifics;
847 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
848 other_cryptographer.GetKeys(nigori->mutable_encryption_keybag());
849 dir_maker_.encryption_handler()->EnableEncryptEverything();
850 GetCryptographer(&wtrans)->SetPendingKeys(nigori->encryption_keybag());
851 EXPECT_TRUE(GetCryptographer(&wtrans)->has_pending_keys());
854 // We need to remember the exact position of our local items, so we can
855 // make updates that do not modify those positions.
856 UniquePosition pos1;
857 UniquePosition pos2;
858 UniquePosition pos3;
860 mock_server_->AddUpdateSpecifics(1, 0, "A", 10, 10, true, 0, bookmark,
861 foreign_cache_guid(), "-1");
862 mock_server_->AddUpdateSpecifics(2, 1, "B", 10, 10, false, 2, bookmark,
863 foreign_cache_guid(), "-2");
864 mock_server_->AddUpdateSpecifics(3, 1, "C", 10, 10, false, 1, bookmark,
865 foreign_cache_guid(), "-3");
866 mock_server_->AddUpdateSpecifics(4, 0, "D", 10, 10, false, 0, pref);
867 SyncShareNudge();
869 // Initial state. Everything is normal.
870 syncable::ReadTransaction rtrans(FROM_HERE, directory());
871 VERIFY_ENTRY(1, false, false, false, 0, 10, 10, ids_, &rtrans);
872 VERIFY_ENTRY(2, false, false, false, 1, 10, 10, ids_, &rtrans);
873 VERIFY_ENTRY(3, false, false, false, 1, 10, 10, ids_, &rtrans);
874 VERIFY_ENTRY(4, false, false, false, 0, 10, 10, ids_, &rtrans);
876 Entry entry1(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1));
877 ASSERT_TRUE(entry1.Get(syncable::UNIQUE_POSITION).Equals(
878 entry1.Get(syncable::SERVER_UNIQUE_POSITION)));
879 pos1 = entry1.Get(syncable::UNIQUE_POSITION);
880 Entry entry2(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(2));
881 pos2 = entry2.Get(syncable::UNIQUE_POSITION);
882 Entry entry3(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(3));
883 pos3 = entry3.Get(syncable::UNIQUE_POSITION);
886 // Server side encryption will not be applied due to undecryptable data.
887 // At this point, BASE_SERVER_SPECIFICS should be filled for all four items.
888 mock_server_->AddUpdateSpecifics(1, 0, kEncryptedString, 20, 20, true, 0,
889 encrypted_bookmark,
890 foreign_cache_guid(), "-1");
891 mock_server_->AddUpdateSpecifics(2, 1, kEncryptedString, 20, 20, false, 2,
892 encrypted_bookmark,
893 foreign_cache_guid(), "-2");
894 mock_server_->AddUpdateSpecifics(3, 1, kEncryptedString, 20, 20, false, 1,
895 encrypted_bookmark,
896 foreign_cache_guid(), "-3");
897 mock_server_->AddUpdateSpecifics(4, 0, kEncryptedString, 20, 20, false, 0,
898 encrypted_pref,
899 foreign_cache_guid(), "-4");
900 SyncShareNudge();
902 // All should be unapplied due to being undecryptable and have a valid
903 // BASE_SERVER_SPECIFICS.
904 syncable::ReadTransaction rtrans(FROM_HERE, directory());
905 VERIFY_ENTRY(1, true, false, true, 0, 10, 20, ids_, &rtrans);
906 VERIFY_ENTRY(2, true, false, true, 1, 10, 20, ids_, &rtrans);
907 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_, &rtrans);
908 VERIFY_ENTRY(4, true, false, true, 0, 10, 20, ids_, &rtrans);
911 // Server side change that don't modify anything should not affect
912 // BASE_SERVER_SPECIFICS (such as name changes and mtime changes).
913 mock_server_->AddUpdateSpecifics(1, 0, kEncryptedString, 30, 30, true, 0,
914 encrypted_bookmark,
915 foreign_cache_guid(), "-1");
916 mock_server_->AddUpdateSpecifics(2, 1, kEncryptedString, 30, 30, false, 2,
917 encrypted_bookmark,
918 foreign_cache_guid(), "-2");
919 // Item 3 doesn't change.
920 mock_server_->AddUpdateSpecifics(4, 0, kEncryptedString, 30, 30, false, 0,
921 encrypted_pref,
922 foreign_cache_guid(), "-4");
923 SyncShareNudge();
925 // Items 1, 2, and 4 should have newer server versions, 3 remains the same.
926 // All should remain unapplied due to be undecryptable.
927 syncable::ReadTransaction rtrans(FROM_HERE, directory());
928 VERIFY_ENTRY(1, true, false, true, 0, 10, 30, ids_, &rtrans);
929 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_, &rtrans);
930 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_, &rtrans);
931 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_, &rtrans);
934 // Positional changes, parent changes, and specifics changes should reset
935 // BASE_SERVER_SPECIFICS.
936 // Became unencrypted.
937 mock_server_->AddUpdateSpecifics(1, 0, "A", 40, 40, true, 0, bookmark,
938 foreign_cache_guid(), "-1");
939 // Reordered to after item 2.
940 mock_server_->AddUpdateSpecifics(3, 1, kEncryptedString, 30, 30, false, 3,
941 encrypted_bookmark,
942 foreign_cache_guid(), "-3");
943 SyncShareNudge();
945 // Items 2 and 4 should be the only ones with BASE_SERVER_SPECIFICS set.
946 // Items 1 is now unencrypted, so should have applied normally.
947 syncable::ReadTransaction rtrans(FROM_HERE, directory());
948 VERIFY_ENTRY(1, false, false, false, 0, 40, 40, ids_, &rtrans);
949 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_, &rtrans);
950 VERIFY_ENTRY(3, true, false, false, 1, 10, 30, ids_, &rtrans);
951 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_, &rtrans);
954 // Make local changes, which should remain unsynced for items 2, 3, 4.
956 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
957 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
958 ASSERT_TRUE(A.good());
959 A.Put(SPECIFICS, modified_bookmark);
960 A.Put(NON_UNIQUE_NAME, kEncryptedString);
961 A.Put(IS_UNSYNCED, true);
962 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
963 ASSERT_TRUE(B.good());
964 B.Put(SPECIFICS, modified_bookmark);
965 B.Put(NON_UNIQUE_NAME, kEncryptedString);
966 B.Put(IS_UNSYNCED, true);
967 MutableEntry C(&wtrans, GET_BY_ID, ids_.FromNumber(3));
968 ASSERT_TRUE(C.good());
969 C.Put(SPECIFICS, modified_bookmark);
970 C.Put(NON_UNIQUE_NAME, kEncryptedString);
971 C.Put(IS_UNSYNCED, true);
972 MutableEntry D(&wtrans, GET_BY_ID, ids_.FromNumber(4));
973 ASSERT_TRUE(D.good());
974 D.Put(SPECIFICS, modified_pref);
975 D.Put(NON_UNIQUE_NAME, kEncryptedString);
976 D.Put(IS_UNSYNCED, true);
978 SyncShareNudge();
980 // Item 1 remains unsynced due to there being pending keys.
981 // Items 2, 3, 4 should remain unsynced since they were not up to date.
982 syncable::ReadTransaction rtrans(FROM_HERE, directory());
983 VERIFY_ENTRY(1, false, true, false, 0, 40, 40, ids_, &rtrans);
984 VERIFY_ENTRY(2, true, true, true, 1, 10, 30, ids_, &rtrans);
985 VERIFY_ENTRY(3, true, true, false, 1, 10, 30, ids_, &rtrans);
986 VERIFY_ENTRY(4, true, true, true, 0, 10, 30, ids_, &rtrans);
990 syncable::ReadTransaction rtrans(FROM_HERE, directory());
991 // Resolve the pending keys.
992 GetCryptographer(&rtrans)->DecryptPendingKeys(key_params);
994 // First cycle resolves conflicts, second cycle commits changes.
995 SyncShareNudge();
996 EXPECT_EQ(2, status().model_neutral_state().num_server_overwrites);
997 EXPECT_EQ(1, status().model_neutral_state().num_local_overwrites);
998 // We successfully commited item(s).
999 EXPECT_EQ(status().model_neutral_state().commit_result, SYNCER_OK);
1000 SyncShareNudge();
1002 // Everything should be resolved now. The local changes should have
1003 // overwritten the server changes for 2 and 4, while the server changes
1004 // overwrote the local for entry 3.
1005 EXPECT_EQ(0, status().model_neutral_state().num_server_overwrites);
1006 EXPECT_EQ(0, status().model_neutral_state().num_local_overwrites);
1007 EXPECT_EQ(status().model_neutral_state().commit_result, SYNCER_OK);
1008 syncable::ReadTransaction rtrans(FROM_HERE, directory());
1009 VERIFY_ENTRY(1, false, false, false, 0, 41, 41, ids_, &rtrans);
1010 VERIFY_ENTRY(2, false, false, false, 1, 31, 31, ids_, &rtrans);
1011 VERIFY_ENTRY(3, false, false, false, 1, 30, 30, ids_, &rtrans);
1012 VERIFY_ENTRY(4, false, false, false, 0, 31, 31, ids_, &rtrans);
1015 #undef VERIFY_ENTRY
1017 TEST_F(SyncerTest, TestGetUnsyncedAndSimpleCommit) {
1019 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1020 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Pete");
1021 ASSERT_TRUE(parent.good());
1022 parent.Put(syncable::IS_UNSYNCED, true);
1023 parent.Put(syncable::IS_DIR, true);
1024 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1025 parent.Put(syncable::BASE_VERSION, 1);
1026 parent.Put(syncable::ID, parent_id_);
1027 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent_id_, "Pete");
1028 ASSERT_TRUE(child.good());
1029 child.Put(syncable::ID, child_id_);
1030 child.Put(syncable::BASE_VERSION, 1);
1031 WriteTestDataToEntry(&wtrans, &child);
1034 SyncShareNudge();
1035 ASSERT_EQ(2u, mock_server_->committed_ids().size());
1036 // If this test starts failing, be aware other sort orders could be valid.
1037 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
1038 EXPECT_TRUE(child_id_ == mock_server_->committed_ids()[1]);
1040 syncable::ReadTransaction rt(FROM_HERE, directory());
1041 Entry entry(&rt, syncable::GET_BY_ID, child_id_);
1042 ASSERT_TRUE(entry.good());
1043 VerifyTestDataInEntry(&rt, &entry);
1047 TEST_F(SyncerTest, TestPurgeWhileUnsynced) {
1048 // Similar to above, but throw a purge operation into the mix. Bug 49278.
1049 syncable::Id pref_node_id = TestIdFactory::MakeServer("Tim");
1051 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1052 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Pete");
1053 ASSERT_TRUE(parent.good());
1054 parent.Put(syncable::IS_UNSYNCED, true);
1055 parent.Put(syncable::IS_DIR, true);
1056 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1057 parent.Put(syncable::BASE_VERSION, 1);
1058 parent.Put(syncable::ID, parent_id_);
1059 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent_id_, "Pete");
1060 ASSERT_TRUE(child.good());
1061 child.Put(syncable::ID, child_id_);
1062 child.Put(syncable::BASE_VERSION, 1);
1063 WriteTestDataToEntry(&wtrans, &child);
1065 MutableEntry parent2(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Tim");
1066 ASSERT_TRUE(parent2.good());
1067 parent2.Put(syncable::IS_UNSYNCED, true);
1068 parent2.Put(syncable::IS_DIR, true);
1069 parent2.Put(syncable::SPECIFICS, DefaultPreferencesSpecifics());
1070 parent2.Put(syncable::BASE_VERSION, 1);
1071 parent2.Put(syncable::ID, pref_node_id);
1074 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES),
1075 ModelTypeSet());
1077 SyncShareNudge();
1078 ASSERT_EQ(2U, mock_server_->committed_ids().size());
1079 // If this test starts failing, be aware other sort orders could be valid.
1080 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
1081 EXPECT_TRUE(child_id_ == mock_server_->committed_ids()[1]);
1083 syncable::ReadTransaction rt(FROM_HERE, directory());
1084 Entry entry(&rt, syncable::GET_BY_ID, child_id_);
1085 ASSERT_TRUE(entry.good());
1086 VerifyTestDataInEntry(&rt, &entry);
1088 directory()->SaveChanges();
1090 syncable::ReadTransaction rt(FROM_HERE, directory());
1091 Entry entry(&rt, syncable::GET_BY_ID, pref_node_id);
1092 ASSERT_FALSE(entry.good());
1096 TEST_F(SyncerTest, TestPurgeWhileUnapplied) {
1097 // Similar to above, but for unapplied items. Bug 49278.
1099 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1100 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Pete");
1101 ASSERT_TRUE(parent.good());
1102 parent.Put(syncable::IS_UNAPPLIED_UPDATE, true);
1103 parent.Put(syncable::IS_DIR, true);
1104 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1105 parent.Put(syncable::BASE_VERSION, 1);
1106 parent.Put(syncable::ID, parent_id_);
1109 directory()->PurgeEntriesWithTypeIn(
1110 ModelTypeSet(BOOKMARKS), ModelTypeSet());
1112 SyncShareNudge();
1113 directory()->SaveChanges();
1115 syncable::ReadTransaction rt(FROM_HERE, directory());
1116 Entry entry(&rt, syncable::GET_BY_ID, parent_id_);
1117 ASSERT_FALSE(entry.good());
1121 TEST_F(SyncerTest, TestPurgeWithJournal) {
1123 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1124 MutableEntry parent(&wtrans, syncable::CREATE, BOOKMARKS, wtrans.root_id(),
1125 "Pete");
1126 ASSERT_TRUE(parent.good());
1127 parent.Put(syncable::IS_DIR, true);
1128 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1129 parent.Put(syncable::BASE_VERSION, 1);
1130 parent.Put(syncable::ID, parent_id_);
1131 MutableEntry child(&wtrans, syncable::CREATE, BOOKMARKS, parent_id_,
1132 "Pete");
1133 ASSERT_TRUE(child.good());
1134 child.Put(syncable::ID, child_id_);
1135 child.Put(syncable::BASE_VERSION, 1);
1136 WriteTestDataToEntry(&wtrans, &child);
1138 MutableEntry parent2(&wtrans, syncable::CREATE, PREFERENCES,
1139 wtrans.root_id(), "Tim");
1140 ASSERT_TRUE(parent2.good());
1141 parent2.Put(syncable::IS_DIR, true);
1142 parent2.Put(syncable::SPECIFICS, DefaultPreferencesSpecifics());
1143 parent2.Put(syncable::BASE_VERSION, 1);
1144 parent2.Put(syncable::ID, TestIdFactory::MakeServer("Tim"));
1147 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES, BOOKMARKS),
1148 ModelTypeSet(BOOKMARKS));
1150 // Verify bookmark nodes are saved in delete journal but not preference
1151 // node.
1152 syncable::ReadTransaction rt(FROM_HERE, directory());
1153 syncable::DeleteJournal* delete_journal = directory()->delete_journal();
1154 EXPECT_EQ(2u, delete_journal->GetDeleteJournalSize(&rt));
1155 syncable::EntryKernelSet journal_entries;
1156 directory()->delete_journal()->GetDeleteJournals(&rt, BOOKMARKS,
1157 &journal_entries);
1158 EXPECT_EQ(parent_id_, (*journal_entries.begin())->ref(syncable::ID));
1159 EXPECT_EQ(child_id_, (*journal_entries.rbegin())->ref(syncable::ID));
1163 TEST_F(SyncerTest, TestCommitListOrderingTwoItemsTall) {
1164 CommitOrderingTest items[] = {
1165 {1, ids_.FromNumber(-1001), ids_.FromNumber(-1000)},
1166 {0, ids_.FromNumber(-1000), ids_.FromNumber(0)},
1167 CommitOrderingTest::MakeLastCommitItem(),
1169 RunCommitOrderingTest(items);
1172 TEST_F(SyncerTest, TestCommitListOrderingThreeItemsTall) {
1173 CommitOrderingTest items[] = {
1174 {1, ids_.FromNumber(-2001), ids_.FromNumber(-2000)},
1175 {0, ids_.FromNumber(-2000), ids_.FromNumber(0)},
1176 {2, ids_.FromNumber(-2002), ids_.FromNumber(-2001)},
1177 CommitOrderingTest::MakeLastCommitItem(),
1179 RunCommitOrderingTest(items);
1182 TEST_F(SyncerTest, TestCommitListOrderingThreeItemsTallLimitedSize) {
1183 context_->set_max_commit_batch_size(2);
1184 CommitOrderingTest items[] = {
1185 {1, ids_.FromNumber(-2001), ids_.FromNumber(-2000)},
1186 {0, ids_.FromNumber(-2000), ids_.FromNumber(0)},
1187 {2, ids_.FromNumber(-2002), ids_.FromNumber(-2001)},
1188 CommitOrderingTest::MakeLastCommitItem(),
1190 RunCommitOrderingTest(items);
1193 TEST_F(SyncerTest, TestCommitListOrderingSingleDeletedItem) {
1194 CommitOrderingTest items[] = {
1195 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED}},
1196 CommitOrderingTest::MakeLastCommitItem(),
1198 RunCommitOrderingTest(items);
1201 TEST_F(SyncerTest, TestCommitListOrderingSingleUncommittedDeletedItem) {
1202 CommitOrderingTest items[] = {
1203 {-1, ids_.FromNumber(-1000), ids_.FromNumber(0), {DELETED}},
1204 CommitOrderingTest::MakeLastCommitItem(),
1206 RunCommitOrderingTest(items);
1209 TEST_F(SyncerTest, TestCommitListOrderingSingleDeletedItemWithUnroll) {
1210 CommitOrderingTest items[] = {
1211 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED}},
1212 CommitOrderingTest::MakeLastCommitItem(),
1214 RunCommitOrderingTest(items);
1217 TEST_F(SyncerTest,
1218 TestCommitListOrderingSingleLongDeletedItemWithUnroll) {
1219 CommitOrderingTest items[] = {
1220 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1221 CommitOrderingTest::MakeLastCommitItem(),
1223 RunCommitOrderingTest(items);
1226 TEST_F(SyncerTest, TestCommitListOrderingTwoLongDeletedItemWithUnroll) {
1227 CommitOrderingTest items[] = {
1228 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1229 {-1, ids_.FromNumber(1001), ids_.FromNumber(1000), {DELETED, OLD_MTIME}},
1230 CommitOrderingTest::MakeLastCommitItem(),
1232 RunCommitOrderingTest(items);
1235 TEST_F(SyncerTest, TestCommitListOrdering3LongDeletedItemsWithSizeLimit) {
1236 context_->set_max_commit_batch_size(2);
1237 CommitOrderingTest items[] = {
1238 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1239 {1, ids_.FromNumber(1001), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1240 {2, ids_.FromNumber(1002), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1241 CommitOrderingTest::MakeLastCommitItem(),
1243 RunCommitOrderingTest(items);
1246 TEST_F(SyncerTest, TestCommitListOrderingTwoDeletedItemsWithUnroll) {
1247 CommitOrderingTest items[] = {
1248 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED}},
1249 {-1, ids_.FromNumber(1001), ids_.FromNumber(1000), {DELETED}},
1250 CommitOrderingTest::MakeLastCommitItem(),
1252 RunCommitOrderingTest(items);
1255 TEST_F(SyncerTest, TestCommitListOrderingComplexDeletionScenario) {
1256 CommitOrderingTest items[] = {
1257 { 0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1258 {-1, ids_.FromNumber(1001), ids_.FromNumber(0), {SYNCED}},
1259 {1, ids_.FromNumber(1002), ids_.FromNumber(1001), {DELETED, OLD_MTIME}},
1260 {-1, ids_.FromNumber(1003), ids_.FromNumber(1001), {SYNCED}},
1261 {2, ids_.FromNumber(1004), ids_.FromNumber(1003), {DELETED}},
1262 CommitOrderingTest::MakeLastCommitItem(),
1264 RunCommitOrderingTest(items);
1267 TEST_F(SyncerTest,
1268 TestCommitListOrderingComplexDeletionScenarioWith2RecentDeletes) {
1269 CommitOrderingTest items[] = {
1270 { 0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1271 {-1, ids_.FromNumber(1001), ids_.FromNumber(0), {SYNCED}},
1272 {1, ids_.FromNumber(1002), ids_.FromNumber(1001), {DELETED, OLD_MTIME}},
1273 {-1, ids_.FromNumber(1003), ids_.FromNumber(1001), {SYNCED}},
1274 {2, ids_.FromNumber(1004), ids_.FromNumber(1003), {DELETED}},
1275 {3, ids_.FromNumber(1005), ids_.FromNumber(1003), {DELETED}},
1276 CommitOrderingTest::MakeLastCommitItem(),
1278 RunCommitOrderingTest(items);
1281 TEST_F(SyncerTest, TestCommitListOrderingDeleteMovedItems) {
1282 CommitOrderingTest items[] = {
1283 {1, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1284 {0, ids_.FromNumber(1001), ids_.FromNumber(1000), {DELETED, OLD_MTIME,
1285 MOVED_FROM_ROOT}},
1286 CommitOrderingTest::MakeLastCommitItem(),
1288 RunCommitOrderingTest(items);
1291 TEST_F(SyncerTest, TestCommitListOrderingWithNesting) {
1292 const base::Time& now_minus_2h =
1293 base::Time::Now() - base::TimeDelta::FromHours(2);
1295 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1297 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Bob");
1298 ASSERT_TRUE(parent.good());
1299 parent.Put(syncable::IS_UNSYNCED, true);
1300 parent.Put(syncable::IS_DIR, true);
1301 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1302 parent.Put(syncable::ID, ids_.FromNumber(100));
1303 parent.Put(syncable::BASE_VERSION, 1);
1304 MutableEntry child(
1305 &wtrans, CREATE, BOOKMARKS, ids_.FromNumber(100), "Bob");
1306 ASSERT_TRUE(child.good());
1307 child.Put(syncable::IS_UNSYNCED, true);
1308 child.Put(syncable::IS_DIR, true);
1309 child.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1310 child.Put(syncable::ID, ids_.FromNumber(101));
1311 child.Put(syncable::BASE_VERSION, 1);
1312 MutableEntry grandchild(
1313 &wtrans, CREATE, BOOKMARKS, ids_.FromNumber(101), "Bob");
1314 ASSERT_TRUE(grandchild.good());
1315 grandchild.Put(syncable::ID, ids_.FromNumber(102));
1316 grandchild.Put(syncable::IS_UNSYNCED, true);
1317 grandchild.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1318 grandchild.Put(syncable::BASE_VERSION, 1);
1321 // Create three deleted items which deletions we expect to be sent to the
1322 // server.
1323 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Pete");
1324 ASSERT_TRUE(parent.good());
1325 parent.Put(syncable::ID, ids_.FromNumber(103));
1326 parent.Put(syncable::IS_UNSYNCED, true);
1327 parent.Put(syncable::IS_DIR, true);
1328 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1329 parent.Put(syncable::IS_DEL, true);
1330 parent.Put(syncable::BASE_VERSION, 1);
1331 parent.Put(syncable::MTIME, now_minus_2h);
1332 MutableEntry child(
1333 &wtrans, CREATE, BOOKMARKS, ids_.FromNumber(103), "Pete");
1334 ASSERT_TRUE(child.good());
1335 child.Put(syncable::ID, ids_.FromNumber(104));
1336 child.Put(syncable::IS_UNSYNCED, true);
1337 child.Put(syncable::IS_DIR, true);
1338 child.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1339 child.Put(syncable::IS_DEL, true);
1340 child.Put(syncable::BASE_VERSION, 1);
1341 child.Put(syncable::MTIME, now_minus_2h);
1342 MutableEntry grandchild(
1343 &wtrans, CREATE, BOOKMARKS, ids_.FromNumber(104), "Pete");
1344 ASSERT_TRUE(grandchild.good());
1345 grandchild.Put(syncable::ID, ids_.FromNumber(105));
1346 grandchild.Put(syncable::IS_UNSYNCED, true);
1347 grandchild.Put(syncable::IS_DEL, true);
1348 grandchild.Put(syncable::IS_DIR, false);
1349 grandchild.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1350 grandchild.Put(syncable::BASE_VERSION, 1);
1351 grandchild.Put(syncable::MTIME, now_minus_2h);
1355 SyncShareNudge();
1356 ASSERT_EQ(6u, mock_server_->committed_ids().size());
1357 // This test will NOT unroll deletes because SERVER_PARENT_ID is not set.
1358 // It will treat these like moves.
1359 vector<syncable::Id> commit_ids(mock_server_->committed_ids());
1360 EXPECT_TRUE(ids_.FromNumber(100) == commit_ids[0]);
1361 EXPECT_TRUE(ids_.FromNumber(101) == commit_ids[1]);
1362 EXPECT_TRUE(ids_.FromNumber(102) == commit_ids[2]);
1363 // We don't guarantee the delete orders in this test, only that they occur
1364 // at the end.
1365 std::sort(commit_ids.begin() + 3, commit_ids.end());
1366 EXPECT_TRUE(ids_.FromNumber(103) == commit_ids[3]);
1367 EXPECT_TRUE(ids_.FromNumber(104) == commit_ids[4]);
1368 EXPECT_TRUE(ids_.FromNumber(105) == commit_ids[5]);
1371 TEST_F(SyncerTest, TestCommitListOrderingWithNewItems) {
1372 syncable::Id parent1_id = ids_.MakeServer("p1");
1373 syncable::Id parent2_id = ids_.MakeServer("p2");
1376 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1377 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "1");
1378 ASSERT_TRUE(parent.good());
1379 parent.Put(syncable::IS_UNSYNCED, true);
1380 parent.Put(syncable::IS_DIR, true);
1381 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1382 parent.Put(syncable::ID, parent1_id);
1383 MutableEntry child(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "2");
1384 ASSERT_TRUE(child.good());
1385 child.Put(syncable::IS_UNSYNCED, true);
1386 child.Put(syncable::IS_DIR, true);
1387 child.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1388 child.Put(syncable::ID, parent2_id);
1389 parent.Put(syncable::BASE_VERSION, 1);
1390 child.Put(syncable::BASE_VERSION, 1);
1393 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1394 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, parent1_id, "A");
1395 ASSERT_TRUE(parent.good());
1396 parent.Put(syncable::IS_UNSYNCED, true);
1397 parent.Put(syncable::IS_DIR, true);
1398 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1399 parent.Put(syncable::ID, ids_.FromNumber(102));
1400 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent1_id, "B");
1401 ASSERT_TRUE(child.good());
1402 child.Put(syncable::IS_UNSYNCED, true);
1403 child.Put(syncable::IS_DIR, true);
1404 child.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1405 child.Put(syncable::ID, ids_.FromNumber(-103));
1406 parent.Put(syncable::BASE_VERSION, 1);
1409 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1410 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, parent2_id, "A");
1411 ASSERT_TRUE(parent.good());
1412 parent.Put(syncable::IS_UNSYNCED, true);
1413 parent.Put(syncable::IS_DIR, true);
1414 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1415 parent.Put(syncable::ID, ids_.FromNumber(-104));
1416 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent2_id, "B");
1417 ASSERT_TRUE(child.good());
1418 child.Put(syncable::IS_UNSYNCED, true);
1419 child.Put(syncable::IS_DIR, true);
1420 child.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1421 child.Put(syncable::ID, ids_.FromNumber(105));
1422 child.Put(syncable::BASE_VERSION, 1);
1425 SyncShareNudge();
1426 ASSERT_EQ(6u, mock_server_->committed_ids().size());
1428 // This strange iteration and std::count() usage is to allow the order to
1429 // vary. All we really care about is that parent1_id and parent2_id are the
1430 // first two IDs, and that the children make up the next four. Other than
1431 // that, ordering doesn't matter.
1433 vector<syncable::Id>::const_iterator i =
1434 mock_server_->committed_ids().begin();
1435 vector<syncable::Id>::const_iterator parents_begin = i;
1436 i++;
1437 i++;
1438 vector<syncable::Id>::const_iterator parents_end = i;
1439 vector<syncable::Id>::const_iterator children_begin = i;
1440 vector<syncable::Id>::const_iterator children_end =
1441 mock_server_->committed_ids().end();
1443 EXPECT_EQ(1, count(parents_begin, parents_end, parent1_id));
1444 EXPECT_EQ(1, count(parents_begin, parents_end, parent2_id));
1446 EXPECT_EQ(1, count(children_begin, children_end, ids_.FromNumber(-103)));
1447 EXPECT_EQ(1, count(children_begin, children_end, ids_.FromNumber(102)));
1448 EXPECT_EQ(1, count(children_begin, children_end, ids_.FromNumber(105)));
1449 EXPECT_EQ(1, count(children_begin, children_end, ids_.FromNumber(-104)));
1452 TEST_F(SyncerTest, TestCommitListOrderingCounterexample) {
1453 syncable::Id child2_id = ids_.NewServerId();
1456 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1457 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "P");
1458 ASSERT_TRUE(parent.good());
1459 parent.Put(syncable::IS_UNSYNCED, true);
1460 parent.Put(syncable::IS_DIR, true);
1461 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1462 parent.Put(syncable::ID, parent_id_);
1463 MutableEntry child1(&wtrans, CREATE, BOOKMARKS, parent_id_, "1");
1464 ASSERT_TRUE(child1.good());
1465 child1.Put(syncable::IS_UNSYNCED, true);
1466 child1.Put(syncable::ID, child_id_);
1467 child1.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1468 MutableEntry child2(&wtrans, CREATE, BOOKMARKS, parent_id_, "2");
1469 ASSERT_TRUE(child2.good());
1470 child2.Put(syncable::IS_UNSYNCED, true);
1471 child2.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1472 child2.Put(syncable::ID, child2_id);
1474 parent.Put(syncable::BASE_VERSION, 1);
1475 child1.Put(syncable::BASE_VERSION, 1);
1476 child2.Put(syncable::BASE_VERSION, 1);
1479 SyncShareNudge();
1480 ASSERT_EQ(3u, mock_server_->committed_ids().size());
1481 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
1482 // There are two possible valid orderings.
1483 if (child2_id == mock_server_->committed_ids()[1]) {
1484 EXPECT_TRUE(child2_id == mock_server_->committed_ids()[1]);
1485 EXPECT_TRUE(child_id_ == mock_server_->committed_ids()[2]);
1486 } else {
1487 EXPECT_TRUE(child_id_ == mock_server_->committed_ids()[1]);
1488 EXPECT_TRUE(child2_id == mock_server_->committed_ids()[2]);
1492 TEST_F(SyncerTest, TestCommitListOrderingAndNewParent) {
1493 string parent1_name = "1";
1494 string parent2_name = "A";
1495 string child_name = "B";
1498 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1499 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(),
1500 parent1_name);
1501 ASSERT_TRUE(parent.good());
1502 parent.Put(syncable::IS_UNSYNCED, true);
1503 parent.Put(syncable::IS_DIR, true);
1504 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1505 parent.Put(syncable::ID, parent_id_);
1506 parent.Put(syncable::BASE_VERSION, 1);
1509 syncable::Id parent2_id = ids_.NewLocalId();
1510 syncable::Id child_id = ids_.NewServerId();
1512 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1513 MutableEntry parent2(
1514 &wtrans, CREATE, BOOKMARKS, parent_id_, parent2_name);
1515 ASSERT_TRUE(parent2.good());
1516 parent2.Put(syncable::IS_UNSYNCED, true);
1517 parent2.Put(syncable::IS_DIR, true);
1518 parent2.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1519 parent2.Put(syncable::ID, parent2_id);
1521 MutableEntry child(
1522 &wtrans, CREATE, BOOKMARKS, parent2_id, child_name);
1523 ASSERT_TRUE(child.good());
1524 child.Put(syncable::IS_UNSYNCED, true);
1525 child.Put(syncable::IS_DIR, true);
1526 child.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1527 child.Put(syncable::ID, child_id);
1528 child.Put(syncable::BASE_VERSION, 1);
1531 SyncShareNudge();
1532 ASSERT_EQ(3u, mock_server_->committed_ids().size());
1533 // If this test starts failing, be aware other sort orders could be valid.
1534 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
1535 EXPECT_TRUE(parent2_id == mock_server_->committed_ids()[1]);
1536 EXPECT_TRUE(child_id == mock_server_->committed_ids()[2]);
1538 syncable::ReadTransaction rtrans(FROM_HERE, directory());
1539 // Check that things committed correctly.
1540 Entry entry_1(&rtrans, syncable::GET_BY_ID, parent_id_);
1541 EXPECT_EQ(entry_1.Get(NON_UNIQUE_NAME), parent1_name);
1542 // Check that parent2 is a subfolder of parent1.
1543 EXPECT_EQ(1, CountEntriesWithName(&rtrans,
1544 parent_id_,
1545 parent2_name));
1547 // Parent2 was a local ID and thus should have changed on commit!
1548 Entry pre_commit_entry_parent2(&rtrans, syncable::GET_BY_ID, parent2_id);
1549 ASSERT_FALSE(pre_commit_entry_parent2.good());
1551 // Look up the new ID.
1552 Id parent2_committed_id =
1553 GetOnlyEntryWithName(&rtrans, parent_id_, parent2_name);
1554 EXPECT_TRUE(parent2_committed_id.ServerKnows());
1556 Entry child(&rtrans, syncable::GET_BY_ID, child_id);
1557 EXPECT_EQ(parent2_committed_id, child.Get(syncable::PARENT_ID));
1561 TEST_F(SyncerTest, TestCommitListOrderingAndNewParentAndChild) {
1562 string parent_name = "1";
1563 string parent2_name = "A";
1564 string child_name = "B";
1567 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1568 MutableEntry parent(&wtrans,
1569 CREATE, BOOKMARKS,
1570 wtrans.root_id(),
1571 parent_name);
1572 ASSERT_TRUE(parent.good());
1573 parent.Put(syncable::IS_UNSYNCED, true);
1574 parent.Put(syncable::IS_DIR, true);
1575 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1576 parent.Put(syncable::ID, parent_id_);
1577 parent.Put(syncable::BASE_VERSION, 1);
1580 int64 meta_handle_b;
1581 const Id parent2_local_id = ids_.NewLocalId();
1582 const Id child_local_id = ids_.NewLocalId();
1584 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1585 MutableEntry parent2(&wtrans, CREATE, BOOKMARKS, parent_id_, parent2_name);
1586 ASSERT_TRUE(parent2.good());
1587 parent2.Put(syncable::IS_UNSYNCED, true);
1588 parent2.Put(syncable::IS_DIR, true);
1589 parent2.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1591 parent2.Put(syncable::ID, parent2_local_id);
1592 MutableEntry child(
1593 &wtrans, CREATE, BOOKMARKS, parent2_local_id, child_name);
1594 ASSERT_TRUE(child.good());
1595 child.Put(syncable::IS_UNSYNCED, true);
1596 child.Put(syncable::IS_DIR, true);
1597 child.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1598 child.Put(syncable::ID, child_local_id);
1599 meta_handle_b = child.Get(syncable::META_HANDLE);
1602 SyncShareNudge();
1603 ASSERT_EQ(3u, mock_server_->committed_ids().size());
1604 // If this test starts failing, be aware other sort orders could be valid.
1605 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
1606 EXPECT_TRUE(parent2_local_id == mock_server_->committed_ids()[1]);
1607 EXPECT_TRUE(child_local_id == mock_server_->committed_ids()[2]);
1609 syncable::ReadTransaction rtrans(FROM_HERE, directory());
1611 Entry parent(&rtrans, syncable::GET_BY_ID,
1612 GetOnlyEntryWithName(&rtrans, rtrans.root_id(), parent_name));
1613 ASSERT_TRUE(parent.good());
1614 EXPECT_TRUE(parent.Get(syncable::ID).ServerKnows());
1616 Entry parent2(&rtrans, syncable::GET_BY_ID,
1617 GetOnlyEntryWithName(&rtrans, parent.Get(ID), parent2_name));
1618 ASSERT_TRUE(parent2.good());
1619 EXPECT_TRUE(parent2.Get(syncable::ID).ServerKnows());
1621 // Id changed on commit, so this should fail.
1622 Entry local_parent2_id_entry(&rtrans,
1623 syncable::GET_BY_ID,
1624 parent2_local_id);
1625 ASSERT_FALSE(local_parent2_id_entry.good());
1627 Entry entry_b(&rtrans, syncable::GET_BY_HANDLE, meta_handle_b);
1628 EXPECT_TRUE(entry_b.Get(syncable::ID).ServerKnows());
1629 EXPECT_TRUE(parent2.Get(syncable::ID) == entry_b.Get(syncable::PARENT_ID));
1633 TEST_F(SyncerTest, UpdateWithZeroLengthName) {
1634 // One illegal update
1635 mock_server_->AddUpdateDirectory(
1636 1, 0, std::string(), 1, 10, foreign_cache_guid(), "-1");
1637 // And one legal one that we're going to delete.
1638 mock_server_->AddUpdateDirectory(2, 0, "FOO", 1, 10,
1639 foreign_cache_guid(), "-2");
1640 SyncShareNudge();
1641 // Delete the legal one. The new update has a null name.
1642 mock_server_->AddUpdateDirectory(
1643 2, 0, std::string(), 2, 20, foreign_cache_guid(), "-2");
1644 mock_server_->SetLastUpdateDeleted();
1645 SyncShareNudge();
1648 TEST_F(SyncerTest, TestBasicUpdate) {
1649 string id = "some_id";
1650 string parent_id = "0";
1651 string name = "in_root";
1652 int64 version = 10;
1653 int64 timestamp = 10;
1654 mock_server_->AddUpdateDirectory(id, parent_id, name, version, timestamp,
1655 foreign_cache_guid(), "-1");
1657 SyncShareNudge();
1659 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1660 Entry entry(&trans, GET_BY_ID,
1661 syncable::Id::CreateFromServerId("some_id"));
1662 ASSERT_TRUE(entry.good());
1663 EXPECT_TRUE(entry.Get(IS_DIR));
1664 EXPECT_TRUE(entry.Get(SERVER_VERSION) == version);
1665 EXPECT_TRUE(entry.Get(BASE_VERSION) == version);
1666 EXPECT_FALSE(entry.Get(IS_UNAPPLIED_UPDATE));
1667 EXPECT_FALSE(entry.Get(IS_UNSYNCED));
1668 EXPECT_FALSE(entry.Get(SERVER_IS_DEL));
1669 EXPECT_FALSE(entry.Get(IS_DEL));
1673 TEST_F(SyncerTest, IllegalAndLegalUpdates) {
1674 Id root = TestIdFactory::root();
1675 // Should apply just fine.
1676 mock_server_->AddUpdateDirectory(1, 0, "in_root", 10, 10,
1677 foreign_cache_guid(), "-1");
1679 // Same name. But this SHOULD work.
1680 mock_server_->AddUpdateDirectory(2, 0, "in_root", 10, 10,
1681 foreign_cache_guid(), "-2");
1683 // Unknown parent: should never be applied. "-80" is a legal server ID,
1684 // because any string sent by the server is a legal server ID in the sync
1685 // protocol, but it's not the ID of any item known to the client. This
1686 // update should succeed validation, but be stuck in the unapplied state
1687 // until an item with the server ID "-80" arrives.
1688 mock_server_->AddUpdateDirectory(3, -80, "bad_parent", 10, 10,
1689 foreign_cache_guid(), "-3");
1691 SyncShareNudge();
1693 // Id 3 should be in conflict now.
1694 EXPECT_EQ(1, status().TotalNumConflictingItems());
1695 EXPECT_EQ(1, status().num_hierarchy_conflicts());
1697 // The only request in that loop should have been a GetUpdate.
1698 // At that point, we didn't know whether or not we had conflicts.
1699 ASSERT_TRUE(mock_server_->last_request().has_get_updates());
1700 VerifyHierarchyConflictsUnspecified(mock_server_->last_request());
1702 // These entries will be used in the second set of updates.
1703 mock_server_->AddUpdateDirectory(4, 0, "newer_version", 20, 10,
1704 foreign_cache_guid(), "-4");
1705 mock_server_->AddUpdateDirectory(5, 0, "circular1", 10, 10,
1706 foreign_cache_guid(), "-5");
1707 mock_server_->AddUpdateDirectory(6, 5, "circular2", 10, 10,
1708 foreign_cache_guid(), "-6");
1709 mock_server_->AddUpdateDirectory(9, 3, "bad_parent_child", 10, 10,
1710 foreign_cache_guid(), "-9");
1711 mock_server_->AddUpdateDirectory(100, 9, "bad_parent_child2", 10, 10,
1712 foreign_cache_guid(), "-100");
1713 mock_server_->AddUpdateDirectory(10, 0, "dir_to_bookmark", 10, 10,
1714 foreign_cache_guid(), "-10");
1716 SyncShareNudge();
1717 // The three items with an unresolved parent should be unapplied (3, 9, 100).
1718 // The name clash should also still be in conflict.
1719 EXPECT_EQ(3, status().TotalNumConflictingItems());
1720 EXPECT_EQ(3, status().num_hierarchy_conflicts());
1722 // This time around, we knew that there were conflicts.
1723 ASSERT_TRUE(mock_server_->last_request().has_get_updates());
1724 VerifyHierarchyConflictsReported(mock_server_->last_request());
1727 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1728 // Even though it has the same name, it should work.
1729 Entry name_clash(&trans, GET_BY_ID, ids_.FromNumber(2));
1730 ASSERT_TRUE(name_clash.good());
1731 EXPECT_FALSE(name_clash.Get(IS_UNAPPLIED_UPDATE))
1732 << "Duplicate name SHOULD be OK.";
1734 Entry bad_parent(&trans, GET_BY_ID, ids_.FromNumber(3));
1735 ASSERT_TRUE(bad_parent.good());
1736 EXPECT_TRUE(bad_parent.Get(IS_UNAPPLIED_UPDATE))
1737 << "child of unknown parent should be in conflict";
1739 Entry bad_parent_child(&trans, GET_BY_ID, ids_.FromNumber(9));
1740 ASSERT_TRUE(bad_parent_child.good());
1741 EXPECT_TRUE(bad_parent_child.Get(IS_UNAPPLIED_UPDATE))
1742 << "grandchild of unknown parent should be in conflict";
1744 Entry bad_parent_child2(&trans, GET_BY_ID, ids_.FromNumber(100));
1745 ASSERT_TRUE(bad_parent_child2.good());
1746 EXPECT_TRUE(bad_parent_child2.Get(IS_UNAPPLIED_UPDATE))
1747 << "great-grandchild of unknown parent should be in conflict";
1750 // Updating 1 should not affect item 2 of the same name.
1751 mock_server_->AddUpdateDirectory(1, 0, "new_name", 20, 20,
1752 foreign_cache_guid(), "-1");
1754 // Moving 5 under 6 will create a cycle: a conflict.
1755 mock_server_->AddUpdateDirectory(5, 6, "circular3", 20, 20,
1756 foreign_cache_guid(), "-5");
1758 // Flip the is_dir bit: should fail verify & be dropped.
1759 mock_server_->AddUpdateBookmark(10, 0, "dir_to_bookmark", 20, 20,
1760 foreign_cache_guid(), "-10");
1761 SyncShareNudge();
1763 // Version number older than last known: should fail verify & be dropped.
1764 mock_server_->AddUpdateDirectory(4, 0, "old_version", 10, 10,
1765 foreign_cache_guid(), "-4");
1766 SyncShareNudge();
1768 syncable::ReadTransaction trans(FROM_HERE, directory());
1770 Entry still_a_dir(&trans, GET_BY_ID, ids_.FromNumber(10));
1771 ASSERT_TRUE(still_a_dir.good());
1772 EXPECT_FALSE(still_a_dir.Get(IS_UNAPPLIED_UPDATE));
1773 EXPECT_EQ(10u, still_a_dir.Get(BASE_VERSION));
1774 EXPECT_EQ(10u, still_a_dir.Get(SERVER_VERSION));
1775 EXPECT_TRUE(still_a_dir.Get(IS_DIR));
1777 Entry rename(&trans, GET_BY_ID, ids_.FromNumber(1));
1778 ASSERT_TRUE(rename.good());
1779 EXPECT_EQ(root, rename.Get(PARENT_ID));
1780 EXPECT_EQ("new_name", rename.Get(NON_UNIQUE_NAME));
1781 EXPECT_FALSE(rename.Get(IS_UNAPPLIED_UPDATE));
1782 EXPECT_TRUE(ids_.FromNumber(1) == rename.Get(ID));
1783 EXPECT_EQ(20u, rename.Get(BASE_VERSION));
1785 Entry name_clash(&trans, GET_BY_ID, ids_.FromNumber(2));
1786 ASSERT_TRUE(name_clash.good());
1787 EXPECT_EQ(root, name_clash.Get(PARENT_ID));
1788 EXPECT_TRUE(ids_.FromNumber(2) == name_clash.Get(ID));
1789 EXPECT_EQ(10u, name_clash.Get(BASE_VERSION));
1790 EXPECT_EQ("in_root", name_clash.Get(NON_UNIQUE_NAME));
1792 Entry ignored_old_version(&trans, GET_BY_ID, ids_.FromNumber(4));
1793 ASSERT_TRUE(ignored_old_version.good());
1794 EXPECT_TRUE(
1795 ignored_old_version.Get(NON_UNIQUE_NAME) == "newer_version");
1796 EXPECT_FALSE(ignored_old_version.Get(IS_UNAPPLIED_UPDATE));
1797 EXPECT_EQ(20u, ignored_old_version.Get(BASE_VERSION));
1799 Entry circular_parent_issue(&trans, GET_BY_ID, ids_.FromNumber(5));
1800 ASSERT_TRUE(circular_parent_issue.good());
1801 EXPECT_TRUE(circular_parent_issue.Get(IS_UNAPPLIED_UPDATE))
1802 << "circular move should be in conflict";
1803 EXPECT_TRUE(circular_parent_issue.Get(PARENT_ID) == root_id_);
1804 EXPECT_TRUE(circular_parent_issue.Get(SERVER_PARENT_ID) ==
1805 ids_.FromNumber(6));
1806 EXPECT_EQ(10u, circular_parent_issue.Get(BASE_VERSION));
1808 Entry circular_parent_target(&trans, GET_BY_ID, ids_.FromNumber(6));
1809 ASSERT_TRUE(circular_parent_target.good());
1810 EXPECT_FALSE(circular_parent_target.Get(IS_UNAPPLIED_UPDATE));
1811 EXPECT_TRUE(circular_parent_issue.Get(ID) ==
1812 circular_parent_target.Get(PARENT_ID));
1813 EXPECT_EQ(10u, circular_parent_target.Get(BASE_VERSION));
1816 EXPECT_FALSE(saw_syncer_event_);
1817 EXPECT_EQ(4, status().TotalNumConflictingItems());
1818 EXPECT_EQ(4, status().num_hierarchy_conflicts());
1821 TEST_F(SyncerTest, CommitTimeRename) {
1822 int64 metahandle_folder;
1823 int64 metahandle_new_entry;
1825 // Create a folder and an entry.
1827 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1828 MutableEntry parent(&trans, CREATE, BOOKMARKS, root_id_, "Folder");
1829 ASSERT_TRUE(parent.good());
1830 parent.Put(IS_DIR, true);
1831 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1832 parent.Put(IS_UNSYNCED, true);
1833 metahandle_folder = parent.Get(META_HANDLE);
1835 MutableEntry entry(&trans, CREATE, BOOKMARKS, parent.Get(ID), "new_entry");
1836 ASSERT_TRUE(entry.good());
1837 metahandle_new_entry = entry.Get(META_HANDLE);
1838 WriteTestDataToEntry(&trans, &entry);
1841 // Mix in a directory creation too for later.
1842 mock_server_->AddUpdateDirectory(2, 0, "dir_in_root", 10, 10,
1843 foreign_cache_guid(), "-2");
1844 mock_server_->SetCommitTimeRename("renamed_");
1845 SyncShareNudge();
1847 // Verify it was correctly renamed.
1849 syncable::ReadTransaction trans(FROM_HERE, directory());
1850 Entry entry_folder(&trans, GET_BY_HANDLE, metahandle_folder);
1851 ASSERT_TRUE(entry_folder.good());
1852 EXPECT_EQ("renamed_Folder", entry_folder.Get(NON_UNIQUE_NAME));
1854 Entry entry_new(&trans, GET_BY_HANDLE, metahandle_new_entry);
1855 ASSERT_TRUE(entry_new.good());
1856 EXPECT_EQ(entry_folder.Get(ID), entry_new.Get(PARENT_ID));
1857 EXPECT_EQ("renamed_new_entry", entry_new.Get(NON_UNIQUE_NAME));
1859 // And that the unrelated directory creation worked without a rename.
1860 Entry new_dir(&trans, GET_BY_ID, ids_.FromNumber(2));
1861 EXPECT_TRUE(new_dir.good());
1862 EXPECT_EQ("dir_in_root", new_dir.Get(NON_UNIQUE_NAME));
1867 TEST_F(SyncerTest, CommitTimeRenameI18N) {
1868 // This is utf-8 for the diacritized Internationalization.
1869 const char* i18nString = "\xc3\x8e\xc3\xb1\x74\xc3\xa9\x72\xc3\xb1"
1870 "\xc3\xa5\x74\xc3\xae\xc3\xb6\xc3\xb1\xc3\xa5\x6c\xc3\xae"
1871 "\xc2\x9e\xc3\xa5\x74\xc3\xae\xc3\xb6\xc3\xb1";
1873 int64 metahandle;
1874 // Create a folder, expect a commit time rename.
1876 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1877 MutableEntry parent(&trans, CREATE, BOOKMARKS, root_id_, "Folder");
1878 ASSERT_TRUE(parent.good());
1879 parent.Put(IS_DIR, true);
1880 parent.Put(SPECIFICS, DefaultBookmarkSpecifics());
1881 parent.Put(IS_UNSYNCED, true);
1882 metahandle = parent.Get(META_HANDLE);
1885 mock_server_->SetCommitTimeRename(i18nString);
1886 SyncShareNudge();
1888 // Verify it was correctly renamed.
1890 syncable::ReadTransaction trans(FROM_HERE, directory());
1891 string expected_folder_name(i18nString);
1892 expected_folder_name.append("Folder");
1895 Entry entry_folder(&trans, GET_BY_HANDLE, metahandle);
1896 ASSERT_TRUE(entry_folder.good());
1897 EXPECT_EQ(expected_folder_name, entry_folder.Get(NON_UNIQUE_NAME));
1901 // A commit with a lost response produces an update that has to be reunited with
1902 // its parent.
1903 TEST_F(SyncerTest, CommitReuniteUpdateAdjustsChildren) {
1904 // Create a folder in the root.
1905 int64 metahandle_folder;
1907 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1908 MutableEntry entry(
1909 &trans, CREATE, BOOKMARKS, trans.root_id(), "new_folder");
1910 ASSERT_TRUE(entry.good());
1911 entry.Put(IS_DIR, true);
1912 entry.Put(SPECIFICS, DefaultBookmarkSpecifics());
1913 entry.Put(IS_UNSYNCED, true);
1914 metahandle_folder = entry.Get(META_HANDLE);
1917 // Verify it and pull the ID out of the folder.
1918 syncable::Id folder_id;
1919 int64 metahandle_entry;
1921 syncable::ReadTransaction trans(FROM_HERE, directory());
1922 Entry entry(&trans, GET_BY_HANDLE, metahandle_folder);
1923 ASSERT_TRUE(entry.good());
1924 folder_id = entry.Get(ID);
1925 ASSERT_TRUE(!folder_id.ServerKnows());
1928 // Create an entry in the newly created folder.
1930 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1931 MutableEntry entry(&trans, CREATE, BOOKMARKS, folder_id, "new_entry");
1932 ASSERT_TRUE(entry.good());
1933 metahandle_entry = entry.Get(META_HANDLE);
1934 WriteTestDataToEntry(&trans, &entry);
1937 // Verify it and pull the ID out of the entry.
1938 syncable::Id entry_id;
1940 syncable::ReadTransaction trans(FROM_HERE, directory());
1941 Entry entry(&trans, syncable::GET_BY_HANDLE, metahandle_entry);
1942 ASSERT_TRUE(entry.good());
1943 EXPECT_EQ(folder_id, entry.Get(PARENT_ID));
1944 EXPECT_EQ("new_entry", entry.Get(NON_UNIQUE_NAME));
1945 entry_id = entry.Get(ID);
1946 EXPECT_TRUE(!entry_id.ServerKnows());
1947 VerifyTestDataInEntry(&trans, &entry);
1950 // Now, to emulate a commit response failure, we just don't commit it.
1951 int64 new_version = 150; // any larger value.
1952 int64 timestamp = 20; // arbitrary value.
1953 syncable::Id new_folder_id =
1954 syncable::Id::CreateFromServerId("folder_server_id");
1956 // The following update should cause the folder to both apply the update, as
1957 // well as reassociate the id.
1958 mock_server_->AddUpdateDirectory(new_folder_id, root_id_,
1959 "new_folder", new_version, timestamp,
1960 local_cache_guid(), folder_id.GetServerId());
1962 // We don't want it accidentally committed, just the update applied.
1963 mock_server_->set_conflict_all_commits(true);
1965 // Alright! Apply that update!
1966 SyncShareNudge();
1968 // The folder's ID should have been updated.
1969 syncable::ReadTransaction trans(FROM_HERE, directory());
1970 Entry folder(&trans, GET_BY_HANDLE, metahandle_folder);
1971 ASSERT_TRUE(folder.good());
1972 EXPECT_EQ("new_folder", folder.Get(NON_UNIQUE_NAME));
1973 EXPECT_TRUE(new_version == folder.Get(BASE_VERSION));
1974 EXPECT_TRUE(new_folder_id == folder.Get(ID));
1975 EXPECT_TRUE(folder.Get(ID).ServerKnows());
1976 EXPECT_EQ(trans.root_id(), folder.Get(PARENT_ID));
1978 // Since it was updated, the old folder should not exist.
1979 Entry old_dead_folder(&trans, GET_BY_ID, folder_id);
1980 EXPECT_FALSE(old_dead_folder.good());
1982 // The child's parent should have changed.
1983 Entry entry(&trans, syncable::GET_BY_HANDLE, metahandle_entry);
1984 ASSERT_TRUE(entry.good());
1985 EXPECT_EQ("new_entry", entry.Get(NON_UNIQUE_NAME));
1986 EXPECT_EQ(new_folder_id, entry.Get(PARENT_ID));
1987 EXPECT_TRUE(!entry.Get(ID).ServerKnows());
1988 VerifyTestDataInEntry(&trans, &entry);
1992 // A commit with a lost response produces an update that has to be reunited with
1993 // its parent.
1994 TEST_F(SyncerTest, CommitReuniteUpdate) {
1995 // Create an entry in the root.
1996 int64 entry_metahandle;
1998 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1999 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "new_entry");
2000 ASSERT_TRUE(entry.good());
2001 entry_metahandle = entry.Get(META_HANDLE);
2002 WriteTestDataToEntry(&trans, &entry);
2005 // Verify it and pull the ID out.
2006 syncable::Id entry_id;
2008 syncable::ReadTransaction trans(FROM_HERE, directory());
2010 Entry entry(&trans, GET_BY_HANDLE, entry_metahandle);
2011 ASSERT_TRUE(entry.good());
2012 entry_id = entry.Get(ID);
2013 EXPECT_TRUE(!entry_id.ServerKnows());
2014 VerifyTestDataInEntry(&trans, &entry);
2017 // Now, to emulate a commit response failure, we just don't commit it.
2018 int64 new_version = 150; // any larger value.
2019 int64 timestamp = 20; // arbitrary value.
2020 syncable::Id new_entry_id = syncable::Id::CreateFromServerId("server_id");
2022 // Generate an update from the server with a relevant ID reassignment.
2023 mock_server_->AddUpdateBookmark(new_entry_id, root_id_,
2024 "new_entry", new_version, timestamp,
2025 local_cache_guid(), entry_id.GetServerId());
2027 // We don't want it accidentally committed, just the update applied.
2028 mock_server_->set_conflict_all_commits(true);
2030 // Alright! Apply that update!
2031 SyncShareNudge();
2033 syncable::ReadTransaction trans(FROM_HERE, directory());
2034 Entry entry(&trans, GET_BY_HANDLE, entry_metahandle);
2035 ASSERT_TRUE(entry.good());
2036 EXPECT_TRUE(new_version == entry.Get(BASE_VERSION));
2037 EXPECT_TRUE(new_entry_id == entry.Get(ID));
2038 EXPECT_EQ("new_entry", entry.Get(NON_UNIQUE_NAME));
2042 // A commit with a lost response must work even if the local entry was deleted
2043 // before the update is applied. We should not duplicate the local entry in
2044 // this case, but just create another one alongside. We may wish to examine
2045 // this behavior in the future as it can create hanging uploads that never
2046 // finish, that must be cleaned up on the server side after some time.
2047 TEST_F(SyncerTest, CommitReuniteUpdateDoesNotChokeOnDeletedLocalEntry) {
2048 // Create a entry in the root.
2049 int64 entry_metahandle;
2051 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2052 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "new_entry");
2053 ASSERT_TRUE(entry.good());
2054 entry_metahandle = entry.Get(META_HANDLE);
2055 WriteTestDataToEntry(&trans, &entry);
2057 // Verify it and pull the ID out.
2058 syncable::Id entry_id;
2060 syncable::ReadTransaction trans(FROM_HERE, directory());
2061 Entry entry(&trans, GET_BY_HANDLE, entry_metahandle);
2062 ASSERT_TRUE(entry.good());
2063 entry_id = entry.Get(ID);
2064 EXPECT_TRUE(!entry_id.ServerKnows());
2065 VerifyTestDataInEntry(&trans, &entry);
2068 // Now, to emulate a commit response failure, we just don't commit it.
2069 int64 new_version = 150; // any larger value.
2070 int64 timestamp = 20; // arbitrary value.
2071 syncable::Id new_entry_id = syncable::Id::CreateFromServerId("server_id");
2073 // Generate an update from the server with a relevant ID reassignment.
2074 mock_server_->AddUpdateBookmark(new_entry_id, root_id_,
2075 "new_entry", new_version, timestamp,
2076 local_cache_guid(), entry_id.GetServerId());
2078 // We don't want it accidentally committed, just the update applied.
2079 mock_server_->set_conflict_all_commits(true);
2081 // Purposefully delete the entry now before the update application finishes.
2083 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2084 Id new_entry_id = GetOnlyEntryWithName(
2085 &trans, trans.root_id(), "new_entry");
2086 MutableEntry entry(&trans, GET_BY_ID, new_entry_id);
2087 ASSERT_TRUE(entry.good());
2088 entry.Put(syncable::IS_DEL, true);
2091 // Just don't CHECK fail in sync, have the update split.
2092 SyncShareNudge();
2094 syncable::ReadTransaction trans(FROM_HERE, directory());
2095 Id new_entry_id = GetOnlyEntryWithName(
2096 &trans, trans.root_id(), "new_entry");
2097 Entry entry(&trans, GET_BY_ID, new_entry_id);
2098 ASSERT_TRUE(entry.good());
2099 EXPECT_FALSE(entry.Get(IS_DEL));
2101 Entry old_entry(&trans, GET_BY_ID, entry_id);
2102 ASSERT_TRUE(old_entry.good());
2103 EXPECT_TRUE(old_entry.Get(IS_DEL));
2107 // TODO(chron): Add more unsanitized name tests.
2108 TEST_F(SyncerTest, ConflictMatchingEntryHandlesUnsanitizedNames) {
2109 mock_server_->AddUpdateDirectory(1, 0, "A/A", 10, 10,
2110 foreign_cache_guid(), "-1");
2111 mock_server_->AddUpdateDirectory(2, 0, "B/B", 10, 10,
2112 foreign_cache_guid(), "-2");
2113 mock_server_->set_conflict_all_commits(true);
2114 SyncShareNudge();
2116 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2118 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2119 ASSERT_TRUE(A.good());
2120 A.Put(IS_UNSYNCED, true);
2121 A.Put(IS_UNAPPLIED_UPDATE, true);
2122 A.Put(SERVER_VERSION, 20);
2124 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
2125 ASSERT_TRUE(B.good());
2126 B.Put(IS_UNAPPLIED_UPDATE, true);
2127 B.Put(SERVER_VERSION, 20);
2129 SyncShareNudge();
2130 saw_syncer_event_ = false;
2131 mock_server_->set_conflict_all_commits(false);
2134 syncable::ReadTransaction trans(FROM_HERE, directory());
2136 Entry A(&trans, GET_BY_ID, ids_.FromNumber(1));
2137 ASSERT_TRUE(A.good());
2138 EXPECT_TRUE(A.Get(IS_UNSYNCED) == false);
2139 EXPECT_TRUE(A.Get(IS_UNAPPLIED_UPDATE) == false);
2140 EXPECT_TRUE(A.Get(SERVER_VERSION) == 20);
2142 Entry B(&trans, GET_BY_ID, ids_.FromNumber(2));
2143 ASSERT_TRUE(B.good());
2144 EXPECT_TRUE(B.Get(IS_UNSYNCED) == false);
2145 EXPECT_TRUE(B.Get(IS_UNAPPLIED_UPDATE) == false);
2146 EXPECT_TRUE(B.Get(SERVER_VERSION) == 20);
2150 TEST_F(SyncerTest, ConflictMatchingEntryHandlesNormalNames) {
2151 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
2152 foreign_cache_guid(), "-1");
2153 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10,
2154 foreign_cache_guid(), "-2");
2155 mock_server_->set_conflict_all_commits(true);
2156 SyncShareNudge();
2158 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2160 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2161 ASSERT_TRUE(A.good());
2162 A.Put(IS_UNSYNCED, true);
2163 A.Put(IS_UNAPPLIED_UPDATE, true);
2164 A.Put(SERVER_VERSION, 20);
2166 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
2167 ASSERT_TRUE(B.good());
2168 B.Put(IS_UNAPPLIED_UPDATE, true);
2169 B.Put(SERVER_VERSION, 20);
2171 SyncShareNudge();
2172 saw_syncer_event_ = false;
2173 mock_server_->set_conflict_all_commits(false);
2176 syncable::ReadTransaction trans(FROM_HERE, directory());
2178 Entry A(&trans, GET_BY_ID, ids_.FromNumber(1));
2179 ASSERT_TRUE(A.good());
2180 EXPECT_TRUE(A.Get(IS_UNSYNCED) == false);
2181 EXPECT_TRUE(A.Get(IS_UNAPPLIED_UPDATE) == false);
2182 EXPECT_TRUE(A.Get(SERVER_VERSION) == 20);
2184 Entry B(&trans, GET_BY_ID, ids_.FromNumber(2));
2185 ASSERT_TRUE(B.good());
2186 EXPECT_TRUE(B.Get(IS_UNSYNCED) == false);
2187 EXPECT_TRUE(B.Get(IS_UNAPPLIED_UPDATE) == false);
2188 EXPECT_TRUE(B.Get(SERVER_VERSION) == 20);
2192 TEST_F(SyncerTest, ReverseFolderOrderingTest) {
2193 mock_server_->AddUpdateDirectory(4, 3, "ggchild", 10, 10,
2194 foreign_cache_guid(), "-4");
2195 mock_server_->AddUpdateDirectory(3, 2, "gchild", 10, 10,
2196 foreign_cache_guid(), "-3");
2197 mock_server_->AddUpdateDirectory(5, 4, "gggchild", 10, 10,
2198 foreign_cache_guid(), "-5");
2199 mock_server_->AddUpdateDirectory(2, 1, "child", 10, 10,
2200 foreign_cache_guid(), "-2");
2201 mock_server_->AddUpdateDirectory(1, 0, "parent", 10, 10,
2202 foreign_cache_guid(), "-1");
2203 SyncShareNudge();
2204 syncable::ReadTransaction trans(FROM_HERE, directory());
2206 Id child_id = GetOnlyEntryWithName(
2207 &trans, ids_.FromNumber(4), "gggchild");
2208 Entry child(&trans, GET_BY_ID, child_id);
2209 ASSERT_TRUE(child.good());
2212 class EntryCreatedInNewFolderTest : public SyncerTest {
2213 public:
2214 void CreateFolderInBob() {
2215 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2216 MutableEntry bob(&trans,
2217 syncable::GET_BY_ID,
2218 GetOnlyEntryWithName(&trans,
2219 TestIdFactory::root(),
2220 "bob"));
2221 CHECK(bob.good());
2223 MutableEntry entry2(
2224 &trans, CREATE, BOOKMARKS, bob.Get(syncable::ID), "bob");
2225 CHECK(entry2.good());
2226 entry2.Put(syncable::IS_DIR, true);
2227 entry2.Put(syncable::IS_UNSYNCED, true);
2228 entry2.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
2232 TEST_F(EntryCreatedInNewFolderTest, EntryCreatedInNewFolderMidSync) {
2234 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2235 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "bob");
2236 ASSERT_TRUE(entry.good());
2237 entry.Put(syncable::IS_DIR, true);
2238 entry.Put(syncable::IS_UNSYNCED, true);
2239 entry.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
2242 mock_server_->SetMidCommitCallback(
2243 base::Bind(&EntryCreatedInNewFolderTest::CreateFolderInBob,
2244 base::Unretained(this)));
2245 syncer_->SyncShare(session_.get(), COMMIT, SYNCER_END);
2246 // We loop until no unsynced handles remain, so we will commit both ids.
2247 EXPECT_EQ(2u, mock_server_->committed_ids().size());
2249 syncable::ReadTransaction trans(FROM_HERE, directory());
2250 Entry parent_entry(&trans, syncable::GET_BY_ID,
2251 GetOnlyEntryWithName(&trans, TestIdFactory::root(), "bob"));
2252 ASSERT_TRUE(parent_entry.good());
2254 Id child_id =
2255 GetOnlyEntryWithName(&trans, parent_entry.Get(ID), "bob");
2256 Entry child(&trans, syncable::GET_BY_ID, child_id);
2257 ASSERT_TRUE(child.good());
2258 EXPECT_EQ(parent_entry.Get(ID), child.Get(PARENT_ID));
2262 TEST_F(SyncerTest, NegativeIDInUpdate) {
2263 mock_server_->AddUpdateBookmark(-10, 0, "bad", 40, 40,
2264 foreign_cache_guid(), "-100");
2265 SyncShareNudge();
2266 // The negative id would make us CHECK!
2269 TEST_F(SyncerTest, UnappliedUpdateOnCreatedItemItemDoesNotCrash) {
2270 int64 metahandle_fred;
2271 syncable::Id orig_id;
2273 // Create an item.
2274 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2275 MutableEntry fred_match(&trans, CREATE, BOOKMARKS, trans.root_id(),
2276 "fred_match");
2277 ASSERT_TRUE(fred_match.good());
2278 metahandle_fred = fred_match.Get(META_HANDLE);
2279 orig_id = fred_match.Get(ID);
2280 WriteTestDataToEntry(&trans, &fred_match);
2282 // Commit it.
2283 SyncShareNudge();
2284 EXPECT_EQ(1u, mock_server_->committed_ids().size());
2285 mock_server_->set_conflict_all_commits(true);
2286 syncable::Id fred_match_id;
2288 // Now receive a change from outside.
2289 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2290 MutableEntry fred_match(&trans, GET_BY_HANDLE, metahandle_fred);
2291 ASSERT_TRUE(fred_match.good());
2292 EXPECT_TRUE(fred_match.Get(ID).ServerKnows());
2293 fred_match_id = fred_match.Get(ID);
2294 mock_server_->AddUpdateBookmark(fred_match_id, trans.root_id(),
2295 "fred_match", 40, 40, local_cache_guid(), orig_id.GetServerId());
2297 // Run the syncer.
2298 for (int i = 0 ; i < 30 ; ++i) {
2299 SyncShareNudge();
2304 * In the event that we have a double changed entry, that is changed on both
2305 * the client and the server, the conflict resolver should just drop one of
2306 * them and accept the other.
2309 TEST_F(SyncerTest, DoublyChangedWithResolver) {
2310 syncable::Id local_id;
2312 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2313 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, root_id_, "Folder");
2314 ASSERT_TRUE(parent.good());
2315 parent.Put(syncable::IS_DIR, true);
2316 parent.Put(syncable::ID, parent_id_);
2317 parent.Put(syncable::BASE_VERSION, 5);
2318 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
2319 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent_id_, "Pete.htm");
2320 ASSERT_TRUE(child.good());
2321 local_id = child.Get(syncable::ID);
2322 child.Put(syncable::ID, child_id_);
2323 child.Put(syncable::BASE_VERSION, 10);
2324 WriteTestDataToEntry(&wtrans, &child);
2326 mock_server_->AddUpdateBookmark(child_id_, parent_id_, "Pete2.htm", 11, 10,
2327 local_cache_guid(), local_id.GetServerId());
2328 mock_server_->set_conflict_all_commits(true);
2329 SyncShareNudge();
2330 syncable::Directory::ChildHandles children;
2332 syncable::ReadTransaction trans(FROM_HERE, directory());
2333 directory()->GetChildHandlesById(&trans, parent_id_, &children);
2334 // We expect the conflict resolver to preserve the local entry.
2335 Entry child(&trans, syncable::GET_BY_ID, child_id_);
2336 ASSERT_TRUE(child.good());
2337 EXPECT_TRUE(child.Get(syncable::IS_UNSYNCED));
2338 EXPECT_FALSE(child.Get(syncable::IS_UNAPPLIED_UPDATE));
2339 EXPECT_TRUE(child.Get(SPECIFICS).has_bookmark());
2340 EXPECT_EQ("Pete.htm", child.Get(NON_UNIQUE_NAME));
2341 VerifyTestBookmarkDataInEntry(&child);
2344 // Only one entry, since we just overwrite one.
2345 EXPECT_EQ(1u, children.size());
2346 saw_syncer_event_ = false;
2349 // We got this repro case when someone was editing bookmarks while sync was
2350 // occuring. The entry had changed out underneath the user.
2351 TEST_F(SyncerTest, CommitsUpdateDoesntAlterEntry) {
2352 const base::Time& test_time = ProtoTimeToTime(123456);
2353 syncable::Id local_id;
2354 int64 entry_metahandle;
2356 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2357 MutableEntry entry(&wtrans, CREATE, BOOKMARKS, root_id_, "Pete");
2358 ASSERT_TRUE(entry.good());
2359 EXPECT_FALSE(entry.Get(ID).ServerKnows());
2360 local_id = entry.Get(ID);
2361 entry.Put(syncable::IS_DIR, true);
2362 entry.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
2363 entry.Put(syncable::IS_UNSYNCED, true);
2364 entry.Put(syncable::MTIME, test_time);
2365 entry_metahandle = entry.Get(META_HANDLE);
2367 SyncShareNudge();
2368 syncable::Id id;
2369 int64 version;
2371 syncable::ReadTransaction trans(FROM_HERE, directory());
2372 Entry entry(&trans, syncable::GET_BY_HANDLE, entry_metahandle);
2373 ASSERT_TRUE(entry.good());
2374 id = entry.Get(ID);
2375 EXPECT_TRUE(id.ServerKnows());
2376 version = entry.Get(BASE_VERSION);
2378 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
2379 update->set_originator_cache_guid(local_cache_guid());
2380 update->set_originator_client_item_id(local_id.GetServerId());
2381 EXPECT_EQ("Pete", update->name());
2382 EXPECT_EQ(id.GetServerId(), update->id_string());
2383 EXPECT_EQ(root_id_.GetServerId(), update->parent_id_string());
2384 EXPECT_EQ(version, update->version());
2385 SyncShareNudge();
2387 syncable::ReadTransaction trans(FROM_HERE, directory());
2388 Entry entry(&trans, syncable::GET_BY_ID, id);
2389 ASSERT_TRUE(entry.good());
2390 EXPECT_TRUE(entry.Get(MTIME) == test_time);
2394 TEST_F(SyncerTest, ParentAndChildBothMatch) {
2395 const FullModelTypeSet all_types = FullModelTypeSet::All();
2396 syncable::Id parent_id = ids_.NewServerId();
2397 syncable::Id child_id = ids_.NewServerId();
2398 syncable::Id parent_local_id;
2399 syncable::Id child_local_id;
2403 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2404 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, root_id_, "Folder");
2405 ASSERT_TRUE(parent.good());
2406 parent_local_id = parent.Get(ID);
2407 parent.Put(IS_DIR, true);
2408 parent.Put(IS_UNSYNCED, true);
2409 parent.Put(ID, parent_id);
2410 parent.Put(BASE_VERSION, 1);
2411 parent.Put(SPECIFICS, DefaultBookmarkSpecifics());
2413 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent.Get(ID), "test.htm");
2414 ASSERT_TRUE(child.good());
2415 child_local_id = child.Get(ID);
2416 child.Put(ID, child_id);
2417 child.Put(BASE_VERSION, 1);
2418 child.Put(SPECIFICS, DefaultBookmarkSpecifics());
2419 WriteTestDataToEntry(&wtrans, &child);
2421 mock_server_->AddUpdateDirectory(parent_id, root_id_, "Folder", 10, 10,
2422 local_cache_guid(),
2423 parent_local_id.GetServerId());
2424 mock_server_->AddUpdateBookmark(child_id, parent_id, "test.htm", 10, 10,
2425 local_cache_guid(),
2426 child_local_id.GetServerId());
2427 mock_server_->set_conflict_all_commits(true);
2428 SyncShareNudge();
2429 SyncShareNudge();
2430 SyncShareNudge();
2432 syncable::ReadTransaction trans(FROM_HERE, directory());
2433 Directory::ChildHandles children;
2434 directory()->GetChildHandlesById(&trans, root_id_, &children);
2435 EXPECT_EQ(1u, children.size());
2436 directory()->GetChildHandlesById(&trans, parent_id, &children);
2437 EXPECT_EQ(1u, children.size());
2438 std::vector<int64> unapplied;
2439 directory()->GetUnappliedUpdateMetaHandles(&trans, all_types, &unapplied);
2440 EXPECT_EQ(0u, unapplied.size());
2441 syncable::Directory::UnsyncedMetaHandles unsynced;
2442 directory()->GetUnsyncedMetaHandles(&trans, &unsynced);
2443 EXPECT_EQ(0u, unsynced.size());
2444 saw_syncer_event_ = false;
2448 TEST_F(SyncerTest, CommittingNewDeleted) {
2450 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2451 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "bob");
2452 entry.Put(IS_UNSYNCED, true);
2453 entry.Put(IS_DEL, true);
2455 SyncShareNudge();
2456 EXPECT_EQ(0u, mock_server_->committed_ids().size());
2459 // Original problem synopsis:
2460 // Check failed: entry->Get(BASE_VERSION) <= entry->Get(SERVER_VERSION)
2461 // Client creates entry, client finishes committing entry. Between
2462 // commit and getting update back, we delete the entry.
2463 // We get the update for the entry, but the local one was modified
2464 // so we store the entry but don't apply it. IS_UNAPPLIED_UPDATE is set.
2465 // We commit deletion and get a new version number.
2466 // We apply unapplied updates again before we get the update about the deletion.
2467 // This means we have an unapplied update where server_version < base_version.
2468 TEST_F(SyncerTest, UnappliedUpdateDuringCommit) {
2469 // This test is a little fake.
2471 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2472 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "bob");
2473 entry.Put(ID, ids_.FromNumber(20));
2474 entry.Put(BASE_VERSION, 1);
2475 entry.Put(SERVER_VERSION, 1);
2476 entry.Put(SERVER_PARENT_ID, ids_.FromNumber(9999)); // Bad parent.
2477 entry.Put(IS_UNSYNCED, true);
2478 entry.Put(IS_UNAPPLIED_UPDATE, true);
2479 entry.Put(SPECIFICS, DefaultBookmarkSpecifics());
2480 entry.Put(SERVER_SPECIFICS, DefaultBookmarkSpecifics());
2481 entry.Put(IS_DEL, false);
2483 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END);
2484 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END);
2485 EXPECT_EQ(1, session_->status_controller().TotalNumConflictingItems());
2486 saw_syncer_event_ = false;
2489 // Original problem synopsis:
2490 // Illegal parent
2491 // Unexpected error during sync if we:
2492 // make a new folder bob
2493 // wait for sync
2494 // make a new folder fred
2495 // move bob into fred
2496 // remove bob
2497 // remove fred
2498 // if no syncing occured midway, bob will have an illegal parent
2499 TEST_F(SyncerTest, DeletingEntryInFolder) {
2500 // This test is a little fake.
2501 int64 existing_metahandle;
2503 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2504 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "existing");
2505 ASSERT_TRUE(entry.good());
2506 entry.Put(IS_DIR, true);
2507 entry.Put(SPECIFICS, DefaultBookmarkSpecifics());
2508 entry.Put(IS_UNSYNCED, true);
2509 existing_metahandle = entry.Get(META_HANDLE);
2511 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END);
2513 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2514 MutableEntry newfolder(&trans, CREATE, BOOKMARKS, trans.root_id(), "new");
2515 ASSERT_TRUE(newfolder.good());
2516 newfolder.Put(IS_DIR, true);
2517 newfolder.Put(SPECIFICS, DefaultBookmarkSpecifics());
2518 newfolder.Put(IS_UNSYNCED, true);
2520 MutableEntry existing(&trans, GET_BY_HANDLE, existing_metahandle);
2521 ASSERT_TRUE(existing.good());
2522 existing.Put(PARENT_ID, newfolder.Get(ID));
2523 existing.Put(IS_UNSYNCED, true);
2524 EXPECT_TRUE(existing.Get(ID).ServerKnows());
2526 newfolder.Put(IS_DEL, true);
2527 existing.Put(IS_DEL, true);
2529 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END);
2530 EXPECT_EQ(0, status().num_server_conflicts());
2533 TEST_F(SyncerTest, DeletingEntryWithLocalEdits) {
2534 int64 newfolder_metahandle;
2536 mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10,
2537 foreign_cache_guid(), "-1");
2538 SyncShareNudge();
2540 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2541 MutableEntry newfolder(
2542 &trans, CREATE, BOOKMARKS, ids_.FromNumber(1), "local");
2543 ASSERT_TRUE(newfolder.good());
2544 newfolder.Put(IS_UNSYNCED, true);
2545 newfolder.Put(IS_DIR, true);
2546 newfolder.Put(SPECIFICS, DefaultBookmarkSpecifics());
2547 newfolder_metahandle = newfolder.Get(META_HANDLE);
2549 mock_server_->AddUpdateDirectory(1, 0, "bob", 2, 20,
2550 foreign_cache_guid(), "-1");
2551 mock_server_->SetLastUpdateDeleted();
2552 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, APPLY_UPDATES);
2554 syncable::ReadTransaction trans(FROM_HERE, directory());
2555 Entry entry(&trans, syncable::GET_BY_HANDLE, newfolder_metahandle);
2556 ASSERT_TRUE(entry.good());
2560 TEST_F(SyncerTest, FolderSwapUpdate) {
2561 mock_server_->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2562 foreign_cache_guid(), "-7801");
2563 mock_server_->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2564 foreign_cache_guid(), "-1024");
2565 SyncShareNudge();
2566 mock_server_->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2567 foreign_cache_guid(), "-1024");
2568 mock_server_->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2569 foreign_cache_guid(), "-7801");
2570 SyncShareNudge();
2572 syncable::ReadTransaction trans(FROM_HERE, directory());
2573 Entry id1(&trans, GET_BY_ID, ids_.FromNumber(7801));
2574 ASSERT_TRUE(id1.good());
2575 EXPECT_TRUE("fred" == id1.Get(NON_UNIQUE_NAME));
2576 EXPECT_TRUE(root_id_ == id1.Get(PARENT_ID));
2577 Entry id2(&trans, GET_BY_ID, ids_.FromNumber(1024));
2578 ASSERT_TRUE(id2.good());
2579 EXPECT_TRUE("bob" == id2.Get(NON_UNIQUE_NAME));
2580 EXPECT_TRUE(root_id_ == id2.Get(PARENT_ID));
2582 saw_syncer_event_ = false;
2585 TEST_F(SyncerTest, NameCollidingFolderSwapWorksFine) {
2586 mock_server_->AddUpdateDirectory(7801, 0, "bob", 1, 10,
2587 foreign_cache_guid(), "-7801");
2588 mock_server_->AddUpdateDirectory(1024, 0, "fred", 1, 10,
2589 foreign_cache_guid(), "-1024");
2590 mock_server_->AddUpdateDirectory(4096, 0, "alice", 1, 10,
2591 foreign_cache_guid(), "-4096");
2592 SyncShareNudge();
2594 syncable::ReadTransaction trans(FROM_HERE, directory());
2595 Entry id1(&trans, GET_BY_ID, ids_.FromNumber(7801));
2596 ASSERT_TRUE(id1.good());
2597 EXPECT_TRUE("bob" == id1.Get(NON_UNIQUE_NAME));
2598 EXPECT_TRUE(root_id_ == id1.Get(PARENT_ID));
2599 Entry id2(&trans, GET_BY_ID, ids_.FromNumber(1024));
2600 ASSERT_TRUE(id2.good());
2601 EXPECT_TRUE("fred" == id2.Get(NON_UNIQUE_NAME));
2602 EXPECT_TRUE(root_id_ == id2.Get(PARENT_ID));
2603 Entry id3(&trans, GET_BY_ID, ids_.FromNumber(4096));
2604 ASSERT_TRUE(id3.good());
2605 EXPECT_TRUE("alice" == id3.Get(NON_UNIQUE_NAME));
2606 EXPECT_TRUE(root_id_ == id3.Get(PARENT_ID));
2608 mock_server_->AddUpdateDirectory(1024, 0, "bob", 2, 20,
2609 foreign_cache_guid(), "-1024");
2610 mock_server_->AddUpdateDirectory(7801, 0, "fred", 2, 20,
2611 foreign_cache_guid(), "-7801");
2612 mock_server_->AddUpdateDirectory(4096, 0, "bob", 2, 20,
2613 foreign_cache_guid(), "-4096");
2614 SyncShareNudge();
2616 syncable::ReadTransaction trans(FROM_HERE, directory());
2617 Entry id1(&trans, GET_BY_ID, ids_.FromNumber(7801));
2618 ASSERT_TRUE(id1.good());
2619 EXPECT_TRUE("fred" == id1.Get(NON_UNIQUE_NAME));
2620 EXPECT_TRUE(root_id_ == id1.Get(PARENT_ID));
2621 Entry id2(&trans, GET_BY_ID, ids_.FromNumber(1024));
2622 ASSERT_TRUE(id2.good());
2623 EXPECT_TRUE("bob" == id2.Get(NON_UNIQUE_NAME));
2624 EXPECT_TRUE(root_id_ == id2.Get(PARENT_ID));
2625 Entry id3(&trans, GET_BY_ID, ids_.FromNumber(4096));
2626 ASSERT_TRUE(id3.good());
2627 EXPECT_TRUE("bob" == id3.Get(NON_UNIQUE_NAME));
2628 EXPECT_TRUE(root_id_ == id3.Get(PARENT_ID));
2630 saw_syncer_event_ = false;
2633 // Committing more than kDefaultMaxCommitBatchSize items requires that
2634 // we post more than one commit command to the server. This test makes
2635 // sure that scenario works as expected.
2636 TEST_F(SyncerTest, CommitManyItemsInOneGo_Success) {
2637 uint32 num_batches = 3;
2638 uint32 items_to_commit = kDefaultMaxCommitBatchSize * num_batches;
2640 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2641 for (uint32 i = 0; i < items_to_commit; i++) {
2642 string nameutf8 = base::StringPrintf("%d", i);
2643 string name(nameutf8.begin(), nameutf8.end());
2644 MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
2645 e.Put(IS_UNSYNCED, true);
2646 e.Put(IS_DIR, true);
2647 e.Put(SPECIFICS, DefaultBookmarkSpecifics());
2650 ASSERT_EQ(items_to_commit, directory()->unsynced_entity_count());
2652 SyncShareNudge();
2653 EXPECT_EQ(num_batches, mock_server_->commit_messages().size());
2654 EXPECT_EQ(0, directory()->unsynced_entity_count());
2657 // Test that a single failure to contact the server will cause us to exit the
2658 // commit loop immediately.
2659 TEST_F(SyncerTest, CommitManyItemsInOneGo_PostBufferFail) {
2660 uint32 num_batches = 3;
2661 uint32 items_to_commit = kDefaultMaxCommitBatchSize * num_batches;
2663 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2664 for (uint32 i = 0; i < items_to_commit; i++) {
2665 string nameutf8 = base::StringPrintf("%d", i);
2666 string name(nameutf8.begin(), nameutf8.end());
2667 MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
2668 e.Put(IS_UNSYNCED, true);
2669 e.Put(IS_DIR, true);
2670 e.Put(SPECIFICS, DefaultBookmarkSpecifics());
2673 ASSERT_EQ(items_to_commit, directory()->unsynced_entity_count());
2675 // The second commit should fail. It will be preceded by one successful
2676 // GetUpdate and one succesful commit.
2677 mock_server_->FailNthPostBufferToPathCall(3);
2678 SyncShareNudge();
2680 EXPECT_EQ(1U, mock_server_->commit_messages().size());
2681 EXPECT_EQ(SYNC_SERVER_ERROR,
2682 session_->status_controller().model_neutral_state().commit_result);
2683 EXPECT_EQ(items_to_commit - kDefaultMaxCommitBatchSize,
2684 directory()->unsynced_entity_count());
2687 // Test that a single conflict response from the server will cause us to exit
2688 // the commit loop immediately.
2689 TEST_F(SyncerTest, CommitManyItemsInOneGo_CommitConflict) {
2690 uint32 num_batches = 2;
2691 uint32 items_to_commit = kDefaultMaxCommitBatchSize * num_batches;
2693 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2694 for (uint32 i = 0; i < items_to_commit; i++) {
2695 string nameutf8 = base::StringPrintf("%d", i);
2696 string name(nameutf8.begin(), nameutf8.end());
2697 MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
2698 e.Put(IS_UNSYNCED, true);
2699 e.Put(IS_DIR, true);
2700 e.Put(SPECIFICS, DefaultBookmarkSpecifics());
2703 ASSERT_EQ(items_to_commit, directory()->unsynced_entity_count());
2705 // Return a CONFLICT response for the first item.
2706 mock_server_->set_conflict_n_commits(1);
2707 SyncShareNudge();
2709 // We should stop looping at the first sign of trouble.
2710 EXPECT_EQ(1U, mock_server_->commit_messages().size());
2711 EXPECT_EQ(items_to_commit - (kDefaultMaxCommitBatchSize - 1),
2712 directory()->unsynced_entity_count());
2715 TEST_F(SyncerTest, HugeConflict) {
2716 int item_count = 300; // We should be able to do 300 or 3000 w/o issue.
2718 syncable::Id parent_id = ids_.NewServerId();
2719 syncable::Id last_id = parent_id;
2720 vector<syncable::Id> tree_ids;
2722 // Create a lot of updates for which the parent does not exist yet.
2723 // Generate a huge deep tree which should all fail to apply at first.
2725 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2726 for (int i = 0; i < item_count ; i++) {
2727 syncable::Id next_id = ids_.NewServerId();
2728 syncable::Id local_id = ids_.NewLocalId();
2729 tree_ids.push_back(next_id);
2730 mock_server_->AddUpdateDirectory(next_id, last_id, "BOB", 2, 20,
2731 foreign_cache_guid(),
2732 local_id.GetServerId());
2733 last_id = next_id;
2736 SyncShareNudge();
2738 // Check they're in the expected conflict state.
2740 syncable::ReadTransaction trans(FROM_HERE, directory());
2741 for (int i = 0; i < item_count; i++) {
2742 Entry e(&trans, GET_BY_ID, tree_ids[i]);
2743 // They should all exist but none should be applied.
2744 ASSERT_TRUE(e.good());
2745 EXPECT_TRUE(e.Get(IS_DEL));
2746 EXPECT_TRUE(e.Get(IS_UNAPPLIED_UPDATE));
2750 // Add the missing parent directory.
2751 mock_server_->AddUpdateDirectory(parent_id, TestIdFactory::root(),
2752 "BOB", 2, 20, foreign_cache_guid(), "-3500");
2753 SyncShareNudge();
2755 // Now they should all be OK.
2757 syncable::ReadTransaction trans(FROM_HERE, directory());
2758 for (int i = 0; i < item_count; i++) {
2759 Entry e(&trans, GET_BY_ID, tree_ids[i]);
2760 ASSERT_TRUE(e.good());
2761 EXPECT_FALSE(e.Get(IS_DEL));
2762 EXPECT_FALSE(e.Get(IS_UNAPPLIED_UPDATE));
2767 TEST_F(SyncerTest, DontCrashOnCaseChange) {
2768 mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10,
2769 foreign_cache_guid(), "-1");
2770 SyncShareNudge();
2772 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2773 MutableEntry e(&trans, GET_BY_ID, ids_.FromNumber(1));
2774 ASSERT_TRUE(e.good());
2775 e.Put(IS_UNSYNCED, true);
2777 mock_server_->set_conflict_all_commits(true);
2778 mock_server_->AddUpdateDirectory(1, 0, "BOB", 2, 20,
2779 foreign_cache_guid(), "-1");
2780 SyncShareNudge(); // USED TO CAUSE AN ASSERT
2781 saw_syncer_event_ = false;
2784 TEST_F(SyncerTest, UnsyncedItemAndUpdate) {
2785 mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10,
2786 foreign_cache_guid(), "-1");
2787 SyncShareNudge();
2788 mock_server_->set_conflict_all_commits(true);
2789 mock_server_->AddUpdateDirectory(2, 0, "bob", 2, 20,
2790 foreign_cache_guid(), "-2");
2791 SyncShareNudge(); // USED TO CAUSE AN ASSERT
2792 saw_syncer_event_ = false;
2795 TEST_F(SyncerTest, NewEntryAndAlteredServerEntrySharePath) {
2796 mock_server_->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
2797 foreign_cache_guid(), "-1");
2798 SyncShareNudge();
2799 int64 local_folder_handle;
2800 syncable::Id local_folder_id;
2802 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2803 MutableEntry new_entry(
2804 &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Bar.htm");
2805 ASSERT_TRUE(new_entry.good());
2806 local_folder_id = new_entry.Get(ID);
2807 local_folder_handle = new_entry.Get(META_HANDLE);
2808 new_entry.Put(IS_UNSYNCED, true);
2809 new_entry.Put(SPECIFICS, DefaultBookmarkSpecifics());
2810 MutableEntry old(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2811 ASSERT_TRUE(old.good());
2812 WriteTestDataToEntry(&wtrans, &old);
2814 mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
2815 foreign_cache_guid(), "-1");
2816 mock_server_->set_conflict_all_commits(true);
2817 SyncShareNudge();
2818 saw_syncer_event_ = false;
2820 // Update #20 should have been dropped in favor of the local version.
2821 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2822 MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2823 MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle);
2824 ASSERT_TRUE(server.good());
2825 ASSERT_TRUE(local.good());
2826 EXPECT_TRUE(local.Get(META_HANDLE) != server.Get(META_HANDLE));
2827 EXPECT_FALSE(server.Get(IS_UNAPPLIED_UPDATE));
2828 EXPECT_FALSE(local.Get(IS_UNAPPLIED_UPDATE));
2829 EXPECT_TRUE(server.Get(IS_UNSYNCED));
2830 EXPECT_TRUE(local.Get(IS_UNSYNCED));
2831 EXPECT_EQ("Foo.htm", server.Get(NON_UNIQUE_NAME));
2832 EXPECT_EQ("Bar.htm", local.Get(NON_UNIQUE_NAME));
2834 // Allow local changes to commit.
2835 mock_server_->set_conflict_all_commits(false);
2836 SyncShareNudge();
2837 saw_syncer_event_ = false;
2839 // Now add a server change to make the two names equal. There should
2840 // be no conflict with that, since names are not unique.
2841 mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
2842 foreign_cache_guid(), "-1");
2843 SyncShareNudge();
2844 saw_syncer_event_ = false;
2846 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2847 MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2848 MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle);
2849 ASSERT_TRUE(server.good());
2850 ASSERT_TRUE(local.good());
2851 EXPECT_TRUE(local.Get(META_HANDLE) != server.Get(META_HANDLE));
2852 EXPECT_FALSE(server.Get(IS_UNAPPLIED_UPDATE));
2853 EXPECT_FALSE(local.Get(IS_UNAPPLIED_UPDATE));
2854 EXPECT_FALSE(server.Get(IS_UNSYNCED));
2855 EXPECT_FALSE(local.Get(IS_UNSYNCED));
2856 EXPECT_EQ("Bar.htm", server.Get(NON_UNIQUE_NAME));
2857 EXPECT_EQ("Bar.htm", local.Get(NON_UNIQUE_NAME));
2858 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
2859 server.Get(SPECIFICS).bookmark().url());
2863 // Same as NewEntryAnddServerEntrySharePath, but using the old-style protocol.
2864 TEST_F(SyncerTest, NewEntryAndAlteredServerEntrySharePath_OldBookmarksProto) {
2865 mock_server_->set_use_legacy_bookmarks_protocol(true);
2866 mock_server_->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
2867 foreign_cache_guid(), "-1");
2868 SyncShareNudge();
2869 int64 local_folder_handle;
2870 syncable::Id local_folder_id;
2872 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2873 MutableEntry new_entry(
2874 &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Bar.htm");
2875 ASSERT_TRUE(new_entry.good());
2876 local_folder_id = new_entry.Get(ID);
2877 local_folder_handle = new_entry.Get(META_HANDLE);
2878 new_entry.Put(IS_UNSYNCED, true);
2879 new_entry.Put(SPECIFICS, DefaultBookmarkSpecifics());
2880 MutableEntry old(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2881 ASSERT_TRUE(old.good());
2882 WriteTestDataToEntry(&wtrans, &old);
2884 mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
2885 foreign_cache_guid(), "-1");
2886 mock_server_->set_conflict_all_commits(true);
2887 SyncShareNudge();
2888 saw_syncer_event_ = false;
2890 // Update #20 should have been dropped in favor of the local version.
2891 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2892 MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2893 MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle);
2894 ASSERT_TRUE(server.good());
2895 ASSERT_TRUE(local.good());
2896 EXPECT_TRUE(local.Get(META_HANDLE) != server.Get(META_HANDLE));
2897 EXPECT_FALSE(server.Get(IS_UNAPPLIED_UPDATE));
2898 EXPECT_FALSE(local.Get(IS_UNAPPLIED_UPDATE));
2899 EXPECT_TRUE(server.Get(IS_UNSYNCED));
2900 EXPECT_TRUE(local.Get(IS_UNSYNCED));
2901 EXPECT_EQ("Foo.htm", server.Get(NON_UNIQUE_NAME));
2902 EXPECT_EQ("Bar.htm", local.Get(NON_UNIQUE_NAME));
2904 // Allow local changes to commit.
2905 mock_server_->set_conflict_all_commits(false);
2906 SyncShareNudge();
2907 saw_syncer_event_ = false;
2909 // Now add a server change to make the two names equal. There should
2910 // be no conflict with that, since names are not unique.
2911 mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
2912 foreign_cache_guid(), "-1");
2913 SyncShareNudge();
2914 saw_syncer_event_ = false;
2916 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2917 MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2918 MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle);
2919 ASSERT_TRUE(server.good());
2920 ASSERT_TRUE(local.good());
2921 EXPECT_TRUE(local.Get(META_HANDLE) != server.Get(META_HANDLE));
2922 EXPECT_FALSE(server.Get(IS_UNAPPLIED_UPDATE));
2923 EXPECT_FALSE(local.Get(IS_UNAPPLIED_UPDATE));
2924 EXPECT_FALSE(server.Get(IS_UNSYNCED));
2925 EXPECT_FALSE(local.Get(IS_UNSYNCED));
2926 EXPECT_EQ("Bar.htm", server.Get(NON_UNIQUE_NAME));
2927 EXPECT_EQ("Bar.htm", local.Get(NON_UNIQUE_NAME));
2928 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
2929 server.Get(SPECIFICS).bookmark().url());
2933 // Circular links should be resolved by the server.
2934 TEST_F(SyncerTest, SiblingDirectoriesBecomeCircular) {
2935 // we don't currently resolve this. This test ensures we don't.
2936 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
2937 foreign_cache_guid(), "-1");
2938 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10,
2939 foreign_cache_guid(), "-2");
2940 SyncShareNudge();
2942 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2943 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2944 ASSERT_TRUE(A.good());
2945 A.Put(IS_UNSYNCED, true);
2946 ASSERT_TRUE(A.Put(PARENT_ID, ids_.FromNumber(2)));
2947 ASSERT_TRUE(A.Put(NON_UNIQUE_NAME, "B"));
2949 mock_server_->AddUpdateDirectory(2, 1, "A", 20, 20,
2950 foreign_cache_guid(), "-2");
2951 mock_server_->set_conflict_all_commits(true);
2952 SyncShareNudge();
2953 saw_syncer_event_ = false;
2955 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2956 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2957 ASSERT_TRUE(A.good());
2958 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
2959 ASSERT_TRUE(B.good());
2960 EXPECT_TRUE(A.Get(NON_UNIQUE_NAME) == "B");
2961 EXPECT_TRUE(B.Get(NON_UNIQUE_NAME) == "B");
2965 TEST_F(SyncerTest, SwapEntryNames) {
2966 // Simple transaction test.
2967 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
2968 foreign_cache_guid(), "-1");
2969 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10,
2970 foreign_cache_guid(), "-2");
2971 mock_server_->set_conflict_all_commits(true);
2972 SyncShareNudge();
2974 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2975 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2976 ASSERT_TRUE(A.good());
2977 A.Put(IS_UNSYNCED, true);
2978 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
2979 ASSERT_TRUE(B.good());
2980 B.Put(IS_UNSYNCED, true);
2981 ASSERT_TRUE(A.Put(NON_UNIQUE_NAME, "C"));
2982 ASSERT_TRUE(B.Put(NON_UNIQUE_NAME, "A"));
2983 ASSERT_TRUE(A.Put(NON_UNIQUE_NAME, "B"));
2985 SyncShareNudge();
2986 saw_syncer_event_ = false;
2989 TEST_F(SyncerTest, DualDeletionWithNewItemNameClash) {
2990 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
2991 foreign_cache_guid(), "-1");
2992 mock_server_->AddUpdateBookmark(2, 0, "B", 10, 10,
2993 foreign_cache_guid(), "-2");
2994 mock_server_->set_conflict_all_commits(true);
2995 SyncShareNudge();
2997 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2998 MutableEntry B(&trans, GET_BY_ID, ids_.FromNumber(2));
2999 ASSERT_TRUE(B.good());
3000 WriteTestDataToEntry(&trans, &B);
3001 B.Put(IS_DEL, true);
3003 mock_server_->AddUpdateBookmark(2, 0, "A", 11, 11,
3004 foreign_cache_guid(), "-2");
3005 mock_server_->SetLastUpdateDeleted();
3006 SyncShareNudge();
3008 syncable::ReadTransaction trans(FROM_HERE, directory());
3009 Entry B(&trans, GET_BY_ID, ids_.FromNumber(2));
3010 ASSERT_TRUE(B.good());
3011 EXPECT_FALSE(B.Get(IS_UNSYNCED));
3012 EXPECT_FALSE(B.Get(IS_UNAPPLIED_UPDATE));
3014 saw_syncer_event_ = false;
3017 // When we undelete an entity as a result of conflict resolution, we reuse the
3018 // existing server id and preserve the old version, simply updating the server
3019 // version with the new non-deleted entity.
3020 TEST_F(SyncerTest, ResolveWeWroteTheyDeleted) {
3021 int64 bob_metahandle;
3023 mock_server_->AddUpdateBookmark(1, 0, "bob", 1, 10,
3024 foreign_cache_guid(), "-1");
3025 SyncShareNudge();
3027 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3028 MutableEntry bob(&trans, GET_BY_ID, ids_.FromNumber(1));
3029 ASSERT_TRUE(bob.good());
3030 bob_metahandle = bob.Get(META_HANDLE);
3031 WriteTestDataToEntry(&trans, &bob);
3033 mock_server_->AddUpdateBookmark(1, 0, "bob", 2, 10,
3034 foreign_cache_guid(), "-1");
3035 mock_server_->SetLastUpdateDeleted();
3036 mock_server_->set_conflict_all_commits(true);
3037 SyncShareNudge();
3038 SyncShareNudge();
3040 syncable::ReadTransaction trans(FROM_HERE, directory());
3041 Entry bob(&trans, GET_BY_HANDLE, bob_metahandle);
3042 ASSERT_TRUE(bob.good());
3043 EXPECT_TRUE(bob.Get(IS_UNSYNCED));
3044 EXPECT_TRUE(bob.Get(ID).ServerKnows());
3045 EXPECT_FALSE(bob.Get(IS_UNAPPLIED_UPDATE));
3046 EXPECT_FALSE(bob.Get(IS_DEL));
3047 EXPECT_EQ(2, bob.Get(SERVER_VERSION));
3048 EXPECT_EQ(2, bob.Get(BASE_VERSION));
3050 saw_syncer_event_ = false;
3053 // This test is to reproduce a check failure. Sometimes we would get a bad ID
3054 // back when creating an entry.
3055 TEST_F(SyncerTest, DuplicateIDReturn) {
3057 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3058 MutableEntry folder(&trans, CREATE, BOOKMARKS, trans.root_id(), "bob");
3059 ASSERT_TRUE(folder.good());
3060 folder.Put(IS_UNSYNCED, true);
3061 folder.Put(IS_DIR, true);
3062 folder.Put(SPECIFICS, DefaultBookmarkSpecifics());
3063 MutableEntry folder2(&trans, CREATE, BOOKMARKS, trans.root_id(), "fred");
3064 ASSERT_TRUE(folder2.good());
3065 folder2.Put(IS_UNSYNCED, false);
3066 folder2.Put(IS_DIR, true);
3067 folder2.Put(SPECIFICS, DefaultBookmarkSpecifics());
3068 folder2.Put(BASE_VERSION, 3);
3069 folder2.Put(ID, syncable::Id::CreateFromServerId("mock_server:10000"));
3071 mock_server_->set_next_new_id(10000);
3072 EXPECT_EQ(1u, directory()->unsynced_entity_count());
3073 // we get back a bad id in here (should never happen).
3074 SyncShareNudge();
3075 EXPECT_EQ(1u, directory()->unsynced_entity_count());
3076 SyncShareNudge(); // another bad id in here.
3077 EXPECT_EQ(0u, directory()->unsynced_entity_count());
3078 saw_syncer_event_ = false;
3081 TEST_F(SyncerTest, DeletedEntryWithBadParentInLoopCalculation) {
3082 mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10,
3083 foreign_cache_guid(), "-1");
3084 SyncShareNudge();
3086 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3087 MutableEntry bob(&trans, GET_BY_ID, ids_.FromNumber(1));
3088 ASSERT_TRUE(bob.good());
3089 // This is valid, because the parent could have gone away a long time ago.
3090 bob.Put(PARENT_ID, ids_.FromNumber(54));
3091 bob.Put(IS_DEL, true);
3092 bob.Put(IS_UNSYNCED, true);
3094 mock_server_->AddUpdateDirectory(2, 1, "fred", 1, 10,
3095 foreign_cache_guid(), "-2");
3096 SyncShareNudge();
3097 SyncShareNudge();
3100 TEST_F(SyncerTest, ConflictResolverMergesLocalDeleteAndServerUpdate) {
3101 syncable::Id local_id;
3103 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3105 MutableEntry local_deleted(
3106 &trans, CREATE, BOOKMARKS, trans.root_id(), "name");
3107 local_id = local_deleted.Get(ID);
3108 local_deleted.Put(ID, ids_.FromNumber(1));
3109 local_deleted.Put(BASE_VERSION, 1);
3110 local_deleted.Put(IS_DEL, true);
3111 local_deleted.Put(IS_DIR, false);
3112 local_deleted.Put(IS_UNSYNCED, true);
3113 local_deleted.Put(SPECIFICS, DefaultBookmarkSpecifics());
3116 mock_server_->AddUpdateBookmark(ids_.FromNumber(1), root_id_, "name", 10, 10,
3117 local_cache_guid(),
3118 local_id.GetServerId());
3120 // We don't care about actually committing, just the resolution.
3121 mock_server_->set_conflict_all_commits(true);
3122 SyncShareNudge();
3125 syncable::ReadTransaction trans(FROM_HERE, directory());
3126 Entry local_deleted(&trans, GET_BY_ID, ids_.FromNumber(1));
3127 EXPECT_TRUE(local_deleted.Get(BASE_VERSION) == 10);
3128 EXPECT_TRUE(local_deleted.Get(IS_UNAPPLIED_UPDATE) == false);
3129 EXPECT_TRUE(local_deleted.Get(IS_UNSYNCED) == true);
3130 EXPECT_TRUE(local_deleted.Get(IS_DEL) == true);
3131 EXPECT_TRUE(local_deleted.Get(IS_DIR) == false);
3135 // See what happens if the IS_DIR bit gets flipped. This can cause us
3136 // all kinds of disasters.
3137 TEST_F(SyncerTest, UpdateFlipsTheFolderBit) {
3138 // Local object: a deleted directory (container), revision 1, unsynced.
3140 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3142 MutableEntry local_deleted(
3143 &trans, CREATE, BOOKMARKS, trans.root_id(), "name");
3144 local_deleted.Put(ID, ids_.FromNumber(1));
3145 local_deleted.Put(BASE_VERSION, 1);
3146 local_deleted.Put(IS_DEL, true);
3147 local_deleted.Put(IS_DIR, true);
3148 local_deleted.Put(IS_UNSYNCED, true);
3149 local_deleted.Put(SPECIFICS, DefaultBookmarkSpecifics());
3152 // Server update: entry-type object (not a container), revision 10.
3153 mock_server_->AddUpdateBookmark(ids_.FromNumber(1), root_id_, "name", 10, 10,
3154 local_cache_guid(),
3155 ids_.FromNumber(1).GetServerId());
3157 // Don't attempt to commit.
3158 mock_server_->set_conflict_all_commits(true);
3160 // The syncer should not attempt to apply the invalid update.
3161 SyncShareNudge();
3164 syncable::ReadTransaction trans(FROM_HERE, directory());
3165 Entry local_deleted(&trans, GET_BY_ID, ids_.FromNumber(1));
3166 EXPECT_TRUE(local_deleted.Get(BASE_VERSION) == 1);
3167 EXPECT_TRUE(local_deleted.Get(IS_UNAPPLIED_UPDATE) == false);
3168 EXPECT_TRUE(local_deleted.Get(IS_UNSYNCED) == true);
3169 EXPECT_TRUE(local_deleted.Get(IS_DEL) == true);
3170 EXPECT_TRUE(local_deleted.Get(IS_DIR) == true);
3174 // Bug Synopsis:
3175 // Merge conflict resolution will merge a new local entry with another entry
3176 // that needs updates, resulting in CHECK.
3177 TEST_F(SyncerTest, MergingExistingItems) {
3178 mock_server_->set_conflict_all_commits(true);
3179 mock_server_->AddUpdateBookmark(1, 0, "base", 10, 10,
3180 local_cache_guid(), "-1");
3181 SyncShareNudge();
3183 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3184 MutableEntry entry(
3185 &trans, CREATE, BOOKMARKS, trans.root_id(), "Copy of base");
3186 WriteTestDataToEntry(&trans, &entry);
3188 mock_server_->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
3189 local_cache_guid(), "-1");
3190 SyncRepeatedlyToTriggerConflictResolution(session_.get());
3193 // In this test a long changelog contains a child at the start of the changelog
3194 // and a parent at the end. While these updates are in progress the client would
3195 // appear stuck.
3196 TEST_F(SyncerTest, LongChangelistWithApplicationConflict) {
3197 const int depth = 400;
3198 syncable::Id folder_id = ids_.FromNumber(1);
3200 // First we an item in a folder in the root. However the folder won't come
3201 // till much later.
3202 syncable::Id stuck_entry_id = TestIdFactory::FromNumber(99999);
3203 mock_server_->AddUpdateDirectory(stuck_entry_id,
3204 folder_id, "stuck", 1, 1,
3205 foreign_cache_guid(), "-99999");
3206 mock_server_->SetChangesRemaining(depth - 1);
3207 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END);
3209 // Buffer up a very long series of downloads.
3210 // We should never be stuck (conflict resolution shouldn't
3211 // kick in so long as we're making forward progress).
3212 for (int i = 0; i < depth; i++) {
3213 mock_server_->NextUpdateBatch();
3214 mock_server_->SetNewTimestamp(i + 1);
3215 mock_server_->SetChangesRemaining(depth - i);
3218 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END);
3220 // Ensure our folder hasn't somehow applied.
3222 syncable::ReadTransaction trans(FROM_HERE, directory());
3223 Entry child(&trans, GET_BY_ID, stuck_entry_id);
3224 EXPECT_TRUE(child.good());
3225 EXPECT_TRUE(child.Get(IS_UNAPPLIED_UPDATE));
3226 EXPECT_TRUE(child.Get(IS_DEL));
3227 EXPECT_FALSE(child.Get(IS_UNSYNCED));
3230 // And finally the folder.
3231 mock_server_->AddUpdateDirectory(folder_id,
3232 TestIdFactory::root(), "folder", 1, 1,
3233 foreign_cache_guid(), "-1");
3234 mock_server_->SetChangesRemaining(0);
3235 SyncShareNudge();
3236 SyncShareNudge();
3237 // Check that everything is as expected after the commit.
3239 syncable::ReadTransaction trans(FROM_HERE, directory());
3240 Entry entry(&trans, GET_BY_ID, folder_id);
3241 ASSERT_TRUE(entry.good());
3242 Entry child(&trans, GET_BY_ID, stuck_entry_id);
3243 EXPECT_EQ(entry.Get(ID), child.Get(PARENT_ID));
3244 EXPECT_EQ("stuck", child.Get(NON_UNIQUE_NAME));
3245 EXPECT_TRUE(child.good());
3249 TEST_F(SyncerTest, DontMergeTwoExistingItems) {
3250 mock_server_->set_conflict_all_commits(true);
3251 mock_server_->AddUpdateBookmark(1, 0, "base", 10, 10,
3252 foreign_cache_guid(), "-1");
3253 mock_server_->AddUpdateBookmark(2, 0, "base2", 10, 10,
3254 foreign_cache_guid(), "-2");
3255 SyncShareNudge();
3257 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3258 MutableEntry entry(&trans, GET_BY_ID, ids_.FromNumber(2));
3259 ASSERT_TRUE(entry.good());
3260 EXPECT_TRUE(entry.Put(NON_UNIQUE_NAME, "Copy of base"));
3261 entry.Put(IS_UNSYNCED, true);
3263 mock_server_->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
3264 foreign_cache_guid(), "-1");
3265 SyncRepeatedlyToTriggerConflictResolution(session_.get());
3267 syncable::ReadTransaction trans(FROM_HERE, directory());
3268 Entry entry1(&trans, GET_BY_ID, ids_.FromNumber(1));
3269 EXPECT_FALSE(entry1.Get(IS_UNAPPLIED_UPDATE));
3270 EXPECT_FALSE(entry1.Get(IS_UNSYNCED));
3271 EXPECT_FALSE(entry1.Get(IS_DEL));
3272 Entry entry2(&trans, GET_BY_ID, ids_.FromNumber(2));
3273 EXPECT_FALSE(entry2.Get(IS_UNAPPLIED_UPDATE));
3274 EXPECT_TRUE(entry2.Get(IS_UNSYNCED));
3275 EXPECT_FALSE(entry2.Get(IS_DEL));
3276 EXPECT_EQ(entry1.Get(NON_UNIQUE_NAME), entry2.Get(NON_UNIQUE_NAME));
3280 TEST_F(SyncerTest, TestUndeleteUpdate) {
3281 mock_server_->set_conflict_all_commits(true);
3282 mock_server_->AddUpdateDirectory(1, 0, "foo", 1, 1,
3283 foreign_cache_guid(), "-1");
3284 mock_server_->AddUpdateDirectory(2, 1, "bar", 1, 2,
3285 foreign_cache_guid(), "-2");
3286 SyncShareNudge();
3287 mock_server_->AddUpdateDirectory(2, 1, "bar", 2, 3,
3288 foreign_cache_guid(), "-2");
3289 mock_server_->SetLastUpdateDeleted();
3290 SyncShareNudge();
3292 int64 metahandle;
3294 syncable::ReadTransaction trans(FROM_HERE, directory());
3295 Entry entry(&trans, GET_BY_ID, ids_.FromNumber(2));
3296 ASSERT_TRUE(entry.good());
3297 EXPECT_TRUE(entry.Get(IS_DEL));
3298 metahandle = entry.Get(META_HANDLE);
3300 mock_server_->AddUpdateDirectory(1, 0, "foo", 2, 4,
3301 foreign_cache_guid(), "-1");
3302 mock_server_->SetLastUpdateDeleted();
3303 SyncShareNudge();
3304 // This used to be rejected as it's an undeletion. Now, it results in moving
3305 // the delete path aside.
3306 mock_server_->AddUpdateDirectory(2, 1, "bar", 3, 5,
3307 foreign_cache_guid(), "-2");
3308 SyncShareNudge();
3310 syncable::ReadTransaction trans(FROM_HERE, directory());
3311 Entry entry(&trans, GET_BY_ID, ids_.FromNumber(2));
3312 ASSERT_TRUE(entry.good());
3313 EXPECT_TRUE(entry.Get(IS_DEL));
3314 EXPECT_FALSE(entry.Get(SERVER_IS_DEL));
3315 EXPECT_TRUE(entry.Get(IS_UNAPPLIED_UPDATE));
3316 EXPECT_NE(entry.Get(META_HANDLE), metahandle);
3320 TEST_F(SyncerTest, TestMoveSanitizedNamedFolder) {
3321 mock_server_->AddUpdateDirectory(1, 0, "foo", 1, 1,
3322 foreign_cache_guid(), "-1");
3323 mock_server_->AddUpdateDirectory(2, 0, ":::", 1, 2,
3324 foreign_cache_guid(), "-2");
3325 SyncShareNudge();
3327 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3328 MutableEntry entry(&trans, GET_BY_ID, ids_.FromNumber(2));
3329 ASSERT_TRUE(entry.good());
3330 EXPECT_TRUE(entry.Put(PARENT_ID, ids_.FromNumber(1)));
3331 EXPECT_TRUE(entry.Put(IS_UNSYNCED, true));
3333 SyncShareNudge();
3334 // We use the same sync ts as before so our times match up.
3335 mock_server_->AddUpdateDirectory(2, 1, ":::", 2, 2,
3336 foreign_cache_guid(), "-2");
3337 SyncShareNudge();
3340 // Don't crash when this occurs.
3341 TEST_F(SyncerTest, UpdateWhereParentIsNotAFolder) {
3342 mock_server_->AddUpdateBookmark(1, 0, "B", 10, 10,
3343 foreign_cache_guid(), "-1");
3344 mock_server_->AddUpdateDirectory(2, 1, "BookmarkParent", 10, 10,
3345 foreign_cache_guid(), "-2");
3346 // Used to cause a CHECK
3347 SyncShareNudge();
3349 syncable::ReadTransaction rtrans(FROM_HERE, directory());
3350 Entry good_entry(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1));
3351 ASSERT_TRUE(good_entry.good());
3352 EXPECT_FALSE(good_entry.Get(IS_UNAPPLIED_UPDATE));
3353 Entry bad_parent(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(2));
3354 ASSERT_TRUE(bad_parent.good());
3355 EXPECT_TRUE(bad_parent.Get(IS_UNAPPLIED_UPDATE));
3359 const char kRootId[] = "0";
3361 TEST_F(SyncerTest, DirectoryUpdateTest) {
3362 Id in_root_id = ids_.NewServerId();
3363 Id in_in_root_id = ids_.NewServerId();
3365 mock_server_->AddUpdateDirectory(in_root_id, TestIdFactory::root(),
3366 "in_root_name", 2, 2,
3367 foreign_cache_guid(), "-1");
3368 mock_server_->AddUpdateDirectory(in_in_root_id, in_root_id,
3369 "in_in_root_name", 3, 3,
3370 foreign_cache_guid(), "-2");
3371 SyncShareNudge();
3373 syncable::ReadTransaction trans(FROM_HERE, directory());
3374 Entry in_root(&trans, GET_BY_ID, in_root_id);
3375 ASSERT_TRUE(in_root.good());
3376 EXPECT_EQ("in_root_name", in_root.Get(NON_UNIQUE_NAME));
3377 EXPECT_EQ(TestIdFactory::root(), in_root.Get(PARENT_ID));
3379 Entry in_in_root(&trans, GET_BY_ID, in_in_root_id);
3380 ASSERT_TRUE(in_in_root.good());
3381 EXPECT_EQ("in_in_root_name", in_in_root.Get(NON_UNIQUE_NAME));
3382 EXPECT_EQ(in_root_id, in_in_root.Get(PARENT_ID));
3386 TEST_F(SyncerTest, DirectoryCommitTest) {
3387 syncable::Id in_root_id, in_dir_id;
3388 int64 foo_metahandle;
3389 int64 bar_metahandle;
3392 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3393 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, root_id_, "foo");
3394 ASSERT_TRUE(parent.good());
3395 parent.Put(syncable::IS_UNSYNCED, true);
3396 parent.Put(syncable::IS_DIR, true);
3397 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
3398 in_root_id = parent.Get(syncable::ID);
3399 foo_metahandle = parent.Get(META_HANDLE);
3401 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent.Get(ID), "bar");
3402 ASSERT_TRUE(child.good());
3403 child.Put(syncable::IS_UNSYNCED, true);
3404 child.Put(syncable::IS_DIR, true);
3405 child.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
3406 bar_metahandle = child.Get(META_HANDLE);
3407 in_dir_id = parent.Get(syncable::ID);
3409 SyncShareNudge();
3411 syncable::ReadTransaction trans(FROM_HERE, directory());
3412 Entry fail_by_old_id_entry(&trans, GET_BY_ID, in_root_id);
3413 ASSERT_FALSE(fail_by_old_id_entry.good());
3415 Entry foo_entry(&trans, GET_BY_HANDLE, foo_metahandle);
3416 ASSERT_TRUE(foo_entry.good());
3417 EXPECT_EQ("foo", foo_entry.Get(NON_UNIQUE_NAME));
3418 EXPECT_NE(foo_entry.Get(syncable::ID), in_root_id);
3420 Entry bar_entry(&trans, GET_BY_HANDLE, bar_metahandle);
3421 ASSERT_TRUE(bar_entry.good());
3422 EXPECT_EQ("bar", bar_entry.Get(NON_UNIQUE_NAME));
3423 EXPECT_NE(bar_entry.Get(syncable::ID), in_dir_id);
3424 EXPECT_EQ(foo_entry.Get(syncable::ID), bar_entry.Get(PARENT_ID));
3428 TEST_F(SyncerTest, TestClientCommandDuringUpdate) {
3429 using sync_pb::ClientCommand;
3431 ClientCommand* command = new ClientCommand();
3432 command->set_set_sync_poll_interval(8);
3433 command->set_set_sync_long_poll_interval(800);
3434 command->set_sessions_commit_delay_seconds(3141);
3435 mock_server_->AddUpdateDirectory(1, 0, "in_root", 1, 1,
3436 foreign_cache_guid(), "-1");
3437 mock_server_->SetGUClientCommand(command);
3438 SyncShareNudge();
3440 EXPECT_TRUE(TimeDelta::FromSeconds(8) ==
3441 last_short_poll_interval_received_);
3442 EXPECT_TRUE(TimeDelta::FromSeconds(800) ==
3443 last_long_poll_interval_received_);
3444 EXPECT_TRUE(TimeDelta::FromSeconds(3141) ==
3445 last_sessions_commit_delay_seconds_);
3447 command = new ClientCommand();
3448 command->set_set_sync_poll_interval(180);
3449 command->set_set_sync_long_poll_interval(190);
3450 command->set_sessions_commit_delay_seconds(2718);
3451 mock_server_->AddUpdateDirectory(1, 0, "in_root", 1, 1,
3452 foreign_cache_guid(), "-1");
3453 mock_server_->SetGUClientCommand(command);
3454 SyncShareNudge();
3456 EXPECT_TRUE(TimeDelta::FromSeconds(180) ==
3457 last_short_poll_interval_received_);
3458 EXPECT_TRUE(TimeDelta::FromSeconds(190) ==
3459 last_long_poll_interval_received_);
3460 EXPECT_TRUE(TimeDelta::FromSeconds(2718) ==
3461 last_sessions_commit_delay_seconds_);
3464 TEST_F(SyncerTest, TestClientCommandDuringCommit) {
3465 using sync_pb::ClientCommand;
3467 ClientCommand* command = new ClientCommand();
3468 command->set_set_sync_poll_interval(8);
3469 command->set_set_sync_long_poll_interval(800);
3470 command->set_sessions_commit_delay_seconds(3141);
3471 CreateUnsyncedDirectory("X", "id_X");
3472 mock_server_->SetCommitClientCommand(command);
3473 SyncShareNudge();
3475 EXPECT_TRUE(TimeDelta::FromSeconds(8) ==
3476 last_short_poll_interval_received_);
3477 EXPECT_TRUE(TimeDelta::FromSeconds(800) ==
3478 last_long_poll_interval_received_);
3479 EXPECT_TRUE(TimeDelta::FromSeconds(3141) ==
3480 last_sessions_commit_delay_seconds_);
3482 command = new ClientCommand();
3483 command->set_set_sync_poll_interval(180);
3484 command->set_set_sync_long_poll_interval(190);
3485 command->set_sessions_commit_delay_seconds(2718);
3486 CreateUnsyncedDirectory("Y", "id_Y");
3487 mock_server_->SetCommitClientCommand(command);
3488 SyncShareNudge();
3490 EXPECT_TRUE(TimeDelta::FromSeconds(180) ==
3491 last_short_poll_interval_received_);
3492 EXPECT_TRUE(TimeDelta::FromSeconds(190) ==
3493 last_long_poll_interval_received_);
3494 EXPECT_TRUE(TimeDelta::FromSeconds(2718) ==
3495 last_sessions_commit_delay_seconds_);
3498 TEST_F(SyncerTest, EnsureWeSendUpOldParent) {
3499 syncable::Id folder_one_id = ids_.FromNumber(1);
3500 syncable::Id folder_two_id = ids_.FromNumber(2);
3502 mock_server_->AddUpdateDirectory(folder_one_id, TestIdFactory::root(),
3503 "folder_one", 1, 1, foreign_cache_guid(), "-1");
3504 mock_server_->AddUpdateDirectory(folder_two_id, TestIdFactory::root(),
3505 "folder_two", 1, 1, foreign_cache_guid(), "-2");
3506 SyncShareNudge();
3508 // A moved entry should send an "old parent."
3509 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3510 MutableEntry entry(&trans, GET_BY_ID, folder_one_id);
3511 ASSERT_TRUE(entry.good());
3512 entry.Put(PARENT_ID, folder_two_id);
3513 entry.Put(IS_UNSYNCED, true);
3514 // A new entry should send no "old parent."
3515 MutableEntry create(
3516 &trans, CREATE, BOOKMARKS, trans.root_id(), "new_folder");
3517 create.Put(IS_UNSYNCED, true);
3518 create.Put(SPECIFICS, DefaultBookmarkSpecifics());
3520 SyncShareNudge();
3521 const sync_pb::CommitMessage& commit = mock_server_->last_sent_commit();
3522 ASSERT_EQ(2, commit.entries_size());
3523 EXPECT_TRUE(commit.entries(0).parent_id_string() == "2");
3524 EXPECT_TRUE(commit.entries(0).old_parent_id() == "0");
3525 EXPECT_FALSE(commit.entries(1).has_old_parent_id());
3528 TEST_F(SyncerTest, Test64BitVersionSupport) {
3529 int64 really_big_int = std::numeric_limits<int64>::max() - 12;
3530 const string name("ringo's dang orang ran rings around my o-ring");
3531 int64 item_metahandle;
3533 // Try writing max int64 to the version fields of a meta entry.
3535 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3536 MutableEntry entry(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), name);
3537 ASSERT_TRUE(entry.good());
3538 entry.Put(syncable::BASE_VERSION, really_big_int);
3539 entry.Put(syncable::SERVER_VERSION, really_big_int);
3540 entry.Put(syncable::ID, ids_.NewServerId());
3541 item_metahandle = entry.Get(META_HANDLE);
3543 // Now read it back out and make sure the value is max int64.
3544 syncable::ReadTransaction rtrans(FROM_HERE, directory());
3545 Entry entry(&rtrans, syncable::GET_BY_HANDLE, item_metahandle);
3546 ASSERT_TRUE(entry.good());
3547 EXPECT_TRUE(really_big_int == entry.Get(syncable::BASE_VERSION));
3550 TEST_F(SyncerTest, TestSimpleUndelete) {
3551 Id id = ids_.MakeServer("undeletion item"), root = TestIdFactory::root();
3552 mock_server_->set_conflict_all_commits(true);
3553 // Let there be an entry from the server.
3554 mock_server_->AddUpdateBookmark(id, root, "foo", 1, 10,
3555 foreign_cache_guid(), "-1");
3556 SyncShareNudge();
3557 // Check it out and delete it.
3559 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3560 MutableEntry entry(&wtrans, GET_BY_ID, id);
3561 ASSERT_TRUE(entry.good());
3562 EXPECT_FALSE(entry.Get(IS_UNAPPLIED_UPDATE));
3563 EXPECT_FALSE(entry.Get(IS_UNSYNCED));
3564 EXPECT_FALSE(entry.Get(IS_DEL));
3565 // Delete it locally.
3566 entry.Put(IS_DEL, true);
3568 SyncShareNudge();
3569 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3571 syncable::ReadTransaction trans(FROM_HERE, directory());
3572 Entry entry(&trans, GET_BY_ID, id);
3573 ASSERT_TRUE(entry.good());
3574 EXPECT_FALSE(entry.Get(IS_UNAPPLIED_UPDATE));
3575 EXPECT_FALSE(entry.Get(IS_UNSYNCED));
3576 EXPECT_TRUE(entry.Get(IS_DEL));
3577 EXPECT_FALSE(entry.Get(SERVER_IS_DEL));
3579 SyncShareNudge();
3580 // Update from server confirming deletion.
3581 mock_server_->AddUpdateBookmark(id, root, "foo", 2, 11,
3582 foreign_cache_guid(), "-1");
3583 mock_server_->SetLastUpdateDeleted();
3584 SyncShareNudge();
3585 // IS_DEL AND SERVER_IS_DEL now both true.
3587 syncable::ReadTransaction trans(FROM_HERE, directory());
3588 Entry entry(&trans, GET_BY_ID, id);
3589 ASSERT_TRUE(entry.good());
3590 EXPECT_FALSE(entry.Get(IS_UNAPPLIED_UPDATE));
3591 EXPECT_FALSE(entry.Get(IS_UNSYNCED));
3592 EXPECT_TRUE(entry.Get(IS_DEL));
3593 EXPECT_TRUE(entry.Get(SERVER_IS_DEL));
3595 // Undelete from server.
3596 mock_server_->AddUpdateBookmark(id, root, "foo", 2, 12,
3597 foreign_cache_guid(), "-1");
3598 SyncShareNudge();
3599 // IS_DEL and SERVER_IS_DEL now both false.
3601 syncable::ReadTransaction trans(FROM_HERE, directory());
3602 Entry entry(&trans, GET_BY_ID, id);
3603 ASSERT_TRUE(entry.good());
3604 EXPECT_FALSE(entry.Get(IS_UNAPPLIED_UPDATE));
3605 EXPECT_FALSE(entry.Get(IS_UNSYNCED));
3606 EXPECT_FALSE(entry.Get(IS_DEL));
3607 EXPECT_FALSE(entry.Get(SERVER_IS_DEL));
3611 TEST_F(SyncerTest, TestUndeleteWithMissingDeleteUpdate) {
3612 Id id = ids_.MakeServer("undeletion item"), root = TestIdFactory::root();
3613 // Let there be a entry, from the server.
3614 mock_server_->set_conflict_all_commits(true);
3615 mock_server_->AddUpdateBookmark(id, root, "foo", 1, 10,
3616 foreign_cache_guid(), "-1");
3617 SyncShareNudge();
3618 // Check it out and delete it.
3620 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3621 MutableEntry entry(&wtrans, GET_BY_ID, id);
3622 ASSERT_TRUE(entry.good());
3623 EXPECT_FALSE(entry.Get(IS_UNAPPLIED_UPDATE));
3624 EXPECT_FALSE(entry.Get(IS_UNSYNCED));
3625 EXPECT_FALSE(entry.Get(IS_DEL));
3626 // Delete it locally.
3627 entry.Put(IS_DEL, true);
3629 SyncShareNudge();
3630 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3632 syncable::ReadTransaction trans(FROM_HERE, directory());
3633 Entry entry(&trans, GET_BY_ID, id);
3634 ASSERT_TRUE(entry.good());
3635 EXPECT_FALSE(entry.Get(IS_UNAPPLIED_UPDATE));
3636 EXPECT_FALSE(entry.Get(IS_UNSYNCED));
3637 EXPECT_TRUE(entry.Get(IS_DEL));
3638 EXPECT_FALSE(entry.Get(SERVER_IS_DEL));
3640 SyncShareNudge();
3641 // Say we do not get an update from server confirming deletion. Undelete
3642 // from server
3643 mock_server_->AddUpdateBookmark(id, root, "foo", 2, 12,
3644 foreign_cache_guid(), "-1");
3645 SyncShareNudge();
3646 // IS_DEL and SERVER_IS_DEL now both false.
3648 syncable::ReadTransaction trans(FROM_HERE, directory());
3649 Entry entry(&trans, GET_BY_ID, id);
3650 ASSERT_TRUE(entry.good());
3651 EXPECT_FALSE(entry.Get(IS_UNAPPLIED_UPDATE));
3652 EXPECT_FALSE(entry.Get(IS_UNSYNCED));
3653 EXPECT_FALSE(entry.Get(IS_DEL));
3654 EXPECT_FALSE(entry.Get(SERVER_IS_DEL));
3658 TEST_F(SyncerTest, TestUndeleteIgnoreCorrectlyUnappliedUpdate) {
3659 Id id1 = ids_.MakeServer("first"), id2 = ids_.MakeServer("second");
3660 Id root = TestIdFactory::root();
3661 // Duplicate! expect path clashing!
3662 mock_server_->set_conflict_all_commits(true);
3663 mock_server_->AddUpdateBookmark(id1, root, "foo", 1, 10,
3664 foreign_cache_guid(), "-1");
3665 mock_server_->AddUpdateBookmark(id2, root, "foo", 1, 10,
3666 foreign_cache_guid(), "-2");
3667 SyncShareNudge();
3668 mock_server_->AddUpdateBookmark(id2, root, "foo2", 2, 20,
3669 foreign_cache_guid(), "-2");
3670 SyncShareNudge(); // Now just don't explode.
3673 TEST_F(SyncerTest, ClientTagServerCreatedUpdatesWork) {
3674 mock_server_->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
3675 foreign_cache_guid(), "-1");
3676 mock_server_->SetLastUpdateClientTag("permfolder");
3678 SyncShareNudge();
3681 syncable::ReadTransaction trans(FROM_HERE, directory());
3682 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
3683 ASSERT_TRUE(perm_folder.good());
3684 EXPECT_FALSE(perm_folder.Get(IS_DEL));
3685 EXPECT_FALSE(perm_folder.Get(IS_UNAPPLIED_UPDATE));
3686 EXPECT_FALSE(perm_folder.Get(IS_UNSYNCED));
3687 EXPECT_EQ(perm_folder.Get(UNIQUE_CLIENT_TAG), "permfolder");
3688 EXPECT_EQ(perm_folder.Get(NON_UNIQUE_NAME), "permitem1");
3691 mock_server_->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
3692 foreign_cache_guid(), "-1");
3693 mock_server_->SetLastUpdateClientTag("permfolder");
3694 SyncShareNudge();
3697 syncable::ReadTransaction trans(FROM_HERE, directory());
3699 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
3700 ASSERT_TRUE(perm_folder.good());
3701 EXPECT_FALSE(perm_folder.Get(IS_DEL));
3702 EXPECT_FALSE(perm_folder.Get(IS_UNAPPLIED_UPDATE));
3703 EXPECT_FALSE(perm_folder.Get(IS_UNSYNCED));
3704 EXPECT_EQ(perm_folder.Get(UNIQUE_CLIENT_TAG), "permfolder");
3705 EXPECT_EQ(perm_folder.Get(NON_UNIQUE_NAME), "permitem_renamed");
3709 TEST_F(SyncerTest, ClientTagIllegalUpdateIgnored) {
3710 mock_server_->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
3711 foreign_cache_guid(), "-1");
3712 mock_server_->SetLastUpdateClientTag("permfolder");
3714 SyncShareNudge();
3717 syncable::ReadTransaction trans(FROM_HERE, directory());
3718 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
3719 ASSERT_TRUE(perm_folder.good());
3720 EXPECT_FALSE(perm_folder.Get(IS_UNAPPLIED_UPDATE));
3721 EXPECT_FALSE(perm_folder.Get(IS_UNSYNCED));
3722 EXPECT_EQ(perm_folder.Get(UNIQUE_CLIENT_TAG), "permfolder");
3723 EXPECT_TRUE(perm_folder.Get(NON_UNIQUE_NAME) == "permitem1");
3724 EXPECT_TRUE(perm_folder.Get(ID).ServerKnows());
3727 mock_server_->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
3728 foreign_cache_guid(), "-1");
3729 mock_server_->SetLastUpdateClientTag("wrongtag");
3730 SyncShareNudge();
3733 syncable::ReadTransaction trans(FROM_HERE, directory());
3735 // This update is rejected because it has the same ID, but a
3736 // different tag than one that is already on the client.
3737 // The client has a ServerKnows ID, which cannot be overwritten.
3738 Entry rejected_update(&trans, GET_BY_CLIENT_TAG, "wrongtag");
3739 EXPECT_FALSE(rejected_update.good());
3741 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
3742 ASSERT_TRUE(perm_folder.good());
3743 EXPECT_FALSE(perm_folder.Get(IS_UNAPPLIED_UPDATE));
3744 EXPECT_FALSE(perm_folder.Get(IS_UNSYNCED));
3745 EXPECT_EQ(perm_folder.Get(NON_UNIQUE_NAME), "permitem1");
3749 TEST_F(SyncerTest, ClientTagUncommittedTagMatchesUpdate) {
3750 int64 original_metahandle = 0;
3753 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3754 MutableEntry pref(
3755 &trans, CREATE, PREFERENCES, ids_.root(), "name");
3756 ASSERT_TRUE(pref.good());
3757 pref.Put(UNIQUE_CLIENT_TAG, "tag");
3758 pref.Put(IS_UNSYNCED, true);
3759 EXPECT_FALSE(pref.Get(IS_UNAPPLIED_UPDATE));
3760 EXPECT_FALSE(pref.Get(ID).ServerKnows());
3761 original_metahandle = pref.Get(META_HANDLE);
3764 syncable::Id server_id = TestIdFactory::MakeServer("id");
3765 mock_server_->AddUpdatePref(server_id.GetServerId(),
3766 ids_.root().GetServerId(),
3767 "tag", 10, 100);
3768 mock_server_->set_conflict_all_commits(true);
3770 SyncShareNudge();
3771 // This should cause client tag reunion, preserving the metahandle.
3773 syncable::ReadTransaction trans(FROM_HERE, directory());
3775 Entry pref(&trans, GET_BY_CLIENT_TAG, "tag");
3776 ASSERT_TRUE(pref.good());
3777 EXPECT_FALSE(pref.Get(IS_DEL));
3778 EXPECT_FALSE(pref.Get(IS_UNAPPLIED_UPDATE));
3779 EXPECT_TRUE(pref.Get(IS_UNSYNCED));
3780 EXPECT_EQ(10, pref.Get(BASE_VERSION));
3781 // Entry should have been given the new ID while preserving the
3782 // metahandle; client should have won the conflict resolution.
3783 EXPECT_EQ(original_metahandle, pref.Get(META_HANDLE));
3784 EXPECT_EQ("tag", pref.Get(UNIQUE_CLIENT_TAG));
3785 EXPECT_TRUE(pref.Get(ID).ServerKnows());
3788 mock_server_->set_conflict_all_commits(false);
3789 SyncShareNudge();
3791 // The resolved entry ought to commit cleanly.
3793 syncable::ReadTransaction trans(FROM_HERE, directory());
3795 Entry pref(&trans, GET_BY_CLIENT_TAG, "tag");
3796 ASSERT_TRUE(pref.good());
3797 EXPECT_FALSE(pref.Get(IS_DEL));
3798 EXPECT_FALSE(pref.Get(IS_UNAPPLIED_UPDATE));
3799 EXPECT_FALSE(pref.Get(IS_UNSYNCED));
3800 EXPECT_TRUE(10 < pref.Get(BASE_VERSION));
3801 // Entry should have been given the new ID while preserving the
3802 // metahandle; client should have won the conflict resolution.
3803 EXPECT_EQ(original_metahandle, pref.Get(META_HANDLE));
3804 EXPECT_EQ("tag", pref.Get(UNIQUE_CLIENT_TAG));
3805 EXPECT_TRUE(pref.Get(ID).ServerKnows());
3809 TEST_F(SyncerTest, ClientTagConflictWithDeletedLocalEntry) {
3811 // Create a deleted local entry with a unique client tag.
3812 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3813 MutableEntry pref(
3814 &trans, CREATE, PREFERENCES, ids_.root(), "name");
3815 ASSERT_TRUE(pref.good());
3816 ASSERT_FALSE(pref.Get(ID).ServerKnows());
3817 pref.Put(UNIQUE_CLIENT_TAG, "tag");
3818 pref.Put(IS_UNSYNCED, true);
3820 // Note: IS_DEL && !ServerKnows() will clear the UNSYNCED bit.
3821 // (We never attempt to commit server-unknown deleted items, so this
3822 // helps us clean up those entries).
3823 pref.Put(IS_DEL, true);
3826 // Prepare an update with the same unique client tag.
3827 syncable::Id server_id = TestIdFactory::MakeServer("id");
3828 mock_server_->AddUpdatePref(server_id.GetServerId(),
3829 ids_.root().GetServerId(),
3830 "tag", 10, 100);
3832 SyncShareNudge();
3833 // The local entry will be overwritten.
3835 syncable::ReadTransaction trans(FROM_HERE, directory());
3837 Entry pref(&trans, GET_BY_CLIENT_TAG, "tag");
3838 ASSERT_TRUE(pref.good());
3839 ASSERT_TRUE(pref.Get(ID).ServerKnows());
3840 EXPECT_FALSE(pref.Get(IS_DEL));
3841 EXPECT_FALSE(pref.Get(IS_UNAPPLIED_UPDATE));
3842 EXPECT_FALSE(pref.Get(IS_UNSYNCED));
3843 EXPECT_EQ(pref.Get(BASE_VERSION), 10);
3844 EXPECT_EQ(pref.Get(UNIQUE_CLIENT_TAG), "tag");
3848 TEST_F(SyncerTest, ClientTagUpdateClashesWithLocalEntry) {
3849 // This test is written assuming that ID comparison
3850 // will work out in a particular way.
3851 EXPECT_TRUE(ids_.FromNumber(1) < ids_.FromNumber(2));
3852 EXPECT_TRUE(ids_.FromNumber(3) < ids_.FromNumber(4));
3854 syncable::Id id1 = TestIdFactory::MakeServer("1");
3855 mock_server_->AddUpdatePref(id1.GetServerId(), ids_.root().GetServerId(),
3856 "tag1", 10, 100);
3858 syncable::Id id4 = TestIdFactory::MakeServer("4");
3859 mock_server_->AddUpdatePref(id4.GetServerId(), ids_.root().GetServerId(),
3860 "tag2", 11, 110);
3862 mock_server_->set_conflict_all_commits(true);
3864 SyncShareNudge();
3865 int64 tag1_metahandle = syncable::kInvalidMetaHandle;
3866 int64 tag2_metahandle = syncable::kInvalidMetaHandle;
3867 // This should cause client tag overwrite.
3869 syncable::ReadTransaction trans(FROM_HERE, directory());
3871 Entry tag1(&trans, GET_BY_CLIENT_TAG, "tag1");
3872 ASSERT_TRUE(tag1.good());
3873 ASSERT_TRUE(tag1.Get(ID).ServerKnows());
3874 ASSERT_TRUE(id1 == tag1.Get(ID));
3875 EXPECT_FALSE(tag1.Get(IS_DEL));
3876 EXPECT_FALSE(tag1.Get(IS_UNAPPLIED_UPDATE));
3877 EXPECT_FALSE(tag1.Get(IS_UNSYNCED));
3878 EXPECT_EQ(10, tag1.Get(BASE_VERSION));
3879 EXPECT_EQ("tag1", tag1.Get(UNIQUE_CLIENT_TAG));
3880 tag1_metahandle = tag1.Get(META_HANDLE);
3882 Entry tag2(&trans, GET_BY_CLIENT_TAG, "tag2");
3883 ASSERT_TRUE(tag2.good());
3884 ASSERT_TRUE(tag2.Get(ID).ServerKnows());
3885 ASSERT_TRUE(id4 == tag2.Get(ID));
3886 EXPECT_FALSE(tag2.Get(IS_DEL));
3887 EXPECT_FALSE(tag2.Get(IS_UNAPPLIED_UPDATE));
3888 EXPECT_FALSE(tag2.Get(IS_UNSYNCED));
3889 EXPECT_EQ(11, tag2.Get(BASE_VERSION));
3890 EXPECT_EQ("tag2", tag2.Get(UNIQUE_CLIENT_TAG));
3891 tag2_metahandle = tag2.Get(META_HANDLE);
3893 syncable::Directory::ChildHandles children;
3894 directory()->GetChildHandlesById(&trans, trans.root_id(), &children);
3895 ASSERT_EQ(2U, children.size());
3898 syncable::Id id2 = TestIdFactory::MakeServer("2");
3899 mock_server_->AddUpdatePref(id2.GetServerId(), ids_.root().GetServerId(),
3900 "tag1", 12, 120);
3901 syncable::Id id3 = TestIdFactory::MakeServer("3");
3902 mock_server_->AddUpdatePref(id3.GetServerId(), ids_.root().GetServerId(),
3903 "tag2", 13, 130);
3904 SyncShareNudge();
3907 syncable::ReadTransaction trans(FROM_HERE, directory());
3909 Entry tag1(&trans, GET_BY_CLIENT_TAG, "tag1");
3910 ASSERT_TRUE(tag1.good());
3911 ASSERT_TRUE(tag1.Get(ID).ServerKnows());
3912 ASSERT_EQ(id1, tag1.Get(ID))
3913 << "ID 1 should be kept, since it was less than ID 2.";
3914 EXPECT_FALSE(tag1.Get(IS_DEL));
3915 EXPECT_FALSE(tag1.Get(IS_UNAPPLIED_UPDATE));
3916 EXPECT_FALSE(tag1.Get(IS_UNSYNCED));
3917 EXPECT_EQ(10, tag1.Get(BASE_VERSION));
3918 EXPECT_EQ("tag1", tag1.Get(UNIQUE_CLIENT_TAG));
3919 EXPECT_EQ(tag1_metahandle, tag1.Get(META_HANDLE));
3921 Entry tag2(&trans, GET_BY_CLIENT_TAG, "tag2");
3922 ASSERT_TRUE(tag2.good());
3923 ASSERT_TRUE(tag2.Get(ID).ServerKnows());
3924 ASSERT_EQ(id3, tag2.Get(ID))
3925 << "ID 3 should be kept, since it was less than ID 4.";
3926 EXPECT_FALSE(tag2.Get(IS_DEL));
3927 EXPECT_FALSE(tag2.Get(IS_UNAPPLIED_UPDATE));
3928 EXPECT_FALSE(tag2.Get(IS_UNSYNCED));
3929 EXPECT_EQ(13, tag2.Get(BASE_VERSION));
3930 EXPECT_EQ("tag2", tag2.Get(UNIQUE_CLIENT_TAG));
3931 EXPECT_EQ(tag2_metahandle, tag2.Get(META_HANDLE));
3933 syncable::Directory::ChildHandles children;
3934 directory()->GetChildHandlesById(&trans, trans.root_id(), &children);
3935 ASSERT_EQ(2U, children.size());
3939 TEST_F(SyncerTest, ClientTagClashWithinBatchOfUpdates) {
3940 // This test is written assuming that ID comparison
3941 // will work out in a particular way.
3942 EXPECT_TRUE(ids_.FromNumber(1) < ids_.FromNumber(4));
3943 EXPECT_TRUE(ids_.FromNumber(201) < ids_.FromNumber(205));
3945 // Least ID: winner.
3946 mock_server_->AddUpdatePref(ids_.FromNumber(1).GetServerId(),
3947 ids_.root().GetServerId(), "tag a", 1, 10);
3948 mock_server_->AddUpdatePref(ids_.FromNumber(2).GetServerId(),
3949 ids_.root().GetServerId(), "tag a", 11, 110);
3950 mock_server_->AddUpdatePref(ids_.FromNumber(3).GetServerId(),
3951 ids_.root().GetServerId(), "tag a", 12, 120);
3952 mock_server_->AddUpdatePref(ids_.FromNumber(4).GetServerId(),
3953 ids_.root().GetServerId(), "tag a", 13, 130);
3955 mock_server_->AddUpdatePref(ids_.FromNumber(105).GetServerId(),
3956 ids_.root().GetServerId(), "tag b", 14, 140);
3957 mock_server_->AddUpdatePref(ids_.FromNumber(102).GetServerId(),
3958 ids_.root().GetServerId(), "tag b", 15, 150);
3959 // Least ID: winner.
3960 mock_server_->AddUpdatePref(ids_.FromNumber(101).GetServerId(),
3961 ids_.root().GetServerId(), "tag b", 16, 160);
3962 mock_server_->AddUpdatePref(ids_.FromNumber(104).GetServerId(),
3963 ids_.root().GetServerId(), "tag b", 17, 170);
3965 mock_server_->AddUpdatePref(ids_.FromNumber(205).GetServerId(),
3966 ids_.root().GetServerId(), "tag c", 18, 180);
3967 mock_server_->AddUpdatePref(ids_.FromNumber(202).GetServerId(),
3968 ids_.root().GetServerId(), "tag c", 19, 190);
3969 mock_server_->AddUpdatePref(ids_.FromNumber(204).GetServerId(),
3970 ids_.root().GetServerId(), "tag c", 20, 200);
3971 // Least ID: winner.
3972 mock_server_->AddUpdatePref(ids_.FromNumber(201).GetServerId(),
3973 ids_.root().GetServerId(), "tag c", 21, 210);
3975 mock_server_->set_conflict_all_commits(true);
3977 SyncShareNudge();
3978 // This should cause client tag overwrite.
3980 syncable::ReadTransaction trans(FROM_HERE, directory());
3982 Entry tag_a(&trans, GET_BY_CLIENT_TAG, "tag a");
3983 ASSERT_TRUE(tag_a.good());
3984 EXPECT_TRUE(tag_a.Get(ID).ServerKnows());
3985 EXPECT_EQ(ids_.FromNumber(1), tag_a.Get(ID));
3986 EXPECT_FALSE(tag_a.Get(IS_DEL));
3987 EXPECT_FALSE(tag_a.Get(IS_UNAPPLIED_UPDATE));
3988 EXPECT_FALSE(tag_a.Get(IS_UNSYNCED));
3989 EXPECT_EQ(1, tag_a.Get(BASE_VERSION));
3990 EXPECT_EQ("tag a", tag_a.Get(UNIQUE_CLIENT_TAG));
3992 Entry tag_b(&trans, GET_BY_CLIENT_TAG, "tag b");
3993 ASSERT_TRUE(tag_b.good());
3994 EXPECT_TRUE(tag_b.Get(ID).ServerKnows());
3995 EXPECT_EQ(ids_.FromNumber(101), tag_b.Get(ID));
3996 EXPECT_FALSE(tag_b.Get(IS_DEL));
3997 EXPECT_FALSE(tag_b.Get(IS_UNAPPLIED_UPDATE));
3998 EXPECT_FALSE(tag_b.Get(IS_UNSYNCED));
3999 EXPECT_EQ(16, tag_b.Get(BASE_VERSION));
4000 EXPECT_EQ("tag b", tag_b.Get(UNIQUE_CLIENT_TAG));
4002 Entry tag_c(&trans, GET_BY_CLIENT_TAG, "tag c");
4003 ASSERT_TRUE(tag_c.good());
4004 EXPECT_TRUE(tag_c.Get(ID).ServerKnows());
4005 EXPECT_EQ(ids_.FromNumber(201), tag_c.Get(ID));
4006 EXPECT_FALSE(tag_c.Get(IS_DEL));
4007 EXPECT_FALSE(tag_c.Get(IS_UNAPPLIED_UPDATE));
4008 EXPECT_FALSE(tag_c.Get(IS_UNSYNCED));
4009 EXPECT_EQ(21, tag_c.Get(BASE_VERSION));
4010 EXPECT_EQ("tag c", tag_c.Get(UNIQUE_CLIENT_TAG));
4012 syncable::Directory::ChildHandles children;
4013 directory()->GetChildHandlesById(&trans, trans.root_id(), &children);
4014 ASSERT_EQ(3U, children.size());
4018 TEST_F(SyncerTest, UniqueServerTagUpdates) {
4019 // As a hurdle, introduce an item whose name is the same as the tag value
4020 // we'll use later.
4021 int64 hurdle_handle = CreateUnsyncedDirectory("bob", "id_bob");
4023 syncable::ReadTransaction trans(FROM_HERE, directory());
4024 Entry hurdle(&trans, GET_BY_HANDLE, hurdle_handle);
4025 ASSERT_TRUE(hurdle.good());
4026 ASSERT_TRUE(!hurdle.Get(IS_DEL));
4027 ASSERT_TRUE(hurdle.Get(UNIQUE_SERVER_TAG).empty());
4028 ASSERT_TRUE(hurdle.Get(NON_UNIQUE_NAME) == "bob");
4030 // Try to lookup by the tagname. These should fail.
4031 Entry tag_alpha(&trans, GET_BY_SERVER_TAG, "alpha");
4032 EXPECT_FALSE(tag_alpha.good());
4033 Entry tag_bob(&trans, GET_BY_SERVER_TAG, "bob");
4034 EXPECT_FALSE(tag_bob.good());
4037 // Now download some tagged items as updates.
4038 mock_server_->AddUpdateDirectory(
4039 1, 0, "update1", 1, 10, std::string(), std::string());
4040 mock_server_->SetLastUpdateServerTag("alpha");
4041 mock_server_->AddUpdateDirectory(
4042 2, 0, "update2", 2, 20, std::string(), std::string());
4043 mock_server_->SetLastUpdateServerTag("bob");
4044 SyncShareNudge();
4047 syncable::ReadTransaction trans(FROM_HERE, directory());
4049 // The new items should be applied as new entries, and we should be able
4050 // to look them up by their tag values.
4051 Entry tag_alpha(&trans, GET_BY_SERVER_TAG, "alpha");
4052 ASSERT_TRUE(tag_alpha.good());
4053 ASSERT_TRUE(!tag_alpha.Get(IS_DEL));
4054 ASSERT_TRUE(tag_alpha.Get(UNIQUE_SERVER_TAG) == "alpha");
4055 ASSERT_TRUE(tag_alpha.Get(NON_UNIQUE_NAME) == "update1");
4056 Entry tag_bob(&trans, GET_BY_SERVER_TAG, "bob");
4057 ASSERT_TRUE(tag_bob.good());
4058 ASSERT_TRUE(!tag_bob.Get(IS_DEL));
4059 ASSERT_TRUE(tag_bob.Get(UNIQUE_SERVER_TAG) == "bob");
4060 ASSERT_TRUE(tag_bob.Get(NON_UNIQUE_NAME) == "update2");
4061 // The old item should be unchanged.
4062 Entry hurdle(&trans, GET_BY_HANDLE, hurdle_handle);
4063 ASSERT_TRUE(hurdle.good());
4064 ASSERT_TRUE(!hurdle.Get(IS_DEL));
4065 ASSERT_TRUE(hurdle.Get(UNIQUE_SERVER_TAG).empty());
4066 ASSERT_TRUE(hurdle.Get(NON_UNIQUE_NAME) == "bob");
4070 TEST_F(SyncerTest, GetUpdatesSetsRequestedTypes) {
4071 // The expectations of this test happen in the MockConnectionManager's
4072 // GetUpdates handler. EnableDatatype sets the expectation value from our
4073 // set of enabled/disabled datatypes.
4074 EnableDatatype(BOOKMARKS);
4075 SyncShareNudge();
4076 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4078 EnableDatatype(AUTOFILL);
4079 SyncShareNudge();
4080 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4082 EnableDatatype(PREFERENCES);
4083 SyncShareNudge();
4084 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4086 DisableDatatype(BOOKMARKS);
4087 SyncShareNudge();
4088 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4090 DisableDatatype(AUTOFILL);
4091 SyncShareNudge();
4092 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4094 DisableDatatype(PREFERENCES);
4095 EnableDatatype(AUTOFILL);
4096 SyncShareNudge();
4097 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4100 // A typical scenario: server and client each have one update for the other.
4101 // This is the "happy path" alternative to UpdateFailsThenDontCommit.
4102 TEST_F(SyncerTest, UpdateThenCommit) {
4103 syncable::Id to_receive = ids_.NewServerId();
4104 syncable::Id to_commit = ids_.NewLocalId();
4106 mock_server_->AddUpdateDirectory(to_receive, ids_.root(), "x", 1, 10,
4107 foreign_cache_guid(), "-1");
4108 int64 commit_handle = CreateUnsyncedDirectory("y", to_commit);
4109 SyncShareNudge();
4111 // The sync cycle should have included a GetUpdate, then a commit. By the
4112 // time the commit happened, we should have known for sure that there were no
4113 // hierarchy conflicts, and reported this fact to the server.
4114 ASSERT_TRUE(mock_server_->last_request().has_commit());
4115 VerifyNoHierarchyConflictsReported(mock_server_->last_request());
4117 syncable::ReadTransaction trans(FROM_HERE, directory());
4119 Entry received(&trans, GET_BY_ID, to_receive);
4120 ASSERT_TRUE(received.good());
4121 EXPECT_FALSE(received.Get(IS_UNSYNCED));
4122 EXPECT_FALSE(received.Get(IS_UNAPPLIED_UPDATE));
4124 Entry committed(&trans, GET_BY_HANDLE, commit_handle);
4125 ASSERT_TRUE(committed.good());
4126 EXPECT_FALSE(committed.Get(IS_UNSYNCED));
4127 EXPECT_FALSE(committed.Get(IS_UNAPPLIED_UPDATE));
4130 // Same as above, but this time we fail to download updates.
4131 // We should not attempt to commit anything unless we successfully downloaded
4132 // updates, otherwise we risk causing a server-side conflict.
4133 TEST_F(SyncerTest, UpdateFailsThenDontCommit) {
4134 syncable::Id to_receive = ids_.NewServerId();
4135 syncable::Id to_commit = ids_.NewLocalId();
4137 mock_server_->AddUpdateDirectory(to_receive, ids_.root(), "x", 1, 10,
4138 foreign_cache_guid(), "-1");
4139 int64 commit_handle = CreateUnsyncedDirectory("y", to_commit);
4140 mock_server_->FailNextPostBufferToPathCall();
4141 SyncShareNudge();
4143 syncable::ReadTransaction trans(FROM_HERE, directory());
4145 // We did not receive this update.
4146 Entry received(&trans, GET_BY_ID, to_receive);
4147 ASSERT_FALSE(received.good());
4149 // And our local update remains unapplied.
4150 Entry committed(&trans, GET_BY_HANDLE, commit_handle);
4151 ASSERT_TRUE(committed.good());
4152 EXPECT_TRUE(committed.Get(IS_UNSYNCED));
4153 EXPECT_FALSE(committed.Get(IS_UNAPPLIED_UPDATE));
4155 // Inform the Mock we won't be fetching all updates.
4156 mock_server_->ClearUpdatesQueue();
4159 // Downloads two updates and applies them successfully.
4160 // This is the "happy path" alternative to ConfigureFailsDontApplyUpdates.
4161 TEST_F(SyncerTest, ConfigureDownloadsTwoBatchesSuccess) {
4162 syncable::Id node1 = ids_.NewServerId();
4163 syncable::Id node2 = ids_.NewServerId();
4165 // Construct the first GetUpdates response.
4166 mock_server_->AddUpdateDirectory(node1, ids_.root(), "one", 1, 10,
4167 foreign_cache_guid(), "-2");
4168 mock_server_->SetChangesRemaining(1);
4169 mock_server_->NextUpdateBatch();
4171 // Construct the second GetUpdates response.
4172 mock_server_->AddUpdateDirectory(node2, ids_.root(), "two", 1, 20,
4173 foreign_cache_guid(), "-2");
4175 SyncShareConfigure();
4177 syncable::ReadTransaction trans(FROM_HERE, directory());
4178 // Both nodes should be downloaded and applied.
4180 Entry n1(&trans, GET_BY_ID, node1);
4181 ASSERT_TRUE(n1.good());
4182 EXPECT_FALSE(n1.Get(IS_UNAPPLIED_UPDATE));
4184 Entry n2(&trans, GET_BY_ID, node2);
4185 ASSERT_TRUE(n2.good());
4186 EXPECT_FALSE(n2.Get(IS_UNAPPLIED_UPDATE));
4189 // Same as the above case, but this time the second batch fails to download.
4190 TEST_F(SyncerTest, ConfigureFailsDontApplyUpdates) {
4191 syncable::Id node1 = ids_.NewServerId();
4192 syncable::Id node2 = ids_.NewServerId();
4194 // The scenario: we have two batches of updates with one update each. A
4195 // normal confgure step would download all the updates one batch at a time and
4196 // apply them. This configure will succeed in downloading the first batch
4197 // then fail when downloading the second.
4198 mock_server_->FailNthPostBufferToPathCall(2);
4200 // Construct the first GetUpdates response.
4201 mock_server_->AddUpdateDirectory(node1, ids_.root(), "one", 1, 10,
4202 foreign_cache_guid(), "-1");
4203 mock_server_->SetChangesRemaining(1);
4204 mock_server_->NextUpdateBatch();
4206 // Consutrct the second GetUpdates response.
4207 mock_server_->AddUpdateDirectory(node2, ids_.root(), "two", 1, 20,
4208 foreign_cache_guid(), "-2");
4210 SyncShareConfigure();
4212 syncable::ReadTransaction trans(FROM_HERE, directory());
4214 // The first node was downloaded, but not applied.
4215 Entry n1(&trans, GET_BY_ID, node1);
4216 ASSERT_TRUE(n1.good());
4217 EXPECT_TRUE(n1.Get(IS_UNAPPLIED_UPDATE));
4219 // The second node was not downloaded.
4220 Entry n2(&trans, GET_BY_ID, node2);
4221 EXPECT_FALSE(n2.good());
4223 // One update remains undownloaded.
4224 mock_server_->ClearUpdatesQueue();
4227 TEST_F(SyncerTest, GetKeySuccess) {
4229 syncable::ReadTransaction rtrans(FROM_HERE, directory());
4230 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans));
4233 SyncShareConfigure();
4235 EXPECT_EQ(session_->status_controller().last_get_key_result(), SYNCER_OK);
4237 syncable::ReadTransaction rtrans(FROM_HERE, directory());
4238 EXPECT_FALSE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans));
4242 TEST_F(SyncerTest, GetKeyEmpty) {
4244 syncable::ReadTransaction rtrans(FROM_HERE, directory());
4245 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans));
4248 mock_server_->SetKeystoreKey(std::string());
4249 SyncShareConfigure();
4251 EXPECT_NE(session_->status_controller().last_get_key_result(), SYNCER_OK);
4253 syncable::ReadTransaction rtrans(FROM_HERE, directory());
4254 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans));
4258 // Test what happens if a client deletes, then recreates, an object very
4259 // quickly. It is possible that the deletion gets sent as a commit, and
4260 // the undelete happens during the commit request. The principle here
4261 // is that with a single committing client, conflicts should never
4262 // be encountered, and a client encountering its past actions during
4263 // getupdates should never feed back to override later actions.
4265 // In cases of ordering A-F below, the outcome should be the same.
4266 // Exercised by UndeleteDuringCommit:
4267 // A. Delete - commit - undelete - commitresponse.
4268 // B. Delete - commit - undelete - commitresponse - getupdates.
4269 // Exercised by UndeleteBeforeCommit:
4270 // C. Delete - undelete - commit - commitresponse.
4271 // D. Delete - undelete - commit - commitresponse - getupdates.
4272 // Exercised by UndeleteAfterCommit:
4273 // E. Delete - commit - commitresponse - undelete - commit
4274 // - commitresponse.
4275 // F. Delete - commit - commitresponse - undelete - commit -
4276 // - commitresponse - getupdates.
4277 class SyncerUndeletionTest : public SyncerTest {
4278 public:
4279 SyncerUndeletionTest()
4280 : client_tag_("foobar"),
4281 metahandle_(syncable::kInvalidMetaHandle) {
4284 void Create() {
4285 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
4286 MutableEntry perm_folder(
4287 &trans, CREATE, BOOKMARKS, ids_.root(), "clientname");
4288 ASSERT_TRUE(perm_folder.good());
4289 perm_folder.Put(UNIQUE_CLIENT_TAG, client_tag_);
4290 perm_folder.Put(IS_UNSYNCED, true);
4291 perm_folder.Put(SYNCING, false);
4292 perm_folder.Put(SPECIFICS, DefaultBookmarkSpecifics());
4293 EXPECT_FALSE(perm_folder.Get(IS_UNAPPLIED_UPDATE));
4294 EXPECT_FALSE(perm_folder.Get(ID).ServerKnows());
4295 metahandle_ = perm_folder.Get(META_HANDLE);
4296 local_id_ = perm_folder.Get(ID);
4299 void Delete() {
4300 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
4301 MutableEntry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4302 ASSERT_TRUE(entry.good());
4303 EXPECT_EQ(metahandle_, entry.Get(META_HANDLE));
4304 entry.Put(IS_DEL, true);
4305 entry.Put(IS_UNSYNCED, true);
4306 entry.Put(SYNCING, false);
4309 void Undelete() {
4310 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
4311 MutableEntry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4312 ASSERT_TRUE(entry.good());
4313 EXPECT_EQ(metahandle_, entry.Get(META_HANDLE));
4314 EXPECT_TRUE(entry.Get(IS_DEL));
4315 entry.Put(IS_DEL, false);
4316 entry.Put(IS_UNSYNCED, true);
4317 entry.Put(SYNCING, false);
4320 int64 GetMetahandleOfTag() {
4321 syncable::ReadTransaction trans(FROM_HERE, directory());
4322 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4323 EXPECT_TRUE(entry.good());
4324 if (!entry.good()) {
4325 return syncable::kInvalidMetaHandle;
4327 return entry.Get(META_HANDLE);
4330 void ExpectUnsyncedCreation() {
4331 EXPECT_EQ(metahandle_, GetMetahandleOfTag());
4332 EXPECT_FALSE(Get(metahandle_, IS_DEL));
4333 EXPECT_FALSE(Get(metahandle_, SERVER_IS_DEL)); // Never been committed.
4334 EXPECT_GE(0, Get(metahandle_, BASE_VERSION));
4335 EXPECT_TRUE(Get(metahandle_, IS_UNSYNCED));
4336 EXPECT_FALSE(Get(metahandle_, IS_UNAPPLIED_UPDATE));
4339 void ExpectUnsyncedUndeletion() {
4340 EXPECT_EQ(metahandle_, GetMetahandleOfTag());
4341 EXPECT_FALSE(Get(metahandle_, IS_DEL));
4342 EXPECT_TRUE(Get(metahandle_, SERVER_IS_DEL));
4343 EXPECT_EQ(0, Get(metahandle_, BASE_VERSION));
4344 EXPECT_TRUE(Get(metahandle_, IS_UNSYNCED));
4345 EXPECT_FALSE(Get(metahandle_, IS_UNAPPLIED_UPDATE));
4346 EXPECT_TRUE(Get(metahandle_, ID).ServerKnows());
4349 void ExpectUnsyncedEdit() {
4350 EXPECT_EQ(metahandle_, GetMetahandleOfTag());
4351 EXPECT_FALSE(Get(metahandle_, IS_DEL));
4352 EXPECT_FALSE(Get(metahandle_, SERVER_IS_DEL));
4353 EXPECT_LT(0, Get(metahandle_, BASE_VERSION));
4354 EXPECT_TRUE(Get(metahandle_, IS_UNSYNCED));
4355 EXPECT_FALSE(Get(metahandle_, IS_UNAPPLIED_UPDATE));
4356 EXPECT_TRUE(Get(metahandle_, ID).ServerKnows());
4359 void ExpectUnsyncedDeletion() {
4360 EXPECT_EQ(metahandle_, GetMetahandleOfTag());
4361 EXPECT_TRUE(Get(metahandle_, IS_DEL));
4362 EXPECT_FALSE(Get(metahandle_, SERVER_IS_DEL));
4363 EXPECT_TRUE(Get(metahandle_, IS_UNSYNCED));
4364 EXPECT_FALSE(Get(metahandle_, IS_UNAPPLIED_UPDATE));
4365 EXPECT_LT(0, Get(metahandle_, BASE_VERSION));
4366 EXPECT_LT(0, Get(metahandle_, SERVER_VERSION));
4369 void ExpectSyncedAndCreated() {
4370 EXPECT_EQ(metahandle_, GetMetahandleOfTag());
4371 EXPECT_FALSE(Get(metahandle_, IS_DEL));
4372 EXPECT_FALSE(Get(metahandle_, SERVER_IS_DEL));
4373 EXPECT_LT(0, Get(metahandle_, BASE_VERSION));
4374 EXPECT_EQ(Get(metahandle_, BASE_VERSION), Get(metahandle_, SERVER_VERSION));
4375 EXPECT_FALSE(Get(metahandle_, IS_UNSYNCED));
4376 EXPECT_FALSE(Get(metahandle_, IS_UNAPPLIED_UPDATE));
4379 void ExpectSyncedAndDeleted() {
4380 EXPECT_EQ(metahandle_, GetMetahandleOfTag());
4381 EXPECT_TRUE(Get(metahandle_, IS_DEL));
4382 EXPECT_TRUE(Get(metahandle_, SERVER_IS_DEL));
4383 EXPECT_FALSE(Get(metahandle_, IS_UNSYNCED));
4384 EXPECT_FALSE(Get(metahandle_, IS_UNAPPLIED_UPDATE));
4385 EXPECT_GE(0, Get(metahandle_, BASE_VERSION));
4386 EXPECT_GE(0, Get(metahandle_, SERVER_VERSION));
4389 protected:
4390 const std::string client_tag_;
4391 syncable::Id local_id_;
4392 int64 metahandle_;
4395 TEST_F(SyncerUndeletionTest, UndeleteDuringCommit) {
4396 Create();
4397 ExpectUnsyncedCreation();
4398 SyncShareNudge();
4400 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4401 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4402 ExpectSyncedAndCreated();
4404 // Delete, begin committing the delete, then undelete while committing.
4405 Delete();
4406 ExpectUnsyncedDeletion();
4407 mock_server_->SetMidCommitCallback(
4408 base::Bind(&SyncerUndeletionTest::Undelete, base::Unretained(this)));
4409 SyncShareNudge();
4411 // We will continue to commit until all nodes are synced, so we expect
4412 // that both the delete and following undelete were committed. We haven't
4413 // downloaded any updates, though, so the SERVER fields will be the same
4414 // as they were at the start of the cycle.
4415 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4416 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4418 // Server fields lag behind.
4419 EXPECT_FALSE(Get(metahandle_, SERVER_IS_DEL));
4421 // We have committed the second (undelete) update.
4422 EXPECT_FALSE(Get(metahandle_, IS_DEL));
4423 EXPECT_FALSE(Get(metahandle_, IS_UNSYNCED));
4424 EXPECT_FALSE(Get(metahandle_, IS_UNAPPLIED_UPDATE));
4426 // Now, encounter a GetUpdates corresponding to the deletion from
4427 // the server. The undeletion should prevail again and be committed.
4428 // None of this should trigger any conflict detection -- it is perfectly
4429 // normal to recieve updates from our own commits.
4430 mock_server_->SetMidCommitCallback(base::Closure());
4431 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
4432 update->set_originator_cache_guid(local_cache_guid());
4433 update->set_originator_client_item_id(local_id_.GetServerId());
4435 SyncShareNudge();
4436 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4437 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4438 ExpectSyncedAndCreated();
4441 TEST_F(SyncerUndeletionTest, UndeleteBeforeCommit) {
4442 Create();
4443 ExpectUnsyncedCreation();
4444 SyncShareNudge();
4446 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4447 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4448 ExpectSyncedAndCreated();
4450 // Delete and undelete, then sync to pick up the result.
4451 Delete();
4452 ExpectUnsyncedDeletion();
4453 Undelete();
4454 ExpectUnsyncedEdit(); // Edit, not undelete: server thinks it exists.
4455 SyncShareNudge();
4457 // The item ought to have committed successfully.
4458 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4459 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4460 ExpectSyncedAndCreated();
4461 EXPECT_EQ(2, Get(metahandle_, BASE_VERSION));
4463 // Now, encounter a GetUpdates corresponding to the just-committed
4464 // update.
4465 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
4466 update->set_originator_cache_guid(local_cache_guid());
4467 update->set_originator_client_item_id(local_id_.GetServerId());
4468 SyncShareNudge();
4469 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4470 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4471 ExpectSyncedAndCreated();
4474 TEST_F(SyncerUndeletionTest, UndeleteAfterCommitButBeforeGetUpdates) {
4475 Create();
4476 ExpectUnsyncedCreation();
4477 SyncShareNudge();
4479 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4480 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4481 ExpectSyncedAndCreated();
4483 // Delete and commit.
4484 Delete();
4485 ExpectUnsyncedDeletion();
4486 SyncShareNudge();
4488 // The item ought to have committed successfully.
4489 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4490 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4491 ExpectSyncedAndDeleted();
4493 // Before the GetUpdates, the item is locally undeleted.
4494 Undelete();
4495 ExpectUnsyncedUndeletion();
4497 // Now, encounter a GetUpdates corresponding to the just-committed
4498 // deletion update. The undeletion should prevail.
4499 mock_server_->AddUpdateFromLastCommit();
4500 SyncShareNudge();
4501 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4502 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4503 ExpectSyncedAndCreated();
4506 TEST_F(SyncerUndeletionTest, UndeleteAfterDeleteAndGetUpdates) {
4507 Create();
4508 ExpectUnsyncedCreation();
4509 SyncShareNudge();
4511 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4512 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4513 ExpectSyncedAndCreated();
4515 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
4516 update->set_originator_cache_guid(local_cache_guid());
4517 update->set_originator_client_item_id(local_id_.GetServerId());
4518 SyncShareNudge();
4519 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4520 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4521 ExpectSyncedAndCreated();
4523 // Delete and commit.
4524 Delete();
4525 ExpectUnsyncedDeletion();
4526 SyncShareNudge();
4528 // The item ought to have committed successfully.
4529 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4530 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4531 ExpectSyncedAndDeleted();
4533 // Now, encounter a GetUpdates corresponding to the just-committed
4534 // deletion update. Should be consistent.
4535 mock_server_->AddUpdateFromLastCommit();
4536 SyncShareNudge();
4537 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4538 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4539 ExpectSyncedAndDeleted();
4541 // After the GetUpdates, the item is locally undeleted.
4542 Undelete();
4543 ExpectUnsyncedUndeletion();
4545 // Now, encounter a GetUpdates corresponding to the just-committed
4546 // deletion update. The undeletion should prevail.
4547 SyncShareNudge();
4548 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4549 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4550 ExpectSyncedAndCreated();
4553 // Test processing of undeletion GetUpdateses.
4554 TEST_F(SyncerUndeletionTest, UndeleteAfterOtherClientDeletes) {
4555 Create();
4556 ExpectUnsyncedCreation();
4557 SyncShareNudge();
4559 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4560 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4561 ExpectSyncedAndCreated();
4563 // Add a delete from the server.
4564 sync_pb::SyncEntity* update1 = mock_server_->AddUpdateFromLastCommit();
4565 update1->set_originator_cache_guid(local_cache_guid());
4566 update1->set_originator_client_item_id(local_id_.GetServerId());
4567 SyncShareNudge();
4568 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4569 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4570 ExpectSyncedAndCreated();
4572 // Some other client deletes the item.
4573 mock_server_->AddUpdateTombstone(Get(metahandle_, ID));
4574 SyncShareNudge();
4576 // The update ought to have applied successfully.
4577 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4578 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4579 ExpectSyncedAndDeleted();
4581 // Undelete it locally.
4582 Undelete();
4583 ExpectUnsyncedUndeletion();
4584 SyncShareNudge();
4585 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4586 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4587 ExpectSyncedAndCreated();
4589 // Now, encounter a GetUpdates corresponding to the just-committed
4590 // deletion update. The undeletion should prevail.
4591 sync_pb::SyncEntity* update2 = mock_server_->AddUpdateFromLastCommit();
4592 update2->set_originator_cache_guid(local_cache_guid());
4593 update2->set_originator_client_item_id(local_id_.GetServerId());
4594 SyncShareNudge();
4595 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4596 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4597 ExpectSyncedAndCreated();
4600 TEST_F(SyncerUndeletionTest, UndeleteAfterOtherClientDeletesImmediately) {
4601 Create();
4602 ExpectUnsyncedCreation();
4603 SyncShareNudge();
4605 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4606 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4607 ExpectSyncedAndCreated();
4609 // Some other client deletes the item before we get a chance
4610 // to GetUpdates our original request.
4611 mock_server_->AddUpdateTombstone(Get(metahandle_, ID));
4612 SyncShareNudge();
4614 // The update ought to have applied successfully.
4615 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4616 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4617 ExpectSyncedAndDeleted();
4619 // Undelete it locally.
4620 Undelete();
4621 ExpectUnsyncedUndeletion();
4622 SyncShareNudge();
4623 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4624 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4625 ExpectSyncedAndCreated();
4627 // Now, encounter a GetUpdates corresponding to the just-committed
4628 // deletion update. The undeletion should prevail.
4629 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
4630 update->set_originator_cache_guid(local_cache_guid());
4631 update->set_originator_client_item_id(local_id_.GetServerId());
4632 SyncShareNudge();
4633 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4634 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4635 ExpectSyncedAndCreated();
4638 TEST_F(SyncerUndeletionTest, OtherClientUndeletes) {
4639 Create();
4640 ExpectUnsyncedCreation();
4641 SyncShareNudge();
4643 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4644 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4645 ExpectSyncedAndCreated();
4647 // Get the updates of our just-committed entry.
4648 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
4649 update->set_originator_cache_guid(local_cache_guid());
4650 update->set_originator_client_item_id(local_id_.GetServerId());
4651 SyncShareNudge();
4652 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4653 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4654 ExpectSyncedAndCreated();
4656 // We delete the item.
4657 Delete();
4658 ExpectUnsyncedDeletion();
4659 SyncShareNudge();
4661 // The update ought to have applied successfully.
4662 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4663 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4664 ExpectSyncedAndDeleted();
4666 // Now, encounter a GetUpdates corresponding to the just-committed
4667 // deletion update.
4668 mock_server_->AddUpdateFromLastCommit();
4669 SyncShareNudge();
4670 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4671 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4672 ExpectSyncedAndDeleted();
4674 // Some other client undeletes the item.
4675 mock_server_->AddUpdateBookmark(Get(metahandle_, ID),
4676 Get(metahandle_, PARENT_ID),
4677 "Thadeusz", 100, 1000,
4678 local_cache_guid(), local_id_.GetServerId());
4679 mock_server_->SetLastUpdateClientTag(client_tag_);
4680 SyncShareNudge();
4681 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4682 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4683 ExpectSyncedAndCreated();
4684 EXPECT_EQ("Thadeusz", Get(metahandle_, NON_UNIQUE_NAME));
4687 TEST_F(SyncerUndeletionTest, OtherClientUndeletesImmediately) {
4688 Create();
4689 ExpectUnsyncedCreation();
4690 SyncShareNudge();
4692 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4693 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4694 ExpectSyncedAndCreated();
4696 // Get the updates of our just-committed entry.
4697 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
4698 update->set_originator_cache_guid(local_cache_guid());
4699 update->set_originator_client_item_id(local_id_.GetServerId());
4700 SyncShareNudge();
4701 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4702 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4703 ExpectSyncedAndCreated();
4705 // We delete the item.
4706 Delete();
4707 ExpectUnsyncedDeletion();
4708 SyncShareNudge();
4710 // The update ought to have applied successfully.
4711 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4712 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4713 ExpectSyncedAndDeleted();
4715 // Some other client undeletes before we see the update from our
4716 // commit.
4717 mock_server_->AddUpdateBookmark(Get(metahandle_, ID),
4718 Get(metahandle_, PARENT_ID),
4719 "Thadeusz", 100, 1000,
4720 local_cache_guid(), local_id_.GetServerId());
4721 mock_server_->SetLastUpdateClientTag(client_tag_);
4722 SyncShareNudge();
4723 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4724 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4725 ExpectSyncedAndCreated();
4726 EXPECT_EQ("Thadeusz", Get(metahandle_, NON_UNIQUE_NAME));
4729 enum {
4730 TEST_PARAM_BOOKMARK_ENABLE_BIT,
4731 TEST_PARAM_AUTOFILL_ENABLE_BIT,
4732 TEST_PARAM_BIT_COUNT
4735 class MixedResult :
4736 public SyncerTest,
4737 public ::testing::WithParamInterface<int> {
4738 protected:
4739 bool ShouldFailBookmarkCommit() {
4740 return (GetParam() & (1 << TEST_PARAM_BOOKMARK_ENABLE_BIT)) == 0;
4742 bool ShouldFailAutofillCommit() {
4743 return (GetParam() & (1 << TEST_PARAM_AUTOFILL_ENABLE_BIT)) == 0;
4747 INSTANTIATE_TEST_CASE_P(ExtensionsActivity,
4748 MixedResult,
4749 testing::Range(0, 1 << TEST_PARAM_BIT_COUNT));
4751 TEST_P(MixedResult, ExtensionsActivity) {
4753 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
4755 MutableEntry pref(&wtrans, CREATE, PREFERENCES, wtrans.root_id(), "pref");
4756 ASSERT_TRUE(pref.good());
4757 pref.Put(syncable::IS_UNSYNCED, true);
4759 MutableEntry bookmark(
4760 &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "bookmark");
4761 ASSERT_TRUE(bookmark.good());
4762 bookmark.Put(syncable::IS_UNSYNCED, true);
4764 if (ShouldFailBookmarkCommit()) {
4765 mock_server_->SetTransientErrorId(bookmark.Get(ID));
4768 if (ShouldFailAutofillCommit()) {
4769 mock_server_->SetTransientErrorId(pref.Get(ID));
4774 // Put some extenions activity records into the monitor.
4776 ExtensionsActivityMonitor::Records records;
4777 records["ABC"].extension_id = "ABC";
4778 records["ABC"].bookmark_write_count = 2049U;
4779 records["xyz"].extension_id = "xyz";
4780 records["xyz"].bookmark_write_count = 4U;
4781 context_->extensions_monitor()->PutRecords(records);
4784 SyncShareNudge();
4786 ExtensionsActivityMonitor::Records final_monitor_records;
4787 context_->extensions_monitor()->GetAndClearRecords(&final_monitor_records);
4788 if (ShouldFailBookmarkCommit()) {
4789 ASSERT_EQ(2U, final_monitor_records.size())
4790 << "Should restore records after unsuccessful bookmark commit.";
4791 EXPECT_EQ("ABC", final_monitor_records["ABC"].extension_id);
4792 EXPECT_EQ("xyz", final_monitor_records["xyz"].extension_id);
4793 EXPECT_EQ(2049U, final_monitor_records["ABC"].bookmark_write_count);
4794 EXPECT_EQ(4U, final_monitor_records["xyz"].bookmark_write_count);
4795 } else {
4796 EXPECT_TRUE(final_monitor_records.empty())
4797 << "Should not restore records after successful bookmark commit.";
4801 } // namespace syncer