| Index: chrome/browser/invalidation/invalidation_frontend_test_template.h
|
| diff --git a/chrome/browser/invalidation/invalidation_frontend_test_template.h b/chrome/browser/invalidation/invalidation_frontend_test_template.h
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..9643504d4000438ac19d5e5489517e28a8129453
|
| --- /dev/null
|
| +++ b/chrome/browser/invalidation/invalidation_frontend_test_template.h
|
| @@ -0,0 +1,384 @@
|
| +// Copyright (c) 2013 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 InvalidationFrontend should
|
| +// pass in order to be conformant. Here's how you use it to test your
|
| +// implementation.
|
| +//
|
| +// Say your class is called MyInvalidationFrontend. Then you need to define a
|
| +// class called MyInvalidationFrontendTestDelegate in
|
| +// my_invalidation_frontend_unittest.cc like this:
|
| +//
|
| +// class MyInvalidationFrontendTestDelegate {
|
| +// public:
|
| +// MyInvalidationFrontendTestDelegate() ...
|
| +//
|
| +// ~MyInvalidationFrontendTestDelegate() {
|
| +// // DestroyInvalidator() may not be explicitly called by tests.
|
| +// DestroyInvalidator();
|
| +// }
|
| +//
|
| +// // Create the InvalidationFrontend implementation with the given params.
|
| +// void CreateInvalidationFrontend() {
|
| +// ...
|
| +// }
|
| +//
|
| +// // Should return the InvalidationFrontend implementation. Only called
|
| +// // after CreateInvalidator and before DestroyInvalidator.
|
| +// MyInvalidationFrontend* GetInvalidationFrontend() {
|
| +// ...
|
| +// }
|
| +//
|
| +// // Destroy the InvalidationFrontend implementation.
|
| +// void DestroyInvalidationFrontend() {
|
| +// ...
|
| +// }
|
| +//
|
| +// // 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 InvalidationFrontend implementation with the given
|
| +// // parameters.
|
| +// void TriggerOnInvalidatorStateChange(InvalidatorState state) {
|
| +// ...
|
| +// }
|
| +//
|
| +// // Should cause OnIncomingInvalidation() to be called on all
|
| +// // observers of the InvalidationFrontend implementation with the given
|
| +// // parameters.
|
| +// void TriggerOnIncomingInvalidation(
|
| +// const ObjectIdInvalidationMap& invalidation_map) {
|
| +// ...
|
| +// }
|
| +// };
|
| +//
|
| +// The InvalidationFrontendTest 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(
|
| +// MyInvalidationFrontend,
|
| +// InvalidationFrontendTest,
|
| +// MyInvalidatorTestDelegate);
|
| +//
|
| +// Easy!
|
| +
|
| +#ifndef CHROME_BROWSER_INVALIDATION_INVALIDATION_FRONTEND_TEST_TEMPLATE_H_
|
| +#define CHROME_BROWSER_INVALIDATION_INVALIDATION_FRONTEND_TEST_TEMPLATE_H_
|
| +
|
| +#include "base/basictypes.h"
|
| +#include "base/compiler_specific.h"
|
| +#include "chrome/browser/invalidation/invalidation_frontend.h"
|
| +#include "google/cacheinvalidation/include/types.h"
|
| +#include "google/cacheinvalidation/types.pb.h"
|
| +#include "sync/notifier/fake_invalidation_handler.h"
|
| +#include "sync/notifier/object_id_invalidation_map.h"
|
| +#include "sync/notifier/object_id_invalidation_map_test_util.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +template <typename InvalidatorTestDelegate>
|
| +class InvalidationFrontendTest : public testing::Test {
|
| + protected:
|
| + // Note: The IDs defined below must be valid. Otherwise they won't make it
|
| + // through the round-trip to ModelTypeInvalidationMap and back that the
|
| + // AndroidInvalidation test requires.
|
| + InvalidationFrontendTest()
|
| + : id1(ipc::invalidation::ObjectSource::CHROME_SYNC, "BOOKMARK"),
|
| + id2(ipc::invalidation::ObjectSource::CHROME_SYNC, "PREFERENCE"),
|
| + id3(ipc::invalidation::ObjectSource::CHROME_SYNC, "AUTOFILL"),
|
| + id4(ipc::invalidation::ObjectSource::CHROME_SYNC, "EXPERIMENTS") {
|
| + }
|
| +
|
| + invalidation::InvalidationFrontend*
|
| + CreateAndInitializeInvalidationFrontend() {
|
| + this->delegate_.CreateInvalidationFrontend();
|
| + return this->delegate_.GetInvalidationFrontend();
|
| + }
|
| +
|
| + InvalidatorTestDelegate delegate_;
|
| +
|
| + const invalidation::ObjectId id1;
|
| + const invalidation::ObjectId id2;
|
| + const invalidation::ObjectId id3;
|
| + const invalidation::ObjectId id4;
|
| +};
|
| +
|
| +TYPED_TEST_CASE_P(InvalidationFrontendTest);
|
| +
|
| +// 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(InvalidationFrontendTest, Basic) {
|
| + invalidation::InvalidationFrontend* const invalidator =
|
| + this->CreateAndInitializeInvalidationFrontend();
|
| +
|
| + syncer::FakeInvalidationHandler handler;
|
| +
|
| + invalidator->RegisterInvalidationHandler(&handler);
|
| +
|
| + syncer::ObjectIdInvalidationMap states;
|
| + states[this->id1].payload = "1";
|
| + states[this->id2].payload = "2";
|
| + states[this->id3].payload = "3";
|
| +
|
| + // Should be ignored since no IDs are registered to |handler|.
|
| + this->delegate_.TriggerOnIncomingInvalidation(states);
|
| + EXPECT_EQ(0, handler.GetInvalidationCount());
|
| +
|
| + syncer::ObjectIdSet ids;
|
| + ids.insert(this->id1);
|
| + ids.insert(this->id2);
|
| + invalidator->UpdateRegisteredInvalidationIds(&handler, ids);
|
| +
|
| + this->delegate_.TriggerOnInvalidatorStateChange(
|
| + syncer::INVALIDATIONS_ENABLED);
|
| + EXPECT_EQ(syncer::INVALIDATIONS_ENABLED, handler.GetInvalidatorState());
|
| +
|
| + syncer::ObjectIdInvalidationMap expected_states;
|
| + expected_states[this->id1].payload = "1";
|
| + expected_states[this->id2].payload = "2";
|
| +
|
| + this->delegate_.TriggerOnIncomingInvalidation(states);
|
| + EXPECT_EQ(1, handler.GetInvalidationCount());
|
| + EXPECT_THAT(expected_states, Eq(handler.GetLastInvalidationMap()));
|
| +
|
| + ids.erase(this->id1);
|
| + ids.insert(this->id3);
|
| + invalidator->UpdateRegisteredInvalidationIds(&handler, ids);
|
| +
|
| + expected_states.erase(this->id1);
|
| + expected_states[this->id3].payload = "3";
|
| +
|
| + // Removed object IDs should not be notified, newly-added ones should.
|
| + this->delegate_.TriggerOnIncomingInvalidation(states);
|
| + EXPECT_EQ(2, handler.GetInvalidationCount());
|
| + EXPECT_THAT(expected_states, Eq(handler.GetLastInvalidationMap()));
|
| +
|
| + this->delegate_.TriggerOnInvalidatorStateChange(
|
| + syncer::TRANSIENT_INVALIDATION_ERROR);
|
| + EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
|
| + handler.GetInvalidatorState());
|
| +
|
| + this->delegate_.TriggerOnInvalidatorStateChange(
|
| + syncer::INVALIDATION_CREDENTIALS_REJECTED);
|
| + EXPECT_EQ(syncer::INVALIDATION_CREDENTIALS_REJECTED,
|
| + handler.GetInvalidatorState());
|
| +
|
| + invalidator->UnregisterInvalidationHandler(&handler);
|
| +
|
| + // Should be ignored since |handler| isn't registered anymore.
|
| + this->delegate_.TriggerOnIncomingInvalidation(states);
|
| + 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(InvalidationFrontendTest, MultipleHandlers) {
|
| + invalidation::InvalidationFrontend* const invalidator =
|
| + this->CreateAndInitializeInvalidationFrontend();
|
| +
|
| + syncer::FakeInvalidationHandler handler1;
|
| + syncer::FakeInvalidationHandler handler2;
|
| + syncer::FakeInvalidationHandler handler3;
|
| + syncer::FakeInvalidationHandler handler4;
|
| +
|
| + invalidator->RegisterInvalidationHandler(&handler1);
|
| + invalidator->RegisterInvalidationHandler(&handler2);
|
| + invalidator->RegisterInvalidationHandler(&handler3);
|
| + invalidator->RegisterInvalidationHandler(&handler4);
|
| +
|
| + {
|
| + syncer::ObjectIdSet ids;
|
| + ids.insert(this->id1);
|
| + ids.insert(this->id2);
|
| + invalidator->UpdateRegisteredInvalidationIds(&handler1, ids);
|
| + }
|
| +
|
| + {
|
| + syncer::ObjectIdSet ids;
|
| + ids.insert(this->id3);
|
| + invalidator->UpdateRegisteredInvalidationIds(&handler2, ids);
|
| + }
|
| +
|
| + // Don't register any IDs for handler3.
|
| +
|
| + {
|
| + syncer::ObjectIdSet ids;
|
| + ids.insert(this->id4);
|
| + invalidator->UpdateRegisteredInvalidationIds(&handler4, ids);
|
| + }
|
| +
|
| + invalidator->UnregisterInvalidationHandler(&handler4);
|
| +
|
| + this->delegate_.TriggerOnInvalidatorStateChange(
|
| + syncer::INVALIDATIONS_ENABLED);
|
| + EXPECT_EQ(syncer::INVALIDATIONS_ENABLED, handler1.GetInvalidatorState());
|
| + EXPECT_EQ(syncer::INVALIDATIONS_ENABLED, handler2.GetInvalidatorState());
|
| + EXPECT_EQ(syncer::INVALIDATIONS_ENABLED, handler3.GetInvalidatorState());
|
| + EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
|
| + handler4.GetInvalidatorState());
|
| +
|
| + {
|
| + syncer::ObjectIdInvalidationMap states;
|
| + states[this->id1].payload = "1";
|
| + states[this->id2].payload = "2";
|
| + states[this->id3].payload = "3";
|
| + states[this->id4].payload = "4";
|
| + this->delegate_.TriggerOnIncomingInvalidation(states);
|
| +
|
| + syncer::ObjectIdInvalidationMap expected_states;
|
| + expected_states[this->id1].payload = "1";
|
| + expected_states[this->id2].payload = "2";
|
| +
|
| + EXPECT_EQ(1, handler1.GetInvalidationCount());
|
| + EXPECT_THAT(expected_states, Eq(handler1.GetLastInvalidationMap()));
|
| +
|
| + expected_states.clear();
|
| + expected_states[this->id3].payload = "3";
|
| +
|
| + EXPECT_EQ(1, handler2.GetInvalidationCount());
|
| + EXPECT_THAT(expected_states, Eq(handler2.GetLastInvalidationMap()));
|
| +
|
| + EXPECT_EQ(0, handler3.GetInvalidationCount());
|
| + EXPECT_EQ(0, handler4.GetInvalidationCount());
|
| + }
|
| +
|
| + this->delegate_.TriggerOnInvalidatorStateChange(
|
| + syncer::TRANSIENT_INVALIDATION_ERROR);
|
| + EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
|
| + handler1.GetInvalidatorState());
|
| + EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
|
| + handler2.GetInvalidatorState());
|
| + EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
|
| + handler3.GetInvalidatorState());
|
| + EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
|
| + handler4.GetInvalidatorState());
|
| +
|
| + invalidator->UnregisterInvalidationHandler(&handler3);
|
| + invalidator->UnregisterInvalidationHandler(&handler2);
|
| + invalidator->UnregisterInvalidationHandler(&handler1);
|
| +}
|
| +
|
| +// Make sure that passing an empty set to UpdateRegisteredInvalidationIds clears
|
| +// the corresponding entries for the handler.
|
| +TYPED_TEST_P(InvalidationFrontendTest, EmptySetUnregisters) {
|
| + invalidation::InvalidationFrontend* const invalidator =
|
| + this->CreateAndInitializeInvalidationFrontend();
|
| +
|
| + syncer::FakeInvalidationHandler handler1;
|
| +
|
| + // Control observer.
|
| + syncer::FakeInvalidationHandler handler2;
|
| +
|
| + invalidator->RegisterInvalidationHandler(&handler1);
|
| + invalidator->RegisterInvalidationHandler(&handler2);
|
| +
|
| + {
|
| + syncer::ObjectIdSet ids;
|
| + ids.insert(this->id1);
|
| + ids.insert(this->id2);
|
| + invalidator->UpdateRegisteredInvalidationIds(&handler1, ids);
|
| + }
|
| +
|
| + {
|
| + syncer::ObjectIdSet ids;
|
| + ids.insert(this->id3);
|
| + invalidator->UpdateRegisteredInvalidationIds(&handler2, ids);
|
| + }
|
| +
|
| + // Unregister the IDs for the first observer. It should not receive any
|
| + // further invalidations.
|
| + invalidator->UpdateRegisteredInvalidationIds(&handler1,
|
| + syncer::ObjectIdSet());
|
| +
|
| + this->delegate_.TriggerOnInvalidatorStateChange(
|
| + syncer::INVALIDATIONS_ENABLED);
|
| + EXPECT_EQ(syncer::INVALIDATIONS_ENABLED, handler1.GetInvalidatorState());
|
| + EXPECT_EQ(syncer::INVALIDATIONS_ENABLED, handler2.GetInvalidatorState());
|
| +
|
| + {
|
| + syncer::ObjectIdInvalidationMap states;
|
| + states[this->id1].payload = "1";
|
| + states[this->id2].payload = "2";
|
| + states[this->id3].payload = "3";
|
| + this->delegate_.TriggerOnIncomingInvalidation(states);
|
| + EXPECT_EQ(0, handler1.GetInvalidationCount());
|
| + EXPECT_EQ(1, handler2.GetInvalidationCount());
|
| + }
|
| +
|
| + this->delegate_.TriggerOnInvalidatorStateChange(
|
| + syncer::TRANSIENT_INVALIDATION_ERROR);
|
| + EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
|
| + handler1.GetInvalidatorState());
|
| + EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
|
| + handler2.GetInvalidatorState());
|
| +
|
| + invalidator->UnregisterInvalidationHandler(&handler2);
|
| + invalidator->UnregisterInvalidationHandler(&handler1);
|
| +}
|
| +
|
| +namespace internal {
|
| +
|
| +// A FakeInvalidationHandler that is "bound" to a specific
|
| +// InvalidationFrontend. This is for cross-referencing state information with
|
| +// the bound InvalidationFrontend.
|
| +class BoundFakeInvalidationHandler : public syncer::FakeInvalidationHandler {
|
| + public:
|
| + explicit BoundFakeInvalidationHandler(
|
| + const invalidation::InvalidationFrontend& invalidator);
|
| + virtual ~BoundFakeInvalidationHandler();
|
| +
|
| + // Returns the last return value of GetInvalidatorState() on the
|
| + // bound invalidator from the last time the invalidator state
|
| + // changed.
|
| + syncer::InvalidatorState GetLastRetrievedState() const;
|
| +
|
| + // InvalidationHandler implementation.
|
| + virtual void OnInvalidatorStateChange(
|
| + syncer::InvalidatorState state) OVERRIDE;
|
| +
|
| + private:
|
| + const invalidation::InvalidationFrontend& invalidator_;
|
| + syncer::InvalidatorState last_retrieved_state_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(BoundFakeInvalidationHandler);
|
| +};
|
| +
|
| +} // namespace internal
|
| +
|
| +TYPED_TEST_P(InvalidationFrontendTest, GetInvalidatorStateAlwaysCurrent) {
|
| + invalidation::InvalidationFrontend* const invalidator =
|
| + this->CreateAndInitializeInvalidationFrontend();
|
| +
|
| + internal::BoundFakeInvalidationHandler handler(*invalidator);
|
| + invalidator->RegisterInvalidationHandler(&handler);
|
| +
|
| + this->delegate_.TriggerOnInvalidatorStateChange(
|
| + syncer::INVALIDATIONS_ENABLED);
|
| + EXPECT_EQ(syncer::INVALIDATIONS_ENABLED, handler.GetInvalidatorState());
|
| + EXPECT_EQ(syncer::INVALIDATIONS_ENABLED, handler.GetLastRetrievedState());
|
| +
|
| + this->delegate_.TriggerOnInvalidatorStateChange(
|
| + syncer::TRANSIENT_INVALIDATION_ERROR);
|
| + EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
|
| + handler.GetInvalidatorState());
|
| + EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR,
|
| + handler.GetLastRetrievedState());
|
| +
|
| + invalidator->UnregisterInvalidationHandler(&handler);
|
| +}
|
| +
|
| +REGISTER_TYPED_TEST_CASE_P(InvalidationFrontendTest,
|
| + Basic, MultipleHandlers, EmptySetUnregisters,
|
| + GetInvalidatorStateAlwaysCurrent);
|
| +
|
| +#endif // CHROME_BROWSER_INVALIDATION_INVALIDATION_FRONTEND_TEST_TEMPLATE_H_
|
|
|