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