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/internal_api/public/base/model_type_invalidation_map.h" 12 #include "sync/notifier/fake_invalidation_handler.h" 13 #include "sync/notifier/invalidator_test_template.h" 14 #include "sync/notifier/object_id_invalidation_map_test_util.h" 15 #include "testing/gtest/include/gtest/gtest.h" 16 17 namespace syncer { 18 19 namespace { 20 21 class P2PInvalidatorTestDelegate { 22 public: 23 P2PInvalidatorTestDelegate() : fake_push_client_(NULL) {} 24 25 ~P2PInvalidatorTestDelegate() { 26 DestroyInvalidator(); 27 } 28 29 void CreateInvalidator( 30 const std::string& invalidator_client_id, 31 const std::string& initial_state, 32 const base::WeakPtr<InvalidationStateTracker>& 33 invalidation_state_tracker) { 34 DCHECK(!fake_push_client_); 35 DCHECK(!invalidator_.get()); 36 fake_push_client_ = new notifier::FakePushClient(); 37 invalidator_.reset( 38 new P2PInvalidator( 39 scoped_ptr<notifier::PushClient>(fake_push_client_), 40 invalidator_client_id, 41 NOTIFY_OTHERS)); 42 } 43 44 P2PInvalidator* GetInvalidator() { 45 return invalidator_.get(); 46 } 47 48 notifier::FakePushClient* GetPushClient() { 49 return fake_push_client_; 50 } 51 52 void DestroyInvalidator() { 53 invalidator_.reset(); 54 fake_push_client_ = NULL; 55 } 56 57 void WaitForInvalidator() { 58 // Do Nothing. 59 } 60 61 void TriggerOnInvalidatorStateChange(InvalidatorState state) { 62 if (state == INVALIDATIONS_ENABLED) { 63 fake_push_client_->EnableNotifications(); 64 } else { 65 fake_push_client_->DisableNotifications(ToNotifierReasonForTest(state)); 66 } 67 } 68 69 void TriggerOnIncomingInvalidation( 70 const ObjectIdInvalidationMap& invalidation_map) { 71 const P2PNotificationData notification_data( 72 std::string(), NOTIFY_ALL, invalidation_map); 73 notifier::Notification notification; 74 notification.channel = kSyncP2PNotificationChannel; 75 notification.data = notification_data.ToString(); 76 fake_push_client_->SimulateIncomingNotification(notification); 77 } 78 79 private: 80 // Owned by |invalidator_|. 81 notifier::FakePushClient* fake_push_client_; 82 scoped_ptr<P2PInvalidator> invalidator_; 83 }; 84 85 class P2PInvalidatorTest : public testing::Test { 86 protected: 87 P2PInvalidatorTest() 88 : next_sent_notification_to_reflect_(0) { 89 delegate_.CreateInvalidator("sender", 90 "fake_state", 91 base::WeakPtr<InvalidationStateTracker>()); 92 delegate_.GetInvalidator()->RegisterHandler(&fake_handler_); 93 } 94 95 virtual ~P2PInvalidatorTest() { 96 delegate_.GetInvalidator()->UnregisterHandler(&fake_handler_); 97 } 98 99 ModelTypeInvalidationMap MakeInvalidationMap(ModelTypeSet types) { 100 return ModelTypeSetToInvalidationMap(types, std::string()); 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 "{\"idInvalidationMap\":[],\"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 const ObjectIdInvalidationMap& invalidation_map = 181 ObjectIdSetToInvalidationMap( 182 ModelTypeSetToObjectIdSet(ModelTypeSet(BOOKMARKS, THEMES)), 183 Invalidation::kUnknownVersion, 184 std::string()); 185 const P2PNotificationData notification_data( 186 "sender", NOTIFY_ALL, invalidation_map); 187 EXPECT_TRUE(notification_data.IsTargeted("sender")); 188 EXPECT_TRUE(notification_data.IsTargeted("other1")); 189 EXPECT_TRUE(notification_data.IsTargeted("other2")); 190 EXPECT_THAT(invalidation_map, 191 Eq(notification_data.GetIdInvalidationMap())); 192 const std::string& notification_data_str = notification_data.ToString(); 193 EXPECT_EQ( 194 "{\"idInvalidationMap\":[" 195 "{\"objectId\":{\"name\":\"BOOKMARK\",\"source\":1004}," 196 "\"state\":{\"ackHandle\":{\"state\":\"\",\"timestamp\":\"0\"}," 197 "\"payload\":\"\",\"version\":\"-1\"}}," 198 "{\"objectId\":{\"name\":\"THEME\",\"source\":1004}," 199 "\"state\":{\"ackHandle\":{\"state\":\"\",\"timestamp\":\"0\"}," 200 "\"payload\":\"\",\"version\":\"-1\"}}" 201 "],\"notificationType\":\"notifyAll\"," 202 "\"senderId\":\"sender\"}", notification_data_str); 203 204 P2PNotificationData notification_data_parsed; 205 EXPECT_TRUE(notification_data_parsed.ResetFromString(notification_data_str)); 206 EXPECT_TRUE(notification_data.Equals(notification_data_parsed)); 207 } 208 209 // Set up the P2PInvalidator, simulate a successful connection, and send 210 // a notification with the default target (NOTIFY_OTHERS). The 211 // observer should receive only a notification from the call to 212 // UpdateEnabledTypes(). 213 TEST_F(P2PInvalidatorTest, NotificationsBasic) { 214 const ModelTypeSet enabled_types(BOOKMARKS, PREFERENCES); 215 216 P2PInvalidator* const invalidator = delegate_.GetInvalidator(); 217 notifier::FakePushClient* const push_client = delegate_.GetPushClient(); 218 219 invalidator->UpdateRegisteredIds(&fake_handler_, 220 ModelTypeSetToObjectIdSet(enabled_types)); 221 222 const char kEmail[] = "foo (at) bar.com"; 223 const char kToken[] = "token"; 224 invalidator->UpdateCredentials(kEmail, kToken); 225 { 226 notifier::Subscription expected_subscription; 227 expected_subscription.channel = kSyncP2PNotificationChannel; 228 expected_subscription.from = kEmail; 229 EXPECT_TRUE(notifier::SubscriptionListsEqual( 230 push_client->subscriptions(), 231 notifier::SubscriptionList(1, expected_subscription))); 232 } 233 EXPECT_EQ(kEmail, push_client->email()); 234 EXPECT_EQ(kToken, push_client->token()); 235 236 ReflectSentNotifications(); 237 push_client->EnableNotifications(); 238 EXPECT_EQ(INVALIDATIONS_ENABLED, fake_handler_.GetInvalidatorState()); 239 240 ReflectSentNotifications(); 241 EXPECT_EQ(1, fake_handler_.GetInvalidationCount()); 242 EXPECT_THAT( 243 ModelTypeInvalidationMapToObjectIdInvalidationMap( 244 MakeInvalidationMap(enabled_types)), 245 Eq(fake_handler_.GetLastInvalidationMap())); 246 247 // Sent with target NOTIFY_OTHERS so should not be propagated to 248 // |fake_handler_|. 249 { 250 const ObjectIdInvalidationMap& invalidation_map = 251 ObjectIdSetToInvalidationMap( 252 ModelTypeSetToObjectIdSet(ModelTypeSet(THEMES, APPS)), 253 Invalidation::kUnknownVersion, 254 std::string()); 255 invalidator->SendInvalidation(invalidation_map); 256 } 257 258 ReflectSentNotifications(); 259 EXPECT_EQ(1, fake_handler_.GetInvalidationCount()); 260 } 261 262 // Set up the P2PInvalidator and send out notifications with various 263 // target settings. The notifications received by the observer should 264 // be consistent with the target settings. 265 TEST_F(P2PInvalidatorTest, SendNotificationData) { 266 const ModelTypeSet enabled_types(BOOKMARKS, PREFERENCES, THEMES); 267 const ModelTypeSet changed_types(THEMES, APPS); 268 const ModelTypeSet expected_types(THEMES); 269 270 const ObjectIdInvalidationMap& invalidation_map = 271 ObjectIdSetToInvalidationMap(ModelTypeSetToObjectIdSet(changed_types), 272 Invalidation::kUnknownVersion, 273 std::string()); 274 275 P2PInvalidator* const invalidator = delegate_.GetInvalidator(); 276 notifier::FakePushClient* const push_client = delegate_.GetPushClient(); 277 278 invalidator->UpdateRegisteredIds(&fake_handler_, 279 ModelTypeSetToObjectIdSet(enabled_types)); 280 281 invalidator->UpdateCredentials("foo (at) bar.com", "fake_token"); 282 283 ReflectSentNotifications(); 284 push_client->EnableNotifications(); 285 EXPECT_EQ(INVALIDATIONS_ENABLED, fake_handler_.GetInvalidatorState()); 286 287 ReflectSentNotifications(); 288 EXPECT_EQ(1, fake_handler_.GetInvalidationCount()); 289 EXPECT_THAT( 290 ModelTypeInvalidationMapToObjectIdInvalidationMap( 291 MakeInvalidationMap(enabled_types)), 292 Eq(fake_handler_.GetLastInvalidationMap())); 293 294 // Should be dropped. 295 invalidator->SendNotificationDataForTest(P2PNotificationData()); 296 ReflectSentNotifications(); 297 EXPECT_EQ(1, fake_handler_.GetInvalidationCount()); 298 299 const ObjectIdInvalidationMap& expected_ids = 300 ModelTypeInvalidationMapToObjectIdInvalidationMap( 301 MakeInvalidationMap(expected_types)); 302 303 // Should be propagated. 304 invalidator->SendNotificationDataForTest( 305 P2PNotificationData("sender", NOTIFY_SELF, invalidation_map)); 306 ReflectSentNotifications(); 307 EXPECT_EQ(2, fake_handler_.GetInvalidationCount()); 308 EXPECT_THAT(expected_ids, Eq(fake_handler_.GetLastInvalidationMap())); 309 310 // Should be dropped. 311 invalidator->SendNotificationDataForTest( 312 P2PNotificationData("sender2", NOTIFY_SELF, invalidation_map)); 313 ReflectSentNotifications(); 314 EXPECT_EQ(2, fake_handler_.GetInvalidationCount()); 315 316 // Should be dropped. 317 invalidator->SendNotificationDataForTest( 318 P2PNotificationData("sender", NOTIFY_SELF, ObjectIdInvalidationMap())); 319 ReflectSentNotifications(); 320 EXPECT_EQ(2, fake_handler_.GetInvalidationCount()); 321 322 // Should be dropped. 323 invalidator->SendNotificationDataForTest( 324 P2PNotificationData("sender", NOTIFY_OTHERS, invalidation_map)); 325 ReflectSentNotifications(); 326 EXPECT_EQ(2, fake_handler_.GetInvalidationCount()); 327 328 // Should be propagated. 329 invalidator->SendNotificationDataForTest( 330 P2PNotificationData("sender2", NOTIFY_OTHERS, invalidation_map)); 331 ReflectSentNotifications(); 332 EXPECT_EQ(3, fake_handler_.GetInvalidationCount()); 333 EXPECT_THAT(expected_ids, Eq(fake_handler_.GetLastInvalidationMap())); 334 335 // Should be dropped. 336 invalidator->SendNotificationDataForTest( 337 P2PNotificationData("sender2", NOTIFY_OTHERS, ObjectIdInvalidationMap())); 338 ReflectSentNotifications(); 339 EXPECT_EQ(3, fake_handler_.GetInvalidationCount()); 340 341 // Should be propagated. 342 invalidator->SendNotificationDataForTest( 343 P2PNotificationData("sender", NOTIFY_ALL, invalidation_map)); 344 ReflectSentNotifications(); 345 EXPECT_EQ(4, fake_handler_.GetInvalidationCount()); 346 EXPECT_THAT(expected_ids, Eq(fake_handler_.GetLastInvalidationMap())); 347 348 // Should be propagated. 349 invalidator->SendNotificationDataForTest( 350 P2PNotificationData("sender2", NOTIFY_ALL, invalidation_map)); 351 ReflectSentNotifications(); 352 EXPECT_EQ(5, fake_handler_.GetInvalidationCount()); 353 EXPECT_THAT(expected_ids, Eq(fake_handler_.GetLastInvalidationMap())); 354 355 // Should be dropped. 356 invalidator->SendNotificationDataForTest( 357 P2PNotificationData("sender2", NOTIFY_ALL, ObjectIdInvalidationMap())); 358 ReflectSentNotifications(); 359 EXPECT_EQ(5, fake_handler_.GetInvalidationCount()); 360 } 361 362 INSTANTIATE_TYPED_TEST_CASE_P( 363 P2PInvalidatorTest, InvalidatorTest, 364 P2PInvalidatorTestDelegate); 365 366 } // namespace 367 368 } // namespace syncer 369