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