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