blob: 4c5580361408475c6c13ad59ede02cba5e88d8b0 [file] [log] [blame]
// 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.
//
// Helper classes for tests including a mock Scheduler, a mock network, a
// mock storage layer, and a mock listener.
#ifndef GOOGLE_CACHEINVALIDATION_TEST_TEST_UTILS_H_
#define GOOGLE_CACHEINVALIDATION_TEST_TEST_UTILS_H_
#include "google/cacheinvalidation/client_protocol.pb.h"
#include "google/cacheinvalidation/include/invalidation-listener.h"
#include "google/cacheinvalidation/include/types.h"
#include "google/cacheinvalidation/types.pb.h"
#include "google/cacheinvalidation/deps/gmock.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/log-macro.h"
#include "google/cacheinvalidation/impl/protocol-handler.h"
#include "google/cacheinvalidation/impl/statistics.h"
#include "google/cacheinvalidation/test/deterministic-scheduler.h"
namespace invalidation {
using ::ipc::invalidation::ObjectSource_Type_TEST;
using ::ipc::invalidation::ClientHeader;
using ::ipc::invalidation::ClientVersion;
using ::ipc::invalidation::InvalidationP;
using ::ipc::invalidation::ObjectIdP;
using ::ipc::invalidation::ProtocolVersion;
using ::ipc::invalidation::RegistrationP_OpType_REGISTER;
using ::ipc::invalidation::RegistrationP_OpType_UNREGISTER;
using ::ipc::invalidation::RegistrationStatus;
using ::ipc::invalidation::RegistrationSummary;
using ::ipc::invalidation::ServerHeader;
using ::ipc::invalidation::ServerToClientMessage;
using ::ipc::invalidation::StatusP_Code_SUCCESS;
using ::ipc::invalidation::Version;
using ::google::protobuf::MessageLite;
using ::testing::_;
using ::testing::EqualsProto;
using ::testing::Matcher;
using ::testing::Property;
using ::testing::SaveArg;
using ::testing::StrictMock;
/* A random class whose RandDouble method always returns a given constant. */
class FakeRandom : public Random {
public:
// Constructs a fake random generator that always returns |return_value|,
// which must be in the range [0, 1).
explicit FakeRandom(double return_value)
: Random(0), return_value_(return_value) {
CHECK_GE(return_value_, 0.0);
CHECK_LT(return_value_, 1.0);
}
virtual ~FakeRandom() {}
virtual double RandDouble() {
return return_value_;
}
private:
double return_value_;
};
// A mock of the Scheduler interface.
class MockScheduler : public Scheduler {
public:
MOCK_METHOD2(Schedule, void(TimeDelta, Closure*)); // NOLINT
MOCK_CONST_METHOD0(IsRunningOnThread, bool());
MOCK_CONST_METHOD0(GetCurrentTime, Time());
MOCK_METHOD1(SetSystemResources, void(SystemResources*)); // NOLINT
};
// A mock of the Network interface.
class MockNetwork : public NetworkChannel {
public:
MOCK_METHOD1(SendMessage, void(const string&)); // NOLINT
MOCK_METHOD1(SetMessageReceiver, void(MessageCallback*)); // NOLINT
MOCK_METHOD1(AddNetworkStatusReceiver, void(NetworkStatusCallback*)); // NOLINT
MOCK_METHOD1(SetSystemResources, void(SystemResources*)); // NOLINT
};
// A mock of the Storage interface.
class MockStorage : public Storage {
public:
MOCK_METHOD3(WriteKey, void(const string&, const string&, WriteKeyCallback*)); // NOLINT
MOCK_METHOD2(ReadKey, void(const string&, ReadKeyCallback*)); // NOLINT
MOCK_METHOD2(DeleteKey, void(const string&, DeleteKeyCallback*)); // NOLINT
MOCK_METHOD1(ReadAllKeys, void(ReadAllKeysCallback*)); // NOLINT
MOCK_METHOD1(SetSystemResources, void(SystemResources*)); // NOLINT
};
// A mock of the InvalidationListener interface.
class MockInvalidationListener : public InvalidationListener {
public:
MOCK_METHOD1(Ready, void(InvalidationClient*)); // NOLINT
MOCK_METHOD3(Invalidate,
void(InvalidationClient *, const Invalidation&, // NOLINT
const AckHandle&)); // NOLINT
MOCK_METHOD3(InvalidateUnknownVersion,
void(InvalidationClient *, const ObjectId&,
const AckHandle&)); // NOLINT
MOCK_METHOD2(InvalidateAll,
void(InvalidationClient *, const AckHandle&)); // NOLINT
MOCK_METHOD3(InformRegistrationStatus,
void(InvalidationClient*, const ObjectId&, RegistrationState)); // NOLINT
MOCK_METHOD4(InformRegistrationFailure,
void(InvalidationClient*, const ObjectId&, bool, const string&));
MOCK_METHOD3(ReissueRegistrations,
void(InvalidationClient*, const string&, int));
MOCK_METHOD2(InformError,
void(InvalidationClient*, const ErrorInfo&));
};
// A base class for unit tests to share common methods and helper routines.
class UnitTestBase : public testing::Test {
public:
// Default smearing to be done to randomize delays. Zero to get
// precise delays.
static const int kDefaultSmearPercent = 0;
// The token or nonce used by default for a client in client to server or
// server to client messages.
static const char *kClientToken;
// When "waiting" at the end of a test to make sure nothing happens, how long
// to wait.
static TimeDelta EndOfTestWaitTime();
// When "waiting" at the end of a test to make sure nothing happens, how long
// to wait. This delay will not only allow to run the processing on the
// workqueue but will also give some 'extra' time to the code to do other
// (incorrect) activities if there is a bug, e.g., make an uneccessary
// listener call, etc.
static TimeDelta MessageHandlingDelay();
// Populates |object_ids| with |count| object ids in the TEST id space, each
// named oid<n>.
static void InitTestObjectIds(int count, vector<ObjectIdP>* object_ids);
// Converts object id protos in |oid_protos| to ObjecIds in |oids|.
static void ConvertFromObjectIdProtos(const vector<ObjectIdP>& oid_protos,
vector<ObjectId> *oids);
// Converts invalidation protos in |inv_protos| to Invalidations in |invs|.
static void ConvertFromInvalidationProtos(
const vector<InvalidationP>& inv_protos, vector<Invalidation> *invs);
// For each object id in |object_ids|, adds an invalidation to |invalidations|
// for that object at an arbitrary version.
static void MakeInvalidationsFromObjectIds(
const vector<ObjectIdP>& object_ids,
vector<InvalidationP>* invalidations);
// For each object in |object_ids|, makes a SUCCESSful registration status for
// that object, alternating between REGISTER and UNREGISTER. The precise
// contents of these messages are unimportant to the protocol handler; we just
// need them to pass the message validator.
static void MakeRegistrationStatusesFromObjectIds(
const vector<ObjectIdP>& object_ids, bool is_reg, bool is_success,
vector<RegistrationStatus>* registration_statuses);
// Returns the maximum smeared delay possible for |delay_ms| given the
// |Smearer|'s default smearing.
static TimeDelta GetMaxDelay(int delay_ms);
// Returns the maximum batching delay that a message will incur in the
// protocol handler.
static TimeDelta GetMaxBatchingDelay(const ProtocolHandlerConfigP& config);
// Initializes |summary| with a registration summary for 0 objects.
static void InitZeroRegistrationSummary(RegistrationSummary* summary);
// Creates a matcher for the parts of the header that the test can predict.
static Matcher<ClientHeader> ClientHeaderMatches(const ClientHeader* header);
// Initialize |reg_message| to contain registrations for all objects in |oids|
// with |is_reg| indicating whether the operation is register or unregister.
static void InitRegistrationMessage(const vector<ObjectIdP>& oids,
bool is_reg, RegistrationMessage *reg_message);
// Initializes |inv_message| to contain the invalidations |invs|.
static void InitInvalidationMessage(const vector<InvalidationP>& invs,
InvalidationMessage *inv_message);
// Initializes |error_message| to contain given the |error_code| and error
// |description|.
static void InitErrorMessage(ErrorMessage::Code error_code,
const string& description, ErrorMessage* error_message);
// Performs setup for protocol handler unit tests, e.g. creating resource
// components and setting up common expectations for certain mock objects.
virtual void SetUp();
// Tears down the test, e.g., drains any schedulers if needed.
virtual void TearDown();
// Initializes the basic system resources, using mocks for various components.
void InitSystemResources();
// Sets up some common expectations for the system resources.
void InitCommonExpectations();
// Initializes a server header with the given token (registration summary is
// picked up the internal state |reg_summary|).
void InitServerHeader(const string& token, ServerHeader* header);
// Gives a ServerToClientMessage |message| to the protocol handler and
// passes time in the internal scheduler by |delay| waiting for processing to
// be done.
void ProcessIncomingMessage(const ServerToClientMessage& message,
TimeDelta delay);
// Returns true iff the messages are equal (with lists interpreted as sets).
bool CompareMessages(
const ::google::protobuf::MessageLite& expected,
const ::google::protobuf::MessageLite& actual);
// Checks that |vec1| and |vec2| contain the same number of elements
// and each element in |vec1| is present in |vec2| and vice-versa (Uses the
// == operator for comparing). Returns true iff it is the case. Note that this
// method will return true for (aab, abb)
template <class T>
static bool CompareVectorsAsSets(const vector<T>& vec1,
const vector<T>& vec2) {
if (vec1.size() != vec2.size()) {
return false;
}
for (size_t i = 0; i < vec1.size(); i++) {
bool found = false;
for (size_t j = 0; (j < vec2.size()) && !found; j++) {
found = found || (vec1[i] == vec2[j]);
}
if (!found) {
return false;
}
}
return true;
}
//
// Internal state
//
// The time at which the test started. Initialized to an arbitrary value to
// ensure that we don't depend on it starting at 0.
Time start_time;
// Components of BasicSystemResources. It takes ownership of all of these,
// and its destructor deletes them, so we need to create fresh ones for each
// test.
// Use a deterministic scheduler for the protocol handler's internals, since
// we want precise control over when batching intervals expire.
DeterministicScheduler* internal_scheduler;
// DeterministicScheduler or MockScheduler depending on the test.
MockScheduler* listener_scheduler;
// Use a mock network to let us trap the protocol handler's message receiver
// and its attempts to send messages.
MockNetwork* network;
// A logger.
Logger* logger;
// Storage shouldn't be used by the protocol handler, so use a strict mock to
// catch any accidental calls.
MockStorage* storage;
// System resources (owned by the test).
scoped_ptr<BasicSystemResources> resources;
// Statistics object for counting occurrences of different types of events.
scoped_ptr<Statistics> statistics;
// Message callback installed by the protocol handler. Captured by the mock
// network.
MessageCallback* message_callback;
// Registration summary to be placed in messages from the client to the server
// and vice-versa.
scoped_ptr<RegistrationSummary> reg_summary;
};
// Creates an action InvokeAndDeleteClosure<k> that invokes the kth closure and
// deletes it after the Run method has been called.
ACTION_TEMPLATE(
InvokeAndDeleteClosure,
HAS_1_TEMPLATE_PARAMS(int, k),
AND_0_VALUE_PARAMS()) {
std::tr1::get<k>(args)->Run();
delete std::tr1::get<k>(args);
}
} // namespace invalidation
#endif // GOOGLE_CACHEINVALIDATION_TEST_TEST_UTILS_H_