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