blob: 27c602c8a3e60a912aad80fa057ef412b87910be [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 and a
// mock storage layer.
#include <set>
#include "google/cacheinvalidation/impl/proto-converter.h"
#include "google/cacheinvalidation/test/test-utils.h"
#include "google/cacheinvalidation/test/test-logger.h"
#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
namespace invalidation {
using ::google::protobuf::io::StringOutputStream;
using ::testing::DeleteArg;
using ::testing::StrictMock;
// Creates an Action InvokeNetworkStatusCallback<k>() that calls the Run method
// on the kth argument with value |true|.
ACTION_TEMPLATE(
InvokeNetworkStatusCallback,
HAS_1_TEMPLATE_PARAMS(int, k),
AND_0_VALUE_PARAMS()) {
std::tr1::get<k>(args)->Run(true);
}
const char* UnitTestBase::kClientToken = "Dummy";
void UnitTestBase::SetUp() {
// Start time at an arbitrary point, just to make sure we don't depend on it
// being 0.
start_time = Time() + TimeDelta::FromDays(9);
statistics.reset(new Statistics());
reg_summary.reset(new RegistrationSummary());
InitZeroRegistrationSummary(reg_summary.get());
InitSystemResources(); // Set up system resources
message_callback = NULL;
// Start the scheduler and resources.
internal_scheduler->StartScheduler();
resources->Start();
}
void UnitTestBase::TearDown() {
if (message_callback != NULL) {
delete message_callback;
message_callback = NULL;
}
}
void UnitTestBase::InitSystemResources() {
logger = new TestLogger();
// Use a deterministic scheduler for the protocol handler's internals, since
// we want precise control over when batching intervals expire.
internal_scheduler = new DeterministicScheduler(logger);
internal_scheduler->SetInitialTime(start_time);
// Use a mock network to let us trap the protocol handler's message receiver
// and its attempts to send messages.
network = new StrictMock<MockNetwork>();
listener_scheduler = new StrictMock<MockScheduler>();
// Storage shouldn't be used by the protocol handler, so use a strict mock
// to catch any accidental calls.
storage = new StrictMock<MockStorage>();
// The BasicSystemResources will set itself in the components.
EXPECT_CALL(*network, SetSystemResources(_));
EXPECT_CALL(*storage, SetSystemResources(_));
EXPECT_CALL(*listener_scheduler, SetSystemResources(_));
// Create the actual resources.
resources.reset(new BasicSystemResources(
logger, internal_scheduler, listener_scheduler, network, storage,
"unit-test"));
}
void UnitTestBase::InitCommonExpectations() {
// When we construct the protocol handler, it will set a message receiver on
// the network. Intercept the call and save the callback.
EXPECT_CALL(*network, SetMessageReceiver(_))
.WillOnce(SaveArg<0>(&message_callback));
// It will also add a network status receiver. The network channel takes
// ownership. Invoke it once with |true| just to exercise that code path,
// then delete it since we won't need it anymore.
EXPECT_CALL(*network, AddNetworkStatusReceiver(_))
.WillOnce(DoAll(InvokeNetworkStatusCallback<0>(), DeleteArg<0>()));
}
void UnitTestBase::InitRegistrationMessage(const vector<ObjectIdP>& oids,
bool is_reg, RegistrationMessage *reg_message) {
RegistrationP::OpType op_type = is_reg ?
RegistrationP::REGISTER : RegistrationP::UNREGISTER;
for (size_t i = 0; i < oids.size(); i++) {
ProtoHelpers::InitRegistrationP(
oids[i], op_type, reg_message->add_registration());
}
}
void UnitTestBase::InitErrorMessage(ErrorMessage::Code error_code,
const string& description, ErrorMessage* error_message) {
error_message->set_code(error_code);
error_message->set_description(description);
}
void UnitTestBase::InitInvalidationMessage(const vector<InvalidationP>& invs,
InvalidationMessage *inv_message) {
for (size_t i = 0; i < invs.size(); ++i) {
inv_message->add_invalidation()->CopyFrom(invs[i]);
}
}
TimeDelta UnitTestBase::EndOfTestWaitTime() {
return TimeDelta::FromSeconds(50);
}
TimeDelta UnitTestBase::MessageHandlingDelay() {
return TimeDelta::FromMilliseconds(50);
}
void UnitTestBase::InitTestObjectIds(int count, vector<ObjectIdP>* object_ids) {
for (int i = 0; i < count; ++i) {
ObjectIdP object_id;
object_id.set_source(ObjectSource_Type_TEST);
object_id.set_name(StringPrintf("oid%d", i));
object_ids->push_back(object_id);
}
}
void UnitTestBase::ConvertFromObjectIdProtos(
const vector<ObjectIdP>& oid_protos, vector<ObjectId> *oids) {
for (size_t i = 0; i < oid_protos.size(); ++i) {
ObjectId object_id;
ProtoConverter::ConvertFromObjectIdProto(oid_protos[i], &object_id);
oids->push_back(object_id);
}
}
void UnitTestBase::ConvertFromInvalidationProtos(
const vector<InvalidationP>& inv_protos, vector<Invalidation> *invs) {
for (size_t i = 0; i < inv_protos.size(); ++i) {
Invalidation inv;
ProtoConverter::ConvertFromInvalidationProto(inv_protos[i], &inv);
invs->push_back(inv);
}
}
void UnitTestBase::MakeInvalidationsFromObjectIds(
const vector<ObjectIdP>& object_ids,
vector<InvalidationP>* invalidations) {
for (size_t i = 0; i < object_ids.size(); ++i) {
InvalidationP invalidation;
invalidation.mutable_object_id()->CopyFrom(object_ids[i]);
invalidation.set_is_known_version(true);
invalidation.set_is_trickle_restart(true);
// Pick an arbitrary version number; it shouldn't really matter, but we
// try not to make them correlated too much with the object name.
invalidation.set_version(100 + ((i * 19) % 31));
invalidations->push_back(invalidation);
}
}
void UnitTestBase::MakeRegistrationStatusesFromObjectIds(
const vector<ObjectIdP>& object_ids, bool is_reg, bool is_success,
vector<RegistrationStatus>* registration_statuses) {
for (size_t i = 0; i < object_ids.size(); ++i) {
RegistrationStatus registration_status;
registration_status.mutable_registration()->mutable_object_id()->CopyFrom(
object_ids[i]);
registration_status.mutable_registration()->set_op_type(
is_reg ? RegistrationP::REGISTER : RegistrationP::UNREGISTER);
registration_status.mutable_status()->set_code(
is_success ? StatusP::SUCCESS : StatusP::TRANSIENT_FAILURE);
registration_statuses->push_back(registration_status);
}
}
TimeDelta UnitTestBase::GetMaxDelay(int delay_ms) {
int64 extra_delay_ms = (delay_ms * kDefaultSmearPercent) / 100.0;
return TimeDelta::FromMilliseconds(extra_delay_ms + delay_ms);
}
TimeDelta UnitTestBase::GetMaxBatchingDelay(
const ProtocolHandlerConfigP& config) {
return GetMaxDelay(config.batching_delay_ms());
}
void UnitTestBase::InitZeroRegistrationSummary(RegistrationSummary* summary) {
summary->set_num_registrations(0);
// "\3329..." can trigger MSVC to warn about "octal escape sequence terminated
// by decimal number", so break the string between the two to avoid the
// warning.
string zero_digest(
"\332" "9\243\356^kK\r2U\277\357\225`\030\220\257\330\007\t");
summary->set_registration_digest(zero_digest);
}
void UnitTestBase::InitServerHeader(const string& token, ServerHeader* header) {
ProtoHelpers::InitProtocolVersion(header->mutable_protocol_version());
header->set_client_token(token);
if (reg_summary.get() != NULL) {
header->mutable_registration_summary()->CopyFrom(*reg_summary.get());
}
// Use arbitrary server time and message id, since they don't matter.
header->set_server_time_ms(314159265);
header->set_message_id("message-id-for-test");
}
bool UnitTestBase::CompareMessages(
const ::google::protobuf::MessageLite& expected,
const ::google::protobuf::MessageLite& actual) {
// TODO: Fill in proper implementation.
return true;
}
void UnitTestBase::ProcessIncomingMessage(const ServerToClientMessage& message,
TimeDelta delay) {
string serialized;
message.SerializeToString(&serialized);
message_callback->Run(serialized);
internal_scheduler->PassTime(delay);
}
Matcher<ClientHeader> UnitTestBase::ClientHeaderMatches(
const ClientHeader* header) {
return AllOf(Property(&ClientHeader::protocol_version,
EqualsProto(header->protocol_version())),
Property(&ClientHeader::registration_summary,
EqualsProto(header->registration_summary())),
Property(&ClientHeader::max_known_server_time_ms,
header->max_known_server_time_ms()),
Property(&ClientHeader::message_id,
header->message_id()));
}
} // namespace invalidation