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