blob: c57c97b7669fc9b08b18385917e2a3d0b95ae0f3 [file] [log] [blame]
// Copyright 2016 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.
//
// Author: cheesy@google.com (Steve Hill)
#ifndef PAGESPEED_CONTROLLER_CONTROLLER_GRPC_MOCKS_H_
#define PAGESPEED_CONTROLLER_CONTROLLER_GRPC_MOCKS_H_
#include "pagespeed/controller/controller.grpc.pb.h"
#include "pagespeed/controller/controller.pb.h"
#include "pagespeed/kernel/base/function.h"
#include "pagespeed/kernel/base/gmock.h"
#include "pagespeed/kernel/base/proto_util.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/thread/sequence.h"
#include "pagespeed/kernel/thread/worker_test_base.h"
#include "pagespeed/kernel/util/grpc.h"
// Various mocks and related classes that are useful for testing client-side
// CentralController gRPC stuff.
using testing::_;
using testing::Eq;
using testing::Invoke;
using testing::WithArgs;
using testing::Return;
using testing::SetArgPointee;
namespace net_instaweb {
// Function wrapper that Notify()s a SyncPoint after running another contained
// Function.
class NotifyFunction : public Function {
public:
NotifyFunction(Function* func, WorkerTestBase::SyncPoint* sync)
: func_(func), sync_(sync) {}
void Run() override {
func_->CallRun();
sync_->Notify();
}
void Cancel() override {
func_->CallCancel();
sync_->Notify();
}
private:
Function* func_;
WorkerTestBase::SyncPoint* sync_;
};
// Mock for grpc::ClientAsyncReaderWriterInterface.
// All callbacks are invoked asynchronously through a Sequence to mimic the
// equivalent gRPC behaviour which uses a gRPC CompletionQueue.
template <typename RequestT, typename ResponseT>
class MockReaderWriterT
: public ::grpc::ClientAsyncReaderWriterInterface<RequestT, ResponseT> {
public:
MockReaderWriterT(Sequence* sequence) : sequence_(sequence) {
EXPECT_CALL(*this, WritesDone(_)).Times(0);
EXPECT_CALL(*this, ReadInitialMetadata(_)).Times(0);
EXPECT_CALL(*this, Finish(_, _)).Times(0);
EXPECT_CALL(*this, Write(_, _)).Times(0);
EXPECT_CALL(*this, Write(_, _, _)).Times(0);
EXPECT_CALL(*this, Read(_, _)).Times(0);
}
void ExpectRead(const GoogleString& asciiProto) {
ResponseT resp;
ASSERT_THAT(ParseTextFormatProtoFromString(asciiProto, &resp), Eq(true));
EXPECT_CALL(*this, Read(_, _))
.WillOnce(DoAll(
SetArgPointee<0>(resp),
WithArgs<1>(Invoke(this, &MockReaderWriterT::QueueVoidFunction))));
}
void ExpectReadFailure() {
EXPECT_CALL(*this, Read(_, _))
.WillOnce(WithArgs<1>(
Invoke(this, &MockReaderWriterT::QueueVoidFunctionForCancel)));
}
// Matcher winds up being a complicated type that includes testing::internal
// stuff, so we just template on it.
template <typename Matcher>
void ExpectWrite(const Matcher& matcher) {
EXPECT_CALL(*this, Write(matcher, _))
.WillOnce(
WithArgs<1>(Invoke(this, &MockReaderWriterT::QueueVoidFunction)));
}
template <typename Matcher>
void ExpectWriteFailure(const Matcher& matcher) {
EXPECT_CALL(*this, Write(matcher, _))
.WillOnce(WithArgs<1>(
Invoke(this, &MockReaderWriterT::QueueVoidFunctionForCancel)));
}
void ExpectFinish(const ::grpc::Status& status) {
EXPECT_CALL(*this, Finish(_, _))
.WillOnce(DoAll(
SetArgPointee<0>(status),
WithArgs<1>(Invoke(this, &MockReaderWriterT::QueueVoidFunction))));
}
void ExpectFinishAndNotify(const ::grpc::Status& status,
WorkerTestBase::SyncPoint* sync) {
EXPECT_CALL(*this, Finish(_, _))
.WillOnce(DoAll(SetArgPointee<0>(status),
WithArgs<1>(Invoke([this, sync](void* f) {
this->QueueVoidFunctionWithNotify(f, sync);
}))));
}
void ExpectFinishFailure() {
EXPECT_CALL(*this, Finish(_, _))
.WillOnce(WithArgs<1>(
Invoke(this, &MockReaderWriterT::QueueVoidFunctionForCancel)));
}
MOCK_METHOD1(WritesDone, void(void* tag));
MOCK_METHOD1(ReadInitialMetadata, void(void* tag));
MOCK_METHOD2(Finish, void(::grpc::Status* status, void* tag));
MOCK_METHOD2_T(Write, void(const RequestT& resp, void* tag));
MOCK_METHOD3_T(Write, void(const RequestT& resp, ::grpc::WriteOptions options, void* tag));
MOCK_METHOD2_T(Read, void(ResponseT* resp, void* tag));
private:
void QueueVoidFunction(void* fv) {
Function* f = static_cast<Function*>(fv);
sequence_->Add(f);
}
void QueueVoidFunctionForCancel(void* fv) {
Function* f = static_cast<Function*>(fv);
sequence_->Add(MakeFunction(f, &Function::CallCancel));
}
void QueueVoidFunctionWithNotify(void* fv, WorkerTestBase::SyncPoint* sync) {
Function* f = static_cast<Function*>(fv);
sequence_->Add(new NotifyFunction(f, sync));
}
Sequence* sequence_;
};
// Mock for CentralControllerRpcServiceStub. Mostly used just to bootstrap
// a MockReaderWriterT, this also features deferred execution to mimic gRPC.
class MockCentralControllerRpcServiceStub
: public grpc::CentralControllerRpcService::StubInterface {
public:
MockCentralControllerRpcServiceStub(Sequence* sequence)
: sequence_(sequence) {
EXPECT_CALL(*this, ScheduleExpensiveOperationRaw(_)).Times(0);
EXPECT_CALL(*this, ScheduleRewriteRaw(_)).Times(0);
EXPECT_CALL(*this, AsyncScheduleExpensiveOperationRaw(_, _, _)).Times(0);
EXPECT_CALL(*this, AsyncScheduleRewriteRaw(_, _, _)).Times(0);
}
MOCK_METHOD1(
ScheduleExpensiveOperationRaw,
::grpc::ClientReaderWriterInterface<
::net_instaweb::ScheduleExpensiveOperationRequest,
::net_instaweb::
ScheduleExpensiveOperationResponse>*(::grpc::ClientContext*));
MOCK_METHOD1(
ScheduleRewriteRaw,
::grpc::ClientReaderWriterInterface<
::net_instaweb::ScheduleRewriteRequest,
::net_instaweb::ScheduleRewriteResponse>*(::grpc::ClientContext*));
MOCK_METHOD3(
AsyncScheduleExpensiveOperationRaw,
::grpc::ClientAsyncReaderWriterInterface<
::net_instaweb::ScheduleExpensiveOperationRequest,
net_instaweb::
ScheduleExpensiveOperationResponse>*(::grpc::ClientContext*,
::grpc::CompletionQueue*,
void*));
MOCK_METHOD3(
AsyncScheduleRewriteRaw,
::grpc::ClientAsyncReaderWriterInterface<
::net_instaweb::ScheduleRewriteRequest,
::net_instaweb::ScheduleRewriteResponse>*(::grpc::ClientContext*,
::grpc::CompletionQueue*,
void*));
void ExpectAsyncScheduleExpensiveOperation(
::grpc::ClientAsyncReaderWriterInterface<
::net_instaweb::ScheduleExpensiveOperationRequest,
::net_instaweb::ScheduleExpensiveOperationResponse>* rw) {
// Configure the stub to invoke the callback and return rw in response to
// a client initiating a request.
EXPECT_CALL(*this,
AsyncScheduleExpensiveOperationRaw(_, nullptr /* queue */, _))
.WillOnce(DoAll(WithArgs<2>(Invoke([this](void* fv) {
sequence_->Add(static_cast<Function*>(fv));
})),
Return(rw)));
}
void ExpectAsyncScheduleExpensiveOperationFailure(
::grpc::ClientAsyncReaderWriterInterface<
::net_instaweb::ScheduleExpensiveOperationRequest,
::net_instaweb::ScheduleExpensiveOperationResponse>* rw) {
// Configure the stub to invoke the Cancel callback and return rw in
// response to a client initiating a request.
EXPECT_CALL(*this,
AsyncScheduleExpensiveOperationRaw(_, nullptr /* queue */, _))
.WillOnce(DoAll(WithArgs<2>(Invoke([this](void* fv) {
sequence_->Add(
MakeFunction(static_cast<Function*>(fv),
&Function::CallCancel));
})),
Return(rw)));
}
void ExpectAsyncScheduleRewrite(
::grpc::ClientAsyncReaderWriterInterface<
::net_instaweb::ScheduleRewriteRequest,
::net_instaweb::ScheduleRewriteResponse>* rw) {
// Configure the stub to invoke the callback and return rw in response to
// a client initiating a request.
EXPECT_CALL(*this,
AsyncScheduleRewriteRaw(_, nullptr /* queue */, _))
.WillOnce(DoAll(WithArgs<2>(Invoke([this](void* fv) {
sequence_->Add(static_cast<Function*>(fv));
})),
Return(rw)));
}
void ExpectAsyncScheduleRewriteFailure(
::grpc::ClientAsyncReaderWriterInterface<
::net_instaweb::ScheduleRewriteRequest,
::net_instaweb::ScheduleRewriteResponse>* rw) {
// Configure the stub to invoke the Cancel callback and return rw in
// response to a client initiating a request.
EXPECT_CALL(*this,
AsyncScheduleRewriteRaw(_, nullptr /* queue */, _))
.WillOnce(DoAll(WithArgs<2>(Invoke([this](void* fv) {
sequence_->Add(
MakeFunction(static_cast<Function*>(fv),
&Function::CallCancel));
})),
Return(rw)));
}
private:
Sequence* sequence_;
};
} // namespace net_instaweb
#endif // PAGESPEED_CONTROLLER_CONTROLLER_GRPC_MOCKS_H_