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