• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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