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