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