diff options
author | Yuchen Zeng <zyc@google.com> | 2016-08-26 14:25:21 -0700 |
---|---|---|
committer | Yuchen Zeng <zyc@google.com> | 2016-08-26 14:25:21 -0700 |
commit | 387afd70d39f6b73cc5fd484b3398c4250f02e7a (patch) | |
tree | 733da4109047526d9aa8628337d59763dd99fb0e /test/cpp/util | |
parent | c931305c398e7e87013da52a5c2697d0fb14ad7f (diff) |
Add list command
Diffstat (limited to 'test/cpp/util')
-rw-r--r-- | test/cpp/util/grpc_tool.cc | 116 | ||||
-rw-r--r-- | test/cpp/util/grpc_tool_test.cc | 139 | ||||
-rw-r--r-- | test/cpp/util/service_describer.cc | 109 | ||||
-rw-r--r-- | test/cpp/util/service_describer.h | 54 |
4 files changed, 396 insertions, 22 deletions
diff --git a/test/cpp/util/grpc_tool.cc b/test/cpp/util/grpc_tool.cc index f06053ca23..bb2d8eb564 100644 --- a/test/cpp/util/grpc_tool.cc +++ b/test/cpp/util/grpc_tool.cc @@ -51,8 +51,13 @@ #include "test/cpp/util/cli_call.h" #include "test/cpp/util/proto_file_parser.h" #include "test/cpp/util/proto_reflection_descriptor_database.h" +#include "test/cpp/util/service_describer.h" #include "test/cpp/util/test_config.h" +namespace grpc { +namespace testing { + +DEFINE_bool(l, false, "Use a long listing format"); DEFINE_bool(remotedb, true, "Use server types to parse and format messages"); DEFINE_string(metadata, "", "Metadata to send to server, in the form of key1:val1:key2:val2"); @@ -62,8 +67,6 @@ DEFINE_bool(binary_input, false, "Input in binary format"); DEFINE_bool(binary_output, false, "Output in binary format"); DEFINE_string(infile, "", "Input file (default is stdin)"); -namespace grpc { -namespace testing { namespace { class GrpcTool { @@ -75,9 +78,9 @@ class GrpcTool { GrpcToolOutputCallback callback); bool CallMethod(int argc, const char** argv, CliCredentials cred, GrpcToolOutputCallback callback); + bool ListServices(int argc, const char** argv, CliCredentials cred, + GrpcToolOutputCallback callback); // TODO(zyc): implement the following methods - // bool ListServices(int argc, const char** argv, GrpcToolOutputCallback - // callback); // bool PrintType(int argc, const char** argv, GrpcToolOutputCallback // callback); // bool PrintTypeId(int argc, const char** argv, GrpcToolOutputCallback @@ -165,8 +168,8 @@ struct Command { const Command ops[] = { {"help", BindWith5Args(&GrpcTool::Help), 0, INT_MAX}, - // {"ls", BindWith5Args(&GrpcTool::ListServices), 1, 3}, - // {"list", BindWith5Args(&GrpcTool::ListServices), 1, 3}, + {"ls", BindWith5Args(&GrpcTool::ListServices), 1, 3}, + {"list", BindWith5Args(&GrpcTool::ListServices), 1, 3}, {"call", BindWith5Args(&GrpcTool::CallMethod), 2, 3}, // {"type", BindWith5Args(&GrpcTool::PrintType), 2, 2}, // {"parse", BindWith5Args(&GrpcTool::ParseMessage), 2, 3}, @@ -178,7 +181,7 @@ void Usage(const grpc::string& msg) { fprintf( stderr, "%s\n" - // " grpc_cli ls ... ; List services\n" + " grpc_cli ls ... ; List services\n" " grpc_cli call ... ; Call method\n" // " grpc_cli type ... ; Print type\n" // " grpc_cli parse ... ; Parse message\n" @@ -257,6 +260,105 @@ bool GrpcTool::Help(int argc, const char** argv, const CliCredentials cred, return true; } +bool GrpcTool::ListServices(int argc, const char** argv, + const CliCredentials cred, + GrpcToolOutputCallback callback) { + CommandUsage( + "List services\n" + " grpc_cli ls <address> [<service>[/<method>]]\n" + " <address> ; host:port\n" + " <service> ; Exported service name\n" + " <method> ; Method name\n" + " --l ; Use a long listing format\n" + " --outfile ; Output filename (defaults to stdout)\n" + + cred.GetCredentialUsage()); + + grpc::string server_address(argv[0]); + std::shared_ptr<grpc::Channel> channel = + grpc::CreateChannel(server_address, cred.GetCredentials()); + grpc::ProtoReflectionDescriptorDatabase desc_db(channel); + google::protobuf::DescriptorPool desc_pool(&desc_db); + + std::vector<grpc::string> service_list; + if (!desc_db.GetServices(&service_list)) { + return false; + } + + // If no service is specified, dump the list of services. + grpc::string output; + if (argc < 2) { + // List all services, if --l is passed, then include full description, + // otherwise include a summarized list only. + if (FLAGS_l) { + output = DescribeServiceList(service_list, desc_pool); + } else { + for (auto const& service : service_list) { + output.append(service); + output.append("\n"); + } + } + } else { + grpc::string service_name; + grpc::string method_name; + std::stringstream ss(argv[1]); + + // Remove leading slashes. + while (ss.peek() == '/') { + ss.get(); + } + + // Parse service and method names. Support the following patterns: + // Service + // Service Method + // Service.Method + // Service/Method + if (argc == 3) { + std::getline(ss, service_name, '/'); + method_name = argv[2]; + } else { + if (std::getline(ss, service_name, '/')) { + std::getline(ss, method_name); + } + } + + const google::protobuf::ServiceDescriptor* service = + desc_pool.FindServiceByName(service_name); + if (service != nullptr) { + if (method_name.empty()) { + output = FLAGS_l ? DescribeService(service) : SummarizeService(service); + } else { + method_name.insert(0, 1, '.'); + method_name.insert(0, service_name); + const google::protobuf::MethodDescriptor* method = + desc_pool.FindMethodByName(method_name); + if (method != nullptr) { + output = FLAGS_l ? DescribeMethod(method) : SummarizeMethod(method); + } else { + fprintf(stderr, "Method %s not found in service %s.\n", + method_name.c_str(), service_name.c_str()); + return false; + } + } + } else { + if (!method_name.empty()) { + fprintf(stderr, "Service %s not found.\n", service_name.c_str()); + return false; + } else { + const google::protobuf::MethodDescriptor* method = + desc_pool.FindMethodByName(service_name); + if (method != nullptr) { + output = FLAGS_l ? DescribeMethod(method) : SummarizeMethod(method); + } else { + fprintf(stderr, "Service or method %s not found.\n", + service_name.c_str()); + return false; + } + } + } + } + return callback(output); +} + bool GrpcTool::CallMethod(int argc, const char** argv, const CliCredentials cred, GrpcToolOutputCallback callback) { diff --git a/test/cpp/util/grpc_tool_test.cc b/test/cpp/util/grpc_tool_test.cc index b96afaf50c..12ca6c4b80 100644 --- a/test/cpp/util/grpc_tool_test.cc +++ b/test/cpp/util/grpc_tool_test.cc @@ -43,6 +43,7 @@ #include <grpc++/server_builder.h> #include <grpc++/server_context.h> #include <grpc/grpc.h> +#include <gflags/gflags.h> #include <gtest/gtest.h> #include "src/proto/grpc/testing/echo.grpc.pb.h" @@ -55,8 +56,41 @@ using grpc::testing::EchoRequest; using grpc::testing::EchoResponse; +#define USAGE_REGEX "( grpc_cli .+\n){2,10}" + +#define ECHO_TEST_SERVICE_SUMMARY \ + "Echo\n" \ + "RequestStream\n" \ + "ResponseStream\n" \ + "BidiStream\n" \ + "Unimplemented\n" + +#define ECHO_TEST_SERVICE_DESCRIPTION \ + "filename: src/proto/grpc/testing/echo.proto\n" \ + "package: grpc.testing;\n" \ + "service EchoTestService {\n" \ + " rpc Echo(grpc.testing.EchoRequest) returns (grpc.testing.EchoResponse) " \ + "{}\n" \ + " rpc RequestStream(stream grpc.testing.EchoRequest) returns " \ + "(grpc.testing.EchoResponse) {}\n" \ + " rpc ResponseStream(grpc.testing.EchoRequest) returns (stream " \ + "grpc.testing.EchoResponse) {}\n" \ + " rpc BidiStream(stream grpc.testing.EchoRequest) returns (stream " \ + "grpc.testing.EchoResponse) {}\n" \ + " rpc Unimplemented(grpc.testing.EchoRequest) returns " \ + "(grpc.testing.EchoResponse) {}\n" \ + "}\n" \ + "\n" + +#define ECHO_METHOD_DESCRIPTION \ + " rpc Echo(grpc.testing.EchoRequest) returns (grpc.testing.EchoResponse) " \ + "{}\n" + namespace grpc { namespace testing { + +DECLARE_bool(l); + namespace { class TestCliCredentials GRPC_FINAL : public grpc::testing::CliCredentials { @@ -68,6 +102,17 @@ class TestCliCredentials GRPC_FINAL : public grpc::testing::CliCredentials { const grpc::string GetCredentialUsage() const GRPC_OVERRIDE { return ""; } }; +bool PrintStream(std::stringstream* ss, const grpc::string& output) { + (*ss) << output; + return true; +} + +template <typename T> +size_t ArraySize(T& a) { + return ((sizeof(a) / sizeof(*(a))) / + static_cast<size_t>(!(sizeof(a) % sizeof(*(a))))); +} + } // namespame class TestServiceImpl : public ::grpc::testing::EchoTestService::Service { @@ -114,19 +159,6 @@ class GrpcToolTest : public ::testing::Test { reflection::ProtoServerReflectionPlugin plugin_; }; -static bool PrintStream(std::stringstream* ss, const grpc::string& output) { - (*ss) << output << std::endl; - return true; -} - -template <typename T> -static size_t ArraySize(T& a) { - return ((sizeof(a) / sizeof(*(a))) / - static_cast<size_t>(!(sizeof(a) % sizeof(*(a))))); -} - -#define USAGE_REGEX "( grpc_cli .+\n){2,10}" - TEST_F(GrpcToolTest, NoCommand) { // Test input "grpc_cli" std::stringstream output_stream; @@ -168,8 +200,85 @@ TEST_F(GrpcToolTest, HelpCommand) { EXPECT_TRUE(0 == output_stream.tellp()); } +TEST_F(GrpcToolTest, ListCommand) { + // Test input "grpc_cli list localhost:<port>" + std::stringstream output_stream; + + const grpc::string server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "ls", server_address.c_str()}; + + FLAGS_l = false; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), + "grpc.testing.EchoTestService\n" + "grpc.reflection.v1alpha.ServerReflection\n")); + + ShutdownServer(); +} + +TEST_F(GrpcToolTest, ListOneService) { + // Test input "grpc_cli list localhost:<port> grpc.testing.EchoTestService" + std::stringstream output_stream; + + const grpc::string server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "ls", server_address.c_str(), + "grpc.testing.EchoTestService"}; + // without -l flag + FLAGS_l = false; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + // Expected output: ECHO_TEST_SERVICE_SUMMARY + EXPECT_TRUE(0 == + strcmp(output_stream.str().c_str(), ECHO_TEST_SERVICE_SUMMARY)); + + // with -l flag + output_stream.str(grpc::string()); + output_stream.clear(); + FLAGS_l = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + // Expected output: ECHO_TEST_SERVICE_DESCRIPTION + EXPECT_TRUE( + 0 == strcmp(output_stream.str().c_str(), ECHO_TEST_SERVICE_DESCRIPTION)); + + ShutdownServer(); +} + +TEST_F(GrpcToolTest, ListOneMethod) { + // Test input "grpc_cli list localhost:<port> grpc.testing.EchoTestService" + std::stringstream output_stream; + + const grpc::string server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "ls", server_address.c_str(), + "grpc.testing.EchoTestService.Echo"}; + // without -l flag + FLAGS_l = false; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + // Expected output: "Echo" + EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), "Echo\n")); + + // with -l flag + output_stream.str(grpc::string()); + output_stream.clear(); + FLAGS_l = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + // Expected output: ECHO_METHOD_DESCRIPTION + EXPECT_TRUE(0 == + strcmp(output_stream.str().c_str(), ECHO_METHOD_DESCRIPTION)); + + ShutdownServer(); +} + TEST_F(GrpcToolTest, CallCommand) { - // Test input "grpc_cli call Echo" + // Test input "grpc_cli call localhost:<port> Echo "message: 'Hello'" std::stringstream output_stream; const grpc::string server_address = SetUpServer(); @@ -186,7 +295,7 @@ TEST_F(GrpcToolTest, CallCommand) { } TEST_F(GrpcToolTest, TooFewArguments) { - // Test input "grpc_cli call localhost:<port> Echo "message: 'Hello'" + // Test input "grpc_cli call Echo" std::stringstream output_stream; const char* argv[] = {"grpc_cli", "call", "Echo"}; diff --git a/test/cpp/util/service_describer.cc b/test/cpp/util/service_describer.cc new file mode 100644 index 0000000000..b475e3633a --- /dev/null +++ b/test/cpp/util/service_describer.cc @@ -0,0 +1,109 @@ +/* + * + * Copyright 2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "service_describer.h" + +#include <google/protobuf/descriptor.pb.h> +#include <iostream> +#include <sstream> +#include <string> +#include <vector> + +namespace grpc { +namespace testing { + +grpc::string DescribeServiceList(std::vector<grpc::string> service_list, + google::protobuf::DescriptorPool& desc_pool) { + std::stringstream result; + for (auto const& service : service_list) { + const google::protobuf::ServiceDescriptor* service_desc = + desc_pool.FindServiceByName(service); + if (service_desc != nullptr) { + result << DescribeService(service_desc); + } + } + return result.str(); +} + +grpc::string DescribeService( + const google::protobuf::ServiceDescriptor* service) { + grpc::string result; + if (service->options().deprecated()) { + result.append("DEPRECATED\n"); + } + result.append("filename: " + service->file()->name() + "\n"); + + grpc::string package = service->full_name(); + size_t pos = package.rfind("." + service->name()); + if (pos != grpc::string::npos) { + package.erase(pos); + result.append("package: " + package + ";\n"); + } + result.append("service " + service->name() + " {\n"); + for (int i = 0; i < service->method_count(); ++i) { + result.append(DescribeMethod(service->method(i))); + } + result.append("}\n\n"); + return result; +} + +grpc::string DescribeMethod(const google::protobuf::MethodDescriptor* method) { + std::stringstream result; + result << " rpc " << method->name() + << (method->client_streaming() ? "(stream " : "(") + << method->input_type()->full_name() << ") returns " + << (method->server_streaming() ? "(stream " : "(") + << method->output_type()->full_name() << ") {}\n"; + if (method->options().deprecated()) { + result << " DEPRECATED"; + } + return result.str(); +} + +grpc::string SummarizeService( + const google::protobuf::ServiceDescriptor* service) { + grpc::string result; + for (int i = 0; i < service->method_count(); ++i) { + result.append(SummarizeMethod(service->method(i))); + } + return result; +} + +grpc::string SummarizeMethod(const google::protobuf::MethodDescriptor* method) { + grpc::string result = method->name(); + result.append("\n"); + return result; +} + +} // namespace testing +} // namespace grpc diff --git a/test/cpp/util/service_describer.h b/test/cpp/util/service_describer.h new file mode 100644 index 0000000000..e4edd2dcf3 --- /dev/null +++ b/test/cpp/util/service_describer.h @@ -0,0 +1,54 @@ +/* + * + * Copyright 2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <google/protobuf/descriptor.h> +#include <grpc++/support/config.h> + +namespace grpc { +namespace testing { + +grpc::string DescribeServiceList(std::vector<grpc::string> service_list, + google::protobuf::DescriptorPool& desc_pool); + +grpc::string DescribeService( + const google::protobuf::ServiceDescriptor* service); + +grpc::string DescribeMethod(const google::protobuf::MethodDescriptor* method); + +grpc::string SummarizeService( + const google::protobuf::ServiceDescriptor* service); + +grpc::string SummarizeMethod(const google::protobuf::MethodDescriptor* method); + +} // namespase testing +} // namespace grpc |