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