aboutsummaryrefslogtreecommitdiffhomepage
path: root/test
diff options
context:
space:
mode:
authorGravatar Vijay Pai <vpai@google.com>2016-08-25 14:49:18 -0700
committerGravatar Vijay Pai <vpai@google.com>2016-08-25 14:49:18 -0700
commitac4113bef128abd57cc166a4921063b0b4c5278d (patch)
treef5b0b4a72310252d86c502659283c6ad76a47539 /test
parent326d939873eb96c68d3ebb859c12b4bf022dadc6 (diff)
parentc931305c398e7e87013da52a5c2697d0fb14ad7f (diff)
Merge branch 'master' into fc_unary
Diffstat (limited to 'test')
-rw-r--r--test/core/bad_client/bad_client.c2
-rw-r--r--test/core/census/resource_test.c52
-rw-r--r--test/core/end2end/cq_verifier.c42
-rw-r--r--test/core/end2end/fixtures/h2_sockpair+trace.c4
-rw-r--r--test/core/end2end/fixtures/h2_sockpair.c4
-rw-r--r--test/core/end2end/fixtures/h2_sockpair_1byte.c4
-rw-r--r--test/core/end2end/fuzzers/api_fuzzer.c2
-rw-r--r--test/core/end2end/fuzzers/client_fuzzer.c2
-rw-r--r--test/core/end2end/fuzzers/server_fuzzer.c2
-rw-r--r--test/cpp/end2end/filter_end2end_test.cc353
-rw-r--r--test/cpp/qps/client.h21
-rw-r--r--test/cpp/qps/client_sync.cc31
-rw-r--r--test/cpp/qps/driver.cc11
-rw-r--r--test/cpp/qps/server_async.cc10
-rw-r--r--test/cpp/util/cli_credentials.cc63
-rw-r--r--test/cpp/util/cli_credentials.h53
-rw-r--r--test/cpp/util/config_grpc_cli.h85
-rw-r--r--test/cpp/util/grpc_cli.cc194
-rw-r--r--test/cpp/util/grpc_tool.cc365
-rw-r--r--test/cpp/util/grpc_tool.h54
-rw-r--r--test/cpp/util/grpc_tool_test.cc227
-rw-r--r--test/cpp/util/proto_file_parser.cc201
-rw-r--r--test/cpp/util/proto_file_parser.h65
-rw-r--r--test/cpp/util/proto_reflection_descriptor_database.cc2
-rw-r--r--test/distrib/csharp/DistribTest/App.config14
-rw-r--r--test/distrib/csharp/DistribTest/DistribTest.csproj54
-rw-r--r--test/distrib/csharp/DistribTest/packages.config8
-rw-r--r--test/distrib/python/distribtest.py4
28 files changed, 1534 insertions, 395 deletions
diff --git a/test/core/bad_client/bad_client.c b/test/core/bad_client/bad_client.c
index 24ee3387a0..be88d4a69a 100644
--- a/test/core/bad_client/bad_client.c
+++ b/test/core/bad_client/bad_client.c
@@ -130,7 +130,7 @@ void grpc_run_bad_client_test(
grpc_server_start(a.server);
transport = grpc_create_chttp2_transport(&exec_ctx, NULL, sfd.server, 0);
server_setup_transport(&a, transport);
- grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
+ grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL);
grpc_exec_ctx_finish(&exec_ctx);
/* Bind everything into the same pollset */
diff --git a/test/core/census/resource_test.c b/test/core/census/resource_test.c
index e1556d7d7b..f0e7039615 100644
--- a/test/core/census/resource_test.c
+++ b/test/core/census/resource_test.c
@@ -95,15 +95,13 @@ static void test_define_single_resource(const char *file, const char *name,
}
// Try deleting various resources (both those that exist and those that don't).
-static void test_delete_resource() {
+static void test_delete_resource(const char *minimal_good, const char *full) {
initialize_resources();
// Try deleting resource before any are defined.
census_delete_resource(0);
// Create and check a couple of resources.
- int32_t rid1 = define_resource_from_file(
- "test/core/census/data/resource_minimal_good.pb");
- int32_t rid2 =
- define_resource_from_file("test/core/census/data/resource_full.pb");
+ int32_t rid1 = define_resource_from_file(minimal_good);
+ int32_t rid2 = define_resource_from_file(full);
GPR_ASSERT(rid1 >= 0 && rid2 >= 0 && rid1 != rid2);
int32_t rid3 = census_resource_id("minimal_good");
int32_t rid4 = census_resource_id("full_resource");
@@ -117,8 +115,7 @@ static void test_delete_resource() {
rid3 = census_resource_id("minimal_good");
GPR_ASSERT(rid3 < 0);
// Check that re-adding works.
- rid1 = define_resource_from_file(
- "test/core/census/data/resource_minimal_good.pb");
+ rid1 = define_resource_from_file(minimal_good);
GPR_ASSERT(rid1 >= 0);
rid3 = census_resource_id("minimal_good");
GPR_ASSERT(rid1 == rid3);
@@ -136,22 +133,37 @@ static void test_base_resources() {
}
int main(int argc, char **argv) {
+ const char *resource_empty_name_pb, *resource_full_pb,
+ *resource_minimal_good_pb, *resource_no_name_pb,
+ *resource_no_numerator_pb, *resource_no_unit_pb;
+ if (argc == 7) {
+ resource_empty_name_pb = argv[1];
+ resource_full_pb = argv[2];
+ resource_minimal_good_pb = argv[3];
+ resource_no_name_pb = argv[4];
+ resource_no_numerator_pb = argv[5];
+ resource_no_unit_pb = argv[6];
+ } else {
+ GPR_ASSERT(argc == 1);
+ resource_empty_name_pb = "test/core/census/data/resource_empty_name.pb";
+ resource_full_pb = "test/core/census/data/resource_full.pb";
+ resource_minimal_good_pb = "test/core/census/data/resource_minimal_good.pb";
+ resource_no_name_pb = "test/core/census/data/resource_no_name.pb";
+ resource_no_numerator_pb = "test/core/census/data/resource_no_numerator.pb";
+ resource_no_unit_pb = "test/core/census/data/resource_no_unit.pb";
+ }
grpc_test_init(argc, argv);
test_enable_disable();
test_empty_definition();
- test_define_single_resource("test/core/census/data/resource_minimal_good.pb",
- "minimal_good", true);
- test_define_single_resource("test/core/census/data/resource_full.pb",
- "full_resource", true);
- test_define_single_resource("test/core/census/data/resource_no_name.pb",
- "resource_no_name", false);
- test_define_single_resource("test/core/census/data/resource_no_numerator.pb",
- "resource_no_numerator", false);
- test_define_single_resource("test/core/census/data/resource_no_unit.pb",
- "resource_no_unit", false);
- test_define_single_resource("test/core/census/data/resource_empty_name.pb",
- "resource_empty_name", false);
- test_delete_resource();
+ test_define_single_resource(resource_minimal_good_pb, "minimal_good", true);
+ test_define_single_resource(resource_full_pb, "full_resource", true);
+ test_define_single_resource(resource_no_name_pb, "resource_no_name", false);
+ test_define_single_resource(resource_no_numerator_pb, "resource_no_numerator",
+ false);
+ test_define_single_resource(resource_no_unit_pb, "resource_no_unit", false);
+ test_define_single_resource(resource_empty_name_pb, "resource_empty_name",
+ false);
+ test_delete_resource(resource_minimal_good_pb, resource_full_pb);
test_base_resources();
return 0;
}
diff --git a/test/core/end2end/cq_verifier.c b/test/core/end2end/cq_verifier.c
index b77568058c..5331049e89 100644
--- a/test/core/end2end/cq_verifier.c
+++ b/test/core/end2end/cq_verifier.c
@@ -61,7 +61,6 @@ typedef struct metadata {
list to detail other expectations */
typedef struct expectation {
struct expectation *next;
- struct expectation *prev;
grpc_completion_type type;
void *tag;
int success;
@@ -71,17 +70,14 @@ typedef struct expectation {
struct cq_verifier {
/* bound completion queue */
grpc_completion_queue *cq;
- /* the root/sentinal expectation */
- expectation expect;
+ /* start of expectation list */
+ expectation *first_expectation;
};
cq_verifier *cq_verifier_create(grpc_completion_queue *cq) {
cq_verifier *v = gpr_malloc(sizeof(cq_verifier));
- v->expect.type = ROOT_EXPECTATION;
- v->expect.tag = NULL;
- v->expect.next = &v->expect;
- v->expect.prev = &v->expect;
v->cq = cq;
+ v->first_expectation = NULL;
return v;
}
@@ -198,7 +194,7 @@ static void expectation_to_strvec(gpr_strvec *buf, expectation *e) {
static void expectations_to_strvec(gpr_strvec *buf, cq_verifier *v) {
expectation *e;
- for (e = v->expect.next; e != &v->expect; e = e->next) {
+ for (e = v->first_expectation; e != NULL; e = e->next) {
expectation_to_strvec(buf, e);
gpr_strvec_add(buf, gpr_strdup("\n"));
}
@@ -226,30 +222,32 @@ void cq_verify(cq_verifier *v) {
gpr_strvec_init(&have_tags);
- while (v->expect.next != &v->expect) {
+ while (v->first_expectation != NULL) {
ev = grpc_completion_queue_next(v->cq, deadline, NULL);
if (ev.type == GRPC_QUEUE_TIMEOUT) {
fail_no_event_received(v);
break;
}
- for (e = v->expect.next; e != &v->expect; e = e->next) {
+ expectation *prev = NULL;
+ for (e = v->first_expectation; e != NULL; e = e->next) {
gpr_asprintf(&s, " %p", e->tag);
gpr_strvec_add(&have_tags, s);
if (e->tag == ev.tag) {
verify_matches(e, &ev);
- e->next->prev = e->prev;
- e->prev->next = e->next;
+ if (e == v->first_expectation) v->first_expectation = e->next;
+ if (prev != NULL) prev->next = e->next;
gpr_free(e);
break;
}
+ prev = e;
}
- if (e == &v->expect) {
+ if (e == NULL) {
s = grpc_event_string(&ev);
- gpr_log(GPR_ERROR, "event not found: %s", s);
+ gpr_log(GPR_ERROR, "cq returned unexpected event: %s", s);
gpr_free(s);
s = gpr_strvec_flatten(&have_tags, NULL);
- gpr_log(GPR_ERROR, "have tags:%s", s);
+ gpr_log(GPR_ERROR, "expected tags:%s", s);
gpr_free(s);
gpr_strvec_destroy(&have_tags);
abort();
@@ -265,7 +263,7 @@ void cq_verify_empty_timeout(cq_verifier *v, int timeout_sec) {
gpr_time_from_seconds(timeout_sec, GPR_TIMESPAN));
grpc_event ev;
- GPR_ASSERT(v->expect.next == &v->expect && "expectation queue must be empty");
+ GPR_ASSERT(v->first_expectation == NULL && "expectation queue must be empty");
ev = grpc_completion_queue_next(v->cq, deadline, NULL);
if (ev.type != GRPC_QUEUE_TIMEOUT) {
@@ -278,16 +276,16 @@ void cq_verify_empty_timeout(cq_verifier *v, int timeout_sec) {
void cq_verify_empty(cq_verifier *v) { cq_verify_empty_timeout(v, 1); }
-static expectation *add(cq_verifier *v, grpc_completion_type type, void *tag) {
+static void add(cq_verifier *v, grpc_completion_type type, void *tag,
+ bool success) {
expectation *e = gpr_malloc(sizeof(expectation));
e->type = type;
e->tag = tag;
- e->next = &v->expect;
- e->prev = e->next->prev;
- e->next->prev = e->prev->next = e;
- return e;
+ e->success = success;
+ e->next = v->first_expectation;
+ v->first_expectation = e;
}
void cq_expect_completion(cq_verifier *v, void *tag, bool success) {
- add(v, GRPC_OP_COMPLETE, tag)->success = success;
+ add(v, GRPC_OP_COMPLETE, tag, success);
}
diff --git a/test/core/end2end/fixtures/h2_sockpair+trace.c b/test/core/end2end/fixtures/h2_sockpair+trace.c
index 6b0769b608..b8a5257ab2 100644
--- a/test/core/end2end/fixtures/h2_sockpair+trace.c
+++ b/test/core/end2end/fixtures/h2_sockpair+trace.c
@@ -108,7 +108,7 @@ static void chttp2_init_client_socketpair(grpc_end2end_test_fixture *f,
grpc_create_chttp2_transport(&exec_ctx, client_args, sfd->client, 1);
client_setup_transport(&exec_ctx, &cs, transport);
GPR_ASSERT(f->client);
- grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
+ grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL);
grpc_exec_ctx_finish(&exec_ctx);
}
@@ -124,7 +124,7 @@ static void chttp2_init_server_socketpair(grpc_end2end_test_fixture *f,
transport =
grpc_create_chttp2_transport(&exec_ctx, server_args, sfd->server, 0);
server_setup_transport(f, transport);
- grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
+ grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL);
grpc_exec_ctx_finish(&exec_ctx);
}
diff --git a/test/core/end2end/fixtures/h2_sockpair.c b/test/core/end2end/fixtures/h2_sockpair.c
index 7be88f8a68..a57990d6e7 100644
--- a/test/core/end2end/fixtures/h2_sockpair.c
+++ b/test/core/end2end/fixtures/h2_sockpair.c
@@ -107,7 +107,7 @@ static void chttp2_init_client_socketpair(grpc_end2end_test_fixture *f,
grpc_create_chttp2_transport(&exec_ctx, client_args, sfd->client, 1);
client_setup_transport(&exec_ctx, &cs, transport);
GPR_ASSERT(f->client);
- grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
+ grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL);
grpc_exec_ctx_finish(&exec_ctx);
}
@@ -123,7 +123,7 @@ static void chttp2_init_server_socketpair(grpc_end2end_test_fixture *f,
transport =
grpc_create_chttp2_transport(&exec_ctx, server_args, sfd->server, 0);
server_setup_transport(f, transport);
- grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
+ grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL);
grpc_exec_ctx_finish(&exec_ctx);
}
diff --git a/test/core/end2end/fixtures/h2_sockpair_1byte.c b/test/core/end2end/fixtures/h2_sockpair_1byte.c
index 166654bcbf..50aac8045a 100644
--- a/test/core/end2end/fixtures/h2_sockpair_1byte.c
+++ b/test/core/end2end/fixtures/h2_sockpair_1byte.c
@@ -107,7 +107,7 @@ static void chttp2_init_client_socketpair(grpc_end2end_test_fixture *f,
grpc_create_chttp2_transport(&exec_ctx, client_args, sfd->client, 1);
client_setup_transport(&exec_ctx, &cs, transport);
GPR_ASSERT(f->client);
- grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
+ grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL);
grpc_exec_ctx_finish(&exec_ctx);
}
@@ -123,7 +123,7 @@ static void chttp2_init_server_socketpair(grpc_end2end_test_fixture *f,
transport =
grpc_create_chttp2_transport(&exec_ctx, server_args, sfd->server, 0);
server_setup_transport(f, transport);
- grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
+ grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL);
grpc_exec_ctx_finish(&exec_ctx);
}
diff --git a/test/core/end2end/fuzzers/api_fuzzer.c b/test/core/end2end/fuzzers/api_fuzzer.c
index 13b8bf7561..96ea82d95e 100644
--- a/test/core/end2end/fuzzers/api_fuzzer.c
+++ b/test/core/end2end/fuzzers/api_fuzzer.c
@@ -258,7 +258,7 @@ static void do_connect(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
grpc_transport *transport =
grpc_create_chttp2_transport(exec_ctx, NULL, server, 0);
grpc_server_setup_transport(exec_ctx, g_server, transport, NULL, NULL);
- grpc_chttp2_transport_start_reading(exec_ctx, transport, NULL, 0);
+ grpc_chttp2_transport_start_reading(exec_ctx, transport, NULL);
grpc_exec_ctx_sched(exec_ctx, fc->closure, GRPC_ERROR_NONE, NULL);
} else {
diff --git a/test/core/end2end/fuzzers/client_fuzzer.c b/test/core/end2end/fuzzers/client_fuzzer.c
index 79b23d7856..00e650a30b 100644
--- a/test/core/end2end/fuzzers/client_fuzzer.c
+++ b/test/core/end2end/fuzzers/client_fuzzer.c
@@ -63,7 +63,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
grpc_completion_queue *cq = grpc_completion_queue_create(NULL);
grpc_transport *transport =
grpc_create_chttp2_transport(&exec_ctx, NULL, mock_endpoint, 1);
- grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
+ grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL);
grpc_channel *channel = grpc_channel_create(
&exec_ctx, "test-target", NULL, GRPC_CLIENT_DIRECT_CHANNEL, transport);
diff --git a/test/core/end2end/fuzzers/server_fuzzer.c b/test/core/end2end/fuzzers/server_fuzzer.c
index 80f568ac92..79eaad70c5 100644
--- a/test/core/end2end/fuzzers/server_fuzzer.c
+++ b/test/core/end2end/fuzzers/server_fuzzer.c
@@ -71,7 +71,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
grpc_transport *transport =
grpc_create_chttp2_transport(&exec_ctx, NULL, mock_endpoint, 0);
grpc_server_setup_transport(&exec_ctx, server, transport, NULL, NULL);
- grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL, 0);
+ grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL);
grpc_call *call1 = NULL;
grpc_call_details call_details1;
diff --git a/test/cpp/end2end/filter_end2end_test.cc b/test/cpp/end2end/filter_end2end_test.cc
new file mode 100644
index 0000000000..853720fd0d
--- /dev/null
+++ b/test/cpp/end2end/filter_end2end_test.cc
@@ -0,0 +1,353 @@
+/*
+ *
+ * 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 <memory>
+#include <mutex>
+
+#include <grpc++/channel.h>
+#include <grpc++/client_context.h>
+#include <grpc++/create_channel.h>
+#include <grpc++/generic/async_generic_service.h>
+#include <grpc++/generic/generic_stub.h>
+#include <grpc++/impl/codegen/proto_utils.h>
+#include <grpc++/server.h>
+#include <grpc++/server_builder.h>
+#include <grpc++/server_context.h>
+#include <grpc++/support/config.h>
+#include <grpc++/support/slice.h>
+#include <grpc/grpc.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
+#include <gtest/gtest.h>
+
+#include "src/cpp/common/channel_filter.h"
+#include "src/proto/grpc/testing/echo.grpc.pb.h"
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+#include "test/cpp/util/byte_buffer_proto_helper.h"
+
+using grpc::testing::EchoRequest;
+using grpc::testing::EchoResponse;
+using std::chrono::system_clock;
+
+namespace grpc {
+namespace testing {
+namespace {
+
+void* tag(int i) { return (void*)(intptr_t)i; }
+
+void verify_ok(CompletionQueue* cq, int i, bool expect_ok) {
+ bool ok;
+ void* got_tag;
+ EXPECT_TRUE(cq->Next(&got_tag, &ok));
+ EXPECT_EQ(expect_ok, ok);
+ EXPECT_EQ(tag(i), got_tag);
+}
+
+namespace {
+
+int global_num_connections = 0;
+int global_num_calls = 0;
+mutex global_mu;
+
+void IncrementConnectionCounter() {
+ unique_lock<mutex> lock(global_mu);
+ ++global_num_connections;
+}
+
+void ResetConnectionCounter() {
+ unique_lock<mutex> lock(global_mu);
+ global_num_connections = 0;
+}
+
+int GetConnectionCounterValue() {
+ unique_lock<mutex> lock(global_mu);
+ return global_num_connections;
+}
+
+void IncrementCallCounter() {
+ unique_lock<mutex> lock(global_mu);
+ ++global_num_calls;
+}
+
+void ResetCallCounter() {
+ unique_lock<mutex> lock(global_mu);
+ global_num_calls = 0;
+}
+
+int GetCallCounterValue() {
+ unique_lock<mutex> lock(global_mu);
+ return global_num_calls;
+}
+
+} // namespace
+
+class ChannelDataImpl : public ChannelData {
+ public:
+ ChannelDataImpl(const grpc_channel_args& args, const char* peer)
+ : ChannelData(args, peer) {
+ IncrementConnectionCounter();
+ }
+};
+
+class CallDataImpl : public CallData {
+ public:
+ explicit CallDataImpl(const ChannelDataImpl& channel_data)
+ : CallData(channel_data) {}
+
+ void StartTransportStreamOp(grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
+ TransportStreamOp* op) GRPC_OVERRIDE {
+ // Incrementing the counter could be done from the ctor, but we want
+ // to test that the individual methods are actually called correctly.
+ if (op->recv_initial_metadata() != nullptr) IncrementCallCounter();
+ grpc_call_next_op(exec_ctx, elem, op->op());
+ }
+};
+
+class FilterEnd2endTest : public ::testing::Test {
+ protected:
+ FilterEnd2endTest() : server_host_("localhost") {}
+
+ void SetUp() GRPC_OVERRIDE {
+ int port = grpc_pick_unused_port_or_die();
+ server_address_ << server_host_ << ":" << port;
+ // Setup server
+ ServerBuilder builder;
+ builder.AddListeningPort(server_address_.str(),
+ InsecureServerCredentials());
+ builder.RegisterAsyncGenericService(&generic_service_);
+ srv_cq_ = builder.AddCompletionQueue();
+ server_ = builder.BuildAndStart();
+ }
+
+ void TearDown() GRPC_OVERRIDE {
+ server_->Shutdown();
+ void* ignored_tag;
+ bool ignored_ok;
+ cli_cq_.Shutdown();
+ srv_cq_->Shutdown();
+ while (cli_cq_.Next(&ignored_tag, &ignored_ok))
+ ;
+ while (srv_cq_->Next(&ignored_tag, &ignored_ok))
+ ;
+ }
+
+ void ResetStub() {
+ std::shared_ptr<Channel> channel =
+ CreateChannel(server_address_.str(), InsecureChannelCredentials());
+ generic_stub_.reset(new GenericStub(channel));
+ ResetConnectionCounter();
+ ResetCallCounter();
+ }
+
+ void server_ok(int i) { verify_ok(srv_cq_.get(), i, true); }
+ void client_ok(int i) { verify_ok(&cli_cq_, i, true); }
+ void server_fail(int i) { verify_ok(srv_cq_.get(), i, false); }
+ void client_fail(int i) { verify_ok(&cli_cq_, i, false); }
+
+ void SendRpc(int num_rpcs) {
+ const grpc::string kMethodName("/grpc.cpp.test.util.EchoTestService/Echo");
+ for (int i = 0; i < num_rpcs; i++) {
+ EchoRequest send_request;
+ EchoRequest recv_request;
+ EchoResponse send_response;
+ EchoResponse recv_response;
+ Status recv_status;
+
+ ClientContext cli_ctx;
+ GenericServerContext srv_ctx;
+ GenericServerAsyncReaderWriter stream(&srv_ctx);
+
+ // The string needs to be long enough to test heap-based slice.
+ send_request.set_message("Hello world. Hello world. Hello world.");
+ std::unique_ptr<GenericClientAsyncReaderWriter> call =
+ generic_stub_->Call(&cli_ctx, kMethodName, &cli_cq_, tag(1));
+ client_ok(1);
+ std::unique_ptr<ByteBuffer> send_buffer =
+ SerializeToByteBuffer(&send_request);
+ call->Write(*send_buffer, tag(2));
+ // Send ByteBuffer can be destroyed after calling Write.
+ send_buffer.reset();
+ client_ok(2);
+ call->WritesDone(tag(3));
+ client_ok(3);
+
+ generic_service_.RequestCall(&srv_ctx, &stream, srv_cq_.get(),
+ srv_cq_.get(), tag(4));
+
+ verify_ok(srv_cq_.get(), 4, true);
+ EXPECT_EQ(server_host_, srv_ctx.host().substr(0, server_host_.length()));
+ EXPECT_EQ(kMethodName, srv_ctx.method());
+ ByteBuffer recv_buffer;
+ stream.Read(&recv_buffer, tag(5));
+ server_ok(5);
+ EXPECT_TRUE(ParseFromByteBuffer(&recv_buffer, &recv_request));
+ EXPECT_EQ(send_request.message(), recv_request.message());
+
+ send_response.set_message(recv_request.message());
+ send_buffer = SerializeToByteBuffer(&send_response);
+ stream.Write(*send_buffer, tag(6));
+ send_buffer.reset();
+ server_ok(6);
+
+ stream.Finish(Status::OK, tag(7));
+ server_ok(7);
+
+ recv_buffer.Clear();
+ call->Read(&recv_buffer, tag(8));
+ client_ok(8);
+ EXPECT_TRUE(ParseFromByteBuffer(&recv_buffer, &recv_response));
+
+ call->Finish(&recv_status, tag(9));
+ client_ok(9);
+
+ EXPECT_EQ(send_response.message(), recv_response.message());
+ EXPECT_TRUE(recv_status.ok());
+ }
+ }
+
+ CompletionQueue cli_cq_;
+ std::unique_ptr<ServerCompletionQueue> srv_cq_;
+ std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_;
+ std::unique_ptr<grpc::GenericStub> generic_stub_;
+ std::unique_ptr<Server> server_;
+ AsyncGenericService generic_service_;
+ const grpc::string server_host_;
+ std::ostringstream server_address_;
+};
+
+TEST_F(FilterEnd2endTest, SimpleRpc) {
+ ResetStub();
+ EXPECT_EQ(0, GetConnectionCounterValue());
+ EXPECT_EQ(0, GetCallCounterValue());
+ SendRpc(1);
+ EXPECT_EQ(1, GetConnectionCounterValue());
+ EXPECT_EQ(1, GetCallCounterValue());
+}
+
+TEST_F(FilterEnd2endTest, SequentialRpcs) {
+ ResetStub();
+ EXPECT_EQ(0, GetConnectionCounterValue());
+ EXPECT_EQ(0, GetCallCounterValue());
+ SendRpc(10);
+ EXPECT_EQ(1, GetConnectionCounterValue());
+ EXPECT_EQ(10, GetCallCounterValue());
+}
+
+// One ping, one pong.
+TEST_F(FilterEnd2endTest, SimpleBidiStreaming) {
+ ResetStub();
+ EXPECT_EQ(0, GetConnectionCounterValue());
+ EXPECT_EQ(0, GetCallCounterValue());
+
+ const grpc::string kMethodName(
+ "/grpc.cpp.test.util.EchoTestService/BidiStream");
+ EchoRequest send_request;
+ EchoRequest recv_request;
+ EchoResponse send_response;
+ EchoResponse recv_response;
+ Status recv_status;
+ ClientContext cli_ctx;
+ GenericServerContext srv_ctx;
+ GenericServerAsyncReaderWriter srv_stream(&srv_ctx);
+
+ cli_ctx.set_compression_algorithm(GRPC_COMPRESS_GZIP);
+ send_request.set_message("Hello");
+ std::unique_ptr<GenericClientAsyncReaderWriter> cli_stream =
+ generic_stub_->Call(&cli_ctx, kMethodName, &cli_cq_, tag(1));
+ client_ok(1);
+
+ generic_service_.RequestCall(&srv_ctx, &srv_stream, srv_cq_.get(),
+ srv_cq_.get(), tag(2));
+
+ verify_ok(srv_cq_.get(), 2, true);
+ EXPECT_EQ(server_host_, srv_ctx.host().substr(0, server_host_.length()));
+ EXPECT_EQ(kMethodName, srv_ctx.method());
+
+ std::unique_ptr<ByteBuffer> send_buffer =
+ SerializeToByteBuffer(&send_request);
+ cli_stream->Write(*send_buffer, tag(3));
+ send_buffer.reset();
+ client_ok(3);
+
+ ByteBuffer recv_buffer;
+ srv_stream.Read(&recv_buffer, tag(4));
+ server_ok(4);
+ EXPECT_TRUE(ParseFromByteBuffer(&recv_buffer, &recv_request));
+ EXPECT_EQ(send_request.message(), recv_request.message());
+
+ send_response.set_message(recv_request.message());
+ send_buffer = SerializeToByteBuffer(&send_response);
+ srv_stream.Write(*send_buffer, tag(5));
+ send_buffer.reset();
+ server_ok(5);
+
+ cli_stream->Read(&recv_buffer, tag(6));
+ client_ok(6);
+ EXPECT_TRUE(ParseFromByteBuffer(&recv_buffer, &recv_response));
+ EXPECT_EQ(send_response.message(), recv_response.message());
+
+ cli_stream->WritesDone(tag(7));
+ client_ok(7);
+
+ srv_stream.Read(&recv_buffer, tag(8));
+ server_fail(8);
+
+ srv_stream.Finish(Status::OK, tag(9));
+ server_ok(9);
+
+ cli_stream->Finish(&recv_status, tag(10));
+ client_ok(10);
+
+ EXPECT_EQ(send_response.message(), recv_response.message());
+ EXPECT_TRUE(recv_status.ok());
+
+ EXPECT_EQ(1, GetCallCounterValue());
+ EXPECT_EQ(1, GetConnectionCounterValue());
+}
+
+void RegisterFilter() {
+ grpc::RegisterChannelFilter<ChannelDataImpl, CallDataImpl>(
+ "test-filter", GRPC_SERVER_CHANNEL, INT_MAX, nullptr);
+}
+
+} // namespace
+} // namespace testing
+} // namespace grpc
+
+int main(int argc, char** argv) {
+ grpc_test_init(argc, argv);
+ ::testing::InitGoogleTest(&argc, argv);
+ grpc::testing::RegisterFilter();
+ return RUN_ALL_TESTS();
+}
diff --git a/test/cpp/qps/client.h b/test/cpp/qps/client.h
index 4045e13460..fada4ba767 100644
--- a/test/cpp/qps/client.h
+++ b/test/cpp/qps/client.h
@@ -169,6 +169,7 @@ class Client {
// Must call AwaitThreadsCompletion before destructor to avoid a race
// between destructor and invocation of virtual ThreadFunc
void AwaitThreadsCompletion() {
+ gpr_atm_rel_store(&thread_pool_done_, static_cast<gpr_atm>(true));
DestroyMultithreading();
std::unique_lock<std::mutex> g(thread_completion_mu_);
while (threads_remaining_ != 0) {
@@ -178,8 +179,10 @@ class Client {
protected:
bool closed_loop_;
+ gpr_atm thread_pool_done_;
void StartThreads(size_t num_threads) {
+ gpr_atm_rel_store(&thread_pool_done_, static_cast<gpr_atm>(false));
threads_remaining_ = num_threads;
for (size_t i = 0; i < num_threads; i++) {
threads_.emplace_back(new Thread(this, i));
@@ -241,18 +244,9 @@ class Client {
class Thread {
public:
Thread(Client* client, size_t idx)
- : done_(false),
- client_(client),
- idx_(idx),
- impl_(&Thread::ThreadFunc, this) {}
+ : client_(client), idx_(idx), impl_(&Thread::ThreadFunc, this) {}
- ~Thread() {
- {
- std::lock_guard<std::mutex> g(mu_);
- done_ = true;
- }
- impl_.join();
- }
+ ~Thread() { impl_.join(); }
void BeginSwap(Histogram* n) {
std::lock_guard<std::mutex> g(mu_);
@@ -282,9 +276,9 @@ class Client {
}
if (!thread_still_ok) {
gpr_log(GPR_ERROR, "Finishing client thread due to RPC error");
- done_ = true;
}
- if (done_) {
+ if (!thread_still_ok ||
+ static_cast<bool>(gpr_atm_acq_load(&client_->thread_pool_done_))) {
client_->CompleteThread();
return;
}
@@ -292,7 +286,6 @@ class Client {
}
std::mutex mu_;
- bool done_;
Histogram histogram_;
Client* client_;
const size_t idx_;
diff --git a/test/cpp/qps/client_sync.cc b/test/cpp/qps/client_sync.cc
index 25c7823553..8062424a1f 100644
--- a/test/cpp/qps/client_sync.cc
+++ b/test/cpp/qps/client_sync.cc
@@ -79,10 +79,29 @@ class SynchronousClient
virtual ~SynchronousClient(){};
protected:
- void WaitToIssue(int thread_idx) {
+ // WaitToIssue returns false if we realize that we need to break out
+ bool WaitToIssue(int thread_idx) {
if (!closed_loop_) {
- gpr_sleep_until(NextIssueTime(thread_idx));
+ const gpr_timespec next_issue_time = NextIssueTime(thread_idx);
+ // Avoid sleeping for too long continuously because we might
+ // need to terminate before then. This is an issue since
+ // exponential distribution can occasionally produce bad outliers
+ while (true) {
+ const gpr_timespec one_sec_delay =
+ gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
+ gpr_time_from_seconds(1, GPR_TIMESPAN));
+ if (gpr_time_cmp(next_issue_time, one_sec_delay) <= 0) {
+ gpr_sleep_until(next_issue_time);
+ return true;
+ } else {
+ gpr_sleep_until(one_sec_delay);
+ if (gpr_atm_acq_load(&thread_pool_done_) != static_cast<gpr_atm>(0)) {
+ return false;
+ }
+ }
+ }
}
+ return true;
}
size_t num_threads_;
@@ -101,7 +120,9 @@ class SynchronousUnaryClient GRPC_FINAL : public SynchronousClient {
~SynchronousUnaryClient() {}
bool ThreadFunc(HistogramEntry* entry, size_t thread_idx) GRPC_OVERRIDE {
- WaitToIssue(thread_idx);
+ if (!WaitToIssue(thread_idx)) {
+ return true;
+ }
auto* stub = channels_[thread_idx % channels_.size()].get_stub();
double start = UsageTimer::Now();
GPR_TIMER_SCOPE("SynchronousUnaryClient::ThreadFunc", 0);
@@ -144,7 +165,9 @@ class SynchronousStreamingClient GRPC_FINAL : public SynchronousClient {
}
bool ThreadFunc(HistogramEntry* entry, size_t thread_idx) GRPC_OVERRIDE {
- WaitToIssue(thread_idx);
+ if (!WaitToIssue(thread_idx)) {
+ return true;
+ }
GPR_TIMER_SCOPE("SynchronousStreamingClient::ThreadFunc", 0);
double start = UsageTimer::Now();
if (stream_[thread_idx]->Write(request_) &&
diff --git a/test/cpp/qps/driver.cc b/test/cpp/qps/driver.cc
index 2aeaea51f2..93f9271553 100644
--- a/test/cpp/qps/driver.cc
+++ b/test/cpp/qps/driver.cc
@@ -310,6 +310,7 @@ std::unique_ptr<ScenarioResult> RunScenario(
// clients is array rather than std::vector to avoid gcc-4.4 issues
// where class contained in std::vector must have a copy constructor
auto* clients = new ClientData[num_clients];
+ size_t channels_allocated = 0;
for (size_t i = 0; i < num_clients; i++) {
const auto& worker = workers[i + num_servers];
gpr_log(GPR_INFO, "Starting client on %s (worker #%" PRIuPTR ")",
@@ -345,6 +346,16 @@ std::unique_ptr<ScenarioResult> RunScenario(
}
}
+ // Reduce channel count so that total channels specified is held regardless
+ // of the number of clients available
+ size_t num_channels =
+ (client_config.client_channels() - channels_allocated) /
+ (num_clients - i);
+ channels_allocated += num_channels;
+ gpr_log(GPR_DEBUG, "Client %" PRIdPTR " gets %" PRIdPTR " channels", i,
+ num_channels);
+ per_client_config.set_client_channels(num_channels);
+
ClientArgs args;
*args.mutable_setup() = per_client_config;
clients[i].stream =
diff --git a/test/cpp/qps/server_async.cc b/test/cpp/qps/server_async.cc
index dea8746331..082b4bc72f 100644
--- a/test/cpp/qps/server_async.cc
+++ b/test/cpp/qps/server_async.cc
@@ -108,14 +108,14 @@ class AsyncQpsServerTest : public Server {
auto request_unary =
std::bind(request_unary_function, &async_service_, _1, _2, _3,
srv_cqs_[j].get(), srv_cqs_[j].get(), _4);
- contexts_.push_front(
+ contexts_.emplace_back(
new ServerRpcContextUnaryImpl(request_unary, process_rpc_bound));
}
if (request_streaming_function) {
auto request_streaming =
std::bind(request_streaming_function, &async_service_, _1, _2,
srv_cqs_[j].get(), srv_cqs_[j].get(), _3);
- contexts_.push_front(new ServerRpcContextStreamingImpl(
+ contexts_.emplace_back(new ServerRpcContextStreamingImpl(
request_streaming, process_rpc_bound));
}
}
@@ -146,10 +146,6 @@ class AsyncQpsServerTest : public Server {
while ((*cq)->Next(&got_tag, &ok))
;
}
- while (!contexts_.empty()) {
- delete contexts_.front();
- contexts_.pop_front();
- }
}
private:
@@ -336,7 +332,7 @@ class AsyncQpsServerTest : public Server {
std::unique_ptr<grpc::Server> server_;
std::vector<std::unique_ptr<grpc::ServerCompletionQueue>> srv_cqs_;
ServiceType async_service_;
- std::forward_list<ServerRpcContext *> contexts_;
+ std::vector<std::unique_ptr<ServerRpcContext>> contexts_;
struct PerThreadShutdownState {
mutable std::mutex mutex;
diff --git a/test/cpp/util/cli_credentials.cc b/test/cpp/util/cli_credentials.cc
new file mode 100644
index 0000000000..8de9393e4d
--- /dev/null
+++ b/test/cpp/util/cli_credentials.cc
@@ -0,0 +1,63 @@
+/*
+ *
+ * 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 "test/cpp/util/cli_credentials.h"
+
+#include <gflags/gflags.h>
+
+DEFINE_bool(enable_ssl, false, "Whether to use ssl/tls.");
+DEFINE_bool(use_auth, false, "Whether to create default google credentials.");
+
+namespace grpc {
+namespace testing {
+
+std::shared_ptr<grpc::ChannelCredentials> CliCredentials::GetCredentials()
+ const {
+ if (!FLAGS_enable_ssl) {
+ return grpc::InsecureChannelCredentials();
+ } else {
+ if (FLAGS_use_auth) {
+ return grpc::GoogleDefaultCredentials();
+ } else {
+ return grpc::SslCredentials(grpc::SslCredentialsOptions());
+ }
+ }
+}
+
+const grpc::string CliCredentials::GetCredentialUsage() const {
+ return " --enable_ssl ; Set whether to use tls\n"
+ " --use_auth ; Set whether to create default google"
+ " credentials\n";
+}
+} // namespace testing
+} // namespace grpc
diff --git a/test/cpp/util/cli_credentials.h b/test/cpp/util/cli_credentials.h
new file mode 100644
index 0000000000..581b77a9c6
--- /dev/null
+++ b/test/cpp/util/cli_credentials.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifndef GRPC_TEST_CPP_UTIL_CLI_CREDENTIALS_H
+#define GRPC_TEST_CPP_UTIL_CLI_CREDENTIALS_H
+
+#include <grpc++/security/credentials.h>
+#include <grpc++/support/config.h>
+
+namespace grpc {
+namespace testing {
+
+class CliCredentials {
+ public:
+ virtual ~CliCredentials() {}
+ virtual std::shared_ptr<grpc::ChannelCredentials> GetCredentials() const;
+ virtual const grpc::string GetCredentialUsage() const;
+};
+
+} // namespace testing
+} // namespace grpc
+
+#endif // GRPC_TEST_CPP_UTIL_CLI_CREDENTIALS_H
diff --git a/test/cpp/util/config_grpc_cli.h b/test/cpp/util/config_grpc_cli.h
new file mode 100644
index 0000000000..ea8231aa26
--- /dev/null
+++ b/test/cpp/util/config_grpc_cli.h
@@ -0,0 +1,85 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#ifndef GRPC_TEST_CPP_UTIL_CONFIG_GRPC_CLI_H
+#define GRPC_TEST_CPP_UTIL_CONFIG_GRPC_CLI_H
+
+#include <grpc++/impl/codegen/config_protobuf.h>
+
+#ifndef GRPC_CUSTOM_DYNAMICMESSAGEFACTORY
+#include <google/protobuf/dynamic_message.h>
+#define GRPC_CUSTOM_DYNAMICMESSAGEFACTORY \
+ ::google::protobuf::DynamicMessageFactory
+#endif
+
+#ifndef GRPC_CUSTOM_DESCRIPTORPOOLDATABASE
+#include <google/protobuf/descriptor.h>
+#define GRPC_CUSTOM_DESCRIPTORPOOLDATABASE \
+ ::google::protobuf::DescriptorPoolDatabase
+#define GRPC_CUSTOM_MERGEDDESCRIPTORDATABASE \
+ ::google::protobuf::MergedDescriptorDatabase
+#endif
+
+#ifndef GRPC_CUSTOM_TEXTFORMAT
+#include <google/protobuf/text_format.h>
+#define GRPC_CUSTOM_TEXTFORMAT ::google::protobuf::TextFormat
+#endif
+
+#ifndef GRPC_CUSTOM_DISKSOURCETREE
+#include <google/protobuf/compiler/importer.h>
+#define GRPC_CUSTOM_DISKSOURCETREE ::google::protobuf::compiler::DiskSourceTree
+#define GRPC_CUSTOM_IMPORTER ::google::protobuf::compiler::Importer
+#define GRPC_CUSTOM_MULTIFILEERRORCOLLECTOR \
+ ::google::protobuf::compiler::MultiFileErrorCollector
+#endif
+
+namespace grpc {
+namespace protobuf {
+
+typedef GRPC_CUSTOM_DYNAMICMESSAGEFACTORY DynamicMessageFactory;
+
+typedef GRPC_CUSTOM_DESCRIPTORPOOLDATABASE DescriptorPoolDatabase;
+typedef GRPC_CUSTOM_MERGEDDESCRIPTORDATABASE MergedDescriptorDatabase;
+
+typedef GRPC_CUSTOM_TEXTFORMAT TextFormat;
+
+namespace compiler {
+typedef GRPC_CUSTOM_DISKSOURCETREE DiskSourceTree;
+typedef GRPC_CUSTOM_IMPORTER Importer;
+typedef GRPC_CUSTOM_MULTIFILEERRORCOLLECTOR MultiFileErrorCollector;
+} // namespace importer
+
+} // namespace protobuf
+} // namespace grpc
+
+#endif // GRPC_TEST_CPP_UTIL_CONFIG_GRPC_CLI_H
diff --git a/test/cpp/util/grpc_cli.cc b/test/cpp/util/grpc_cli.cc
index 53529da782..fe248601ee 100644
--- a/test/cpp/util/grpc_cli.cc
+++ b/test/cpp/util/grpc_cli.cc
@@ -33,12 +33,14 @@
/*
A command line tool to talk to a grpc server.
+ Run `grpc_cli help` command to see its usage information.
+
Example of talking to grpc interop server:
grpc_cli call localhost:50051 UnaryCall "response_size:10" \
--protofiles=src/proto/grpc/testing/test.proto --enable_ssl=false
Options:
- 1. --protofiles, use this flag to provide a proto file if the server does
+ 1. --protofiles, use this flag to provide proto files if the server does
does not have the reflection service.
2. --proto_path, if your proto file is not under current working directory,
use this flag to provide a search root. It should work similar to the
@@ -48,15 +50,17 @@
--metadata="MyHeaderKey1:Value1:MyHeaderKey2:Value2"
4. --enable_ssl, whether to use tls.
5. --use_auth, if set to true, attach a GoogleDefaultCredentials to the call
- 6. --input_binary_file, a file containing the serialized request. The file
- can be generated by calling something like:
+ 6. --infile, input filename (defaults to stdin)
+ 7. --outfile, output filename (defaults to stdout)
+ 8. --binary_input, use the serialized request as input. The serialized
+ request can be generated by calling something like:
protoc --proto_path=src/proto/grpc/testing/ \
--encode=grpc.testing.SimpleRequest \
src/proto/grpc/testing/messages.proto \
< input.txt > input.bin
If this is used and no proto file is provided in the argument list, the
method string has to be exact in the form of /package.service/method.
- 7. --output_binary_file, a file to write binary format response into, it can
+ 9. --binary_output, use binary format response as output, it can
be later decoded using protoc:
protoc --proto_path=src/proto/grpc/testing/ \
--decode=grpc.testing.SimpleResponse \
@@ -64,182 +68,34 @@
< output.bin > output.txt
*/
-#include <unistd.h>
#include <fstream>
+#include <functional>
#include <iostream>
-#include <sstream>
#include <gflags/gflags.h>
-#include <grpc++/channel.h>
-#include <grpc++/create_channel.h>
-#include <grpc++/security/credentials.h>
-#include <grpc++/support/string_ref.h>
-#include <grpc/grpc.h>
-
-#include "test/cpp/util/cli_call.h"
-#include "test/cpp/util/proto_file_parser.h"
-#include "test/cpp/util/string_ref_helper.h"
+#include <grpc++/support/config.h>
+#include "test/cpp/util/cli_credentials.h"
+#include "test/cpp/util/grpc_tool.h"
#include "test/cpp/util/test_config.h"
-DEFINE_bool(enable_ssl, true, "Whether to use ssl/tls.");
-DEFINE_bool(use_auth, false, "Whether to create default google credentials.");
-DEFINE_string(input_binary_file, "",
- "Path to input file containing serialized request.");
-DEFINE_string(output_binary_file, "",
- "Path to output file to write serialized response.");
-DEFINE_string(metadata, "",
- "Metadata to send to server, in the form of key1:val1:key2:val2");
-DEFINE_string(proto_path, ".", "Path to look for the proto file.");
-// TODO(zyc): support a list of input proto files
-DEFINE_string(protofiles, "", "Name of the proto file.");
-
-void ParseMetadataFlag(
- std::multimap<grpc::string, grpc::string>* client_metadata) {
- if (FLAGS_metadata.empty()) {
- return;
- }
- std::vector<grpc::string> fields;
- const char* delim = ":";
- size_t cur, next = -1;
- do {
- cur = next + 1;
- next = FLAGS_metadata.find_first_of(delim, cur);
- fields.push_back(FLAGS_metadata.substr(cur, next - cur));
- } while (next != grpc::string::npos);
- if (fields.size() % 2) {
- std::cout << "Failed to parse metadata flag" << std::endl;
- exit(1);
- }
- for (size_t i = 0; i < fields.size(); i += 2) {
- client_metadata->insert(
- std::pair<grpc::string, grpc::string>(fields[i], fields[i + 1]));
- }
-}
+DEFINE_string(outfile, "", "Output file (default is stdout)");
-template <typename T>
-void PrintMetadata(const T& m, const grpc::string& message) {
- if (m.empty()) {
- return;
- }
- std::cout << message << std::endl;
- grpc::string pair;
- for (typename T::const_iterator iter = m.begin(); iter != m.end(); ++iter) {
- pair.clear();
- pair.append(iter->first.data(), iter->first.size());
- pair.append(" : ");
- pair.append(iter->second.data(), iter->second.size());
- std::cout << pair << std::endl;
+static bool SimplePrint(const grpc::string& outfile,
+ const grpc::string& output) {
+ if (outfile.empty()) {
+ std::cout << output;
+ } else {
+ std::ofstream output_file(outfile, std::ios::trunc | std::ios::binary);
+ output_file << output;
+ output_file.close();
}
+ return true;
}
int main(int argc, char** argv) {
grpc::testing::InitTest(&argc, &argv, true);
- if (argc < 4 || grpc::string(argv[1]) != "call") {
- std::cout << "Usage: grpc_cli call server_host:port method_name "
- << "[proto file] [text format request] [<options>]" << std::endl;
- return 1;
- }
-
- grpc::string request_text;
- grpc::string server_address(argv[2]);
- grpc::string method_name(argv[3]);
- std::unique_ptr<grpc::testing::ProtoFileParser> parser;
- grpc::string serialized_request_proto;
-
- if (argc == 5) {
- request_text = argv[4];
- }
-
- std::shared_ptr<grpc::ChannelCredentials> creds;
- if (!FLAGS_enable_ssl) {
- creds = grpc::InsecureChannelCredentials();
- } else {
- if (FLAGS_use_auth) {
- creds = grpc::GoogleDefaultCredentials();
- } else {
- creds = grpc::SslCredentials(grpc::SslCredentialsOptions());
- }
- }
- std::shared_ptr<grpc::Channel> channel =
- grpc::CreateChannel(server_address, creds);
-
- if (request_text.empty() && FLAGS_input_binary_file.empty()) {
- if (isatty(STDIN_FILENO)) {
- std::cout << "reading request message from stdin..." << std::endl;
- }
- std::stringstream input_stream;
- input_stream << std::cin.rdbuf();
- request_text = input_stream.str();
- }
-
- if (!request_text.empty()) {
- if (!FLAGS_protofiles.empty()) {
- parser.reset(new grpc::testing::ProtoFileParser(
- FLAGS_proto_path, FLAGS_protofiles, method_name));
- } else {
- parser.reset(new grpc::testing::ProtoFileParser(channel, method_name));
- }
- method_name = parser->GetFullMethodName();
- if (parser->HasError()) {
- return 1;
- }
-
- if (!FLAGS_input_binary_file.empty()) {
- std::cout
- << "warning: request given in argv, ignoring --input_binary_file"
- << std::endl;
- }
- }
-
- if (parser) {
- serialized_request_proto =
- parser->GetSerializedProto(request_text, true /* is_request */);
- if (parser->HasError()) {
- return 1;
- }
- } else if (!FLAGS_input_binary_file.empty()) {
- std::ifstream input_file(FLAGS_input_binary_file,
- std::ios::in | std::ios::binary);
- std::stringstream input_stream;
- input_stream << input_file.rdbuf();
- serialized_request_proto = input_stream.str();
- }
- std::cout << "connecting to " << server_address << std::endl;
-
- grpc::string serialized_response_proto;
- std::multimap<grpc::string, grpc::string> client_metadata;
- std::multimap<grpc::string_ref, grpc::string_ref> server_initial_metadata,
- server_trailing_metadata;
- ParseMetadataFlag(&client_metadata);
- PrintMetadata(client_metadata, "Sending client initial metadata:");
- grpc::Status s = grpc::testing::CliCall::Call(
- channel, method_name, serialized_request_proto,
- &serialized_response_proto, client_metadata, &server_initial_metadata,
- &server_trailing_metadata);
- PrintMetadata(server_initial_metadata,
- "Received initial metadata from server:");
- PrintMetadata(server_trailing_metadata,
- "Received trailing metadata from server:");
- if (s.ok()) {
- std::cout << "Rpc succeeded with OK status" << std::endl;
- if (parser) {
- grpc::string response_text = parser->GetTextFormat(
- serialized_response_proto, false /* is_request */);
- if (parser->HasError()) {
- return 1;
- }
- std::cout << "Response: \n " << response_text << std::endl;
- }
- if (!FLAGS_output_binary_file.empty()) {
- std::ofstream output_file(FLAGS_output_binary_file,
- std::ios::trunc | std::ios::binary);
- output_file << serialized_response_proto;
- }
- } else {
- std::cout << "Rpc failed with status code " << s.error_code()
- << ", error message: " << s.error_message() << std::endl;
- }
-
- return 0;
+ return grpc::testing::GrpcToolMainLib(
+ argc, (const char**)argv, grpc::testing::CliCredentials(),
+ std::bind(SimplePrint, FLAGS_outfile, std::placeholders::_1));
}
diff --git a/test/cpp/util/grpc_tool.cc b/test/cpp/util/grpc_tool.cc
new file mode 100644
index 0000000000..f06053ca23
--- /dev/null
+++ b/test/cpp/util/grpc_tool.cc
@@ -0,0 +1,365 @@
+/*
+ *
+ * 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 "test/cpp/util/grpc_tool.h"
+
+#include <unistd.h>
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <sstream>
+#include <string>
+
+#include <gflags/gflags.h>
+#include <grpc++/channel.h>
+#include <grpc++/create_channel.h>
+#include <grpc++/grpc++.h>
+#include <grpc++/security/credentials.h>
+#include <grpc++/support/string_ref.h>
+#include <grpc/grpc.h>
+
+#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/test_config.h"
+
+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");
+DEFINE_string(proto_path, ".", "Path to look for the proto file.");
+DEFINE_string(proto_file, "", "Name of the proto file.");
+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 {
+ public:
+ explicit GrpcTool();
+ virtual ~GrpcTool() {}
+
+ bool Help(int argc, const char** argv, CliCredentials cred,
+ GrpcToolOutputCallback callback);
+ bool CallMethod(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
+ // callback);
+ // bool ParseMessage(int argc, const char** argv, GrpcToolOutputCallback
+ // callback);
+ // bool ToText(int argc, const char** argv, GrpcToolOutputCallback callback);
+ // bool ToBinary(int argc, const char** argv, GrpcToolOutputCallback
+ // callback);
+
+ void SetPrintCommandMode(int exit_status) {
+ print_command_usage_ = true;
+ usage_exit_status_ = exit_status;
+ }
+
+ private:
+ void CommandUsage(const grpc::string& usage) const;
+ bool print_command_usage_;
+ int usage_exit_status_;
+ const grpc::string cred_usage_;
+};
+
+template <typename T>
+std::function<bool(GrpcTool*, int, const char**, const CliCredentials,
+ GrpcToolOutputCallback)>
+BindWith5Args(T&& func) {
+ return std::bind(std::forward<T>(func), std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3,
+ std::placeholders::_4, std::placeholders::_5);
+}
+
+template <typename T>
+size_t ArraySize(T& a) {
+ return ((sizeof(a) / sizeof(*(a))) /
+ static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))));
+}
+
+void ParseMetadataFlag(
+ std::multimap<grpc::string, grpc::string>* client_metadata) {
+ if (FLAGS_metadata.empty()) {
+ return;
+ }
+ std::vector<grpc::string> fields;
+ const char* delim = ":";
+ size_t cur, next = -1;
+ do {
+ cur = next + 1;
+ next = FLAGS_metadata.find_first_of(delim, cur);
+ fields.push_back(FLAGS_metadata.substr(cur, next - cur));
+ } while (next != grpc::string::npos);
+ if (fields.size() % 2) {
+ fprintf(stderr, "Failed to parse metadata flag.\n");
+ exit(1);
+ }
+ for (size_t i = 0; i < fields.size(); i += 2) {
+ client_metadata->insert(
+ std::pair<grpc::string, grpc::string>(fields[i], fields[i + 1]));
+ }
+}
+
+template <typename T>
+void PrintMetadata(const T& m, const grpc::string& message) {
+ if (m.empty()) {
+ return;
+ }
+ fprintf(stderr, "%s\n", message.c_str());
+ grpc::string pair;
+ for (typename T::const_iterator iter = m.begin(); iter != m.end(); ++iter) {
+ pair.clear();
+ pair.append(iter->first.data(), iter->first.size());
+ pair.append(" : ");
+ pair.append(iter->second.data(), iter->second.size());
+ fprintf(stderr, "%s\n", pair.c_str());
+ }
+}
+
+struct Command {
+ const char* command;
+ std::function<bool(GrpcTool*, int, const char**, const CliCredentials,
+ GrpcToolOutputCallback)>
+ function;
+ int min_args;
+ int max_args;
+};
+
+const Command ops[] = {
+ {"help", BindWith5Args(&GrpcTool::Help), 0, INT_MAX},
+ // {"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},
+ // {"totext", BindWith5Args(&GrpcTool::ToText), 2, 3},
+ // {"tobinary", BindWith5Args(&GrpcTool::ToBinary), 2, 3},
+};
+
+void Usage(const grpc::string& msg) {
+ fprintf(
+ stderr,
+ "%s\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"
+ // " grpc_cli totext ... ; Convert binary message to text\n"
+ // " grpc_cli tobinary ... ; Convert text message to binary\n"
+ " grpc_cli help ... ; Print this message, or per-command usage\n"
+ "\n",
+ msg.c_str());
+
+ exit(1);
+}
+
+const Command* FindCommand(const grpc::string& name) {
+ for (int i = 0; i < (int)ArraySize(ops); i++) {
+ if (name == ops[i].command) {
+ return &ops[i];
+ }
+ }
+ return NULL;
+}
+} // namespace
+
+int GrpcToolMainLib(int argc, const char** argv, const CliCredentials cred,
+ GrpcToolOutputCallback callback) {
+ if (argc < 2) {
+ Usage("No command specified");
+ }
+
+ grpc::string command = argv[1];
+ argc -= 2;
+ argv += 2;
+
+ const Command* cmd = FindCommand(command);
+ if (cmd != NULL) {
+ GrpcTool grpc_tool;
+ if (argc < cmd->min_args || argc > cmd->max_args) {
+ // Force the command to print its usage message
+ fprintf(stderr, "\nWrong number of arguments for %s\n", command.c_str());
+ grpc_tool.SetPrintCommandMode(1);
+ return cmd->function(&grpc_tool, -1, NULL, cred, callback);
+ }
+ const bool ok = cmd->function(&grpc_tool, argc, argv, cred, callback);
+ return ok ? 0 : 1;
+ } else {
+ Usage("Invalid command '" + grpc::string(command.c_str()) + "'");
+ }
+ return 1;
+}
+
+GrpcTool::GrpcTool() : print_command_usage_(false), usage_exit_status_(0) {}
+
+void GrpcTool::CommandUsage(const grpc::string& usage) const {
+ if (print_command_usage_) {
+ fprintf(stderr, "\n%s%s\n", usage.c_str(),
+ (usage.empty() || usage[usage.size() - 1] != '\n') ? "\n" : "");
+ exit(usage_exit_status_);
+ }
+}
+
+bool GrpcTool::Help(int argc, const char** argv, const CliCredentials cred,
+ GrpcToolOutputCallback callback) {
+ CommandUsage(
+ "Print help\n"
+ " grpc_cli help [subcommand]\n");
+
+ if (argc == 0) {
+ Usage("");
+ } else {
+ const Command* cmd = FindCommand(argv[0]);
+ if (cmd == NULL) {
+ Usage("Unknown command '" + grpc::string(argv[0]) + "'");
+ }
+ SetPrintCommandMode(0);
+ cmd->function(this, -1, NULL, cred, callback);
+ }
+ return true;
+}
+
+bool GrpcTool::CallMethod(int argc, const char** argv,
+ const CliCredentials cred,
+ GrpcToolOutputCallback callback) {
+ CommandUsage(
+ "Call method\n"
+ " grpc_cli call <address> <service>[.<method>] <request>\n"
+ " <address> ; host:port\n"
+ " <service> ; Exported service name\n"
+ " <method> ; Method name\n"
+ " <request> ; Text protobuffer (overrides infile)\n"
+ " --proto_file ; Comma separated proto files used as a"
+ " fallback when parsing request/response\n"
+ " --proto_path ; The search path of proto files, valid"
+ " only when --proto_file is given\n"
+ " --metadata ; The metadata to be sent to the server\n"
+ " --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" +
+ cred.GetCredentialUsage());
+
+ std::stringstream output_ss;
+ grpc::string request_text;
+ grpc::string server_address(argv[0]);
+ grpc::string method_name(argv[1]);
+ std::unique_ptr<grpc::testing::ProtoFileParser> parser;
+ grpc::string serialized_request_proto;
+
+ if (argc == 3) {
+ request_text = argv[2];
+ if (!FLAGS_infile.empty()) {
+ fprintf(stderr, "warning: request given in argv, ignoring --infile\n");
+ }
+ } else {
+ std::stringstream input_stream;
+ if (FLAGS_infile.empty()) {
+ if (isatty(STDIN_FILENO)) {
+ fprintf(stderr, "reading request message from stdin...\n");
+ }
+ input_stream << std::cin.rdbuf();
+ } else {
+ std::ifstream input_file(FLAGS_infile, std::ios::in | std::ios::binary);
+ input_stream << input_file.rdbuf();
+ input_file.close();
+ }
+ request_text = input_stream.str();
+ }
+
+ std::shared_ptr<grpc::Channel> channel =
+ grpc::CreateChannel(server_address, cred.GetCredentials());
+ if (!FLAGS_binary_input || !FLAGS_binary_output) {
+ parser.reset(
+ new grpc::testing::ProtoFileParser(FLAGS_remotedb ? channel : nullptr,
+ FLAGS_proto_path, FLAGS_proto_file));
+ if (parser->HasError()) {
+ return false;
+ }
+ }
+
+ if (FLAGS_binary_input) {
+ serialized_request_proto = request_text;
+ } else {
+ serialized_request_proto = parser->GetSerializedProtoFromMethod(
+ method_name, request_text, true /* is_request */);
+ if (parser->HasError()) {
+ return false;
+ }
+ }
+ fprintf(stderr, "connecting to %s\n", server_address.c_str());
+
+ grpc::string serialized_response_proto;
+ std::multimap<grpc::string, grpc::string> client_metadata;
+ std::multimap<grpc::string_ref, grpc::string_ref> server_initial_metadata,
+ server_trailing_metadata;
+ ParseMetadataFlag(&client_metadata);
+ PrintMetadata(client_metadata, "Sending client initial metadata:");
+ grpc::Status status = grpc::testing::CliCall::Call(
+ channel, parser->GetFormatedMethodName(method_name),
+ serialized_request_proto, &serialized_response_proto, client_metadata,
+ &server_initial_metadata, &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, "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: \n " << response_text << std::endl;
+ }
+ } else {
+ fprintf(stderr, "Rpc failed with status code %d, error message: %s\n",
+ status.error_code(), status.error_message().c_str());
+ }
+
+ return callback(output_ss.str());
+}
+
+} // namespace testing
+} // namespace grpc
diff --git a/test/cpp/util/grpc_tool.h b/test/cpp/util/grpc_tool.h
new file mode 100644
index 0000000000..3da86937c2
--- /dev/null
+++ b/test/cpp/util/grpc_tool.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.
+ *
+ */
+
+#ifndef GRPC_TEST_CPP_UTIL_GRPC_TOOL_H
+#define GRPC_TEST_CPP_UTIL_GRPC_TOOL_H
+
+#include <functional>
+
+#include <grpc++/support/config.h>
+
+#include "test/cpp/util/cli_credentials.h"
+
+namespace grpc {
+namespace testing {
+
+typedef std::function<bool(const grpc::string &)> GrpcToolOutputCallback;
+
+int GrpcToolMainLib(int argc, const char **argv, CliCredentials cred,
+ GrpcToolOutputCallback callback);
+
+} // namespace testing
+} // namespace grpc
+
+#endif // GRPC_TEST_CPP_UTIL_GRPC_TOOL_H
diff --git a/test/cpp/util/grpc_tool_test.cc b/test/cpp/util/grpc_tool_test.cc
new file mode 100644
index 0000000000..b96afaf50c
--- /dev/null
+++ b/test/cpp/util/grpc_tool_test.cc
@@ -0,0 +1,227 @@
+/*
+ *
+ * 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 "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/cli_credentials.h"
+#include "test/cpp/util/string_ref_helper.h"
+
+using grpc::testing::EchoRequest;
+using grpc::testing::EchoResponse;
+
+namespace grpc {
+namespace testing {
+namespace {
+
+class TestCliCredentials GRPC_FINAL : public grpc::testing::CliCredentials {
+ public:
+ std::shared_ptr<grpc::ChannelCredentials> GetCredentials() const
+ GRPC_OVERRIDE {
+ return InsecureChannelCredentials();
+ }
+ const grpc::string GetCredentialUsage() const GRPC_OVERRIDE { return ""; }
+};
+
+} // namespame
+
+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() {}
+
+ // SetUpServer cannot be used with EXPECT_EXIT. grpc_pick_unused_port_or_die()
+ // uses atexit() to free chosen ports, and it will spawn a new thread in
+ // resolve_address_posix.c:192 at exit time.
+ const grpc::string SetUpServer() {
+ std::ostringstream server_address;
+ 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();
+ return server_address.str();
+ }
+
+ void ShutdownServer() { server_->Shutdown(); }
+
+ std::unique_ptr<Server> server_;
+ 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, TestCliCredentials(),
+ 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, TestCliCredentials(),
+ 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, TestCliCredentials(),
+ 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;
+
+ const grpc::string server_address = SetUpServer();
+ const char* argv[] = {"grpc_cli", "call", server_address.c_str(), "Echo",
+ "message: 'Hello'"};
+
+ EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(),
+ std::bind(PrintStream, &output_stream,
+ std::placeholders::_1)));
+ // Expected output: "message: \"Hello\""
+ EXPECT_TRUE(NULL !=
+ strstr(output_stream.str().c_str(), "message: \"Hello\""));
+ ShutdownServer();
+}
+
+TEST_F(GrpcToolTest, TooFewArguments) {
+ // Test input "grpc_cli call localhost:<port> Echo "message: 'Hello'"
+ std::stringstream output_stream;
+ const char* argv[] = {"grpc_cli", "call", "Echo"};
+
+ // Exit with 1
+ EXPECT_EXIT(
+ GrpcToolMainLib(
+ ArraySize(argv), argv, TestCliCredentials(),
+ 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;
+ const char* argv[] = {"grpc_cli", "call", "localhost:10000",
+ "Echo", "Echo", "message: 'Hello'"};
+
+ // Exit with 1
+ EXPECT_EXIT(
+ GrpcToolMainLib(
+ ArraySize(argv), argv, TestCliCredentials(),
+ 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();
+}
diff --git a/test/cpp/util/proto_file_parser.cc b/test/cpp/util/proto_file_parser.cc
index 5b0d925e1c..0c88c24448 100644
--- a/test/cpp/util/proto_file_parser.cc
+++ b/test/cpp/util/proto_file_parser.cc
@@ -37,7 +37,6 @@
#include <iostream>
#include <sstream>
-#include <google/protobuf/text_format.h>
#include <grpc++/support/config.h>
namespace grpc {
@@ -56,8 +55,7 @@ bool MethodNameMatch(const grpc::string& full_name, const grpc::string& input) {
}
} // namespace
-class ErrorPrinter
- : public google::protobuf::compiler::MultiFileErrorCollector {
+class ErrorPrinter : public protobuf::compiler::MultiFileErrorCollector {
public:
explicit ErrorPrinter(ProtoFileParser* parser) : parser_(parser) {}
@@ -71,7 +69,7 @@ class ErrorPrinter
void AddWarning(const grpc::string& filename, int line, int column,
const grpc::string& message) GRPC_OVERRIDE {
- std::cout << "warning " << filename << " " << line << " " << column << " "
+ std::cerr << "warning " << filename << " " << line << " " << column << " "
<< message << std::endl;
}
@@ -79,62 +77,69 @@ class ErrorPrinter
ProtoFileParser* parser_; // not owned
};
-ProtoFileParser::ProtoFileParser(const grpc::string& proto_path,
- const grpc::string& file_name,
- const grpc::string& method)
+ProtoFileParser::ProtoFileParser(std::shared_ptr<grpc::Channel> channel,
+ const grpc::string& proto_path,
+ const grpc::string& protofiles)
: has_error_(false) {
- source_tree_.MapPath("", proto_path);
- error_printer_.reset(new ErrorPrinter(this));
- importer_.reset(new google::protobuf::compiler::Importer(
- &source_tree_, error_printer_.get()));
- const auto* file_desc = importer_->Import(file_name);
- if (!file_desc) {
- LogError("");
- return;
+ std::vector<std::string> service_list;
+ if (channel) {
+ reflection_db_.reset(new grpc::ProtoReflectionDescriptorDatabase(channel));
+ reflection_db_->GetServices(&service_list);
}
- dynamic_factory_.reset(
- new google::protobuf::DynamicMessageFactory(importer_->pool()));
- std::vector<const google::protobuf::ServiceDescriptor*> service_desc_list;
- for (int i = 0; i < file_desc->service_count(); i++) {
- service_desc_list.push_back(file_desc->service(i));
- }
- InitProtoFileParser(method, service_desc_list);
-}
+ if (!protofiles.empty()) {
+ source_tree_.MapPath("", proto_path);
+ error_printer_.reset(new ErrorPrinter(this));
+ importer_.reset(
+ new protobuf::compiler::Importer(&source_tree_, error_printer_.get()));
-ProtoFileParser::ProtoFileParser(std::shared_ptr<grpc::Channel> channel,
- const grpc::string& method)
- : has_error_(false),
- desc_db_(new grpc::ProtoReflectionDescriptorDatabase(channel)),
- desc_pool_(new google::protobuf::DescriptorPool(desc_db_.get())) {
- std::vector<std::string> service_list;
- if (!desc_db_->GetServices(&service_list)) {
- LogError(
- "Failed to get services from the server, "
- "it may not have the reflection service.\n"
- "Please try to use the --protofiles option to provide a proto file.");
+ grpc::string file_name;
+ std::stringstream ss(protofiles);
+ while (std::getline(ss, file_name, ',')) {
+ const auto* file_desc = importer_->Import(file_name);
+ if (file_desc) {
+ for (int i = 0; i < file_desc->service_count(); i++) {
+ service_desc_list_.push_back(file_desc->service(i));
+ }
+ } else {
+ std::cerr << file_name << " not found" << std::endl;
+ }
+ }
+
+ file_db_.reset(new protobuf::DescriptorPoolDatabase(*importer_->pool()));
}
- if (has_error_) {
+
+ if (!reflection_db_ && !file_db_) {
+ LogError("No available proto database");
return;
}
- dynamic_factory_.reset(
- new google::protobuf::DynamicMessageFactory(desc_pool_.get()));
- std::vector<const google::protobuf::ServiceDescriptor*> service_desc_list;
+ if (!reflection_db_) {
+ desc_db_ = std::move(file_db_);
+ } else if (!file_db_) {
+ desc_db_ = std::move(reflection_db_);
+ } else {
+ desc_db_.reset(new protobuf::MergedDescriptorDatabase(reflection_db_.get(),
+ file_db_.get()));
+ }
+
+ 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++) {
- service_desc_list.push_back(desc_pool_->FindServiceByName(*it));
+ if (const protobuf::ServiceDescriptor* service_desc =
+ desc_pool_->FindServiceByName(*it)) {
+ service_desc_list_.push_back(service_desc);
+ }
}
- InitProtoFileParser(method, service_desc_list);
}
ProtoFileParser::~ProtoFileParser() {}
-void ProtoFileParser::InitProtoFileParser(
- const grpc::string& method,
- const std::vector<const google::protobuf::ServiceDescriptor*>
- service_desc_list) {
- const google::protobuf::MethodDescriptor* method_descriptor = nullptr;
- for (auto it = service_desc_list.begin(); it != service_desc_list.end();
+grpc::string ProtoFileParser::GetFullMethodName(const grpc::string& method) {
+ has_error_ = false;
+ const protobuf::MethodDescriptor* method_descriptor = nullptr;
+ for (auto it = service_desc_list_.begin(); it != service_desc_list_.end();
it++) {
const auto* service_desc = *it;
for (int j = 0; j < service_desc->method_count(); j++) {
@@ -154,28 +159,82 @@ void ProtoFileParser::InitProtoFileParser(
LogError("Method name not found");
}
if (has_error_) {
- return;
+ return "";
+ }
+
+ return method_descriptor->full_name();
+}
+
+grpc::string ProtoFileParser::GetFormatedMethodName(
+ const grpc::string& method) {
+ has_error_ = false;
+ grpc::string formated_method_name = GetFullMethodName(method);
+ if (has_error_) {
+ return "";
}
- full_method_name_ = method_descriptor->full_name();
- size_t last_dot = full_method_name_.find_last_of('.');
+ size_t last_dot = formated_method_name.find_last_of('.');
if (last_dot != grpc::string::npos) {
- full_method_name_[last_dot] = '/';
+ formated_method_name[last_dot] = '/';
+ }
+ formated_method_name.insert(formated_method_name.begin(), '/');
+ return formated_method_name;
+}
+
+grpc::string ProtoFileParser::GetMessageTypeFromMethod(
+ const grpc::string& method, bool is_request) {
+ has_error_ = false;
+ grpc::string full_method_name = GetFullMethodName(method);
+ if (has_error_) {
+ return "";
+ }
+ const protobuf::MethodDescriptor* method_desc =
+ desc_pool_->FindMethodByName(full_method_name);
+ if (!method_desc) {
+ LogError("Method not found");
+ return "";
}
- full_method_name_.insert(full_method_name_.begin(), '/');
- request_prototype_.reset(
- dynamic_factory_->GetPrototype(method_descriptor->input_type())->New());
- response_prototype_.reset(
- dynamic_factory_->GetPrototype(method_descriptor->output_type())->New());
+ return is_request ? method_desc->input_type()->full_name()
+ : method_desc->output_type()->full_name();
}
-grpc::string ProtoFileParser::GetSerializedProto(
- const grpc::string& text_format_proto, bool is_request) {
+grpc::string ProtoFileParser::GetSerializedProtoFromMethod(
+ const grpc::string& method, const grpc::string& text_format_proto,
+ bool is_request) {
+ has_error_ = false;
+ grpc::string message_type_name = GetMessageTypeFromMethod(method, is_request);
+ if (has_error_) {
+ return "";
+ }
+ return GetSerializedProtoFromMessageType(message_type_name,
+ text_format_proto);
+}
+
+grpc::string ProtoFileParser::GetTextFormatFromMethod(
+ const grpc::string& method, const grpc::string& serialized_proto,
+ bool is_request) {
+ has_error_ = false;
+ grpc::string message_type_name = GetMessageTypeFromMethod(method, is_request);
+ if (has_error_) {
+ return "";
+ }
+ return GetTextFormatFromMessageType(message_type_name, serialized_proto);
+}
+
+grpc::string ProtoFileParser::GetSerializedProtoFromMessageType(
+ const grpc::string& message_type_name,
+ const grpc::string& text_format_proto) {
+ has_error_ = false;
grpc::string serialized;
- grpc::protobuf::Message* msg =
- is_request ? request_prototype_.get() : response_prototype_.get();
- bool ok =
- google::protobuf::TextFormat::ParseFromString(text_format_proto, msg);
+ const protobuf::Descriptor* desc =
+ desc_pool_->FindMessageTypeByName(message_type_name);
+ if (!desc) {
+ LogError("Message type not found");
+ return "";
+ }
+ 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 "";
@@ -188,16 +247,24 @@ grpc::string ProtoFileParser::GetSerializedProto(
return serialized;
}
-grpc::string ProtoFileParser::GetTextFormat(
- const grpc::string& serialized_proto, bool is_request) {
- grpc::protobuf::Message* msg =
- is_request ? request_prototype_.get() : response_prototype_.get();
+grpc::string ProtoFileParser::GetTextFormatFromMessageType(
+ const grpc::string& message_type_name,
+ const grpc::string& serialized_proto) {
+ has_error_ = false;
+ const protobuf::Descriptor* desc =
+ desc_pool_->FindMessageTypeByName(message_type_name);
+ if (!desc) {
+ LogError("Message type not found");
+ return "";
+ }
+ std::unique_ptr<grpc::protobuf::Message> msg(
+ dynamic_factory_->GetPrototype(desc)->New());
if (!msg->ParseFromString(serialized_proto)) {
LogError("Failed to deserialize proto.");
return "";
}
grpc::string text_format;
- if (!google::protobuf::TextFormat::PrintToString(*msg, &text_format)) {
+ if (!protobuf::TextFormat::PrintToString(*msg.get(), &text_format)) {
LogError("Failed to print proto message to text format");
return "";
}
@@ -206,7 +273,7 @@ grpc::string ProtoFileParser::GetTextFormat(
void ProtoFileParser::LogError(const grpc::string& error_msg) {
if (!error_msg.empty()) {
- std::cout << error_msg << std::endl;
+ std::cerr << error_msg << std::endl;
}
has_error_ = true;
}
diff --git a/test/cpp/util/proto_file_parser.h b/test/cpp/util/proto_file_parser.h
index b442d77db9..eda3991e72 100644
--- a/test/cpp/util/proto_file_parser.h
+++ b/test/cpp/util/proto_file_parser.h
@@ -36,11 +36,9 @@
#include <memory>
-#include <google/protobuf/compiler/importer.h>
-#include <google/protobuf/dynamic_message.h>
#include <grpc++/channel.h>
-#include "src/compiler/config.h"
+#include "test/cpp/util/config_grpc_cli.h"
#include "test/cpp/util/proto_reflection_descriptor_database.h"
namespace grpc {
@@ -50,44 +48,63 @@ class ErrorPrinter;
// Find method and associated request/response types.
class ProtoFileParser {
public:
- // The given proto file_name will be searched in a source tree rooted from
- // proto_path. The method could be a partial string such as Service.Method or
- // even just Method. It will log an error if there is ambiguity.
- ProtoFileParser(const grpc::string& proto_path, const grpc::string& file_name,
- const grpc::string& method);
-
+ // The parser will search proto files using the server reflection service
+ // provided on the given channel. The given protofiles in a source tree rooted
+ // from proto_path will also be searched.
ProtoFileParser(std::shared_ptr<grpc::Channel> channel,
- const grpc::string& method);
+ const grpc::string& proto_path,
+ const grpc::string& protofiles);
+
~ProtoFileParser();
- grpc::string GetFullMethodName() const { return full_method_name_; }
+ // The input method name in the following four functions could be a partial
+ // string such as Service.Method or even just Method. It will log an error if
+ // there is ambiguity.
+ // Full method name is in the form of Service.Method, it's good to be used in
+ // descriptor database queries.
+ grpc::string GetFullMethodName(const grpc::string& method);
+
+ // Formated method name is in the form of /Service/Method, it's good to be
+ // used as the argument of Stub::Call()
+ grpc::string GetFormatedMethodName(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);
- grpc::string GetSerializedProto(const grpc::string& text_format_proto,
- bool is_request);
+ grpc::string GetSerializedProtoFromMessageType(
+ const grpc::string& message_type_name,
+ const grpc::string& text_format_proto);
- grpc::string GetTextFormat(const grpc::string& serialized_proto,
- bool is_request);
+ grpc::string GetTextFormatFromMessageType(
+ const grpc::string& message_type_name,
+ const grpc::string& serialized_proto);
bool HasError() const { return has_error_; }
void LogError(const grpc::string& error_msg);
private:
- void InitProtoFileParser(
- const grpc::string& method,
- const std::vector<const google::protobuf::ServiceDescriptor*> services);
+ grpc::string GetMessageTypeFromMethod(const grpc::string& method,
+ bool is_request);
bool has_error_;
grpc::string request_text_;
- grpc::string full_method_name_;
- google::protobuf::compiler::DiskSourceTree source_tree_;
+ protobuf::compiler::DiskSourceTree source_tree_;
std::unique_ptr<ErrorPrinter> error_printer_;
- std::unique_ptr<google::protobuf::compiler::Importer> importer_;
- std::unique_ptr<grpc::ProtoReflectionDescriptorDatabase> desc_db_;
- std::unique_ptr<google::protobuf::DescriptorPool> desc_pool_;
- std::unique_ptr<google::protobuf::DynamicMessageFactory> dynamic_factory_;
+ std::unique_ptr<protobuf::compiler::Importer> importer_;
+ std::unique_ptr<grpc::ProtoReflectionDescriptorDatabase> reflection_db_;
+ std::unique_ptr<protobuf::DescriptorPoolDatabase> file_db_;
+ std::unique_ptr<protobuf::DescriptorDatabase> desc_db_;
+ std::unique_ptr<protobuf::DescriptorPool> desc_pool_;
+ std::unique_ptr<protobuf::DynamicMessageFactory> dynamic_factory_;
std::unique_ptr<grpc::protobuf::Message> request_prototype_;
std::unique_ptr<grpc::protobuf::Message> response_prototype_;
+ std::vector<const protobuf::ServiceDescriptor*> service_desc_list_;
};
} // namespace testing
diff --git a/test/cpp/util/proto_reflection_descriptor_database.cc b/test/cpp/util/proto_reflection_descriptor_database.cc
index 8fd466feb0..f0d14c686a 100644
--- a/test/cpp/util/proto_reflection_descriptor_database.cc
+++ b/test/cpp/util/proto_reflection_descriptor_database.cc
@@ -58,7 +58,7 @@ ProtoReflectionDescriptorDatabase::~ProtoReflectionDescriptorDatabase() {
stream_->WritesDone();
Status status = stream_->Finish();
if (!status.ok()) {
- gpr_log(GPR_ERROR,
+ gpr_log(GPR_INFO,
"ServerReflectionInfo rpc failed. Error code: %d, details: %s",
(int)status.error_code(), status.error_message().c_str());
}
diff --git a/test/distrib/csharp/DistribTest/App.config b/test/distrib/csharp/DistribTest/App.config
deleted file mode 100644
index 30d3e09472..0000000000
--- a/test/distrib/csharp/DistribTest/App.config
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<configuration>
- <startup>
- <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
- </startup>
- <runtime>
- <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
- <dependentAssembly>
- <assemblyIdentity name="System.Net.Http.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
- <bindingRedirect oldVersion="0.0.0.0-4.2.29.0" newVersion="4.2.29.0" />
- </dependentAssembly>
- </assemblyBinding>
- </runtime>
-</configuration> \ No newline at end of file
diff --git a/test/distrib/csharp/DistribTest/DistribTest.csproj b/test/distrib/csharp/DistribTest/DistribTest.csproj
index 1acb34d1b2..6ca03b2c80 100644
--- a/test/distrib/csharp/DistribTest/DistribTest.csproj
+++ b/test/distrib/csharp/DistribTest/DistribTest.csproj
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -41,6 +41,8 @@
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
+ <WarningLevel>4</WarningLevel>
+ <Optimize>false</Optimize>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
@@ -51,39 +53,15 @@
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
+ <WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
- <Reference Include="BouncyCastle.Crypto">
- <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
- </Reference>
- <Reference Include="Google.Apis.Auth">
- <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.dll</HintPath>
- </Reference>
- <Reference Include="Google.Apis.Auth.PlatformServices">
- <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath>
- </Reference>
- <Reference Include="Google.Apis.Core">
- <HintPath>..\packages\Google.Apis.Core.1.9.3\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath>
- </Reference>
<Reference Include="Grpc.Auth">
<HintPath>..\packages\Grpc.Auth.__GRPC_NUGET_VERSION__\lib\net45\Grpc.Auth.dll</HintPath>
</Reference>
<Reference Include="Grpc.Core">
<HintPath>..\packages\Grpc.Core.__GRPC_NUGET_VERSION__\lib\net45\Grpc.Core.dll</HintPath>
</Reference>
- <Reference Include="Microsoft.Threading.Tasks">
- <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
- </Reference>
- <Reference Include="Microsoft.Threading.Tasks.Extensions">
- <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
- </Reference>
- <Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop">
- <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath>
- </Reference>
- <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
- </Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Interactive.Async">
@@ -91,25 +69,33 @@
</Reference>
<Reference Include="System.Net" />
<Reference Include="System.Net.Http" />
- <Reference Include="System.Net.Http.Extensions">
- <HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll</HintPath>
- </Reference>
- <Reference Include="System.Net.Http.Primitives">
- <HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll</HintPath>
- </Reference>
<Reference Include="System.Net.Http.WebRequest" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
+ <Reference Include="BouncyCastle.Crypto">
+ <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
+ </Reference>
+ <Reference Include="Newtonsoft.Json">
+ <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
+ </Reference>
+ <Reference Include="Google.Apis.Core">
+ <HintPath>..\packages\Google.Apis.Core.1.15.0\lib\net45\Google.Apis.Core.dll</HintPath>
+ </Reference>
+ <Reference Include="Google.Apis.Auth">
+ <HintPath>..\packages\Google.Apis.Auth.1.15.0\lib\net45\Google.Apis.Auth.dll</HintPath>
+ </Reference>
+ <Reference Include="Google.Apis.Auth.PlatformServices">
+ <HintPath>..\packages\Google.Apis.Auth.1.15.0\lib\net45\Google.Apis.Auth.PlatformServices.dll</HintPath>
+ </Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
- <None Include="App.config" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
@@ -119,9 +105,7 @@
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Grpc.Core.__GRPC_NUGET_VERSION__\build\net45\Grpc.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Grpc.Core.__GRPC_NUGET_VERSION__\build\net45\Grpc.Core.targets'))" />
- <Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" />
</Target>
- <Import Project="..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
diff --git a/test/distrib/csharp/DistribTest/packages.config b/test/distrib/csharp/DistribTest/packages.config
index 69630193d0..e5d2d9d383 100644
--- a/test/distrib/csharp/DistribTest/packages.config
+++ b/test/distrib/csharp/DistribTest/packages.config
@@ -1,15 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="BouncyCastle" version="1.7.0" targetFramework="net45" />
- <package id="Google.Apis.Auth" version="1.9.3" targetFramework="net45" />
- <package id="Google.Apis.Core" version="1.9.3" targetFramework="net45" />
+ <package id="Google.Apis.Auth" version="1.15.0" targetFramework="net45" />
+ <package id="Google.Apis.Core" version="1.15.0" targetFramework="net45" />
<package id="Grpc" version="__GRPC_NUGET_VERSION__" targetFramework="net45" />
<package id="Grpc.Auth" version="__GRPC_NUGET_VERSION__" targetFramework="net45" />
<package id="Grpc.Core" version="__GRPC_NUGET_VERSION__" targetFramework="net45" />
<package id="Ix-Async" version="1.2.3" targetFramework="net45" />
- <package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" />
- <package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net45" />
- <package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" />
- <package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" />
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
</packages>
diff --git a/test/distrib/python/distribtest.py b/test/distrib/python/distribtest.py
index dc20688140..0125ee6a56 100644
--- a/test/distrib/python/distribtest.py
+++ b/test/distrib/python/distribtest.py
@@ -27,10 +27,10 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-from grpc.beta import implementations
+import grpc
# This code doesn't do much but makes sure the native extension is loaded
# which is what we are testing here.
-channel = implementations.insecure_channel('localhost', 1000)
+channel = grpc.insecure_channel('localhost:1000')
del channel
print 'Success!'