aboutsummaryrefslogtreecommitdiffhomepage
path: root/test/cpp/util
diff options
context:
space:
mode:
authorGravatar Yuchen Zeng <zyc@google.com>2016-08-26 14:25:21 -0700
committerGravatar Yuchen Zeng <zyc@google.com>2016-08-26 14:25:21 -0700
commit387afd70d39f6b73cc5fd484b3398c4250f02e7a (patch)
tree733da4109047526d9aa8628337d59763dd99fb0e /test/cpp/util
parentc931305c398e7e87013da52a5c2697d0fb14ad7f (diff)
Add list command
Diffstat (limited to 'test/cpp/util')
-rw-r--r--test/cpp/util/grpc_tool.cc116
-rw-r--r--test/cpp/util/grpc_tool_test.cc139
-rw-r--r--test/cpp/util/service_describer.cc109
-rw-r--r--test/cpp/util/service_describer.h54
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