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/internal_api/public/base/object_id_invalidation_map_test_util.h" 85#include "sync/notifier/fake_invalidation_handler.h" 86#include "sync/notifier/fake_invalidation_state_tracker.h" 87#include "sync/notifier/invalidator.h" 88#include "testing/gtest/include/gtest/gtest.h" 89 90namespace syncer { 91 92template <typename InvalidatorTestDelegate> 93class InvalidatorTest : public testing::Test { 94 protected: 95 InvalidatorTest() 96 : id1(ipc::invalidation::ObjectSource::TEST, "a"), 97 id2(ipc::invalidation::ObjectSource::TEST, "b"), 98 id3(ipc::invalidation::ObjectSource::TEST, "c"), 99 id4(ipc::invalidation::ObjectSource::TEST, "d") { 100 } 101 102 Invalidator* CreateAndInitializeInvalidator() { 103 this->delegate_.CreateInvalidator("fake_invalidator_client_id", 104 "fake_initial_state", 105 this->fake_tracker_.AsWeakPtr()); 106 Invalidator* const invalidator = this->delegate_.GetInvalidator(); 107 108 this->delegate_.WaitForInvalidator(); 109 invalidator->UpdateCredentials("foo@bar.com", "fake_token"); 110 this->delegate_.WaitForInvalidator(); 111 112 return invalidator; 113 } 114 115 FakeInvalidationStateTracker fake_tracker_; 116 InvalidatorTestDelegate delegate_; 117 118 const invalidation::ObjectId id1; 119 const invalidation::ObjectId id2; 120 const invalidation::ObjectId id3; 121 const invalidation::ObjectId id4; 122}; 123 124TYPED_TEST_CASE_P(InvalidatorTest); 125 126// Initialize the invalidator, register a handler, register some IDs for that 127// handler, and then unregister the handler, dispatching invalidations in 128// between. The handler should only see invalidations when its registered and 129// its IDs are registered. 130TYPED_TEST_P(InvalidatorTest, Basic) { 131 Invalidator* const invalidator = this->CreateAndInitializeInvalidator(); 132 133 FakeInvalidationHandler handler; 134 135 invalidator->RegisterHandler(&handler); 136 137 ObjectIdInvalidationMap invalidation_map; 138 invalidation_map.Insert(Invalidation::Init(this->id1, 1, "1")); 139 invalidation_map.Insert(Invalidation::Init(this->id2, 2, "2")); 140 invalidation_map.Insert(Invalidation::Init(this->id3, 3, "3")); 141 142 // Should be ignored since no IDs are registered to |handler|. 143 this->delegate_.TriggerOnIncomingInvalidation(invalidation_map); 144 EXPECT_EQ(0, handler.GetInvalidationCount()); 145 146 ObjectIdSet ids; 147 ids.insert(this->id1); 148 ids.insert(this->id2); 149 invalidator->UpdateRegisteredIds(&handler, ids); 150 151 this->delegate_.TriggerOnInvalidatorStateChange(INVALIDATIONS_ENABLED); 152 EXPECT_EQ(INVALIDATIONS_ENABLED, handler.GetInvalidatorState()); 153 154 ObjectIdInvalidationMap expected_invalidations; 155 expected_invalidations.Insert(Invalidation::Init(this->id1, 1, "1")); 156 expected_invalidations.Insert(Invalidation::Init(this->id2, 2, "2")); 157 158 this->delegate_.TriggerOnIncomingInvalidation(invalidation_map); 159 EXPECT_EQ(1, handler.GetInvalidationCount()); 160 EXPECT_THAT(expected_invalidations, Eq(handler.GetLastInvalidationMap())); 161 162 ids.erase(this->id1); 163 ids.insert(this->id3); 164 invalidator->UpdateRegisteredIds(&handler, ids); 165 166 expected_invalidations = ObjectIdInvalidationMap(); 167 expected_invalidations.Insert(Invalidation::Init(this->id2, 2, "2")); 168 expected_invalidations.Insert(Invalidation::Init(this->id3, 3, "3")); 169 170 // Removed object IDs should not be notified, newly-added ones should. 171 this->delegate_.TriggerOnIncomingInvalidation(invalidation_map); 172 EXPECT_EQ(2, handler.GetInvalidationCount()); 173 EXPECT_THAT(expected_invalidations, 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(invalidation_map); 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. 196TYPED_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 invalidation_map; 240 invalidation_map.Insert(Invalidation::Init(this->id1, 1, "1")); 241 invalidation_map.Insert(Invalidation::Init(this->id2, 2, "2")); 242 invalidation_map.Insert(Invalidation::Init(this->id3, 3, "3")); 243 invalidation_map.Insert(Invalidation::Init(this->id4, 4, "4")); 244 245 this->delegate_.TriggerOnIncomingInvalidation(invalidation_map); 246 247 ObjectIdInvalidationMap expected_invalidations; 248 expected_invalidations.Insert(Invalidation::Init(this->id1, 1, "1")); 249 expected_invalidations.Insert(Invalidation::Init(this->id2, 2, "2")); 250 251 EXPECT_EQ(1, handler1.GetInvalidationCount()); 252 EXPECT_THAT(expected_invalidations, Eq(handler1.GetLastInvalidationMap())); 253 254 expected_invalidations = ObjectIdInvalidationMap(); 255 expected_invalidations.Insert(Invalidation::Init(this->id3, 3, "3")); 256 257 EXPECT_EQ(1, handler2.GetInvalidationCount()); 258 EXPECT_THAT(expected_invalidations, Eq(handler2.GetLastInvalidationMap())); 259 260 EXPECT_EQ(0, handler3.GetInvalidationCount()); 261 EXPECT_EQ(0, handler4.GetInvalidationCount()); 262 } 263 264 this->delegate_.TriggerOnInvalidatorStateChange(TRANSIENT_INVALIDATION_ERROR); 265 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler1.GetInvalidatorState()); 266 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler2.GetInvalidatorState()); 267 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler3.GetInvalidatorState()); 268 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler4.GetInvalidatorState()); 269 270 invalidator->UnregisterHandler(&handler3); 271 invalidator->UnregisterHandler(&handler2); 272 invalidator->UnregisterHandler(&handler1); 273} 274 275// Make sure that passing an empty set to UpdateRegisteredIds clears the 276// corresponding entries for the handler. 277TYPED_TEST_P(InvalidatorTest, EmptySetUnregisters) { 278 Invalidator* const invalidator = this->CreateAndInitializeInvalidator(); 279 280 FakeInvalidationHandler handler1; 281 282 // Control observer. 283 FakeInvalidationHandler handler2; 284 285 invalidator->RegisterHandler(&handler1); 286 invalidator->RegisterHandler(&handler2); 287 288 { 289 ObjectIdSet ids; 290 ids.insert(this->id1); 291 ids.insert(this->id2); 292 invalidator->UpdateRegisteredIds(&handler1, ids); 293 } 294 295 { 296 ObjectIdSet ids; 297 ids.insert(this->id3); 298 invalidator->UpdateRegisteredIds(&handler2, ids); 299 } 300 301 // Unregister the IDs for the first observer. It should not receive any 302 // further invalidations. 303 invalidator->UpdateRegisteredIds(&handler1, ObjectIdSet()); 304 305 this->delegate_.TriggerOnInvalidatorStateChange(INVALIDATIONS_ENABLED); 306 EXPECT_EQ(INVALIDATIONS_ENABLED, handler1.GetInvalidatorState()); 307 EXPECT_EQ(INVALIDATIONS_ENABLED, handler2.GetInvalidatorState()); 308 309 { 310 ObjectIdInvalidationMap invalidation_map; 311 invalidation_map.Insert(Invalidation::Init(this->id1, 1, "1")); 312 invalidation_map.Insert(Invalidation::Init(this->id2, 2, "2")); 313 invalidation_map.Insert(Invalidation::Init(this->id3, 3, "3")); 314 this->delegate_.TriggerOnIncomingInvalidation(invalidation_map); 315 EXPECT_EQ(0, handler1.GetInvalidationCount()); 316 EXPECT_EQ(1, handler2.GetInvalidationCount()); 317 } 318 319 this->delegate_.TriggerOnInvalidatorStateChange(TRANSIENT_INVALIDATION_ERROR); 320 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler1.GetInvalidatorState()); 321 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler2.GetInvalidatorState()); 322 323 invalidator->UnregisterHandler(&handler2); 324 invalidator->UnregisterHandler(&handler1); 325} 326 327namespace internal { 328 329// A FakeInvalidationHandler that is "bound" to a specific 330// Invalidator. This is for cross-referencing state information with 331// the bound Invalidator. 332class BoundFakeInvalidationHandler : public FakeInvalidationHandler { 333 public: 334 explicit BoundFakeInvalidationHandler(const Invalidator& invalidator); 335 virtual ~BoundFakeInvalidationHandler(); 336 337 // Returns the last return value of GetInvalidatorState() on the 338 // bound invalidator from the last time the invalidator state 339 // changed. 340 InvalidatorState GetLastRetrievedState() const; 341 342 // InvalidationHandler implementation. 343 virtual void OnInvalidatorStateChange(InvalidatorState state) OVERRIDE; 344 345 private: 346 const Invalidator& invalidator_; 347 InvalidatorState last_retrieved_state_; 348 349 DISALLOW_COPY_AND_ASSIGN(BoundFakeInvalidationHandler); 350}; 351 352} // namespace internal 353 354TYPED_TEST_P(InvalidatorTest, GetInvalidatorStateAlwaysCurrent) { 355 Invalidator* const invalidator = this->CreateAndInitializeInvalidator(); 356 357 internal::BoundFakeInvalidationHandler handler(*invalidator); 358 invalidator->RegisterHandler(&handler); 359 360 this->delegate_.TriggerOnInvalidatorStateChange(INVALIDATIONS_ENABLED); 361 EXPECT_EQ(INVALIDATIONS_ENABLED, handler.GetInvalidatorState()); 362 EXPECT_EQ(INVALIDATIONS_ENABLED, handler.GetLastRetrievedState()); 363 364 this->delegate_.TriggerOnInvalidatorStateChange(TRANSIENT_INVALIDATION_ERROR); 365 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler.GetInvalidatorState()); 366 EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler.GetLastRetrievedState()); 367 368 invalidator->UnregisterHandler(&handler); 369} 370 371REGISTER_TYPED_TEST_CASE_P(InvalidatorTest, 372 Basic, MultipleHandlers, EmptySetUnregisters, 373 GetInvalidatorStateAlwaysCurrent); 374 375} // namespace syncer 376 377#endif // SYNC_NOTIFIER_INVALIDATOR_TEST_TEMPLATE_H_ 378