diff --git a/test/cpp/util/cli_call.cc b/test/cpp/util/cli_call.cc index 1101abe3c9a4e875c5b6b1e9bd2670e6ea1266ef..4d045da09816dfc47bdb8d78a0b2928398a7ca9f 100644 --- a/test/cpp/util/cli_call.cc +++ b/test/cpp/util/cli_call.cc @@ -67,6 +67,8 @@ CliCall::CliCall(std::shared_ptr<grpc::Channel> channel, const grpc::string& method, const OutgoingMetadataContainer& metadata) : stub_(new grpc::GenericStub(channel)) { + gpr_mu_init(&write_mu_); + gpr_cv_init(&write_cv_); if (!metadata.empty()) { for (OutgoingMetadataContainer::const_iterator iter = metadata.begin(); iter != metadata.end(); ++iter) { @@ -80,6 +82,11 @@ CliCall::CliCall(std::shared_ptr<grpc::Channel> channel, GPR_ASSERT(ok); } +CliCall::~CliCall() { + gpr_cv_destroy(&write_cv_); + gpr_mu_destroy(&write_mu_); +} + void CliCall::Write(const grpc::string& request) { void* got_tag; bool ok; @@ -126,6 +133,81 @@ void CliCall::WritesDone() { GPR_ASSERT(ok); } +void CliCall::WriteAndWait(const grpc::string& request) { + grpc_slice s = grpc_slice_from_copied_string(request.c_str()); + grpc::Slice req_slice(s, grpc::Slice::STEAL_REF); + grpc::ByteBuffer send_buffer(&req_slice, 1); + + gpr_mu_lock(&write_mu_); + call_->Write(send_buffer, tag(2)); + write_done_ = false; + while (!write_done_) { + gpr_cv_wait(&write_cv_, &write_mu_, gpr_inf_future(GPR_CLOCK_REALTIME)); + } + gpr_mu_unlock(&write_mu_); +} + +void CliCall::WritesDoneAndWait() { + gpr_mu_lock(&write_mu_); + call_->WritesDone(tag(4)); + write_done_ = false; + while (!write_done_) { + gpr_cv_wait(&write_cv_, &write_mu_, gpr_inf_future(GPR_CLOCK_REALTIME)); + } + gpr_mu_unlock(&write_mu_); +} + +bool CliCall::ReadAndMaybeNotifyWrite( + grpc::string* response, + IncomingMetadataContainer* server_initial_metadata) { + void* got_tag; + bool ok; + grpc::ByteBuffer recv_buffer; + + call_->Read(&recv_buffer, tag(3)); + bool cq_result = cq_.Next(&got_tag, &ok); + + while (got_tag != tag(3)) { + gpr_mu_lock(&write_mu_); + write_done_ = true; + gpr_cv_signal(&write_cv_); + gpr_mu_unlock(&write_mu_); + + cq_result = cq_.Next(&got_tag, &ok); + if (got_tag == tag(2)) { + GPR_ASSERT(ok); + } + } + + if (!cq_result || !ok) { + // If the RPC is ended on the server side, we should still wait for the + // pending write on the client side to be done. + if (!ok) { + gpr_mu_lock(&write_mu_); + if (!write_done_) { + cq_.Next(&got_tag, &ok); + GPR_ASSERT(got_tag != tag(2)); + write_done_ = true; + gpr_cv_signal(&write_cv_); + } + gpr_mu_unlock(&write_mu_); + } + return false; + } + + std::vector<grpc::Slice> slices; + recv_buffer.Dump(&slices); + response->clear(); + for (size_t i = 0; i < slices.size(); i++) { + response->append(reinterpret_cast<const char*>(slices[i].begin()), + slices[i].size()); + } + if (server_initial_metadata) { + *server_initial_metadata = ctx_.GetServerInitialMetadata(); + } + return true; +} + Status CliCall::Finish(IncomingMetadataContainer* server_trailing_metadata) { void* got_tag; bool ok; diff --git a/test/cpp/util/cli_call.h b/test/cpp/util/cli_call.h index 34fa88433f6669d212d034dc8ef2db229f414ed4..91f0dbc9edc4fda061ee56b43fa0757675438db8 100644 --- a/test/cpp/util/cli_call.h +++ b/test/cpp/util/cli_call.h @@ -48,6 +48,9 @@ class ClientContext; namespace testing { +// CliCall handles the sending and receiving of generic messages given the name +// of the remote method. This class is only used by GrpcTool. Its thread-safe +// and thread-unsafe methods should not be used together. class CliCall final { public: typedef std::multimap<grpc::string, grpc::string> OutgoingMetadataContainer; @@ -56,7 +59,9 @@ class CliCall final { CliCall(std::shared_ptr<grpc::Channel> channel, const grpc::string& method, const OutgoingMetadataContainer& metadata); + ~CliCall(); + // Perform an unary generic RPC. static Status Call(std::shared_ptr<grpc::Channel> channel, const grpc::string& method, const grpc::string& request, grpc::string* response, @@ -64,13 +69,32 @@ class CliCall final { IncomingMetadataContainer* server_initial_metadata, IncomingMetadataContainer* server_trailing_metadata); + // Send a generic request message in a synchronous manner. NOT thread-safe. void Write(const grpc::string& request); + // Send a generic request message in a synchronous manner. NOT thread-safe. void WritesDone(); + // Receive a generic response message in a synchronous manner.NOT thread-safe. bool Read(grpc::string* response, IncomingMetadataContainer* server_initial_metadata); + // Thread-safe write. Must be used with ReadAndMaybeNotifyWrite. Send out a + // generic request message and wait for ReadAndMaybeNotifyWrite to finish it. + void WriteAndWait(const grpc::string& request); + + // Thread-safe WritesDone. Must be used with ReadAndMaybeNotifyWrite. Send out + // WritesDone for gereneric request messages and wait for + // ReadAndMaybeNotifyWrite to finish it. + void WritesDoneAndWait(); + + // Thread-safe Read. Blockingly receive a generic response message. Notify + // writes if they are finished when this read is waiting for a resposne. + bool ReadAndMaybeNotifyWrite( + grpc::string* response, + IncomingMetadataContainer* server_initial_metadata); + + // Finish the RPC. Status Finish(IncomingMetadataContainer* server_trailing_metadata); private: @@ -78,6 +102,9 @@ class CliCall final { grpc::ClientContext ctx_; std::unique_ptr<grpc::GenericClientAsyncReaderWriter> call_; grpc::CompletionQueue cq_; + gpr_mu write_mu_; + gpr_cv write_cv_; // Protected by write_mu_; + bool write_done_; // Portected by write_mu_; }; } // namespace testing diff --git a/test/cpp/util/grpc_cli.cc b/test/cpp/util/grpc_cli.cc index fe68ccb619a111ec1cd910fcfb4b941a3c590ad1..a78bed4b90ee9ca7bee04344b387211ee87fca3d 100644 --- a/test/cpp/util/grpc_cli.cc +++ b/test/cpp/util/grpc_cli.cc @@ -85,7 +85,7 @@ static bool SimplePrint(const grpc::string& outfile, if (outfile.empty()) { std::cout << output << std::endl; } else { - std::ofstream output_file(outfile, std::ios::trunc | std::ios::binary); + std::ofstream output_file(outfile, std::ios::app | std::ios::binary); output_file << output << std::endl; output_file.close(); } diff --git a/test/cpp/util/grpc_tool.cc b/test/cpp/util/grpc_tool.cc index 762f8e8c23d21a04b49239ae26b955f247013dea..39acd8eb4b96187be2f5837493a0b83583184c4f 100644 --- a/test/cpp/util/grpc_tool.cc +++ b/test/cpp/util/grpc_tool.cc @@ -39,6 +39,7 @@ #include <memory> #include <sstream> #include <string> +#include <thread> #include <gflags/gflags.h> #include <grpc++/channel.h> @@ -159,6 +160,36 @@ void PrintMetadata(const T& m, const grpc::string& message) { } } +void ReadResponse(CliCall* call, const grpc::string& method_name, + GrpcToolOutputCallback callback, ProtoFileParser* parser, + gpr_mu* parser_mu, bool print_mode) { + grpc::string serialized_response_proto; + std::multimap<grpc::string_ref, grpc::string_ref> server_initial_metadata; + + for (bool receive_initial_metadata = true; call->ReadAndMaybeNotifyWrite( + &serialized_response_proto, + receive_initial_metadata ? &server_initial_metadata : nullptr); + receive_initial_metadata = false) { + fprintf(stderr, "got response.\n"); + if (!FLAGS_binary_output) { + gpr_mu_lock(parser_mu); + serialized_response_proto = parser->GetTextFormatFromMethod( + method_name, serialized_response_proto, false /* is_request */); + if (parser->HasError() && print_mode) { + fprintf(stderr, "Failed to parse response.\n"); + } + gpr_mu_unlock(parser_mu); + } + if (receive_initial_metadata) { + PrintMetadata(server_initial_metadata, + "Received initial metadata from server:"); + } + if (!callback(serialized_response_proto) && print_mode) { + fprintf(stderr, "Failed to output response.\n"); + } + } +} + struct Command { const char* command; std::function<bool(GrpcTool*, int, const char**, const CliCredentials&, @@ -416,7 +447,7 @@ bool GrpcTool::CallMethod(int argc, const char** argv, grpc::string server_address(argv[0]); grpc::string method_name(argv[1]); grpc::string formatted_method_name; - std::unique_ptr<grpc::testing::ProtoFileParser> parser; + std::unique_ptr<ProtoFileParser> parser; grpc::string serialized_request_proto; bool print_mode = false; @@ -428,21 +459,17 @@ bool GrpcTool::CallMethod(int argc, const char** argv, parser.reset(new grpc::testing::ProtoFileParser(channel, FLAGS_proto_path, FLAGS_protofiles)); - grpc::string formated_method_name = - parser->GetFormatedMethodName(method_name); + if (FLAGS_binary_input) { + formatted_method_name = method_name; + } else { + formatted_method_name = parser->GetFormattedMethodName(method_name); + } if (parser->HasError()) { return false; } if (parser->IsStreaming(method_name, true /* is_request */)) { - // TODO(zyc): Support BidiStream - if (parser->IsStreaming(method_name, false /* is_request */)) { - fprintf(stderr, - "Bidirectional-streaming method is not supported."); - return false; - } - std::istream* input_stream; std::ifstream input_file; @@ -454,7 +481,7 @@ bool GrpcTool::CallMethod(int argc, const char** argv, ParseMetadataFlag(&client_metadata); PrintMetadata(client_metadata, "Sending client initial metadata:"); - CliCall call(channel, formated_method_name, client_metadata); + CliCall call(channel, formatted_method_name, client_metadata); if (FLAGS_infile.empty()) { if (isatty(STDIN_FILENO)) { @@ -467,6 +494,11 @@ bool GrpcTool::CallMethod(int argc, const char** argv, input_stream = &input_file; } + gpr_mu parser_mu; + gpr_mu_init(&parser_mu); + std::thread read_thread(ReadResponse, &call, method_name, callback, + parser.get(), &parser_mu, print_mode); + std::stringstream request_ss; grpc::string line; while (!request_text.empty() || @@ -476,6 +508,7 @@ bool GrpcTool::CallMethod(int argc, const char** argv, serialized_request_proto = request_text; request_text.clear(); } else { + gpr_mu_lock(&parser_mu); serialized_request_proto = parser->GetSerializedProtoFromMethod( method_name, request_text, true /* is_request */); request_text.clear(); @@ -483,11 +516,13 @@ bool GrpcTool::CallMethod(int argc, const char** argv, if (print_mode) { fprintf(stderr, "Failed to parse request.\n"); } + gpr_mu_unlock(&parser_mu); continue; } + gpr_mu_unlock(&parser_mu); } - call.Write(serialized_request_proto); + call.WriteAndWait(serialized_request_proto); if (print_mode) { fprintf(stderr, "Request sent.\n"); } @@ -505,35 +540,21 @@ bool GrpcTool::CallMethod(int argc, const char** argv, input_file.close(); } - call.WritesDone(); + call.WritesDoneAndWait(); + read_thread.join(); - grpc::string serialized_response_proto; - std::multimap<grpc::string_ref, grpc::string_ref> server_initial_metadata, - server_trailing_metadata; - if (!call.Read(&serialized_response_proto, &server_trailing_metadata)) { - fprintf(stderr, "Failed to read response.\n"); - } + std::multimap<grpc::string_ref, grpc::string_ref> server_trailing_metadata; Status status = call.Finish(&server_trailing_metadata); - - PrintMetadata(server_initial_metadata, - "Received initial metadata from server:"); PrintMetadata(server_trailing_metadata, "Received trailing metadata from server:"); + if (status.ok()) { fprintf(stderr, "Stream RPC succeeded with OK status\n"); - if (FLAGS_binary_output) { - output_ss << serialized_response_proto; - } else { - grpc::string response_text = parser->GetTextFormatFromMethod( - method_name, serialized_response_proto, false /* is_request */); - if (parser->HasError()) { - return false; - } - output_ss << response_text; - } + return true; } else { fprintf(stderr, "Rpc failed with status code %d, error message: %s\n", status.error_code(), status.error_message().c_str()); + return false; } } else { // parser->IsStreaming(method_name, true /* is_request */) @@ -559,7 +580,9 @@ bool GrpcTool::CallMethod(int argc, const char** argv, if (FLAGS_binary_input) { serialized_request_proto = request_text; + // formatted_method_name = method_name; } else { + // formatted_method_name = parser->GetFormattedMethodName(method_name); serialized_request_proto = parser->GetSerializedProtoFromMethod( method_name, request_text, true /* is_request */); if (parser->HasError()) { @@ -575,7 +598,7 @@ bool GrpcTool::CallMethod(int argc, const char** argv, ParseMetadataFlag(&client_metadata); PrintMetadata(client_metadata, "Sending client initial metadata:"); - CliCall call(channel, formated_method_name, client_metadata); + CliCall call(channel, formatted_method_name, client_metadata); call.Write(serialized_request_proto); call.WritesDone(); @@ -608,7 +631,7 @@ bool GrpcTool::CallMethod(int argc, const char** argv, return false; } } - return callback(output_ss.str()); + GPR_UNREACHABLE_CODE(return false); } bool GrpcTool::ParseMessage(int argc, const char** argv, diff --git a/test/cpp/util/grpc_tool_test.cc b/test/cpp/util/grpc_tool_test.cc index e2eebd40899890d78718ebbeff14654336b96fd1..26e2b1f50228996dcad8502429306d043071119b 100644 --- a/test/cpp/util/grpc_tool_test.cc +++ b/test/cpp/util/grpc_tool_test.cc @@ -142,7 +142,7 @@ class TestServiceImpl : public ::grpc::testing::EchoTestService::Service { Status RequestStream(ServerContext* context, ServerReader<EchoRequest>* reader, - EchoResponse* response) GRPC_OVERRIDE { + EchoResponse* response) override { EchoRequest request; response->set_message(""); if (!context->client_metadata().empty()) { @@ -162,7 +162,7 @@ class TestServiceImpl : public ::grpc::testing::EchoTestService::Service { } Status ResponseStream(ServerContext* context, const EchoRequest* request, - ServerWriter<EchoResponse>* writer) GRPC_OVERRIDE { + ServerWriter<EchoResponse>* writer) override { if (!context->client_metadata().empty()) { for (std::multimap<grpc::string_ref, grpc::string_ref>::const_iterator iter = context->client_metadata().begin(); @@ -181,6 +181,29 @@ class TestServiceImpl : public ::grpc::testing::EchoTestService::Service { return Status::OK; } + + Status BidiStream( + ServerContext* context, + ServerReaderWriter<EchoResponse, EchoRequest>* stream) override { + EchoRequest request; + EchoResponse response; + if (!context->client_metadata().empty()) { + for (std::multimap<grpc::string_ref, grpc::string_ref>::const_iterator + iter = context->client_metadata().begin(); + iter != context->client_metadata().end(); ++iter) { + context->AddInitialMetadata(ToString(iter->first), + ToString(iter->second)); + } + } + context->AddTrailingMetadata("trailing_key", "trailing_value"); + + while (stream->Read(&request)) { + response.set_message(request.message()); + stream->Write(response); + } + + return Status::OK; + } }; } // namespace @@ -391,48 +414,32 @@ TEST_F(GrpcToolTest, CallCommand) { ShutdownServer(); } -TEST_F(GrpcToolTest, ParseCommand) { - // Test input "grpc_cli parse localhost:<port> grpc.testing.EchoResponse - // ECHO_RESPONSE_MESSAGE" +TEST_F(GrpcToolTest, CallCommandRequestStream) { + // Test input: grpc_cli call localhost:<port> RequestStream "message: + // 'Hello0'" std::stringstream output_stream; - std::stringstream binary_output_stream; const grpc::string server_address = SetUpServer(); - const char* argv[] = {"grpc_cli", "parse", server_address.c_str(), - "grpc.testing.EchoResponse", ECHO_RESPONSE_MESSAGE}; + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), + "RequestStream", "message: 'Hello0'"}; - FLAGS_binary_input = false; - FLAGS_binary_output = false; - EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), - std::bind(PrintStream, &output_stream, - std::placeholders::_1))); - // Expected output: ECHO_RESPONSE_MESSAGE - EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), ECHO_RESPONSE_MESSAGE)); + // Mock std::cin input "message: 'Hello1'\n\n message: 'Hello2'\n\n" + std::streambuf* orig = std::cin.rdbuf(); + std::istringstream ss("message: 'Hello1'\n\n message: 'Hello2'\n\n"); + std::cin.rdbuf(ss.rdbuf()); - // Parse text message to binary message and then parse it back to text message - output_stream.str(grpc::string()); - output_stream.clear(); - FLAGS_binary_output = true; EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), std::bind(PrintStream, &output_stream, std::placeholders::_1))); - grpc::string binary_data = output_stream.str(); - output_stream.str(grpc::string()); - output_stream.clear(); - argv[4] = binary_data.c_str(); - FLAGS_binary_input = true; - FLAGS_binary_output = false; - EXPECT_TRUE(0 == GrpcToolMainLib(5, argv, TestCliCredentials(), - std::bind(PrintStream, &output_stream, - std::placeholders::_1))); - - // Expected output: ECHO_RESPONSE_MESSAGE - EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), ECHO_RESPONSE_MESSAGE)); + // Expected output: "message: \"Hello0Hello1Hello2\"" + EXPECT_TRUE(NULL != strstr(output_stream.str().c_str(), + "message: \"Hello0Hello1Hello2\"")); + std::cin.rdbuf(orig); ShutdownServer(); } -TEST_F(GrpcToolTest, CallCommandRequestStream) { +TEST_F(GrpcToolTest, CallCommandRequestStreamWithBadRequest) { // Test input: grpc_cli call localhost:<port> RequestStream "message: // 'Hello0'" std::stringstream output_stream; @@ -441,18 +448,18 @@ TEST_F(GrpcToolTest, CallCommandRequestStream) { const char* argv[] = {"grpc_cli", "call", server_address.c_str(), "RequestStream", "message: 'Hello0'"}; - // Mock std::cin input "message: 'Hello1'\n\n message: 'Hello2'\n\n" + // Mock std::cin input "bad_field: 'Hello1'\n\n message: 'Hello2'\n\n" std::streambuf* orig = std::cin.rdbuf(); - std::istringstream ss("message: 'Hello1'\n\n message: 'Hello2'\n\n"); + std::istringstream ss("bad_field: 'Hello1'\n\n message: 'Hello2'\n\n"); std::cin.rdbuf(ss.rdbuf()); EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), std::bind(PrintStream, &output_stream, std::placeholders::_1))); - // Expected output: "message: \"Hello0Hello1Hello2\"" - EXPECT_TRUE(NULL != strstr(output_stream.str().c_str(), - "message: \"Hello0Hello1Hello2\"")); + // Expected output: "message: \"Hello0Hello2\"" + EXPECT_TRUE(NULL != + strstr(output_stream.str().c_str(), "message: \"Hello0Hello2\"")); std::cin.rdbuf(orig); ShutdownServer(); } @@ -470,12 +477,10 @@ TEST_F(GrpcToolTest, CallCommandResponseStream) { std::bind(PrintStream, &output_stream, std::placeholders::_1))); - fprintf(stderr, "%s\n", output_stream.str().c_str()); // Expected output: "message: \"Hello{n}\"" - for (int i = 0; i < kNumResponseStreamsMsgs; i++) { grpc::string expected_response_text = - "message: \"Hello" + grpc::to_string(i) + "\"\n\n"; + "message: \"Hello" + grpc::to_string(i) + "\"\n"; EXPECT_TRUE(NULL != strstr(output_stream.str().c_str(), expected_response_text.c_str())); } @@ -483,6 +488,99 @@ TEST_F(GrpcToolTest, CallCommandResponseStream) { ShutdownServer(); } +TEST_F(GrpcToolTest, CallCommandBidiStream) { + // Test input: grpc_cli call localhost:<port> BidiStream "message: 'Hello0'" + std::stringstream output_stream; + + const grpc::string server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), + "BidiStream", "message: 'Hello0'"}; + + // Mock std::cin input "message: 'Hello1'\n\n message: 'Hello2'\n\n" + std::streambuf* orig = std::cin.rdbuf(); + std::istringstream ss("message: 'Hello1'\n\n message: 'Hello2'\n\n"); + std::cin.rdbuf(ss.rdbuf()); + + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + + // Expected output: "message: \"Hello0\"\nmessage: \"Hello1\"\nmessage: + // \"Hello2\"\n\n" + EXPECT_TRUE(NULL != strstr(output_stream.str().c_str(), + "message: \"Hello0\"\nmessage: " + "\"Hello1\"\nmessage: \"Hello2\"\n")); + std::cin.rdbuf(orig); + ShutdownServer(); +} + +TEST_F(GrpcToolTest, CallCommandBidiStreamWithBadRequest) { + // Test input: grpc_cli call localhost:<port> BidiStream "message: 'Hello0'" + std::stringstream output_stream; + + const grpc::string server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), + "BidiStream", "message: 'Hello0'"}; + + // Mock std::cin input "message: 'Hello1'\n\n message: 'Hello2'\n\n" + std::streambuf* orig = std::cin.rdbuf(); + std::istringstream ss("message: 1.0\n\n message: 'Hello2'\n\n"); + std::cin.rdbuf(ss.rdbuf()); + + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + + // Expected output: "message: \"Hello0\"\nmessage: \"Hello1\"\nmessage: + // \"Hello2\"\n\n" + EXPECT_TRUE(NULL != strstr(output_stream.str().c_str(), + "message: \"Hello0\"\nmessage: \"Hello2\"\n")); + std::cin.rdbuf(orig); + + ShutdownServer(); +} + +TEST_F(GrpcToolTest, ParseCommand) { + // Test input "grpc_cli parse localhost:<port> grpc.testing.EchoResponse + // ECHO_RESPONSE_MESSAGE" + std::stringstream output_stream; + std::stringstream binary_output_stream; + + const grpc::string server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "parse", server_address.c_str(), + "grpc.testing.EchoResponse", ECHO_RESPONSE_MESSAGE}; + + FLAGS_binary_input = false; + FLAGS_binary_output = false; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + // Expected output: ECHO_RESPONSE_MESSAGE + EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), ECHO_RESPONSE_MESSAGE)); + + // Parse text message to binary message and then parse it back to text message + output_stream.str(grpc::string()); + output_stream.clear(); + FLAGS_binary_output = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + grpc::string binary_data = output_stream.str(); + output_stream.str(grpc::string()); + output_stream.clear(); + argv[4] = binary_data.c_str(); + FLAGS_binary_input = true; + FLAGS_binary_output = false; + EXPECT_TRUE(0 == GrpcToolMainLib(5, argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + + // Expected output: ECHO_RESPONSE_MESSAGE + EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), ECHO_RESPONSE_MESSAGE)); + + ShutdownServer(); +} + TEST_F(GrpcToolTest, TooFewArguments) { // Test input "grpc_cli call Echo" std::stringstream output_stream; diff --git a/test/cpp/util/proto_file_parser.cc b/test/cpp/util/proto_file_parser.cc index 9f1f05595ecabcd26864060a716996ca2893b806..d501c3697b27197930da81076a7d705b82712bb6 100644 --- a/test/cpp/util/proto_file_parser.cc +++ b/test/cpp/util/proto_file_parser.cc @@ -81,8 +81,9 @@ class ErrorPrinter : public protobuf::compiler::MultiFileErrorCollector { ProtoFileParser::ProtoFileParser(std::shared_ptr<grpc::Channel> channel, const grpc::string& proto_path, const grpc::string& protofiles) - : has_error_(false) { - std::vector<grpc::string> service_list; + : has_error_(false), + dynamic_factory_(new protobuf::DynamicMessageFactory()) { + std::vector<std::string> service_list; if (channel) { reflection_db_.reset(new grpc::ProtoReflectionDescriptorDatabase(channel)); reflection_db_->GetServices(&service_list); @@ -127,7 +128,6 @@ ProtoFileParser::ProtoFileParser(std::shared_ptr<grpc::Channel> channel, } desc_pool_.reset(new protobuf::DescriptorPool(desc_db_.get())); - dynamic_factory_.reset(new protobuf::DynamicMessageFactory(desc_pool_.get())); for (auto it = service_list.begin(); it != service_list.end(); it++) { if (known_services.find(*it) == known_services.end()) {