syncer_unittest.cc revision 68043e1e95eeb07d5cae7aca370b26518b0867d6
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
3252TEST_F(SyncerTest, DirectoryUpdateTest) {
3253  Id in_root_id = ids_.NewServerId();
3254  Id in_in_root_id = ids_.NewServerId();
3255
3256  mock_server_->AddUpdateDirectory(in_root_id, TestIdFactory::root(),
3257                                   "in_root_name", 2, 2,
3258                                   foreign_cache_guid(), "-1");
3259  mock_server_->AddUpdateDirectory(in_in_root_id, in_root_id,
3260                                   "in_in_root_name", 3, 3,
3261                                   foreign_cache_guid(), "-2");
3262  SyncShareNudge();
3263  {
3264    syncable::ReadTransaction trans(FROM_HERE, directory());
3265    Entry in_root(&trans, GET_BY_ID, in_root_id);
3266    ASSERT_TRUE(in_root.good());
3267    EXPECT_EQ("in_root_name", in_root.GetNonUniqueName());
3268    EXPECT_EQ(TestIdFactory::root(), in_root.GetParentId());
3269
3270    Entry in_in_root(&trans, GET_BY_ID, in_in_root_id);
3271    ASSERT_TRUE(in_in_root.good());
3272    EXPECT_EQ("in_in_root_name", in_in_root.GetNonUniqueName());
3273    EXPECT_EQ(in_root_id, in_in_root.GetParentId());
3274  }
3275}
3276
3277TEST_F(SyncerTest, DirectoryCommitTest) {
3278  syncable::Id in_root_id, in_dir_id;
3279  int64 foo_metahandle;
3280  int64 bar_metahandle;
3281
3282  {
3283    WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3284    MutableEntry parent(&wtrans, CREATE, BOOKMARKS, root_id_, "foo");
3285    ASSERT_TRUE(parent.good());
3286    parent.PutIsUnsynced(true);
3287    parent.PutIsDir(true);
3288    parent.PutSpecifics(DefaultBookmarkSpecifics());
3289    in_root_id = parent.GetId();
3290    foo_metahandle = parent.GetMetahandle();
3291
3292    MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent.GetId(), "bar");
3293    ASSERT_TRUE(child.good());
3294    child.PutIsUnsynced(true);
3295    child.PutIsDir(true);
3296    child.PutSpecifics(DefaultBookmarkSpecifics());
3297    bar_metahandle = child.GetMetahandle();
3298    in_dir_id = parent.GetId();
3299  }
3300  SyncShareNudge();
3301  {
3302    syncable::ReadTransaction trans(FROM_HERE, directory());
3303    Entry fail_by_old_id_entry(&trans, GET_BY_ID, in_root_id);
3304    ASSERT_FALSE(fail_by_old_id_entry.good());
3305
3306    Entry foo_entry(&trans, GET_BY_HANDLE, foo_metahandle);
3307    ASSERT_TRUE(foo_entry.good());
3308    EXPECT_EQ("foo", foo_entry.GetNonUniqueName());
3309    EXPECT_NE(foo_entry.GetId(), in_root_id);
3310
3311    Entry bar_entry(&trans, GET_BY_HANDLE, bar_metahandle);
3312    ASSERT_TRUE(bar_entry.good());
3313    EXPECT_EQ("bar", bar_entry.GetNonUniqueName());
3314    EXPECT_NE(bar_entry.GetId(), in_dir_id);
3315    EXPECT_EQ(foo_entry.GetId(), bar_entry.GetParentId());
3316  }
3317}
3318
3319TEST_F(SyncerTest, TestClientCommandDuringUpdate) {
3320  using sync_pb::ClientCommand;
3321
3322  ClientCommand* command = new ClientCommand();
3323  command->set_set_sync_poll_interval(8);
3324  command->set_set_sync_long_poll_interval(800);
3325  command->set_sessions_commit_delay_seconds(3141);
3326  command->set_client_invalidation_hint_buffer_size(11);
3327  mock_server_->AddUpdateDirectory(1, 0, "in_root", 1, 1,
3328                                   foreign_cache_guid(), "-1");
3329  mock_server_->SetGUClientCommand(command);
3330  SyncShareNudge();
3331
3332  EXPECT_TRUE(TimeDelta::FromSeconds(8) ==
3333              last_short_poll_interval_received_);
3334  EXPECT_TRUE(TimeDelta::FromSeconds(800) ==
3335              last_long_poll_interval_received_);
3336  EXPECT_TRUE(TimeDelta::FromSeconds(3141) ==
3337              last_sessions_commit_delay_seconds_);
3338  EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_);
3339
3340  command = new ClientCommand();
3341  command->set_set_sync_poll_interval(180);
3342  command->set_set_sync_long_poll_interval(190);
3343  command->set_sessions_commit_delay_seconds(2718);
3344  command->set_client_invalidation_hint_buffer_size(9);
3345  mock_server_->AddUpdateDirectory(1, 0, "in_root", 1, 1,
3346                                   foreign_cache_guid(), "-1");
3347  mock_server_->SetGUClientCommand(command);
3348  SyncShareNudge();
3349
3350  EXPECT_TRUE(TimeDelta::FromSeconds(180) ==
3351              last_short_poll_interval_received_);
3352  EXPECT_TRUE(TimeDelta::FromSeconds(190) ==
3353              last_long_poll_interval_received_);
3354  EXPECT_TRUE(TimeDelta::FromSeconds(2718) ==
3355              last_sessions_commit_delay_seconds_);
3356  EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_);
3357}
3358
3359TEST_F(SyncerTest, TestClientCommandDuringCommit) {
3360  using sync_pb::ClientCommand;
3361
3362  ClientCommand* command = new ClientCommand();
3363  command->set_set_sync_poll_interval(8);
3364  command->set_set_sync_long_poll_interval(800);
3365  command->set_sessions_commit_delay_seconds(3141);
3366  command->set_client_invalidation_hint_buffer_size(11);
3367  CreateUnsyncedDirectory("X", "id_X");
3368  mock_server_->SetCommitClientCommand(command);
3369  SyncShareNudge();
3370
3371  EXPECT_TRUE(TimeDelta::FromSeconds(8) ==
3372              last_short_poll_interval_received_);
3373  EXPECT_TRUE(TimeDelta::FromSeconds(800) ==
3374              last_long_poll_interval_received_);
3375  EXPECT_TRUE(TimeDelta::FromSeconds(3141) ==
3376              last_sessions_commit_delay_seconds_);
3377  EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_);
3378
3379  command = new ClientCommand();
3380  command->set_set_sync_poll_interval(180);
3381  command->set_set_sync_long_poll_interval(190);
3382  command->set_sessions_commit_delay_seconds(2718);
3383  command->set_client_invalidation_hint_buffer_size(9);
3384  CreateUnsyncedDirectory("Y", "id_Y");
3385  mock_server_->SetCommitClientCommand(command);
3386  SyncShareNudge();
3387
3388  EXPECT_TRUE(TimeDelta::FromSeconds(180) ==
3389              last_short_poll_interval_received_);
3390  EXPECT_TRUE(TimeDelta::FromSeconds(190) ==
3391              last_long_poll_interval_received_);
3392  EXPECT_TRUE(TimeDelta::FromSeconds(2718) ==
3393              last_sessions_commit_delay_seconds_);
3394  EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_);
3395}
3396
3397TEST_F(SyncerTest, EnsureWeSendUpOldParent) {
3398  syncable::Id folder_one_id = ids_.FromNumber(1);
3399  syncable::Id folder_two_id = ids_.FromNumber(2);
3400
3401  mock_server_->AddUpdateDirectory(folder_one_id, TestIdFactory::root(),
3402      "folder_one", 1, 1, foreign_cache_guid(), "-1");
3403  mock_server_->AddUpdateDirectory(folder_two_id, TestIdFactory::root(),
3404      "folder_two", 1, 1, foreign_cache_guid(), "-2");
3405  SyncShareNudge();
3406  {
3407    // A moved entry should send an "old parent."
3408    WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3409    MutableEntry entry(&trans, GET_BY_ID, folder_one_id);
3410    ASSERT_TRUE(entry.good());
3411    entry.PutParentId(folder_two_id);
3412    entry.PutIsUnsynced(true);
3413    // A new entry should send no "old parent."
3414    MutableEntry create(
3415        &trans, CREATE, BOOKMARKS, trans.root_id(), "new_folder");
3416    create.PutIsUnsynced(true);
3417    create.PutSpecifics(DefaultBookmarkSpecifics());
3418  }
3419  SyncShareNudge();
3420  const sync_pb::CommitMessage& commit = mock_server_->last_sent_commit();
3421  ASSERT_EQ(2, commit.entries_size());
3422  EXPECT_TRUE(commit.entries(0).parent_id_string() == "2");
3423  EXPECT_TRUE(commit.entries(0).old_parent_id() == "0");
3424  EXPECT_FALSE(commit.entries(1).has_old_parent_id());
3425}
3426
3427TEST_F(SyncerTest, Test64BitVersionSupport) {
3428  int64 really_big_int = std::numeric_limits<int64>::max() - 12;
3429  const string name("ringo's dang orang ran rings around my o-ring");
3430  int64 item_metahandle;
3431
3432  // Try writing max int64 to the version fields of a meta entry.
3433  {
3434    WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3435    MutableEntry entry(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), name);
3436    ASSERT_TRUE(entry.good());
3437    entry.PutBaseVersion(really_big_int);
3438    entry.PutServerVersion(really_big_int);
3439    entry.PutId(ids_.NewServerId());
3440    item_metahandle = entry.GetMetahandle();
3441  }
3442  // Now read it back out and make sure the value is max int64.
3443  syncable::ReadTransaction rtrans(FROM_HERE, directory());
3444  Entry entry(&rtrans, syncable::GET_BY_HANDLE, item_metahandle);
3445  ASSERT_TRUE(entry.good());
3446  EXPECT_TRUE(really_big_int == entry.GetBaseVersion());
3447}
3448
3449TEST_F(SyncerTest, TestSimpleUndelete) {
3450  Id id = ids_.MakeServer("undeletion item"), root = TestIdFactory::root();
3451  mock_server_->set_conflict_all_commits(true);
3452  // Let there be an entry from the server.
3453  mock_server_->AddUpdateBookmark(id, root, "foo", 1, 10,
3454                                  foreign_cache_guid(), "-1");
3455  SyncShareNudge();
3456  // Check it out and delete it.
3457  {
3458    WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3459    MutableEntry entry(&wtrans, GET_BY_ID, id);
3460    ASSERT_TRUE(entry.good());
3461    EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3462    EXPECT_FALSE(entry.GetIsUnsynced());
3463    EXPECT_FALSE(entry.GetIsDel());
3464    // Delete it locally.
3465    entry.PutIsDel(true);
3466  }
3467  SyncShareNudge();
3468  // Confirm we see IS_DEL and not SERVER_IS_DEL.
3469  {
3470    syncable::ReadTransaction trans(FROM_HERE, directory());
3471    Entry entry(&trans, GET_BY_ID, id);
3472    ASSERT_TRUE(entry.good());
3473    EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3474    EXPECT_FALSE(entry.GetIsUnsynced());
3475    EXPECT_TRUE(entry.GetIsDel());
3476    EXPECT_FALSE(entry.GetServerIsDel());
3477  }
3478  SyncShareNudge();
3479  // Update from server confirming deletion.
3480  mock_server_->AddUpdateBookmark(id, root, "foo", 2, 11,
3481                                  foreign_cache_guid(), "-1");
3482  mock_server_->SetLastUpdateDeleted();
3483  SyncShareNudge();
3484  // IS_DEL AND SERVER_IS_DEL now both true.
3485  {
3486    syncable::ReadTransaction trans(FROM_HERE, directory());
3487    Entry entry(&trans, GET_BY_ID, id);
3488    ASSERT_TRUE(entry.good());
3489    EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3490    EXPECT_FALSE(entry.GetIsUnsynced());
3491    EXPECT_TRUE(entry.GetIsDel());
3492    EXPECT_TRUE(entry.GetServerIsDel());
3493  }
3494  // Undelete from server.
3495  mock_server_->AddUpdateBookmark(id, root, "foo", 2, 12,
3496                                  foreign_cache_guid(), "-1");
3497  SyncShareNudge();
3498  // IS_DEL and SERVER_IS_DEL now both false.
3499  {
3500    syncable::ReadTransaction trans(FROM_HERE, directory());
3501    Entry entry(&trans, GET_BY_ID, id);
3502    ASSERT_TRUE(entry.good());
3503    EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3504    EXPECT_FALSE(entry.GetIsUnsynced());
3505    EXPECT_FALSE(entry.GetIsDel());
3506    EXPECT_FALSE(entry.GetServerIsDel());
3507  }
3508}
3509
3510TEST_F(SyncerTest, TestUndeleteWithMissingDeleteUpdate) {
3511  Id id = ids_.MakeServer("undeletion item"), root = TestIdFactory::root();
3512  // Let there be a entry, from the server.
3513  mock_server_->set_conflict_all_commits(true);
3514  mock_server_->AddUpdateBookmark(id, root, "foo", 1, 10,
3515                                  foreign_cache_guid(), "-1");
3516  SyncShareNudge();
3517  // Check it out and delete it.
3518  {
3519    WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3520    MutableEntry entry(&wtrans, GET_BY_ID, id);
3521    ASSERT_TRUE(entry.good());
3522    EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3523    EXPECT_FALSE(entry.GetIsUnsynced());
3524    EXPECT_FALSE(entry.GetIsDel());
3525    // Delete it locally.
3526    entry.PutIsDel(true);
3527  }
3528  SyncShareNudge();
3529  // Confirm we see IS_DEL and not SERVER_IS_DEL.
3530  {
3531    syncable::ReadTransaction trans(FROM_HERE, directory());
3532    Entry entry(&trans, GET_BY_ID, id);
3533    ASSERT_TRUE(entry.good());
3534    EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3535    EXPECT_FALSE(entry.GetIsUnsynced());
3536    EXPECT_TRUE(entry.GetIsDel());
3537    EXPECT_FALSE(entry.GetServerIsDel());
3538  }
3539  SyncShareNudge();
3540  // Say we do not get an update from server confirming deletion. Undelete
3541  // from server
3542  mock_server_->AddUpdateBookmark(id, root, "foo", 2, 12,
3543                                  foreign_cache_guid(), "-1");
3544  SyncShareNudge();
3545  // IS_DEL and SERVER_IS_DEL now both false.
3546  {
3547    syncable::ReadTransaction trans(FROM_HERE, directory());
3548    Entry entry(&trans, GET_BY_ID, id);
3549    ASSERT_TRUE(entry.good());
3550    EXPECT_FALSE(entry.GetIsUnappliedUpdate());
3551    EXPECT_FALSE(entry.GetIsUnsynced());
3552    EXPECT_FALSE(entry.GetIsDel());
3553    EXPECT_FALSE(entry.GetServerIsDel());
3554  }
3555}
3556
3557TEST_F(SyncerTest, TestUndeleteIgnoreCorrectlyUnappliedUpdate) {
3558  Id id1 = ids_.MakeServer("first"), id2 = ids_.MakeServer("second");
3559  Id root = TestIdFactory::root();
3560  // Duplicate! expect path clashing!
3561  mock_server_->set_conflict_all_commits(true);
3562  mock_server_->AddUpdateBookmark(id1, root, "foo", 1, 10,
3563                                  foreign_cache_guid(), "-1");
3564  mock_server_->AddUpdateBookmark(id2, root, "foo", 1, 10,
3565                                  foreign_cache_guid(), "-2");
3566  SyncShareNudge();
3567  mock_server_->AddUpdateBookmark(id2, root, "foo2", 2, 20,
3568                                  foreign_cache_guid(), "-2");
3569  SyncShareNudge();  // Now just don't explode.
3570}
3571
3572TEST_F(SyncerTest, ClientTagServerCreatedUpdatesWork) {
3573  mock_server_->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
3574                                   foreign_cache_guid(), "-1");
3575  mock_server_->SetLastUpdateClientTag("permfolder");
3576
3577  SyncShareNudge();
3578
3579  {
3580    syncable::ReadTransaction trans(FROM_HERE, directory());
3581    Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
3582    ASSERT_TRUE(perm_folder.good());
3583    EXPECT_FALSE(perm_folder.GetIsDel());
3584    EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate());
3585    EXPECT_FALSE(perm_folder.GetIsUnsynced());
3586    EXPECT_EQ(perm_folder.GetUniqueClientTag(), "permfolder");
3587    EXPECT_EQ(perm_folder.GetNonUniqueName(), "permitem1");
3588  }
3589
3590  mock_server_->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
3591                                   foreign_cache_guid(), "-1");
3592  mock_server_->SetLastUpdateClientTag("permfolder");
3593  SyncShareNudge();
3594
3595  {
3596    syncable::ReadTransaction trans(FROM_HERE, directory());
3597
3598    Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
3599    ASSERT_TRUE(perm_folder.good());
3600    EXPECT_FALSE(perm_folder.GetIsDel());
3601    EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate());
3602    EXPECT_FALSE(perm_folder.GetIsUnsynced());
3603    EXPECT_EQ(perm_folder.GetUniqueClientTag(), "permfolder");
3604    EXPECT_EQ(perm_folder.GetNonUniqueName(), "permitem_renamed");
3605  }
3606}
3607
3608TEST_F(SyncerTest, ClientTagIllegalUpdateIgnored) {
3609  mock_server_->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
3610                                   foreign_cache_guid(), "-1");
3611  mock_server_->SetLastUpdateClientTag("permfolder");
3612
3613  SyncShareNudge();
3614
3615  {
3616    syncable::ReadTransaction trans(FROM_HERE, directory());
3617    Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
3618    ASSERT_TRUE(perm_folder.good());
3619    EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate());
3620    EXPECT_FALSE(perm_folder.GetIsUnsynced());
3621    EXPECT_EQ(perm_folder.GetUniqueClientTag(), "permfolder");
3622    EXPECT_TRUE(perm_folder.GetNonUniqueName()== "permitem1");
3623    EXPECT_TRUE(perm_folder.GetId().ServerKnows());
3624  }
3625
3626  mock_server_->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
3627                                   foreign_cache_guid(), "-1");
3628  mock_server_->SetLastUpdateClientTag("wrongtag");
3629  SyncShareNudge();
3630
3631  {
3632    syncable::ReadTransaction trans(FROM_HERE, directory());
3633
3634    // This update is rejected because it has the same ID, but a
3635    // different tag than one that is already on the client.
3636    // The client has a ServerKnows ID, which cannot be overwritten.
3637    Entry rejected_update(&trans, GET_BY_CLIENT_TAG, "wrongtag");
3638    EXPECT_FALSE(rejected_update.good());
3639
3640    Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
3641    ASSERT_TRUE(perm_folder.good());
3642    EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate());
3643    EXPECT_FALSE(perm_folder.GetIsUnsynced());
3644    EXPECT_EQ(perm_folder.GetNonUniqueName(), "permitem1");
3645  }
3646}
3647
3648TEST_F(SyncerTest, ClientTagUncommittedTagMatchesUpdate) {
3649  int64 original_metahandle = 0;
3650
3651  {
3652    WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3653    MutableEntry pref(
3654        &trans, CREATE, PREFERENCES, ids_.root(), "name");
3655    ASSERT_TRUE(pref.good());
3656    pref.PutUniqueClientTag("tag");
3657    pref.PutIsUnsynced(true);
3658    EXPECT_FALSE(pref.GetIsUnappliedUpdate());
3659    EXPECT_FALSE(pref.GetId().ServerKnows());
3660    original_metahandle = pref.GetMetahandle();
3661  }
3662
3663  syncable::Id server_id = TestIdFactory::MakeServer("id");
3664  mock_server_->AddUpdatePref(server_id.GetServerId(),
3665                              ids_.root().GetServerId(),
3666                              "tag", 10, 100);
3667  mock_server_->set_conflict_all_commits(true);
3668
3669  SyncShareNudge();
3670  // This should cause client tag reunion, preserving the metahandle.
3671  {
3672    syncable::ReadTransaction trans(FROM_HERE, directory());
3673
3674    Entry pref(&trans, GET_BY_CLIENT_TAG, "tag");
3675    ASSERT_TRUE(pref.good());
3676    EXPECT_FALSE(pref.GetIsDel());
3677    EXPECT_FALSE(pref.GetIsUnappliedUpdate());
3678    EXPECT_TRUE(pref.GetIsUnsynced());
3679    EXPECT_EQ(10, pref.GetBaseVersion());
3680    // Entry should have been given the new ID while preserving the
3681    // metahandle; client should have won the conflict resolution.
3682    EXPECT_EQ(original_metahandle, pref.GetMetahandle());
3683    EXPECT_EQ("tag", pref.GetUniqueClientTag());
3684    EXPECT_TRUE(pref.GetId().ServerKnows());
3685  }
3686
3687  mock_server_->set_conflict_all_commits(false);
3688  SyncShareNudge();
3689
3690  // The resolved entry ought to commit cleanly.
3691  {
3692    syncable::ReadTransaction trans(FROM_HERE, directory());
3693
3694    Entry pref(&trans, GET_BY_CLIENT_TAG, "tag");
3695    ASSERT_TRUE(pref.good());
3696    EXPECT_FALSE(pref.GetIsDel());
3697    EXPECT_FALSE(pref.GetIsUnappliedUpdate());
3698    EXPECT_FALSE(pref.GetIsUnsynced());
3699    EXPECT_TRUE(10 < pref.GetBaseVersion());
3700    // Entry should have been given the new ID while preserving the
3701    // metahandle; client should have won the conflict resolution.
3702    EXPECT_EQ(original_metahandle, pref.GetMetahandle());
3703    EXPECT_EQ("tag", pref.GetUniqueClientTag());
3704    EXPECT_TRUE(pref.GetId().ServerKnows());
3705  }
3706}
3707
3708TEST_F(SyncerTest, ClientTagConflictWithDeletedLocalEntry) {
3709  {
3710    // Create a deleted local entry with a unique client tag.
3711    WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3712    MutableEntry pref(
3713        &trans, CREATE, PREFERENCES, ids_.root(), "name");
3714    ASSERT_TRUE(pref.good());
3715    ASSERT_FALSE(pref.GetId().ServerKnows());
3716    pref.PutUniqueClientTag("tag");
3717    pref.PutIsUnsynced(true);
3718
3719    // Note: IS_DEL && !ServerKnows() will clear the UNSYNCED bit.
3720    // (We never attempt to commit server-unknown deleted items, so this
3721    // helps us clean up those entries).
3722    pref.PutIsDel(true);
3723  }
3724
3725  // Prepare an update with the same unique client tag.
3726  syncable::Id server_id = TestIdFactory::MakeServer("id");
3727  mock_server_->AddUpdatePref(server_id.GetServerId(),
3728                              ids_.root().GetServerId(),
3729                              "tag", 10, 100);
3730
3731  SyncShareNudge();
3732  // The local entry will be overwritten.
3733  {
3734    syncable::ReadTransaction trans(FROM_HERE, directory());
3735
3736    Entry pref(&trans, GET_BY_CLIENT_TAG, "tag");
3737    ASSERT_TRUE(pref.good());
3738    ASSERT_TRUE(pref.GetId().ServerKnows());
3739    EXPECT_FALSE(pref.GetIsDel());
3740    EXPECT_FALSE(pref.GetIsUnappliedUpdate());
3741    EXPECT_FALSE(pref.GetIsUnsynced());
3742    EXPECT_EQ(pref.GetBaseVersion(), 10);
3743    EXPECT_EQ(pref.GetUniqueClientTag(), "tag");
3744  }
3745}
3746
3747TEST_F(SyncerTest, ClientTagUpdateClashesWithLocalEntry) {
3748  // This test is written assuming that ID comparison
3749  // will work out in a particular way.
3750  EXPECT_TRUE(ids_.FromNumber(1) < ids_.FromNumber(2));
3751  EXPECT_TRUE(ids_.FromNumber(3) < ids_.FromNumber(4));
3752
3753  syncable::Id id1 = TestIdFactory::MakeServer("1");
3754  mock_server_->AddUpdatePref(id1.GetServerId(), ids_.root().GetServerId(),
3755                              "tag1", 10, 100);
3756
3757  syncable::Id id4 = TestIdFactory::MakeServer("4");
3758  mock_server_->AddUpdatePref(id4.GetServerId(), ids_.root().GetServerId(),
3759                              "tag2", 11, 110);
3760
3761  mock_server_->set_conflict_all_commits(true);
3762
3763  SyncShareNudge();
3764  int64 tag1_metahandle = syncable::kInvalidMetaHandle;
3765  int64 tag2_metahandle = syncable::kInvalidMetaHandle;
3766  // This should cause client tag overwrite.
3767  {
3768    syncable::ReadTransaction trans(FROM_HERE, directory());
3769
3770    Entry tag1(&trans, GET_BY_CLIENT_TAG, "tag1");
3771    ASSERT_TRUE(tag1.good());
3772    ASSERT_TRUE(tag1.GetId().ServerKnows());
3773    ASSERT_TRUE(id1 == tag1.GetId());
3774    EXPECT_FALSE(tag1.GetIsDel());
3775    EXPECT_FALSE(tag1.GetIsUnappliedUpdate());
3776    EXPECT_FALSE(tag1.GetIsUnsynced());
3777    EXPECT_EQ(10, tag1.GetBaseVersion());
3778    EXPECT_EQ("tag1", tag1.GetUniqueClientTag());
3779    tag1_metahandle = tag1.GetMetahandle();
3780
3781    Entry tag2(&trans, GET_BY_CLIENT_TAG, "tag2");
3782    ASSERT_TRUE(tag2.good());
3783    ASSERT_TRUE(tag2.GetId().ServerKnows());
3784    ASSERT_TRUE(id4 == tag2.GetId());
3785    EXPECT_FALSE(tag2.GetIsDel());
3786    EXPECT_FALSE(tag2.GetIsUnappliedUpdate());
3787    EXPECT_FALSE(tag2.GetIsUnsynced());
3788    EXPECT_EQ(11, tag2.GetBaseVersion());
3789    EXPECT_EQ("tag2", tag2.GetUniqueClientTag());
3790    tag2_metahandle = tag2.GetMetahandle();
3791
3792    syncable::Directory::Metahandles children;
3793    directory()->GetChildHandlesById(&trans, trans.root_id(), &children);
3794    ASSERT_EQ(2U, children.size());
3795  }
3796
3797  syncable::Id id2 = TestIdFactory::MakeServer("2");
3798  mock_server_->AddUpdatePref(id2.GetServerId(), ids_.root().GetServerId(),
3799                              "tag1", 12, 120);
3800  syncable::Id id3 = TestIdFactory::MakeServer("3");
3801  mock_server_->AddUpdatePref(id3.GetServerId(), ids_.root().GetServerId(),
3802                              "tag2", 13, 130);
3803  SyncShareNudge();
3804
3805  {
3806    syncable::ReadTransaction trans(FROM_HERE, directory());
3807
3808    Entry tag1(&trans, GET_BY_CLIENT_TAG, "tag1");
3809    ASSERT_TRUE(tag1.good());
3810    ASSERT_TRUE(tag1.GetId().ServerKnows());
3811    ASSERT_EQ(id1, tag1.GetId())
3812        << "ID 1 should be kept, since it was less than ID 2.";
3813    EXPECT_FALSE(tag1.GetIsDel());
3814    EXPECT_FALSE(tag1.GetIsUnappliedUpdate());
3815    EXPECT_FALSE(tag1.GetIsUnsynced());
3816    EXPECT_EQ(10, tag1.GetBaseVersion());
3817    EXPECT_EQ("tag1", tag1.GetUniqueClientTag());
3818    EXPECT_EQ(tag1_metahandle, tag1.GetMetahandle());
3819
3820    Entry tag2(&trans, GET_BY_CLIENT_TAG, "tag2");
3821    ASSERT_TRUE(tag2.good());
3822    ASSERT_TRUE(tag2.GetId().ServerKnows());
3823    ASSERT_EQ(id3, tag2.GetId())
3824        << "ID 3 should be kept, since it was less than ID 4.";
3825    EXPECT_FALSE(tag2.GetIsDel());
3826    EXPECT_FALSE(tag2.GetIsUnappliedUpdate());
3827    EXPECT_FALSE(tag2.GetIsUnsynced());
3828    EXPECT_EQ(13, tag2.GetBaseVersion());
3829    EXPECT_EQ("tag2", tag2.GetUniqueClientTag());
3830    EXPECT_EQ(tag2_metahandle, tag2.GetMetahandle());
3831
3832    syncable::Directory::Metahandles children;
3833    directory()->GetChildHandlesById(&trans, trans.root_id(), &children);
3834    ASSERT_EQ(2U, children.size());
3835  }
3836}
3837
3838TEST_F(SyncerTest, ClientTagClashWithinBatchOfUpdates) {
3839  // This test is written assuming that ID comparison
3840  // will work out in a particular way.
3841  EXPECT_TRUE(ids_.FromNumber(1) < ids_.FromNumber(4));
3842  EXPECT_TRUE(ids_.FromNumber(201) < ids_.FromNumber(205));
3843
3844  // Least ID: winner.
3845  mock_server_->AddUpdatePref(ids_.FromNumber(1).GetServerId(),
3846                              ids_.root().GetServerId(), "tag a", 1, 10);
3847  mock_server_->AddUpdatePref(ids_.FromNumber(2).GetServerId(),
3848                              ids_.root().GetServerId(), "tag a", 11, 110);
3849  mock_server_->AddUpdatePref(ids_.FromNumber(3).GetServerId(),
3850                              ids_.root().GetServerId(), "tag a", 12, 120);
3851  mock_server_->AddUpdatePref(ids_.FromNumber(4).GetServerId(),
3852                              ids_.root().GetServerId(), "tag a", 13, 130);
3853
3854  mock_server_->AddUpdatePref(ids_.FromNumber(105).GetServerId(),
3855                              ids_.root().GetServerId(), "tag b", 14, 140);
3856  mock_server_->AddUpdatePref(ids_.FromNumber(102).GetServerId(),
3857                              ids_.root().GetServerId(), "tag b", 15, 150);
3858  // Least ID: winner.
3859  mock_server_->AddUpdatePref(ids_.FromNumber(101).GetServerId(),
3860                              ids_.root().GetServerId(), "tag b", 16, 160);
3861  mock_server_->AddUpdatePref(ids_.FromNumber(104).GetServerId(),
3862                              ids_.root().GetServerId(), "tag b", 17, 170);
3863
3864  mock_server_->AddUpdatePref(ids_.FromNumber(205).GetServerId(),
3865                              ids_.root().GetServerId(), "tag c", 18, 180);
3866  mock_server_->AddUpdatePref(ids_.FromNumber(202).GetServerId(),
3867                              ids_.root().GetServerId(), "tag c", 19, 190);
3868  mock_server_->AddUpdatePref(ids_.FromNumber(204).GetServerId(),
3869                              ids_.root().GetServerId(), "tag c", 20, 200);
3870  // Least ID: winner.
3871  mock_server_->AddUpdatePref(ids_.FromNumber(201).GetServerId(),
3872                              ids_.root().GetServerId(), "tag c", 21, 210);
3873
3874  mock_server_->set_conflict_all_commits(true);
3875
3876  SyncShareNudge();
3877  // This should cause client tag overwrite.
3878  {
3879    syncable::ReadTransaction trans(FROM_HERE, directory());
3880
3881    Entry tag_a(&trans, GET_BY_CLIENT_TAG, "tag a");
3882    ASSERT_TRUE(tag_a.good());
3883    EXPECT_TRUE(tag_a.GetId().ServerKnows());
3884    EXPECT_EQ(ids_.FromNumber(1), tag_a.GetId());
3885    EXPECT_FALSE(tag_a.GetIsDel());
3886    EXPECT_FALSE(tag_a.GetIsUnappliedUpdate());
3887    EXPECT_FALSE(tag_a.GetIsUnsynced());
3888    EXPECT_EQ(1, tag_a.GetBaseVersion());
3889    EXPECT_EQ("tag a", tag_a.GetUniqueClientTag());
3890
3891    Entry tag_b(&trans, GET_BY_CLIENT_TAG, "tag b");
3892    ASSERT_TRUE(tag_b.good());
3893    EXPECT_TRUE(tag_b.GetId().ServerKnows());
3894    EXPECT_EQ(ids_.FromNumber(101), tag_b.GetId());
3895    EXPECT_FALSE(tag_b.GetIsDel());
3896    EXPECT_FALSE(tag_b.GetIsUnappliedUpdate());
3897    EXPECT_FALSE(tag_b.GetIsUnsynced());
3898    EXPECT_EQ(16, tag_b.GetBaseVersion());
3899    EXPECT_EQ("tag b", tag_b.GetUniqueClientTag());
3900
3901    Entry tag_c(&trans, GET_BY_CLIENT_TAG, "tag c");
3902    ASSERT_TRUE(tag_c.good());
3903    EXPECT_TRUE(tag_c.GetId().ServerKnows());
3904    EXPECT_EQ(ids_.FromNumber(201), tag_c.GetId());
3905    EXPECT_FALSE(tag_c.GetIsDel());
3906    EXPECT_FALSE(tag_c.GetIsUnappliedUpdate());
3907    EXPECT_FALSE(tag_c.GetIsUnsynced());
3908    EXPECT_EQ(21, tag_c.GetBaseVersion());
3909    EXPECT_EQ("tag c", tag_c.GetUniqueClientTag());
3910
3911    syncable::Directory::Metahandles children;
3912    directory()->GetChildHandlesById(&trans, trans.root_id(), &children);
3913    ASSERT_EQ(3U, children.size());
3914  }
3915}
3916
3917TEST_F(SyncerTest, UniqueServerTagUpdates) {
3918  // As a hurdle, introduce an item whose name is the same as the tag value
3919  // we'll use later.
3920  int64 hurdle_handle = CreateUnsyncedDirectory("bob", "id_bob");
3921  {
3922    syncable::ReadTransaction trans(FROM_HERE, directory());
3923    Entry hurdle(&trans, GET_BY_HANDLE, hurdle_handle);
3924    ASSERT_TRUE(hurdle.good());
3925    ASSERT_TRUE(!hurdle.GetIsDel());
3926    ASSERT_TRUE(hurdle.GetUniqueServerTag().empty());
3927    ASSERT_TRUE(hurdle.GetNonUniqueName()== "bob");
3928
3929    // Try to lookup by the tagname.  These should fail.
3930    Entry tag_alpha(&trans, GET_BY_SERVER_TAG, "alpha");
3931    EXPECT_FALSE(tag_alpha.good());
3932    Entry tag_bob(&trans, GET_BY_SERVER_TAG, "bob");
3933    EXPECT_FALSE(tag_bob.good());
3934  }
3935
3936  // Now download some tagged items as updates.
3937  mock_server_->AddUpdateDirectory(
3938      1, 0, "update1", 1, 10, std::string(), std::string());
3939  mock_server_->SetLastUpdateServerTag("alpha");
3940  mock_server_->AddUpdateDirectory(
3941      2, 0, "update2", 2, 20, std::string(), std::string());
3942  mock_server_->SetLastUpdateServerTag("bob");
3943  SyncShareNudge();
3944
3945  {
3946    syncable::ReadTransaction trans(FROM_HERE, directory());
3947
3948    // The new items should be applied as new entries, and we should be able
3949    // to look them up by their tag values.
3950    Entry tag_alpha(&trans, GET_BY_SERVER_TAG, "alpha");
3951    ASSERT_TRUE(tag_alpha.good());
3952    ASSERT_TRUE(!tag_alpha.GetIsDel());
3953    ASSERT_TRUE(tag_alpha.GetUniqueServerTag()== "alpha");
3954    ASSERT_TRUE(tag_alpha.GetNonUniqueName()== "update1");
3955    Entry tag_bob(&trans, GET_BY_SERVER_TAG, "bob");
3956    ASSERT_TRUE(tag_bob.good());
3957    ASSERT_TRUE(!tag_bob.GetIsDel());
3958    ASSERT_TRUE(tag_bob.GetUniqueServerTag()== "bob");
3959    ASSERT_TRUE(tag_bob.GetNonUniqueName()== "update2");
3960    // The old item should be unchanged.
3961    Entry hurdle(&trans, GET_BY_HANDLE, hurdle_handle);
3962    ASSERT_TRUE(hurdle.good());
3963    ASSERT_TRUE(!hurdle.GetIsDel());
3964    ASSERT_TRUE(hurdle.GetUniqueServerTag().empty());
3965    ASSERT_TRUE(hurdle.GetNonUniqueName()== "bob");
3966  }
3967}
3968
3969TEST_F(SyncerTest, GetUpdatesSetsRequestedTypes) {
3970  // The expectations of this test happen in the MockConnectionManager's
3971  // GetUpdates handler.  EnableDatatype sets the expectation value from our
3972  // set of enabled/disabled datatypes.
3973  EnableDatatype(BOOKMARKS);
3974  SyncShareNudge();
3975  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
3976
3977  EnableDatatype(AUTOFILL);
3978  SyncShareNudge();
3979  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
3980
3981  EnableDatatype(PREFERENCES);
3982  SyncShareNudge();
3983  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
3984
3985  DisableDatatype(BOOKMARKS);
3986  SyncShareNudge();
3987  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
3988
3989  DisableDatatype(AUTOFILL);
3990  SyncShareNudge();
3991  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
3992
3993  DisableDatatype(PREFERENCES);
3994  EnableDatatype(AUTOFILL);
3995  SyncShareNudge();
3996  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
3997}
3998
3999// A typical scenario: server and client each have one update for the other.
4000// This is the "happy path" alternative to UpdateFailsThenDontCommit.
4001TEST_F(SyncerTest, UpdateThenCommit) {
4002  syncable::Id to_receive = ids_.NewServerId();
4003  syncable::Id to_commit = ids_.NewLocalId();
4004
4005  mock_server_->AddUpdateDirectory(to_receive, ids_.root(), "x", 1, 10,
4006                                   foreign_cache_guid(), "-1");
4007  int64 commit_handle = CreateUnsyncedDirectory("y", to_commit);
4008  SyncShareNudge();
4009
4010  // The sync cycle should have included a GetUpdate, then a commit.  By the
4011  // time the commit happened, we should have known for sure that there were no
4012  // hierarchy conflicts, and reported this fact to the server.
4013  ASSERT_TRUE(mock_server_->last_request().has_commit());
4014  VerifyNoHierarchyConflictsReported(mock_server_->last_request());
4015
4016  syncable::ReadTransaction trans(FROM_HERE, directory());
4017
4018  Entry received(&trans, GET_BY_ID, to_receive);
4019  ASSERT_TRUE(received.good());
4020  EXPECT_FALSE(received.GetIsUnsynced());
4021  EXPECT_FALSE(received.GetIsUnappliedUpdate());
4022
4023  Entry committed(&trans, GET_BY_HANDLE, commit_handle);
4024  ASSERT_TRUE(committed.good());
4025  EXPECT_FALSE(committed.GetIsUnsynced());
4026  EXPECT_FALSE(committed.GetIsUnappliedUpdate());
4027}
4028
4029// Same as above, but this time we fail to download updates.
4030// We should not attempt to commit anything unless we successfully downloaded
4031// updates, otherwise we risk causing a server-side conflict.
4032TEST_F(SyncerTest, UpdateFailsThenDontCommit) {
4033  syncable::Id to_receive = ids_.NewServerId();
4034  syncable::Id to_commit = ids_.NewLocalId();
4035
4036  mock_server_->AddUpdateDirectory(to_receive, ids_.root(), "x", 1, 10,
4037                                   foreign_cache_guid(), "-1");
4038  int64 commit_handle = CreateUnsyncedDirectory("y", to_commit);
4039  mock_server_->FailNextPostBufferToPathCall();
4040  SyncShareNudge();
4041
4042  syncable::ReadTransaction trans(FROM_HERE, directory());
4043
4044  // We did not receive this update.
4045  Entry received(&trans, GET_BY_ID, to_receive);
4046  ASSERT_FALSE(received.good());
4047
4048  // And our local update remains unapplied.
4049  Entry committed(&trans, GET_BY_HANDLE, commit_handle);
4050  ASSERT_TRUE(committed.good());
4051  EXPECT_TRUE(committed.GetIsUnsynced());
4052  EXPECT_FALSE(committed.GetIsUnappliedUpdate());
4053
4054  // Inform the Mock we won't be fetching all updates.
4055  mock_server_->ClearUpdatesQueue();
4056}
4057
4058// Downloads two updates and applies them successfully.
4059// This is the "happy path" alternative to ConfigureFailsDontApplyUpdates.
4060TEST_F(SyncerTest, ConfigureDownloadsTwoBatchesSuccess) {
4061  syncable::Id node1 = ids_.NewServerId();
4062  syncable::Id node2 = ids_.NewServerId();
4063
4064  // Construct the first GetUpdates response.
4065  mock_server_->AddUpdateDirectory(node1, ids_.root(), "one", 1, 10,
4066                                   foreign_cache_guid(), "-2");
4067  mock_server_->SetChangesRemaining(1);
4068  mock_server_->NextUpdateBatch();
4069
4070  // Construct the second GetUpdates response.
4071  mock_server_->AddUpdateDirectory(node2, ids_.root(), "two", 1, 20,
4072                                   foreign_cache_guid(), "-2");
4073
4074  SyncShareConfigure();
4075
4076  syncable::ReadTransaction trans(FROM_HERE, directory());
4077  // Both nodes should be downloaded and applied.
4078
4079  Entry n1(&trans, GET_BY_ID, node1);
4080  ASSERT_TRUE(n1.good());
4081  EXPECT_FALSE(n1.GetIsUnappliedUpdate());
4082
4083  Entry n2(&trans, GET_BY_ID, node2);
4084  ASSERT_TRUE(n2.good());
4085  EXPECT_FALSE(n2.GetIsUnappliedUpdate());
4086}
4087
4088// Same as the above case, but this time the second batch fails to download.
4089TEST_F(SyncerTest, ConfigureFailsDontApplyUpdates) {
4090  syncable::Id node1 = ids_.NewServerId();
4091  syncable::Id node2 = ids_.NewServerId();
4092
4093  // The scenario: we have two batches of updates with one update each.  A
4094  // normal confgure step would download all the updates one batch at a time and
4095  // apply them.  This configure will succeed in downloading the first batch
4096  // then fail when downloading the second.
4097  mock_server_->FailNthPostBufferToPathCall(2);
4098
4099  // Construct the first GetUpdates response.
4100  mock_server_->AddUpdateDirectory(node1, ids_.root(), "one", 1, 10,
4101                                   foreign_cache_guid(), "-1");
4102  mock_server_->SetChangesRemaining(1);
4103  mock_server_->NextUpdateBatch();
4104
4105  // Consutrct the second GetUpdates response.
4106  mock_server_->AddUpdateDirectory(node2, ids_.root(), "two", 1, 20,
4107                                   foreign_cache_guid(), "-2");
4108
4109  SyncShareConfigure();
4110
4111  syncable::ReadTransaction trans(FROM_HERE, directory());
4112
4113  // The first node was downloaded, but not applied.
4114  Entry n1(&trans, GET_BY_ID, node1);
4115  ASSERT_TRUE(n1.good());
4116  EXPECT_TRUE(n1.GetIsUnappliedUpdate());
4117
4118  // The second node was not downloaded.
4119  Entry n2(&trans, GET_BY_ID, node2);
4120  EXPECT_FALSE(n2.good());
4121
4122  // One update remains undownloaded.
4123  mock_server_->ClearUpdatesQueue();
4124}
4125
4126TEST_F(SyncerTest, GetKeySuccess) {
4127  {
4128    syncable::ReadTransaction rtrans(FROM_HERE, directory());
4129    EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans));
4130  }
4131
4132  SyncShareConfigure();
4133
4134  EXPECT_EQ(session_->status_controller().last_get_key_result(), SYNCER_OK);
4135  {
4136    syncable::ReadTransaction rtrans(FROM_HERE, directory());
4137    EXPECT_FALSE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans));
4138  }
4139}
4140
4141TEST_F(SyncerTest, GetKeyEmpty) {
4142  {
4143    syncable::ReadTransaction rtrans(FROM_HERE, directory());
4144    EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans));
4145  }
4146
4147  mock_server_->SetKeystoreKey(std::string());
4148  SyncShareConfigure();
4149
4150  EXPECT_NE(session_->status_controller().last_get_key_result(), SYNCER_OK);
4151  {
4152    syncable::ReadTransaction rtrans(FROM_HERE, directory());
4153    EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans));
4154  }
4155}
4156
4157// Test what happens if a client deletes, then recreates, an object very
4158// quickly.  It is possible that the deletion gets sent as a commit, and
4159// the undelete happens during the commit request.  The principle here
4160// is that with a single committing client, conflicts should never
4161// be encountered, and a client encountering its past actions during
4162// getupdates should never feed back to override later actions.
4163//
4164// In cases of ordering A-F below, the outcome should be the same.
4165//   Exercised by UndeleteDuringCommit:
4166//     A. Delete - commit - undelete - commitresponse.
4167//     B. Delete - commit - undelete - commitresponse - getupdates.
4168//   Exercised by UndeleteBeforeCommit:
4169//     C. Delete - undelete - commit - commitresponse.
4170//     D. Delete - undelete - commit - commitresponse - getupdates.
4171//   Exercised by UndeleteAfterCommit:
4172//     E. Delete - commit - commitresponse - undelete - commit
4173//        - commitresponse.
4174//     F. Delete - commit - commitresponse - undelete - commit -
4175//        - commitresponse - getupdates.
4176class SyncerUndeletionTest : public SyncerTest {
4177 public:
4178  SyncerUndeletionTest()
4179      : client_tag_("foobar"),
4180        metahandle_(syncable::kInvalidMetaHandle) {
4181  }
4182
4183  void Create() {
4184    WriteTransaction trans(FROM_HERE, UNITTEST, directory());
4185    MutableEntry perm_folder(
4186        &trans, CREATE, BOOKMARKS, ids_.root(), "clientname");
4187    ASSERT_TRUE(perm_folder.good());
4188    perm_folder.PutUniqueClientTag(client_tag_);
4189    perm_folder.PutIsUnsynced(true);
4190    perm_folder.PutSyncing(false);
4191    perm_folder.PutSpecifics(DefaultBookmarkSpecifics());
4192    EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate());
4193    EXPECT_FALSE(perm_folder.GetId().ServerKnows());
4194    metahandle_ = perm_folder.GetMetahandle();
4195    local_id_ = perm_folder.GetId();
4196  }
4197
4198  void Delete() {
4199    WriteTransaction trans(FROM_HERE, UNITTEST, directory());
4200    MutableEntry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4201    ASSERT_TRUE(entry.good());
4202    EXPECT_EQ(metahandle_, entry.GetMetahandle());
4203    entry.PutIsDel(true);
4204    entry.PutIsUnsynced(true);
4205    entry.PutSyncing(false);
4206  }
4207
4208  void Undelete() {
4209    WriteTransaction trans(FROM_HERE, UNITTEST, directory());
4210    MutableEntry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4211    ASSERT_TRUE(entry.good());
4212    EXPECT_EQ(metahandle_, entry.GetMetahandle());
4213    EXPECT_TRUE(entry.GetIsDel());
4214    entry.PutIsDel(false);
4215    entry.PutIsUnsynced(true);
4216    entry.PutSyncing(false);
4217  }
4218
4219  int64 GetMetahandleOfTag() {
4220    syncable::ReadTransaction trans(FROM_HERE, directory());
4221    Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4222    EXPECT_TRUE(entry.good());
4223    if (!entry.good()) {
4224      return syncable::kInvalidMetaHandle;
4225    }
4226    return entry.GetMetahandle();
4227  }
4228
4229  void ExpectUnsyncedCreation() {
4230    syncable::ReadTransaction trans(FROM_HERE, directory());
4231    Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4232
4233    EXPECT_EQ(metahandle_, entry.GetMetahandle());
4234    EXPECT_FALSE(entry.GetIsDel());
4235    EXPECT_FALSE(entry.GetServerIsDel());  // Never been committed.
4236    EXPECT_GE(0, entry.GetBaseVersion());
4237    EXPECT_TRUE(entry.GetIsUnsynced());
4238    EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4239  }
4240
4241  void ExpectUnsyncedUndeletion() {
4242    syncable::ReadTransaction trans(FROM_HERE, directory());
4243    Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4244
4245    EXPECT_EQ(metahandle_, entry.GetMetahandle());
4246    EXPECT_FALSE(entry.GetIsDel());
4247    EXPECT_TRUE(entry.GetServerIsDel());
4248    EXPECT_EQ(0, entry.GetBaseVersion());
4249    EXPECT_TRUE(entry.GetIsUnsynced());
4250    EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4251    EXPECT_TRUE(entry.GetId().ServerKnows());
4252  }
4253
4254  void ExpectUnsyncedEdit() {
4255    syncable::ReadTransaction trans(FROM_HERE, directory());
4256    Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4257
4258    EXPECT_EQ(metahandle_, entry.GetMetahandle());
4259    EXPECT_FALSE(entry.GetIsDel());
4260    EXPECT_FALSE(entry.GetServerIsDel());
4261    EXPECT_LT(0, entry.GetBaseVersion());
4262    EXPECT_TRUE(entry.GetIsUnsynced());
4263    EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4264    EXPECT_TRUE(entry.GetId().ServerKnows());
4265  }
4266
4267  void ExpectUnsyncedDeletion() {
4268    syncable::ReadTransaction trans(FROM_HERE, directory());
4269    Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4270
4271    EXPECT_EQ(metahandle_, entry.GetMetahandle());
4272    EXPECT_TRUE(entry.GetIsDel());
4273    EXPECT_FALSE(entry.GetServerIsDel());
4274    EXPECT_TRUE(entry.GetIsUnsynced());
4275    EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4276    EXPECT_LT(0, entry.GetBaseVersion());
4277    EXPECT_LT(0, entry.GetServerVersion());
4278  }
4279
4280  void ExpectSyncedAndCreated() {
4281    syncable::ReadTransaction trans(FROM_HERE, directory());
4282    Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4283
4284    EXPECT_EQ(metahandle_, entry.GetMetahandle());
4285    EXPECT_FALSE(entry.GetIsDel());
4286    EXPECT_FALSE(entry.GetServerIsDel());
4287    EXPECT_LT(0, entry.GetBaseVersion());
4288    EXPECT_EQ(entry.GetBaseVersion(), entry.GetServerVersion());
4289    EXPECT_FALSE(entry.GetIsUnsynced());
4290    EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4291  }
4292
4293  void ExpectSyncedAndDeleted() {
4294    syncable::ReadTransaction trans(FROM_HERE, directory());
4295    Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
4296
4297    EXPECT_EQ(metahandle_, entry.GetMetahandle());
4298    EXPECT_TRUE(entry.GetIsDel());
4299    EXPECT_TRUE(entry.GetServerIsDel());
4300    EXPECT_FALSE(entry.GetIsUnsynced());
4301    EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4302    EXPECT_GE(0, entry.GetBaseVersion());
4303    EXPECT_GE(0, entry.GetServerVersion());
4304  }
4305
4306 protected:
4307  const std::string client_tag_;
4308  syncable::Id local_id_;
4309  int64 metahandle_;
4310};
4311
4312TEST_F(SyncerUndeletionTest, UndeleteDuringCommit) {
4313  Create();
4314  ExpectUnsyncedCreation();
4315  SyncShareNudge();
4316
4317  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4318  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4319  ExpectSyncedAndCreated();
4320
4321  // Delete, begin committing the delete, then undelete while committing.
4322  Delete();
4323  ExpectUnsyncedDeletion();
4324  mock_server_->SetMidCommitCallback(
4325      base::Bind(&SyncerUndeletionTest::Undelete, base::Unretained(this)));
4326  SyncShareNudge();
4327
4328  // We will continue to commit until all nodes are synced, so we expect
4329  // that both the delete and following undelete were committed.  We haven't
4330  // downloaded any updates, though, so the SERVER fields will be the same
4331  // as they were at the start of the cycle.
4332  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4333  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4334
4335  {
4336    syncable::ReadTransaction trans(FROM_HERE, directory());
4337    Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4338
4339    // Server fields lag behind.
4340    EXPECT_FALSE(entry.GetServerIsDel());
4341
4342    // We have committed the second (undelete) update.
4343    EXPECT_FALSE(entry.GetIsDel());
4344    EXPECT_FALSE(entry.GetIsUnsynced());
4345    EXPECT_FALSE(entry.GetIsUnappliedUpdate());
4346  }
4347
4348  // Now, encounter a GetUpdates corresponding to the deletion from
4349  // the server.  The undeletion should prevail again and be committed.
4350  // None of this should trigger any conflict detection -- it is perfectly
4351  // normal to recieve updates from our own commits.
4352  mock_server_->SetMidCommitCallback(base::Closure());
4353  sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
4354  update->set_originator_cache_guid(local_cache_guid());
4355  update->set_originator_client_item_id(local_id_.GetServerId());
4356
4357  SyncShareNudge();
4358  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4359  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4360  ExpectSyncedAndCreated();
4361}
4362
4363TEST_F(SyncerUndeletionTest, UndeleteBeforeCommit) {
4364  Create();
4365  ExpectUnsyncedCreation();
4366  SyncShareNudge();
4367
4368  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4369  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4370  ExpectSyncedAndCreated();
4371
4372  // Delete and undelete, then sync to pick up the result.
4373  Delete();
4374  ExpectUnsyncedDeletion();
4375  Undelete();
4376  ExpectUnsyncedEdit();  // Edit, not undelete: server thinks it exists.
4377  SyncShareNudge();
4378
4379  // The item ought to have committed successfully.
4380  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4381  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4382  ExpectSyncedAndCreated();
4383  {
4384    syncable::ReadTransaction trans(FROM_HERE, directory());
4385    Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4386    EXPECT_EQ(2, entry.GetBaseVersion());
4387  }
4388
4389  // Now, encounter a GetUpdates corresponding to the just-committed
4390  // update.
4391  sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
4392  update->set_originator_cache_guid(local_cache_guid());
4393  update->set_originator_client_item_id(local_id_.GetServerId());
4394  SyncShareNudge();
4395  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4396  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4397  ExpectSyncedAndCreated();
4398}
4399
4400TEST_F(SyncerUndeletionTest, UndeleteAfterCommitButBeforeGetUpdates) {
4401  Create();
4402  ExpectUnsyncedCreation();
4403  SyncShareNudge();
4404
4405  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4406  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4407  ExpectSyncedAndCreated();
4408
4409  // Delete and commit.
4410  Delete();
4411  ExpectUnsyncedDeletion();
4412  SyncShareNudge();
4413
4414  // The item ought to have committed successfully.
4415  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4416  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4417  ExpectSyncedAndDeleted();
4418
4419  // Before the GetUpdates, the item is locally undeleted.
4420  Undelete();
4421  ExpectUnsyncedUndeletion();
4422
4423  // Now, encounter a GetUpdates corresponding to the just-committed
4424  // deletion update.  The undeletion should prevail.
4425  mock_server_->AddUpdateFromLastCommit();
4426  SyncShareNudge();
4427  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4428  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4429  ExpectSyncedAndCreated();
4430}
4431
4432TEST_F(SyncerUndeletionTest, UndeleteAfterDeleteAndGetUpdates) {
4433  Create();
4434  ExpectUnsyncedCreation();
4435  SyncShareNudge();
4436
4437  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4438  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4439  ExpectSyncedAndCreated();
4440
4441  sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
4442  update->set_originator_cache_guid(local_cache_guid());
4443  update->set_originator_client_item_id(local_id_.GetServerId());
4444  SyncShareNudge();
4445  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4446  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4447  ExpectSyncedAndCreated();
4448
4449  // Delete and commit.
4450  Delete();
4451  ExpectUnsyncedDeletion();
4452  SyncShareNudge();
4453
4454  // The item ought to have committed successfully.
4455  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4456  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4457  ExpectSyncedAndDeleted();
4458
4459  // Now, encounter a GetUpdates corresponding to the just-committed
4460  // deletion update.  Should be consistent.
4461  mock_server_->AddUpdateFromLastCommit();
4462  SyncShareNudge();
4463  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4464  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4465  ExpectSyncedAndDeleted();
4466
4467  // After the GetUpdates, the item is locally undeleted.
4468  Undelete();
4469  ExpectUnsyncedUndeletion();
4470
4471  // Now, encounter a GetUpdates corresponding to the just-committed
4472  // deletion update.  The undeletion should prevail.
4473  SyncShareNudge();
4474  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4475  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4476  ExpectSyncedAndCreated();
4477}
4478
4479// Test processing of undeletion GetUpdateses.
4480TEST_F(SyncerUndeletionTest, UndeleteAfterOtherClientDeletes) {
4481  Create();
4482  ExpectUnsyncedCreation();
4483  SyncShareNudge();
4484
4485  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4486  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4487  ExpectSyncedAndCreated();
4488
4489  // Add a delete from the server.
4490  sync_pb::SyncEntity* update1 = mock_server_->AddUpdateFromLastCommit();
4491  update1->set_originator_cache_guid(local_cache_guid());
4492  update1->set_originator_client_item_id(local_id_.GetServerId());
4493  SyncShareNudge();
4494  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4495  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4496  ExpectSyncedAndCreated();
4497
4498  // Some other client deletes the item.
4499  {
4500    syncable::ReadTransaction trans(FROM_HERE, directory());
4501    Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4502    mock_server_->AddUpdateTombstone(entry.GetId());
4503  }
4504  SyncShareNudge();
4505
4506  // The update ought to have applied successfully.
4507  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4508  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4509  ExpectSyncedAndDeleted();
4510
4511  // Undelete it locally.
4512  Undelete();
4513  ExpectUnsyncedUndeletion();
4514  SyncShareNudge();
4515  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4516  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4517  ExpectSyncedAndCreated();
4518
4519  // Now, encounter a GetUpdates corresponding to the just-committed
4520  // deletion update.  The undeletion should prevail.
4521  sync_pb::SyncEntity* update2 = mock_server_->AddUpdateFromLastCommit();
4522  update2->set_originator_cache_guid(local_cache_guid());
4523  update2->set_originator_client_item_id(local_id_.GetServerId());
4524  SyncShareNudge();
4525  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4526  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4527  ExpectSyncedAndCreated();
4528}
4529
4530TEST_F(SyncerUndeletionTest, UndeleteAfterOtherClientDeletesImmediately) {
4531  Create();
4532  ExpectUnsyncedCreation();
4533  SyncShareNudge();
4534
4535  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4536  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4537  ExpectSyncedAndCreated();
4538
4539  // Some other client deletes the item before we get a chance
4540  // to GetUpdates our original request.
4541  {
4542    syncable::ReadTransaction trans(FROM_HERE, directory());
4543    Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4544    mock_server_->AddUpdateTombstone(entry.GetId());
4545  }
4546  SyncShareNudge();
4547
4548  // The update ought to have applied successfully.
4549  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4550  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4551  ExpectSyncedAndDeleted();
4552
4553  // Undelete it locally.
4554  Undelete();
4555  ExpectUnsyncedUndeletion();
4556  SyncShareNudge();
4557  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4558  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4559  ExpectSyncedAndCreated();
4560
4561  // Now, encounter a GetUpdates corresponding to the just-committed
4562  // deletion update.  The undeletion should prevail.
4563  sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
4564  update->set_originator_cache_guid(local_cache_guid());
4565  update->set_originator_client_item_id(local_id_.GetServerId());
4566  SyncShareNudge();
4567  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4568  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4569  ExpectSyncedAndCreated();
4570}
4571
4572TEST_F(SyncerUndeletionTest, OtherClientUndeletes) {
4573  Create();
4574  ExpectUnsyncedCreation();
4575  SyncShareNudge();
4576
4577  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4578  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4579  ExpectSyncedAndCreated();
4580
4581  // Get the updates of our just-committed entry.
4582  sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
4583  update->set_originator_cache_guid(local_cache_guid());
4584  update->set_originator_client_item_id(local_id_.GetServerId());
4585  SyncShareNudge();
4586  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4587  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4588  ExpectSyncedAndCreated();
4589
4590  // We delete the item.
4591  Delete();
4592  ExpectUnsyncedDeletion();
4593  SyncShareNudge();
4594
4595  // The update ought to have applied successfully.
4596  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4597  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4598  ExpectSyncedAndDeleted();
4599
4600  // Now, encounter a GetUpdates corresponding to the just-committed
4601  // deletion update.
4602  mock_server_->AddUpdateFromLastCommit();
4603  SyncShareNudge();
4604  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4605  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4606  ExpectSyncedAndDeleted();
4607
4608  // Some other client undeletes the item.
4609  {
4610    syncable::ReadTransaction trans(FROM_HERE, directory());
4611    Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4612    mock_server_->AddUpdateBookmark(
4613        entry.GetId(),
4614        entry.GetParentId(),
4615        "Thadeusz", 100, 1000,
4616        local_cache_guid(), local_id_.GetServerId());
4617  }
4618  mock_server_->SetLastUpdateClientTag(client_tag_);
4619  SyncShareNudge();
4620  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4621  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4622  ExpectSyncedAndCreated();
4623  {
4624    syncable::ReadTransaction trans(FROM_HERE, directory());
4625    Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4626    EXPECT_EQ("Thadeusz", entry.GetNonUniqueName());
4627  }
4628}
4629
4630TEST_F(SyncerUndeletionTest, OtherClientUndeletesImmediately) {
4631  Create();
4632  ExpectUnsyncedCreation();
4633  SyncShareNudge();
4634
4635  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4636  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4637  ExpectSyncedAndCreated();
4638
4639  // Get the updates of our just-committed entry.
4640  sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
4641  update->set_originator_cache_guid(local_cache_guid());
4642  {
4643    syncable::ReadTransaction trans(FROM_HERE, directory());
4644    Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4645    update->set_originator_client_item_id(local_id_.GetServerId());
4646  }
4647  SyncShareNudge();
4648  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4649  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4650  ExpectSyncedAndCreated();
4651
4652  // We delete the item.
4653  Delete();
4654  ExpectUnsyncedDeletion();
4655  SyncShareNudge();
4656
4657  // The update ought to have applied successfully.
4658  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4659  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4660  ExpectSyncedAndDeleted();
4661
4662  // Some other client undeletes before we see the update from our
4663  // commit.
4664  {
4665    syncable::ReadTransaction trans(FROM_HERE, directory());
4666    Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4667    mock_server_->AddUpdateBookmark(
4668        entry.GetId(),
4669        entry.GetParentId(),
4670        "Thadeusz", 100, 1000,
4671        local_cache_guid(), local_id_.GetServerId());
4672  }
4673  mock_server_->SetLastUpdateClientTag(client_tag_);
4674  SyncShareNudge();
4675  EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4676  EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4677  ExpectSyncedAndCreated();
4678  {
4679    syncable::ReadTransaction trans(FROM_HERE, directory());
4680    Entry entry(&trans, GET_BY_HANDLE, metahandle_);
4681    EXPECT_EQ("Thadeusz", entry.GetNonUniqueName());
4682  }
4683}
4684
4685enum {
4686  TEST_PARAM_BOOKMARK_ENABLE_BIT,
4687  TEST_PARAM_AUTOFILL_ENABLE_BIT,
4688  TEST_PARAM_BIT_COUNT
4689};
4690
4691class MixedResult :
4692    public SyncerTest,
4693    public ::testing::WithParamInterface<int> {
4694 protected:
4695  bool ShouldFailBookmarkCommit() {
4696    return (GetParam() & (1 << TEST_PARAM_BOOKMARK_ENABLE_BIT)) == 0;
4697  }
4698  bool ShouldFailAutofillCommit() {
4699    return (GetParam() & (1 << TEST_PARAM_AUTOFILL_ENABLE_BIT)) == 0;
4700  }
4701};
4702
4703INSTANTIATE_TEST_CASE_P(ExtensionsActivity,
4704                        MixedResult,
4705                        testing::Range(0, 1 << TEST_PARAM_BIT_COUNT));
4706
4707TEST_P(MixedResult, ExtensionsActivity) {
4708  {
4709    WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
4710
4711    MutableEntry pref(&wtrans, CREATE, PREFERENCES, wtrans.root_id(), "pref");
4712    ASSERT_TRUE(pref.good());
4713    pref.PutIsUnsynced(true);
4714
4715    MutableEntry bookmark(
4716        &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "bookmark");
4717    ASSERT_TRUE(bookmark.good());
4718    bookmark.PutIsUnsynced(true);
4719
4720    if (ShouldFailBookmarkCommit()) {
4721      mock_server_->SetTransientErrorId(bookmark.GetId());
4722    }
4723
4724    if (ShouldFailAutofillCommit()) {
4725      mock_server_->SetTransientErrorId(pref.GetId());
4726    }
4727  }
4728
4729
4730  // Put some extenions activity records into the monitor.
4731  {
4732    ExtensionsActivity::Records records;
4733    records["ABC"].extension_id = "ABC";
4734    records["ABC"].bookmark_write_count = 2049U;
4735    records["xyz"].extension_id = "xyz";
4736    records["xyz"].bookmark_write_count = 4U;
4737    context_->extensions_activity()->PutRecords(records);
4738  }
4739
4740  SyncShareNudge();
4741
4742  ExtensionsActivity::Records final_monitor_records;
4743  context_->extensions_activity()->GetAndClearRecords(&final_monitor_records);
4744  if (ShouldFailBookmarkCommit()) {
4745    ASSERT_EQ(2U, final_monitor_records.size())
4746        << "Should restore records after unsuccessful bookmark commit.";
4747    EXPECT_EQ("ABC", final_monitor_records["ABC"].extension_id);
4748    EXPECT_EQ("xyz", final_monitor_records["xyz"].extension_id);
4749    EXPECT_EQ(2049U, final_monitor_records["ABC"].bookmark_write_count);
4750    EXPECT_EQ(4U,    final_monitor_records["xyz"].bookmark_write_count);
4751  } else {
4752    EXPECT_TRUE(final_monitor_records.empty())
4753        << "Should not restore records after successful bookmark commit.";
4754  }
4755}
4756
4757}  // namespace syncer
4758