diff options
author | Yang Gao <yangg@google.com> | 2018-10-16 09:12:58 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-10-16 09:12:58 -0700 |
commit | 191766c0f21dc43d0928bb5ab70f73df54ba5701 (patch) | |
tree | cb522602b884ed34eae5db29049a91fec494e881 /test/cpp/util | |
parent | cf408bb222b4f310773582a93015c76e07101b7b (diff) | |
parent | 00bb2a035ee11a2821c4749c814747454a5824c6 (diff) |
Merge pull request #16766 from gfecher/master
grpc_cli json support
Diffstat (limited to 'test/cpp/util')
-rw-r--r-- | test/cpp/util/config_grpc_cli.h | 7 | ||||
-rw-r--r-- | test/cpp/util/grpc_tool.cc | 67 | ||||
-rw-r--r-- | test/cpp/util/grpc_tool_test.cc | 397 | ||||
-rw-r--r-- | test/cpp/util/proto_file_parser.cc | 67 | ||||
-rw-r--r-- | test/cpp/util/proto_file_parser.h | 52 |
5 files changed, 533 insertions, 57 deletions
diff --git a/test/cpp/util/config_grpc_cli.h b/test/cpp/util/config_grpc_cli.h index 358884196d..1df7b36e2e 100644 --- a/test/cpp/util/config_grpc_cli.h +++ b/test/cpp/util/config_grpc_cli.h @@ -40,6 +40,11 @@ #define GRPC_CUSTOM_TEXTFORMAT ::google::protobuf::TextFormat #endif +#ifndef GRPC_CUSTOM_JSONUTIL +#include <google/protobuf/util/json_util.h> +#define GRPC_CUSTOM_JSONUTIL ::google::protobuf::util +#endif + #ifndef GRPC_CUSTOM_DISKSOURCETREE #include <google/protobuf/compiler/importer.h> #define GRPC_CUSTOM_DISKSOURCETREE ::google::protobuf::compiler::DiskSourceTree @@ -58,6 +63,8 @@ typedef GRPC_CUSTOM_MERGEDDESCRIPTORDATABASE MergedDescriptorDatabase; typedef GRPC_CUSTOM_TEXTFORMAT TextFormat; +namespace json = GRPC_CUSTOM_JSONUTIL; + namespace compiler { typedef GRPC_CUSTOM_DISKSOURCETREE DiskSourceTree; typedef GRPC_CUSTOM_IMPORTER Importer; diff --git a/test/cpp/util/grpc_tool.cc b/test/cpp/util/grpc_tool.cc index ccc60cca27..80eaf4f727 100644 --- a/test/cpp/util/grpc_tool.cc +++ b/test/cpp/util/grpc_tool.cc @@ -57,6 +57,8 @@ DEFINE_string(proto_path, ".", "Path to look for the proto file."); DEFINE_string(protofiles, "", "Name of the proto file."); DEFINE_bool(binary_input, false, "Input in binary format"); DEFINE_bool(binary_output, false, "Output in binary format"); +DEFINE_bool(json_input, false, "Input in json format"); +DEFINE_bool(json_output, false, "Output in json format"); DEFINE_string(infile, "", "Input file (default is stdin)"); DEFINE_bool(batch, false, "Input contains multiple requests. Please do not use this to send " @@ -88,6 +90,8 @@ class GrpcTool { GrpcToolOutputCallback callback); bool ToText(int argc, const char** argv, const CliCredentials& cred, GrpcToolOutputCallback callback); + bool ToJson(int argc, const char** argv, const CliCredentials& cred, + GrpcToolOutputCallback callback); bool ToBinary(int argc, const char** argv, const CliCredentials& cred, GrpcToolOutputCallback callback); @@ -189,8 +193,9 @@ void ReadResponse(CliCall* call, const grpc::string& method_name, 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 */); + serialized_response_proto = parser->GetFormattedStringFromMethod( + method_name, serialized_response_proto, false /* is_request */, + FLAGS_json_output); if (parser->HasError() && print_mode) { fprintf(stderr, "Failed to parse response.\n"); } @@ -233,6 +238,7 @@ const Command ops[] = { {"parse", BindWith5Args(&GrpcTool::ParseMessage), 2, 3}, {"totext", BindWith5Args(&GrpcTool::ToText), 2, 3}, {"tobinary", BindWith5Args(&GrpcTool::ToBinary), 2, 3}, + {"tojson", BindWith5Args(&GrpcTool::ToJson), 2, 3}, }; void Usage(const grpc::string& msg) { @@ -244,6 +250,7 @@ void Usage(const grpc::string& msg) { " grpc_cli type ... ; Print type\n" " grpc_cli parse ... ; Parse message\n" " grpc_cli totext ... ; Convert binary message to text\n" + " grpc_cli tojson ... ; Convert binary message to json\n" " grpc_cli tobinary ... ; Convert text message to binary\n" " grpc_cli help ... ; Print this message, or per-command usage\n" "\n", @@ -465,7 +472,9 @@ bool GrpcTool::CallMethod(int argc, const char** argv, " --infile ; Input filename (defaults to stdin)\n" " --outfile ; Output filename (defaults to stdout)\n" " --binary_input ; Input in binary format\n" - " --binary_output ; Output in binary format\n" + + " --binary_output ; Output in binary format\n" + " --json_input ; Input in json format\n" + " --json_output ; Output in json format\n" + cred.GetCredentialUsage()); std::stringstream output_ss; @@ -548,7 +557,8 @@ bool GrpcTool::CallMethod(int argc, const char** argv, } else { gpr_mu_lock(&parser_mu); serialized_request_proto = parser->GetSerializedProtoFromMethod( - method_name, request_text, true /* is_request */); + method_name, request_text, true /* is_request */, + FLAGS_json_input); request_text.clear(); if (parser->HasError()) { if (print_mode) { @@ -632,7 +642,8 @@ bool GrpcTool::CallMethod(int argc, const char** argv, request_text.clear(); } else { serialized_request_proto = parser->GetSerializedProtoFromMethod( - method_name, request_text, true /* is_request */); + method_name, request_text, true /* is_request */, + FLAGS_json_input); request_text.clear(); if (parser->HasError()) { if (print_mode) { @@ -668,9 +679,10 @@ bool GrpcTool::CallMethod(int argc, const char** argv, break; } } else { - grpc::string response_text = parser->GetTextFormatFromMethod( + grpc::string response_text = parser->GetFormattedStringFromMethod( method_name, serialized_response_proto, - false /* is_request */); + false /* is_request */, FLAGS_json_output); + if (parser->HasError() && print_mode) { fprintf(stderr, "Failed to parse response.\n"); } else { @@ -727,7 +739,7 @@ bool GrpcTool::CallMethod(int argc, const char** argv, serialized_request_proto = request_text; } else { serialized_request_proto = parser->GetSerializedProtoFromMethod( - method_name, request_text, true /* is_request */); + method_name, request_text, true /* is_request */, FLAGS_json_input); if (parser->HasError()) { fprintf(stderr, "Failed to parse request.\n"); return false; @@ -751,13 +763,15 @@ bool GrpcTool::CallMethod(int argc, const char** argv, receive_initial_metadata ? &server_initial_metadata : nullptr); receive_initial_metadata = false) { if (!FLAGS_binary_output) { - serialized_response_proto = parser->GetTextFormatFromMethod( - method_name, serialized_response_proto, false /* is_request */); + serialized_response_proto = parser->GetFormattedStringFromMethod( + method_name, serialized_response_proto, false /* is_request */, + FLAGS_json_output); if (parser->HasError()) { fprintf(stderr, "Failed to parse response.\n"); return false; } } + if (receive_initial_metadata) { PrintMetadata(server_initial_metadata, "Received initial metadata from server:"); @@ -797,7 +811,9 @@ bool GrpcTool::ParseMessage(int argc, const char** argv, " --infile ; Input filename (defaults to stdin)\n" " --outfile ; Output filename (defaults to stdout)\n" " --binary_input ; Input in binary format\n" - " --binary_output ; Output in binary format\n" + + " --binary_output ; Output in binary format\n" + " --json_input ; Input in json format\n" + " --json_output ; Output in json format\n" + cred.GetCredentialUsage()); std::stringstream output_ss; @@ -844,8 +860,8 @@ bool GrpcTool::ParseMessage(int argc, const char** argv, if (FLAGS_binary_input) { serialized_request_proto = message_text; } else { - serialized_request_proto = - parser->GetSerializedProtoFromMessageType(type_name, message_text); + serialized_request_proto = parser->GetSerializedProtoFromMessageType( + type_name, message_text, FLAGS_json_input); if (parser->HasError()) { fprintf(stderr, "Failed to serialize the message.\n"); return false; @@ -855,12 +871,14 @@ bool GrpcTool::ParseMessage(int argc, const char** argv, if (FLAGS_binary_output) { output_ss << serialized_request_proto; } else { - grpc::string output_text = parser->GetTextFormatFromMessageType( - type_name, serialized_request_proto); + grpc::string output_text; + output_text = parser->GetFormattedStringFromMessageType( + type_name, serialized_request_proto, FLAGS_json_output); if (parser->HasError()) { fprintf(stderr, "Failed to deserialize the message.\n"); return false; } + output_ss << output_text << std::endl; } @@ -885,6 +903,25 @@ bool GrpcTool::ToText(int argc, const char** argv, const CliCredentials& cred, return ParseMessage(argc, argv, cred, callback); } +bool GrpcTool::ToJson(int argc, const char** argv, const CliCredentials& cred, + GrpcToolOutputCallback callback) { + CommandUsage( + "Convert binary message to json\n" + " grpc_cli tojson <protofiles> <type>\n" + " <protofiles> ; Comma separated list of proto files\n" + " <type> ; Protocol buffer type name\n" + " --proto_path ; The search path of proto files\n" + " --infile ; Input filename (defaults to stdin)\n" + " --outfile ; Output filename (defaults to stdout)\n"); + + FLAGS_protofiles = argv[0]; + FLAGS_remotedb = false; + FLAGS_binary_input = true; + FLAGS_binary_output = false; + FLAGS_json_output = true; + return ParseMessage(argc, argv, cred, callback); +} + bool GrpcTool::ToBinary(int argc, const char** argv, const CliCredentials& cred, GrpcToolOutputCallback callback) { CommandUsage( diff --git a/test/cpp/util/grpc_tool_test.cc b/test/cpp/util/grpc_tool_test.cc index 3aae090e81..be9a624a2c 100644 --- a/test/cpp/util/grpc_tool_test.cc +++ b/test/cpp/util/grpc_tool_test.cc @@ -74,11 +74,20 @@ using grpc::testing::EchoResponse; " rpc Echo(grpc.testing.EchoRequest) returns (grpc.testing.EchoResponse) " \ "{}\n" -#define ECHO_RESPONSE_MESSAGE \ - "message: \"echo\"\n" \ - "param {\n" \ - " host: \"localhost\"\n" \ - " peer: \"peer\"\n" \ +#define ECHO_RESPONSE_MESSAGE_TEXT_FORMAT \ + "message: \"echo\"\n" \ + "param {\n" \ + " host: \"localhost\"\n" \ + " peer: \"peer\"\n" \ + "}\n\n" + +#define ECHO_RESPONSE_MESSAGE_JSON_FORMAT \ + "{\n" \ + " \"message\": \"echo\",\n" \ + " \"param\": {\n" \ + " \"host\": \"localhost\",\n" \ + " \"peer\": \"peer\"\n" \ + " }\n" \ "}\n\n" DECLARE_string(channel_creds_type); @@ -89,6 +98,8 @@ namespace testing { DECLARE_bool(binary_input); DECLARE_bool(binary_output); +DECLARE_bool(json_input); +DECLARE_bool(json_output); DECLARE_bool(l); DECLARE_bool(batch); DECLARE_string(metadata); @@ -426,6 +437,61 @@ TEST_F(GrpcToolTest, CallCommand) { // Expected output: "message: \"Hello\"" EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), "message: \"Hello\"")); + + // with json_output + output_stream.str(grpc::string()); + output_stream.clear(); + + FLAGS_json_output = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output = false; + + // Expected output: + // { + // "message": "Hello" + // } + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "{\n \"message\": \"Hello\"\n}")); + + ShutdownServer(); +} + +TEST_F(GrpcToolTest, CallCommandJsonInput) { + // Test input "grpc_cli call localhost:<port> Echo "{ \"message\": \"Hello\"}" + std::stringstream output_stream; + + const grpc::string server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), "Echo", + "{ \"message\": \"Hello\"}"}; + + FLAGS_json_input = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + // Expected output: "message: \"Hello\"" + EXPECT_TRUE(nullptr != + strstr(output_stream.str().c_str(), "message: \"Hello\"")); + + // with json_output + output_stream.str(grpc::string()); + output_stream.clear(); + + FLAGS_json_output = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output = false; + FLAGS_json_input = false; + + // Expected output: + // { + // "message": "Hello" + // } + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "{\n \"message\": \"Hello\"\n}")); + ShutdownServer(); } @@ -453,6 +519,101 @@ TEST_F(GrpcToolTest, CallCommandBatch) { EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), "message: \"Hello0\"\nmessage: " "\"Hello1\"\nmessage: \"Hello2\"\n")); + // with json_output + output_stream.str(grpc::string()); + output_stream.clear(); + ss.clear(); + ss.seekg(0); + std::cin.rdbuf(ss.rdbuf()); + + FLAGS_batch = true; + FLAGS_json_output = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output = false; + FLAGS_batch = false; + + // Expected output: + // { + // "message": "Hello0" + // } + // { + // "message": "Hello1" + // } + // { + // "message": "Hello2" + // } + // Expected output: "message: "Hello0"\nmessage: "Hello1"\nmessage: + // "Hello2"\n" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "{\n \"message\": \"Hello0\"\n}\n" + "{\n \"message\": \"Hello1\"\n}\n" + "{\n \"message\": \"Hello2\"\n}\n")); + + std::cin.rdbuf(orig); + ShutdownServer(); +} + +TEST_F(GrpcToolTest, CallCommandBatchJsonInput) { + // Test input "grpc_cli call Echo" + std::stringstream output_stream; + + const grpc::string server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), "Echo", + "{\"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()); + + FLAGS_json_input = true; + FLAGS_batch = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_batch = false; + + // Expected output: "message: "Hello0"\nmessage: "Hello1"\nmessage: + // "Hello2"\n" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "message: \"Hello0\"\nmessage: " + "\"Hello1\"\nmessage: \"Hello2\"\n")); + // with json_output + output_stream.str(grpc::string()); + output_stream.clear(); + ss.clear(); + ss.seekg(0); + std::cin.rdbuf(ss.rdbuf()); + + FLAGS_batch = true; + FLAGS_json_output = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output = false; + FLAGS_batch = false; + FLAGS_json_input = false; + + // Expected output: + // { + // "message": "Hello0" + // } + // { + // "message": "Hello1" + // } + // { + // "message": "Hello2" + // } + // Expected output: "message: "Hello0"\nmessage: "Hello1"\nmessage: + // "Hello2"\n" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "{\n \"message\": \"Hello0\"\n}\n" + "{\n \"message\": \"Hello1\"\n}\n" + "{\n \"message\": \"Hello2\"\n}\n")); + std::cin.rdbuf(orig); ShutdownServer(); } @@ -479,6 +640,95 @@ TEST_F(GrpcToolTest, CallCommandBatchWithBadRequest) { // Expected output: "message: "Hello0"\nmessage: "Hello2"\n" EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), "message: \"Hello0\"\nmessage: \"Hello2\"\n")); + + // with json_output + output_stream.str(grpc::string()); + output_stream.clear(); + ss.clear(); + ss.seekg(0); + std::cin.rdbuf(ss.rdbuf()); + + FLAGS_batch = true; + FLAGS_json_output = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output = false; + FLAGS_batch = false; + + // Expected output: + // { + // "message": "Hello0" + // } + // { + // "message": "Hello2" + // } + // Expected output: "message: "Hello0"\nmessage: "Hello1"\nmessage: + // "Hello2"\n" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "{\n \"message\": \"Hello0\"\n}\n" + "{\n \"message\": \"Hello2\"\n}\n")); + + std::cin.rdbuf(orig); + ShutdownServer(); +} + +TEST_F(GrpcToolTest, CallCommandBatchJsonInputWithBadRequest) { + // Test input "grpc_cli call Echo" + std::stringstream output_stream; + + const grpc::string server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), "Echo", + "{ \"message\": \"Hello0\"}"}; + + // Mock std::cin input "message: 1\n\n message: 'Hello2'\n\n" + std::streambuf* orig = std::cin.rdbuf(); + std::istringstream ss( + "{ \"message\": 1 }\n\n { \"message\": \"Hello2\" }\n\n"); + std::cin.rdbuf(ss.rdbuf()); + + FLAGS_batch = true; + FLAGS_json_input = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_input = false; + FLAGS_batch = false; + + // Expected output: "message: "Hello0"\nmessage: "Hello2"\n" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "message: \"Hello0\"\nmessage: \"Hello2\"\n")); + + // with json_output + output_stream.str(grpc::string()); + output_stream.clear(); + ss.clear(); + ss.seekg(0); + std::cin.rdbuf(ss.rdbuf()); + + FLAGS_batch = true; + FLAGS_json_input = true; + FLAGS_json_output = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output = false; + FLAGS_json_input = false; + FLAGS_batch = false; + + // Expected output: + // { + // "message": "Hello0" + // } + // { + // "message": "Hello2" + // } + // Expected output: "message: "Hello0"\nmessage: "Hello1"\nmessage: + // "Hello2"\n" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "{\n \"message\": \"Hello0\"\n}\n" + "{\n \"message\": \"Hello2\"\n}\n")); + std::cin.rdbuf(orig); ShutdownServer(); } @@ -508,6 +758,34 @@ TEST_F(GrpcToolTest, CallCommandRequestStream) { ShutdownServer(); } +TEST_F(GrpcToolTest, CallCommandRequestStreamJsonInput) { + // Test input: grpc_cli call localhost:<port> RequestStream "{ \"message\": + // \"Hello0\"}" + std::stringstream output_stream; + + const grpc::string server_address = SetUpServer(); + 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" + std::streambuf* orig = std::cin.rdbuf(); + std::istringstream ss( + "{ \"message\": \"Hello1\" }\n\n{ \"message\": \"Hello2\" }\n\n"); + std::cin.rdbuf(ss.rdbuf()); + + FLAGS_json_input = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_input = false; + + // Expected output: "message: \"Hello0Hello1Hello2\"" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "message: \"Hello0Hello1Hello2\"")); + std::cin.rdbuf(orig); + ShutdownServer(); +} + TEST_F(GrpcToolTest, CallCommandRequestStreamWithBadRequest) { // Test input: grpc_cli call localhost:<port> RequestStream "message: // 'Hello0'" @@ -533,6 +811,34 @@ TEST_F(GrpcToolTest, CallCommandRequestStreamWithBadRequest) { ShutdownServer(); } +TEST_F(GrpcToolTest, CallCommandRequestStreamWithBadRequestJsonInput) { + // Test input: grpc_cli call localhost:<port> RequestStream "message: + // 'Hello0'" + std::stringstream output_stream; + + const grpc::string server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), + "RequestStream", "{ \"message\": \"Hello0\" }"}; + + // Mock std::cin input "bad_field: 'Hello1'\n\n message: 'Hello2'\n\n" + std::streambuf* orig = std::cin.rdbuf(); + std::istringstream ss( + "{ \"bad_field\": \"Hello1\" }\n\n{ \"message\": \"Hello2\" }\n\n"); + std::cin.rdbuf(ss.rdbuf()); + + FLAGS_json_input = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_input = false; + + // Expected output: "message: \"Hello0Hello2\"" + EXPECT_TRUE(nullptr != + strstr(output_stream.str().c_str(), "message: \"Hello0Hello2\"")); + std::cin.rdbuf(orig); + ShutdownServer(); +} + TEST_F(GrpcToolTest, CallCommandResponseStream) { // Test input: grpc_cli call localhost:<port> ResponseStream "message: // 'Hello'" @@ -554,6 +860,24 @@ TEST_F(GrpcToolTest, CallCommandResponseStream) { expected_response_text.c_str())); } + // with json_output + output_stream.str(grpc::string()); + output_stream.clear(); + + FLAGS_json_output = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output = false; + + // Expected output: "{\n \"message\": \"Hello{n}\"\n}\n" + for (int i = 0; i < kServerDefaultResponseStreamsToSend; i++) { + grpc::string expected_response_text = + "{\n \"message\": \"Hello" + grpc::to_string(i) + "\"\n}\n"; + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + expected_response_text.c_str())); + } + ShutdownServer(); } @@ -617,15 +941,31 @@ TEST_F(GrpcToolTest, ParseCommand) { const grpc::string server_address = SetUpServer(); const char* argv[] = {"grpc_cli", "parse", server_address.c_str(), - "grpc.testing.EchoResponse", ECHO_RESPONSE_MESSAGE}; + "grpc.testing.EchoResponse", + ECHO_RESPONSE_MESSAGE_TEXT_FORMAT}; 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)); + // Expected output: ECHO_RESPONSE_MESSAGE_TEXT_FORMAT + EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), + ECHO_RESPONSE_MESSAGE_TEXT_FORMAT)); + + // with json_output + output_stream.str(grpc::string()); + output_stream.clear(); + + FLAGS_json_output = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output = false; + + // Expected output: ECHO_RESPONSE_MESSAGE_JSON_FORMAT + EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), + ECHO_RESPONSE_MESSAGE_JSON_FORMAT)); // Parse text message to binary message and then parse it back to text message output_stream.str(grpc::string()); @@ -645,13 +985,52 @@ TEST_F(GrpcToolTest, ParseCommand) { std::placeholders::_1))); // Expected output: ECHO_RESPONSE_MESSAGE - EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), ECHO_RESPONSE_MESSAGE)); + EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), + ECHO_RESPONSE_MESSAGE_TEXT_FORMAT)); FLAGS_binary_input = false; FLAGS_binary_output = false; ShutdownServer(); } +TEST_F(GrpcToolTest, ParseCommandJsonFormat) { + // Test input "grpc_cli parse localhost:<port> grpc.testing.EchoResponse + // ECHO_RESPONSE_MESSAGE_JSON_FORMAT" + 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_JSON_FORMAT}; + + FLAGS_json_input = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + + // Expected output: ECHO_RESPONSE_MESSAGE_TEXT_FORMAT + EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), + ECHO_RESPONSE_MESSAGE_TEXT_FORMAT)); + + // with json_output + output_stream.str(grpc::string()); + output_stream.clear(); + + FLAGS_json_output = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output = false; + FLAGS_json_input = false; + + // Expected output: ECHO_RESPONSE_MESSAGE_JSON_FORMAT + EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), + ECHO_RESPONSE_MESSAGE_JSON_FORMAT)); + + 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 a530ed1ffc..68ecfeae2c 100644 --- a/test/cpp/util/proto_file_parser.cc +++ b/test/cpp/util/proto_file_parser.cc @@ -217,31 +217,32 @@ bool ProtoFileParser::IsStreaming(const grpc::string& method, bool is_request) { } grpc::string ProtoFileParser::GetSerializedProtoFromMethod( - const grpc::string& method, const grpc::string& text_format_proto, - bool is_request) { + const grpc::string& method, const grpc::string& formatted_proto, + bool is_request, bool is_json_format) { has_error_ = false; grpc::string message_type_name = GetMessageTypeFromMethod(method, is_request); if (has_error_) { return ""; } - return GetSerializedProtoFromMessageType(message_type_name, - text_format_proto); + return GetSerializedProtoFromMessageType(message_type_name, formatted_proto, + is_json_format); } -grpc::string ProtoFileParser::GetTextFormatFromMethod( +grpc::string ProtoFileParser::GetFormattedStringFromMethod( const grpc::string& method, const grpc::string& serialized_proto, - bool is_request) { + bool is_request, bool is_json_format) { has_error_ = false; grpc::string message_type_name = GetMessageTypeFromMethod(method, is_request); if (has_error_) { return ""; } - return GetTextFormatFromMessageType(message_type_name, serialized_proto); + return GetFormattedStringFromMessageType(message_type_name, serialized_proto, + is_json_format); } grpc::string ProtoFileParser::GetSerializedProtoFromMessageType( - const grpc::string& message_type_name, - const grpc::string& text_format_proto) { + const grpc::string& message_type_name, const grpc::string& formatted_proto, + bool is_json_format) { has_error_ = false; grpc::string serialized; const protobuf::Descriptor* desc = @@ -252,11 +253,23 @@ grpc::string ProtoFileParser::GetSerializedProtoFromMessageType( } std::unique_ptr<grpc::protobuf::Message> msg( dynamic_factory_->GetPrototype(desc)->New()); - bool ok = protobuf::TextFormat::ParseFromString(text_format_proto, msg.get()); - if (!ok) { - LogError("Failed to parse text format to proto."); - return ""; + + bool ok; + if (is_json_format) { + ok = grpc::protobuf::json::JsonStringToMessage(formatted_proto, msg.get()) + .ok(); + if (!ok) { + LogError("Failed to convert json format to proto."); + return ""; + } + } else { + ok = protobuf::TextFormat::ParseFromString(formatted_proto, msg.get()); + if (!ok) { + LogError("Failed to convert text format to proto."); + return ""; + } } + ok = msg->SerializeToString(&serialized); if (!ok) { LogError("Failed to serialize proto."); @@ -265,9 +278,9 @@ grpc::string ProtoFileParser::GetSerializedProtoFromMessageType( return serialized; } -grpc::string ProtoFileParser::GetTextFormatFromMessageType( - const grpc::string& message_type_name, - const grpc::string& serialized_proto) { +grpc::string ProtoFileParser::GetFormattedStringFromMessageType( + const grpc::string& message_type_name, const grpc::string& serialized_proto, + bool is_json_format) { has_error_ = false; const protobuf::Descriptor* desc = desc_pool_->FindMessageTypeByName(message_type_name); @@ -281,12 +294,24 @@ grpc::string ProtoFileParser::GetTextFormatFromMessageType( LogError("Failed to deserialize proto."); return ""; } - grpc::string text_format; - if (!protobuf::TextFormat::PrintToString(*msg.get(), &text_format)) { - LogError("Failed to print proto message to text format"); - return ""; + grpc::string formatted_string; + + if (is_json_format) { + grpc::protobuf::json::JsonPrintOptions jsonPrintOptions; + jsonPrintOptions.add_whitespace = true; + if (!grpc::protobuf::json::MessageToJsonString( + *msg.get(), &formatted_string, jsonPrintOptions) + .ok()) { + LogError("Failed to print proto message to json format"); + return ""; + } + } else { + if (!protobuf::TextFormat::PrintToString(*msg.get(), &formatted_string)) { + LogError("Failed to print proto message to text format"); + return ""; + } } - return text_format; + return formatted_string; } void ProtoFileParser::LogError(const grpc::string& error_msg) { diff --git a/test/cpp/util/proto_file_parser.h b/test/cpp/util/proto_file_parser.h index eb1d793c2b..1e49c98daf 100644 --- a/test/cpp/util/proto_file_parser.h +++ b/test/cpp/util/proto_file_parser.h @@ -53,21 +53,49 @@ class ProtoFileParser { // used as the argument of Stub::Call() grpc::string GetFormattedMethodName(const grpc::string& method); - grpc::string GetSerializedProtoFromMethod( - const grpc::string& method, const grpc::string& text_format_proto, - bool is_request); - - grpc::string GetTextFormatFromMethod(const grpc::string& method, - const grpc::string& serialized_proto, - bool is_request); - + /// Converts a text or json string to its binary proto representation for the + /// given method's input or return type. + /// \param method the name of the method (does not need to be fully qualified + /// name) + /// \param formatted_proto the text- or json-formatted proto string + /// \param is_request if \c true the resolved type is that of the input + /// parameter of the method, otherwise it is the output type + /// \param is_json_format if \c true the \c formatted_proto is treated as a + /// json-formatted proto, otherwise it is treated as a text-formatted + /// proto + /// \return the serialised binary proto represenation of \c formatted_proto + grpc::string GetSerializedProtoFromMethod(const grpc::string& method, + const grpc::string& formatted_proto, + bool is_request, + bool is_json_format); + + /// Converts a text or json string to its proto representation for the given + /// message type. + /// \param formatted_proto the text- or json-formatted proto string + /// \return the serialised binary proto represenation of \c formatted_proto grpc::string GetSerializedProtoFromMessageType( const grpc::string& message_type_name, - const grpc::string& text_format_proto); - - grpc::string GetTextFormatFromMessageType( + const grpc::string& formatted_proto, bool is_json_format); + + /// Converts a binary proto string to its text or json string representation + /// for the given method's input or return type. + /// \param method the name of the method (does not need to be a fully + /// qualified name) + /// \param the serialised binary proto representation of type + /// \c message_type_name + /// \return the text- or json-formatted proto string of \c serialized_proto + grpc::string GetFormattedStringFromMethod( + const grpc::string& method, const grpc::string& serialized_proto, + bool is_request, bool is_json_format); + + /// Converts a binary proto string to its text or json string representation + /// for the given message type. + /// \param the serialised binary proto representation of type + /// \c message_type_name + /// \return the text- or json-formatted proto string of \c serialized_proto + grpc::string GetFormattedStringFromMessageType( const grpc::string& message_type_name, - const grpc::string& serialized_proto); + const grpc::string& serialized_proto, bool is_json_format); bool IsStreaming(const grpc::string& method, bool is_request); |