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