Initial commit: Auto-generate GMOCK code for client stub.

pull/10644/head
Mahak Mukhi 8 years ago
parent 7e1e1e3a56
commit 31d92d42ff
  1. 7
      Makefile
  2. 1
      build.yaml
  3. 128
      include/grpc++/test/mock_stream.h
  4. 184
      src/compiler/cpp_generator.cc
  5. 12
      src/compiler/cpp_generator.h
  6. 10
      src/compiler/cpp_plugin.cc
  7. 142
      test/cpp/end2end/mock_test.cc

@ -409,8 +409,9 @@ AROPTS = $(GRPC_CROSS_AROPTS) # e.g., rc --target=elf32-little
USE_BUILT_PROTOC = false
endif
GTEST_LIB = -Ithird_party/googletest/googletest/include -Ithird_party/googletest/googletest third_party/googletest/googletest/src/gtest-all.cc
GTEST_LIB = -Ithird_party/googletest/googletest/include -Ithird_party/googletest/googletest third_party/googletest/googletest/src/gtest-all.cc -Ithird_party/googletest/googlemock/include -Ithird_party/googletest/googlemock third_party/googletest/googlemock/src/gmock-all.cc
GTEST_LIB += -lgflags
ifeq ($(V),1)
E = @:
Q =
@ -784,7 +785,7 @@ PROTOBUF_PKG_CONFIG = false
PC_REQUIRES_GRPCXX =
PC_LIBS_GRPCXX =
CPPFLAGS := -Ithird_party/googletest/googletest/include $(CPPFLAGS)
CPPFLAGS := -Ithird_party/googletest/googletest/include -Ithird_party/googletest/googlemock/include $(CPPFLAGS)
PROTOC_PLUGINS_ALL = $(BINDIR)/$(CONFIG)/grpc_cpp_plugin $(BINDIR)/$(CONFIG)/grpc_csharp_plugin $(BINDIR)/$(CONFIG)/grpc_node_plugin $(BINDIR)/$(CONFIG)/grpc_objective_c_plugin $(BINDIR)/$(CONFIG)/grpc_php_plugin $(BINDIR)/$(CONFIG)/grpc_python_plugin $(BINDIR)/$(CONFIG)/grpc_ruby_plugin
PROTOC_PLUGINS_DIR = $(BINDIR)/$(CONFIG)
@ -15198,7 +15199,7 @@ else
$(BINDIR)/$(CONFIG)/mock_test: $(PROTOBUF_DEP) $(MOCK_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
$(E) "[LD] Linking $@"
$(Q) mkdir -p `dirname $@`
$(Q) $(LDXX) $(LDFLAGS) $(MOCK_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/mock_test
$(Q) $(LDXX) $(LDFLAGS) $(MOCK_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/mock_test
endif

@ -949,6 +949,7 @@ filegroups:
language: c++
public_headers:
- include/grpc++/test/server_context_test_spouse.h
- include/grpc++/test/mock_stream.h
deps:
- grpc++
- name: thrift_util

@ -0,0 +1,128 @@
#ifndef NET_GRPC_PUBLIC_INCLUDE_TEST_MOCK_STREAM_H_
#define NET_GRPC_PUBLIC_INCLUDE_TEST_MOCK_STREAM_H_
#include <stdint.h>
#include <grpc++/impl/codegen/call.h>
#include <grpc++/support/async_stream.h>
#include <grpc++/support/async_unary_call.h>
#include <grpc++/support/sync_stream.h>
#include <gmock/gmock.h>
namespace grpc {
namespace testing {
template <class R>
class MockClientReader : public ClientReaderInterface<R> {
public:
MockClientReader() = default;
// ClientStreamingInterface
MOCK_METHOD0_T(Finish, Status());
// ReaderInterface
MOCK_METHOD1_T(NextMessageSize, bool(uint32_t*));
MOCK_METHOD1_T(Read, bool(R*));
// ClientReaderInterface
MOCK_METHOD0_T(WaitForInitialMetadata, void());
};
template <class W>
class MockClientWriter : public ClientWriterInterface<W> {
public:
MockClientWriter() = default;
// ClientStreamingInterface
MOCK_METHOD0_T(Finish, Status());
// WriterInterface
MOCK_METHOD2_T(Write, bool(const W&, const WriteOptions&));
// ClientWriterInterface
MOCK_METHOD0_T(WritesDone, bool());
};
template <class W, class R>
class MockClientReaderWriter : public ClientReaderWriterInterface<W, R> {
public:
MockClientReaderWriter() = default;
// ClientStreamingInterface
MOCK_METHOD0_T(Finish, Status());
// ReaderInterface
MOCK_METHOD1_T(NextMessageSize, bool(uint32_t*));
MOCK_METHOD1_T(Read, bool(R*));
// WriterInterface
MOCK_METHOD2_T(Write, bool(const W&, const WriteOptions));
// ClientReaderWriterInterface
MOCK_METHOD0_T(WaitForInitialMetadata, void());
MOCK_METHOD0_T(WritesDone, bool());
};
template <class R>
class MockClientAsyncResponseReader
: public ClientAsyncResponseReaderInterface<R> {
public:
MockClientAsyncResponseReader() = default;
MOCK_METHOD1_T(ReadInitialMetadata, void(void*));
MOCK_METHOD3_T(Finish, void(R*, Status*, void*));
};
template <class R>
class MockClientAsyncReader : public ClientAsyncReaderInterface<R> {
public:
MockClientAsyncReader() = default;
// ClientAsyncStreamingInterface
MOCK_METHOD1_T(ReadInitialMetadata, void(void*));
MOCK_METHOD2_T(Finish, void(Status*, void*));
// AsyncReaderInterface
MOCK_METHOD2_T(Read, void(R*, void*));
};
template <class W>
class MockClientAsyncWriter : public ClientAsyncWriterInterface<W> {
public:
MockClientAsyncWriter() = default;
// ClientAsyncStreamingInterface
MOCK_METHOD1_T(ReadInitialMetadata, void(void*));
MOCK_METHOD2_T(Finish, void(Status*, void*));
// AsyncWriterInterface
MOCK_METHOD2_T(Write, void(const W&, void*));
// ClientAsyncWriterInterface
MOCK_METHOD1_T(WritesDone, void(void*));
};
template <class W, class R>
class MockClientAsyncReaderWriter
: public ClientAsyncReaderWriterInterface<W, R> {
public:
MockClientAsyncReaderWriter() = default;
// ClientAsyncStreamingInterface
MOCK_METHOD1_T(ReadInitialMetadata, void(void*));
MOCK_METHOD2_T(Finish, void(Status*, void*));
// AsyncWriterInterface
MOCK_METHOD2_T(Write, void(const W&, void*));
// AsyncReaderInterface
MOCK_METHOD2_T(Read, void(R*, void*));
// ClientAsyncReaderWriterInterface
MOCK_METHOD1_T(WritesDone, void(void*));
};
} // namespace testing
} // namespace grpc
#endif // NET_GRPC_PUBLIC_INCLUDE_TEST_MOCK_STREAM_H_

@ -1407,4 +1407,188 @@ grpc::string GetSourceEpilogue(grpc_generator::File *file,
return temp;
}
// TODO(mmukhi): Make sure we need parameters or not.
grpc::string GetMockPrologue(File *file, const Parameters & ) {
grpc::string output;
{
// Scope the output stream so it closes and finalizes output to the string.
auto printer = file->CreatePrinter(&output);
std::map<grpc::string, grpc::string> vars;
vars["filename"] = file->filename();
vars["filename_base"] = file->filename_without_ext();
vars["message_header_ext"] = file->message_header_ext();
vars["service_header_ext"] = file->service_header_ext();
printer->Print(vars, "// Generated by the gRPC C++ plugin.\n");
printer->Print(vars,
"// If you make any local change, they will be lost.\n");
printer->Print(vars, "// source: $filename$\n\n");
printer->Print(vars, "#include \"$filename_base$$message_header_ext$\"\n");
printer->Print(vars, "#include \"$filename_base$$service_header_ext$\"\n");
printer->Print(vars, file->additional_headers().c_str());
printer->Print(vars, "\n");
}
return output;
}
// TODO(mmukhi): Add client-stream and completion-queue headers.
grpc::string GetMockIncludes(File *file, const Parameters &params) {
grpc::string output;
{
// Scope the output stream so it closes and finalizes output to the string.
auto printer = file->CreatePrinter(&output);
std::map<grpc::string, grpc::string> vars;
static const char *headers_strs[] = {
"grpc++/impl/codegen/async_stream.h",
"grpc++/impl/codegen/sync_stream.h",
"gmock/gmock.h",
};
std::vector<grpc::string> headers(headers_strs, array_end(headers_strs));
PrintIncludes(printer.get(), headers, params);
if (!file->package().empty()) {
std::vector<grpc::string> parts = file->package_parts();
for(auto part = parts.begin(); part != parts.end(); part++) {
vars["part"] = *part;
printer->Print(vars, "namespace $part$ {\n");
}
}
printer->Print(vars, "\n");
}
return output;
}
void PrintMockClientMethods(
Printer *printer, const Method *method,
std::map<grpc::string, grpc::string> *vars) {
(*vars)["Method"] = method->name();
(*vars)["Request"] = method->input_type_name();
(*vars)["Response"] = method->output_type_name();
if (method->NoStreaming()) {
printer->Print(
*vars,
"MOCK_METHOD3($Method$, ::grpc::Status(::grpc::ClientContext* context, "
"const $Request$& request, $Response$* response));\n");
printer->Print(
*vars,
"MOCK_METHOD3(Async$Method$Raw, "
"::grpc::ClientAsyncResponseReaderInterface< $Response$>*"
"(::grpc::ClientContext* context, const $Request$& request, "
"::grpc::CompletionQueue* cq));\n");
} else if (method->ClientOnlyStreaming()) {
printer->Print(
*vars,
"MOCK_METHOD2($Method$Raw, "
"::grpc::ClientWriterInterface< $Request$>*"
"(::grpc::ClientContext* context, $Response$* response));\n");
printer->Print(
*vars,
"MOCK_METHOD4(Async$Method$Raw, "
"::grpc::ClientAsyncWriterInterface< $Request$>*"
"(::grpc::ClientContext* context, $Response$* response, "
"::grpc::CompletionQueue* cq, void* tag));\n");
} else if (method->ServerOnlyStreaming()) {
printer->Print(
*vars,
"MOCK_METHOD2($Method$Raw, "
"::grpc::ClientReaderInterface< $Response$>*"
"(::grpc::ClientContext* context, const $Request$& request));\n");
printer->Print(
*vars,
"MOCK_METHOD4(Async$Method$Raw, "
"::grpc::ClientAsyncReaderInterface< $Response$>*"
"(::grpc::ClientContext* context, const $Request$& request, "
"::grpc::CompletionQueue* cq, void* tag));\n");
} else if (method->BidiStreaming()) {
printer->Print(
*vars,
"MOCK_METHOD1($Method$Raw, "
"::grpc::ClientReaderWriterInterface< $Request$, $Response$>*"
"(::grpc::ClientContext* context));\n");
printer->Print(
*vars,
"MOCK_METHOD3(Async$Method$Raw, "
"::grpc::ClientAsyncReaderWriterInterface<$Request$, $Response$>*"
"(::grpc::ClientContext* context, ::grpc::CompletionQueue* cq, "
"void* tag));\n");
}
}
void PrintMockService(Printer *printer,
const Service *service,
std::map<grpc::string, grpc::string> *vars) {
(*vars)["Service"] = service->name();
printer->Print(service->GetLeadingComments().c_str());
printer->Print(*vars,
"class Mock$Service$Stub : public $Service$::StubInterface {\n"
" public:\n");
printer->Indent();
printer->Print(*vars,
"Mock$Service$Stub(){}\n"
"~Mock$Service$Stub(){}\n");
for (int i = 0; i < service->method_count(); ++i) {
printer->Print(service->method(i)->GetLeadingComments().c_str());
PrintMockClientMethods(printer, service->method(i).get(), vars);
printer->Print(service->method(i)->GetTrailingComments().c_str());
}
printer->Outdent();
printer->Print("};\n");
}
grpc::string GetMockServices(File *file,
const Parameters &params) {
grpc::string output;
{
// Scope the output stream so it closes and finalizes output to the string.
auto printer = file->CreatePrinter(&output);
std::map<grpc::string, grpc::string> vars;
// Package string is empty or ends with a dot. It is used to fully qualify
// method names.
vars["Package"] = file->package();
if (!file->package().empty()) {
vars["Package"].append(".");
}
if(!params.services_namespace.empty()) {
vars["services_namespace"] = params.services_namespace;
printer->Print(vars, "\nnamespace $services_namespace$ {\n\n");
}
for (int i =0; i < file->service_count(); i++) {
PrintMockService(printer.get(), file->service(i).get(), &vars);
printer->Print("\n");
}
if (!params.services_namespace.empty()) {
printer->Print(vars, "} // namespace $services_namespace$\n\n");
}
}
return output;
}
grpc::string GetMockEpilogue(File *file, const Parameters &) {
grpc::string temp;
if (!file->package().empty()) {
std::vector<grpc::string> parts = file->package_parts();
for (auto part = parts.begin(); part != parts.end(); part++){
temp.append("} // namespace ");
temp.append(*part);
temp.append("\n");
}
temp.append("\n");
}
return temp;
}
} // namespace grpc_cpp_generator

@ -99,6 +99,18 @@ grpc::string GetSourceServices(grpc_generator::File *file,
grpc::string GetSourceEpilogue(grpc_generator::File *file,
const Parameters &params);
// Return the prologue of the generated mock file.
grpc::string GetMockPrologue(File *file, const Parameters &params);
// Return the includes needed for generated mock file.
grpc::string GetMockIncludes(File *file, const Parameters &params);
// Return the services for generated mock file.
grpc::string GetMockServices(File* file, const Parameters &params);
// Return the epilogue of generated mock file.
grpc::string GetMockEpilogue(File* file, const Parameters &params);
} // namespace grpc_cpp_generator
#endif // GRPC_INTERNAL_COMPILER_CPP_GENERATOR_H

@ -114,6 +114,16 @@ class CppGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
grpc::protobuf::io::CodedOutputStream source_coded_out(source_output.get());
source_coded_out.WriteRaw(source_code.data(), source_code.size());
grpc::string mock_code =
grpc_cpp_generator::GetMockPrologue(&pbfile, generator_parameters) +
grpc_cpp_generator::GetMockIncludes(&pbfile, generator_parameters) +
grpc_cpp_generator::GetMockServices(&pbfile, generator_parameters) +
grpc_cpp_generator::GetMockEpilogue(&pbfile, generator_parameters);
std::unique_ptr<grpc::protobuf::io::ZeroCopyOutputStream> mock_output(
context->Open(file_name + "_mock.grpc.pb.h"));
grpc::protobuf::io::CodedOutputStream mock_coded_out(mock_output.get());
mock_coded_out.WriteRaw(mock_code.data(), mock_code.size());
return true;
}

@ -1,5 +1,5 @@
/*
*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
@ -45,121 +45,37 @@
#include <grpc/support/thd.h>
#include <grpc/support/time.h>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <grpc++/test/mock_stream.h>
#include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h"
#include "src/proto/grpc/testing/echo.grpc.pb.h"
#include "src/proto/grpc/testing/echo_mock.grpc.pb.h"
#include "test/core/util/port.h"
#include "test/core/util/test_config.h"
#include<iostream>
using namespace std;
using grpc::testing::EchoRequest;
using grpc::testing::EchoResponse;
using grpc::testing::EchoTestService;
using grpc::testing::MockClientReaderWriter;
using std::chrono::system_clock;
using ::testing::AtLeast;
using ::testing::SetArgPointee;
using ::testing::SaveArg;
using ::testing::_;
using ::testing::Return;
using ::testing::Invoke;
using ::testing::WithArg;
using ::testing::DoAll;
namespace grpc {
namespace testing {
namespace {
template <class W, class R>
class MockClientReaderWriter final : public ClientReaderWriterInterface<W, R> {
public:
void WaitForInitialMetadata() override {}
bool NextMessageSize(uint32_t* sz) override {
*sz = UINT_MAX;
return true;
}
bool Read(R* msg) override { return true; }
bool Write(const W& msg) override { return true; }
bool WritesDone() override { return true; }
Status Finish() override { return Status::OK; }
};
template <>
class MockClientReaderWriter<EchoRequest, EchoResponse> final
: public ClientReaderWriterInterface<EchoRequest, EchoResponse> {
public:
MockClientReaderWriter() : writes_done_(false) {}
void WaitForInitialMetadata() override {}
bool NextMessageSize(uint32_t* sz) override {
*sz = UINT_MAX;
return true;
}
bool Read(EchoResponse* msg) override {
if (writes_done_) return false;
msg->set_message(last_message_);
return true;
}
bool Write(const EchoRequest& msg, WriteOptions options) override {
gpr_log(GPR_INFO, "mock recv msg %s", msg.message().c_str());
last_message_ = msg.message();
return true;
}
bool WritesDone() override {
writes_done_ = true;
return true;
}
Status Finish() override { return Status::OK; }
private:
bool writes_done_;
grpc::string last_message_;
};
// Mocked stub.
class MockStub : public EchoTestService::StubInterface {
public:
MockStub() {}
~MockStub() {}
Status Echo(ClientContext* context, const EchoRequest& request,
EchoResponse* response) override {
response->set_message(request.message());
return Status::OK;
}
Status Unimplemented(ClientContext* context, const EchoRequest& request,
EchoResponse* response) override {
return Status::OK;
}
private:
ClientAsyncResponseReaderInterface<EchoResponse>* AsyncEchoRaw(
ClientContext* context, const EchoRequest& request,
CompletionQueue* cq) override {
return nullptr;
}
ClientWriterInterface<EchoRequest>* RequestStreamRaw(
ClientContext* context, EchoResponse* response) override {
return nullptr;
}
ClientAsyncWriterInterface<EchoRequest>* AsyncRequestStreamRaw(
ClientContext* context, EchoResponse* response, CompletionQueue* cq,
void* tag) override {
return nullptr;
}
ClientReaderInterface<EchoResponse>* ResponseStreamRaw(
ClientContext* context, const EchoRequest& request) override {
return nullptr;
}
ClientAsyncReaderInterface<EchoResponse>* AsyncResponseStreamRaw(
ClientContext* context, const EchoRequest& request, CompletionQueue* cq,
void* tag) override {
return nullptr;
}
ClientReaderWriterInterface<EchoRequest, EchoResponse>* BidiStreamRaw(
ClientContext* context) override {
return new MockClientReaderWriter<EchoRequest, EchoResponse>();
}
ClientAsyncReaderWriterInterface<EchoRequest, EchoResponse>*
AsyncBidiStreamRaw(ClientContext* context, CompletionQueue* cq,
void* tag) override {
return nullptr;
}
ClientAsyncResponseReaderInterface<EchoResponse>* AsyncUnimplementedRaw(
ClientContext* context, const EchoRequest& request,
CompletionQueue* cq) override {
return nullptr;
}
};
class FakeClient {
public:
explicit FakeClient(EchoTestService::StubInterface* stub) : stub_(stub) {}
@ -267,16 +183,36 @@ TEST_F(MockTest, SimpleRpc) {
ResetStub();
FakeClient client(stub_.get());
client.DoEcho();
MockStub stub;
MockEchoTestServiceStub stub;
EchoResponse resp;
resp.set_message("hello world");
EXPECT_CALL(stub, Echo(_, _, _)).Times(AtLeast(1)).WillOnce(DoAll(SetArgPointee<2>(resp), Return(Status::OK)));
client.ResetStub(&stub);
client.DoEcho();
}
ACTION_P(copy, msg) {
arg0->set_message(msg->message());
}
TEST_F(MockTest, BidiStream) {
ResetStub();
FakeClient client(stub_.get());
client.DoBidiStream();
MockStub stub;
MockEchoTestServiceStub stub;
auto rw = new MockClientReaderWriter<EchoRequest, EchoResponse>();
EchoRequest msg;
EXPECT_CALL(*rw, Write(_, _)).Times(3).WillRepeatedly(DoAll(SaveArg<0>(&msg), Return(true)));
EXPECT_CALL(*rw, Read(_)).
WillOnce(DoAll(WithArg<0>(copy(&msg)), Return(true))).
WillOnce(DoAll(WithArg<0>(copy(&msg)), Return(true))).
WillOnce(DoAll(WithArg<0>(copy(&msg)), Return(true))).
WillOnce(Return(false));
EXPECT_CALL(*rw, WritesDone());
EXPECT_CALL(*rw, Finish()).WillOnce(Return(Status::OK));
EXPECT_CALL(stub, BidiStreamRaw(_)).WillOnce(Return(rw));
client.ResetStub(&stub);
client.DoBidiStream();
}

Loading…
Cancel
Save