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