| Index: third_party/cacheinvalidation/src/google/cacheinvalidation/impl/protocol-handler_test.cc
|
| diff --git a/third_party/cacheinvalidation/src/google/cacheinvalidation/impl/protocol-handler_test.cc b/third_party/cacheinvalidation/src/google/cacheinvalidation/impl/protocol-handler_test.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..1ca15685f416729ac325c1850352f41f7ff794d3
|
| --- /dev/null
|
| +++ b/third_party/cacheinvalidation/src/google/cacheinvalidation/impl/protocol-handler_test.cc
|
| @@ -0,0 +1,674 @@
|
| +// Copyright 2012 Google Inc.
|
| +//
|
| +// Licensed under the Apache License, Version 2.0 (the "License");
|
| +// you may not use this file except in compliance with the License.
|
| +// You may obtain a copy of the License at
|
| +//
|
| +// http://www.apache.org/licenses/LICENSE-2.0
|
| +//
|
| +// Unless required by applicable law or agreed to in writing, software
|
| +// distributed under the License is distributed on an "AS IS" BASIS,
|
| +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| +// See the License for the specific language governing permissions and
|
| +// limitations under the License.
|
| +
|
| +// Unit tests for the ProtocolHandler class.
|
| +
|
| +#include "google/cacheinvalidation/types.pb.h"
|
| +#include "google/cacheinvalidation/include/types.h"
|
| +#include "google/cacheinvalidation/deps/gmock.h"
|
| +#include "google/cacheinvalidation/deps/googletest.h"
|
| +#include "google/cacheinvalidation/deps/string_util.h"
|
| +#include "google/cacheinvalidation/impl/basic-system-resources.h"
|
| +#include "google/cacheinvalidation/impl/constants.h"
|
| +#include "google/cacheinvalidation/impl/invalidation-client-impl.h"
|
| +#include "google/cacheinvalidation/impl/protocol-handler.h"
|
| +#include "google/cacheinvalidation/impl/statistics.h"
|
| +#include "google/cacheinvalidation/impl/throttle.h"
|
| +#include "google/cacheinvalidation/impl/ticl-message-validator.h"
|
| +#include "google/cacheinvalidation/test/deterministic-scheduler.h"
|
| +#include "google/cacheinvalidation/test/test-logger.h"
|
| +#include "google/cacheinvalidation/test/test-utils.h"
|
| +
|
| +namespace invalidation {
|
| +
|
| +using ::ipc::invalidation::ClientType_Type_TEST;
|
| +using ::ipc::invalidation::ObjectSource_Type_TEST;
|
| +using ::testing::_;
|
| +using ::testing::AllOf;
|
| +using ::testing::ByRef;
|
| +using ::testing::DoAll;
|
| +using ::testing::ElementsAre;
|
| +using ::testing::EqualsProto;
|
| +using ::testing::Eq;
|
| +using ::testing::Matcher;
|
| +using ::testing::Property;
|
| +using ::testing::Return;
|
| +using ::testing::ReturnPointee;
|
| +using ::testing::SaveArg;
|
| +using ::testing::SetArgPointee;
|
| +using ::testing::StrictMock;
|
| +using ::testing::proto::WhenDeserializedAs;
|
| +
|
| +/* Returns whether two headers are equal. */
|
| +bool HeaderEqual(const ServerMessageHeader& expected,
|
| + const ServerMessageHeader& actual) {
|
| + // If the token is different or if one of the registration summaries is NULL
|
| + // and the other is non-NULL, return false.
|
| + if (((expected.registration_summary() != NULL) !=
|
| + (actual.registration_summary() != NULL)) ||
|
| + (expected.token() != actual.token())) {
|
| + return false;
|
| + }
|
| +
|
| + // The tokens are the same and registration summaries are either both
|
| + // null or non-null.
|
| + return (expected.registration_summary() == NULL) ||
|
| + ((expected.registration_summary()->num_registrations() ==
|
| + actual.registration_summary()->num_registrations()) &&
|
| + (expected.registration_summary()->registration_digest() ==
|
| + actual.registration_summary()->registration_digest()));
|
| +}
|
| +
|
| +// A mock of the ProtocolListener interface.
|
| +class MockProtocolListener : public ProtocolListener {
|
| + public:
|
| + MOCK_METHOD0(HandleMessageSent, void());
|
| +
|
| + MOCK_METHOD1(HandleNetworkStatusChange, void(bool)); // NOLINT
|
| +
|
| + MOCK_METHOD1(GetRegistrationSummary, void(RegistrationSummary*)); // NOLINT
|
| +
|
| + MOCK_METHOD0(GetClientToken, string());
|
| +};
|
| +
|
| +// Tests the basic functionality of the protocol handler.
|
| +class ProtocolHandlerTest : public UnitTestBase {
|
| + public:
|
| + virtual ~ProtocolHandlerTest() {}
|
| +
|
| + // Performs setup for protocol handler unit tests, e.g. creating resource
|
| + // components and setting up common expectations for certain mock objects.
|
| + virtual void SetUp() {
|
| + // Use a strict mock scheduler for the listener, since it shouldn't be used
|
| + // at all by the protocol handler.
|
| + UnitTestBase::SetUp();
|
| + InitListenerExpectations();
|
| + validator.reset(new TiclMessageValidator(logger)); // Create msg validator
|
| +
|
| + // Create the protocol handler object.
|
| + random.reset(new Random(InvalidationClientUtil::GetCurrentTimeMs(
|
| + resources.get()->internal_scheduler())));
|
| + smearer.reset(new Smearer(random.get(), kDefaultSmearPercent));
|
| + protocol_handler.reset(
|
| + new ProtocolHandler(
|
| + config, resources.get(), smearer.get(), statistics.get(),
|
| + ClientType_Type_TEST, "unit-test", &listener, validator.get()));
|
| + batching_task.reset(
|
| + new BatchingTask(protocol_handler.get(), smearer.get(),
|
| + TimeDelta::FromMilliseconds(config.batching_delay_ms())));
|
| + }
|
| +
|
| + // Configuration for the protocol handler (uses defaults).
|
| + ProtocolHandlerConfigP config;
|
| +
|
| + // The protocol handler being tested. Created fresh for each test function.
|
| + scoped_ptr<ProtocolHandler> protocol_handler;
|
| +
|
| + // A mock protocol listener. We make this strict in order to have tight
|
| + // control over the interactions between this and the protocol handler.
|
| + // SetUp() installs expectations to allow GetClientToken() and
|
| + // GetRegistrationSummary() to be called any time and to give them
|
| + // reasonable behavior.
|
| + StrictMock<MockProtocolListener> listener;
|
| +
|
| + // Ticl message validator. We do not mock this, since the correctness of the
|
| + // protocol handler depends on it.
|
| + scoped_ptr<TiclMessageValidator> validator;
|
| +
|
| + // Token and registration summary for the mock listener to return when
|
| + // the protocol handler requests them.
|
| + string token;
|
| + RegistrationSummary summary;
|
| +
|
| + // A smearer to randomize delays.
|
| + scoped_ptr<Smearer> smearer;
|
| +
|
| + // A random number generator.
|
| + scoped_ptr<Random> random;
|
| +
|
| + // Batching task for the protocol handler.
|
| + scoped_ptr<BatchingTask> batching_task;
|
| +
|
| + void AddExpectationForHandleMessageSent() {
|
| + EXPECT_CALL(listener, HandleMessageSent());
|
| + }
|
| +
|
| + /*
|
| + * Processes a |message| using the protocol handler, initializing
|
| + * |parsed_message| with the result.
|
| + *
|
| + * Returns whether the message could be parsed.
|
| + */
|
| + bool ProcessMessage(ServerToClientMessage message,
|
| + ParsedMessage* parsed_message) {
|
| + string serialized;
|
| + message.SerializeToString(&serialized);
|
| + bool accepted = protocol_handler->HandleIncomingMessage(
|
| + serialized, parsed_message);
|
| + return accepted;
|
| + }
|
| +
|
| + private:
|
| + void InitListenerExpectations() {
|
| + // When the handler asks the listener for the client token, return whatever
|
| + // |token| currently is.
|
| + EXPECT_CALL(listener, GetClientToken())
|
| + .WillRepeatedly(ReturnPointee(&token));
|
| +
|
| + // If the handler asks the listener for a registration summary, respond by
|
| + // supplying a fake summary.
|
| + InitZeroRegistrationSummary(&summary);
|
| + EXPECT_CALL(listener, GetRegistrationSummary(_))
|
| + .WillRepeatedly(SetArgPointee<0>(summary));
|
| + }
|
| +};
|
| +
|
| +// Asks the protocol handler to send an initialize message. Waits for the
|
| +// batching delay to pass. Checks that appropriate calls are made on the
|
| +// listener and that a proper message is sent on the network.
|
| +TEST_F(ProtocolHandlerTest, SendInitializeOnly) {
|
| + ApplicationClientIdP app_client_id;
|
| + app_client_id.set_client_name("unit-test-client-id");
|
| + app_client_id.set_client_type(ClientType_Type_TEST);
|
| +
|
| + // Client's token is initially empty. Give it an arbitrary nonce.
|
| + token = "";
|
| + string nonce = "unit-test-nonce";
|
| +
|
| + // SendInitializeMessage checks that it's running on the work queue thread, so
|
| + // we need to schedule the call.
|
| + internal_scheduler->Schedule(
|
| + Scheduler::NoDelay(),
|
| + NewPermanentCallback(
|
| + protocol_handler.get(), &ProtocolHandler::SendInitializeMessage,
|
| + app_client_id, nonce, batching_task.get(), "Startup"));
|
| +
|
| + AddExpectationForHandleMessageSent();
|
| + ClientToServerMessage expected_message;
|
| +
|
| + // Build the header.
|
| + ClientHeader* header = expected_message.mutable_header();
|
| + ProtoHelpers::InitProtocolVersion(header->mutable_protocol_version());
|
| + header->mutable_registration_summary()->CopyFrom(summary);
|
| + header->set_max_known_server_time_ms(0);
|
| + header->set_message_id("1");
|
| +
|
| + // Note: because the batching task is smeared, we don't know what the client's
|
| + // timestamp will be. We omit it from this proto and do a partial match in
|
| + // the EXPECT_CALL but also save the proto and check later that it doesn't
|
| + // contain anything we don't expect.
|
| +
|
| + // Create the expected initialize message.
|
| + InitializeMessage* initialize_message =
|
| + expected_message.mutable_initialize_message();
|
| + initialize_message->set_client_type(ClientType_Type_TEST);
|
| + initialize_message->set_nonce(nonce);
|
| + initialize_message->mutable_application_client_id()->CopyFrom(app_client_id);
|
| + initialize_message->set_digest_serialization_type(
|
| + InitializeMessage_DigestSerializationType_BYTE_BASED);
|
| +
|
| + string actual_serialized;
|
| + EXPECT_CALL(
|
| + *network,
|
| + SendMessage(WhenDeserializedAs<ClientToServerMessage>(
|
| + // Check that the deserialized message has the initialize message and
|
| + // header fields we expect.
|
| + AllOf(Property(&ClientToServerMessage::initialize_message,
|
| + EqualsProto(*initialize_message)),
|
| + Property(&ClientToServerMessage::header,
|
| + ClientHeaderMatches(header))))))
|
| + .WillOnce(SaveArg<0>(&actual_serialized));
|
| +
|
| + // The actual message won't be sent until after the batching delay, which is
|
| + // smeared, so double it to be sure enough time will have passed.
|
| + TimeDelta wait_time = GetMaxBatchingDelay(config);
|
| + internal_scheduler->PassTime(wait_time);
|
| +
|
| + // By now we expect the message to have been sent, so we'll deserialize it
|
| + // and check that it doesn't have anything we don't expect.
|
| + ClientToServerMessage actual_message;
|
| + actual_message.ParseFromString(actual_serialized);
|
| + ASSERT_FALSE(actual_message.has_info_message());
|
| + ASSERT_FALSE(actual_message.has_invalidation_ack_message());
|
| + ASSERT_FALSE(actual_message.has_registration_message());
|
| + ASSERT_FALSE(actual_message.has_registration_sync_message());
|
| + ASSERT_GE(actual_message.header().client_time_ms(),
|
| + InvalidationClientUtil::GetTimeInMillis(start_time));
|
| + ASSERT_LE(actual_message.header().client_time_ms(),
|
| + InvalidationClientUtil::GetTimeInMillis(start_time + wait_time));
|
| +}
|
| +
|
| +// Tests the receipt of a token control message like what we'd expect in
|
| +// response to an initialize message. Check that appropriate calls are made on
|
| +// the protocol listener.
|
| +TEST_F(ProtocolHandlerTest, ReceiveTokenControlOnly) {
|
| + ServerToClientMessage message;
|
| + ServerHeader* header = message.mutable_header();
|
| + string nonce = "fake nonce";
|
| + InitServerHeader(nonce, header);
|
| +
|
| + string new_token = "new token";
|
| + message.mutable_token_control_message()->set_new_token(new_token);
|
| +
|
| + ServerMessageHeader expected_header;
|
| + expected_header.InitFrom(&nonce, &header->registration_summary());
|
| + ParsedMessage parsed_message;
|
| + ProcessMessage(message, &parsed_message);
|
| + ASSERT_TRUE(HeaderEqual(expected_header, parsed_message.header));
|
| + ASSERT_TRUE(parsed_message.token_control_message != NULL);
|
| +}
|
| +
|
| +// Test that the protocol handler correctly buffers multiple message types.
|
| +// Tell it to send registrations, then unregistrations (with some overlap in the
|
| +// sets of objects). Then send some invalidation acks and finally a
|
| +// registration subtree. Wait for the batching interval to pass, and then check
|
| +// that the message sent out contains everything we expect.
|
| +TEST_F(ProtocolHandlerTest, SendMultipleMessageTypes) {
|
| + // Concoct some performance counters and config parameters, and ask to send
|
| + // an info message with them.
|
| + vector<pair<string, int> > perf_counters;
|
| + perf_counters.push_back(make_pair("x", 3));
|
| + perf_counters.push_back(make_pair("y", 81));
|
| + ClientConfigP client_config;
|
| + InvalidationClientImpl::InitConfig(&client_config);
|
| +
|
| + internal_scheduler->Schedule(
|
| + Scheduler::NoDelay(),
|
| + NewPermanentCallback(
|
| + protocol_handler.get(), &ProtocolHandler::SendInfoMessage,
|
| + perf_counters, &client_config, true, batching_task.get()));
|
| +
|
| + // Synthesize a few test object ids.
|
| + vector<ObjectIdP> oids;
|
| + InitTestObjectIds(3, &oids);
|
| +
|
| + // Register for the first two.
|
| + vector<ObjectIdP> oid_vec;
|
| + oid_vec.push_back(oids[0]);
|
| + oid_vec.push_back(oids[1]);
|
| +
|
| + internal_scheduler->Schedule(
|
| + Scheduler::NoDelay(),
|
| + NewPermanentCallback(
|
| + protocol_handler.get(), &ProtocolHandler::SendRegistrations,
|
| + oid_vec, RegistrationP_OpType_REGISTER, batching_task.get()));
|
| +
|
| + // Then unregister for the second and third. This overrides the registration
|
| + // on oids[1].
|
| + oid_vec.clear();
|
| + oid_vec.push_back(oids[1]);
|
| + oid_vec.push_back(oids[2]);
|
| + internal_scheduler->Schedule(
|
| + Scheduler::NoDelay(),
|
| + NewPermanentCallback(
|
| + protocol_handler.get(), &ProtocolHandler::SendRegistrations,
|
| + oid_vec, RegistrationP_OpType_UNREGISTER, batching_task.get()));
|
| +
|
| + // Send a couple of invalidations.
|
| + vector<InvalidationP> invalidations;
|
| + MakeInvalidationsFromObjectIds(oids, &invalidations);
|
| + invalidations.pop_back();
|
| + for (size_t i = 0; i < invalidations.size(); ++i) {
|
| + internal_scheduler->Schedule(
|
| + Scheduler::NoDelay(),
|
| + NewPermanentCallback(
|
| + protocol_handler.get(), &ProtocolHandler::SendInvalidationAck,
|
| + invalidations[i], batching_task.get()));
|
| + }
|
| +
|
| + // Send a simple registration subtree.
|
| + RegistrationSubtree subtree;
|
| + subtree.add_registered_object()->CopyFrom(oids[0]);
|
| + internal_scheduler->Schedule(
|
| + Scheduler::NoDelay(),
|
| + NewPermanentCallback(
|
| + protocol_handler.get(), &ProtocolHandler::SendRegistrationSyncSubtree,
|
| + subtree, batching_task.get()));
|
| +
|
| + AddExpectationForHandleMessageSent();
|
| +
|
| + token = "test token";
|
| +
|
| + // The message it sends should contain all of the expected information:
|
| + ClientToServerMessage expected_message;
|
| +
|
| + // Header.
|
| + ClientHeader* header = expected_message.mutable_header();
|
| + ProtoHelpers::InitProtocolVersion(header->mutable_protocol_version());
|
| + header->mutable_registration_summary()->CopyFrom(summary);
|
| + header->set_client_token(token);
|
| + header->set_max_known_server_time_ms(0);
|
| + header->set_message_id("1");
|
| +
|
| + // Note: because the batching task is smeared, we don't know what the client's
|
| + // timestamp will be. We omit it from this proto and do a partial match in
|
| + // the EXPECT_CALL but also save the proto and check later that it doesn't
|
| + // contain anything we don't expect.
|
| +
|
| + // Registrations.
|
| + RegistrationMessage* reg_message =
|
| + expected_message.mutable_registration_message();
|
| + RegistrationP* registration;
|
| + registration = reg_message->add_registration();
|
| + registration->mutable_object_id()->CopyFrom(oids[0]);
|
| + registration->set_op_type(RegistrationP_OpType_REGISTER);
|
| +
|
| + registration = reg_message->add_registration();
|
| + registration->mutable_object_id()->CopyFrom(oids[1]);
|
| + registration->set_op_type(RegistrationP_OpType_UNREGISTER);
|
| +
|
| + registration = reg_message->add_registration();
|
| + registration->mutable_object_id()->CopyFrom(oids[2]);
|
| + registration->set_op_type(RegistrationP_OpType_UNREGISTER);
|
| +
|
| + // Registration sync message.
|
| + expected_message.mutable_registration_sync_message()->add_subtree()
|
| + ->CopyFrom(subtree);
|
| +
|
| + // Invalidation acks.
|
| + InvalidationMessage* invalidation_message =
|
| + expected_message.mutable_invalidation_ack_message();
|
| + InitInvalidationMessage(invalidations, invalidation_message);
|
| +
|
| + // Info message.
|
| + InfoMessage* info_message = expected_message.mutable_info_message();
|
| + ProtoHelpers::InitClientVersion("unit-test", "unit-test",
|
| + info_message->mutable_client_version());
|
| + info_message->set_server_registration_summary_requested(true);
|
| + info_message->mutable_client_config()->CopyFrom(client_config);
|
| + PropertyRecord* prop_rec;
|
| + for (uint32 i = 0; i < perf_counters.size(); ++i) {
|
| + prop_rec = info_message->add_performance_counter();
|
| + prop_rec->set_name(perf_counters[i].first);
|
| + prop_rec->set_value(perf_counters[i].second);
|
| + }
|
| +
|
| + string actual_serialized;
|
| + EXPECT_CALL(
|
| + *network,
|
| + SendMessage(
|
| + WhenDeserializedAs<ClientToServerMessage>(
|
| + // Check that the deserialized message has the invalidation acks,
|
| + // registrations, info message, and header fields we expect.
|
| + AllOf(Property(&ClientToServerMessage::invalidation_ack_message,
|
| + EqualsProto(*invalidation_message)),
|
| + Property(&ClientToServerMessage::registration_message,
|
| + EqualsProto(*reg_message)),
|
| + Property(&ClientToServerMessage::info_message,
|
| + EqualsProto(*info_message)),
|
| + Property(&ClientToServerMessage::header,
|
| + ClientHeaderMatches(header))))))
|
| + .WillOnce(SaveArg<0>(&actual_serialized));
|
| +
|
| + TimeDelta wait_time = GetMaxBatchingDelay(config);
|
| + internal_scheduler->PassTime(wait_time);
|
| +
|
| + ClientToServerMessage actual_message;
|
| + actual_message.ParseFromString(actual_serialized);
|
| +
|
| + ASSERT_FALSE(actual_message.has_initialize_message());
|
| + ASSERT_GE(actual_message.header().client_time_ms(),
|
| + InvalidationClientUtil::GetTimeInMillis(start_time));
|
| + ASSERT_LE(actual_message.header().client_time_ms(),
|
| + InvalidationClientUtil::GetTimeInMillis(start_time + wait_time));
|
| +}
|
| +
|
| +// Check that if the protocol handler receives a message with several sub-
|
| +// messages set, it makes all the appropriate calls on the listener.
|
| +TEST_F(ProtocolHandlerTest, IncomingCompositeMessage) {
|
| + // Build up a message with a number of sub-messages in it:
|
| + ServerToClientMessage message;
|
| +
|
| + // First the header.
|
| + token = "test token";
|
| + InitServerHeader(token, message.mutable_header());
|
| +
|
| + // Fabricate a few object ids for use in invalidations and registration
|
| + // statuses.
|
| + vector<ObjectIdP> object_ids;
|
| + InitTestObjectIds(3, &object_ids);
|
| +
|
| + // Add invalidations.
|
| + vector<InvalidationP> invalidations;
|
| + MakeInvalidationsFromObjectIds(object_ids, &invalidations);
|
| + for (int i = 0; i < 3; ++i) {
|
| + message.mutable_invalidation_message()->add_invalidation()->CopyFrom(
|
| + invalidations[i]);
|
| + }
|
| +
|
| + // Add registration statuses.
|
| + vector<RegistrationStatus> registration_statuses;
|
| + MakeRegistrationStatusesFromObjectIds(object_ids, true, true,
|
| + ®istration_statuses);
|
| + for (int i = 0; i < 3; ++i) {
|
| + message.mutable_registration_status_message()
|
| + ->add_registration_status()->CopyFrom(registration_statuses[i]);
|
| + }
|
| +
|
| + // Add a registration sync request message.
|
| + message.mutable_registration_sync_request_message();
|
| +
|
| + // Add an info request message.
|
| + message.mutable_info_request_message()->add_info_type(
|
| + InfoRequestMessage_InfoType_GET_PERFORMANCE_COUNTERS);
|
| +
|
| + // The header we expect the listener to be called with.
|
| + ServerMessageHeader expected_header;
|
| + expected_header.InitFrom(&token, &summary);
|
| +
|
| + ParsedMessage parsed_message;
|
| + ProcessMessage(message, &parsed_message);
|
| + ASSERT_TRUE(HeaderEqual(expected_header, parsed_message.header));
|
| + ASSERT_TRUE(parsed_message.invalidation_message != NULL);
|
| + ASSERT_TRUE(parsed_message.registration_status_message != NULL);
|
| + ASSERT_TRUE(parsed_message.registration_sync_request_message != NULL);
|
| + ASSERT_TRUE(parsed_message.info_request_message != NULL);
|
| +}
|
| +
|
| +// Test that the protocol handler drops an invalid message.
|
| +TEST_F(ProtocolHandlerTest, InvalidInboundMessage) {
|
| + // Make an invalid message (omit protocol version from header).
|
| + ServerToClientMessage message;
|
| + string token = "test token";
|
| + ServerHeader* header = message.mutable_header();
|
| + InitServerHeader(token, header);
|
| + header->clear_protocol_version();
|
| +
|
| + // Add an info request message to check that it doesn't get processed.
|
| + message.mutable_info_request_message()->add_info_type(
|
| + InfoRequestMessage_InfoType_GET_PERFORMANCE_COUNTERS);
|
| + ParsedMessage parsed_message;
|
| + ProcessMessage(message, &parsed_message);
|
| + ASSERT_EQ(1, statistics->GetClientErrorCounterForTest(
|
| + Statistics::ClientErrorType_INCOMING_MESSAGE_FAILURE));
|
| +}
|
| +
|
| +// Test that the protocol handler drops a message whose major version doesn't
|
| +// match what it understands.
|
| +TEST_F(ProtocolHandlerTest, MajorVersionMismatch) {
|
| + // Make a message with a different protocol major version.
|
| + ServerToClientMessage message;
|
| + token = "test token";
|
| + ServerHeader* header = message.mutable_header();
|
| + InitServerHeader(token, header);
|
| + header->mutable_protocol_version()->mutable_version()->set_major_version(1);
|
| +
|
| + // Add an info request message to check that it doesn't get processed.
|
| + message.mutable_info_request_message()->add_info_type(
|
| + InfoRequestMessage_InfoType_GET_PERFORMANCE_COUNTERS);
|
| +
|
| + ParsedMessage parsed_message;
|
| + ProcessMessage(message, &parsed_message);
|
| + ASSERT_EQ(1, statistics->GetClientErrorCounterForTest(
|
| + Statistics::ClientErrorType_PROTOCOL_VERSION_FAILURE));
|
| +}
|
| +
|
| +// Test that the protocol handler doesn't drop a message whose minor version
|
| +// doesn't match what it understands.
|
| +TEST_F(ProtocolHandlerTest, MinorVersionMismatch) {
|
| + // Make a message with a different protocol minor version.
|
| + ServerToClientMessage message;
|
| + token = "test token";
|
| + ServerHeader* header = message.mutable_header();
|
| + InitServerHeader(token, header);
|
| + header->mutable_protocol_version()->mutable_version()->set_minor_version(4);
|
| +
|
| + ServerMessageHeader expected_header;
|
| + expected_header.InitFrom(&token, &summary);
|
| +
|
| + ParsedMessage parsed_message;
|
| + ProcessMessage(message, &parsed_message);
|
| + ASSERT_TRUE(HeaderEqual(expected_header, parsed_message.header));
|
| + ASSERT_EQ(0, statistics->GetClientErrorCounterForTest(
|
| + Statistics::ClientErrorType_PROTOCOL_VERSION_FAILURE));
|
| +}
|
| +
|
| +// Test that the protocol handler honors a config message (even if the server
|
| +// token doesn't match) and does not call any listener methods.
|
| +TEST_F(ProtocolHandlerTest, ConfigMessage) {
|
| + // Fabricate a config message.
|
| + ServerToClientMessage message;
|
| + token = "test token";
|
| + InitServerHeader(token, message.mutable_header());
|
| + token = "token-that-should-mismatch";
|
| +
|
| + int next_message_delay_ms = 2000 * 1000;
|
| + message.mutable_config_change_message()->set_next_message_delay_ms(
|
| + next_message_delay_ms);
|
| +
|
| + ParsedMessage parsed_message;
|
| + ProcessMessage(message, &parsed_message);
|
| +
|
| + // Check that the protocol handler recorded receiving the config change
|
| + // message, and that it has updated the next time it will send a message.
|
| + ASSERT_EQ(1, statistics->GetReceivedMessageCounterForTest(
|
| + Statistics::ReceivedMessageType_CONFIG_CHANGE));
|
| + ASSERT_EQ(
|
| + InvalidationClientUtil::GetTimeInMillis(
|
| + start_time + TimeDelta::FromMilliseconds(next_message_delay_ms)),
|
| + protocol_handler->GetNextMessageSendTimeMsForTest());
|
| +
|
| + // Request to send an info message, and check that it doesn't get sent.
|
| + vector<pair<string, int> > empty_vector;
|
| + internal_scheduler->Schedule(
|
| + Scheduler::NoDelay(),
|
| + NewPermanentCallback(
|
| + protocol_handler.get(), &ProtocolHandler::SendInfoMessage,
|
| + empty_vector, NULL, false, batching_task.get()));
|
| +
|
| + // Keep simulating passage of time until just before the quiet period ends.
|
| + // Nothing should be sent. (The mock network will catch any attempts to send
|
| + // and fail the test.)
|
| + internal_scheduler->PassTime(
|
| + TimeDelta::FromMilliseconds(next_message_delay_ms - 1));
|
| +}
|
| +
|
| +// Test that the protocol handler properly delivers an error message to the
|
| +// listener.
|
| +TEST_F(ProtocolHandlerTest, ErrorMessage) {
|
| + // Fabricate an error message.
|
| + ServerToClientMessage message;
|
| + token = "test token";
|
| + InitServerHeader(token, message.mutable_header());
|
| +
|
| + // Add an error message.
|
| + ErrorMessage::Code error_code = ErrorMessage_Code_AUTH_FAILURE;
|
| + string description = "invalid auth token";
|
| + InitErrorMessage(error_code, description, message.mutable_error_message());
|
| + ServerMessageHeader expected_header;
|
| + expected_header.InitFrom(&token, &summary);
|
| +
|
| + // Deliver the message.
|
| + ParsedMessage parsed_message;
|
| + ProcessMessage(message, &parsed_message);
|
| + ASSERT_TRUE(HeaderEqual(expected_header, parsed_message.header));
|
| + ASSERT_TRUE(parsed_message.error_message != NULL);
|
| +}
|
| +
|
| +// Tests that the protocol handler accepts a message from the server if the
|
| +// token doesn't match the client's (the caller is responsible for checking
|
| +// the token).
|
| +TEST_F(ProtocolHandlerTest, TokenMismatch) {
|
| + // Create the server message with one token.
|
| + token = "test token";
|
| + ServerToClientMessage message;
|
| + InitServerHeader(token, message.mutable_header());
|
| +
|
| + // Give the client a different token.
|
| + token = "token-that-should-mismatch";
|
| +
|
| + // Deliver the message.
|
| + ParsedMessage parsed_message;
|
| + bool accepted = ProcessMessage(message, &parsed_message);
|
| + ASSERT_TRUE(accepted);
|
| +
|
| + ASSERT_EQ(0, statistics->GetClientErrorCounterForTest(
|
| + Statistics::ClientErrorType_TOKEN_MISMATCH));
|
| +}
|
| +
|
| +// Tests that the protocol handler won't send out a non-initialize message if
|
| +// the client has no token.
|
| +TEST_F(ProtocolHandlerTest, TokenMissing) {
|
| + token = "";
|
| + vector<pair<string, int> > empty_vector;
|
| +
|
| + internal_scheduler->Schedule(
|
| + Scheduler::NoDelay(),
|
| + NewPermanentCallback(
|
| + protocol_handler.get(),
|
| + &ProtocolHandler::SendInfoMessage, empty_vector, NULL, true,
|
| + batching_task.get()));
|
| +
|
| + internal_scheduler->PassTime(GetMaxBatchingDelay(config));
|
| +
|
| + ASSERT_EQ(1, statistics->GetClientErrorCounterForTest(
|
| + Statistics::ClientErrorType_TOKEN_MISSING_FAILURE));
|
| +}
|
| +
|
| +// Tests that the protocol handler won't send out a message that fails
|
| +// validation (in this case, an invalidation ack with a missing version).
|
| +TEST_F(ProtocolHandlerTest, InvalidOutboundMessage) {
|
| + token = "test token";
|
| +
|
| + vector<ObjectIdP> object_ids;
|
| + InitTestObjectIds(1, &object_ids);
|
| + vector<InvalidationP> invalidations;
|
| + MakeInvalidationsFromObjectIds(object_ids, &invalidations);
|
| + invalidations[0].clear_version();
|
| +
|
| + internal_scheduler->Schedule(
|
| + Scheduler::NoDelay(),
|
| + NewPermanentCallback(
|
| + protocol_handler.get(),
|
| + &ProtocolHandler::SendInvalidationAck,
|
| + invalidations[0],
|
| + batching_task.get()));
|
| +
|
| + internal_scheduler->PassTime(GetMaxBatchingDelay(config));
|
| +
|
| + ASSERT_EQ(1, statistics->GetClientErrorCounterForTest(
|
| + Statistics::ClientErrorType_OUTGOING_MESSAGE_FAILURE));
|
| +}
|
| +
|
| +// Tests that the protocol handler drops an unparseable message.
|
| +TEST_F(ProtocolHandlerTest, UnparseableInboundMessage) {
|
| + // Make an unparseable message.
|
| + string serialized = "this can't be a valid protocol buffer!";
|
| + ParsedMessage parsed_message;
|
| + bool accepted = protocol_handler->HandleIncomingMessage(serialized,
|
| + &parsed_message);
|
| + ASSERT_FALSE(accepted);
|
| +}
|
| +
|
| +} // namespace invalidation
|
|
|