Home | History | Annotate | Download | only in invalidation
      1 // Copyright 2014 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 #include <cstddef>
      6 #include <map>
      7 #include <set>
      8 #include <string>
      9 #include <vector>
     10 
     11 #include "base/compiler_specific.h"
     12 #include "base/message_loop/message_loop.h"
     13 #include "base/stl_util.h"
     14 #include "components/invalidation/fake_invalidation_state_tracker.h"
     15 #include "components/invalidation/invalidation_util.h"
     16 #include "components/invalidation/object_id_invalidation_map.h"
     17 #include "components/invalidation/push_client_channel.h"
     18 #include "components/invalidation/sync_invalidation_listener.h"
     19 #include "components/invalidation/unacked_invalidation_set_test_util.h"
     20 #include "google/cacheinvalidation/include/invalidation-client.h"
     21 #include "google/cacheinvalidation/include/types.h"
     22 #include "jingle/notifier/listener/fake_push_client.h"
     23 #include "testing/gtest/include/gtest/gtest.h"
     24 
     25 namespace syncer {
     26 
     27 namespace {
     28 
     29 using invalidation::AckHandle;
     30 using invalidation::ObjectId;
     31 
     32 const char kClientId[] = "client_id";
     33 const char kClientInfo[] = "client_info";
     34 
     35 const char kState[] = "state";
     36 const char kNewState[] = "new_state";
     37 
     38 const char kPayload1[] = "payload1";
     39 const char kPayload2[] = "payload2";
     40 
     41 const int64 kVersion1 = 1LL;
     42 const int64 kVersion2 = 2LL;
     43 
     44 const int kChromeSyncSourceId = 1004;
     45 
     46 struct AckHandleLessThan {
     47   bool operator()(const AckHandle& lhs, const AckHandle& rhs) const {
     48     return lhs.handle_data() < rhs.handle_data();
     49   }
     50 };
     51 
     52 typedef std::set<AckHandle, AckHandleLessThan> AckHandleSet;
     53 
     54 // Fake invalidation::InvalidationClient implementation that keeps
     55 // track of registered IDs and acked handles.
     56 class FakeInvalidationClient : public invalidation::InvalidationClient {
     57  public:
     58   FakeInvalidationClient() : started_(false) {}
     59   virtual ~FakeInvalidationClient() {}
     60 
     61   const ObjectIdSet& GetRegisteredIds() const {
     62     return registered_ids_;
     63   }
     64 
     65   void ClearAckedHandles() {
     66     acked_handles_.clear();
     67   }
     68 
     69   bool IsAckedHandle(const AckHandle& ack_handle) const {
     70     return (acked_handles_.find(ack_handle) != acked_handles_.end());
     71   }
     72 
     73   // invalidation::InvalidationClient implementation.
     74 
     75   virtual void Start() OVERRIDE {
     76     started_ = true;
     77   }
     78 
     79   virtual void Stop() OVERRIDE {
     80     started_ = false;
     81   }
     82 
     83   virtual void Register(const ObjectId& object_id) OVERRIDE {
     84     if (!started_) {
     85       ADD_FAILURE();
     86       return;
     87     }
     88     registered_ids_.insert(object_id);
     89   }
     90 
     91   virtual void Register(
     92       const invalidation::vector<ObjectId>& object_ids) OVERRIDE {
     93     if (!started_) {
     94       ADD_FAILURE();
     95       return;
     96     }
     97     registered_ids_.insert(object_ids.begin(), object_ids.end());
     98   }
     99 
    100   virtual void Unregister(const ObjectId& object_id) OVERRIDE {
    101     if (!started_) {
    102       ADD_FAILURE();
    103       return;
    104     }
    105     registered_ids_.erase(object_id);
    106   }
    107 
    108   virtual void Unregister(
    109       const invalidation::vector<ObjectId>& object_ids) OVERRIDE {
    110     if (!started_) {
    111       ADD_FAILURE();
    112       return;
    113     }
    114     for (invalidation::vector<ObjectId>::const_iterator
    115              it = object_ids.begin(); it != object_ids.end(); ++it) {
    116       registered_ids_.erase(*it);
    117     }
    118   }
    119 
    120   virtual void Acknowledge(const AckHandle& ack_handle) OVERRIDE {
    121     if (!started_) {
    122       ADD_FAILURE();
    123       return;
    124     }
    125     acked_handles_.insert(ack_handle);
    126   }
    127 
    128  private:
    129   bool started_;
    130   ObjectIdSet registered_ids_;
    131   AckHandleSet acked_handles_;
    132 };
    133 
    134 // Fake delegate tkat keeps track of invalidation counts, payloads,
    135 // and state.
    136 class FakeDelegate : public SyncInvalidationListener::Delegate {
    137  public:
    138   explicit FakeDelegate(SyncInvalidationListener* listener)
    139       : state_(TRANSIENT_INVALIDATION_ERROR) {}
    140   virtual ~FakeDelegate() {}
    141 
    142   size_t GetInvalidationCount(const ObjectId& id) const {
    143     Map::const_iterator it = invalidations_.find(id);
    144     if (it == invalidations_.end()) {
    145       return 0;
    146     } else {
    147       return it->second.size();
    148     }
    149   }
    150 
    151   int64 GetVersion(const ObjectId& id) const {
    152     Map::const_iterator it = invalidations_.find(id);
    153     if (it == invalidations_.end()) {
    154       ADD_FAILURE() << "No invalidations for ID " << ObjectIdToString(id);
    155       return 0;
    156     } else {
    157       return it->second.back().version();
    158     }
    159   }
    160 
    161   std::string GetPayload(const ObjectId& id) const {
    162     Map::const_iterator it = invalidations_.find(id);
    163     if (it == invalidations_.end()) {
    164       ADD_FAILURE() << "No invalidations for ID " << ObjectIdToString(id);
    165       return 0;
    166     } else {
    167       return it->second.back().payload();
    168     }
    169   }
    170 
    171   bool IsUnknownVersion(const ObjectId& id) const {
    172     Map::const_iterator it = invalidations_.find(id);
    173     if (it == invalidations_.end()) {
    174       ADD_FAILURE() << "No invalidations for ID " << ObjectIdToString(id);
    175       return false;
    176     } else {
    177       return it->second.back().is_unknown_version();
    178     }
    179   }
    180 
    181   bool StartsWithUnknownVersion(const ObjectId& id) const {
    182     Map::const_iterator it = invalidations_.find(id);
    183     if (it == invalidations_.end()) {
    184       ADD_FAILURE() << "No invalidations for ID " << ObjectIdToString(id);
    185       return false;
    186     } else {
    187       return it->second.front().is_unknown_version();
    188     }
    189   }
    190 
    191   InvalidatorState GetInvalidatorState() const {
    192     return state_;
    193   }
    194 
    195   void AcknowledgeNthInvalidation(const ObjectId& id, size_t n) {
    196     List& list = invalidations_[id];
    197     List::iterator it = list.begin() + n;
    198     it->Acknowledge();
    199   }
    200 
    201   void AcknowledgeAll(const ObjectId& id) {
    202     List& list = invalidations_[id];
    203     for (List::iterator it = list.begin(); it != list.end(); ++it) {
    204       it->Acknowledge();
    205     }
    206   }
    207 
    208   void DropNthInvalidation(const ObjectId& id, size_t n) {
    209     List& list = invalidations_[id];
    210     List::iterator it = list.begin() + n;
    211     it->Drop();
    212     dropped_invalidations_map_.erase(id);
    213     dropped_invalidations_map_.insert(std::make_pair(id, *it));
    214   }
    215 
    216   void RecoverFromDropEvent(const ObjectId& id) {
    217     DropMap::iterator it = dropped_invalidations_map_.find(id);
    218     if (it != dropped_invalidations_map_.end()) {
    219       it->second.Acknowledge();
    220       dropped_invalidations_map_.erase(it);
    221     }
    222   }
    223 
    224   // SyncInvalidationListener::Delegate implementation.
    225   virtual void OnInvalidate(
    226       const ObjectIdInvalidationMap& invalidation_map) OVERRIDE {
    227     ObjectIdSet ids = invalidation_map.GetObjectIds();
    228     for (ObjectIdSet::iterator it = ids.begin(); it != ids.end(); ++it) {
    229       const SingleObjectInvalidationSet& incoming =
    230           invalidation_map.ForObject(*it);
    231       List& list = invalidations_[*it];
    232       list.insert(list.end(), incoming.begin(), incoming.end());
    233     }
    234   }
    235 
    236   virtual void OnInvalidatorStateChange(InvalidatorState state) OVERRIDE {
    237     state_ = state;
    238   }
    239 
    240  private:
    241   typedef std::vector<Invalidation> List;
    242   typedef std::map<ObjectId, List, ObjectIdLessThan> Map;
    243   typedef std::map<ObjectId, Invalidation, ObjectIdLessThan> DropMap;
    244 
    245   Map invalidations_;
    246   InvalidatorState state_;
    247   DropMap dropped_invalidations_map_;
    248 };
    249 
    250 invalidation::InvalidationClient* CreateFakeInvalidationClient(
    251     FakeInvalidationClient** fake_invalidation_client,
    252     invalidation::SystemResources* resources,
    253     int client_type,
    254     const invalidation::string& client_name,
    255     const invalidation::string& application_name,
    256     invalidation::InvalidationListener* listener) {
    257   *fake_invalidation_client = new FakeInvalidationClient();
    258   return *fake_invalidation_client;
    259 }
    260 
    261 class SyncInvalidationListenerTest : public testing::Test {
    262  protected:
    263   SyncInvalidationListenerTest()
    264       : kBookmarksId_(kChromeSyncSourceId, "BOOKMARK"),
    265         kPreferencesId_(kChromeSyncSourceId, "PREFERENCE"),
    266         kExtensionsId_(kChromeSyncSourceId, "EXTENSION"),
    267         kAppsId_(kChromeSyncSourceId, "APP"),
    268         fake_push_client_(new notifier::FakePushClient()),
    269         fake_invalidation_client_(NULL),
    270         listener_(scoped_ptr<SyncNetworkChannel>(new PushClientChannel(
    271             scoped_ptr<notifier::PushClient>(fake_push_client_)))),
    272         fake_delegate_(&listener_) {}
    273 
    274   virtual void SetUp() {
    275     StartClient();
    276 
    277     registered_ids_.insert(kBookmarksId_);
    278     registered_ids_.insert(kPreferencesId_);
    279     listener_.UpdateRegisteredIds(registered_ids_);
    280   }
    281 
    282   virtual void TearDown() {
    283     StopClient();
    284   }
    285 
    286   // Restart client without re-registering IDs.
    287   void RestartClient() {
    288     StopClient();
    289     StartClient();
    290   }
    291 
    292   void StartClient() {
    293     fake_invalidation_client_ = NULL;
    294     listener_.Start(
    295         base::Bind(&CreateFakeInvalidationClient, &fake_invalidation_client_),
    296         kClientId,
    297         kClientInfo,
    298         kState,
    299         fake_tracker_.GetSavedInvalidations(),
    300         fake_tracker_.AsWeakPtr(),
    301         base::MessageLoopProxy::current(),
    302         &fake_delegate_);
    303     DCHECK(fake_invalidation_client_);
    304   }
    305 
    306   void StopClient() {
    307     // listener_.StopForTest() stops the invalidation scheduler, which
    308     // deletes any pending tasks without running them.  Some tasks
    309     // "run and delete" another task, so they must be run in order to
    310     // avoid leaking the inner task.  listener_.StopForTest() does not
    311     // schedule any tasks, so it's both necessary and sufficient to
    312     // drain the task queue before calling it.
    313     FlushPendingWrites();
    314     fake_invalidation_client_ = NULL;
    315     listener_.StopForTest();
    316   }
    317 
    318   size_t GetInvalidationCount(const ObjectId& id) const {
    319     return fake_delegate_.GetInvalidationCount(id);
    320   }
    321 
    322   int64 GetVersion(const ObjectId& id) const {
    323     return fake_delegate_.GetVersion(id);
    324   }
    325 
    326   std::string GetPayload(const ObjectId& id) const {
    327     return fake_delegate_.GetPayload(id);
    328   }
    329 
    330   bool IsUnknownVersion(const ObjectId& id) const {
    331     return fake_delegate_.IsUnknownVersion(id);
    332   }
    333 
    334   bool StartsWithUnknownVersion(const ObjectId& id) const {
    335     return fake_delegate_.StartsWithUnknownVersion(id);
    336   }
    337 
    338   void AcknowledgeNthInvalidation(const ObjectId& id, size_t n) {
    339     fake_delegate_.AcknowledgeNthInvalidation(id, n);
    340   }
    341 
    342   void DropNthInvalidation(const ObjectId& id, size_t n) {
    343     return fake_delegate_.DropNthInvalidation(id, n);
    344   }
    345 
    346   void RecoverFromDropEvent(const ObjectId& id) {
    347     return fake_delegate_.RecoverFromDropEvent(id);
    348   }
    349 
    350   void AcknowledgeAll(const ObjectId& id) {
    351     fake_delegate_.AcknowledgeAll(id);
    352   }
    353 
    354   InvalidatorState GetInvalidatorState() const {
    355     return fake_delegate_.GetInvalidatorState();
    356   }
    357 
    358   std::string GetInvalidatorClientId() const {
    359     return fake_tracker_.GetInvalidatorClientId();
    360   }
    361 
    362   std::string GetBootstrapData() const {
    363     return fake_tracker_.GetBootstrapData();
    364   }
    365 
    366   UnackedInvalidationsMap GetSavedInvalidations() {
    367     // Allow any queued writes to go through first.
    368     FlushPendingWrites();
    369     return fake_tracker_.GetSavedInvalidations();
    370   }
    371 
    372   SingleObjectInvalidationSet GetSavedInvalidationsForType(const ObjectId& id) {
    373     const UnackedInvalidationsMap& saved_state = GetSavedInvalidations();
    374     UnackedInvalidationsMap::const_iterator it =
    375         saved_state.find(kBookmarksId_);
    376     if (it == saved_state.end()) {
    377       ADD_FAILURE() << "No state saved for ID " << ObjectIdToString(id);
    378       return SingleObjectInvalidationSet();
    379     }
    380     ObjectIdInvalidationMap map;
    381     it->second.ExportInvalidations(
    382         base::WeakPtr<AckHandler>(),
    383         scoped_refptr<base::SingleThreadTaskRunner>(),
    384         &map);
    385     if (map.Empty()) {
    386       return SingleObjectInvalidationSet();
    387     } else  {
    388       return map.ForObject(id);
    389     }
    390   }
    391 
    392   ObjectIdSet GetRegisteredIds() const {
    393     return fake_invalidation_client_->GetRegisteredIds();
    394   }
    395 
    396   // |payload| can be NULL.
    397   void FireInvalidate(const ObjectId& object_id,
    398                       int64 version, const char* payload) {
    399     invalidation::Invalidation inv;
    400     if (payload) {
    401       inv = invalidation::Invalidation(object_id, version, payload);
    402     } else {
    403       inv = invalidation::Invalidation(object_id, version);
    404     }
    405     const AckHandle ack_handle("fakedata");
    406     fake_invalidation_client_->ClearAckedHandles();
    407     listener_.Invalidate(fake_invalidation_client_, inv, ack_handle);
    408     EXPECT_TRUE(fake_invalidation_client_->IsAckedHandle(ack_handle));
    409   }
    410 
    411   // |payload| can be NULL, but not |type_name|.
    412   void FireInvalidateUnknownVersion(const ObjectId& object_id) {
    413     const AckHandle ack_handle("fakedata_unknown");
    414     fake_invalidation_client_->ClearAckedHandles();
    415     listener_.InvalidateUnknownVersion(fake_invalidation_client_,
    416                                        object_id,
    417                                        ack_handle);
    418     EXPECT_TRUE(fake_invalidation_client_->IsAckedHandle(ack_handle));
    419   }
    420 
    421   void FireInvalidateAll() {
    422     const AckHandle ack_handle("fakedata_all");
    423     fake_invalidation_client_->ClearAckedHandles();
    424     listener_.InvalidateAll(fake_invalidation_client_, ack_handle);
    425     EXPECT_TRUE(fake_invalidation_client_->IsAckedHandle(ack_handle));
    426   }
    427 
    428   void WriteState(const std::string& new_state) {
    429     listener_.WriteState(new_state);
    430 
    431     // Pump message loop to trigger
    432     // InvalidationStateTracker::WriteState().
    433     FlushPendingWrites();
    434   }
    435 
    436   void FlushPendingWrites() {
    437     message_loop_.RunUntilIdle();
    438   }
    439 
    440   void EnableNotifications() {
    441     fake_push_client_->EnableNotifications();
    442   }
    443 
    444   void DisableNotifications(notifier::NotificationsDisabledReason reason) {
    445     fake_push_client_->DisableNotifications(reason);
    446   }
    447 
    448   const ObjectId kBookmarksId_;
    449   const ObjectId kPreferencesId_;
    450   const ObjectId kExtensionsId_;
    451   const ObjectId kAppsId_;
    452 
    453   ObjectIdSet registered_ids_;
    454 
    455  private:
    456   base::MessageLoop message_loop_;
    457   notifier::FakePushClient* const fake_push_client_;
    458 
    459  protected:
    460   // A derrived test needs direct access to this.
    461   FakeInvalidationStateTracker fake_tracker_;
    462 
    463   // Tests need to access these directly.
    464   FakeInvalidationClient* fake_invalidation_client_;
    465   SyncInvalidationListener listener_;
    466 
    467  private:
    468   FakeDelegate fake_delegate_;
    469 };
    470 
    471 // Write a new state to the client.  It should propagate to the
    472 // tracker.
    473 TEST_F(SyncInvalidationListenerTest, WriteState) {
    474   WriteState(kNewState);
    475 
    476   EXPECT_EQ(kNewState, GetBootstrapData());
    477 }
    478 
    479 // Invalidation tests.
    480 
    481 // Fire an invalidation without a payload.  It should be processed,
    482 // the payload should remain empty, and the version should be updated.
    483 TEST_F(SyncInvalidationListenerTest, InvalidateNoPayload) {
    484   const ObjectId& id = kBookmarksId_;
    485 
    486   FireInvalidate(id, kVersion1, NULL);
    487 
    488   ASSERT_EQ(1U, GetInvalidationCount(id));
    489   ASSERT_FALSE(IsUnknownVersion(id));
    490   EXPECT_EQ(kVersion1, GetVersion(id));
    491   EXPECT_EQ("", GetPayload(id));
    492 }
    493 
    494 // Fire an invalidation with an empty payload.  It should be
    495 // processed, the payload should remain empty, and the version should
    496 // be updated.
    497 TEST_F(SyncInvalidationListenerTest, InvalidateEmptyPayload) {
    498   const ObjectId& id = kBookmarksId_;
    499 
    500   FireInvalidate(id, kVersion1, "");
    501 
    502   ASSERT_EQ(1U, GetInvalidationCount(id));
    503   ASSERT_FALSE(IsUnknownVersion(id));
    504   EXPECT_EQ(kVersion1, GetVersion(id));
    505   EXPECT_EQ("", GetPayload(id));
    506 }
    507 
    508 // Fire an invalidation with a payload.  It should be processed, and
    509 // both the payload and the version should be updated.
    510 TEST_F(SyncInvalidationListenerTest, InvalidateWithPayload) {
    511   const ObjectId& id = kPreferencesId_;
    512 
    513   FireInvalidate(id, kVersion1, kPayload1);
    514 
    515   ASSERT_EQ(1U, GetInvalidationCount(id));
    516   ASSERT_FALSE(IsUnknownVersion(id));
    517   EXPECT_EQ(kVersion1, GetVersion(id));
    518   EXPECT_EQ(kPayload1, GetPayload(id));
    519 }
    520 
    521 // Fire ten invalidations in a row.  All should be received.
    522 TEST_F(SyncInvalidationListenerTest, ManyInvalidations_NoDrop) {
    523   const int kRepeatCount = 10;
    524   const ObjectId& id = kPreferencesId_;
    525   int64 initial_version = kVersion1;
    526   for (int64 i = initial_version; i < initial_version + kRepeatCount; ++i) {
    527     FireInvalidate(id, i, kPayload1);
    528   }
    529   ASSERT_EQ(static_cast<size_t>(kRepeatCount), GetInvalidationCount(id));
    530   ASSERT_FALSE(IsUnknownVersion(id));
    531   EXPECT_EQ(kPayload1, GetPayload(id));
    532   EXPECT_EQ(initial_version + kRepeatCount - 1, GetVersion(id));
    533 }
    534 
    535 // Fire an invalidation for an unregistered object ID with a payload.  It should
    536 // still be processed, and both the payload and the version should be updated.
    537 TEST_F(SyncInvalidationListenerTest, InvalidateBeforeRegistration_Simple) {
    538   const ObjectId kUnregisteredId(kChromeSyncSourceId, "unregistered");
    539   const ObjectId& id = kUnregisteredId;
    540   ObjectIdSet ids;
    541   ids.insert(id);
    542 
    543   EXPECT_EQ(0U, GetInvalidationCount(id));
    544 
    545   FireInvalidate(id, kVersion1, kPayload1);
    546 
    547   ASSERT_EQ(0U, GetInvalidationCount(id));
    548 
    549   EnableNotifications();
    550   listener_.Ready(fake_invalidation_client_);
    551   listener_.UpdateRegisteredIds(ids);
    552 
    553   ASSERT_EQ(1U, GetInvalidationCount(id));
    554   ASSERT_FALSE(IsUnknownVersion(id));
    555   EXPECT_EQ(kVersion1, GetVersion(id));
    556   EXPECT_EQ(kPayload1, GetPayload(id));
    557 }
    558 
    559 // Fire ten invalidations before an object registers.  Some invalidations will
    560 // be dropped an replaced with an unknown version invalidation.
    561 TEST_F(SyncInvalidationListenerTest, InvalidateBeforeRegistration_Drop) {
    562   const int kRepeatCount =
    563       UnackedInvalidationSet::kMaxBufferedInvalidations + 1;
    564   const ObjectId kUnregisteredId(kChromeSyncSourceId, "unregistered");
    565   const ObjectId& id = kUnregisteredId;
    566   ObjectIdSet ids;
    567   ids.insert(id);
    568 
    569   EXPECT_EQ(0U, GetInvalidationCount(id));
    570 
    571   int64 initial_version = kVersion1;
    572   for (int64 i = initial_version; i < initial_version + kRepeatCount; ++i) {
    573     FireInvalidate(id, i, kPayload1);
    574   }
    575 
    576   EnableNotifications();
    577   listener_.Ready(fake_invalidation_client_);
    578   listener_.UpdateRegisteredIds(ids);
    579 
    580   ASSERT_EQ(UnackedInvalidationSet::kMaxBufferedInvalidations,
    581             GetInvalidationCount(id));
    582   ASSERT_FALSE(IsUnknownVersion(id));
    583   EXPECT_EQ(initial_version + kRepeatCount - 1, GetVersion(id));
    584   EXPECT_EQ(kPayload1, GetPayload(id));
    585   EXPECT_TRUE(StartsWithUnknownVersion(id));
    586 }
    587 
    588 // Fire an invalidation, then fire another one with a lower version.  Both
    589 // should be received.
    590 TEST_F(SyncInvalidationListenerTest, InvalidateVersion) {
    591   const ObjectId& id = kPreferencesId_;
    592 
    593   FireInvalidate(id, kVersion2, kPayload2);
    594 
    595   ASSERT_EQ(1U, GetInvalidationCount(id));
    596   ASSERT_FALSE(IsUnknownVersion(id));
    597   EXPECT_EQ(kVersion2, GetVersion(id));
    598   EXPECT_EQ(kPayload2, GetPayload(id));
    599 
    600   FireInvalidate(id, kVersion1, kPayload1);
    601 
    602   ASSERT_EQ(2U, GetInvalidationCount(id));
    603   ASSERT_FALSE(IsUnknownVersion(id));
    604 
    605   EXPECT_EQ(kVersion1, GetVersion(id));
    606   EXPECT_EQ(kPayload1, GetPayload(id));
    607 }
    608 
    609 // Fire an invalidation with an unknown version.
    610 TEST_F(SyncInvalidationListenerTest, InvalidateUnknownVersion) {
    611   const ObjectId& id = kBookmarksId_;
    612 
    613   FireInvalidateUnknownVersion(id);
    614 
    615   ASSERT_EQ(1U, GetInvalidationCount(id));
    616   EXPECT_TRUE(IsUnknownVersion(id));
    617 }
    618 
    619 // Fire an invalidation for all enabled IDs.
    620 TEST_F(SyncInvalidationListenerTest, InvalidateAll) {
    621   FireInvalidateAll();
    622 
    623   for (ObjectIdSet::const_iterator it = registered_ids_.begin();
    624        it != registered_ids_.end(); ++it) {
    625     ASSERT_EQ(1U, GetInvalidationCount(*it));
    626     EXPECT_TRUE(IsUnknownVersion(*it));
    627   }
    628 }
    629 
    630 // Test a simple scenario for multiple IDs.
    631 TEST_F(SyncInvalidationListenerTest, InvalidateMultipleIds) {
    632   FireInvalidate(kBookmarksId_, 3, NULL);
    633 
    634   ASSERT_EQ(1U, GetInvalidationCount(kBookmarksId_));
    635   ASSERT_FALSE(IsUnknownVersion(kBookmarksId_));
    636   EXPECT_EQ(3, GetVersion(kBookmarksId_));
    637   EXPECT_EQ("", GetPayload(kBookmarksId_));
    638 
    639   // kExtensionId is not registered, so the invalidation should not get through.
    640   FireInvalidate(kExtensionsId_, 2, NULL);
    641   ASSERT_EQ(0U, GetInvalidationCount(kExtensionsId_));
    642 }
    643 
    644 // Registration tests.
    645 
    646 // With IDs already registered, enable notifications then ready the
    647 // client.  The IDs should be registered only after the client is
    648 // readied.
    649 TEST_F(SyncInvalidationListenerTest, RegisterEnableReady) {
    650   EXPECT_TRUE(GetRegisteredIds().empty());
    651 
    652   EnableNotifications();
    653 
    654   EXPECT_TRUE(GetRegisteredIds().empty());
    655 
    656   listener_.Ready(fake_invalidation_client_);
    657 
    658   EXPECT_EQ(registered_ids_, GetRegisteredIds());
    659 }
    660 
    661 // With IDs already registered, ready the client then enable
    662 // notifications.  The IDs should be registered after the client is
    663 // readied.
    664 TEST_F(SyncInvalidationListenerTest, RegisterReadyEnable) {
    665   EXPECT_TRUE(GetRegisteredIds().empty());
    666 
    667   listener_.Ready(fake_invalidation_client_);
    668 
    669   EXPECT_EQ(registered_ids_, GetRegisteredIds());
    670 
    671   EnableNotifications();
    672 
    673   EXPECT_EQ(registered_ids_, GetRegisteredIds());
    674 }
    675 
    676 // Unregister the IDs, enable notifications, re-register the IDs, then
    677 // ready the client.  The IDs should be registered only after the
    678 // client is readied.
    679 TEST_F(SyncInvalidationListenerTest, EnableRegisterReady) {
    680   listener_.UpdateRegisteredIds(ObjectIdSet());
    681 
    682   EXPECT_TRUE(GetRegisteredIds().empty());
    683 
    684   EnableNotifications();
    685 
    686   EXPECT_TRUE(GetRegisteredIds().empty());
    687 
    688   listener_.UpdateRegisteredIds(registered_ids_);
    689 
    690   EXPECT_TRUE(GetRegisteredIds().empty());
    691 
    692   listener_.Ready(fake_invalidation_client_);
    693 
    694   EXPECT_EQ(registered_ids_, GetRegisteredIds());
    695 }
    696 
    697 // Unregister the IDs, enable notifications, ready the client, then
    698 // re-register the IDs.  The IDs should be registered only after the
    699 // client is readied.
    700 TEST_F(SyncInvalidationListenerTest, EnableReadyRegister) {
    701   listener_.UpdateRegisteredIds(ObjectIdSet());
    702 
    703   EXPECT_TRUE(GetRegisteredIds().empty());
    704 
    705   EnableNotifications();
    706 
    707   EXPECT_TRUE(GetRegisteredIds().empty());
    708 
    709   listener_.Ready(fake_invalidation_client_);
    710 
    711   EXPECT_TRUE(GetRegisteredIds().empty());
    712 
    713   listener_.UpdateRegisteredIds(registered_ids_);
    714 
    715   EXPECT_EQ(registered_ids_, GetRegisteredIds());
    716 }
    717 
    718 // Unregister the IDs, ready the client, enable notifications, then
    719 // re-register the IDs.  The IDs should be registered only after the
    720 // client is readied.
    721 TEST_F(SyncInvalidationListenerTest, ReadyEnableRegister) {
    722   listener_.UpdateRegisteredIds(ObjectIdSet());
    723 
    724   EXPECT_TRUE(GetRegisteredIds().empty());
    725 
    726   EnableNotifications();
    727 
    728   EXPECT_TRUE(GetRegisteredIds().empty());
    729 
    730   listener_.Ready(fake_invalidation_client_);
    731 
    732   EXPECT_TRUE(GetRegisteredIds().empty());
    733 
    734   listener_.UpdateRegisteredIds(registered_ids_);
    735 
    736   EXPECT_EQ(registered_ids_, GetRegisteredIds());
    737 }
    738 
    739 // Unregister the IDs, ready the client, re-register the IDs, then
    740 // enable notifications. The IDs should be registered only after the
    741 // client is readied.
    742 //
    743 // This test is important: see http://crbug.com/139424.
    744 TEST_F(SyncInvalidationListenerTest, ReadyRegisterEnable) {
    745   listener_.UpdateRegisteredIds(ObjectIdSet());
    746 
    747   EXPECT_TRUE(GetRegisteredIds().empty());
    748 
    749   listener_.Ready(fake_invalidation_client_);
    750 
    751   EXPECT_TRUE(GetRegisteredIds().empty());
    752 
    753   listener_.UpdateRegisteredIds(registered_ids_);
    754 
    755   EXPECT_EQ(registered_ids_, GetRegisteredIds());
    756 
    757   EnableNotifications();
    758 
    759   EXPECT_EQ(registered_ids_, GetRegisteredIds());
    760 }
    761 
    762 // With IDs already registered, ready the client, restart the client,
    763 // then re-ready it.  The IDs should still be registered.
    764 TEST_F(SyncInvalidationListenerTest, RegisterTypesPreserved) {
    765   EXPECT_TRUE(GetRegisteredIds().empty());
    766 
    767   listener_.Ready(fake_invalidation_client_);
    768 
    769   EXPECT_EQ(registered_ids_, GetRegisteredIds());
    770 
    771   RestartClient();
    772 
    773   EXPECT_TRUE(GetRegisteredIds().empty());
    774 
    775   listener_.Ready(fake_invalidation_client_);
    776 
    777   EXPECT_EQ(registered_ids_, GetRegisteredIds());
    778 }
    779 
    780 // Make sure that state is correctly purged from the local invalidation state
    781 // map cache when an ID is unregistered.
    782 TEST_F(SyncInvalidationListenerTest, UnregisterCleansUpStateMapCache) {
    783   const ObjectId& id = kBookmarksId_;
    784   listener_.Ready(fake_invalidation_client_);
    785 
    786   EXPECT_TRUE(GetSavedInvalidations().empty());
    787   FireInvalidate(id, 1, "hello");
    788   EXPECT_EQ(1U, GetSavedInvalidations().size());
    789   EXPECT_TRUE(ContainsKey(GetSavedInvalidations(), id));
    790   FireInvalidate(kPreferencesId_, 2, "world");
    791   EXPECT_EQ(2U, GetSavedInvalidations().size());
    792 
    793   EXPECT_TRUE(ContainsKey(GetSavedInvalidations(), id));
    794   EXPECT_TRUE(ContainsKey(GetSavedInvalidations(), kPreferencesId_));
    795 
    796   ObjectIdSet ids;
    797   ids.insert(id);
    798   listener_.UpdateRegisteredIds(ids);
    799   EXPECT_EQ(1U, GetSavedInvalidations().size());
    800   EXPECT_TRUE(ContainsKey(GetSavedInvalidations(), id));
    801 }
    802 
    803 TEST_F(SyncInvalidationListenerTest, DuplicateInvaldiations_Simple) {
    804   const ObjectId& id = kBookmarksId_;
    805   listener_.Ready(fake_invalidation_client_);
    806 
    807   // Send a stream of invalidations, including two copies of the second.
    808   FireInvalidate(id, 1, "one");
    809   FireInvalidate(id, 2, "two");
    810   FireInvalidate(id, 3, "three");
    811   FireInvalidate(id, 2, "two");
    812 
    813   // Expect that the duplicate was discarded.
    814   SingleObjectInvalidationSet list = GetSavedInvalidationsForType(id);
    815   EXPECT_EQ(3U, list.GetSize());
    816   SingleObjectInvalidationSet::const_iterator it = list.begin();
    817   EXPECT_EQ(1, it->version());
    818   it++;
    819   EXPECT_EQ(2, it->version());
    820   it++;
    821   EXPECT_EQ(3, it->version());
    822 }
    823 
    824 TEST_F(SyncInvalidationListenerTest, DuplicateInvalidations_NearBufferLimit) {
    825   const size_t kPairsToSend = UnackedInvalidationSet::kMaxBufferedInvalidations;
    826   const ObjectId& id = kBookmarksId_;
    827   listener_.Ready(fake_invalidation_client_);
    828 
    829   // We will have enough buffer space in the state tracker for all these
    830   // invalidations only if duplicates are ignored.
    831   for (size_t i = 0; i < kPairsToSend; ++i) {
    832     FireInvalidate(id, i, "payload");
    833     FireInvalidate(id, i, "payload");
    834   }
    835 
    836   // Expect that the state map ignored duplicates.
    837   SingleObjectInvalidationSet list = GetSavedInvalidationsForType(id);
    838   EXPECT_EQ(kPairsToSend, list.GetSize());
    839   EXPECT_FALSE(list.begin()->is_unknown_version());
    840 
    841   // Expect that all invalidations (including duplicates) were emitted.
    842   EXPECT_EQ(kPairsToSend*2, GetInvalidationCount(id));
    843 
    844   // Acknowledge all invalidations to clear the internal state.
    845   AcknowledgeAll(id);
    846   EXPECT_TRUE(GetSavedInvalidationsForType(id).IsEmpty());
    847 }
    848 
    849 TEST_F(SyncInvalidationListenerTest, DuplicateInvalidations_UnknownVersion) {
    850   const ObjectId& id = kBookmarksId_;
    851   listener_.Ready(fake_invalidation_client_);
    852 
    853   FireInvalidateUnknownVersion(id);
    854   FireInvalidateUnknownVersion(id);
    855 
    856   {
    857     SingleObjectInvalidationSet list = GetSavedInvalidationsForType(id);
    858     EXPECT_EQ(1U, list.GetSize());
    859   }
    860 
    861   // Acknowledge the second.  There should be no effect on the stored list.
    862   ASSERT_EQ(2U, GetInvalidationCount(id));
    863   AcknowledgeNthInvalidation(id, 1);
    864   {
    865     SingleObjectInvalidationSet list = GetSavedInvalidationsForType(id);
    866     EXPECT_EQ(1U, list.GetSize());
    867   }
    868 
    869   // Acknowledge the first.  This should remove the invalidation from the list.
    870   ASSERT_EQ(2U, GetInvalidationCount(id));
    871   AcknowledgeNthInvalidation(id, 0);
    872   {
    873     SingleObjectInvalidationSet list = GetSavedInvalidationsForType(id);
    874     EXPECT_EQ(0U, list.GetSize());
    875   }
    876 }
    877 
    878 // Make sure that acknowledgements erase items from the local store.
    879 TEST_F(SyncInvalidationListenerTest, AcknowledgementsCleanUpStateMapCache) {
    880   const ObjectId& id = kBookmarksId_;
    881   listener_.Ready(fake_invalidation_client_);
    882 
    883   EXPECT_TRUE(GetSavedInvalidations().empty());
    884   FireInvalidate(id, 10, "hello");
    885   FireInvalidate(id, 20, "world");
    886   FireInvalidateUnknownVersion(id);
    887 
    888   // Expect that all three invalidations have been saved to permanent storage.
    889   {
    890     SingleObjectInvalidationSet list = GetSavedInvalidationsForType(id);
    891     ASSERT_EQ(3U, list.GetSize());
    892     EXPECT_TRUE(list.begin()->is_unknown_version());
    893     EXPECT_EQ(20, list.back().version());
    894   }
    895 
    896   // Acknowledge the second sent invaldiation (version 20) and verify it was
    897   // removed from storage.
    898   AcknowledgeNthInvalidation(id, 1);
    899   {
    900     SingleObjectInvalidationSet list = GetSavedInvalidationsForType(id);
    901     ASSERT_EQ(2U, list.GetSize());
    902     EXPECT_TRUE(list.begin()->is_unknown_version());
    903     EXPECT_EQ(10, list.back().version());
    904   }
    905 
    906   // Acknowledge the last sent invalidation (unknown version) and verify it was
    907   // removed from storage.
    908   AcknowledgeNthInvalidation(id, 2);
    909   {
    910     SingleObjectInvalidationSet list = GetSavedInvalidationsForType(id);
    911     ASSERT_EQ(1U, list.GetSize());
    912     EXPECT_FALSE(list.begin()->is_unknown_version());
    913     EXPECT_EQ(10, list.back().version());
    914   }
    915 }
    916 
    917 // Make sure that drops erase items from the local store.
    918 TEST_F(SyncInvalidationListenerTest, DropsCleanUpStateMapCache) {
    919   const ObjectId& id = kBookmarksId_;
    920   listener_.Ready(fake_invalidation_client_);
    921 
    922   EXPECT_TRUE(GetSavedInvalidations().empty());
    923   FireInvalidate(id, 10, "hello");
    924   FireInvalidate(id, 20, "world");
    925   FireInvalidateUnknownVersion(id);
    926 
    927   // Expect that all three invalidations have been saved to permanent storage.
    928   {
    929     SingleObjectInvalidationSet list = GetSavedInvalidationsForType(id);
    930     ASSERT_EQ(3U, list.GetSize());
    931     EXPECT_TRUE(list.begin()->is_unknown_version());
    932     EXPECT_EQ(20, list.back().version());
    933   }
    934 
    935   // Drop the second sent invalidation (version 20) and verify it was removed
    936   // from storage.  Also verify we still have an unknown version invalidation.
    937   DropNthInvalidation(id, 1);
    938   {
    939     SingleObjectInvalidationSet list = GetSavedInvalidationsForType(id);
    940     ASSERT_EQ(2U, list.GetSize());
    941     EXPECT_TRUE(list.begin()->is_unknown_version());
    942     EXPECT_EQ(10, list.back().version());
    943   }
    944 
    945   // Drop the remaining invalidation.  Verify an unknown version is all that
    946   // remains.
    947   DropNthInvalidation(id, 0);
    948   {
    949     SingleObjectInvalidationSet list = GetSavedInvalidationsForType(id);
    950     ASSERT_EQ(1U, list.GetSize());
    951     EXPECT_TRUE(list.begin()->is_unknown_version());
    952   }
    953 
    954   // Announce that the delegate has recovered from the drop.  Verify no
    955   // invalidations remain saved.
    956   RecoverFromDropEvent(id);
    957   EXPECT_TRUE(GetSavedInvalidationsForType(id).IsEmpty());
    958 
    959   RecoverFromDropEvent(id);
    960 }
    961 
    962 // Without readying the client, disable notifications, then enable
    963 // them.  The listener should still think notifications are disabled.
    964 TEST_F(SyncInvalidationListenerTest, EnableNotificationsNotReady) {
    965   EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR,
    966             GetInvalidatorState());
    967 
    968   DisableNotifications(
    969       notifier::TRANSIENT_NOTIFICATION_ERROR);
    970 
    971   EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, GetInvalidatorState());
    972 
    973   DisableNotifications(notifier::NOTIFICATION_CREDENTIALS_REJECTED);
    974 
    975   EXPECT_EQ(INVALIDATION_CREDENTIALS_REJECTED, GetInvalidatorState());
    976 
    977   EnableNotifications();
    978 
    979   EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, GetInvalidatorState());
    980 }
    981 
    982 // Enable notifications then Ready the invalidation client.  The
    983 // delegate should then be ready.
    984 TEST_F(SyncInvalidationListenerTest, EnableNotificationsThenReady) {
    985   EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, GetInvalidatorState());
    986 
    987   EnableNotifications();
    988 
    989   EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, GetInvalidatorState());
    990 
    991   listener_.Ready(fake_invalidation_client_);
    992 
    993   EXPECT_EQ(INVALIDATIONS_ENABLED, GetInvalidatorState());
    994 }
    995 
    996 // Ready the invalidation client then enable notifications.  The
    997 // delegate should then be ready.
    998 TEST_F(SyncInvalidationListenerTest, ReadyThenEnableNotifications) {
    999   EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, GetInvalidatorState());
   1000 
   1001   listener_.Ready(fake_invalidation_client_);
   1002 
   1003   EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, GetInvalidatorState());
   1004 
   1005   EnableNotifications();
   1006 
   1007   EXPECT_EQ(INVALIDATIONS_ENABLED, GetInvalidatorState());
   1008 }
   1009 
   1010 // Enable notifications and ready the client.  Then disable
   1011 // notifications with an auth error and re-enable notifications.  The
   1012 // delegate should go into an auth error mode and then back out.
   1013 TEST_F(SyncInvalidationListenerTest, PushClientAuthError) {
   1014   EnableNotifications();
   1015   listener_.Ready(fake_invalidation_client_);
   1016 
   1017   EXPECT_EQ(INVALIDATIONS_ENABLED, GetInvalidatorState());
   1018 
   1019   DisableNotifications(
   1020       notifier::NOTIFICATION_CREDENTIALS_REJECTED);
   1021 
   1022   EXPECT_EQ(INVALIDATION_CREDENTIALS_REJECTED, GetInvalidatorState());
   1023 
   1024   EnableNotifications();
   1025 
   1026   EXPECT_EQ(INVALIDATIONS_ENABLED, GetInvalidatorState());
   1027 }
   1028 
   1029 // Enable notifications and ready the client.  Then simulate an auth
   1030 // error from the invalidation client.  Simulate some notification
   1031 // events, then re-ready the client.  The delegate should go into an
   1032 // auth error mode and come out of it only after the client is ready.
   1033 TEST_F(SyncInvalidationListenerTest, InvalidationClientAuthError) {
   1034   EnableNotifications();
   1035   listener_.Ready(fake_invalidation_client_);
   1036 
   1037   EXPECT_EQ(INVALIDATIONS_ENABLED, GetInvalidatorState());
   1038 
   1039   listener_.InformError(
   1040       fake_invalidation_client_,
   1041       invalidation::ErrorInfo(
   1042           invalidation::ErrorReason::AUTH_FAILURE,
   1043           false /* is_transient */,
   1044           "auth error",
   1045           invalidation::ErrorContext()));
   1046 
   1047   EXPECT_EQ(INVALIDATION_CREDENTIALS_REJECTED, GetInvalidatorState());
   1048 
   1049   DisableNotifications(notifier::TRANSIENT_NOTIFICATION_ERROR);
   1050 
   1051   EXPECT_EQ(INVALIDATION_CREDENTIALS_REJECTED, GetInvalidatorState());
   1052 
   1053   DisableNotifications(notifier::TRANSIENT_NOTIFICATION_ERROR);
   1054 
   1055   EXPECT_EQ(INVALIDATION_CREDENTIALS_REJECTED, GetInvalidatorState());
   1056 
   1057   EnableNotifications();
   1058 
   1059   EXPECT_EQ(INVALIDATION_CREDENTIALS_REJECTED, GetInvalidatorState());
   1060 
   1061   listener_.Ready(fake_invalidation_client_);
   1062 
   1063   EXPECT_EQ(INVALIDATIONS_ENABLED, GetInvalidatorState());
   1064 }
   1065 
   1066 // A variant of SyncInvalidationListenerTest that starts with some initial
   1067 // state.  We make not attempt to abstract away the contents of this state.  The
   1068 // tests that make use of this harness depend on its implementation details.
   1069 class SyncInvalidationListenerTest_WithInitialState
   1070     : public SyncInvalidationListenerTest {
   1071  public:
   1072   virtual void SetUp() {
   1073     UnackedInvalidationSet bm_state(kBookmarksId_);
   1074     UnackedInvalidationSet ext_state(kExtensionsId_);
   1075 
   1076     Invalidation bm_unknown = Invalidation::InitUnknownVersion(kBookmarksId_);
   1077     Invalidation bm_v100 = Invalidation::Init(kBookmarksId_, 100, "hundred");
   1078     bm_state.Add(bm_unknown);
   1079     bm_state.Add(bm_v100);
   1080 
   1081     Invalidation ext_v10 = Invalidation::Init(kExtensionsId_, 10, "ten");
   1082     Invalidation ext_v20 = Invalidation::Init(kExtensionsId_, 20, "twenty");
   1083     ext_state.Add(ext_v10);
   1084     ext_state.Add(ext_v20);
   1085 
   1086     initial_state.insert(std::make_pair(kBookmarksId_, bm_state));
   1087     initial_state.insert(std::make_pair(kExtensionsId_, ext_state));
   1088 
   1089     fake_tracker_.SetSavedInvalidations(initial_state);
   1090 
   1091     SyncInvalidationListenerTest::SetUp();
   1092   }
   1093 
   1094   UnackedInvalidationsMap initial_state;
   1095 };
   1096 
   1097 // Verify that saved invalidations are forwarded when handlers register.
   1098 TEST_F(SyncInvalidationListenerTest_WithInitialState,
   1099        ReceiveSavedInvalidations) {
   1100   EnableNotifications();
   1101   listener_.Ready(fake_invalidation_client_);
   1102 
   1103   EXPECT_THAT(initial_state, test_util::Eq(GetSavedInvalidations()));
   1104 
   1105   ASSERT_EQ(2U, GetInvalidationCount(kBookmarksId_));
   1106   EXPECT_EQ(100, GetVersion(kBookmarksId_));
   1107 
   1108   ASSERT_EQ(0U, GetInvalidationCount(kExtensionsId_));
   1109 
   1110   FireInvalidate(kExtensionsId_, 30, "thirty");
   1111 
   1112   ObjectIdSet ids = GetRegisteredIds();
   1113   ids.insert(kExtensionsId_);
   1114   listener_.UpdateRegisteredIds(ids);
   1115 
   1116   ASSERT_EQ(3U, GetInvalidationCount(kExtensionsId_));
   1117   EXPECT_EQ(30, GetVersion(kExtensionsId_));
   1118 }
   1119 
   1120 }  // namespace
   1121 
   1122 }  // namespace syncer
   1123