Home | History | Annotate | Download | only in invalidation
      1 // Copyright 2013 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 // This class defines tests that implementations of InvalidationService should
      6 // pass in order to be conformant.  Here's how you use it to test your
      7 // implementation.
      8 //
      9 // Say your class is called MyInvalidationService.  Then you need to define a
     10 // class called MyInvalidationServiceTestDelegate in
     11 // my_invalidation_frontend_unittest.cc like this:
     12 //
     13 //   class MyInvalidationServiceTestDelegate {
     14 //    public:
     15 //     MyInvalidationServiceTestDelegate() ...
     16 //
     17 //     ~MyInvalidationServiceTestDelegate() {
     18 //       // DestroyInvalidator() may not be explicitly called by tests.
     19 //       DestroyInvalidator();
     20 //     }
     21 //
     22 //     // Create the InvalidationService implementation with the given params.
     23 //     void CreateInvalidationService() {
     24 //       ...
     25 //     }
     26 //
     27 //     // Should return the InvalidationService implementation.  Only called
     28 //     // after CreateInvalidator and before DestroyInvalidator.
     29 //     MyInvalidationService* GetInvalidationService() {
     30 //       ...
     31 //     }
     32 //
     33 //     // Destroy the InvalidationService implementation.
     34 //     void DestroyInvalidationService() {
     35 //       ...
     36 //     }
     37 //
     38 //     // The Trigger* functions below should block until the effects of
     39 //     // the call are visible on the current thread.
     40 //
     41 //     // Should cause OnInvalidatorStateChange() to be called on all
     42 //     // observers of the InvalidationService implementation with the given
     43 //     // parameters.
     44 //     void TriggerOnInvalidatorStateChange(InvalidatorState state) {
     45 //       ...
     46 //     }
     47 //
     48 //     // Should cause OnIncomingInvalidation() to be called on all
     49 //     // observers of the InvalidationService implementation with the given
     50 //     // parameters.
     51 //     void TriggerOnIncomingInvalidation(
     52 //         const ObjectIdInvalidationMap& invalidation_map) {
     53 //       ...
     54 //     }
     55 //   };
     56 //
     57 // The InvalidationServiceTest test harness will have a member variable of
     58 // this delegate type and will call its functions in the various
     59 // tests.
     60 //
     61 // Then you simply #include this file as well as gtest.h and add the
     62 // following statement to my_sync_notifier_unittest.cc:
     63 //
     64 //   INSTANTIATE_TYPED_TEST_CASE_P(
     65 //       MyInvalidationService,
     66 //       InvalidationServiceTest,
     67 //       MyInvalidatorTestDelegate);
     68 //
     69 // Easy!
     70 
     71 #ifndef CHROME_BROWSER_INVALIDATION_INVALIDATION_SERVICE_TEST_TEMPLATE_H_
     72 #define CHROME_BROWSER_INVALIDATION_INVALIDATION_SERVICE_TEST_TEMPLATE_H_
     73 
     74 #include "base/basictypes.h"
     75 #include "base/compiler_specific.h"
     76 #include "chrome/browser/invalidation/invalidation_service.h"
     77 #include "google/cacheinvalidation/include/types.h"
     78 #include "google/cacheinvalidation/types.pb.h"
     79 #include "sync/notifier/fake_invalidation_handler.h"
     80 #include "sync/notifier/object_id_invalidation_map.h"
     81 #include "sync/notifier/object_id_invalidation_map_test_util.h"
     82 #include "testing/gtest/include/gtest/gtest.h"
     83 
     84 template <typename InvalidatorTestDelegate>
     85 class InvalidationServiceTest : public testing::Test {
     86  protected:
     87   InvalidationServiceTest()
     88       : id1(ipc::invalidation::ObjectSource::CHROME_SYNC, "BOOKMARK"),
     89         id2(ipc::invalidation::ObjectSource::CHROME_SYNC, "PREFERENCE"),
     90         id3(ipc::invalidation::ObjectSource::CHROME_SYNC, "AUTOFILL"),
     91         id4(ipc::invalidation::ObjectSource::CHROME_PUSH_MESSAGING,
     92             "PUSH_MESSAGE") {
     93   }
     94 
     95   invalidation::InvalidationService*
     96   CreateAndInitializeInvalidationService() {
     97     this->delegate_.CreateInvalidationService();
     98     return this->delegate_.GetInvalidationService();
     99   }
    100 
    101   InvalidatorTestDelegate delegate_;
    102 
    103   const invalidation::ObjectId id1;
    104   const invalidation::ObjectId id2;
    105   const invalidation::ObjectId id3;
    106   const invalidation::ObjectId id4;
    107 };
    108 
    109 TYPED_TEST_CASE_P(InvalidationServiceTest);
    110 
    111 // Initialize the invalidator, register a handler, register some IDs for that
    112 // handler, and then unregister the handler, dispatching invalidations in
    113 // between.  The handler should only see invalidations when its registered and
    114 // its IDs are registered.
    115 TYPED_TEST_P(InvalidationServiceTest, Basic) {
    116   invalidation::InvalidationService* const invalidator =
    117       this->CreateAndInitializeInvalidationService();
    118 
    119   syncer::FakeInvalidationHandler handler;
    120 
    121   invalidator->RegisterInvalidationHandler(&handler);
    122 
    123   syncer::ObjectIdInvalidationMap states;
    124   states[this->id1].payload = "1";
    125   states[this->id2].payload = "2";
    126   states[this->id3].payload = "3";
    127 
    128   // Should be ignored since no IDs are registered to |handler|.
    129   this->delegate_.TriggerOnIncomingInvalidation(states);
    130   EXPECT_EQ(0, handler.GetInvalidationCount());
    131 
    132   syncer::ObjectIdSet ids;
    133   ids.insert(this->id1);
    134   ids.insert(this->id2);
    135   invalidator->UpdateRegisteredInvalidationIds(&handler, ids);
    136 
    137   this->delegate_.TriggerOnInvalidatorStateChange(
    138       syncer::INVALIDATIONS_ENABLED);
    139   EXPECT_EQ(syncer::INVALIDATIONS_ENABLED, handler.GetInvalidatorState());
    140 
    141   syncer::ObjectIdInvalidationMap expected_states;
    142   expected_states[this->id1].payload = "1";
    143   expected_states[this->id2].payload = "2";
    144 
    145   this->delegate_.TriggerOnIncomingInvalidation(states);
    146   EXPECT_EQ(1, handler.GetInvalidationCount());
    147   EXPECT_THAT(expected_states, Eq(handler.GetLastInvalidationMap()));
    148 
    149   ids.erase(this->id1);
    150   ids.insert(this->id3);
    151   invalidator->UpdateRegisteredInvalidationIds(&handler, ids);
    152 
    153   expected_states.erase(this->id1);
    154   expected_states[this->id3].payload = "3";
    155 
    156   // Removed object IDs should not be notified, newly-added ones should.
    157   this->delegate_.TriggerOnIncomingInvalidation(states);
    158   EXPECT_EQ(2, handler.GetInvalidationCount());
    159   EXPECT_THAT(expected_states, Eq(handler.GetLastInvalidationMap()));
    160 
    161   this->delegate_.TriggerOnInvalidatorStateChange(
    162       syncer::TRANSIENT_INVALIDATION_ERROR);
    163   EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
    164             handler.GetInvalidatorState());
    165 
    166   this->delegate_.TriggerOnInvalidatorStateChange(
    167       syncer::INVALIDATIONS_ENABLED);
    168   EXPECT_EQ(syncer::INVALIDATIONS_ENABLED,
    169             handler.GetInvalidatorState());
    170 
    171   invalidator->UnregisterInvalidationHandler(&handler);
    172 
    173   // Should be ignored since |handler| isn't registered anymore.
    174   this->delegate_.TriggerOnIncomingInvalidation(states);
    175   EXPECT_EQ(2, handler.GetInvalidationCount());
    176 }
    177 
    178 // Register handlers and some IDs for those handlers, register a handler with
    179 // no IDs, and register a handler with some IDs but unregister it.  Then,
    180 // dispatch some invalidations and invalidations.  Handlers that are registered
    181 // should get invalidations, and the ones that have registered IDs should
    182 // receive invalidations for those IDs.
    183 TYPED_TEST_P(InvalidationServiceTest, MultipleHandlers) {
    184   invalidation::InvalidationService* const invalidator =
    185       this->CreateAndInitializeInvalidationService();
    186 
    187   syncer::FakeInvalidationHandler handler1;
    188   syncer::FakeInvalidationHandler handler2;
    189   syncer::FakeInvalidationHandler handler3;
    190   syncer::FakeInvalidationHandler handler4;
    191 
    192   invalidator->RegisterInvalidationHandler(&handler1);
    193   invalidator->RegisterInvalidationHandler(&handler2);
    194   invalidator->RegisterInvalidationHandler(&handler3);
    195   invalidator->RegisterInvalidationHandler(&handler4);
    196 
    197   {
    198     syncer::ObjectIdSet ids;
    199     ids.insert(this->id1);
    200     ids.insert(this->id2);
    201     invalidator->UpdateRegisteredInvalidationIds(&handler1, ids);
    202   }
    203 
    204   {
    205     syncer::ObjectIdSet ids;
    206     ids.insert(this->id3);
    207     invalidator->UpdateRegisteredInvalidationIds(&handler2, ids);
    208   }
    209 
    210   // Don't register any IDs for handler3.
    211 
    212   {
    213     syncer::ObjectIdSet ids;
    214     ids.insert(this->id4);
    215     invalidator->UpdateRegisteredInvalidationIds(&handler4, ids);
    216   }
    217 
    218   invalidator->UnregisterInvalidationHandler(&handler4);
    219 
    220   this->delegate_.TriggerOnInvalidatorStateChange(
    221       syncer::INVALIDATIONS_ENABLED);
    222   EXPECT_EQ(syncer::INVALIDATIONS_ENABLED, handler1.GetInvalidatorState());
    223   EXPECT_EQ(syncer::INVALIDATIONS_ENABLED, handler2.GetInvalidatorState());
    224   EXPECT_EQ(syncer::INVALIDATIONS_ENABLED, handler3.GetInvalidatorState());
    225   EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
    226             handler4.GetInvalidatorState());
    227 
    228   {
    229     syncer::ObjectIdInvalidationMap states;
    230     states[this->id1].payload = "1";
    231     states[this->id2].payload = "2";
    232     states[this->id3].payload = "3";
    233     states[this->id4].payload = "4";
    234     this->delegate_.TriggerOnIncomingInvalidation(states);
    235 
    236     syncer::ObjectIdInvalidationMap expected_states;
    237     expected_states[this->id1].payload = "1";
    238     expected_states[this->id2].payload = "2";
    239 
    240     EXPECT_EQ(1, handler1.GetInvalidationCount());
    241     EXPECT_THAT(expected_states, Eq(handler1.GetLastInvalidationMap()));
    242 
    243     expected_states.clear();
    244     expected_states[this->id3].payload = "3";
    245 
    246     EXPECT_EQ(1, handler2.GetInvalidationCount());
    247     EXPECT_THAT(expected_states, Eq(handler2.GetLastInvalidationMap()));
    248 
    249     EXPECT_EQ(0, handler3.GetInvalidationCount());
    250     EXPECT_EQ(0, handler4.GetInvalidationCount());
    251   }
    252 
    253   this->delegate_.TriggerOnInvalidatorStateChange(
    254       syncer::TRANSIENT_INVALIDATION_ERROR);
    255   EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
    256             handler1.GetInvalidatorState());
    257   EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
    258             handler2.GetInvalidatorState());
    259   EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
    260             handler3.GetInvalidatorState());
    261   EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
    262             handler4.GetInvalidatorState());
    263 
    264   invalidator->UnregisterInvalidationHandler(&handler3);
    265   invalidator->UnregisterInvalidationHandler(&handler2);
    266   invalidator->UnregisterInvalidationHandler(&handler1);
    267 }
    268 
    269 // Make sure that passing an empty set to UpdateRegisteredInvalidationIds clears
    270 // the corresponding entries for the handler.
    271 TYPED_TEST_P(InvalidationServiceTest, EmptySetUnregisters) {
    272   invalidation::InvalidationService* const invalidator =
    273       this->CreateAndInitializeInvalidationService();
    274 
    275   syncer::FakeInvalidationHandler handler1;
    276 
    277   // Control observer.
    278   syncer::FakeInvalidationHandler handler2;
    279 
    280   invalidator->RegisterInvalidationHandler(&handler1);
    281   invalidator->RegisterInvalidationHandler(&handler2);
    282 
    283   {
    284     syncer::ObjectIdSet ids;
    285     ids.insert(this->id1);
    286     ids.insert(this->id2);
    287     invalidator->UpdateRegisteredInvalidationIds(&handler1, ids);
    288   }
    289 
    290   {
    291     syncer::ObjectIdSet ids;
    292     ids.insert(this->id3);
    293     invalidator->UpdateRegisteredInvalidationIds(&handler2, ids);
    294   }
    295 
    296   // Unregister the IDs for the first observer. It should not receive any
    297   // further invalidations.
    298   invalidator->UpdateRegisteredInvalidationIds(&handler1,
    299                                                syncer::ObjectIdSet());
    300 
    301   this->delegate_.TriggerOnInvalidatorStateChange(
    302       syncer::INVALIDATIONS_ENABLED);
    303   EXPECT_EQ(syncer::INVALIDATIONS_ENABLED, handler1.GetInvalidatorState());
    304   EXPECT_EQ(syncer::INVALIDATIONS_ENABLED, handler2.GetInvalidatorState());
    305 
    306   {
    307     syncer::ObjectIdInvalidationMap states;
    308     states[this->id1].payload = "1";
    309     states[this->id2].payload = "2";
    310     states[this->id3].payload = "3";
    311     this->delegate_.TriggerOnIncomingInvalidation(states);
    312     EXPECT_EQ(0, handler1.GetInvalidationCount());
    313     EXPECT_EQ(1, handler2.GetInvalidationCount());
    314   }
    315 
    316   this->delegate_.TriggerOnInvalidatorStateChange(
    317       syncer::TRANSIENT_INVALIDATION_ERROR);
    318   EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
    319             handler1.GetInvalidatorState());
    320   EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
    321             handler2.GetInvalidatorState());
    322 
    323   invalidator->UnregisterInvalidationHandler(&handler2);
    324   invalidator->UnregisterInvalidationHandler(&handler1);
    325 }
    326 
    327 namespace internal {
    328 
    329 // A FakeInvalidationHandler that is "bound" to a specific
    330 // InvalidationService.  This is for cross-referencing state information with
    331 // the bound InvalidationService.
    332 class BoundFakeInvalidationHandler : public syncer::FakeInvalidationHandler {
    333  public:
    334   explicit BoundFakeInvalidationHandler(
    335       const invalidation::InvalidationService& invalidator);
    336   virtual ~BoundFakeInvalidationHandler();
    337 
    338   // Returns the last return value of GetInvalidatorState() on the
    339   // bound invalidator from the last time the invalidator state
    340   // changed.
    341   syncer::InvalidatorState GetLastRetrievedState() const;
    342 
    343   // InvalidationHandler implementation.
    344   virtual void OnInvalidatorStateChange(
    345       syncer::InvalidatorState state) OVERRIDE;
    346 
    347  private:
    348   const invalidation::InvalidationService& invalidator_;
    349   syncer::InvalidatorState last_retrieved_state_;
    350 
    351   DISALLOW_COPY_AND_ASSIGN(BoundFakeInvalidationHandler);
    352 };
    353 
    354 }  // namespace internal
    355 
    356 TYPED_TEST_P(InvalidationServiceTest, GetInvalidatorStateAlwaysCurrent) {
    357   invalidation::InvalidationService* const invalidator =
    358       this->CreateAndInitializeInvalidationService();
    359 
    360   internal::BoundFakeInvalidationHandler handler(*invalidator);
    361   invalidator->RegisterInvalidationHandler(&handler);
    362 
    363   this->delegate_.TriggerOnInvalidatorStateChange(
    364       syncer::INVALIDATIONS_ENABLED);
    365   EXPECT_EQ(syncer::INVALIDATIONS_ENABLED, handler.GetInvalidatorState());
    366   EXPECT_EQ(syncer::INVALIDATIONS_ENABLED, handler.GetLastRetrievedState());
    367 
    368   this->delegate_.TriggerOnInvalidatorStateChange(
    369       syncer::TRANSIENT_INVALIDATION_ERROR);
    370   EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
    371             handler.GetInvalidatorState());
    372   EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
    373             handler.GetLastRetrievedState());
    374 
    375   invalidator->UnregisterInvalidationHandler(&handler);
    376 }
    377 
    378 REGISTER_TYPED_TEST_CASE_P(InvalidationServiceTest,
    379                            Basic, MultipleHandlers, EmptySetUnregisters,
    380                            GetInvalidatorStateAlwaysCurrent);
    381 
    382 #endif  // CHROME_BROWSER_INVALIDATION_INVALIDATION_SERVICE_TEST_TEMPLATE_H_
    383