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 #include "sync/notifier/p2p_invalidator.h"
      6 
      7 #include <cstddef>
      8 
      9 #include "jingle/notifier/listener/fake_push_client.h"
     10 #include "sync/internal_api/public/base/model_type.h"
     11 #include "sync/notifier/fake_invalidation_handler.h"
     12 #include "sync/notifier/invalidator_test_template.h"
     13 #include "testing/gtest/include/gtest/gtest.h"
     14 
     15 namespace syncer {
     16 
     17 namespace {
     18 
     19 class P2PInvalidatorTestDelegate {
     20  public:
     21   P2PInvalidatorTestDelegate() : fake_push_client_(NULL) {}
     22 
     23   ~P2PInvalidatorTestDelegate() {
     24     DestroyInvalidator();
     25   }
     26 
     27   void CreateInvalidator(
     28       const std::string& invalidator_client_id,
     29       const std::string& initial_state,
     30       const base::WeakPtr<InvalidationStateTracker>&
     31           invalidation_state_tracker) {
     32     DCHECK(!fake_push_client_);
     33     DCHECK(!invalidator_.get());
     34     fake_push_client_ = new notifier::FakePushClient();
     35     invalidator_.reset(
     36         new P2PInvalidator(
     37             scoped_ptr<notifier::PushClient>(fake_push_client_),
     38             invalidator_client_id,
     39             NOTIFY_OTHERS));
     40   }
     41 
     42   P2PInvalidator* GetInvalidator() {
     43     return invalidator_.get();
     44   }
     45 
     46   notifier::FakePushClient* GetPushClient() {
     47     return fake_push_client_;
     48   }
     49 
     50   void DestroyInvalidator() {
     51     invalidator_.reset();
     52     fake_push_client_ = NULL;
     53   }
     54 
     55   void WaitForInvalidator() {
     56     // Do Nothing.
     57   }
     58 
     59   void TriggerOnInvalidatorStateChange(InvalidatorState state) {
     60     if (state == INVALIDATIONS_ENABLED) {
     61       fake_push_client_->EnableNotifications();
     62     } else {
     63       fake_push_client_->DisableNotifications(ToNotifierReasonForTest(state));
     64     }
     65   }
     66 
     67   void TriggerOnIncomingInvalidation(
     68       const ObjectIdInvalidationMap& invalidation_map) {
     69     const P2PNotificationData notification_data(
     70         std::string(), NOTIFY_ALL, invalidation_map);
     71     notifier::Notification notification;
     72     notification.channel = kSyncP2PNotificationChannel;
     73     notification.data = notification_data.ToString();
     74     fake_push_client_->SimulateIncomingNotification(notification);
     75   }
     76 
     77  private:
     78   // Owned by |invalidator_|.
     79   notifier::FakePushClient* fake_push_client_;
     80   scoped_ptr<P2PInvalidator> invalidator_;
     81 };
     82 
     83 class P2PInvalidatorTest : public testing::Test {
     84  protected:
     85   P2PInvalidatorTest()
     86       : next_sent_notification_to_reflect_(0) {
     87     delegate_.CreateInvalidator("sender",
     88                                 "fake_state",
     89                                 base::WeakPtr<InvalidationStateTracker>());
     90     delegate_.GetInvalidator()->RegisterHandler(&fake_handler_);
     91   }
     92 
     93   virtual ~P2PInvalidatorTest() {
     94     delegate_.GetInvalidator()->UnregisterHandler(&fake_handler_);
     95   }
     96 
     97   ObjectIdInvalidationMap MakeInvalidationMap(ModelTypeSet types) {
     98     ObjectIdInvalidationMap invalidations;
     99     ObjectIdSet ids = ModelTypeSetToObjectIdSet(types);
    100     return ObjectIdInvalidationMap::InvalidateAll(ids);
    101   }
    102 
    103   // Simulate receiving all the notifications we sent out since last
    104   // time this was called.
    105   void ReflectSentNotifications() {
    106     const std::vector<notifier::Notification>& sent_notifications =
    107         delegate_.GetPushClient()->sent_notifications();
    108     for(size_t i = next_sent_notification_to_reflect_;
    109         i < sent_notifications.size(); ++i) {
    110       delegate_.GetInvalidator()->OnIncomingNotification(sent_notifications[i]);
    111     }
    112     next_sent_notification_to_reflect_ = sent_notifications.size();
    113   }
    114 
    115   FakeInvalidationHandler fake_handler_;
    116   P2PInvalidatorTestDelegate delegate_;
    117 
    118  private:
    119   size_t next_sent_notification_to_reflect_;
    120 };
    121 
    122 // Make sure the P2PNotificationTarget <-> string conversions work.
    123 TEST_F(P2PInvalidatorTest, P2PNotificationTarget) {
    124   for (int i = FIRST_NOTIFICATION_TARGET;
    125        i <= LAST_NOTIFICATION_TARGET; ++i) {
    126     P2PNotificationTarget target = static_cast<P2PNotificationTarget>(i);
    127     const std::string& target_str = P2PNotificationTargetToString(target);
    128     EXPECT_FALSE(target_str.empty());
    129     EXPECT_EQ(target, P2PNotificationTargetFromString(target_str));
    130   }
    131   EXPECT_EQ(NOTIFY_SELF, P2PNotificationTargetFromString("unknown"));
    132 }
    133 
    134 // Make sure notification targeting works correctly.
    135 TEST_F(P2PInvalidatorTest, P2PNotificationDataIsTargeted) {
    136   {
    137     const P2PNotificationData notification_data(
    138         "sender", NOTIFY_SELF, ObjectIdInvalidationMap());
    139     EXPECT_TRUE(notification_data.IsTargeted("sender"));
    140     EXPECT_FALSE(notification_data.IsTargeted("other1"));
    141     EXPECT_FALSE(notification_data.IsTargeted("other2"));
    142   }
    143   {
    144     const P2PNotificationData notification_data(
    145         "sender", NOTIFY_OTHERS, ObjectIdInvalidationMap());
    146     EXPECT_FALSE(notification_data.IsTargeted("sender"));
    147     EXPECT_TRUE(notification_data.IsTargeted("other1"));
    148     EXPECT_TRUE(notification_data.IsTargeted("other2"));
    149   }
    150   {
    151     const P2PNotificationData notification_data(
    152         "sender", NOTIFY_ALL, ObjectIdInvalidationMap());
    153     EXPECT_TRUE(notification_data.IsTargeted("sender"));
    154     EXPECT_TRUE(notification_data.IsTargeted("other1"));
    155     EXPECT_TRUE(notification_data.IsTargeted("other2"));
    156   }
    157 }
    158 
    159 // Make sure the P2PNotificationData <-> string conversions work for a
    160 // default-constructed P2PNotificationData.
    161 TEST_F(P2PInvalidatorTest, P2PNotificationDataDefault) {
    162   const P2PNotificationData notification_data;
    163   EXPECT_TRUE(notification_data.IsTargeted(std::string()));
    164   EXPECT_FALSE(notification_data.IsTargeted("other1"));
    165   EXPECT_FALSE(notification_data.IsTargeted("other2"));
    166   EXPECT_TRUE(notification_data.GetIdInvalidationMap().Empty());
    167   const std::string& notification_data_str = notification_data.ToString();
    168   EXPECT_EQ(
    169       "{\"invalidations\":[],\"notificationType\":\"notifySelf\","
    170       "\"senderId\":\"\"}", notification_data_str);
    171 
    172   P2PNotificationData notification_data_parsed;
    173   EXPECT_TRUE(notification_data_parsed.ResetFromString(notification_data_str));
    174   EXPECT_TRUE(notification_data.Equals(notification_data_parsed));
    175 }
    176 
    177 // Make sure the P2PNotificationData <-> string conversions work for a
    178 // non-default-constructed P2PNotificationData.
    179 TEST_F(P2PInvalidatorTest, P2PNotificationDataNonDefault) {
    180   ObjectIdInvalidationMap invalidation_map =
    181       ObjectIdInvalidationMap::InvalidateAll(
    182           ModelTypeSetToObjectIdSet(ModelTypeSet(BOOKMARKS, THEMES)));
    183   const P2PNotificationData notification_data("sender",
    184                                               NOTIFY_ALL,
    185                                               invalidation_map);
    186   EXPECT_TRUE(notification_data.IsTargeted("sender"));
    187   EXPECT_TRUE(notification_data.IsTargeted("other1"));
    188   EXPECT_TRUE(notification_data.IsTargeted("other2"));
    189   EXPECT_EQ(invalidation_map, notification_data.GetIdInvalidationMap());
    190   const std::string& notification_data_str = notification_data.ToString();
    191   EXPECT_EQ(
    192       "{\"invalidations\":["
    193       "{\"isUnknownVersion\":true,"
    194        "\"objectId\":{\"name\":\"BOOKMARK\",\"source\":1004}},"
    195       "{\"isUnknownVersion\":true,"
    196        "\"objectId\":{\"name\":\"THEME\",\"source\":1004}}"
    197       "],\"notificationType\":\"notifyAll\","
    198       "\"senderId\":\"sender\"}", notification_data_str);
    199 
    200   P2PNotificationData notification_data_parsed;
    201   EXPECT_TRUE(notification_data_parsed.ResetFromString(notification_data_str));
    202   EXPECT_TRUE(notification_data.Equals(notification_data_parsed));
    203 }
    204 
    205 // Set up the P2PInvalidator, simulate a successful connection, and send
    206 // a notification with the default target (NOTIFY_OTHERS).  The
    207 // observer should receive only a notification from the call to
    208 // UpdateEnabledTypes().
    209 TEST_F(P2PInvalidatorTest, NotificationsBasic) {
    210   const ModelTypeSet enabled_types(BOOKMARKS, PREFERENCES);
    211 
    212   P2PInvalidator* const invalidator = delegate_.GetInvalidator();
    213   notifier::FakePushClient* const push_client = delegate_.GetPushClient();
    214 
    215   invalidator->UpdateRegisteredIds(&fake_handler_,
    216                                    ModelTypeSetToObjectIdSet(enabled_types));
    217 
    218   const char kEmail[] = "foo (at) bar.com";
    219   const char kToken[] = "token";
    220   invalidator->UpdateCredentials(kEmail, kToken);
    221   {
    222     notifier::Subscription expected_subscription;
    223     expected_subscription.channel = kSyncP2PNotificationChannel;
    224     expected_subscription.from = kEmail;
    225     EXPECT_TRUE(notifier::SubscriptionListsEqual(
    226         push_client->subscriptions(),
    227         notifier::SubscriptionList(1, expected_subscription)));
    228   }
    229   EXPECT_EQ(kEmail, push_client->email());
    230   EXPECT_EQ(kToken, push_client->token());
    231 
    232   ReflectSentNotifications();
    233   push_client->EnableNotifications();
    234   EXPECT_EQ(INVALIDATIONS_ENABLED, fake_handler_.GetInvalidatorState());
    235 
    236   ReflectSentNotifications();
    237   EXPECT_EQ(1, fake_handler_.GetInvalidationCount());
    238   EXPECT_THAT(
    239       MakeInvalidationMap(enabled_types),
    240       Eq(fake_handler_.GetLastInvalidationMap()));
    241 
    242   // Sent with target NOTIFY_OTHERS so should not be propagated to
    243   // |fake_handler_|.
    244   invalidator->SendInvalidation(
    245       ModelTypeSetToObjectIdSet(ModelTypeSet(THEMES, APPS)));
    246 
    247   ReflectSentNotifications();
    248   EXPECT_EQ(1, fake_handler_.GetInvalidationCount());
    249 }
    250 
    251 // Set up the P2PInvalidator and send out notifications with various
    252 // target settings.  The notifications received by the observer should
    253 // be consistent with the target settings.
    254 TEST_F(P2PInvalidatorTest, SendNotificationData) {
    255   const ModelTypeSet enabled_types(BOOKMARKS, PREFERENCES, THEMES);
    256   const ModelTypeSet changed_types(THEMES, APPS);
    257   const ModelTypeSet expected_types(THEMES);
    258 
    259   const ObjectIdInvalidationMap& invalidation_map =
    260       MakeInvalidationMap(changed_types);
    261 
    262   P2PInvalidator* const invalidator = delegate_.GetInvalidator();
    263   notifier::FakePushClient* const push_client = delegate_.GetPushClient();
    264 
    265   invalidator->UpdateRegisteredIds(&fake_handler_,
    266                                    ModelTypeSetToObjectIdSet(enabled_types));
    267 
    268   invalidator->UpdateCredentials("foo (at) bar.com", "fake_token");
    269 
    270   ReflectSentNotifications();
    271   push_client->EnableNotifications();
    272   EXPECT_EQ(INVALIDATIONS_ENABLED, fake_handler_.GetInvalidatorState());
    273 
    274   ReflectSentNotifications();
    275   EXPECT_EQ(1, fake_handler_.GetInvalidationCount());
    276   EXPECT_EQ(ModelTypeSetToObjectIdSet(enabled_types),
    277             fake_handler_.GetLastInvalidationMap().GetObjectIds());
    278 
    279   // Should be dropped.
    280   invalidator->SendNotificationDataForTest(P2PNotificationData());
    281   ReflectSentNotifications();
    282   EXPECT_EQ(1, fake_handler_.GetInvalidationCount());
    283 
    284   const ObjectIdSet& expected_ids = ModelTypeSetToObjectIdSet(expected_types);
    285 
    286   // Should be propagated.
    287   invalidator->SendNotificationDataForTest(
    288       P2PNotificationData("sender", NOTIFY_SELF, invalidation_map));
    289   ReflectSentNotifications();
    290   EXPECT_EQ(2, fake_handler_.GetInvalidationCount());
    291   EXPECT_EQ(expected_ids,
    292             fake_handler_.GetLastInvalidationMap().GetObjectIds());
    293 
    294   // Should be dropped.
    295   invalidator->SendNotificationDataForTest(
    296       P2PNotificationData("sender2", NOTIFY_SELF, invalidation_map));
    297   ReflectSentNotifications();
    298   EXPECT_EQ(2, fake_handler_.GetInvalidationCount());
    299 
    300   // Should be dropped.
    301   invalidator->SendNotificationDataForTest(
    302       P2PNotificationData("sender", NOTIFY_SELF, ObjectIdInvalidationMap()));
    303   ReflectSentNotifications();
    304   EXPECT_EQ(2, fake_handler_.GetInvalidationCount());
    305 
    306   // Should be dropped.
    307   invalidator->SendNotificationDataForTest(
    308       P2PNotificationData("sender", NOTIFY_OTHERS, invalidation_map));
    309   ReflectSentNotifications();
    310   EXPECT_EQ(2, fake_handler_.GetInvalidationCount());
    311 
    312   // Should be propagated.
    313   invalidator->SendNotificationDataForTest(
    314       P2PNotificationData("sender2", NOTIFY_OTHERS, invalidation_map));
    315   ReflectSentNotifications();
    316   EXPECT_EQ(3, fake_handler_.GetInvalidationCount());
    317   EXPECT_EQ(expected_ids,
    318             fake_handler_.GetLastInvalidationMap().GetObjectIds());
    319 
    320   // Should be dropped.
    321   invalidator->SendNotificationDataForTest(
    322       P2PNotificationData("sender2", NOTIFY_OTHERS, ObjectIdInvalidationMap()));
    323   ReflectSentNotifications();
    324   EXPECT_EQ(3, fake_handler_.GetInvalidationCount());
    325 
    326   // Should be propagated.
    327   invalidator->SendNotificationDataForTest(
    328       P2PNotificationData("sender", NOTIFY_ALL, invalidation_map));
    329   ReflectSentNotifications();
    330   EXPECT_EQ(4, fake_handler_.GetInvalidationCount());
    331   EXPECT_EQ(expected_ids,
    332             fake_handler_.GetLastInvalidationMap().GetObjectIds());
    333 
    334   // Should be propagated.
    335   invalidator->SendNotificationDataForTest(
    336       P2PNotificationData("sender2", NOTIFY_ALL, invalidation_map));
    337   ReflectSentNotifications();
    338   EXPECT_EQ(5, fake_handler_.GetInvalidationCount());
    339   EXPECT_EQ(expected_ids,
    340             fake_handler_.GetLastInvalidationMap().GetObjectIds());
    341 
    342   // Should be dropped.
    343   invalidator->SendNotificationDataForTest(
    344   P2PNotificationData("sender2", NOTIFY_ALL, ObjectIdInvalidationMap()));
    345   ReflectSentNotifications();
    346   EXPECT_EQ(5, fake_handler_.GetInvalidationCount());
    347 }
    348 
    349 INSTANTIATE_TYPED_TEST_CASE_P(
    350     P2PInvalidatorTest, InvalidatorTest,
    351     P2PInvalidatorTestDelegate);
    352 
    353 }  // namespace
    354 
    355 }  // namespace syncer
    356