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