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