Index: sync/notifier/invalidator_test_template.h |
diff --git a/sync/notifier/invalidator_test_template.h b/sync/notifier/invalidator_test_template.h |
new file mode 100644 |
index 0000000000000000000000000000000000000000..67cd0536eb7b956ed1c8eaad6b20769b6f670e65 |
--- /dev/null |
+++ b/sync/notifier/invalidator_test_template.h |
@@ -0,0 +1,377 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+// This class defines tests that implementations of Invalidator should pass in |
+// order to be conformant. Here's how you use it to test your implementation. |
+// |
+// Say your class is called MyInvalidator. Then you need to define a class |
+// called MyInvalidatorTestDelegate in my_sync_notifier_unittest.cc like this: |
+// |
+// class MyInvalidatorTestDelegate { |
+// public: |
+// MyInvalidatorTestDelegate() ... |
+// |
+// ~MyInvalidatorTestDelegate() { |
+// // DestroyInvalidator() may not be explicitly called by tests. |
+// DestroyInvalidator(); |
+// } |
+// |
+// // Create the Invalidator implementation with the given parameters. |
+// void CreateInvalidator( |
+// const std::string& initial_state, |
+// const base::WeakPtr<InvalidationStateTracker>& |
+// invalidation_state_tracker) { |
+// ... |
+// } |
+// |
+// // Should return the Invalidator implementation. Only called after |
+// // CreateInvalidator and before DestroyInvalidator. |
+// MyInvalidator* GetInvalidator() { |
+// ... |
+// } |
+// |
+// // Destroy the Invalidator implementation. |
+// void DestroyInvalidator() { |
+// ... |
+// } |
+// |
+// // Called after a call to SetUniqueId(), or UpdateCredentials() on the |
+// // Invalidator implementation. Should block until the effects of the |
+// // call are visible on the current thread. |
+// void WaitForInvalidator() { |
+// ... |
+// } |
+// |
+// // The Trigger* functions below should block until the effects of |
+// // the call are visible on the current thread. |
+// |
+// // Should cause OnInvalidatorStateChange() to be called on all |
+// // observers of the Invalidator implementation with the given |
+// // parameters. |
+// void TriggerOnInvalidatorStateChange(InvalidatorState state) { |
+// ... |
+// } |
+// |
+// // Should cause OnIncomingInvalidation() to be called on all |
+// // observers of the Invalidator implementation with the given |
+// // parameters. |
+// void TriggerOnIncomingInvalidation( |
+// const ObjectIdInvalidationMap& invalidation_map) { |
+// ... |
+// } |
+// }; |
+// |
+// The InvalidatorTest test harness will have a member variable of |
+// this delegate type and will call its functions in the various |
+// tests. |
+// |
+// Then you simply #include this file as well as gtest.h and add the |
+// following statement to my_sync_notifier_unittest.cc: |
+// |
+// INSTANTIATE_TYPED_TEST_CASE_P( |
+// MyInvalidator, InvalidatorTest, MyInvalidatorTestDelegate); |
+// |
+// Easy! |
+ |
+#ifndef SYNC_NOTIFIER_INVALIDATOR_TEST_TEMPLATE_H_ |
+#define SYNC_NOTIFIER_INVALIDATOR_TEST_TEMPLATE_H_ |
+ |
+#include "base/basictypes.h" |
+#include "base/compiler_specific.h" |
+#include "google/cacheinvalidation/include/types.h" |
+#include "google/cacheinvalidation/types.pb.h" |
+#include "sync/internal_api/public/base/object_id_invalidation_map_test_util.h" |
+#include "sync/notifier/fake_invalidation_handler.h" |
+#include "sync/notifier/fake_invalidation_state_tracker.h" |
+#include "sync/notifier/invalidator.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace syncer { |
+ |
+template <typename InvalidatorTestDelegate> |
+class InvalidatorTest : public testing::Test { |
+ protected: |
+ InvalidatorTest() |
+ : id1(ipc::invalidation::ObjectSource::TEST, "a"), |
+ id2(ipc::invalidation::ObjectSource::TEST, "b"), |
+ id3(ipc::invalidation::ObjectSource::TEST, "c"), |
+ id4(ipc::invalidation::ObjectSource::TEST, "d") { |
+ } |
+ |
+ Invalidator* CreateAndInitializeInvalidator() { |
+ this->delegate_.CreateInvalidator("fake_invalidator_client_id", |
+ "fake_initial_state", |
+ this->fake_tracker_.AsWeakPtr()); |
+ Invalidator* const invalidator = this->delegate_.GetInvalidator(); |
+ |
+ this->delegate_.WaitForInvalidator(); |
+ invalidator->UpdateCredentials("foo@bar.com", "fake_token"); |
+ this->delegate_.WaitForInvalidator(); |
+ |
+ return invalidator; |
+ } |
+ |
+ FakeInvalidationStateTracker fake_tracker_; |
+ InvalidatorTestDelegate delegate_; |
+ |
+ const invalidation::ObjectId id1; |
+ const invalidation::ObjectId id2; |
+ const invalidation::ObjectId id3; |
+ const invalidation::ObjectId id4; |
+}; |
+ |
+TYPED_TEST_CASE_P(InvalidatorTest); |
+ |
+// Initialize the invalidator, register a handler, register some IDs for that |
+// handler, and then unregister the handler, dispatching invalidations in |
+// between. The handler should only see invalidations when its registered and |
+// its IDs are registered. |
+TYPED_TEST_P(InvalidatorTest, Basic) { |
+ Invalidator* const invalidator = this->CreateAndInitializeInvalidator(); |
+ |
+ FakeInvalidationHandler handler; |
+ |
+ invalidator->RegisterHandler(&handler); |
+ |
+ ObjectIdInvalidationMap invalidation_map; |
+ invalidation_map.Insert(Invalidation::Init(this->id1, 1, "1")); |
+ invalidation_map.Insert(Invalidation::Init(this->id2, 2, "2")); |
+ invalidation_map.Insert(Invalidation::Init(this->id3, 3, "3")); |
+ |
+ // Should be ignored since no IDs are registered to |handler|. |
+ this->delegate_.TriggerOnIncomingInvalidation(invalidation_map); |
+ EXPECT_EQ(0, handler.GetInvalidationCount()); |
+ |
+ ObjectIdSet ids; |
+ ids.insert(this->id1); |
+ ids.insert(this->id2); |
+ invalidator->UpdateRegisteredIds(&handler, ids); |
+ |
+ this->delegate_.TriggerOnInvalidatorStateChange(INVALIDATIONS_ENABLED); |
+ EXPECT_EQ(INVALIDATIONS_ENABLED, handler.GetInvalidatorState()); |
+ |
+ ObjectIdInvalidationMap expected_invalidations; |
+ expected_invalidations.Insert(Invalidation::Init(this->id1, 1, "1")); |
+ expected_invalidations.Insert(Invalidation::Init(this->id2, 2, "2")); |
+ |
+ this->delegate_.TriggerOnIncomingInvalidation(invalidation_map); |
+ EXPECT_EQ(1, handler.GetInvalidationCount()); |
+ EXPECT_THAT(expected_invalidations, Eq(handler.GetLastInvalidationMap())); |
+ |
+ ids.erase(this->id1); |
+ ids.insert(this->id3); |
+ invalidator->UpdateRegisteredIds(&handler, ids); |
+ |
+ expected_invalidations = ObjectIdInvalidationMap(); |
+ expected_invalidations.Insert(Invalidation::Init(this->id2, 2, "2")); |
+ expected_invalidations.Insert(Invalidation::Init(this->id3, 3, "3")); |
+ |
+ // Removed object IDs should not be notified, newly-added ones should. |
+ this->delegate_.TriggerOnIncomingInvalidation(invalidation_map); |
+ EXPECT_EQ(2, handler.GetInvalidationCount()); |
+ EXPECT_THAT(expected_invalidations, Eq(handler.GetLastInvalidationMap())); |
+ |
+ this->delegate_.TriggerOnInvalidatorStateChange(TRANSIENT_INVALIDATION_ERROR); |
+ EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, |
+ handler.GetInvalidatorState()); |
+ |
+ this->delegate_.TriggerOnInvalidatorStateChange( |
+ INVALIDATION_CREDENTIALS_REJECTED); |
+ EXPECT_EQ(INVALIDATION_CREDENTIALS_REJECTED, |
+ handler.GetInvalidatorState()); |
+ |
+ invalidator->UnregisterHandler(&handler); |
+ |
+ // Should be ignored since |handler| isn't registered anymore. |
+ this->delegate_.TriggerOnIncomingInvalidation(invalidation_map); |
+ EXPECT_EQ(2, handler.GetInvalidationCount()); |
+} |
+ |
+// Register handlers and some IDs for those handlers, register a handler with |
+// no IDs, and register a handler with some IDs but unregister it. Then, |
+// dispatch some invalidations and invalidations. Handlers that are registered |
+// should get invalidations, and the ones that have registered IDs should |
+// receive invalidations for those IDs. |
+TYPED_TEST_P(InvalidatorTest, MultipleHandlers) { |
+ Invalidator* const invalidator = this->CreateAndInitializeInvalidator(); |
+ |
+ FakeInvalidationHandler handler1; |
+ FakeInvalidationHandler handler2; |
+ FakeInvalidationHandler handler3; |
+ FakeInvalidationHandler handler4; |
+ |
+ invalidator->RegisterHandler(&handler1); |
+ invalidator->RegisterHandler(&handler2); |
+ invalidator->RegisterHandler(&handler3); |
+ invalidator->RegisterHandler(&handler4); |
+ |
+ { |
+ ObjectIdSet ids; |
+ ids.insert(this->id1); |
+ ids.insert(this->id2); |
+ invalidator->UpdateRegisteredIds(&handler1, ids); |
+ } |
+ |
+ { |
+ ObjectIdSet ids; |
+ ids.insert(this->id3); |
+ invalidator->UpdateRegisteredIds(&handler2, ids); |
+ } |
+ |
+ // Don't register any IDs for handler3. |
+ |
+ { |
+ ObjectIdSet ids; |
+ ids.insert(this->id4); |
+ invalidator->UpdateRegisteredIds(&handler4, ids); |
+ } |
+ |
+ invalidator->UnregisterHandler(&handler4); |
+ |
+ this->delegate_.TriggerOnInvalidatorStateChange(INVALIDATIONS_ENABLED); |
+ EXPECT_EQ(INVALIDATIONS_ENABLED, handler1.GetInvalidatorState()); |
+ EXPECT_EQ(INVALIDATIONS_ENABLED, handler2.GetInvalidatorState()); |
+ EXPECT_EQ(INVALIDATIONS_ENABLED, handler3.GetInvalidatorState()); |
+ EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler4.GetInvalidatorState()); |
+ |
+ { |
+ ObjectIdInvalidationMap invalidation_map; |
+ invalidation_map.Insert(Invalidation::Init(this->id1, 1, "1")); |
+ invalidation_map.Insert(Invalidation::Init(this->id2, 2, "2")); |
+ invalidation_map.Insert(Invalidation::Init(this->id3, 3, "3")); |
+ invalidation_map.Insert(Invalidation::Init(this->id4, 4, "4")); |
+ |
+ this->delegate_.TriggerOnIncomingInvalidation(invalidation_map); |
+ |
+ ObjectIdInvalidationMap expected_invalidations; |
+ expected_invalidations.Insert(Invalidation::Init(this->id1, 1, "1")); |
+ expected_invalidations.Insert(Invalidation::Init(this->id2, 2, "2")); |
+ |
+ EXPECT_EQ(1, handler1.GetInvalidationCount()); |
+ EXPECT_THAT(expected_invalidations, Eq(handler1.GetLastInvalidationMap())); |
+ |
+ expected_invalidations = ObjectIdInvalidationMap(); |
+ expected_invalidations.Insert(Invalidation::Init(this->id3, 3, "3")); |
+ |
+ EXPECT_EQ(1, handler2.GetInvalidationCount()); |
+ EXPECT_THAT(expected_invalidations, Eq(handler2.GetLastInvalidationMap())); |
+ |
+ EXPECT_EQ(0, handler3.GetInvalidationCount()); |
+ EXPECT_EQ(0, handler4.GetInvalidationCount()); |
+ } |
+ |
+ this->delegate_.TriggerOnInvalidatorStateChange(TRANSIENT_INVALIDATION_ERROR); |
+ EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler1.GetInvalidatorState()); |
+ EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler2.GetInvalidatorState()); |
+ EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler3.GetInvalidatorState()); |
+ EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler4.GetInvalidatorState()); |
+ |
+ invalidator->UnregisterHandler(&handler3); |
+ invalidator->UnregisterHandler(&handler2); |
+ invalidator->UnregisterHandler(&handler1); |
+} |
+ |
+// Make sure that passing an empty set to UpdateRegisteredIds clears the |
+// corresponding entries for the handler. |
+TYPED_TEST_P(InvalidatorTest, EmptySetUnregisters) { |
+ Invalidator* const invalidator = this->CreateAndInitializeInvalidator(); |
+ |
+ FakeInvalidationHandler handler1; |
+ |
+ // Control observer. |
+ FakeInvalidationHandler handler2; |
+ |
+ invalidator->RegisterHandler(&handler1); |
+ invalidator->RegisterHandler(&handler2); |
+ |
+ { |
+ ObjectIdSet ids; |
+ ids.insert(this->id1); |
+ ids.insert(this->id2); |
+ invalidator->UpdateRegisteredIds(&handler1, ids); |
+ } |
+ |
+ { |
+ ObjectIdSet ids; |
+ ids.insert(this->id3); |
+ invalidator->UpdateRegisteredIds(&handler2, ids); |
+ } |
+ |
+ // Unregister the IDs for the first observer. It should not receive any |
+ // further invalidations. |
+ invalidator->UpdateRegisteredIds(&handler1, ObjectIdSet()); |
+ |
+ this->delegate_.TriggerOnInvalidatorStateChange(INVALIDATIONS_ENABLED); |
+ EXPECT_EQ(INVALIDATIONS_ENABLED, handler1.GetInvalidatorState()); |
+ EXPECT_EQ(INVALIDATIONS_ENABLED, handler2.GetInvalidatorState()); |
+ |
+ { |
+ ObjectIdInvalidationMap invalidation_map; |
+ invalidation_map.Insert(Invalidation::Init(this->id1, 1, "1")); |
+ invalidation_map.Insert(Invalidation::Init(this->id2, 2, "2")); |
+ invalidation_map.Insert(Invalidation::Init(this->id3, 3, "3")); |
+ this->delegate_.TriggerOnIncomingInvalidation(invalidation_map); |
+ EXPECT_EQ(0, handler1.GetInvalidationCount()); |
+ EXPECT_EQ(1, handler2.GetInvalidationCount()); |
+ } |
+ |
+ this->delegate_.TriggerOnInvalidatorStateChange(TRANSIENT_INVALIDATION_ERROR); |
+ EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler1.GetInvalidatorState()); |
+ EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler2.GetInvalidatorState()); |
+ |
+ invalidator->UnregisterHandler(&handler2); |
+ invalidator->UnregisterHandler(&handler1); |
+} |
+ |
+namespace internal { |
+ |
+// A FakeInvalidationHandler that is "bound" to a specific |
+// Invalidator. This is for cross-referencing state information with |
+// the bound Invalidator. |
+class BoundFakeInvalidationHandler : public FakeInvalidationHandler { |
+ public: |
+ explicit BoundFakeInvalidationHandler(const Invalidator& invalidator); |
+ virtual ~BoundFakeInvalidationHandler(); |
+ |
+ // Returns the last return value of GetInvalidatorState() on the |
+ // bound invalidator from the last time the invalidator state |
+ // changed. |
+ InvalidatorState GetLastRetrievedState() const; |
+ |
+ // InvalidationHandler implementation. |
+ virtual void OnInvalidatorStateChange(InvalidatorState state) OVERRIDE; |
+ |
+ private: |
+ const Invalidator& invalidator_; |
+ InvalidatorState last_retrieved_state_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(BoundFakeInvalidationHandler); |
+}; |
+ |
+} // namespace internal |
+ |
+TYPED_TEST_P(InvalidatorTest, GetInvalidatorStateAlwaysCurrent) { |
+ Invalidator* const invalidator = this->CreateAndInitializeInvalidator(); |
+ |
+ internal::BoundFakeInvalidationHandler handler(*invalidator); |
+ invalidator->RegisterHandler(&handler); |
+ |
+ this->delegate_.TriggerOnInvalidatorStateChange(INVALIDATIONS_ENABLED); |
+ EXPECT_EQ(INVALIDATIONS_ENABLED, handler.GetInvalidatorState()); |
+ EXPECT_EQ(INVALIDATIONS_ENABLED, handler.GetLastRetrievedState()); |
+ |
+ this->delegate_.TriggerOnInvalidatorStateChange(TRANSIENT_INVALIDATION_ERROR); |
+ EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler.GetInvalidatorState()); |
+ EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, handler.GetLastRetrievedState()); |
+ |
+ invalidator->UnregisterHandler(&handler); |
+} |
+ |
+REGISTER_TYPED_TEST_CASE_P(InvalidatorTest, |
+ Basic, MultipleHandlers, EmptySetUnregisters, |
+ GetInvalidatorStateAlwaysCurrent); |
+ |
+} // namespace syncer |
+ |
+#endif // SYNC_NOTIFIER_INVALIDATOR_TEST_TEMPLATE_H_ |