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