Command processing, validation
diff --git a/test/cpp/util/grpc_tool_test.cc b/test/cpp/util/grpc_tool_test.cc
new file mode 100644
index 0000000..77c3f3f
--- /dev/null
+++ b/test/cpp/util/grpc_tool_test.cc
@@ -0,0 +1,219 @@
+/*
+ *
+ * Copyright 2015, 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 "test/cpp/util/grpc_tool.h"
+
+#include <sstream>
+
+#include <grpc++/channel.h>
+#include <grpc++/client_context.h>
+#include <grpc++/create_channel.h>
+#include <grpc++/ext/proto_server_reflection_plugin.h>
+#include <grpc++/server.h>
+#include <grpc++/server_builder.h>
+#include <grpc++/server_context.h>
+#include <grpc/grpc.h>
+#include <gtest/gtest.h>
+
+#include "src/proto/grpc/testing/echo.grpc.pb.h"
+#include "src/proto/grpc/testing/echo.pb.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+#include "test/cpp/util/string_ref_helper.h"
+
+using grpc::testing::EchoRequest;
+using grpc::testing::EchoResponse;
+
+namespace grpc {
+namespace testing {
+
+class TestServiceImpl : public ::grpc::testing::EchoTestService::Service {
+ public:
+ Status Echo(ServerContext* context, const EchoRequest* request,
+ EchoResponse* response) GRPC_OVERRIDE {
+ 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");
+ response->set_message(request->message());
+ return Status::OK;
+ }
+};
+
+class GrpcToolTest : public ::testing::Test {
+ protected:
+ GrpcToolTest() {}
+
+ void SetUp() GRPC_OVERRIDE {
+ int port = grpc_pick_unused_port_or_die();
+ server_address_ << "localhost:" << port;
+ // Setup server
+ ServerBuilder builder;
+ builder.AddListeningPort(server_address_.str(),
+ InsecureServerCredentials());
+ builder.RegisterService(&service_);
+ server_ = builder.BuildAndStart();
+ }
+
+ void TearDown() GRPC_OVERRIDE { server_->Shutdown(); }
+
+ void ResetStub() {
+ channel_ =
+ CreateChannel(server_address_.str(), InsecureChannelCredentials());
+ stub_ = grpc::testing::EchoTestService::NewStub(channel_);
+ }
+
+ std::shared_ptr<Channel> channel_;
+ std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_;
+ std::unique_ptr<Server> server_;
+ std::ostringstream server_address_;
+ TestServiceImpl service_;
+ 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;
+ const char* argv[] = {"grpc_cli"};
+ // Exit with 1, print usage instruction in stderr
+ EXPECT_EXIT(
+ GrpcToolMainLib(
+ ArraySize(argv), argv,
+ std::bind(PrintStream, &output_stream, std::placeholders::_1)),
+ ::testing::ExitedWithCode(1), "No command specified\n" USAGE_REGEX);
+ // No output
+ EXPECT_TRUE(0 == output_stream.tellp());
+}
+
+TEST_F(GrpcToolTest, InvalidCommand) {
+ // Test input "grpc_cli"
+ std::stringstream output_stream;
+ const char* argv[] = {"grpc_cli", "abc"};
+ // Exit with 1, print usage instruction in stderr
+ EXPECT_EXIT(
+ GrpcToolMainLib(
+ ArraySize(argv), argv,
+ std::bind(PrintStream, &output_stream, std::placeholders::_1)),
+ ::testing::ExitedWithCode(1), "Invalid command 'abc'\n" USAGE_REGEX);
+ // No output
+ EXPECT_TRUE(0 == output_stream.tellp());
+}
+
+TEST_F(GrpcToolTest, HelpCommand) {
+ // Test input "grpc_cli help"
+ std::stringstream output_stream;
+ const char* argv[] = {"grpc_cli", "help"};
+ // Exit with 1, print usage instruction in stderr
+ EXPECT_EXIT(GrpcToolMainLib(ArraySize(argv), argv,
+ std::bind(PrintStream, &output_stream,
+ std::placeholders::_1)),
+ ::testing::ExitedWithCode(1), USAGE_REGEX);
+ // No output
+ EXPECT_TRUE(0 == output_stream.tellp());
+}
+
+TEST_F(GrpcToolTest, CallCommand) {
+ // Test input "grpc_cli call Echo"
+ std::stringstream output_stream;
+ grpc::string server_address = server_address_.str();
+ const char* argv[] = {"grpc_cli", "call", server_address.c_str(), "Echo",
+ "message: 'Hello'"};
+
+ EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv,
+ std::bind(PrintStream, &output_stream,
+ std::placeholders::_1)));
+ // Expected output: "message: \"Hello\""
+ EXPECT_TRUE(NULL !=
+ strstr(output_stream.str().c_str(), "message: \"Hello\""));
+}
+
+TEST_F(GrpcToolTest, TooFewArguments) {
+ // Test input "grpc_cli call localhost:<port> Echo "message: 'Hello'"
+ std::stringstream output_stream;
+ grpc::string server_address = server_address_.str();
+ const char* argv[] = {"grpc_cli", "call", "Echo"};
+
+ // Exit with 1
+ EXPECT_EXIT(
+ GrpcToolMainLib(
+ ArraySize(argv), argv,
+ std::bind(PrintStream, &output_stream, std::placeholders::_1)),
+ ::testing::ExitedWithCode(1), ".*Wrong number of arguments for call.*");
+ // No output
+ EXPECT_TRUE(0 == output_stream.tellp());
+}
+
+TEST_F(GrpcToolTest, TooManyArguments) {
+ // Test input "grpc_cli call localhost:<port> Echo Echo "message: 'Hello'"
+ std::stringstream output_stream;
+ grpc::string server_address = server_address_.str();
+ const char* argv[] = {"grpc_cli", "call", server_address.c_str(),
+ "Echo", "Echo", "message: 'Hello'"};
+
+ // Exit with 1
+ EXPECT_EXIT(
+ GrpcToolMainLib(
+ ArraySize(argv), argv,
+ std::bind(PrintStream, &output_stream, std::placeholders::_1)),
+ ::testing::ExitedWithCode(1), ".*Wrong number of arguments for call.*");
+ // No output
+ EXPECT_TRUE(0 == output_stream.tellp());
+}
+
+} // namespace testing
+} // namespace grpc
+
+int main(int argc, char** argv) {
+ grpc_test_init(argc, argv);
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+ return RUN_ALL_TESTS();
+}