Home | History | Annotate | Download | only in notifier
      1 // Copyright (c) 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 // 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 SYNC_NOTIFIER_INVALIDATOR_TEST_TEMPLATE_H_
     78 #define SYNC_NOTIFIER_INVALIDATOR_TEST_TEMPLATE_H_
     79 
     80 #include "base/basictypes.h"
     81 #include "base/compiler_specific.h"
     82 #include "google/cacheinvalidation/include/types.h"
     83 #include "google/cacheinvalidation/types.pb.h"
     84 #include "sync/notifier/fake_invalidation_handler.h"
     85 #include "sync/notifier/fake_invalidation_state_tracker.h"
     86 #include "sync/notifier/invalidator.h"
     87 #include "sync/notifier/object_id_invalidation_map.h"
     88 #include "sync/notifier/object_id_invalidation_map_test_util.h"
     89 #include "testing/gtest/include/gtest/gtest.h"
     90 
     91 namespace syncer {
     92 
     93 template <typename InvalidatorTestDelegate>
     94 class InvalidatorTest : public testing::Test {
     95  protected:
     96   InvalidatorTest()
     97       : id1(ipc::invalidation::ObjectSource::TEST, "a"),
     98         id2(ipc::invalidation::ObjectSource::TEST, "b"),
     99         id3(ipc::invalidation::ObjectSource::TEST, "c"),
    100         id4(ipc::invalidation::ObjectSource::TEST, "d") {
    101   }
    102 
    103   Invalidator* CreateAndInitializeInvalidator() {
    104     this->delegate_.CreateInvalidator("fake_invalidator_client_id",
    105                                       "fake_initial_state",
    106                                       this->fake_tracker_.AsWeakPtr());
    107     Invalidator* const invalidator = this->delegate_.GetInvalidator();
    108 
    109     this->delegate_.WaitForInvalidator();
    110     invalidator->UpdateCredentials("foo (at) bar.com", "fake_token");
    111     this->delegate_.WaitForInvalidator();
    112 
    113     return invalidator;
    114   }
    115 
    116   FakeInvalidationStateTracker fake_tracker_;
    117   InvalidatorTestDelegate delegate_;
    118 
    119   const invalidation::ObjectId id1;
    120   const invalidation::ObjectId id2;
    121   const invalidation::ObjectId id3;
    122   const invalidation::ObjectId id4;
    123 };
    124 
    125 TYPED_TEST_CASE_P(InvalidatorTest);
    126 
    127 // Initialize the invalidator, register a handler, register some IDs for that
    128 // handler, and then unregister the handler, dispatching invalidations in
    129 // between.  The handler should only see invalidations when its registered and
    130 // its IDs are registered.
    131 TYPED_TEST_P(InvalidatorTest, Basic) {
    132   Invalidator* const invalidator = this->CreateAndInitializeInvalidator();
    133 
    134   FakeInvalidationHandler handler;
    135 
    136   invalidator->RegisterHandler(&handler);
    137 
    138   ObjectIdInvalidationMap states;
    139   states[this->id1].payload = "1";
    140   states[this->id2].payload = "2";
    141   states[this->id3].payload = "3";
    142 
    143   // Should be ignored since no IDs are registered to |handler|.
    144   this->delegate_.TriggerOnIncomingInvalidation(states);
    145   EXPECT_EQ(0, handler.GetInvalidationCount());
    146 
    147   ObjectIdSet ids;
    148   ids.insert(this->id1);
    149   ids.insert(this->id2);
    150   invalidator->UpdateRegisteredIds(&handler, ids);
    151 
    152   this->delegate_.TriggerOnInvalidatorStateChange(INVALIDATIONS_ENABLED);
    153   EXPECT_EQ(INVALIDATIONS_ENABLED, handler.GetInvalidatorState());
    154 
    155   ObjectIdInvalidationMap expected_states;
    156   expected_states[this->id1].payload = "1";
    157   expected_states[this->id2].payload = "2";
    158 
    159   this->delegate_.TriggerOnIncomingInvalidation(states);
    160   EXPECT_EQ(1, handler.GetInvalidationCount());
    161   EXPECT_THAT(expected_states, Eq(handler.GetLastInvalidationMap()));
    162 
    163   ids.erase(this->id1);
    164   ids.insert(this->id3);
    165   invalidator->UpdateRegisteredIds(&handler, ids);
    166 
    167   expected_states.erase(this->id1);
    168   expected_states[this->id3].payload = "3";
    169 
    170   // Removed object IDs should not be notified, newly-added ones should.
    171   this->delegate_.TriggerOnIncomingInvalidation(states);
    172   EXPECT_EQ(2, handler.GetInvalidationCount());
    173   EXPECT_THAT(expected_states, 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(states);
    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 states;
    240     states[this->id1].payload = "1";
    241     states[this->id2].payload = "2";
    242     states[this->id3].payload = "3";
    243     states[this->id4].payload = "4";
    244     this->delegate_.TriggerOnIncomingInvalidation(states);
    245 
    246     ObjectIdInvalidationMap expected_states;
    247     expected_states[this->id1].payload = "1";
    248     expected_states[this->id2].payload = "2";
    249 
    250     EXPECT_EQ(1, handler1.GetInvalidationCount());
    251     EXPECT_THAT(expected_states, Eq(handler1.GetLastInvalidationMap()));
    252 
    253     expected_states.clear();
    254     expected_states[this->id3].payload = "3";
    255 
    256     EXPECT_EQ(1, handler2.GetInvalidationCount());
    257     EXPECT_THAT(expected_states, Eq(handler2.GetLastInvalidationMap()));
    258 
    259     EXPECT_EQ(0, handler3.GetInvalidationCount());
    260     EXPECT_EQ(0, handler4.GetInvalidationCount());
    261   }
    262 
    263   this->delegate_.TriggerOnInvalidatorStateChange(TRANSIENT_INVALIDATION_ERROR);
    264   EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler1.GetInvalidatorState());
    265   EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler2.GetInvalidatorState());
    266   EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler3.GetInvalidatorState());
    267   EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler4.GetInvalidatorState());
    268 
    269   invalidator->UnregisterHandler(&handler3);
    270   invalidator->UnregisterHandler(&handler2);
    271   invalidator->UnregisterHandler(&handler1);
    272 }
    273 
    274 // Make sure that passing an empty set to UpdateRegisteredIds clears the
    275 // corresponding entries for the handler.
    276 TYPED_TEST_P(InvalidatorTest, EmptySetUnregisters) {
    277   Invalidator* const invalidator = this->CreateAndInitializeInvalidator();
    278 
    279   FakeInvalidationHandler handler1;
    280 
    281   // Control observer.
    282   FakeInvalidationHandler handler2;
    283 
    284   invalidator->RegisterHandler(&handler1);
    285   invalidator->RegisterHandler(&handler2);
    286 
    287   {
    288     ObjectIdSet ids;
    289     ids.insert(this->id1);
    290     ids.insert(this->id2);
    291     invalidator->UpdateRegisteredIds(&handler1, ids);
    292   }
    293 
    294   {
    295     ObjectIdSet ids;
    296     ids.insert(this->id3);
    297     invalidator->UpdateRegisteredIds(&handler2, ids);
    298   }
    299 
    300   // Unregister the IDs for the first observer. It should not receive any
    301   // further invalidations.
    302   invalidator->UpdateRegisteredIds(&handler1, ObjectIdSet());
    303 
    304   this->delegate_.TriggerOnInvalidatorStateChange(INVALIDATIONS_ENABLED);
    305   EXPECT_EQ(INVALIDATIONS_ENABLED, handler1.GetInvalidatorState());
    306   EXPECT_EQ(INVALIDATIONS_ENABLED, handler2.GetInvalidatorState());
    307 
    308   {
    309     ObjectIdInvalidationMap states;
    310     states[this->id1].payload = "1";
    311     states[this->id2].payload = "2";
    312     states[this->id3].payload = "3";
    313     this->delegate_.TriggerOnIncomingInvalidation(states);
    314     EXPECT_EQ(0, handler1.GetInvalidationCount());
    315     EXPECT_EQ(1, handler2.GetInvalidationCount());
    316   }
    317 
    318   this->delegate_.TriggerOnInvalidatorStateChange(TRANSIENT_INVALIDATION_ERROR);
    319   EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler1.GetInvalidatorState());
    320   EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler2.GetInvalidatorState());
    321 
    322   invalidator->UnregisterHandler(&handler2);
    323   invalidator->UnregisterHandler(&handler1);
    324 }
    325 
    326 namespace internal {
    327 
    328 // A FakeInvalidationHandler that is "bound" to a specific
    329 // Invalidator.  This is for cross-referencing state information with
    330 // the bound Invalidator.
    331 class BoundFakeInvalidationHandler : public FakeInvalidationHandler {
    332  public:
    333   explicit BoundFakeInvalidationHandler(const Invalidator& invalidator);
    334   virtual ~BoundFakeInvalidationHandler();
    335 
    336   // Returns the last return value of GetInvalidatorState() on the
    337   // bound invalidator from the last time the invalidator state
    338   // changed.
    339   InvalidatorState GetLastRetrievedState() const;
    340 
    341   // InvalidationHandler implementation.
    342   virtual void OnInvalidatorStateChange(InvalidatorState state) OVERRIDE;
    343 
    344  private:
    345   const Invalidator& invalidator_;
    346   InvalidatorState last_retrieved_state_;
    347 
    348   DISALLOW_COPY_AND_ASSIGN(BoundFakeInvalidationHandler);
    349 };
    350 
    351 }  // namespace internal
    352 
    353 TYPED_TEST_P(InvalidatorTest, GetInvalidatorStateAlwaysCurrent) {
    354   Invalidator* const invalidator = this->CreateAndInitializeInvalidator();
    355 
    356   internal::BoundFakeInvalidationHandler handler(*invalidator);
    357   invalidator->RegisterHandler(&handler);
    358 
    359   this->delegate_.TriggerOnInvalidatorStateChange(INVALIDATIONS_ENABLED);
    360   EXPECT_EQ(INVALIDATIONS_ENABLED, handler.GetInvalidatorState());
    361   EXPECT_EQ(INVALIDATIONS_ENABLED, handler.GetLastRetrievedState());
    362 
    363   this->delegate_.TriggerOnInvalidatorStateChange(TRANSIENT_INVALIDATION_ERROR);
    364   EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler.GetInvalidatorState());
    365   EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler.GetLastRetrievedState());
    366 
    367   invalidator->UnregisterHandler(&handler);
    368 }
    369 
    370 REGISTER_TYPED_TEST_CASE_P(InvalidatorTest,
    371                            Basic, MultipleHandlers, EmptySetUnregisters,
    372                            GetInvalidatorStateAlwaysCurrent);
    373 
    374 }  // namespace syncer
    375 
    376 #endif  // SYNC_NOTIFIER_INVALIDATOR_TEST_TEMPLATE_H_
    377