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